对NS3 seventh.cc为例说明Probe< 一>长篇分析总结如下:
一 结构
要使用Gnuplot helper对象画图需要用到关键函数PlotProbe,其格式:
PlotProbe(probetype, external trace source's path,probe trace source,legend of data line in the figure,the location of key in the figure)
probetype在NS3中一共存在如下几类:
类型 路径 作用
• BooleanProbe src/stats/model
• DoubleProbe src/stats/model
• Uinteger8Probe src/stats/model connects to an ns-3 trace source exporting an uint8_t
• Uinteger16Probe src/stats/model connects to an ns-3 trace source exporting an uint16_t
• Uinteger32Probe src/stats/model connects to an ns-3 trace source exporting an uint32_t
• TimeProbe src/stats/model
• PacketProbe src/network/utils connects to an ns-3 trace source exporting a packet
• ApplicationPacketProbe src/applications/model connects to an ns-3 trace source exporting a packet and a socket address
• Ipv4PacketProbe src/internet/model connects to an ns-3 trace source exporting a packet, an IPv4 object, and an interface
它们都继承虚基类Probe,通过实现代码文件中的trace source相关联,实现对应Probe类数据的导出。例如seventh.cc里为Ipv4PacketProbe。
该类存在两个TraceSource,我们称之为内部Source:
m_output-------------Output,回调标签是Ipv4L3Protocol::TxRxTracedCallback
m_outputBytes-------------OutputBytes,回调标签是Packet::SizeTracedCallback
external trace source's path,即在仿真程序中需要真正检测统计观察的变量或者对象,我们称之为外部Source,这里是/NodeList/*/$ns3::Ipv4L3Protocol/Tx,通过API查询可知Ipv4L3Protocol对应的Trace Source多个,Tx就是其中之一:
TypeId
Ipv4L3Protocol::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::Ipv4L3Protocol")
.SetParent<Ipv4> ()
.SetGroupName ("Internet")
.AddConstructor<Ipv4L3Protocol> ()
.AddTraceSource ("Tx",
"Send ipv4 packet to outgoing interface.",
MakeTraceSourceAccessor (&Ipv4L3Protocol::m_txTrace),
"ns3::Ipv4L3Protocol::TxRxTracedCallback")
.AddTraceSource ("Rx",
"Receive ipv4 packet from incoming interface.",
MakeTraceSourceAccessor (&Ipv4L3Protocol::m_rxTrace),
"ns3::Ipv4L3Protocol::TxRxTracedCallback")
.AddTraceSource ("Drop",
"Drop ipv4 packet",
MakeTraceSourceAccessor (&Ipv4L3Protocol::m_dropTrace),
"ns3::Ipv4L3Protocol::DropTracedCallback")
;
return tid;
}
可见这个外部Source如下:
Ipv4L3Protocol::m_txTrace--------Tx,回调标签是Ipv4L3Protocol::TxRxTracedCallback
probe trace source是从Probe类选择一个Trace Source对于外部Source,如这里的OutputBytes
二 Probe
在了解完整的执行流程之前,先必须知道所用的probetype内容。其分类上述已经说明,这里先拿较为简单的DoubleProbe类进行举例说明,其常用属性有Name,Enabled等,例如必须要Enabled=1时该Probe才有效。
打开src/stats/examples/double-probe-example.cc
#include <string>
#include "ns3/core-module.h"
#include "ns3/double-probe.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("DoubleProbeExample");
/*
* This is our test object, an object that increments counters at
* various times and emits one of them as a trace source.
*/
class Emitter : public Object
{
public:
/**
* Register this type.
* \return The TypeId.
*/
static TypeId GetTypeId (void);
Emitter ();
private:
void DoInitialize (void);
void Emit (void);
void Count (void);
TracedValue<double> m_counter; // normally this would be integer type
Ptr<ExponentialRandomVariable> m_var;
};
NS_OBJECT_ENSURE_REGISTERED (Emitter);
TypeId
Emitter::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::Emitter")
.SetParent<Object> ()
.SetGroupName ("Stats")
.AddConstructor<Emitter> ()
.AddTraceSource ("Counter",
"sample counter",
MakeTraceSourceAccessor (&Emitter::m_counter),
"ns3::TracedValueCallback::Double")
;
return tid;
}
Emitter::Emitter (void)
{
NS_LOG_FUNCTION (this);
m_counter = 0;
m_var = CreateObject<ExponentialRandomVariable> ();
}
void
Emitter::DoInitialize (void)
{
NS_LOG_FUNCTION (this);
Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Emit, this);
Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this);
}
void
Emitter::Emit (void)
{
NS_LOG_FUNCTION (this);
NS_LOG_DEBUG ("Emitting at " << Simulator::Now ().GetSeconds ());
Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Emit, this);
}
void
Emitter::Count (void)
{
NS_LOG_FUNCTION (this);
NS_LOG_DEBUG ("Counting at " << Simulator::Now ().GetSeconds ());
m_counter += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this);
}
// This is a function to test hooking a raw function to the trace source
void
NotifyViaTraceSource (std::string context, double oldVal, double newVal)
{
NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal);
}
// This is a function to test hooking it to the probe output
void
NotifyViaProbe (std::string context, double oldVal, double newVal)
{
NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal);
}
int main (int argc, char *argv[])
{
CommandLine cmd;
cmd.Parse (argc, argv);
bool connected;
Ptr<Emitter> emitter = CreateObject<Emitter> ();
Names::Add ("/Names/Emitter", emitter);//自定义类路径加入到NS3 配置空间中
//
// The below shows typical functionality without a probe
// (connect a sink function to a trace source)
//
connected = emitter->TraceConnect ("Counter", "sample context", MakeCallback (&NotifyViaTraceSource));
NS_ASSERT_MSG (connected, "Trace source not connected");
//
// Next, we'll show several use cases of using a Probe to access and
// filter the values of the underlying trace source
//
//
// Probe1 will be hooked directly to the Emitter trace source object
//
// probe1 will be hooked to the Emitter trace source
Ptr<DoubleProbe> probe1 = CreateObject<DoubleProbe> ();
// the probe's name can serve as its context in the tracing
probe1->SetName ("ObjectProbe");
// Connect the probe to the emitter's Counter
connected = probe1->ConnectByObject ("Counter", emitter);
NS_ASSERT_MSG (connected, "Trace source not connected to probe1");
// The probe itself should generate output. The context that we provide
// to this probe (in this case, the probe name) will help to disambiguate
// the source of the trace
connected = probe1->TraceConnect ("Output", probe1->GetName (), MakeCallback (&NotifyViaProbe));
NS_ASSERT_MSG (connected, "Trace source not connected to probe1 Output");
//
// Probe2 will be hooked to the Emitter trace source object by
// accessing it by path name in the Config database
//
// Create another similar probe; this will hook up via a Config path
Ptr<DoubleProbe> probe2 = CreateObject<DoubleProbe> ();
probe2->SetName ("PathProbe");
// Note, no return value is checked here
probe2->ConnectByPath ("/Names/Emitter/Counter");
// The probe itself should generate output. The context that we provide
// to this probe (in this case, the probe name) will help to disambiguate
// the source of the trace
connected = probe2->TraceConnect ("Output", "/Names/Probes/PathProbe/Output", MakeCallback (&NotifyViaProbe));
NS_ASSERT_MSG (connected, "Trace source not connected to probe2 Output");
//
// Probe3 will be called by the emitter directly through the
// static method SetValueByPath().
//
Ptr<DoubleProbe> probe3 = CreateObject<DoubleProbe> ();
probe3->SetName ("StaticallyAccessedProbe");
// We must add it to the config database
Names::Add ("/Names/Probes", probe3->GetName (), probe3);
// The probe itself should generate output. The context that we provide
// to this probe (in this case, the probe name) will help to disambiguate
// the source of the trace
connected = probe3->TraceConnect ("Output", "/Names/Probes/StaticallyAccessedProbe/Output", MakeCallback (&NotifyViaProbe));
NS_ASSERT_MSG (connected, "Trace source not connected to probe3 Output");
// The Emitter object is not associated with an ns-3 node, so
// it won't get started automatically, so we need to do this ourselves
Simulator::Schedule (Seconds (0.0), &Emitter::Initialize, emitter);
Simulator::Stop (Seconds (100.0));
Simulator::Run ();
Simulator::Destroy ();
return 0;
}
首先自定义了一个类Emitter,类似上面的Ipv4L3Protocol其主要信息:
typeid是“ns3::Emitter”, VS ns3::Ipv4L3Protocol
Trace Source:m_counter-----Counter,标签ns3::TracedValueCallback::Double VS m_txTrace----Tx, 回调标签ns3::Ipv4L3Protocol::TxRxTracedCallback
然后定义了两个回调函数:
// This is a function to test hooking a raw function to the trace source
void
NotifyViaTraceSource (std::string context, double oldVal, double newVal)
{
NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal);
}
// This is a function to test hooking it to the probe output
void
NotifyViaProbe (std::string context, double oldVal, double newVal)
{
NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal);
}
在仿真程序主函数里定义了四种针对Trace source处理的方法:
a 无probe,常有的source-sink形式
connected = emitter->TraceConnect ("Counter", "sample context", MakeCallback (&NotifyViaTraceSource));
b 有probe,采用对象
// probe1 will be hooked to the Emitter trace source
Ptr<DoubleProbe> probe1 = CreateObject<DoubleProbe> ();
// the probe's name can serve as its context in the tracing
probe1->SetName ("ObjectProbe");
// Connect the probe to the emitter's Counter
connected = probe1->ConnectByObject ("Counter", emitter);
c 有probe,采用路径
// Create another similar probe; this will hook up via a Config path
Ptr<DoubleProbe> probe2 = CreateObject<DoubleProbe> ();
probe2->SetName ("PathProbe");
// Note, no return value is checked here
probe2->ConnectByPath ("/Names/Emitter/Counter");
// The probe itself should generate output. The context that we provide
// to this probe (in this case, the probe name) will help to disambiguate
// the source of the trace
connected = probe2->TraceConnect ("Output", "/Names/Probes/PathProbe/Output", MakeCallback (&NotifyViaProbe));
d 有probe,该probe也写进NS3配置空间
Ptr<DoubleProbe> probe3 = CreateObject<DoubleProbe> ();
probe3->SetName ("StaticallyAccessedProbe");
// We must add it to the config database
Names::Add ("/Names/Probes", probe3->GetName (), probe3);
// The probe itself should generate output. The context that we provide
// to this probe (in this case, the probe name) will help to disambiguate
// the source of the trace
connected = probe3->TraceConnect ("Output", "/Names/Probes/StaticallyAccessedProbe/Output", MakeCallback (&NotifyViaProbe));
由此可知,
probe不是简单的就是trace source,而是对trace source进行处理滤波。于此同时,可以知道,probe类最重要的两个成员函数是:ConnectByObject/ConnectByPath和最终的TraceConnect。有必要了解DoubleProbe,故gedit src/stats/model/double-probe.cc,可类比上面的Ipv4-Packet-Probe
DoubleProbe Ipv4-Packet-Probe
typeid DoubleProbe Ipv4PacketProbe
内部source Output------m_output m_output-------------Output,m_outputBytes-------------OutputBytes
内部source签名 ns3::TracedValueCallback::Double Ipv4L3Protocol::TxRxTracedCallback,Packet::SizeTracedCallback
ConnectByObject/ConnectByPath函数如下:
bool
DoubleProbe::ConnectByObject (std::string traceSource, Ptr<Object> obj)
{
NS_LOG_FUNCTION (this << traceSource << obj);
NS_LOG_DEBUG ("Name of trace source (if any) in names database: " << Names::FindPath (obj));
bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::DoubleProbe::TraceSink, this));
return connected;
}
void
DoubleProbe::ConnectByPath (std::string path)
{
NS_LOG_FUNCTION (this << path);
NS_LOG_DEBUG ("Name of trace source to search for in config database: " << path);
Config::ConnectWithoutContext (path, MakeCallback (&ns3::DoubleProbe::TraceSink, this));
}
可以发现这种函数主要是将外部属于对象obj属性的trace source与本probe建立关系,具体而言是将外部source作为probe内部一source,将内部TraceSink函数作为该外部source的sink函数。
void
DoubleProbe::TraceSink (double oldData, double newData)
{
NS_LOG_FUNCTION (this << oldData << newData);
if (IsEnabled ())
{
m_output = newData;
}
}
当外部source变化-->内部TraceSink函数回调-->sink对probe内部变量m_output赋值
故src/stats/examples/double-probe-example.cc 对应probe部分运行流程分析如下:
主程序中emitter对象的source Counter值变化->通过ConnectByObject函数,调用了probe内部函数TraceSink-->TraceSink完成对probe内部变量如m_output赋值--->m_output正好是probe内部source,通过调用TraceConnect完成与对应sink函数的关联-->调用对应sink函数NotifyViaProbe -->输出结果
三 执行流程
有了上面的分析,对于sevem.cc的probe分析就很简单了。
vim scratch/myseven.cc
分析函数PlotProbe(WriteProbe类似分析),打开对应的定义:
其主要实现是由函数ConnectProbeToAggregator实现:主要函数:
1 AddProbe 将外部source 路径path作为参数,内部通过ConnectByPath将这个外部source与Probe类内部sink相关联,Probe类是Ipv4PacketProbe类,其ConnectByPath定义可见src/internet/model/ipv4-packet-probe.cc,它将外部source与内部sink函数ns3::Ipv4PacketProbe::TraceSInk关联,TraceSInk是对类内数据成员赋值也包括内部source的赋值。故完成了以下功能:
外部source--->Ipv4PacketProbe内部sink函数---->内部source赋值
2 多条条件选择,选择对应自己的Probe,如这里是Ipv4PacketProbe,可以看到由TraceConnectWithoutContext实现,内部source即probeTraceSource,这里是OutputBytes,与最终的sink关联,这里sink函数选择的是TimeSeriesAdaptor::TraceSInkUinteger32完成对应时间下新数据的输出。故这里完成:
内部source赋值-->最终sink函数关联-->输出有外部source触发导致的内部source新值。
3 Adaptor(主要是TimeSeriesAdaptor)完成Probe与Aggregator直接相关,Aggregator是最终处理仿真数据并输出画图所需的文件如.txt .dat .plt .sh
可以看到sink函数选择的是TimeSeriesAdaptor::TraceSInkUinteger32,而该函数右主要是调用sink函数选择的是TimeSeriesAdaptor::TraceSInkDouble完成功能:
m_output (Simulator::Now ().GetSeconds (), newData);
查看time-series-adaptor可知m_output是TimeSeriesAdaptorl类成员函数,是一个函数签名:
TracedCallback<double,double> m_output
并且并规定为TimeSeriesAdaptorl一个Trace Source:
.AddTraceSource ( "Output",
"The current simulation time versus "
"the current value converted to a double",
MakeTraceSourceAccessor (&TimeSeriesAdaptor::m_output),
"ns3::TimeSeriesAdaptor::OutputTracedCallback")
最后通过TimeSeriesAdaptorl类成员m_timeSeriesAdaptorMap调用TraceConnect(adaptosource,MakeCallback(&GnuplotAggregator::Write2d,aggregator));
总结:
seventh.cc-->外部Source/NodeList/*/$ns3::Ipv4L3Protocol/Tx---->
1通过PlotProbe的ConnectProbeToAggregator的AddProbe完成该外部sourec与Probe内部sink函数f1关联
--->Probe内部sink函数f1调用导致Probe内部成员变量赋值,内部source也被赋值
2在通过TraceConnectWithoutContext完成Proeb内部source 与Adaptor内sink函数f2的关联-->Adaptor内的f2被调用导致Adaptor的source发生变化
3最终通过adptor的TraceConnect将Adaptor的source与Aggregator函数(sink函数f3)关联外部source1-->probe::sink f1--->probe::source2-->adaptor::sink f2--->adaptor::source3--->aggregator::sink f3