NS2中Trace部分的一点认识

NS2中Trace部分的一点认识

 

NS2追踪部分的源码主要涉及以下几个文件:

 

ns-2.34/trace/basetrace[.cc][.h]

ns-2.34/trace/trace[.cc][.h]

ns-2.34/trace/cmu-trace[.cc][.h]

 

ns-2.34/tcl/lib/ns-lib.tcl

ns-2.34/tcl/lib/ns-mobilenode.tcl

 

仿真输出文件如何与 OTcl 解释层次关联

以标准的仿真脚本文件 ns-2.34/tcl/ex/simple.tcl 为例,在开头部分有如下定义:

 

set f [open out.tr w]

$ns trace-all $f

 

第一句的作用是创建并且以只写的方式打开了追踪文件out.tr,第二句则调用了trace-all 这个 tcl 函数将创建的文件的操作句柄传递 tcl 变量。其原型如下(ns/tcl/lib/ns-lib.tcl):

 

Simulator instproc trace-all file {

    $self instvar traceAllFile_

    set traceAllFile_ $file

}

 

由此可知,在 OTcl 类 Simulator 中增加一个成员 traceAllFile_,并将追踪文件的句柄赋值给它。通过这个操作,追踪文件便被传递到解释层次中,可供随时调用。traceAllFile_ 这个成员变量比较重要,后面还会用到。

 

 

编译层次C++下的trace函数如何实现追踪数据:

通过阅读 ns-2.34/trace/ 下的 .cc 和 .h 代码,由函数的调用关系大概可以猜测到当我们在追踪 packet 的时候,应该是系统在某个地方调用了类class Trace 的 recv 函数,而 recv 继而会调用 format 函数去整理输出信息。在 format 函数中,有效信息都被输入到 pt_->buffer() 中了,在这里可能会产生疑问,我们最终看到的结果都是在输出文件 out.tr 中,在这怎么都输出到 pt_ 中了?查一下 pt_ 发现是类 class Trace 在构造的时候创建的 pt_ = new BaseTrace; 原来这个 pt_ 是一个由类 class BaseTrace 产生的对象。

 

Trace 又是怎样和输出文件 out.tr 关联起来的呢?

前面已经提到 out.tr 和解释层次 OTcl 中的成员变量 traceAllFile_ 关联起来了,那我们可以猜测 traceAllFile_ 应该是和 BaseTrace 在某个地方被关联起来了。而最有可能就是在创建节点的时候。

 

脚本中调用 set n0 [$ns node] 创建节点的过程我就不细说了。在ns-2.34/tcl/lib/ns-lib.tcl 中我们找到

Simulator instproc create-wireless-node 函数,中发现创建节点的时候有这样的操作:

 

set tracefd [$self get-ns-traceall]

        if {$tracefd != "" } {

        $node nodetrace $tracefd

        $node agenttrace $tracefd

    }

    set namtracefd [$self get-nam-traceall]

    if {$namtracefd != "" } {

        $node namattach $namtracefd

    }

 

第一句中又调用了 get-ns-traceall 这个函数,看一下它的原型只可它返回的就是 traceAllFile_。思路开始渐渐清晰,$node nodetrace $tracefd 调用了 nodetrace 这个函数,并将 $tracefd 也就是 traceAllFile_ 作为参数传递过去。在 ns/tcl/lib/ns-mobilenode.tcl 中,定义如下:

 

Node/MobileNode instproc nodetrace { tracefd } {

    #

    # This Trace Target is used to log changes in direction

    # and velocity for the mobile node.

    #

    set T [new Trace/Generic]

    $T target [[Simulator instance] set nullAgent_]

    $T attach $tracefd

    $T set src_ [$self id]

    $self log-target $T   

}

 

到此我们豁然开朗,在这函数中,创建了一个新的 Trace/Generic 对象,并通过 $T attach $tracefd 将这个新的对象链接到 traceAllFile_ 上,也就是链接到了输出文件 out.tr 上。

最后一句 $self log-target $T,这里 $self 指的是 Node/MobileNode 我们查看编译层次中类 class MobileNode 的 command 函数(ns-2.34/common/mobilenode[.cc][.h])会发现 log-target 这个操作的原型:

 

else if(strcmp(argv[1], "log-target") == 0) {

            log_target_ = (Trace*) TclObject::lookup(argv[2]);

            if (log_target_ == 0)

                return TCL_ERROR;

            return TCL_OK;

        }

 

log_target_ 是在类 class MobileNode 中定义的 Trace* log_target_;

 

现在我们终于明白了NS2追踪功能的链接过程,在编译层次中声明一个类 class Trace 的指针 log_target_ (通常叫这个名字),然后在解释层次中生成一个新的 Trace 对象,并将它们链接起来。

 

这部分遗留了一个问题:生成的新的 Trace/Generic 对象从其写法上看应该是从类 Trace 派生出来的一个子类。但是在 ns-2.34/trace/ 文件夹下却没找到它的声明,迷茫中。

 

链接的过程我们大概已经有了一点认识,贯穿起来想一下,追踪功能是怎么实现的?

 

无线仿真的脚本中,通常在 $ns_ node-config 的时候会传递一堆参数进去,其中就有追踪功能的开关,我们仅以 -macTrace ON 为例。逐步分析一下。

 

首先我们在ns/tcl/lib/ns-lib.tcl中找到 Simulator instproc node-config 这个函数,并且在其中找到如下的定义:

 

if [info exists macTrace_] {

        Simulator set MacTrace_ $macTrace_

    }

        if [info exists routerTrace_] {

        Simulator set RouterTrace_ $routerTrace_

    }

        if [info exists agentTrace_] {

        Simulator set AgentTrace_ $agentTrace_

    }

 

由此可以看出,传递进来的变量macTrace_被赋值给MacTrace_,那么也就是说MacTrace_ 就是那个开关。我们继续搜索 MacTrace_ 这个变量,发现它在 ns/tcl/lib/ns-mobilenode.tcl 中的 Node/MobileNode instproc add-interface 函数出现过。源码如下:

 

if { [Simulator set MacTrace_] == "ON" } {

        #

        # Trace RTS/CTS/ACK Packets

        #

        if {$imepflag != ""} {

            set rcvT [$self mobility-trace Recv "MAC"]

        } else {

            set rcvT [cmu-trace Recv "MAC" $self]

        }

        $mac log-target $rcvT

        if { $namfp != "" } {

            $rcvT namattach $namfp

        }

        #

        # Trace Sent Packets

        #

        if {$imepflag != ""} {

            set sndT [$self mobility-trace Send "MAC"]

        } else {

            set sndT [cmu-trace Send "MAC" $self]

        }

        $sndT target [$mac down-target]

        $mac down-target $sndT

        if { $namfp != "" } {

            $sndT namattach $namfp

        }

        #

        # Trace Received Packets

        #

        if {$imepflag != ""} {

            set rcvT [$self mobility-trace Recv "MAC"]

        } else {

            set rcvT [cmu-trace Recv "MAC" $self]

        }

        $rcvT target [$mac up-target]

        $mac up-target $rcvT

        if { $namfp != "" } {

            $rcvT namattach $namfp

        }

        #

        # Trace Dropped Packets

        #

        if {$imepflag != ""} {

            set drpT [$self mobility-trace Drop "MAC"]

        } else {

            set drpT [cmu-trace Drop "MAC" $self]

        }

        $mac drop-target $drpT

        if { $namfp != "" } {

            $drpT namattach $namfp

        }

    } else {

        $mac log-target [$ns set nullAgent_]

        $mac drop-target [$ns set nullAgent_]

    }

 

越来越接近问题所在,我们取出最开始的一段代码分析,

 

if {$imepflag != ""} {

            set rcvT [$self mobility-trace Recv "MAC"]

        } else {

            set rcvT [cmu-trace Recv "MAC" $self]

        }

$mac log-target $rcvT

 

先不管 imepflag 是干嘛的(实际上在 Simulator instproc create-wireless-node 的开头已经被屏蔽),

 

注意 set rcvT [$self mobility-trace Recv "MAC"] 函数,在ns/tcl/lib/ns-mobilenode.tcl 可以找到 Node/MobileNode instproc mobility-trace 的定义:

 

Node/MobileNode instproc mobility-trace { ttype atype } {

    set ns [Simulator instance]

        set tracefd [$ns get-ns-traceall]

        if { $tracefd == "" } {

            puts "Warning: You have not defined you tracefile yet!"

            puts "Please use trace-all command to define it."

        return ""

    }

    set T [new CMUTrace/$ttype $atype]

    $T newtrace [Simulator set WirelessNewTrace_]

    $T tagged [Simulator set TaggedTrace_]

    $T target [$ns nullagent]

    $T attach $tracefd

        $T set src_ [$self id]

        $T node $self

    return $T

}

 

这也是在解释层次创建一个新的 Trace 对象,并完成到 out.tr 的链接。最后一句 $mac log-target $rcvT 就是调用 command 将编译层次声明的指针与解释层次生成的对象连接。

 

链接完成如何使用?在类 class Mac 中有这样的定义:

 

private:

        void mac_log(Packet *p) {

                logtarget_->recv(p, (Handler*) 0);

        }

        NsObject*       logtarget_;

 

注意这里并没有将 logtarget_ 声明为 Trace*,但是查看追踪的基类 class Trace 你会发现 Trace 就是从 NsObject 继承来的,因此 logtarget_就是在 command 中被链接的指针。

而函数void mac_log(Packet *p){

               logtarget_->recv(p, (Handler*)0);

       }

就是调用 Trace 的 recv,继而调用 format 记录有效信息。

 

剩下的工作我们通篇搜索函数mac_log的出现,容易发现有3个地方,在ns-2.34/mac/mac-802_11.cc中,参考其应用即可。

 

最后有个细节需要注意,基类中的声明:

 

private:

        void mac_log(Packet *p) {

                logtarget_->recv(p, (Handler*) 0);

        }

        NsObject*       logtarget_;

 

是 private,如果你要实现自己的追踪,必须在你新建的类中重新声明这个变量和函数,并且在 command 中实现链接。

 

 

 

你可能感兴趣的:(trace,trace,认识,ns2,ns2)