NS3 日志
转:
NS-3日志子系统的提供了各种查看仿真结果的渠道:
一、使用Logging Module
0、【预备知识】日志级别及其对应的宏
NS-3提供了若干个日志级别来满足不同的Debug需求,每一级的日志内容都涵盖了低一级的内容。这些级别对应的宏从低到高排列为:
*NS_LOG_ERROR — Log error messages;
*NS_LOG_WARN — Log warning messages;
*NS_LOG_DEBUG — Log relatively rare, ad-hoc debugging messages;
*NS_LOG_INFO — Log informational messages about program progress;
*NS_LOG_FUNCTION — Log a message describing each function called;
*NS_LOG_LOGIC — Log messages describing logical flow within a function;
*NS_LOG_ALL — Log everything.
*NS_LOG_UNCOND — 无条件输出
方式1、通过设置shell环境变量NS_LOG使用日志系统
1.1)首先,定义好一个日志模块:
可以在脚本中使用宏NS_LOG_COMPONENT_DEFINE(name)定义一个日志模块。(注意,为了使用宏NS_LOG(name, level)来输出这个模块所定义的内容,这个定义语句必须写在每个脚本文件的开始。宏NS_LOG将在方式2中进行介绍。)
也有一些日志模块是内置的,比如上文的名为“UdpEchoClientApplication”“UdpEchoServerApplication”的模块就是UdpEcho应用程序内置的日志模块,只要使用了相应的类,就可以启用相应的日志模块。
1.2)在shell中通过设置环境变量NS_LOG,来控制仿真输出级别:
$~/ns-3.2.1 > export NS_LOG = '<日志模块名称> =level_all | prefix_func | prefix_time'
*level_all表示启用所有级别(=error | warn | debug | info | function | logic)
*prefix_func表示记录输出该消息的函数
*prefix_time表示加上时间前缀
$~/ns-3.2.1 > export NS_LOG = '<日志模块名称1>=level_all : <日志模块名称2>=info'
*符号:隔开两个不同的日志模块
$~/ns-3.2.1 > export NS_LOG = * = level_all
*符号*作为通配符。上行命令表示启用所有可用模块的所有日志级别。
*这一般会形成大量的数据,此时可以使用shell的输出重定向保存日志到文件里面:
$~/ns-3.2.1 > ./waf --run scratch/example >& log.out
方式2、通过在脚本里使用宏NS_LOG调用日志模块
2.0)宏NS_LOG(level, msg)用于定义对应level的输出内容;为了方便使用,系统预定义了各个级别的NS_LOG宏NS_LOG_ERROR等(参见【预备知识】):
#define NS_LOG_ERROR(msg) NS_LOG(ns3::LOG_ERROR, msg)
2.1)如上文,在脚本里使用宏NS_LOG_COMPONENT_DEFINE(name)定义一个日志模块;
2.2)使用宏LogComponentEnable(name, level)启用日志(对应地,有宏LogComponentDisable(name, level)用于禁用日志);
2.3)使用【预备知识】里定义的各种级别的宏输出内容,注意程序只会输出低于等于已经启用的level的宏内容。
NS_LOG_COMPONENT_DEFINE("Example");
LogComponentEnable("Example", LOG_LEVEL_INFO); //等价于shell中:export NS_LOG = 'Example=info'
//运行时显示程序中用语句NS_LOG_INFO(“字符串”)定义的字符串,便于检查错误。
NS_LOG_WARN("Message:level_warn");
NS_LOG_INFO("Message:level_info");
NS_LOG_LOGIC("Message:level_logic");
//由于我们启用的日志level是INFO,因此编译运行后,程序会输出低于和等于INFO级别的内容,而高于INFO级别的宏内容不会被输出
//即,Message:level_warn和Message:level_info会被输出,而Message:level_logic不会被输出
===============================================================================================================
二、使用Command Line参数
仿真一般是为了收集各种不同条件下的数据,常常需要改变一些变量。NS-3提供了Command Line参数接口,可以在运行时对脚本中的变量进行设置,免去了每次更改变量后要重新编译的麻烦。(相当于在运行前进行变量的scanf/cin操作,但因为有默认值,CLI更灵活一些。)
1、在脚本中添加语句
int main (int argc, char *argv[])
{
...
CommandLine cmd;
cmd.Parse (argc, argv); //将命令行输入的参数作为类CommandLine的参数进行分析
...
}
这样可以在shell中使用某些附加参数如PrintHelp:
$~/ns-3.2.1 > ./waf --run "scratch/example --PrintHelp"
这条命令将会列出example当前可用的命令参数:
Entering directory '/home/craigdo/repos/ns-3-dev/build'
Compilation finished successfully
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.
从输出中(倒数第二行)我们知道可以打印某些类的属性:
$~/ns-3.2.1 > ./waf --run "scratch/example --PrintAttributes=ns3::PointToPointNetDevice"
这条命令将会列出类型为PointToPointNetDevice的设备的属性:
--ns3::PointToPointNetDevice::DataRate=[32768bps]:
The default data rate for point to point links
知道了属性名称,我们也可以使用命令更改这个属性:
$~/ns-3.2.1 > ./waf --run "scratch/example --ns3::PointToPointNetDevice::DataRate=5Mbps"
2、使用CommandLine::AddValue添加自己的变量,使之成为CommandLine可以使用的参数
CommandLine cmd;
cmd.AddValue("nPackets", "Number of packets to echo", nPackets); //(属性名称,属性说明,变量)
cmd.Parse(argc, argv);
这样在shell中我们可以在命令中更改这个属性:
$~/ns-3.2.1 > ./waf --run "scratch/example --nPackets=2"
===============================================================================================================
三、使用Tracing System
1、启用ASCII Tracing
NS-3提供了类似NS-2的日志输出(*.tr文件),记录系统中的动作。在Simulator::Run()之前添加语句:
#include <fstream>
...
std::ofstream ascii;
ascii.open ("example.tr");
PointToPointHelper::EnableAsciiAll (ascii);
则运行后我们可以在example.tr文件中看到系统的日志(使用ASCII文本阅读器即可),其中每一行都是以+/-/d/r开头的:
+: An enqueue operation occurred on the device queue;
-: A dequeue operation occurred on the device queue;
d: A packet was dropped, typically because the queue was full;
r: A packet was received by the net device.
例如我们可以看到文件中的第一行(为了说明方便,这里分段编号显示),显示了一个入队操作:
00 +
01 2
02 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
03 ns3::PppHeader (
04 Point-to-Point Protocol: IP (0x0021))
05 ns3::Ipv4Header (
06 tos 0x0 ttl 64 id 0 offset 0 flags [none]
07 length: 1052 10.1.1.1 > 10.1.1.2)
08 ns3::UdpHeader (
09 length: 1032 49153 > 9)
10 Payload (size=1024)
其中编号为02的部分显示了发生操作的路径:根/NodeList是NS-3维护的所有节点列表,因此/NodeList/0表示编号为0的节点;随后的/DeviceList/0表示在该节点上的编号为0的NetDivece(比如网卡);接下来的$ns3::PointToPointNetDevice指明了该NetDivece的类型;最后的TxQueue/Enqueue表示在传送队列上发生了入队操作,也就是行开头的+所表现的意义。
2、启用PCAP Tracing
NS-3也可以生成*.pcap文件,从而可以使用诸如Wireshark、tcpdump(前文NS-3入门[1]概念引入介绍过)等工具进行分析。
2.1)在脚本Simulator::Run()之前添加语句:
PointToPointHelper::EnablePcapAll ("example");
这个语句将会产生若干*.pcap文件,命名为example-<Node编号>-<NetDevice编号>.pcap,分别记录每个设备的日志。也可以使用语句***Helper::EnablePcap (filename, NodeId, DeviceId)来只产生特定设备的pcap文件:
PointToPointHelper::EnablePcap ("example", p2pNodes.Get (0)->GetId (), 0); //只产生example-0-0.pcap文件
2.2)使用tcpdump在命令行阅读pcap文件:
~/ns-3.2.1 > tcpdump -r example-0-0.pcap -nn -tt
reading from file second-0-0.pcap, link-type PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.2.4.9: UDP, length 1024
2.007382 IP 10.1.2.4.9 > 10.1.1.1.49153: UDP, length 1024
2.3)使用Wireshark等软件打开pcap文件。
=======End===================================================================