doc/tracing.h
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Tue, 06 Nov 2007 15:05:56 +0100
changeset 2081 434742b27b1e
parent 1416 2daa908b1b33
child 1807 46f2df7d4eae
permissions -rw-r--r--
use the new style support

/**
 * \defgroup TraceSourceList List of trace sources
 */

/**
 * \defgroup tracing Tracing
 *
 * The flexibility of the ns-3 tracing system comes at the cost of quite
 * a bit of complexity so, before trying to use the low-level aspects
 * of the tracing API, it is important to focus on some basic definitions:
 *
 * - A trace source is an object instance which can report trace events
 *   to a set of listening trace sinks.
 *
 * - A trace sink is a user-provided callback (a function) which can
 *   be connected to a set of trace sources to receive the events generated
 *   by each trace source.
 *
 * - A trace resolver is an object which allows users to establish 
 *   connections between a set of trace sources and a set of trace sinks.
 *
 * \section TraceSource Generating Trace Events
 *
 * So, what does it look like in practice ? First, let's look at trace
 * sources. We have two types of trace sources: numeric, and, normal 
 * trace sources. Numeric trace sources behave as normal c++ integers 
 * or c++ floating point numbers except that they report as trace events 
 * each change of their value. For example:
 * \code
 * class MyModel 
 * {
 * public:
 *   void DoSomething (void) 
 *   {
 *     // use the "int" trace source just 
 *     // like any other "int" variable.
 *     m_cwnd *= 2;
 *     m_cwnd += 4;
 *     if (m_cwnd > 100)
 *       {
 *         // do something.
 *       }
 *   }
 * private:
 *   // declare an instance of a "int" trace source
 *   SVTraceSource<int> m_cwnd;
 * };
 * \endcode
 * Normal trace sources, on the other hand, allow you to trace the
 * call of arbitrary functions and methods, as shown below. They are
 * typically used to track "rx", "tx", or "drop" events but could
 * also be used to track route change events, or position change
 * events:
 * \code
 * class MyModel 
 * {
 * public:
 *   void DoSomething (Packet packet) 
 *   {
 *     // report this event on packet
 *     m_doSomething (packet);
 *     // do something
 *   }
 * private:
 *   // report every "something" function call.
 *   CallbackTraceSource<Packet> m_doSomething;
 * };
 * \endcode
 * Every type of trace source derives from the ns3::TraceSource base class.
 * As of today, the set of concrete subclasses is relatively short:
 * ns3::CallbackTraceSource, ns3::SvTraceSource, ns3::UvTraceSource, and,
 * ns3::FvTraceSource.
 *
 * \section TraceSink Receiving Trace Events
 *
 * To receive these trace events, a user should specify a set of trace sinks.
 * For example, to receive the "int" and the "something" events shown in the
 * examples above, a user would declare the following functions:
 * \code
 * // oldValue and newValue contain the previous and new values of 
 * // the connected SVTraceSource<int> trace source.
 * void 
 * CwndTraceSink (const TraceContext &context, int64_t oldValue, int64_t newValue)
 * {
 *   // for example, print the new value:
 *   std::cout << "cwnd=" << newValue << std::endl;
 * }
 * void 
 * DoSomethingTraceSink (const TraceContext &context, Packet packet)
 * {
 *   // for example, print the arguments
 *   std::cout << "packet " << packet << std::endl;
 * }
 * \endcode
 * Each of these sink function takes, as a first argument, a reference to a 
 * const TraceContext object. This context object contains information which
 * describes the instance of the connected trace source: that information is
 * setup during the connection process and does not change afterwards
 * The type and the number of the other arguments to each trace sink depends
 * on the type of the connected trace source: it conveys per-event information
 * from the trace source to the trace sink. For example, UVTraceSource and 
 * SVTraceSource trace sources require two extra arguments. The former requires
 * two unsigned 64 bit integers while the latter requires two signed 64 bit 
 * integers. More generally, users can consult the \ref TraceSourceList
 * to figure out the arguments which a trace sink is required to receive
 * for each trace source: a signature of the user trace sink must match 
 * _exactly_ the signature documented in the \ref TraceSourceList.
 *
 *
 * \section TraceSourceSimpleExport A simple way to connect Trace Sources with Trace Sinks
 *
 * The crux of the complexity of the ns-3 tracing system comes from its 
 * flexible system used to connect trace sources to trace sinks but what is really
 * nice about it is that it is not necessary to use it to setup simple traces.
 * 
 * The simplest way to export a set of trace sources to a user, for example, 
 * during the early prototyping phases of a system, is to add a set of public methods
 * to give to your users access to the trace source object instances you use to generate
 * trace events:
 * \code
 * class MyModel 
 * {
 * public:
 *   void DoSomething (Packet packet) 
 *   {
 *     // report this event on packet
 *     m_doSomething (packet);
 *     // do something
 *   }
 *   CallbackTraceSource<Packet> *PeekSomethingTraceSource (void) const 
 *   {
 *     return &m_doSomething
 *   }
 * private:
 *   // report every "something" function call.
 *   CallbackTraceSource<Packet> m_doSomething;
 * };
 * \endcode
 * If your users hold a pointer to an instance of MyModel, and if they want to connect
 * a MySomethingSink, they can simply do the following which invokes the 
 * TraceSource::AddCallback method and creates a Callback object from the user's
 * sink with the MakeCallback function.
 * \code
 * void 
 * MySomethingSink (const TraceContext &context, Packet packet)
 * {
 *   // do whatever you want.
 * }
 * MyModel *model = ...;
 * CallbackTraceSource<Packet> *source = model->PeekSomethingTraceSource ();
 * source->AddCallback (MakeCallback (&MySomethingSink));
 * \endcode
 *
 * The full power of the tracing system comes however from its ns3::NodeList::Connect
 * method which is described in the following sections.
 *
 * \section TraceConnection Connecting Trace Sources to Trace Sinks
 * 
 * If a trace source is integrated in the ns-3 trace connection facility, a user 
 * should call the ns3::NodeList::Connect method to establish a connection between
 * a trace sink and a set of matching trace sources. The second argument to that
 * method is a callback to the user's trace sink.
 * That callback is easy to construct: call ns3::MakeCallback and you are done. The
 * first argument is a string whose format is similar to a unix path and which is 
 * used to uniquely identify the set of trace sources you want to connect to.
 * The set of acceptable path strings is also documented in the \ref TraceSourceList.
 *
 * So, what does this look like from the perspective of a user ? If we wanted to 
 * connect to a trace source defined somewhere deep into the a set of NetDevice objects
 * located in some nodes of the system, we could write the following:
 * \code
 * void 
 * DoSomethingTraceSink (const TraceContext &context, Packet packet)
 * {
 *   // for example, print the arguments
 *   std::cout << "packet: " << packet << std::endl;
 * }
 * // connect the above sink to a matching trace source
 * NodeList::Connect ("/nodes/* /devices/* /rx", MakeCallback &DoSomethingTraceSink);
 * \endcode
 *
 * The connection path string "/nodes/* /devices/* /rx" matches the "rx" trace source
 * located in every netdevice located in every node. The syntax of that path string
 * is loosely based on regular expressions so, a user could conceivably connect
 * to the trace sources present in only one node identified by node index:
 * "/nodex/3/devices/* /rx".
 *
 * The matching algorithm used here is very useful since it allows you to connect
 * at once a large set of trace sources to a single sink but it introduces another 
 * problem: it becomes impossible when you receive an event in your trace sink to
 * know from _which_ trace source the event is coming from. In our example, the
 * trace source might be coming from the NetDevice number 2 of Node 10 or Netdevice
 * number 0 of Node 5. In both cases, you might need to know which of these NetDevice
 * is generating this event, if only to generate some ascii trace dump. Another 
 * similar use-case is that you might have connected the same trace sink to
 * multiple types of events which have the same signature: it is quite common
 * to receive all tx, rx, and drop events in the same trace sink and that would be
 * quite trivial to achieve with a string such as: "/nodes/* /devices/* /*"
 *
 * The source of a trace event can be retrieved from a trace sink using 
 * different means: the simplest
 * way to get this information is to use the builtin printing facility of
 * the TraceContext object:
 * \code
 * void 
 * DoSomethingTraceSink (const TraceContext &context, Packet packet)
 * {
 *   // for example, print the arguments
 *   std::cout << "context=\"" << context << "\" packet: " << packet << std::endl;
 * }
 * \endcode
 * The above code is going to generate output which looks like the following:
 * \code
 * context="nodeid=2 device=0 dev-rx" packet: IPV4(tos 0x0 ttl 64 id 0 offset ...
 * context="nodeid=1 device=0 dev-rx" packet: IPV4(tos 0x0 ttl 64 id 0 offset ...
 * ...
 * \endcode
 *
 * Another more advanced way to get information out of a TraceContext is to call its
 * ns3::TraceContext::GetElement method. This method takes as its first and only
 * argument an instance of the object we want to read and the list of available
 * object instances we can read from a TraceContext is documented, once again,
 * in the \ref TraceSourceList. For example, we could write the following to
 * generate adhoc trace output:
 * \code
 * void DeviceRxSink (const TraceContext &context, const Packet &packet)
 * {
 *   NodeListIndex nodeIndex;
 *   NodeNetDeviceIndex deviceIndex;
 *   context.GetElement (nodeIndex);
 *   context.GetElement (deviceIndex);
 *   std::cout << "node-index=" << nodeIndex.Get ();
 *   std::cout << ", device-index=" << deviceIndex.Get ();
 *   std::cout << ", packet: " << packet;
 *   std::cout << std::endl;
 * }
 * \endcode
 *
 * \section ExportingTraceSources Exporting new Trace Sources
 *
 * Using existing trace sources to connect them to a set of adhoc trace sinks
 * is not really complicated but, setting up new trace sources which can hook
 * in this automatic connection system is a bit more complicated.
 *
 * So far, we know that a model author can generate trace events really easily:
 * \code
 * class MyModel 
 * {
 * public:
 *   void DoSomething (Packet packet) 
 *   {
 *     // report this event on packet with value
 *     m_doSomething (packet);
 *     // do something
 *   }
 * private:
 *   // report every "something" function call.
 *   CallbackTraceSource<Packet> m_doSomething;
 * };
 * \endcode
 *
 * To make these new trace sources available to the rest of the connection system,
 * the first step is to make sure that your model object derives from the ns3::Object
 * base class either directly (as shown below) or indirectly through another base class:
 * \code
 * class MyModel : public Object {...};
 * // or:
 * class SomeOtherObject : public Object {...};
 * class MyModel : public SomeOtherObject {...};
 * \endcode
 *
 * This is pretty trivial and lays the ground for the second step: overriding the
 * ns3::Object::GetTraceResolver method:
 * \code
 * class MyModel : public MyParent
 * {
 * public:
 *   // declare overriden method
 *   virtual Ptr<TraceResolver> GetTraceResolver (void) const;
 * private:
 *   // the new trace source to export.
 *   CallbackTraceSource<Packet> m_rxSource;
 * };
 * \endcode
 *
 * To implement this method, you could attempt to implement a new subclass of
 * the ns3::TraceResolver base class and return an instance from this method but
 * this would be very hard. Instead, you should use the helper class
 * ns3::CompositeTraceResolver to register your trace sources and chain up to
 * your parent:
 * \code
 * Ptr<TraceResolver>
 * MyModel::GetTraceResolver (void) const
 * {
 *   // create an empty trace resolver
 *   Ptr<CompositeTraceResolver> resolver = Create<CompositeTraceResolver> ();
 *   // register m_rxSource
 *   resolver->AddSource ("rx", // the name of the trace source in the path string
 *                        TraceDoc ("some help text to explain the purpose of this trace source",
 *                                  "Packet", // the type of the first argument to the trace source
 *                                  "the purpose of the first argument",
 *                                  "type-of-second-argument", "purpose-of-second-argument"),
 *                        m_rxSource // the trace source itself is registered
 *                       );
 *   // make sure we include the trace sources implemented in the parent.
 *   resolver->SetParentResolver (MyParent::GetTraceResolver ());
 *   return resolver;
 * }
 * \endcode
 *
 * Once you have written that code, you must make sure that this new method GetTraceResolver
 * is going to be called at some point by the tracing system. If your model is located somewhere
 * deep in MAC or PHY layer, that is, it is part of a NetDevice implementation, all you
 * have to do is to make sure that your model is registered as a "composite" of your NetDevice
 * subclass:
 * \code
 * class MyNetDevice : public NetDevice
 * {
 * public:
 *   Ptr<TraceResolver> GetTraceResolver (void) const;
 * private:
 *   Ptr<MyModel> m_model;
 * };
 * 
 * Ptr<TraceResolver>
 * MyNetDevice::GetTraceResolver (void) const
 * {
 *   Ptr<CompositeTraceResolver> resolver = ...;
 *   // register other trace source
 *   ...
 *   // register now your model as a "composite"
 *   resolver->AddComposite ("my-model", m_model);
 *   // chain up to parent.
 *   resolver->SetParentResolver (NetDevice::GetTraceResolver ());
 *   return resolver;
 * }
 * \endcode
 * 
 * The code above will make your "rx" trace source appear under the
 * /nodes/xx/devices/xx/my-model/rx namespace path.
 *
 * If you have implemented a new layer 3 or 4 protocol object, the process to
 * export your trace sources is quite similar. You need to subclass from
 * ns3::Object, override the ns3::Object::GetTraceResolver method, make
 * sure you chain up to your parent's GetTraceResolver method, and, finally,
 * make sure that someone calls your new GetTraceResolver method. How to accomplish
 * the latter should be documented in the node's API documentation which describes
 * how to implement a new layer 3 or 4 protocol object.
 *
 * \section AdvancedTraceContext Creating new Trace Context Elements
 *
 * The last important feature which model developers need to understand
 * is how to provide extra context information to trace sinks. For example,
 * if your model exports both rx and tx trace sources which share the same 
 * signature, it is quite natural for a user to connect to a single trace sink
 * to both of them with a trace path string such as "/nodes/* /devices/* /(rx|tx)".
 * In this case, it becomes necessary to be able, from the trace sink function,
 * to tell which event triggered the call to the trace sink: a rx or a tx event.
 *
 * That example is detailed below with a TX, a RX, and a DROP source:
 * \code
 * class MyModel
 * {
 * private:
 *   CallbackTraceSource<Packet> m_rxSource;
 *   CallbackTraceSource<Packet> m_txSource;
 *   CallbackTraceSource<Packet> m_dropSource;
 * };
 * \endcode
 * When a single sink is connected to all 3 sources here, one might want
 * to write code like the following:
 * \code
 * void DeviceRxSink (const TraceContext &context, const Packet &packet)
 * {
 *   switch (type) {
 *     case RX:
 *       std::cout << "rx" << std::endl;
 *       break;
 *     case TX:
 *       std::cout << "tx" << std::endl;
 *       break;
 *     case DROP:
 *       std::cout << "drop" << std::endl;
 *       break;
 *   }
 * \endcode
 *
 * \subsection AdvancedTraceContextSimpleSolution The simple solution
 *
 * The simplest way to do achieve the result shown above is to include
 * in the trace source an extra explicit argument which describes the source event:
 *   - define a small enum with 3 values
 *   - change the signature of m_rxSource, m_txSource, and m_dropSource to include
 *     the enum
 *   - pass the enum value in each event
 *
 * The resulting code is shown below:
 * \code
 * class MyModel
 * {
 * public:
 *   // define the trace type enum.
 *   enum TraceType {
 *     RX,
 *     TX,
 *     DROP
 *   };
 * private:
 *   // generate events
 *   void NotifyRxPacket (Packet p) {
 *     m_rxSource (p, MyModel::RX);
 *   }
 *   void NotifyTxPacket (Packet p) {
 *     m_rxSource (p, MyModel::TX);
 *   }
 *   void NotifyDropPacket (Packet p) {
 *     m_rxSource (p, MyModel::DROP);
 *   }
 *   CallbackTraceSource<Packet,enum TraceType> m_rxSource;
 *   CallbackTraceSource<Packet,enum TraceType> m_txSource;
 *   CallbackTraceSource<Packet,enum TraceType> m_dropSource;
 * };
 * \endcode
 * These 3 new sources can be connected easily to a new trace sink:
 * \code
 * void ASimpleTraceSink (const TraceContext &context, const Packet &packet, enum MyModel::TraceType type)
 * {
 *   // here, read the "type" argument
 * }
 * \endcode
 *
 * This solution works but it makes it impossible to connect a single trace sink to a set
 * of trace sources which represent "rx" events in different NetDevice objects since
 * each of them will define a different enum type with different values: since the
 * trace sink signature must match exactly the trace source signature, it is impossible
 * to connect at the same time to all "rx" events of different NetDevice.
 *
 * \subsection AdvancedTraceContextFancySolution The more complex and generic solution
 *
 * There is, hopefully, a way to get the best of both worlds, that is, to allow a
 * user to connect to a lot of trace source events of the same kind but coming from different
 * implementations and to allow the user to differentiate between these different
 * implementations.
 *
 * Rather than define an adhoc enum type with a list of trace sources, you can also
 * define a new ns3::TraceContextElement for your source sources. For example, if you
 * define a new MyModelTraceType class which contains the type of trace, your users can
 * then write trace sink code which looks like this:
 * \code
 * void AFancyTraceSink (const TraceContext &context, const Packet &packet)
 * {
 *   MyModelTraceType type;
 *   if (context.GetElement (type))
 *     {
 *       switch (type.Get ())
 *         {
 *         case MyModelTraceType::RX:
 *           std::cout << "rx" << std::endl;
 *           break;
 *         case MyModelTraceType::TX:
 *           std::cout << "tx" << std::endl;
 *           break;
 *         case MyModelTraceType::DROP:
 *           std::cout << "drop" << std::endl;
 *           break;
 *         }
 *     }
 * }
 * \endcode
 *
 * Of course, since the type of trace is stored in the TraceContext, your users can
 * also take the shortcut which uses the printing functionality of the TraceContext:
 * \code
 * void ALessFancyTraceSink (const TraceContext &context, const Packet &packet)
 * {
 *   std::cout << "context=\"" << context << "\" packet: " << packet << std::endl;
 * }
 * \endcode
 * which will generate something like the following when the trace source comes
 * from MyModel:
 * \code
 * context="my-model-rx" packet: ...
 * \endcode
 *
 * The first step to achieve this is to define and implement a new
 * subclass of the ns3::TraceContextElement base class. The exact list of
 * public methods which must be implemented is described in the API
 * documentation of the ns3::TraceContextElement class. 
 * \code
 * class MyModelTraceType : public TraceContextElement
 * {
 * public:
 *   enum Type {
 *     RX,
 *     TX,
 *     DROP
 *   };
 *   // called from MyModel::GetTraceResolver
 *   MyModelTraceType (enum Type type);
 *   // called from trace sink
 *   enum Type Get (void) const;
 *   // needed by the tracing subsystem
 *   static uint16_t GetUid (void);
 *   // needed by the tracing subsystem to
 *   // print the content of a TraceContext
 *   void Print (std::ostream &os) const;
 *   // needed by the tracing subsystem to
 *   // generate the doxygen documentation.
 *   std::string GetTypeName (void) const;
 * private:
 *   enum Type m_type;
 * };
 * \endcode
 * The implementation does not require much thinking:
 * \code
 * MyModelTraceType::MyModelTraceType (enum Type type)
 *  : m_type (type)
 * {}
 * enum Type 
 * MyModelTraceType::Get (void) const
 * {
 *   return m_type;
 * }
 * uint16_t 
 * MyModelTraceType::GetUid (void)
 * {
 *   // use protected TraceContextElement::AllocateUid method
 *   // the input string is used to uniquely identify this new subclass
 *   static uint16_t uid = AllocateUid<MyModelTraceType> ("ns3::MyModelTraceType");
 *   return uid;
 * }
 * void 
 * MyModelTraceType::Print (std::ostream &os) const
 * (
 *   // this method is invoked by the print function of a TraceContext
 *   // if it contains an instance of this TraceContextElement.
 *   switch (m_type) {
 *     case RX: os << "rx"; break;
 *     // ...
 *   }
 * )
 * std::string 
 * MyModelTraceType::GetTypeName (void) const
 * {
 *   // This method should return a fully-qualified c++ typename
 *   // This method is used only for documentation purposes to
 *   // generate the content of the Trace Source List.
 *   return "ns3::MyModelTraceType";
 * }
 * \endcode
 *
 * Once this subclass is implemented, the work is almost completed: you
 * just need to pass an instance of that class as the last argument of 
 * the ns3::CompositeTraceResolver::AddSource method as shown below:
 * \code
 * Ptr<TraceResolver>
 * MyModel::GetTraceResolver (void) const
 * {
 *   // create an empty trace resolver
 *   Ptr<CompositeTraceResolver> resolver = Create<CompositeTraceResolver> ();
 *   // register m_rxSource
 *   resolver->AddSource ("rx", // the name of the trace source in the path string
 *                        TraceDoc ("some help text to explain the purpose of this trace source",
 *                                  "Packet", // the type of the first argument to the trace source
 *                                  "the purpose of the first argument",
 *                                  "type-of-second-argument", "purpose-of-second-argument"),
 *                        m_rxSource, // the trace source itself is registered
 *                        // the TraceContextElement associated to this trace source.
 *                        MyModelTraceType (MyModelTraceType::RX) 
 *                       );
 *   // make sure we include the trace sources implemented in the parent.
 *   resolver->SetParentResolver (MyParent::GetTraceResolver ());
 *   return resolver;
 * }
 * \endcode
 */