개인 공부

NS-3 독학 - 4.fourth.cc(Tracing)

Beige00 2024. 1. 13. 10:59

fourth.cc는 지금까지와 다르게 토폴로지를 가정하고 시뮬레이션 환경을 구성하는 것이 아니라

시뮬레이터를 돌리며 로그를 찍는 방법을 다루고 있다.(TCP throughput, error rate, SNR 등) 

따라서 해당 기법을 배우면 IRSML에서 필요로 하는 데이터들을 뽑아내는 방법을 고안해낼 수 있을 것 같다.

(현재까지의 학습으로 Wireless network를 구성하는 법, PCAP 읽는 법, LOG를 찍어 data 추출하는 법을 할 수 있게 되었다.)

* https://velog.io/@choiyhking/6-NS-3-Tracing 를 참조함.


* Tracing

NS-3에서 output을 얻는 전략은 크게 2가지가 있다.

1. bulk output 메커니즘을 사용해 그 output에서 data를 뽑아내는 방법.

2. 원하는 data만 선별해서 추출하는 output 메커니즘을 직접 개발하는 방법.

 

1.의 경우는 유의미한 데이터를 뽑아내기 위해 스크립트를 작성하는 것(추출법 정의)외에 ns-3에 대해 아무것도 변경하지 않아도 된다. (Pcap, NS_LOG output) 

그러나 NS_LOG output은 오직 debug build에서만 이용 가능해서 성능에 패널티를 받는다.

 

2.의 경우가 Tracing 메커니즘이다. ("low-level" tracing)

high-level tracing은 trace helper를 활용해 사전에 정의된 소스 정보를 얻는 것이다.

low-level은 소스를 직접 custom sink에 연결하는 것이다.

2.의 장점은 관리해야할 데이터를 줄일 수 있고, 후처리 과정(스크립트 작성)을 하지 않을 수 있다는 점이다.

 

결론적으로 tracing system은 각각 독립적인 tracing source와 tracing sink의 개념으로 설계되었으며, source를 sink에 연결하는 메커니즘을 사용한다.

 

Trace source : Event Alarm( ex: TCP의 cwnd 크기가 변할 때 마다, 연결된 sink들은 old, new value를 전달 받음.)

Trace sink : callback Function. (source가 전달한 정보의 소비자.)

-> sink가 trace event를 받아 처리하고 싶으면, trace soure가 가지고 있는 Callback List에 본인을 추가한다.

(Observer Pattern)


class MyObject : public Object
{
  public:
    /**
     * Register this type.
     * \return The TypeId.
     */
    static TypeId GetTypeId()
    {
        static TypeId tid = TypeId("MyObject")
                                .SetParent<Object>()
                                .SetGroupName("Tutorial")
                                .AddConstructor<MyObject>()
                                .AddTraceSource("MyInteger",
                                                "An integer value to trace.",
                                                MakeTraceSourceAccessor(&MyObject::m_myInt),
                                                "ns3::TracedValueCallback::Int32");
        return tid;
    }

    MyObject()
    {
    }

    TracedValue<int32_t> m_myInt; //!< The traced value.
};

void
IntTrace(int32_t oldValue, int32_t newValue)
{
    std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
}

int
main(int argc, char* argv[])
{
    Ptr<MyObject> myObject = CreateObject<MyObject>();
    myObject->TraceConnectWithoutContext("MyInteger", MakeCallback(&IntTrace));

    myObject->m_myInt = 1234;

    return 0;
}

소스코드 전문이다.

class하나와 함수 하나를 정의하는 모습을 볼 수 있다.(주석을 참조해가며 분석하면 쉽다.)

MyObject의 경우 Object를 상속하는데 Object는 ns-3에서 정의되어있는 클래스이다. (src>core>model>object.h)

주석의 설명에 따르면 메모리 관리와 객체 집합을 제공하는 기본 클래스라고 한다.

Tracing system은 Attriubtes, Object와 통합되므로 trace source를 위한 NS-3 Object 객체가 필요하다.

이 때 해당 Object를 우리 입맛에 맞게 custom 해서 사용해주면 된다.

(MyObject)

 

1.   static TypeId GetTypeId()
    {
        static TypeId tid = TypeId("MyObject")
                                .SetParent<Object>()
                                .SetGroupName("Tutorial")
                                .AddConstructor<MyObject>()
                                .AddTraceSource("MyInteger",
                                                "An integer value to trace.",
                                                MakeTraceSourceAccessor(&MyObject::m_myInt),
                                                "ns3::TracedValueCallback::Int32");
        return tid;
    } :

이 함수는 실행이 되면 TraceSource에 연결할 sink를 만들어 return해준다.

여기서 AddTraceSource는 trace source를 연결해주는 역할이다.

해당 메소드의 첫번째 파라미터는 source의 이름, 두번째는 도움 문자열, 세번째는 MyObject상에 정의된 m_myInt Trace할 것을 정의하는 것이다. 마지막 ns3::TracedValueCallback::Int32는 추적될 값의 타입을 나타낸다.

결론적으로 TypeID는 MyObject이며 Object를 상속하고, MyObject class의 그룹이름을 Tutorial로 설정한 뒤,

MyObject의 생성자를 등록, 추적할 값을 지정해주는 절차를 통해 TypeId tid를 만드는 것이다.

(Trace system 설정)

 

2.  MyObject()
    {
    }

    TracedValue<int32_t> m_myInt; //!< The traced value. :

Trace 될 값을 class 내부에 정의해준다. m_myInt는 사전에 설명한 대로 TracedValue<int32_5> 다.

 

3. void
IntTrace(int32_t oldValue, int32_t newValue)
{
    std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
} :

여기까지 class 정의가 끝났다면, MyObject class에서 data member인 m_myInt는 TracedValue<int32_t> 이므로

이 값이 변경되면 TracedValue class는 old, new 값을 제공한다.

따라서 이 값을 제공할 trace sink 함수는 old, new value를 제공해주어야한다.

따라서 이것이 trace function이 될 것이고, trace Value와 함께 제공해줄 것이다.

 

4. int
main(int argc, char* argv[])
{
    Ptr<MyObject> myObject = CreateObject<MyObject>();
    myObject->TraceConnectWithoutContext("MyInteger", MakeCallback(&IntTrace));

    myObject->m_myInt = 1234;

    return 0;
} :

MyObject의 인스턴스를 만들어준다. (MyObject 포인터)

그리고 myObject를 TraceConnectWithoutContext( trace value, trace funtion); 으로 source, sink 연결을 해준다.

그 후 myObject->m_myInt = 1234;로 값을 바꾸어주면 trace 정보가 출력될 것이다.

더보기
닫기
TraceConnectWithoutContext의 정의

 


* 실행결과

근데 공부를 하다가 찾은 자료에 의하면, TraceConnectWithoutContext는 실제 시스템에서 잘 이용되지 않는다고 한다.

대신 Config path를 사용해서 trace source를 선택하는 Config System이 주로 사용된다고 한다.

(상단에 첨부한 링크 참고)

결론적으로, 적절한 trace sink 코드를 작성해 source와 sink를 연결하고,

trace source의 path string을 만들어 Connect method에 넘겨준다. 그리고 동시에 callback 함수를 넘겨주면 정보를 뽑아볼 수 있게 된다.

이를 통해 third.cc에서 MH들의 이동 과정을 찍어보겠다.

더보기
닫기

-> Config Connect는 Simulator::Run()위에 추가하면 된다.

결국에는 Trace source를 지정해주고, Config path를 사용해서 직접 만든 trace sink 함수에 연결하면 된다.

Config::Connect는 Ptr<Object>를 찾고, 값이 변경되었을 시 Callback 함수를 실행한다.

결론적으로 어떤 trace source를 이용해서 해당 값을 받아서 어떻게 처리할지를 trace sink에 정의하고 trace source에 연결해주면 되는 것이다.

 

* 이용가능한 Trace source list

https://www.nsnam.org/docs/release/3.37/doxygen/de/de5/_trace_source_list.html

 

ns-3: All TraceSources

This is a list of all Tracing sources. For more information see the Tracing section of this API documentation and the Tracing sections in the Tutorial and Manual. ns3::AarfcdWifiManager Rate: Traced value for rate changes (b/s) ns3::AarfWifiManager Rate: T

www.nsnam.org

 

결론

1. Tracing할 source를 결정. (mobility 이동을 볼 것이므로 Mobility Model)

2. Config path 확인

3. position 값을 확인하고 싶으므로 Callback signature도 확인.

함수 명은 CourseChange가 되어야 한다.
void CourseChange

따라서 최종 path는 /NodeList/[i]/$ns3::MobilityModel/CourseChange가 될 것이다.

 

4. 형식에 맞춰 Trace sink 함수 구성

void 
CourseChange(std::string context, Ptr<const MobilityModel> model)
{
	Vector position = model->GetPosition();
    NS_LOG_UNCOND (context<<"@ x = " << position.x << ", y = " << position.y);
}
//Documetation에 의하면 Postion은 Vector type이다.
//따라서 Trace source model에게서 position 값을 추출해서,
// uncondition log 에 x,y를 기록해 준다.

여기서 우리가 연결에 사용할 함수는 Config::Connect이다. 해당 함수의 정의를 보면

Config::Connect() 정의

다음과 같이 정의되어있는데, 여기서 Connect는 CallbackImpl<void, std::string, ns3::Ptr<ns3::MobilityModel const>>를 요구하므로, Callback 함수의 파라미터 규칙또한 다음과 같이 맞추어주어야 한다.

 

5. Connect로 sink, source 연결

 Config::Connect ("/NodeList/7/$ns3::MobilityModel/CourseChange", MakeCallback (&CourseChange));

 


 

더보기
닫기

! 추가 : 이해가 잘되지 않아 직접 알아보았는데, Config::Connect에 콜백을 연결할 때,  TracedCallback은 파라미터가 Ptr밖에 없다.

그러나 우리가 사용할 Connect 함수에 연결되어야하는 CallBack 함수는 전체 컨텍스트를 사용하므로

콜백 시그니쳐에 path를 받아줄 string 파라미터가 추가로 필요하게 된 것이다.

ns-3 문서

결론만 요약하면 Config::Connect를 사용할 때는 std::string context 파라미터가 필요하다.

먄약 

void
CourseChange(Ptr<const MobilityModel> model)
{
    Vector position = model->GetPosition();
    NS_LOG_UNCOND ("@ x = " << position.x << ", y = " << position.y);
}
//Documetation에 의하면 Postion은 Vector type이다.
//따라서 Trace source model에게서 position 값을 추출해서,
// uncondition log 에 x,y를 기록해 준다.

과 같이 context 없이 함수를 구성하고 싶다면, ConnectWithoutContext를 사용하면 된다.

 

 

추가로, 내가 "/NodeList/7/$ns3::MobilityModel/CourseChange"을 path로 지정시 다음과 같은 일이 일어나는 것과 같다.

 

Ptr<NetDevice> nd = nodes.Get (6);          // Get the NetDevice

PtrMobilityModel> wd = DynamicCast<NetDevice> (nd);  

wd->TraceConnectWithoutContext ("CourseChange", MakeCallback (&CourseChangeWithoutContext)); // Connect 

'개인 공부' 카테고리의 다른 글

NS-3 독학 - 6. sixth.cc  (0) 2024.01.15
NS-3 독학 - 5.fifth.cc  (0) 2024.01.15
NS-3 독학 - 3.Third.cc  (0) 2024.01.12
NS-3 독학 - 2. Second.cc  (0) 2024.01.11
NS-3 독학 - 1. First.cc  (0) 2024.01.11