diff --git a/doc/main.h b/doc/main.h index ce9d233e0..260438f8c 100644 --- a/doc/main.h +++ b/doc/main.h @@ -27,9 +27,7 @@ * ns-3 requires Doxygen version 1.5.4 or greater to fully build all items, * although earlier versions of Doxygen will mostly work. * - * Type "./waf --check" followed by "./waf --doxygen" to build the documentation. - * There is a program that runs during "./waf --check" that builds pieces of - * the documentation through introspection. The doc/ directory contains + * Type "./waf doxygen" to build the documentation. The doc/ directory contains * configuration for Doxygen (doxygen.conf and main.txt). The Doxygen * build process puts html files into the doc/html/ directory, and latex * filex into the doc/latex/ directory. diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi new file mode 100644 index 000000000..210794066 --- /dev/null +++ b/doc/tutorial/tracing.texi @@ -0,0 +1,1105 @@ + +@c ============================================================================ +@c Begin document body here +@c ============================================================================ + +@c ============================================================================ +@c PART: The Tracing System +@c ============================================================================ +@c The below chapters are under the major heading "The Tracing System" +@c This is similar to the Latex \part command +@c +@c ============================================================================ +@c The Tracing System +@c ============================================================================ +@node The Tracing System +@chapter The Tracing System + +@menu +* Background:: +@end menu + +@c ============================================================================ +@c Background +@c ============================================================================ +@node Background +@section Background + +As mentioned in the Using the Tracing System section, the whole point of running +an @code{ns-3} simulation is to generate output for study. You have two basic +strategies to work with in @code{ns-3}: using generic pre-defined bulk output +mechanisms and parsing their content to extract interesting information; or +somehow developing an output mechanism that conveys exactly (and perhaps only) +the information wanted. + +Using pre-defined bulk output mechansims has the advantage of not requiring any +changes to @code{ns-3}, but it does require programming. Often, pcap or NS_LOG +output messages are gathered during simulation runs and separately run through +scripts that use grep, sed or awk to parse the messages and reduce and transform +the data to a manageable form. Programs must be written to do the +transformation, so this does not come for free. Of course, if the information +of interest in does not exist in any of the pre-defined output mechanisms, +this approach fails. + +If you need to add some tidbit of information to the pre-defined bulk mechanisms, +this can certainly be done; and if you use one of the @code{ns-3} mechanisms, +you may get your code added as a contribution. + +@code{ns-3} provides another mechanism, called Tracing, that avoids some of the +problems inherent in the bulk output mechanisms. It has several important +advantages. First, you can reduce the amount of data you have to manage by only +tracing the events of interest to you. Second, if you use this method, you can +control the format of the output directly so you avoid the postprocessing step +with sed or awk script. If you desire, your output can be formatted directly into +a form acceptable by gnuplot, for example. You can add hooks in the core which +can then be accessed by other users, but which will produce no information unless +explicitly asked to do so. For these reasons, we believe that the @code{ns-3} +Tracing system is the best way to get information out of a simulation and is also +therefore one of the most important mechanisms to understand in @command{ns-3}. + +@subsection Blunt Instruments +There are many ways to get information out of a program. The most +straightforward way is to just directly print the information to the standard +output, as in, + +@verbatim + #include + ... + void + SomeFunction (void) + { + uint32_t x = SOME_INTERESTING_VALUE; + ... + std::cout << "The value of x is " << x << std::endl; + ... + } +@end verbatim + +Nobody is going to prevent you from going deep into the core of @code{ns-3} and +adding print statements. This is insanely easy to do and, after all, you have +complete control of your own @code{ns-3} branch. This will probably not turn +out to be very satisfactory in the long term, though. + +As the number of print statements increases in your programs, the task of +dealing with the large number of outputs will become more and more complicated. +Eventually, you may feel the need to control what information is being printed +in some way; perhaps by turning on and off certain categories of prints, or +increasing or decreasing the amount of information you want. If you continue +down this path you may discover that you have re-implemented the @code{NS_LOG} +mechanism. In order to avoid that, one of the first things you might consider +is using @code{NS_LOG} itself. + +We mentioned above that one way to get information out of @code{ns-3} is to +parse existing NS_LOG output for interesting information. If you discover that +some tidbit of information you need is not present in existing log output, you +could edit the core of @code{ns-3} and simply add your interesting information +to the output stream. Now, this is certainly better than adding your own +print statements since it follows @code{ns-3} coding conventions and could +potentially be useful to other people as a patch to the existing core. + +Let's pick a random example. If you wanted to add more logging to the +@code{ns-3} TCP socket (@code{tcp-socket-impl.cc}) you could just add a new +message down in the implementation. Notice that in TcpSocketImpl::ProcessAction() +there is no log message for the @code{ACK_TX} case. You could simply add one, +changing the code from: + +@verbatim + bool TcpSocketImpl::ProcessAction (Actions_t a) + { // These actions do not require a packet or any TCP Headers + NS_LOG_FUNCTION (this << a); + switch (a) + { + case NO_ACT: + NS_LOG_LOGIC ("TcpSocketImpl " << this <<" Action: NO_ACT"); + break; + case ACK_TX: + SendEmptyPacket (TcpHeader::ACK); + break; + ... +@end verbatim + +to add a new @code{NS_LOG_LOGIC} in the appropriate @code{case} statement: + +@verbatim + bool TcpSocketImpl::ProcessAction (Actions_t a) + { // These actions do not require a packet or any TCP Headers + NS_LOG_FUNCTION (this << a); + switch (a) + { + case NO_ACT: + NS_LOG_LOGIC ("TcpSocketImpl " << this << " Action: NO_ACT"); + break; + case ACK_TX: + NS_LOG_LOGIC ("TcpSocketImpl " << this << " Action: ACK_TX"); + SendEmptyPacket (TcpHeader::ACK); + break; + ... +@end verbatim + +This may seem fairly simple and satisfying at first glance, but something to +consider is that you will be writing code to add the @code{NS_LOG} statement +and you will also have to write code (as in grep, sed or awk scripts) to parse +the log output in order to isolate your information. This is because even +though you have some control over what is output by the logging system, you +only have control down to the log component level. + +If you are adding code to an existing module, you will also have to live with the +output that every other developer has found interesting. You may find that in +order to get the small amount of information you need, you may have to wade +through huge amounts of extraneous messages that are of no interest to you. You +may be forced to save huge log files to disk and process them down to a few lines +whenever you want to do anything. + +Since there are no guarantees in @code{ns-3} about the stability of @code{NS_LOG} +messages, you may also discover that pieces of log output on which you depend +disappear or change between releases. If you depend on the structure of the +output, you may find other messages being added or deleted which may affect your +parsing code. + +For these reasons, we consider prints to @code{std::cout} and NS_LOG messages +simple ways to get more information out of @code{ns-3}, but they are really +unstable and quite blunt instruments. + +It is desirable to have a stable facility using stable APIs that allow one to +reach into the core system and only get the information required. It is +desirable to be able to do this without having to change and recompile the +core system. Even better would be a system that notified the user when an item +of interest changed or an interesting event happened so the user doesn't have +to actively go poke around in the system looking for things. + +The @command{ns-3} tracing system is designed to work along those lines and is +well-integrated with the Attribute and Config subsystems allowing for relatively +simple use scenarios. + +@node Overview +@section Overview + +The ns-3 tracing system is built on the concepts of independent tracing sources +and tracing sinks; along with a uniform mechanism for connecting sources to sinks. + +Trace sources are entities that can signal events that happen in a simulation and +provide access to interesting underlying data. For example, a trace source could +indicate when a packet is received by a net device and provide access to the +packet contents for interested trace sinks. A trace source might also indicate +when an iteresting state change happens in a model. For example, the congestion +window of a TCP model is a prime candidate for a trace source. + +Trace sources are not useful by themselves; they must be connected to other pieces +of code that actually do something useful with the information provided by the source. +The entities that consume trace information are called trace sinks. Trace sources +are generators of events and trace sinks are consumers. This explicit division +allows for large numbers of trace sources to be scattered around the system in +places which model authors believe might be useful. + +There can be zero or more consumers of trace events generated by a trace source. +One can think of a trace source as a kind of point-to-multipoint information link. +Your code looking for trace events from a particular piece of core code could +happily coexist with other code doing something entirely different from the same +information. + +Unless a user connects a trace sink to one of these sources, nothing is output. By +using the tracing system, both you and other people at the same trace source are +getting exactly what they want and only what they want out of the system. Neither +of you are impacting any other user by changing what information is output by the +system. If you happen to add a trace source, your work as a good open-source +citizen may allow other users to provide new utilities that are perhaps very useful +overall, without making any changes to the @code{ns-3} core. + +@node A Simple Low-Level Example +@subsection A Simple Low-Level Example + +Let's take a few minutes and walk through a simple tracing example. We are going +to need a little background on Callbacks to understand what is happening in the +example, so we have to take a small detour right away. + +@node Callbacks +@subsubsection Callbacks + +The goal of the Callback system in @code{ns-3} is to allow one piece of code to +call a function (or method in C++) without any specific inter-module dependency. +This ultimately means you need some kind of indirection -- you treat the address +of the called function as a variable. This variable is called a pointer-to-function +variable. The relationship between function and pointer-to-function pointer is +really no different that that of object and pointer-to-object. + +In C the canonical example of a pointer-to-function is a +pointer-to-function-returning-integer (PFI). For a PFI taking one int parameter, +this could be declared like, + +@verbatim + int (*pfi)(int arg) = 0; +@end verbatim + +What you get from this is a variable named simply ``pfi'' that is initialized +to the value 0. If you want to initialize this pointer to something meaningful, +you have to have a function with a matching signature. In this case, you could +provide a function that looks like, + +@verbatim + int MyFunction (int arg) {} +@end verbatim + +If you have this target, you can initialize the variable to point to your +function: + +@verbatim + pfi = MyFunction; +@end verbatim + +You can then call MyFunction indirectly using the more suggestive form of +the call, + +@verbatim + int result = (*pfi) (1234); +@end verbatim + +This is suggestive since it looks like you are dereferencing the function +pointer just like you would dereference any pointer. Typically, however, +people take advantage of the fact that the compiler knows what is going on +and will just use a shorter form, + +@verbatim + int result = pfi (1234); +@end verbatim + +This looks like you are calling a function named ``pfi,'' but the compiler is +smart enough to know to call through the variable @code{pfi} indirectly to +the function @code{MyFunction}. + +Conceptually, this is almost exactly how the tracing system will work. +Basically, a trace source @emph{is} a callback. When a trace sink expresses +interest in receiving trace events, it adds a Callback to a list of Callbacks +internally held by the trace source. When an interesting event happens, the +trace source invokes its @code{operator()} providing zero or more parameters. +The @code{operator()} eventually wanders down into the system and does something +remarkably like the indirect call you just saw. It provides zero or more +parameters (the call to ``pfi'' above passed one parameter to the target function +@code{MyFunction}. + +The important difference that the tracing system adds is that for each trace +source there is an internal list of Callbacks. Instead of just making one +indirect call, a trace source may invoke any number of Callbacks. When a trace +sink expresses interest in notifications from a trace source, it basically just +arranges to add its own function to the callback list. + +If you are interested in more details about how this is actually arranged in +@code{ns-3}, feel free to peruse the Callback section of the manual. + +@node Example Code +@subsubsection Example Code + +We have provided some code to implement what is really the simplest example +of tracing that can be assembled. You can find this code in the tutorial +directory as @code{fourth.cc}. Let's walk through it. + +@verbatim + /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ + /* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include "ns3/object.h" + #include "ns3/uinteger.h" + #include "ns3/traced-value.h" + #include "ns3/trace-source-accessor.h" + + #include + + using namespace ns3; +@end verbatim + +Most of this code should be quite familiar to you. As mentioned above, the +trace system makes heavy use of the Object and Attribute systems, so you will +need to include them. The first two includes above bring in the declarations +for those systems explicitly. You could use the core module header, but this +illustrates how simple this all really is. + +The file, @code{traced-value.h} brings in the required declarations for tracing +of data that obeys value semantics. In general, value semantics just means that +you can pass the object around, not an address. In order to use value semantics +at all you have to have an object with an associated copy constructor and +assignment operator available. We extend the requirements to talk about the set +of operators that are pre-defined for plain-old-data (POD) types. Operator=, +operator++, operator---, operator+, operator==, etc. + +What this all really means is that you will be able to trace changes to a C++ +object made using those operators. + +Since the tracing system is integrated with Attributes, and Attributes work +with Objects, there must be an @command{ns-3} @code{Object} for the trace source +to live in. The next code snippet declares and defines a simple Object we can +work with. + +@verbatim + class MyObject : public Object + { + public: + static TypeId GetTypeId (void) + { + static TypeId tid = TypeId ("MyObject") + .SetParent (Object::GetTypeId ()) + .AddConstructor () + .AddTraceSource ("MyInteger", + "An integer value to trace.", + MakeTraceSourceAccessor (&MyObject::m_myInt)) + ; + return tid; + } + + MyObject () {} + TracedValue m_myInt; + }; +@end verbatim + +The two important lines of code, above, with respect to tracing are the +@code{.AddTraceSource} and the @code{TracedValue} declaration of @code{m_myInt}. + +The @code{.AddTraceSource} provides the ``hooks'' used for connecting the trace +source to the outside world through the config system. The @code{TracedValue} +declaration provides the infrastructure that overloads the operators mentioned +above and drives the callback process. + +@verbatim + void + IntTrace (int32_t oldValue, int32_t newValue) + { + std::cout << "Traced " << oldValue << " to " << newValue << std::endl; + } +@end verbatim + +This is the definition of the trace sink. It corresponds directly to a callback +function. Once it is connected, this function will be called whenever one of the +overloaded operators of the @code{TracedValue} is executed. + +We have now seen the trace source and the trace sink. What remains is code to +connect the source to the sink. + +@verbatim + int + main (int argc, char *argv[]) + { + Ptr myObject = CreateObject (); + myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace)); + + myObject->m_myInt = 1234; + } +@end verbatim + +Here we first create the Object in which the trace source lives. + +The next step, the @code{TraceConnectWithoutContext}, forms the connection +between the trace source and the trace sink. Notice the @code{MakeCallback} +template function. This function does the magic required to create the +underlying @code{ns-3} Callback object and associate it with the function +@code{IntTrace}. TraceConnect makes the association between your provided +function and the overloaded @code{operator()} in the traced variable referred +to by the ``MyInteger'' Attribute. After this association is made, the trace +source will ``fire'' your provided callback function. + +The code to make all of this happen is, of course, non-trivial, but the essence +is that you are arranging for something that looks just like the @code{pfi()} +example above to be called by the trace source. The declaration of the +@code{TracedValue m_myInt;} in the Object itself performs the magic +needed to provide the overloaded operators (++, ---, etc.) that will use the +@code{operator()} to actually invoke the Callback with the desired parameters. +The @code{.AddTraceSource} performs the magic to connect the Callback to the +Config system, and @code{TraceConnectWithoutContext} performs the magic to +connect your function to the trace source, which is specified by Attribute +name. + +Let's ignore the bit about context for now. + +Finally, the line, + +@verbatim + myObject->m_myInt = 1234; +@end verbatim + +should be interpreted as an invocation of @code{operator=} on the member +variable @code{m_myInt} with the integer @code{1234} passed as a parameter. + +It turns out that this operator is defined (by @code{TracedValue}) to execute +a callback that returns void and takes two integer values as parameters --- +an old value and a new value for the integer in question. That is exactly +the function signature for the callback function we provided --- @code{IntTrace}. + +To summarize, a trace source is, in essence, a variable that holds a list of +callbacks. A trace sink is a function used as the target of a callback. The +Attribute and object type information systems are used to provide a way to +connect trace sources to trace sinks. The act of ``hitting'' a trace source +is executing an operator on the trace source which fires callbacks. This +results in the trace sink callbacks registering interest in the source being +called with the parameters provided by the source. + +If you now build and run this example, + +@verbatim + ./waf --run fourth +@end verbatim + +you will see the output from the @code{IntTrace} function execute as soon as the +trace source is hit: + +@verbatim + Traced 0 to 1234 +@end verbatim + +When we executed the code, @code{myObject->m_myInt = 1234;}, the trace source +fired and automatically provided the before and after values to the trace sink. +The function @code{IntTrace} then printed this to the standard output. No +problem. + +@subsection Using the Config Subsystem to Connect to Trace Sources + +The @code{TraceConnectWithoutContext} call shown above in the simple example is +actually very rarely used in the system. More typically, the @code{Config} +subsystem is used to allow selecting a trace source in the system using what is +called a @emph{config path}. We saw an example of this in the previous section +where we hooked the ``CourseChange'' event when we were playing with +@code{third.cc}. + +Recall that we defined a trace sink to print course change information from the +mobility models of our simulation. It should now be a lot more clear to you +what this function is doing. + +@verbatim + void + CourseChange (std::string context, Ptr model) + { + Vector position = model->GetPosition (); + NS_LOG_UNCOND (context << + " x = " << position.x << ", y = " << position.y); + } +@end verbatim + +When we connected the ``CourseChange'' trace source to the above trace sink, +we used what is called a ``Config Path'' to specify the source when we +arranged a connection between the pre-defined trace source and the new trace +sink: + +@verbatim + std::ostringstream oss; + oss << + "/NodeList/" << wifiStaNodes.Get (nWifi - 1)->GetId () << + "/$ns3::MobilityModel/CourseChange"; + + Config::Connect (oss.str (), MakeCallback (&CourseChange)); +@end verbatim + +Let's try and make some sense of what is sometimes considered relatively +mysterious code. For the purposes of discussion, assume that the node +number returned by the @code{GetId()} is ``7''. In this case, the path +above turns out to be, + +@verbatim + "/NodeList/7/$ns3::MobilityModel/CourseChange" +@end verbatim + +The last segment of a config path must be an @code{Attribute} of an +@code{Object}. In fact, if you had a pointer to the @code{Object} that has the +``CourseChange'' @code{Attribute} handy, you could write this just like we did +in the previous example. You know by now that we typically store pointers to +our nodes in a NodeContainer. In the @code{third.cc} example, the Nodes of +interest are stored in the @code{wifiStaNodes} NodeContainer. In fact, while +putting the path together, we used this container to get a Ptr which we +used to call GetId() on. We could have used this Ptr directly to call +a connect method directly: + +@verbatim + Ptr theObject = wifiStaNodes.Get (nWifi - 1); + theObject->TraceConnectWithoutContext ("CourseChange", MakeCallback (&CourseChange)); +@end verbatim + +In the @code{third.cc} example, we actually want an additional ``context'' to +be delivered along with the Callback parameters (which will be explained below) so we +could actually use the following equivalent code, + +@verbatim + Ptr theObject = wifiStaNodes.Get (nWifi - 1); + theObject->TraceConnect ("CourseChange", MakeCallback (&CourseChange)); +@end verbatim + +It turns out that the internal code for @code{Config::ConnectWithoutContext} and +@code{Config::Connect} actually do find a Ptr and call the appropriate +TraceConnect method at the lowest level. + +The @code{Config} functions take a path that represents a chain of @code{Object} +pointers. Each segment of a path corresponds to an Object Attribute. The last +segment is the Attribute of interest, and prior segments must be typed to contain +or find Objects. The @code{Config} code parses and ``walks'' this path until it +gets to the final segment of the path. It then interprets the last segment as +an @code{Attribute} on the last Object it found while walking the path. The +@code{Config} functions then call the appropriate @code{TraceConnect} or +@code{TraceConnectWithoutContext} method on the final Object. Let's see what +happens in a bit more detail when the above path is walked. + +The leading ``/'' character in the path refers to a so-called namespace. One +of the predefined namespaces in the config system is ``NodeList'' which is a +list of all of the nodes in the simulation. Items in the list are referred to +by indices into the list, so ``/NodeList/7'' refers to the eighth node in the +list of nodes created during the simulation. This reference is actually a +@code{Ptr} and so is a subclass of an @code{ns3::Object}. + +As described in the Object Model section of the @code{ns-3} manual, we support +Object Aggregation. This allows us to form an association between different +Objects without any programming. Each Object in an Aggregation can be reached +from the other Objects. + +The next path segment being walked begins with the ``$'' character. This +indicates to the config system that a @code{GetObject} call should be made +looking for the type that follows. It turns out that the MobilityHelper used in +@code{third.cc} arranges to Aggregate, or associate, a mobility model to each of +the wireless Nodes. When you add the ``$'' you are asking for another Object that +has presumably been previously aggregated. You can think of this as switching +pointers from the original Ptr as specified by ``/NodeList/7'' to its +associated mobility model --- which is of type ``$ns3::MobilityModel''. If you +are familiar with @code{GetObject}, we have asked the system to do the following: + +@verbatim + Ptr mobilityModel = node->GetObject () +@end verbatim + +We are now at the last Object in the path, so we turn our attention to the +Attributes of that Object. The @code{MobilityModel} class defines an Attribute +called ``CourseChange.'' You can see this by looking at the source code in +@code{src/mobility/mobility-model.cc} and searching for ``CourseChange'' in your +favorite editor. You should find, + +@verbatim + .AddTraceSource (``CourseChange'', + ``The value of the position and/or velocity vector changed'', + MakeTraceSourceAccessor (&MobilityModel::m_courseChangeTrace)) +@end verbatim + +which should look very familiar at this point. + +If you look for the corresponding declaration of the underlying traced variable +in @code{mobility-model.h} you will find + +@verbatim + TracedCallback > m_courseChangeTrace; +@end verbatim + +The type declaration @code{TracedCallback} identifies @code{m_courseChangeTrace} +as a special list of Callbacks that can be hooked using the Config functions +described above. + +The @code{MobilityModel} class is designed to be a base class providing a common +interface for all of the specific subclasses. If you search down to the end of +the file, you will see a method defined called @code{NotifyCourseChange()}: + +@verbatim + void + MobilityModel::NotifyCourseChange (void) const + { + m_courseChangeTrace(this); + } +@end verbatim + +Derived classes will call into this method whenever they do a course change to +support tracing. This method invokes @code{operator()} on the underlying +@code{m_courseChangeTrace}, which will, in turn, invoke all of the registered +Callbacks, calling all of the trace sinks that have registered interest in the +trace source by calling a Config function. + +So, in the @code{third.cc} example we looked at, whenever a course change is +made in one of the @code{RandomWalk2dMobilityModel} instances installed, there +will be a @code{NotifyCourseChange()} call which calls up into the +@code{MobilityModel} base class. As seen above, this invokes @code{operator()} +on @code{m_courseChangeTrace}, which in turn, calls any registered trace sinks. +In the example, the only code registering an interest was the code that provided +the config path. Therefore, the @code{CourseChange} function that was hooked +from Node number seven will be the only Callback called. + +The final piece of the puzzle is the ``context.'' Recall that we saw an output +looking something like the following from @code{third.cc}: + +@verbatim + /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.27897, y = 2.22677 +@end verbatim + +The first part of the output is the context. It is simply the path through +which the config code located the trace source. In the case we have been looking at +there can be any number of trace sources in the system corresponding to any number +of nodes with mobility models. There needs to be some way to identify which trace +source is actually the one that fired the Callback. An easy way is to request a +trace context when you @code{Config::Connect}. + +@subsection How to Find and Connect Trace Sources, and Discover Callback Signatures + +The first question that inevitably comes up for new users of the Tracing system is, +``okay, I know that there must be trace sources in the simulation core, but how do +I find out what trace sources are available to me''? + +The second question is, ``okay, I found a trace source, how do I figure out the +config path to use when I connect to it''? + +The third question is, ``okay, I found a trace source, how do I figure out what +the return type and formal arguments of my callback function need to be''? + +The fourth question is, ``okay, I typed that all in and got this incredibly bizarre +error message, what in the world does it mean''? + +@subsection What Trace Sources are Available? + +The answer to this question is found in the @code{ns-3} Doxygen. Go to the +@code{ns-3} web site @uref{http://www.nsnam.org/getting_started.html,,``here''} +and select the ``Doxygen (stable)'' link ``Documentation'' on the navigation +bar to the left side of the page. Expand the ``Modules'' book in the NS-3 +documentation tree a the upper left by clicking the ``+'' box. Now, expand +the ``Core'' book in the tree by clicking its ``+'' box. You should now +see three extremely useful links: + +@itemize @bullet +@item The list of all trace sources +@item The list of all attributes +@item The list of all global values +@end itemize + +The list of interest to us here is ``the list of all trace sources.'' Go +ahead and select that link. You will see, perhaps not too surprisingly, a +list of all of the trace sources available in the @code{ns-3} core. + +As an example, scroll down to @code{ns3::MobilityModel}. You will find +an entry for + +@verbatim + CourseChange: The value of the position and/or velocity vector changed +@end verbatim + +You should recognize this as the trace source we used in the @code{third.cc} +example. Perusing this list will be helpful. + +@subsection What String do I use to Connect? + +The easiest way to do this is to grep around in the @code{ns-3} codebase for someone +who has already figured it out, You should always try to copy someone else's +working code before you start to write your own. Try something like: + +@verbatim + find . -name '*.cc' | xargs grep CourseChange | grep Connect +@end verbatim + +and you may find your answer along with working code. For example, in this +case, @code{./ns-3-dev/examples/wireless/mixed-wireless.cc} has something +just waiting for you to use: + +@verbatim + Config::Connect (``/NodeList/*/$ns3::MobilityModel/CourseChange'', + MakeCallback (&CourseChangeCallback)); +@end verbatim + +If you cannot find any examples in the distribution, you can find this out +from the @code{ns-3} Doxygen. It will probably be simplest just to walk +through the ``CourseChanged'' example. + +Let's assume that you have just found the ``CourseChanged'' trace source in +``The list of all trace sources'' and you want to figure out how to connect to +it. You know that you are using (again, from the @code{third.cc} example) an +@code{ns3::RandomWalk2dMobilityModel}. So open the ``Class List'' book in +the NS-3 documentation tree by clicking its ``+'' box. You will now see a +list of all of the classes in @code{ns-3}. Scroll down until you see the +entry for @code{ns3::RandomWalk2dMobilityModel} and follow that link. +You should now be looking at the ``ns3::RandomWalk2dMobilityModel Class +Reference.'' + +If you now scroll down to the ``Member Function Documentation'' section, you +will see documentation for the @code{GetTypeId} function. You constructed one +of these in the simple tracing example above: + +@verbatim + static TypeId GetTypeId (void) + { + static TypeId tid = TypeId ("MyObject") + .SetParent (Object::GetTypeId ()) + .AddConstructor () + .AddTraceSource ("MyInteger", + "An integer value to trace.", + MakeTraceSourceAccessor (&MyObject::m_myInt)) + ; + return tid; + } +@end verbatim + +As mentioned above, this is the bit of code that connected the Config +and Attribute systems to the underlying trace source. This is also the +place where you should start looking for information about the way to +connect. + +You are looking at the same information for the RandomWalk2dMobilityModel; and +the information you want is now right there in front of you in the Doxygen: + +@verbatim + This object is accessible through the following paths with Config::Set and Config::Connect: + + /NodeList/[i]/$ns3::MobilityModel/$ns3::RandomWalk2dMobilityModel +@end verbatim + +The documentation tells you how to get to the @code{RandomWalk2dMobilityModel} +Object. Compare the string above with the string we actually used in the +example code: + +@verbatim + "/NodeList/7/$ns3::MobilityModel" +@end verbatim + +The difference is due to the fact that two @code{GetObject} calls are implied +in the string found in the documentation. The first, for @code{$ns3::MobilityModel} +will query the aggregation for the base class. The second implied +@code{GetObject} call, for @code{$ns3::RandomWalk2dMobilityModel}, is used to ``cast'' +the base class to the concrete imlementation class. The documentation shows +both of these operations for you. It turns out that the actual Attribute you are +going to be looking for is found in the base class as we have seen. + +Look further down in the @code{GetTypeId} doxygen. You will find, + +@verbatim + No TraceSources defined for this type. + TraceSources defined in parent class ns3::MobilityModel: + + CourseChange: The value of the position and/or velocity vector changed + Reimplemented from ns3::MobilityModel +@end verbatim + +This is exactly what you need to know. The trace source of interest is found in +@code{ns3::MobilityModel} (which you knew anyway). The interesting thing this +bit of Doxygen tells you is that you don't need that extra cast in the config +path above to get to the concrete class, since the trace source is actually in +the base class. Therefore the additional @code{GetObject} is not required and +you simply use the path: + +@verbatim + /NodeList/[i]/$ns3::MobilityModel +@end verbatim + +which perfectly matches the example path: + +@verbatim + /NodeList/7/$ns3::MobilityModel +@end verbatim + +@subsection What Return Value and Formal Arguments? + +The easiest way to do this is to grep around in the @code{ns-3} codebase for someone +who has already figured it out, You should always try to copy someone else's +working code. Try something like: + +@verbatim + find . -name '*.cc' | xargs grep CourseChange | grep Connect +@end verbatim + +and you may find your answer along with working code. For example, in this +case, @code{./ns-3-dev/examples/wireless/mixed-wireless.cc} has something +just waiting for you to use. You will find + +@verbatim + Config::Connect (``/NodeList/*/$ns3::MobilityModel/CourseChange'', + MakeCallback (&CourseChangeCallback)); +@end verbatim + +as a result of your grep. The @code{MakeCallback} should indicate to you that +there is a callback function there which you can use. Sure enough, there is: + +@verbatim + static void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +@subsubsection Take my Word for It + +If there are no examples to work from, this can be, well, challenging to +actually figure out from the source code. + +Before embarking on a walkthrough of the code, I'll be kind and just tell you +a simple way to figure this out: The return value of your callback will always +be void. The formal parameter list for a @code{TracedCallback} can be found +from the template parameter list in the declaration. Recall that for our +current example, this is in @code{mobility-model.h}, where we have previously +found: + +@verbatim + TracedCallback > m_courseChangeTrace; +@end verbatim + +There is a one-to-one correspondence between the template parameter list in +the declaration and the formal arguments of the callback function. Here, +there is one template parameter, which is a @code{Ptr}. +This tells you that you need a function that returns void and takes a +a @code{Ptr}. For example, + +@verbatim + void + CourseChangeCallback (Ptr model) + { + ... + } +@end verbatim + +That's all you need if you want to @code{Config::ConnectWithoutContext}. If +you want a context, you need to @code{Config::Connect} and use a Callback +function that takes a string context, then the required argument. + +@verbatim + void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +If you want to ensure that your @code{CourseChangeCallback} is only visible +in your local file, you can add the keyword @code{static} and come up with: + +@verbatim + static void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +which is exactly what we used in the @code{third.cc} example. + +@subsubsection The Hard Way + +This section is entirely optional. It is going to be a bumpy ride, especially +for those unfamiliar with the details of templates. However, if you get through +this, you will have a very good handle on a lot of the @code{ns-3} low level +idioms. + +So, again, let's figure out what signature of callback function is required for +the ``CourseChange'' Attribute. This is going to be painful, but you only need +to do this once. After you get through this, you will be able to just look at +a @code{TracedCallback} and understand it. + +The first thing we need to look at is the declaration of the trace source. +Recall that this is in @code{mobility-model.h}, where we have previously +found: + +@verbatim + TracedCallback > m_courseChangeTrace; +@end verbatim + +This declaration is for a template. The template parameter is inside the +angle-brackets, so we are really interested in finding out what that +@code{TracedCallback<>} is. If you have absolutely no idea where this might +be found, grep is your friend. + +We are probably going to be interested in some kind of declaration in the +@code{ns-3} source, so first change into the @code{src} directory. Then, +we know this declaration is going to have to be in some kind of header file, +so just grep for it using: + +@verbatim + find . -name '*.h' | xargs grep TracedCallback +@end verbatim + +You'll see 124 lines fly by (I piped this through wc to see how bad it was). +Although that may seem like it, that's not really a lot. Just pipe the output +through more and start scanning through it. On the first page, you will see +some very suspiciously template-looking stuff. + +@verbatim + TracedCallback::TracedCallback () + TracedCallback::ConnectWithoutContext (c ... + TracedCallback::Connect (const CallbackB ... + TracedCallback::DisconnectWithoutContext ... + TracedCallback::Disconnect (const Callba ... + TracedCallback::operator() (void) const ... + TracedCallback::operator() (T1 a1) const ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... +@end verbatim + +It turns out that all of this comes from the header file +@code{traced-callback.h} which sounds very promising. You can then take a +look at @code{mobility-model.h} and see that there is a line which confirms +this hunch: + +@verbatim + #include "ns3/traced-callback.h" +@end verbatim + +Of course, you could have gone at this from the other direction and started +by looking at the includes in @code{mobility-model.h} and noticing the +include of @code{traced-callback.h} and inferring that this must be the file +you want. + +In either case, the next step is to take a look at @code{src/core/traced-callback.h} +in your favorite editor to see what is happening. + +You will see a comment at the top of the file that should be comforting: + +@verbatim + An ns3::TracedCallback has almost exactly the same API as a normal ns3::Callback but + instead of forwarding calls to a single function (as an ns3::Callback normally does), + it forwards calls to a chain of ns3::Callback. +@end verbatim + +This should sound very familiar and let you know you are on the right track. + +Just after this comment, you will find, + +@verbatim + template + class TracedCallback + { + ... +@end verbatim + +This tells you that TracedCallback is a templated class. It has eight possible +type parameters with default values. Go back and compare this with the +declaration you are trying to understand: + +@verbatim + TracedCallback > m_courseChangeTrace; +@end verbatim + +The @code{typename T1} in the templated class declaration corresponds to the +@code{Ptr} in the declaration above. All of the other +type parameters are left as defaults. Looking at the constructor really +doesn't tell you much. The one place where you have seen a connection made +between your Callback function and the tracing system is in the @code{Connect} +and @code{ConnectWithoutContext} functions. If you scroll down, you will see +a @code{ConnectWithoutContext} method here: + +@verbatim + template + void + TracedCallback::ConnectWithoutContext ... + { + Callback cb; + cb.Assign (callback); + m_callbackList.push_back (cb); + } +@end verbatim + +You are now in the belly of the beast. When the template is instantiated for +the declaration above, the compiler will replace @code{T1} with +@code{Ptr}. + +@verbatim + void + TracedCallback::ConnectWithoutContext ... cb + { + Callback > cb; + cb.Assign (callback); + m_callbackList.push_back (cb); + } +@end verbatim + +You can now see the implementation of everything we've been talking about. The +code creates a Callback of the right type and assigns your function to it. This +is the equivalent of the @code{pfi = MyFunction} we discussed at the start of +this section. The code then adds the Callback to the list of Callbacks for +this source. The only thing left is to look at the definition of Callback. +Using the same grep trick as we used to find @code{TracedCallback}, you will be +able to find that the file @code{./core/callback.h} is the one we need to look at. + +If you look down through the file, you will see a lot of probably almost +incomprehensible template code. You will eventually come to some Doxygen for +the Callback template class, though. Fortunately, there is some English: + +@verbatim + This class template implements the Functor Design Pattern. + It is used to declare the type of a Callback: + - the first non-optional template argument represents + the return type of the callback. + - the second optional template argument represents + the type of the first argument to the callback. + - the third optional template argument represents + the type of the second argument to the callback. + - the fourth optional template argument represents + the type of the third argument to the callback. + - the fifth optional template argument represents + the type of the fourth argument to the callback. + - the sixth optional template argument represents + the type of the fifth argument to the callback. +@end verbatim + +We are trying to figure out what the + +@verbatim + Callback > cb; +@end verbatim + +declaration means. Now we are in a position to understand that the first +(non-optional) parameter, @code{void}, represents the return type of the +Callback. The second (non-optional) parameter, @code{Ptr} +represents the first argument to the callback. + +The Callback in question is your function to receive the trace events. From +this you can infer that you need a function that returns @code{void} and takes +a @code{Ptr}. For example, + +@verbatim + void + CourseChangeCallback (Ptr model) + { + ... + } +@end verbatim + +That's all you need if you want to @code{Config::ConnectWithoutContext}. If +you want a context, you need to @code{Config::Connect} and use a Callback +function that takes a string context. This is because the @code{Connect} +function will provide the context for you. You'll need: + +@verbatim + void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +If you want to ensure that your @code{CourseChangeCallback} is only visible +in your local file, you can add the keyword @code{static} and come up with: + +@verbatim + static void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +which is exactly what we used in the @code{third.cc} example. Perhaps you +should now go back and reread the previous section (Take My Word for It). + +If you are interested in more details regarding the implementation of +Callbacks, feel free to take a look at the @code{ns-3} manual. They are one +of the most frequently used constructs in the low-level parts of @code{ns-3}. +It is, in my opinion, a quite elegant thing. + + + + + + + + diff --git a/doc/tutorial/tutorial.texi b/doc/tutorial/tutorial.texi index 80bae6a6a..e445fed1e 100644 --- a/doc/tutorial/tutorial.texi +++ b/doc/tutorial/tutorial.texi @@ -84,6 +84,7 @@ see @uref{http://www.nsnam.org/docs/tutorial.pdf}. * Conceptual Overview:: * Tweaking ns-3:: * Building Topologies:: +* The Tracing System:: @end menu @include introduction.texi diff --git a/src/common/pcap-file-test-suite.cc b/src/common/pcap-file-test-suite.cc index c96f7135a..6a9e3903f 100644 --- a/src/common/pcap-file-test-suite.cc +++ b/src/common/pcap-file-test-suite.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "ns3/test.h" #include "ns3/pcap-file.h" @@ -175,6 +176,7 @@ WriteModeCreateTestCase::DoRun (void) // data. // uint8_t buffer[128]; + memset(buffer, 0, sizeof(buffer)); err = f.Write (0, 0, buffer, 128); NS_TEST_ASSERT_MSG_EQ (err, false, "Write (write-only-file " << m_testFilename << ") returns error"); @@ -370,6 +372,7 @@ AppendModeCreateTestCase::DoRun (void) // We should be able to write to it since it was opened in "a" mode. // uint8_t buffer[128]; + memset(buffer, 0, sizeof(buffer)); err = f.Write (0, 0, buffer, 128); NS_TEST_ASSERT_MSG_EQ (err, false, "Write (append-mode-file " << m_testFilename << ") returns error"); diff --git a/src/helper/application-container.h b/src/helper/application-container.h index a2cebe6ae..04a20e4e8 100644 --- a/src/helper/application-container.h +++ b/src/helper/application-container.h @@ -157,7 +157,7 @@ public: * \brief Append the contents of another ApplicationContainer to the end of * this container. * - * \param The ApplicationContainer to append. + * \param other The ApplicationContainer to append. */ void Add (ApplicationContainer other); @@ -202,9 +202,9 @@ public: * down and stop doing their thing (Stop) at a common time. * * This method simply iterates through the contained Applications and calls - * their Start() methods with the provided Time. + * their Stop() methods with the provided Time. * - * \param start The Time at which each of the applications should start. + * \param stop The Time at which each of the applications should stop. */ void Stop (Time stop); diff --git a/src/helper/dot11s-installer.h b/src/helper/dot11s-installer.h index a142591da..3c92eec1e 100644 --- a/src/helper/dot11s-installer.h +++ b/src/helper/dot11s-installer.h @@ -54,7 +54,7 @@ public: /** * \brief Install an 802.11s stack. - * \param The Ptr to use when setting up the PMP. + * \param mp The Ptr to use when setting up the PMP. */ bool InstallStack (Ptr mp); diff --git a/src/helper/flame-installer.h b/src/helper/flame-installer.h index b7d93b0f2..efeb4d4d4 100644 --- a/src/helper/flame-installer.h +++ b/src/helper/flame-installer.h @@ -58,7 +58,7 @@ public: /** * \brief Install a flame stack on the given MeshPointDevice - * \param The Ptr to use. + * \param mp The Ptr to use. */ bool InstallStack (Ptr mp); diff --git a/src/helper/flow-monitor-helper.h b/src/helper/flow-monitor-helper.h index 4786a195e..409872d08 100644 --- a/src/helper/flow-monitor-helper.h +++ b/src/helper/flow-monitor-helper.h @@ -46,7 +46,7 @@ public: /// \param nodes A NodeContainer holding the set of nodes to work with. Ptr Install (NodeContainer nodes); /// \brief Enable flow monitoring on a single node - /// \param nodes A Ptr to the node on which to enable flow monitoring. + /// \param node A Ptr to the node on which to enable flow monitoring. Ptr Install (Ptr node); /// \brief Enable flow monitoring on all nodes Ptr InstallAll (); diff --git a/src/helper/ipv4-interface-container.h b/src/helper/ipv4-interface-container.h index 1e4e74562..703702a33 100644 --- a/src/helper/ipv4-interface-container.h +++ b/src/helper/ipv4-interface-container.h @@ -79,12 +79,11 @@ public: * Manually add an entry to the container consisting of a previously composed * entry std::pair. * - * \param ipv4 pointer to Ipv4 object - * \param interface interface index of the Ipv4Interface to add to the container + * \param ipInterfacePair the pair of a pointer to Ipv4 object and interface index of the Ipv4Interface to add to the container * * @see Ipv4InterfaceContainer */ - void Add (std::pair, uint32_t>); + void Add (std::pair, uint32_t> ipInterfacePair); /** * Manually add an entry to the container consisting of the individual parts diff --git a/src/helper/net-device-container.h b/src/helper/net-device-container.h index 79db52daf..1940d28d5 100644 --- a/src/helper/net-device-container.h +++ b/src/helper/net-device-container.h @@ -175,14 +175,14 @@ public: * \brief Append the contents of another NetDeviceContainer to the end of * this container. * - * \param The NetDeviceContainer to append. + * \param other The NetDeviceContainer to append. */ void Add (NetDeviceContainer other); /** * \brief Append a single Ptr to this container. * - * \param application The Ptr to append. + * \param device The Ptr to append. */ void Add (Ptr device); @@ -190,7 +190,7 @@ public: * \brief Append to this container the single Ptr referred to * via its object name service registered name. * - * \param name The name of the NetDevice Object to add to the container. + * \param deviceName The name of the NetDevice Object to add to the container. */ void Add (std::string deviceName); diff --git a/src/helper/node-container.h b/src/helper/node-container.h index 0e2634be5..7a07f5e0b 100644 --- a/src/helper/node-container.h +++ b/src/helper/node-container.h @@ -58,7 +58,7 @@ public: * instantiated and assigned a name using the Object Name Service. This * Node is then specified by its assigned name. * - * \param name The name of the Node Object to add to the container. + * \param nodeName The name of the Node Object to add to the container. */ NodeContainer (std::string nodeName); @@ -240,7 +240,7 @@ public: * \brief Append the contents of another NodeContainer to the end of * this container. * - * \param The NodeContainer to append. + * \param other The NodeContainer to append. */ void Add (NodeContainer other); diff --git a/src/helper/udp-echo-helper.h b/src/helper/udp-echo-helper.h index c554359f4..bc24dd041 100644 --- a/src/helper/udp-echo-helper.h +++ b/src/helper/udp-echo-helper.h @@ -121,7 +121,7 @@ public: * packet (what is sent as data to the server) to the contents of the fill * string (including the trailing zero terminator). * - * \warn The size of resulting echo packets will be automatically adjusted + * \warning The size of resulting echo packets will be automatically adjusted * to reflect the size of the fill string -- this means that the PacketSize * attribute may be changed as a result of this call. * @@ -137,7 +137,7 @@ public: * * The fill byte will be used to initialize the contents of the data packet. * - * \warn The size of resulting echo packets will be automatically adjusted + * \warning The size of resulting echo packets will be automatically adjusted * to reflect the dataLength parameter -- this means that the PacketSize * attribute may be changed as a result of this call. * @@ -156,7 +156,7 @@ public: * by providing a complete buffer with fillLength set to your desired * dataLength * - * \warn The size of resulting echo packets will be automatically adjusted + * \warning The size of resulting echo packets will be automatically adjusted * to reflect the dataLength parameter -- this means that the PacketSize * attribute of the Application may be changed as a result of this call. * @@ -183,7 +183,7 @@ public: * is provided as a string name of a Node that has been previously * associated using the Object Name Service. * - * \param node The name of the node on which to create the UdpEchoClientApplication + * \param nodeName The name of the node on which to create the UdpEchoClientApplication * * \returns An ApplicationContainer that holds a Ptr to the * application created diff --git a/src/simulator/cairo-wideint-private.h b/src/simulator/cairo-wideint-private.h index 6c8358b9f..bbbca4fd7 100644 --- a/src/simulator/cairo-wideint-private.h +++ b/src/simulator/cairo-wideint-private.h @@ -36,10 +36,12 @@ #define cairo_private #define HAVE_UINT64_T 1 -/*for compatibility with MacOS*/ +/*for compatibility with MacOS and Cygwin*/ #ifndef HAVE_STDINT_H #ifdef __APPLE__ #define HAVE_STDINT_H 1 +#elif defined(WIN32) +#define HAVE_STDINT_H 1 #endif #endif diff --git a/test.py b/test.py index accacd289..1edcd0453 100755 --- a/test.py +++ b/test.py @@ -119,6 +119,7 @@ example_tests = [ ("tutorial/hello-simulator", "True"), ("tutorial/second", "True"), ("tutorial/third", "True"), + ("tutorial/fourth", "True"), ("udp/udp-echo", "True"), diff --git a/wscript b/wscript index d299625be..1d18b155c 100644 --- a/wscript +++ b/wscript @@ -137,16 +137,6 @@ def set_options(opt): action="store_true", default=False, dest='lcov_report') - opt.add_option('--doxygen', - help=('Run doxygen to generate html documentation from source comments'), - action="store_true", default=False, - dest='doxygen') - opt.add_option('--doxygen-no-build', - help=('Run doxygen to generate html documentation from source comments, ' - 'but do not wait for ns-3 to finish the full build.'), - action="store_true", default=False, - dest='doxygen_no_build') - opt.add_option('--run', help=('Run a locally built program; argument can be a program name,' ' or a command starting with the program name.'), @@ -590,25 +580,12 @@ def build(bld): raise Utils.WafError("Cannot run regression tests: building the ns-3 examples is not enabled" " (regression tests are based on examples)") -# if Options.options.check: -# Options.options.compile_targets += ',run-tests' -# if env['ENABLE_PYTHON_BINDINGS']: -# Options.options.compile_targets += ',ns3module,pybindgen-command' -# _run_check(bld) - - if Options.options.doxygen_no_build: - doxygen() - raise SystemExit(0) - def shutdown(ctx): bld = wutils.bld if wutils.bld is None: return env = bld.env - #if Options.commands['check']: - # _run_waf_check() - if Options.options.lcov_report: lcov_report() @@ -626,25 +603,14 @@ def shutdown(ctx): if Options.options.check: raise Utils.WafError("Please run `./test.py' now, instead of `./waf --check'") - if Options.options.doxygen: - doxygen() - raise SystemExit(0) - check_shell(bld) - if Options.options.doxygen: - doxygen() - raise SystemExit(0) - - check_context = Build.BuildContext def check(bld): """run the equivalent of the old ns-3 unit tests using test.py""" - bld = wutils.bld - env = bld.env - wutils.run_python_program("test.py -c core", env) - + env = wutils.bld.env + wutils.run_python_program("test.py -n -c core", env) class print_introspected_doxygen_task(Task.TaskBase): after = 'cc cxx cc_link cxx_link' @@ -696,111 +662,6 @@ class run_python_unit_tests_task(Task.TaskBase): wutils.run_argv([self.bld.env['PYTHON'], os.path.join("..", "utils", "python-unit-tests.py")], self.bld.env, proc_env, force_no_valgrind=True) -#class run_a_unit_test_task(Task.TaskBase): -# after = 'cc cxx cc_link cxx_link' -# color = 'BLUE' -# -# def __init__(self, bld, name_of_test): -# self.bld = bld -# super(run_a_unit_test_task, self).__init__(generator=self) -# self.name_of_test = name_of_test -# try: -# program_obj = wutils.find_program("run-tests", self.bld.env) -# except ValueError, ex: -# raise Utils.WafError(str(ex)) -# program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)) -# self.program_path = program_node.abspath(self.bld.env) -# -# def __str__(self): -# return 'run-unit-test(%s)\n' % self.name_of_test -# -# def runnable_status(self): -# return Task.RUN_ME -# -# def run(self): -# #print repr([self.program_path, self.name_of_test]) -# try: -# self.retval = wutils.run_argv([self.program_path, self.name_of_test], self.bld.env) -# except Utils.WafError: -# self.retval = 1 -# #print "running test %s: exit with %i" % (self.name_of_test, retval) -# return 0 -# -#class get_list_of_unit_tests_task(Task.TaskBase): -# after = 'cc cxx cc_link cxx_link' -# color = 'BLUE' -# -# def __init__(self, bld): -# self.bld = bld -# super(get_list_of_unit_tests_task, self).__init__(generator=self) -# self.tests = [] -# -# def __str__(self): -# return 'get-unit-tests-list\n' -# -# def runnable_status(self): -# return Task.RUN_ME -# -# def run(self): -# try: -# program_obj = wutils.find_program("run-tests", self.bld.env) -# except ValueError, ex: -# raise Utils.WafError(str(ex)) -# program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)) -# program_path = program_node.abspath(self.bld.env) -# proc = subprocess.Popen([program_path, "--ListTests"], stdout=subprocess.PIPE, -# env=wutils.get_proc_env()) -# self.tests = [l.rstrip() for l in proc.stdout.readlines()] -# retval = proc.wait() -# if retval: -# return retval -# test_tasks = [] -# for name_of_test in self.tests: -# test_tasks.append(run_a_unit_test_task(self.bld, name_of_test)) -# collector = collect_unit_test_results_task(self.bld, list(test_tasks)) -# collector.run_after = list(test_tasks) -# self.more_tasks = [collector] + test_tasks -# -# -#class collect_unit_test_results_task(Task.TaskBase): -# after = 'run_a_unit_test_task' -# color = 'BLUE' -# -# def __init__(self, bld, test_tasks): -# self.bld = bld -# super(collect_unit_test_results_task, self).__init__(generator=self) -# self.test_tasks = test_tasks -# -# def __str__(self): -# return 'collect-unit-tests-results\n' -# -# def runnable_status(self): -# for t in self.run_after: -# if not t.hasrun: -# return Task.ASK_LATER -# return Task.RUN_ME -# -# def run(self): -# failed_tasks = [] -# for task in self.test_tasks: -# if task.retval: -# failed_tasks.append(task) -# if failed_tasks: -# print "C++ UNIT TESTS: %i tests passed, %i failed (%s)." % \ -# (len(self.test_tasks) - len(failed_tasks), len(failed_tasks), -# ', '.join(t.name_of_test for t in failed_tasks)) -# return 1 -# else: -# print "C++ UNIT TESTS: all %i tests passed." % (len(self.test_tasks),) -# return 0 -# -# -#def _run_check(bld): -# task = get_list_of_unit_tests_task(bld) -# print_introspected_doxygen_task(bld) -# if bld.env['ENABLE_PYTHON_BINDINGS']: -# run_python_unit_tests_task(bld) - def check_shell(bld): if 'NS3_MODULE_PATH' not in os.environ: return @@ -835,11 +696,26 @@ def shell(ctx): env = wutils.bld.env wutils.run_argv([shell], env, {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])}) -def doxygen(): - if not os.path.exists('doc/introspected-doxygen.h'): - Logs.warn("doc/introspected-doxygen.h does not exist; run waf check to generate it.") +def doxygen(bld): + """do a full build, generate the introspected doxygen and then the doxygen""" + Scripting.build(bld) + env = wutils.bld.env + proc_env = wutils.get_proc_env() + + try: + program_obj = wutils.find_program('print-introspected-doxygen', env) + except ValueError: + Logs.warn("print-introspected-doxygen does not exist") + raise SystemExit(1) + return + + prog = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)).abspath(env) + out = open(os.path.join('doc', 'introspected-doxygen.h'), 'w') + + if subprocess.Popen([prog], stdout=out, env=proc_env).wait(): + raise SystemExit(1) + out.close() - ## run doxygen doxygen_config = os.path.join('doc', 'doxygen.conf') if subprocess.Popen(['doxygen', doxygen_config]).wait(): raise SystemExit(1) @@ -876,9 +752,6 @@ def lcov_report(): finally: os.chdir("..") - - - ## ## The default WAF DistDir implementation is rather slow, because it ## first copies everything and only later removes unwanted files and