본문

ns-3의 Tracing subsystem #1. 소개와 기본예제

이 글은 ns-3 매뉴얼 페이지(http://www.nsnam.org/docs/manual/html/tracing.html)를 기반으로 작성되었습니다.


ns-3의 값 추적에 있어 간편하게 Log component를 사용할 수 있겠지만, fine-grained하게 제어할 수 없는 성질때문에, 그리고 너무나 단순한 기능제공으로인해 더욱 강력한 tracing system이 필요하였다. 이때, core system에 접근하여, 프로그램의 재작성없이, 원하는 자료만 뽑아올 수 있는 시스템이 필요하였고, 이 뿐만 아니라 만약 특정 값의 변경이나 특정 이벤트의 발생시 공지하는 기능이 있으면 더욱 좋을 것이다-->는 이제부터 설명할 ns-3의 tracing subsystem에서 다루어 진다. ns-3의 tracing subsystem은 ns-3의 Callback과 Attribute 메커니즘을 사용하였기 때문에, 이들에 대해 친숙해진 이후에 이를 읽는것을 권장한다. tracing system은 source와 sink가 분리되어있으며, 특별한 메커니즘으로 이들을 연결시킨다. 


trace source란, 시뮬레이션 도중 이벤트를 발생하는 동시에, 특정 데이터에게 접근권한을 부여할 수 있는 개체로서, 예를 들자면 net device로 패킷이 들어왔을 때를 알려주고, trace sink에게 패킷에 대한 접근권한을 부여하는 경우를 생각해볼 수 있다. trace source는 또한, 모델에서의 상태변화를 알려주는 기능을 하는데, TCP 모델에서의 혼잡 윈도우(congestion window)가 대표적인 예이다.


trace source는 독립적으로 존재할 수 없으며, 반드시 trace 정보를 사용할 trace sink가 필요하다. trace source가 생산자라면, trace sink는 소비자로 생각할 수 있다. 이와같은 명백한 분리는 시스템 상에서 다양하고 많은 trace source들이 혼재할 수 있도록 하며, 언제든지 원할때마다 이들을 sink와 연결하여 사용할 수 있도록 한다(trace sink와 연결되지 않는 trace source들은 아무런 기능도 수행하지 않는다). 또한 하나의 trace source에 여러개의 trace sink를 연결할 수도 있다. 


이와같이 1:n 관계로 trace source와 trace sink를 연결하도록 도와주는 '프로토콜'은 ns-3의 'Callback'이다. 다시 기억을 되살려보자면, Callback system은 시스템상의 두개의 모듈들 function call을 사용하여 서로간에 통신을 가능하게 하며, 이와 동시에 서로간의 연결을 언제든 끊을 수 있는 특성을 가지고 있다. trace source는 다수의 함수가 등록되는 callback개체이다. trace sink가 특정 이벤트를 처리하고자 할때, 이는 trace source내의 callback목록에 자신의 callback을 추가한다. 특정 이벤트가 발생하게 되면, trace source는 operator()을 수행하여 callback목록상에서 해당 이벤트에대한 모든 callback을 순차적으로 처리하도록 한다. 이런 방식으로 매개변수는 function으로 이루어진 trace sink로 보내지게 된다.


* 예제

#include "ns3/object.h"

#include "ns3/uinteger.h"

#include "ns3/traced-value.h""

#include "ns3/trace-source-accessor.h"

 

#include <iostream>

 

using namespace ns3;


처음 2개의 include는 Object와 Attribute 시스템을 위해, 그리고 traced-value.h는 value semantics를 지원하는 데이터에 대한 선언을 불러오기 위해 include된다. 보통 value semantics라 한다면 주소가 아닌 '값으로' 해당 변수를 처리한다는 것을 의미한다. 기본적으로 value semantics을 사용하려면 연관복사생성자(associated copy constructior)와 할당 연산자가 등록되어있어야 한다. tracing을 위하여, 이에 더불어 plain-old-data(POD)형을 위한 미리 지정된 연산자, 즉, Operator=, operator++, operator–, operator+, operator==, 등을 추가하기가 요구된다. --이들 연산자를 사용한 연산 이벤트는 tracing 시스템에서 사용할 수 있게 된다.


class MyObject : public Object {

public:

  static TypeId GetTypeId (void)  {

    static TypeId tid = TypeId ("MyObject")

      .SetParent (Object::GetTypeId ())

      .AddConstructor<MyObject> ()

      .AddTraceSource ("MyInteger",

                       "An integer value to trace.",

                       MakeTraceSourceAccessor (&MyObject::m_myInt));

    return tid;

  }

 

  MyObject () {}

  TracedValue<uint32_t> m_myInt;

};


tracing system은 Attributes와, 그리고 Attributes는 Object와 연계되었기 떄문에, trace source는 Object를 상속받아야 한다. 그리고 위 소스에서 중요한 두부분은 .AddTraceSource()와 TracedValue의 선언이다. .AddTraceSource()는 trace source와 외부세계를 연결하는 'hook'을 제공한다. TracedValue선언은 위에서 언급한 연산자들에 대한 지원을 하며, callback 프로세스를 진행하도록 한다. 


int main (int argc, char *argv[]) {

  Ptr<MyObject> myObject = CreateObject<MyObject> ();

  myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));

  myObject->m_myInt = 1234;

}

 

void IntTrace (Int oldValue, Int newValue) {

  std::cout << "Traced " << oldValue << " to " << newValue << std::endl;

}


위는 trace sink의 정의이다. 이는 callback 함수와 직접적으로 연결되어, TraceValue에서의 연산자가 수행될 때마다 함수가 호출될 것이다. 가장 처음으로 할 일은 trace source를 포함하는 객체를 생성하는 것이다. 그리고나서, TraceConnectWithoutContext를 사용하여 trace source와 trace sink를 서로 연결시켜 준다. 이때 중요한 점은, MakeCallBack()은 템플릿 함수라는 것이다. 이에 대한 사항을 떠올려 보자면, 이 함수는 오버로드된 operator()을 제공하는 특별한 functor를 생성하여 callback을 수행하도록 하는 기능을 한다. ++, --등과 같은 오버로드된 연산자들은 이 operator()을 사용하여 callback을 invoke한다. 


TraceConnectWithoutContext는 trace source에 할당된 Attribute의 이름(문자열)을 취한다. main함수의 마지막줄인 myObject->m_myInt = 1234;는 매개변수로 전달되는 1234값이 operator=을 통하여 멤버 변수에 할당되는 과정으로 설명할 수 있다. 이때 이 operator=연산자는 TraceValue에 의해 재정의되어서, 두개의 int형을 매개변수로 받는, void를 반환하는 callback(여기서는 IntTrace())을 수행하도록 설계되어있다. 

댓글

Holic Spirit :: Tistory Edition

design by tokiidesu. powerd by kakao.