src/common/trace-root.h
changeset 345 47b41507a45a
child 393 18ed386bee75
equal deleted inserted replaced
344:b547ec7dbbc1 345:47b41507a45a
       
     1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
       
     2 /*
       
     3  * Copyright (c) 2007 INRIA
       
     4  * All rights reserved.
       
     5  *
       
     6  * This program is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License version 2 as
       
     8  * published by the Free Software Foundation;
       
     9  *
       
    10  * This program is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13  * GNU General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License
       
    16  * along with this program; if not, write to the Free Software
       
    17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    18  *
       
    19  * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
       
    20  */
       
    21 #ifndef TRACE_ROOT_H
       
    22 #define TRACE_ROOT_H
       
    23 
       
    24 #include <string>
       
    25 #include "ns3/callback.h"
       
    26 
       
    27 /**
       
    28  * \defgroup tracing Tracing
       
    29  *
       
    30  * The low-level tracing framework is built around a few very simple
       
    31  * concepts:
       
    32  *   - There can be any number of trace source objects. Each trace source
       
    33  *     object can generate any number of trace events. The current
       
    34  *     trace source objects are: ns3::CallbackTraceSourceSource, ns3::UVTraceSource,
       
    35  *     ns3::SVTraceSource, and, ns3::FVTraceSource.
       
    36  *   - Each trace source can be connected to any number of trace sinks.
       
    37  *     A trace sink is a ns3::Callback with a very special signature. Its
       
    38  *     first argument is always a ns3::TraceContext.
       
    39  *   - Every trace source is uniquely identified by a ns3::TraceContext. Every
       
    40  *     trace sink can query a ns3::TraceContext for information. This allows
       
    41  *     a trace sink which is connected to multiple trace sources to identify
       
    42  *     from which source each event is coming from.
       
    43  *
       
    44  * To allow the user to connect his own trace sinks to each trace source
       
    45  * defined by any of the models he is using, the tracing framework defines
       
    46  * a hierarchical namespace. The root of this namespace is accessed through
       
    47  * the ns3::TraceRoot class. The namespace is represented as a string made
       
    48  * of multiple elements, each of which is separated from the other elements
       
    49  * by the '/' character. A namespace string always starts with a '/'.
       
    50  *
       
    51  * By default, the simulation models provide a '/nodes' tracing root. This
       
    52  * '/nodes' namespace is structured as follows:
       
    53  * \code
       
    54  *  /nodes/n/udp
       
    55  *  /nodes/n/ipv4
       
    56  *               /tx
       
    57  *               /rx
       
    58  *               /drop
       
    59  *               /interfaces/n/netdevice
       
    60  *                 (NetDevice only)    /queue/
       
    61  *                                           /enque
       
    62  *                                           /deque
       
    63  *                                           /drop
       
    64  *  /nodes/n/arp
       
    65  * \endcode
       
    66  *
       
    67  * The 'n' element which follows the /nodes and /interfaces namespace elements
       
    68  * identify a specific node and interface through their index within the 
       
    69  * ns3::NodeList and ns3::Ipv4 objects respectively.
       
    70  *
       
    71  * To connect a trace sink to a trace source identified by a namespace string,
       
    72  * a user can call the ns3::TraceRoot::Connect method (the ns3::TraceRoot::Disconnect
       
    73  * method does the symmetric operation). This connection method can accept
       
    74  * fully-detailed namespace strings but it can also perform pattern matching
       
    75  * on the user-provided namespace strings to connect multiple trace sources
       
    76  * to a single trace sink in a single connection operation.
       
    77  *
       
    78  * The syntax of the pattern matching rules are loosely based on regular 
       
    79  * expressions:
       
    80  *   - the '*' character matches every element
       
    81  *   - the (a|b) construct matches element 'a' or 'b'
       
    82  *   - the [ss-ee] construct matches all numerical values which belong
       
    83  *     to the interval which includes ss and ee
       
    84  *
       
    85  * For example, the user could use the following to connect a single sink
       
    86  * to the ipv4 tx, rx, and drop trace events:
       
    87  *
       
    88  * \code
       
    89  * void MyTraceSink (TraceContext const &context, Packet &packet);
       
    90  * TraceRoot::Connect ("/nodes/ * /ipv4/ *", MakeCallback (&MyTraceSink));
       
    91  * \endcode
       
    92  *
       
    93  * Of course, this code would work only if the signature of the trace sink
       
    94  * is exactly equal to the signature of all the trace sources which match
       
    95  * the namespace string (if one of the matching trace source does not match
       
    96  * exactly, a fatal error will be triggered at runtime during the connection
       
    97  * process). The ns3::TraceContext extra argument contains
       
    98  * information on where the trace source is located in the namespace tree.
       
    99  * In that example, if there are multiple nodes in this scenario, each
       
   100  * call to the MyTraceSink function would receive a different TraceContext,
       
   101  * each of which would contain a different NodeList::Index object.
       
   102  *
       
   103  * It is important to understand exactly what an ns3::TraceContext
       
   104  * is. It is a container for a number of type instances. Each instance of
       
   105  * a ns3::TraceContext contains one and only one instance of a given type.
       
   106  * ns3::TraceContext::Add can be called to add a type instance into a 
       
   107  * TraceContext instance and ns3::TraceContext::Get can be called to get
       
   108  * a copy of a type instance stored into the ns3::TraceContext. If ::Get
       
   109  * cannot retrieve the requested type, a fatal error is triggered at
       
   110  * runtime. The values stored into an ns3::TraceContext attached to a 
       
   111  * trace source are automatically determined during the namespace
       
   112  * resolution process. To retrieve a value from a ns3::TraceContext, the
       
   113  * code can be as simple as this:
       
   114  * \code
       
   115  * void MyTraceSink (TraceContext const &context, Packet &packet)
       
   116  * {
       
   117  *   NodeList::Index index;
       
   118  *   context.Get (index);
       
   119  *   std::cout << "node id=" << NodeList::GetNode (index)->GetId () << std::endl;
       
   120  * }
       
   121  * \endcode
       
   122  *
       
   123  * To define new trace sources, a model author needs to instante one trace source
       
   124  * object for each kind of tracing event he wants to export. The trace source objects
       
   125  * currently defined are:
       
   126  *  - ns3::CallbackTraceSourceSource: this trace source can be used to convey any kind of 
       
   127  *    trace event to the user. It is a functor, that is, it is a variable
       
   128  *    which behaves like a function which will forward every event to every
       
   129  *    connected trace sink (i.e., ns3::Callback). This trace source takes
       
   130  *    up to four arguments and forwards these 4 arguments together with the
       
   131  *    ns3::TraceContext which identifies this trace source to the connected
       
   132  *    trace sinks.
       
   133  *  - ns3::UVTraceSource: this trace source is used to convey key state variable
       
   134  *    changes to the user. It behaves like a normal integer unsigned variable:
       
   135  *    you can apply every normal arithmetic operator to it. It will forward
       
   136  *    every change in the value of the variable back to every connected trace 
       
   137  *    sink by providing a TraceContext, the old value and the new value.
       
   138  *  - ns3::SVTraceSource: this is the signed integer equivalent of 
       
   139  *    ns3::UVTraceSource.
       
   140  *  - ns3::FVTraceSource: this is the floating point equivalent of 
       
   141  *    ns3::UVTraceSource and ns3::SVTraceSource.
       
   142  *
       
   143  * Once the model author has instantiated these objects and has wired them
       
   144  * in his simulation code (that is, he calls them wherever he wants to
       
   145  * trigger a trace event), he needs to hook these trace sources into the
       
   146  * global tracing namespace. The first step to do this is to define a method
       
   147  * which returns a pointer to a ns3::TraceResolver object and which takes
       
   148  * as argument a reference to a const ns3::TraceContext. The name of this method
       
   149  * depends on how you will hook into the global tracing namespace. Before
       
   150  * we get there, you need to implement this method. To do this, you could
       
   151  * attempt to do everything by hand: define a subclass of the 
       
   152  * ns3::TraceResolver base class and implement its DoConnect, DoDisconnect
       
   153  * and DoLookup methods. Because doing this can be a bit tedious, our
       
   154  * tracing framework provides a number of helper template classes which
       
   155  * should save you from having to implement your own in most cases:
       
   156  *   - ns3::CompositeTraceResolver: this subclass of ns3::TraceResolver
       
   157  *     can be used to aggregate together multiple trace sources and
       
   158  *     multiple other ns3::TraceResolver instances.
       
   159  *   - ns3::ArrayTraceResolver: this subclass of ns3::TraceResolver
       
   160  *     can be used to match any number of elements within an array
       
   161  *     where every element is identified by its index.
       
   162  *
       
   163  * Once you can instantiate your own ns3::TraceResolver object instance,
       
   164  * you have to hook it up into the global namespace. There are two ways 
       
   165  * to do this:
       
   166  *   - you can hook your ns3::TraceResolver creation method as a new trace 
       
   167  *     root by using the ns3::TraceRoot::Register method
       
   168  *   - you can hook your new ns3::TraceResolver creation method into 
       
   169  *     the container of your model.
       
   170  *     For example, if you wrote a new l3 protocol, all you have to do
       
   171  *     to hook into your container L3Demux class is to implement
       
   172  *     the pure virtual method inherited from the L3Protocol class
       
   173  *     whose name is ns3::L3protocol::CreateTraceResolver.
       
   174  *
       
   175  * If you really want to have fun and implement your own ns3::TraceResolver 
       
   176  * subclass, you need to understand the basic Connection and Disconnection
       
   177  * algorithm. The code of that algorithm is wholy contained in the
       
   178  * ns3::TraceResolver::Connect and ns3::TraceResolver::Disconnect methods.
       
   179  * The idea is that we recursively parse the input namespace string by removing
       
   180  * the first namespace element. This element is 'resolved' is calling
       
   181  * the ns3::TraceResolver::DoLookup method which returns a list of
       
   182  * TraceResolver instances. Each of the returned TraceResolver instance is
       
   183  * then given what is left of the namespace by calling ns3::TraceResolver::Connect
       
   184  * until the last namespace element is processed. At this point, we invoke
       
   185  * the ns3::TraceResolver::DoConnect or ns3::TraceResolver::DoDisconnect 
       
   186  * methods to break the recursion. A good way to understand this algorithm
       
   187  * is to trace its behavior. Let's say that you want to connect to
       
   188  * '/nodes/ * /ipv4/interfaces/ * /netdevice/queue/ *'. It would generate
       
   189  * the following call traces:
       
   190  *
       
   191  * \code
       
   192  * TraceRoot::Connect (/nodes/ * /ipv4/interfaces/ * /netdevice/queue/ *);
       
   193  * resolver = NodeList::CreateTraceResolver ();
       
   194  * resolver->Connect (/nodes/ * /ipv4/interfaces/ * /netdevice/queue/ *);
       
   195  * list = CompositeTraceResolver::DoLookup ('nodes');
       
   196  * resolver->Connect (/ * /ipv4/interfaces/ * /netdevice/queue/ *);
       
   197  * list = ArrayTraceResolver::DoLookup ('*');
       
   198  * resolver->Connect ('/ipv4/interfaces/ * /netdevice/queue/ *');
       
   199  * list = CompositeTraceResolver::DoLookup ('ipv4');
       
   200  * resolver->Connect ('/interfaces/ * /netdevice/queue/ *');
       
   201  * list = CompositeTraceResolver::DoLookup ('interfaces');
       
   202  * resolver->Connect ('/ * /netdevice/queue/ *');
       
   203  * list = ArrayTraceResolver::DoLookup ('*');
       
   204  * resolver->Connect ('/netdevice/queue/ *');
       
   205  * list = CompositeTraceResolver::DoLookup ('netdevice');
       
   206  * resolver->Connect ('/queue/ *');
       
   207  * list = CompositeTraceResolver::DoLookup ('queue');
       
   208  * resolver->Connect ('/ *');
       
   209  * list = CompositeTraceResolver::DoLookup ('*');
       
   210  * resolver->DoConnect ();
       
   211  * \endcode
       
   212  *
       
   213  * This namespace resolution algorithm makes sure that each subpart of the
       
   214  * namespace is resolved separately by each component. It allows you to
       
   215  * never have to know the entire namespace structure to resolve a namespace
       
   216  * string. All namespace knowledge is local which makes it very easy to plug
       
   217  * in new components and have them extend the global tracing namespace.
       
   218  *
       
   219  * What is central to this namespace parsing and resolution algorithm is the
       
   220  * construction of an ns3::TraceContext for each trace source during the 
       
   221  * connection process. The root trace context is intialized to be empty and
       
   222  * TraceResolver::DoLookup method is responsible for incrementally constructing
       
   223  * the TraceContext assigned to each terminal TraceSource object.
       
   224  */
       
   225 
       
   226 namespace ns3 {
       
   227 
       
   228 class CompositeTraceResolver;
       
   229 class TraceResolver;
       
   230 class TraceContext;
       
   231 class CallbackBase;
       
   232 
       
   233 /**
       
   234  * \brief The main class used to access tracing functionality for
       
   235  * a user.
       
   236  *
       
   237  * \ingroup tracing
       
   238  */
       
   239 class TraceRoot
       
   240 {
       
   241 public:
       
   242   static void Connect (std::string path, CallbackBase const &cb);
       
   243   static void Disconnect (std::string path, CallbackBase const &cb);
       
   244   static void Register (std::string name, 
       
   245                         Callback<TraceResolver *,TraceContext const &> createResolver);
       
   246 private:
       
   247   static CompositeTraceResolver *GetComposite (void);
       
   248   enum TraceType {
       
   249     NOTHING,
       
   250   };
       
   251 };
       
   252 
       
   253 }// namespace ns3
       
   254 
       
   255 #endif /* TRACE_ROOT_H */