2009-10-08 10:36:26 -07:00
|
|
|
@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::
|
2009-10-19 16:44:41 -07:00
|
|
|
* Overview::
|
2010-01-25 16:09:07 -08:00
|
|
|
* A Real Example::
|
2010-01-27 17:21:36 -08:00
|
|
|
* Using Mid-Level Trace Helpers::
|
2009-10-08 10:36:26 -07:00
|
|
|
@end menu
|
|
|
|
|
|
|
|
|
|
@c ============================================================================
|
|
|
|
|
@c Background
|
|
|
|
|
@c ============================================================================
|
|
|
|
|
@node Background
|
|
|
|
|
@section Background
|
|
|
|
|
|
|
|
|
|
As mentioned in the Using the Tracing System section, the whole point of running
|
2009-10-08 11:37:29 -07:00
|
|
|
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
|
2009-10-08 10:36:26 -07:00
|
|
|
mechanisms and parsing their content to extract interesting information; or
|
2009-10-08 11:37:29 -07:00
|
|
|
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.
|
|
|
|
|
|
2009-10-09 15:45:17 +04:00
|
|
|
If you need to add some tidbit of information to the pre-defined bulk mechanisms,
|
2009-10-08 11:37:29 -07:00
|
|
|
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
|
2010-02-01 13:35:47 -08:00
|
|
|
tracing the events of interest to you (for large simulations, dumping everything
|
|
|
|
|
to disk for post-processing can create I/O bottlenecks). 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}.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
@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 <iostream>
|
|
|
|
|
...
|
|
|
|
|
void
|
|
|
|
|
SomeFunction (void)
|
|
|
|
|
{
|
2009-10-09 15:45:17 +04:00
|
|
|
uint32_t x = SOME_INTERESTING_VALUE;
|
2009-10-08 10:36:26 -07:00
|
|
|
...
|
|
|
|
|
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
|
2009-10-08 11:37:29 -07:00
|
|
|
adding print statements. This is insanely easy to do and, after all, you have
|
2009-10-09 15:45:17 +04:00
|
|
|
complete control of your own @code{ns-3} branch. This will probably not turn
|
2009-10-08 11:37:29 -07:00
|
|
|
out to be very satisfactory in the long term, though.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
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
|
2009-10-08 11:37:29 -07:00
|
|
|
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:
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
@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
|
|
|
|
|
|
2009-10-09 15:45:17 +04:00
|
|
|
This may seem fairly simple and satisfying at first glance, but something to
|
2009-10-08 11:37:29 -07:00
|
|
|
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.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2009-10-08 11:37:29 -07:00
|
|
|
Since there are no guarantees in @code{ns-3} about the stability of @code{NS_LOG}
|
2010-02-01 13:35:47 -08:00
|
|
|
output, you may also discover that pieces of log output on which you depend
|
2009-10-08 11:37:29 -07:00
|
|
|
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.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
2009-10-08 11:37:29 -07:00
|
|
|
For these reasons, we consider prints to @code{std::cout} and NS_LOG messages
|
2010-02-01 13:35:47 -08:00
|
|
|
to be quick and dirty ways to get more information out of @code{ns-3}.
|
2009-10-08 11:37:29 -07:00
|
|
|
|
|
|
|
|
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
|
2009-10-08 10:36:26 -07:00
|
|
|
core system. Even better would be a system that notified the user when an item
|
2009-10-08 11:37:29 -07:00
|
|
|
of interest changed or an interesting event happened so the user doesn't have
|
2010-02-01 13:35:47 -08:00
|
|
|
to actively poke around in the system looking for things.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
The @command{ns-3} tracing system is designed to work along those lines and is
|
2009-10-09 15:45:17 +04:00
|
|
|
well-integrated with the Attribute and Config subsystems allowing for relatively
|
2009-10-08 10:36:26 -07:00
|
|
|
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
|
2010-02-01 13:35:47 -08:00
|
|
|
when an interesting state change happens in a model. For example, the congestion
|
2009-10-08 10:36:26 -07:00
|
|
|
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
|
2009-10-09 15:45:17 +04:00
|
|
|
indirect call, a trace source may invoke any number of Callbacks. When a trace
|
2009-10-08 10:36:26 -07:00
|
|
|
sink expresses interest in notifications from a trace source, it basically just
|
2009-10-09 15:45:17 +04:00
|
|
|
arranges to add its own function to the callback list.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
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 <iostream>
|
|
|
|
|
|
|
|
|
|
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=,
|
2009-10-09 15:45:17 +04:00
|
|
|
operator++, operator---, operator+, operator==, etc.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
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
|
2009-10-08 13:59:26 -07:00
|
|
|
to live in. The next code snippet declares and defines a simple Object we can
|
2009-10-08 10:36:26 -07:00
|
|
|
work with.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
class MyObject : public Object
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static TypeId GetTypeId (void)
|
|
|
|
|
{
|
|
|
|
|
static TypeId tid = TypeId ("MyObject")
|
|
|
|
|
.SetParent (Object::GetTypeId ())
|
|
|
|
|
.AddConstructor<MyObject> ()
|
|
|
|
|
.AddTraceSource ("MyInteger",
|
|
|
|
|
"An integer value to trace.",
|
|
|
|
|
MakeTraceSourceAccessor (&MyObject::m_myInt))
|
|
|
|
|
;
|
|
|
|
|
return tid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MyObject () {}
|
|
|
|
|
TracedValue<int32_t> 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
|
2009-10-08 13:59:26 -07:00
|
|
|
function. Once it is connected, this function will be called whenever one of the
|
|
|
|
|
overloaded operators of the @code{TracedValue} is executed.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
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> myObject = CreateObject<MyObject> ();
|
|
|
|
|
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
|
2009-10-08 13:59:26 -07:00
|
|
|
underlying @code{ns-3} Callback object and associate it with the function
|
2009-10-09 15:45:17 +04:00
|
|
|
@code{IntTrace}. TraceConnect makes the association between your provided
|
2009-10-08 13:59:26 -07:00
|
|
|
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<int32_t> m_myInt;} in the Object itself performs the magic
|
2009-10-09 15:45:17 +04:00
|
|
|
needed to provide the overloaded operators (++, ---, etc.) that will use the
|
2009-10-08 13:59:26 -07:00
|
|
|
@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.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
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.
|
2009-10-08 13:59:26 -07:00
|
|
|
|
2009-10-08 10:36:26 -07:00
|
|
|
It turns out that this operator is defined (by @code{TracedValue}) to execute
|
2009-10-09 15:45:17 +04:00
|
|
|
a callback that returns void and takes two integer values as parameters ---
|
2009-10-08 10:36:26 -07:00
|
|
|
an old value and a new value for the integer in question. That is exactly
|
2009-10-09 15:45:17 +04:00
|
|
|
the function signature for the callback function we provided --- @code{IntTrace}.
|
2009-10-08 10:36:26 -07:00
|
|
|
|
|
|
|
|
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<const MobilityModel> 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,
|
2009-10-09 15:45:17 +04:00
|
|
|
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
|
2009-10-08 10:36:26 -07:00
|
|
|
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<Node> which we
|
|
|
|
|
used to call GetId() on. We could have used this Ptr<Node> directly to call
|
|
|
|
|
a connect method directly:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<Object> 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
|
2009-10-09 15:45:17 +04:00
|
|
|
be delivered along with the Callback parameters (which will be explained below) so we
|
2009-10-08 10:36:26 -07:00
|
|
|
could actually use the following equivalent code,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<Object> 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<Object> 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<Node>} 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<Node> 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> mobilityModel = node->GetObject<MobilityModel> ()
|
|
|
|
|
@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
|
2010-01-27 17:21:36 -08:00
|
|
|
called ``CourseChange''. You can see this by looking at the source code in
|
2009-10-08 10:36:26 -07:00
|
|
|
@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<Ptr<const MobilityModel> > 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.
|
|
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
The final piece of the puzzle is the ``context''. Recall that we saw an output
|
2009-10-08 10:36:26 -07:00
|
|
|
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}.
|
|
|
|
|
|
2009-10-08 13:59:26 -07:00
|
|
|
@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
|
|
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
The list of interest to us here is ``the list of all trace sources''. Go
|
2009-10-08 13:59:26 -07:00
|
|
|
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?
|
|
|
|
|
|
2009-10-09 15:45:17 +04:00
|
|
|
The easiest way to do this is to grep around in the @code{ns-3} codebase for someone
|
2009-10-08 17:57:06 -07:00
|
|
|
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.
|
2009-10-08 13:59:26 -07:00
|
|
|
|
|
|
|
|
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
|
2010-01-27 17:21:36 -08:00
|
|
|
Reference''.
|
2009-10-08 13:59:26 -07:00
|
|
|
|
|
|
|
|
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<MyObject> ()
|
|
|
|
|
.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
|
2009-10-09 15:45:17 +04:00
|
|
|
bit of Doxygen tells you is that you don't need that extra cast in the config
|
2009-10-08 13:59:26 -07:00
|
|
|
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?
|
|
|
|
|
|
2009-10-09 15:45:17 +04:00
|
|
|
The easiest way to do this is to grep around in the @code{ns-3} codebase for someone
|
2009-10-08 17:57:06 -07:00
|
|
|
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<const MobilityModel> model)
|
|
|
|
|
{
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2009-10-08 19:20:28 -07:00
|
|
|
@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<Ptr<const MobilityModel> > 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<const MobilityModel>}.
|
|
|
|
|
This tells you that you need a function that returns void and takes a
|
|
|
|
|
a @code{Ptr<const MobilityModel>}. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
|
|
|
|
CourseChangeCallback (Ptr<const MobilityModel> 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<const MobilityModel> 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<const MobilityModel> model)
|
|
|
|
|
{
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2009-10-09 15:45:17 +04:00
|
|
|
which is exactly what we used in the @code{third.cc} example.
|
2009-10-08 19:20:28 -07:00
|
|
|
|
|
|
|
|
@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<Ptr<const MobilityModel> > 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).
|
2009-10-09 15:45:17 +04:00
|
|
|
Although that may seem like it, that's not really a lot. Just pipe the output
|
2009-10-08 19:20:28 -07:00
|
|
|
through more and start scanning through it. On the first page, you will see
|
|
|
|
|
some very suspiciously template-looking stuff.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::TracedCallback ()
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext (c ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Connect (const CallbackB ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::DisconnectWithoutContext ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Disconnect (const Callba ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (void) const ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1) const ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ...
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::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"
|
2009-10-09 15:45:17 +04:00
|
|
|
@end verbatim
|
2009-10-08 19:20:28 -07:00
|
|
|
|
|
|
|
|
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
|
2009-10-09 15:45:17 +04:00
|
|
|
include of @code{traced-callback.h} and inferring that this must be the file
|
2009-10-08 19:20:28 -07:00
|
|
|
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<typename T1 = empty, typename T2 = empty,
|
|
|
|
|
typename T3 = empty, typename T4 = empty,
|
|
|
|
|
typename T5 = empty, typename T6 = empty,
|
|
|
|
|
typename T7 = empty, typename T8 = empty>
|
|
|
|
|
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<Ptr<const MobilityModel> > m_courseChangeTrace;
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The @code{typename T1} in the templated class declaration corresponds to the
|
|
|
|
|
@code{Ptr<const MobilityModel>} 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<typename T1, typename T2,
|
|
|
|
|
typename T3, typename T4,
|
|
|
|
|
typename T5, typename T6,
|
|
|
|
|
typename T7, typename T8>
|
|
|
|
|
void
|
|
|
|
|
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext ...
|
|
|
|
|
{
|
|
|
|
|
Callback<void,T1,T2,T3,T4,T5,T6,T7,T8> 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<const MobilityModel>}.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
|
|
|
|
TracedCallback<Ptr<const MobilityModel>::ConnectWithoutContext ... cb
|
|
|
|
|
{
|
|
|
|
|
Callback<void, Ptr<const MobilityModel> > 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
|
2009-10-09 15:45:17 +04:00
|
|
|
able to find that the file @code{./core/callback.h} is the one we need to look at.
|
2009-10-08 19:20:28 -07:00
|
|
|
|
|
|
|
|
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<void, Ptr<const MobilityModel> > 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<const MobilityModel>}
|
|
|
|
|
represents the first argument to the callback.
|
|
|
|
|
|
|
|
|
|
The Callback in question is your function to receive the trace events. From
|
2009-10-09 15:45:17 +04:00
|
|
|
this you can infer that you need a function that returns @code{void} and takes
|
2009-10-08 19:20:28 -07:00
|
|
|
a @code{Ptr<const MobilityModel>}. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
|
|
|
|
CourseChangeCallback (Ptr<const MobilityModel> 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<const MobilityModel> 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<const MobilityModel> model)
|
|
|
|
|
{
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2009-10-09 15:45:17 +04:00
|
|
|
which is exactly what we used in the @code{third.cc} example. Perhaps you
|
2009-10-08 19:20:28 -07:00
|
|
|
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.
|
|
|
|
|
|
2009-10-19 16:44:41 -07:00
|
|
|
@subsection What About TracedValue?
|
|
|
|
|
|
|
|
|
|
Earlier in this section, we presented a simple piece of code that used a
|
|
|
|
|
@code{TracedValue<int32_t>} to demonstrate the basics of the tracing code.
|
|
|
|
|
We just glossed over the way to find the return type and formal arguments
|
|
|
|
|
for the @code{TracedValue}. Rather than go through the whole exercise, we
|
|
|
|
|
will just point you at the correct file, @code{src/core/traced-value.h} and
|
|
|
|
|
to the important piece of code:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
template <typename T>
|
|
|
|
|
class TracedValue
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
...
|
|
|
|
|
void Set (const T &v) {
|
|
|
|
|
if (m_v != v)
|
|
|
|
|
{
|
|
|
|
|
m_cb (m_v, v);
|
|
|
|
|
m_v = v;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
...
|
|
|
|
|
private:
|
|
|
|
|
T m_v;
|
|
|
|
|
TracedCallback<T,T> m_cb;
|
|
|
|
|
};
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Here you see that the @code{TracedValue} is templated, of course. In the simple
|
|
|
|
|
example case at the start of the section, the typename is int32_t. This means
|
2009-10-20 10:45:12 -07:00
|
|
|
that the member variable being traced (@code{m_v} in the private section of the
|
2009-10-19 16:44:41 -07:00
|
|
|
class) will be an @code{int32_t m_v}. The @code{Set} method will take a
|
2009-10-20 10:45:12 -07:00
|
|
|
@code{const int32_t &v} as a parameter. You should now be able to understand
|
2009-10-19 16:44:41 -07:00
|
|
|
that the @code{Set} code will fire the @code{m_cb} callback with two parameters:
|
|
|
|
|
the first being the current value of the @code{TracedValue}; and the second
|
|
|
|
|
being the new value being set.
|
|
|
|
|
|
|
|
|
|
The callback, @code{m_cb} is declared as a @code{TracedCallback<T, T>} which
|
|
|
|
|
will correspond to a @code{TracedCallback<int32_t, int32_t>} when the class is
|
|
|
|
|
instantiated.
|
|
|
|
|
|
|
|
|
|
Recall that the callback target of a TracedCallback always returns @code{void}.
|
|
|
|
|
Further recall that there is a one-to-one correspondence between the template
|
|
|
|
|
parameter list in the declaration and the formal arguments of the callback
|
|
|
|
|
function. Therefore the callback will need to have a function signature that
|
|
|
|
|
looks like:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
2009-10-20 10:45:12 -07:00
|
|
|
MyCallback (int32_t oldValue, int32_t newValue)
|
2009-10-19 16:44:41 -07:00
|
|
|
{
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
It probably won't surprise you that this is exactly what we provided in that
|
|
|
|
|
simple example we covered so long ago:
|
2009-10-08 17:57:06 -07:00
|
|
|
|
2009-10-19 16:44:41 -07:00
|
|
|
@verbatim
|
|
|
|
|
void
|
|
|
|
|
IntTrace (int32_t oldValue, int32_t newValue)
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
@c ============================================================================
|
|
|
|
|
@c A Real Example
|
|
|
|
|
@c ============================================================================
|
|
|
|
|
@node A Real Example
|
|
|
|
|
@section A Real Example
|
2009-10-08 13:59:26 -07:00
|
|
|
|
2009-10-19 16:44:41 -07:00
|
|
|
Let's do an example taken from one of the best-known books on TCP around.
|
|
|
|
|
``TCP/IP Illustrated, Volume 1: The Protocols,'' by W. Richard Stevens is a
|
|
|
|
|
classic. I just flipped the book open and ran across a nice plot of both the
|
|
|
|
|
congestion window and sequence numbers versus time on page 366. Stevens calls
|
|
|
|
|
this, ``Figure 21.10. Value of cwnd and send sequence number while data is being
|
|
|
|
|
transmitted.'' Let's just recreate the cwnd part of that plot in @command{ns-3}
|
|
|
|
|
using the tracing system and @code{gnuplot}.
|
2009-10-08 13:59:26 -07:00
|
|
|
|
2009-10-19 16:44:41 -07:00
|
|
|
@subsection Are There Trace Sources Available?
|
|
|
|
|
|
|
|
|
|
The first thing to think about is how we want to get the data out. What is it
|
|
|
|
|
that we need to trace? The first thing to do is to consult ``The list of all
|
|
|
|
|
trace sources'' to see what we have to work with. Recall that this is found
|
|
|
|
|
in the @command{ns-3} Doxygen in the ``Core'' Module section. If you scroll
|
|
|
|
|
through the list, you will eventually find:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
ns3::TcpSocketImpl
|
|
|
|
|
CongestionWindow: The TCP connection's congestion window
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
It turns out that the @command{ns-3} TCP implementation lives (mostly) in the
|
|
|
|
|
file @code{src/internet-stack/tcp-socket-impl.cc}. If you don't know this a
|
|
|
|
|
priori, you can use the recursive grep trick:
|
|
|
|
|
|
2009-10-19 18:47:01 -07:00
|
|
|
@verbatim
|
2009-10-19 16:44:41 -07:00
|
|
|
find . -name '*.cc' | xargs grep -i tcp
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You will find page after page of instances of tcp pointing you to that file.
|
|
|
|
|
|
|
|
|
|
If you open @code{src/internet-stack/tcp-socket-impl.cc} in your favorite
|
|
|
|
|
editor, you will see right up at the top of the file, the following declarations:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
TypeId
|
|
|
|
|
TcpSocketImpl::GetTypeId ()
|
|
|
|
|
{
|
|
|
|
|
static TypeId tid = TypeId(``ns3::TcpSocketImpl'')
|
|
|
|
|
.SetParent<TcpSocket> ()
|
|
|
|
|
.AddTraceSource (``CongestionWindow'',
|
|
|
|
|
``The TCP connection's congestion window'',
|
|
|
|
|
MakeTraceSourceAccessor (&TcpSocketImpl::m_cWnd))
|
|
|
|
|
;
|
|
|
|
|
return tid;
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This should tell you to look for the declaration of @code{m_cWnd} in the header
|
|
|
|
|
file @code{src/internet-stack/tcp-socket-impl.h}. If you open this file in your
|
|
|
|
|
favorite editor, you will find:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
TracedValue<uint32_t> m_cWnd; //Congestion window
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You should now understand this code completely. If we have a pointer to the
|
|
|
|
|
@code{TcpSocketImpl}, we can @code{TraceConnect} to the ``CongestionWindow'' trace
|
|
|
|
|
source if we provide an appropriate callback target. This is the same kind of
|
|
|
|
|
trace source that we saw in the simple example at the start of this section,
|
|
|
|
|
except that we are talking about @code{uint32_t} instead of @code{int32_t}.
|
|
|
|
|
|
|
|
|
|
We now know that we need to provide a callback that returns void and takes
|
|
|
|
|
two @code{uint32_t} parameters, the first being the old value and the second
|
|
|
|
|
being the new value:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
|
|
|
|
CwndTrace (uint32_t oldValue, uint32_t newValue)
|
|
|
|
|
{
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
@subsection What Script to Use?
|
|
|
|
|
|
|
|
|
|
It's always best to try and find working code laying around that you can
|
2009-10-20 10:45:12 -07:00
|
|
|
modify, rather than starting from scratch. So the first order of business now
|
2009-10-19 16:44:41 -07:00
|
|
|
is to find some code that already hooks the ``CongestionWindow'' trace source
|
|
|
|
|
and see if we can modify it. As usual, grep is your friend:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
find . -name '*.cc' | xargs grep CongestionWindow
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This will point out a couple of promising candidates:
|
|
|
|
|
@code{examples/tcp/tcp-large-transfer.cc} and
|
|
|
|
|
@code{src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc}.
|
|
|
|
|
|
|
|
|
|
We haven't visited any of the test code yet, so let's take a look there. You
|
|
|
|
|
will typically find that test code is fairly minimal, so this is probably a
|
|
|
|
|
very good bet. Open @code{src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc} in your
|
2010-01-27 17:21:36 -08:00
|
|
|
favorite editor and search for ``CongestionWindow''. You will find,
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
ns3TcpSocket->TraceConnectWithoutContext (``CongestionWindow'',
|
|
|
|
|
MakeCallback (&Ns3TcpCwndTestCase1::CwndChange, this));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This should look very familiar to you. We mentioned above that if we had a
|
|
|
|
|
pointer to the @code{TcpSocketImpl}, we could @code{TraceConnect} to the
|
|
|
|
|
``CongestionWindow'' trace source. That's exactly what we have here; so it
|
|
|
|
|
turns out that this line of code does exactly what we want. Let's go ahead
|
|
|
|
|
and extract the code we need from this function
|
|
|
|
|
(@code{Ns3TcpCwndTestCase1::DoRun (void)}). If you look at this function,
|
|
|
|
|
you will find that it looks just like an @code{ns-3} script. It turns out that
|
|
|
|
|
is exactly what it is. It is a script run by the test framework, so we can just
|
|
|
|
|
pull it out and wrap it in @code{main} instead of in @code{DoRun}. Rather than
|
2009-10-20 10:45:12 -07:00
|
|
|
walk through this, step, by step, we have provided the file that results from
|
2009-10-19 18:47:01 -07:00
|
|
|
porting this test back to a native @code{ns-3} script --
|
|
|
|
|
@code{examples/tutorial/fifth.cc}.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@subsection A Common Problem and Solution
|
|
|
|
|
|
|
|
|
|
The @code{fifth.cc} example demonstrates an extremely important rule that you
|
|
|
|
|
must understand before using any kind of @code{Attribute}: you must ensure
|
|
|
|
|
that the target of a @code{Config} command exists before trying to use it.
|
|
|
|
|
This is no different than saying an object must be instantiated before trying
|
2009-10-20 10:45:12 -07:00
|
|
|
to call it. Although this may seem obvious when stated this way, it does
|
2009-10-19 16:44:41 -07:00
|
|
|
trip up many people trying to use the system for the first time.
|
|
|
|
|
|
|
|
|
|
Let's return to basics for a moment. There are three basic time periods that
|
|
|
|
|
exist in any @command{ns-3} script. The first time period is sometimes called
|
|
|
|
|
``Configuration Time'' or ``Setup Time,'' and is in force during the period
|
|
|
|
|
when the @code{main} function of your script is running, but before
|
|
|
|
|
@code{Simulator::Run} is called. The second time period is sometimes called
|
|
|
|
|
``Simulation Time'' and is in force during the time period when
|
|
|
|
|
@code{Simulator::Run} is actively executing its events. After it completes
|
|
|
|
|
executing the simulation, @code{Simulator::Run} will return control back to
|
|
|
|
|
the @code{main} function. When this happens, the script enters what can be
|
|
|
|
|
called ``Teardown Time,'' which is when the structures and objects created
|
2009-10-20 10:45:12 -07:00
|
|
|
during setup and taken apart and released.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
Perhaps the most common mistake made in trying to use the tracing system is
|
|
|
|
|
assuming that entities constructed dynamically during simulation time are
|
|
|
|
|
available during configuration time. In particular, an @command{ns-3}
|
|
|
|
|
@code{Socket} is a dynamic object often created by @code{Applications} to
|
|
|
|
|
communicate between @code{Nodes}. An @command{ns-3} @code{Application}
|
|
|
|
|
always has a ``Start Time'' and a ``Stop Time'' associated with it. In the
|
|
|
|
|
vast majority of cases, an @code{Application} will not attempt to create
|
2010-03-05 11:14:53 -08:00
|
|
|
a dynamic object until its @code{StartApplication} method is called at some
|
|
|
|
|
``Start Time''. This is to ensure that the simulation is completely
|
|
|
|
|
configured before the app tries to do anything (what would happen if it tried
|
|
|
|
|
to connect to a node that didn't exist yet during configuration time). The
|
|
|
|
|
answer to this issue is to 1) create a simulator event that is run after the
|
|
|
|
|
dynamic object is created and hook the trace when that event is executed; or
|
|
|
|
|
2) create the dynamic object at configuration time, hook it then, and give
|
|
|
|
|
the object to the system to use during simulation time. We took the second
|
|
|
|
|
approach in the @code{fifth.cc} example. This decision required us to create
|
|
|
|
|
the @code{MyApp} @code{Application}, the entire purpose of which is to take
|
|
|
|
|
a @code{Socket} as a parameter.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@subsection A fifth.cc Walkthrough
|
|
|
|
|
|
2009-10-20 10:45:12 -07:00
|
|
|
Now, let's take a look at the example program we constructed by dissecting
|
2009-10-19 16:44:41 -07:00
|
|
|
the congestion window test. Open @code{examples/tutorial/fifth.cc} in your
|
|
|
|
|
favorite editor. You should see some familiar looking code:
|
|
|
|
|
|
|
|
|
|
@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, Include., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
*/
|
|
|
|
|
|
2009-10-19 18:47:01 -07:00
|
|
|
#include <fstream>
|
|
|
|
|
#include "ns3/core-module.h"
|
|
|
|
|
#include "ns3/common-module.h"
|
|
|
|
|
#include "ns3/simulator-module.h"
|
|
|
|
|
#include "ns3/node-module.h"
|
|
|
|
|
#include "ns3/helper-module.h"
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
using namespace ns3;
|
|
|
|
|
|
2009-10-19 18:47:01 -07:00
|
|
|
NS_LOG_COMPONENT_DEFINE ("FifthScriptExample");
|
2009-10-19 16:44:41 -07:00
|
|
|
@end verbatim
|
|
|
|
|
|
2009-10-19 18:47:01 -07:00
|
|
|
This has all been covered, so we won't rehash it. The next lines of source are
|
2009-10-19 16:44:41 -07:00
|
|
|
the network illustration and a comment addressing the problem described above
|
|
|
|
|
with @code{Socket}.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
// ===========================================================================
|
|
|
|
|
//
|
|
|
|
|
// node 0 node 1
|
|
|
|
|
// +----------------+ +----------------+
|
|
|
|
|
// | ns-3 TCP | | ns-3 TCP |
|
|
|
|
|
// +----------------+ +----------------+
|
|
|
|
|
// | 10.1.1.1 | | 10.1.1.2 |
|
|
|
|
|
// +----------------+ +----------------+
|
|
|
|
|
// | point-to-point | | point-to-point |
|
|
|
|
|
// +----------------+ +----------------+
|
|
|
|
|
// | |
|
|
|
|
|
// +---------------------+
|
|
|
|
|
// 5 Mbps, 2 ms
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// We want to look at changes in the ns-3 TCP congestion window. We need
|
|
|
|
|
// to crank up a flow and hook the CongestionWindow attribute on the socket
|
|
|
|
|
// of the sender. Normally one would use an on-off application to generate a
|
|
|
|
|
// flow, but this has a couple of problems. First, the socket of the on-off
|
|
|
|
|
// application is not created until Application Start time, so we wouldn't be
|
|
|
|
|
// able to hook the socket (now) at configuration time. Second, even if we
|
|
|
|
|
// could arrange a call after start time, the socket is not public so we
|
|
|
|
|
// couldn't get at it.
|
|
|
|
|
//
|
|
|
|
|
// So, we can cook up a simple version of the on-off application that does what
|
|
|
|
|
// we want. On the plus side we don't need all of the complexity of the on-off
|
|
|
|
|
// application. On the minus side, we don't have a helper, so we have to get
|
|
|
|
|
// a little more involved in the details, but this is trivial.
|
|
|
|
|
//
|
|
|
|
|
// So first, we create a socket and do the trace connect on it; then we pass
|
|
|
|
|
// this socket into the constructor of our simple application which we then
|
|
|
|
|
// install in the source node.
|
|
|
|
|
// ===========================================================================
|
|
|
|
|
//
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This should also be self-explanatory.
|
|
|
|
|
|
2009-10-19 17:35:02 -07:00
|
|
|
The next part is the declaration of the @code{MyApp} @code{Application} that
|
2009-10-19 16:44:41 -07:00
|
|
|
we put together to allow the @code{Socket} to be created at configuration time.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2009-10-19 17:35:02 -07:00
|
|
|
class MyApp : public Application
|
2009-10-19 16:44:41 -07:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
2009-10-19 17:35:02 -07:00
|
|
|
MyApp ();
|
|
|
|
|
virtual ~MyApp();
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
void Setup (Ptr<Socket> socket, Address address, uint32_t packetSize,
|
|
|
|
|
uint32_t nPackets, DataRate dataRate);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
virtual void StartApplication (void);
|
|
|
|
|
virtual void StopApplication (void);
|
|
|
|
|
|
|
|
|
|
void ScheduleTx (void);
|
|
|
|
|
void SendPacket (void);
|
|
|
|
|
|
|
|
|
|
Ptr<Socket> m_socket;
|
|
|
|
|
Address m_peer;
|
|
|
|
|
uint32_t m_packetSize;
|
|
|
|
|
uint32_t m_nPackets;
|
|
|
|
|
DataRate m_dataRate;
|
|
|
|
|
EventId m_sendEvent;
|
|
|
|
|
bool m_running;
|
|
|
|
|
uint32_t m_packetsSent;
|
|
|
|
|
};
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can see that this class inherits from the @command{ns-3} @code{Application}
|
|
|
|
|
class. Take a look at @code{src/node/application.h} if you are interested in
|
2009-10-19 18:47:01 -07:00
|
|
|
what is inherited. The @code{MyApp} class is obligated to override the
|
2009-10-19 16:44:41 -07:00
|
|
|
@code{StartApplication} and @code{StopApplication} methods. These methods are
|
2010-03-05 11:14:53 -08:00
|
|
|
automatically called when @code{MyApp} is required to start and stop sending
|
|
|
|
|
data during the simulation.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
2010-03-05 11:14:53 -08:00
|
|
|
@subsubsection How Applications are Started and Stopped (optional)
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
It is worthwhile to spend a bit of time explaining how events actually get
|
2010-03-05 11:14:53 -08:00
|
|
|
started in the system. This is another fairly deep explanation, and can be
|
|
|
|
|
ignored if you aren't planning on venturing down into the guts of the system.
|
|
|
|
|
It is useful, however, in that the discussion touches on how some very important
|
|
|
|
|
parts of @code{ns-3} work and exposes some important idioms. If you are
|
|
|
|
|
planning on implementing new models, you probably want to understand this
|
|
|
|
|
section.
|
|
|
|
|
|
|
|
|
|
The most common way to start pumping events is to start an @code{Application}.
|
|
|
|
|
This is done as the result of the following (hopefully) familar lines of an
|
|
|
|
|
@command{ns-3} script:
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
ApplicationContainer apps = ...
|
|
|
|
|
apps.Start (Seconds (1.0));
|
|
|
|
|
apps.Stop (Seconds (10.0));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The application container code (see @code{src/helper/application-container.h} if
|
|
|
|
|
you are interested) loops through its contained applications and calls,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-05 11:14:53 -08:00
|
|
|
app->SetStartTime (startTime);
|
2009-10-19 16:44:41 -07:00
|
|
|
@end verbatim
|
|
|
|
|
|
2010-03-05 11:14:53 -08:00
|
|
|
as a result of the @code{apps.Start} call and
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-05 11:14:53 -08:00
|
|
|
app->SetStopTime (stopTime);
|
2009-10-19 16:44:41 -07:00
|
|
|
@end verbatim
|
|
|
|
|
|
2010-03-05 11:14:53 -08:00
|
|
|
as a result of the @code{apps.Stop} call.
|
|
|
|
|
|
|
|
|
|
The ulitmate result of these calls is that we want to have the simulator
|
|
|
|
|
automatically make calls into our @code{Applications} to tell them when to
|
|
|
|
|
start and stop. In the case of @code{MyApp}, it inherits from class
|
|
|
|
|
@code{Application} and overrides @code{StartApplication}, and
|
|
|
|
|
@code{StopApplication}. These are the functions that will be called by
|
|
|
|
|
the simulator at the appropriate time. In the case of @code{MyApp} you
|
|
|
|
|
will find that @code{MyApp::StartApplication} does the initial @code{Bind},
|
|
|
|
|
and @code{Connect} on the socket, and then starts data flowing by calling
|
|
|
|
|
@code{MyApp::SendPacket}. @code{MyApp::StopApplication} stops generating
|
|
|
|
|
packets by cancelling any pending send events and closing the socket.
|
|
|
|
|
|
|
|
|
|
One of the nice things about @command{ns-3} is that you can completely
|
|
|
|
|
ignore the implementation details of how your @code{Application} is
|
|
|
|
|
``automagically'' called by the simulator at the correct time. But since
|
|
|
|
|
we have already ventured deep into @command{ns-3} already, let's go for it.
|
|
|
|
|
|
|
|
|
|
If you look at @code{src/node/application.cc} you will find that the
|
|
|
|
|
@code{SetStartTime} method of an @code{Application} just sets the member
|
|
|
|
|
variable @code{m_startTime} and the @code{SetStopTime} method just sets
|
|
|
|
|
@code{m_stopTime}. From there, without some hints, the trail will probably
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
The key to picking up the trail again is to know that there is a global
|
|
|
|
|
list of all of the nodes in the system. Whenever you create a node in
|
|
|
|
|
a simulation, a pointer to that node is added to the global @code{NodeList}.
|
|
|
|
|
|
|
|
|
|
Take a look at @code{src/node/node-list.cc} and search for
|
|
|
|
|
@code{NodeList::Add}. The public static implementation calls into a private
|
|
|
|
|
implementation called @code{NodeListPriv::Add}. This is a relatively common
|
|
|
|
|
idom in @command{ns-3}. So, take a look at @code{NodeListPriv::Add}. There
|
|
|
|
|
you will find,
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-05 11:14:53 -08:00
|
|
|
Simulator::ScheduleWithContext (index, TimeStep (0), &Node::Start, node);
|
2009-10-19 16:44:41 -07:00
|
|
|
@end verbatim
|
|
|
|
|
|
2010-03-05 11:14:53 -08:00
|
|
|
This tells you that whenever a @code{Node} is created in a simulation, as
|
|
|
|
|
a side-effect, a call to that node's @code{Start} method is scheduled for
|
|
|
|
|
you that happens at time zero. Don't read too much into that name, yet.
|
|
|
|
|
It doesn't mean that the node is going to start doing anything, it can be
|
|
|
|
|
interpreted as an informational call into the @code{Node} telling it that
|
|
|
|
|
the simulation has started, not a call for action telling the @code{Node}
|
|
|
|
|
to start doing something.
|
|
|
|
|
|
|
|
|
|
So, @code{NodeList::Add} indirectly schedules a call to @code{Node::Start}
|
|
|
|
|
at time zero to advise a new node that the simulation has started. If you
|
|
|
|
|
look in @code{src/node/node.h} you will, however, not find a method called
|
|
|
|
|
@code{Node::Start}. It turns out that the @code{Start} method is inherited
|
|
|
|
|
from class @code{Object}. All objects in the system can be notified when
|
|
|
|
|
the simulation starts, and objects of class @code{Node} are just one kind
|
|
|
|
|
of those objects.
|
|
|
|
|
|
|
|
|
|
Take a look at @code{src/core/object.cc} next and search for @code{Object::Start}.
|
|
|
|
|
This code is not as straightforward as you might have expected since
|
|
|
|
|
@command{ns-3} @code{Objects} support aggregation. The code in
|
|
|
|
|
@code{Object::Start} then loops through all of the objects that have been
|
|
|
|
|
aggretated together and calls their @code{DoStart} method. This is another
|
|
|
|
|
idiom that is very common in @command{ns-3}. There is a public API method,
|
|
|
|
|
that stays constant across implementations, that calls a private implementation
|
|
|
|
|
method that is inherited and implemented by subclasses. The names are typically
|
|
|
|
|
something like @code{MethodName} for the public API and @code{DoMethodName} for
|
|
|
|
|
the private API.
|
|
|
|
|
|
|
|
|
|
This tells us that we should look for a @code{Node::DoStart} method in
|
|
|
|
|
@code{src/node/node.cc} for the method that will continue our trail. If you
|
|
|
|
|
locate the code, you will find a method that loops through all of the devices
|
|
|
|
|
in the node and then all of the applications in the node calling
|
|
|
|
|
@code{device->Start} and @code{application->Start} respectively.
|
|
|
|
|
|
|
|
|
|
You may already know that classes @code{Device} and @code{Application} both
|
|
|
|
|
inherit from class @code{Object} and so the next step will be to look at
|
|
|
|
|
what happens when @code{Application::DoStart} is called. Take a look at
|
|
|
|
|
@code{src/node/application.cc} and you will find:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
|
|
|
|
Application::DoStart (void)
|
|
|
|
|
{
|
|
|
|
|
m_startEvent = Simulator::Schedule (m_startTime, &Application::StartApplication, this);
|
|
|
|
|
if (m_stopTime != TimeStep (0))
|
|
|
|
|
{
|
|
|
|
|
m_stopEvent = Simulator::Schedule (m_stopTime, &Application::StopApplication, this);
|
|
|
|
|
}
|
|
|
|
|
Object::DoStart ();
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
2009-10-19 16:44:41 -07:00
|
|
|
|
2010-03-05 11:14:53 -08:00
|
|
|
Here, we finally come to the end of the trail. If you have kept it all straight,
|
|
|
|
|
when you implement an @command{ns-3} @code{Application}, your new application
|
|
|
|
|
inherits from class @code{Application}. You override the @code{StartApplication}
|
|
|
|
|
and @code{StopApplication} methods and provide mechanisms for starting and
|
|
|
|
|
stopping the flow of data out of your new @code{Application}. When a @code{Node}
|
|
|
|
|
is created in the simulation, it is added to a global @code{NodeList}. The act
|
|
|
|
|
of adding a node to this @code{NodeList} causes a simulator event to be scheduled
|
|
|
|
|
for time zero which calls the @code{Node::Start} method of the newly added
|
|
|
|
|
@code{Node} to be called when the simulation starts. Since a @code{Node} inherits
|
|
|
|
|
from @code{Object}, this calls the @code{Object::Start} method on the @code{Node}
|
|
|
|
|
which, in turn, calls the @code{DoStart} methods on all of the @code{Objects}
|
|
|
|
|
aggregated to the @code{Node} (think mobility models). Since the @code{Node}
|
|
|
|
|
@code{Object} has overridden @code{DoStart}, that method is called when the
|
|
|
|
|
simulation starts. The @code{Node::DoStart} method calls the @code{Start} methods
|
|
|
|
|
of all of the @code{Applications} on the node. Since @code{Applications} are
|
|
|
|
|
also @code{Objects}, this causes @code{Application::DoStart} to be called. When
|
|
|
|
|
@code{Application::DoStart} is called, it schedules events for the
|
|
|
|
|
@code{StartApplication} and @code{StopApplication} calls on the @code{Application}.
|
|
|
|
|
These calls are designed to start and stop the flow of data from the
|
|
|
|
|
@code{Application}
|
|
|
|
|
|
|
|
|
|
This has been another fairly long journey, but it only has to be made once, and
|
|
|
|
|
you now understand another very deep piece of @command{ns-3}.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
2009-10-19 17:35:02 -07:00
|
|
|
@subsubsection The MyApp Application
|
2009-10-19 16:44:41 -07:00
|
|
|
|
2009-10-19 17:35:02 -07:00
|
|
|
The @code{MyApp} @code{Application} needs a constructor and a destructor,
|
2009-10-19 16:44:41 -07:00
|
|
|
of course:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2009-10-19 17:35:02 -07:00
|
|
|
MyApp::MyApp ()
|
2009-10-19 16:44:41 -07:00
|
|
|
: m_socket (0),
|
|
|
|
|
m_peer (),
|
|
|
|
|
m_packetSize (0),
|
|
|
|
|
m_nPackets (0),
|
|
|
|
|
m_dataRate (0),
|
|
|
|
|
m_sendEvent (),
|
|
|
|
|
m_running (false),
|
|
|
|
|
m_packetsSent (0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-19 17:35:02 -07:00
|
|
|
MyApp::~MyApp()
|
2009-10-19 16:44:41 -07:00
|
|
|
{
|
|
|
|
|
m_socket = 0;
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The existence of the next bit of code is the whole reason why we wrote this
|
|
|
|
|
@code{Application} in the first place.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
2009-10-19 17:35:02 -07:00
|
|
|
MyApp::Setup (Ptr<Socket> socket, Address address, uint32_t packetSize,
|
2009-10-19 16:44:41 -07:00
|
|
|
uint32_t nPackets, DataRate dataRate)
|
|
|
|
|
{
|
|
|
|
|
m_socket = socket;
|
|
|
|
|
m_peer = address;
|
|
|
|
|
m_packetSize = packetSize;
|
|
|
|
|
m_nPackets = nPackets;
|
|
|
|
|
m_dataRate = dataRate;
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This code should be pretty self-explanatory. We are just initializing member
|
|
|
|
|
variables. The important one from the perspective of tracing is the
|
|
|
|
|
@code{Ptr<Socket> socket} which we needed to provide to the application
|
|
|
|
|
during configuration time. Recall that we are going to create the @code{Socket}
|
|
|
|
|
as a @code{TcpSocket} (which is implemented by @code{TcpSocketImpl}) and hook
|
|
|
|
|
its ``CongestionWindow'' trace source before passing it to the @code{Setup}
|
|
|
|
|
method.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
2009-10-19 17:35:02 -07:00
|
|
|
MyApp::StartApplication (void)
|
2009-10-19 16:44:41 -07:00
|
|
|
{
|
|
|
|
|
m_running = true;
|
|
|
|
|
m_packetsSent = 0;
|
|
|
|
|
m_socket->Bind ();
|
|
|
|
|
m_socket->Connect (m_peer);
|
|
|
|
|
SendPacket ();
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The above code is the overridden implementation @code{Application::StartApplication}
|
|
|
|
|
that will be automatically called by the simulator to start our @code{Application}
|
2010-03-05 11:14:53 -08:00
|
|
|
running at the appropriate time. You can see that it does a @code{Socket} @code{Bind}
|
|
|
|
|
operation. If you are familiar with Berkeley Sockets this shouldn't be a surprise.
|
|
|
|
|
It performs the required work on the local side of the connection just as you might
|
|
|
|
|
expect. The following @code{Connect} will do what is required to establish a connection
|
2009-10-19 18:47:01 -07:00
|
|
|
with the TCP at @code{Address} m_peer. It should now be clear why we need to defer
|
|
|
|
|
a lot of this to simulation time, since the @code{Connect} is going to need a fully
|
2009-10-20 10:45:12 -07:00
|
|
|
functioning network to complete. After the @code{Connect}, the @code{Application}
|
2009-10-19 18:47:01 -07:00
|
|
|
then starts creating simulation events by calling @code{SendPacket}.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
The next bit of code explains to the @code{Application} how to stop creating
|
|
|
|
|
simulation events.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
2009-10-19 17:35:02 -07:00
|
|
|
MyApp::StopApplication (void)
|
2009-10-19 16:44:41 -07:00
|
|
|
{
|
|
|
|
|
m_running = false;
|
|
|
|
|
|
|
|
|
|
if (m_sendEvent.IsRunning ())
|
|
|
|
|
{
|
|
|
|
|
Simulator::Cancel (m_sendEvent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_socket)
|
|
|
|
|
{
|
|
|
|
|
m_socket->Close ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Every time a simulation event is scheduled, an @code{Event} is created. If the
|
|
|
|
|
@code{Event} is pending execution or executing, its method @code{IsRunning} will
|
|
|
|
|
return @code{true}. In this code, if @code{IsRunning()} returns true, we
|
|
|
|
|
@code{Cancel} the event which removes it from the simulator event queue. By
|
|
|
|
|
doing this, we break the chain of events that the @code{Application} is using to
|
2009-10-19 18:47:01 -07:00
|
|
|
keep sending its @code{Packets} and the @code{Application} goes quiet. After we
|
|
|
|
|
quiet the @code{Application} we @code{Close} the socket which tears down the TCP
|
|
|
|
|
connection.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
The socket is actually deleted in the destructor when the @code{m_socket = 0} is
|
|
|
|
|
executed. This removes the last reference to the underlying Ptr<Socket> which
|
|
|
|
|
causes the destructor of that Object to be called.
|
|
|
|
|
|
|
|
|
|
Recall that @code{StartApplication} called @code{SendPacket} to start the
|
|
|
|
|
chain of events that describes the @code{Application} behavior.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
2009-10-19 17:35:02 -07:00
|
|
|
MyApp::SendPacket (void)
|
2009-10-19 16:44:41 -07:00
|
|
|
{
|
|
|
|
|
Ptr<Packet> packet = Create<Packet> (m_packetSize);
|
|
|
|
|
m_socket->Send (packet);
|
|
|
|
|
|
|
|
|
|
if (++m_packetsSent < m_nPackets)
|
|
|
|
|
{
|
|
|
|
|
ScheduleTx ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Here, you see that @code{SendPacket} does just that. It creates a @code{Packet}
|
|
|
|
|
and then does a @code{Send} which, if you know Berkeley Sockets, is probably
|
|
|
|
|
just what you expected to see.
|
|
|
|
|
|
|
|
|
|
It is the responsibility of the @code{Application} to keep scheduling the
|
|
|
|
|
chain of events, so the next lines call @code{ScheduleTx} to schedule another
|
|
|
|
|
transmit event (a @code{SendPacket}) until the @code{Application} decides it
|
|
|
|
|
has sent enough.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void
|
2009-10-19 17:35:02 -07:00
|
|
|
MyApp::ScheduleTx (void)
|
2009-10-19 16:44:41 -07:00
|
|
|
{
|
|
|
|
|
if (m_running)
|
|
|
|
|
{
|
|
|
|
|
Time tNext (Seconds (m_packetSize * 8 / static_cast<double> (m_dataRate.GetBitRate ())));
|
2009-10-19 17:35:02 -07:00
|
|
|
m_sendEvent = Simulator::Schedule (tNext, &MyApp::SendPacket, this);
|
2009-10-19 16:44:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Here, you see that @code{ScheduleTx} does exactly that. If the @code{Applciation}
|
2009-10-20 10:45:12 -07:00
|
|
|
is running (if @code{StopApplication} has not been called) it will schedule a
|
2009-10-19 16:44:41 -07:00
|
|
|
new event, which calls @code{SendPacket} again. The alert reader will spot
|
|
|
|
|
something that also trips up new users. The data rate of an @code{Application} is
|
|
|
|
|
just that. It has nothing to do with the data rate of an underlying @code{Channel}.
|
|
|
|
|
This is the rate at which the @code{Application} produces bits. It does not take
|
|
|
|
|
into account any overhead for the various protocols or channels that it uses to
|
|
|
|
|
transport the data. If you set the data rate of an @code{Application} to the same
|
|
|
|
|
data rate as your underlying @code{Channel} you will eventually get a buffer overflow.
|
|
|
|
|
|
|
|
|
|
@subsubsection The Trace Sinks
|
|
|
|
|
|
2009-10-19 18:47:01 -07:00
|
|
|
The whole point of this exercise is to get trace callbacks from TCP indicating the
|
|
|
|
|
congestion window has been updated. The next piece of code implements the
|
|
|
|
|
corresponding trace sink:
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
static void
|
|
|
|
|
CwndChange (uint32_t oldCwnd, uint32_t newCwnd)
|
|
|
|
|
{
|
|
|
|
|
NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << ``\t'' << newCwnd);
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This should be very familiar to you now, so we won't dwell on the details. This
|
|
|
|
|
function just logs the current simulation time and the new value of the
|
|
|
|
|
congestion window every time it is changed. You can probably imagine that you
|
|
|
|
|
could load the resulting output into a graphics program (gnuplot or Excel) and
|
|
|
|
|
immediately see a nice graph of the congestion window behavior over time.
|
|
|
|
|
|
2009-10-20 10:45:12 -07:00
|
|
|
We added a new trace sink to show where packets are dropped. We are going to
|
|
|
|
|
add an error model to this code also, so we wanted to demonstrate this working.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
static void
|
|
|
|
|
RxDrop (Ptr<const Packet> p)
|
|
|
|
|
{
|
|
|
|
|
NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ());
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This trace sink will be connected to the ``PhyRxDrop'' trace source of the
|
2009-10-19 18:47:01 -07:00
|
|
|
point-to-point NetDevice. This trace source fires when a packet is dropped
|
|
|
|
|
by the physical layer of a @code{NetDevice}. If you take a small detour to the
|
|
|
|
|
source (@code{src/devices/point-to-point/point-to-point-net-device.cc}) you will
|
|
|
|
|
see that this trace source refers to @code{PointToPointNetDevice::m_phyRxDropTrace}.
|
2009-10-19 16:44:41 -07:00
|
|
|
If you then look in @code{src/devices/point-to-point/point-to-point-net-device.h}
|
|
|
|
|
for this member variable, you will find that it is declared as a
|
|
|
|
|
@code{TracedCallback<Ptr<const Packet> >}. This should tell you that the
|
|
|
|
|
callback target should be a function that returns void and takes a single
|
|
|
|
|
parameter which is a @code{Ptr<const Packet>} -- just what we have above.
|
|
|
|
|
|
|
|
|
|
@subsubsection The Main Program
|
|
|
|
|
|
|
|
|
|
The following code should be very familiar to you by now:
|
|
|
|
|
|
2009-10-19 18:47:01 -07:00
|
|
|
@verbatim
|
2009-10-19 16:44:41 -07:00
|
|
|
int
|
|
|
|
|
main (int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
NodeContainer nodes;
|
|
|
|
|
nodes.Create (2);
|
|
|
|
|
|
|
|
|
|
PointToPointHelper pointToPoint;
|
|
|
|
|
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
|
|
|
|
|
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
|
|
|
|
|
|
|
|
|
|
NetDeviceContainer devices;
|
|
|
|
|
devices = pointToPoint.Install (nodes);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This creates two nodes with a point-to-point channel between them, just as
|
|
|
|
|
shown in the illustration at the start of the file.
|
|
|
|
|
|
2009-10-20 10:45:12 -07:00
|
|
|
The next few lines of code show something new. If we trace a connection that
|
2009-10-19 16:44:41 -07:00
|
|
|
behaves perfectly, we will end up with a monotonically increasing congestion
|
|
|
|
|
window. To see any interesting behavior, we really want to introduce link
|
|
|
|
|
errors which will drop packets, cause duplicate ACKs and trigger the more
|
|
|
|
|
interesting behaviors of the congestion window.
|
|
|
|
|
|
|
|
|
|
@command{ns-3} provides @code{ErrorModel} objects which can be attached to
|
|
|
|
|
@code{Channels}. We are using the @code{RateErrorModel} which allows us
|
|
|
|
|
to introduce errors into a @code{Channel} at a given @emph{rate}.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<RateErrorModel> em = CreateObjectWithAttributes<RateErrorModel> (
|
|
|
|
|
"RanVar", RandomVariableValue (UniformVariable (0., 1.)),
|
2009-10-19 18:47:01 -07:00
|
|
|
"ErrorRate", DoubleValue (0.00001));
|
2009-10-20 10:45:12 -07:00
|
|
|
devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em));
|
2009-10-19 16:44:41 -07:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The above code instantiates a @code{RateErrorModel} Object. Rather than
|
|
|
|
|
using the two-step process of instantiating it and then setting Attributes,
|
|
|
|
|
we use the convenience function @code{CreateObjectWithAttributes} which
|
|
|
|
|
allows us to do both at the same time. We set the ``RanVar''
|
|
|
|
|
@code{Attribute} to a random variable that generates a uniform distribution
|
2009-10-19 18:47:01 -07:00
|
|
|
from 0 to 1. We also set the ``ErrorRate'' @code{Attribute}.
|
2009-10-19 16:44:41 -07:00
|
|
|
We then set the resulting instantiated @code{RateErrorModel} as the error
|
2009-10-19 18:47:01 -07:00
|
|
|
model used by the point-to-point @code{NetDevice}. This will give us some
|
|
|
|
|
retransmissions and make our plot a little more interesting.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
InternetStackHelper stack;
|
|
|
|
|
stack.Install (nodes);
|
|
|
|
|
|
|
|
|
|
Ipv4AddressHelper address;
|
|
|
|
|
address.SetBase (``10.1.1.0'', ``255.255.255.252'');
|
|
|
|
|
Ipv4InterfaceContainer interfaces = address.Assign (devices);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The above code should be familiar. It installs internet stacks on our two
|
|
|
|
|
nodes and creates interfaces and assigns IP addresses for the point-to-point
|
|
|
|
|
devices.
|
|
|
|
|
|
|
|
|
|
Since we are using TCP, we need something on the destination node to receive
|
|
|
|
|
TCP connections and data. The @code{PacketSink} @code{Application} is commonly
|
|
|
|
|
used in @command{ns-3} for that purpose.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
uint16_t sinkPort = 8080;
|
|
|
|
|
Address sinkAddress (InetSocketAddress(interfaces.GetAddress (1), sinkPort));
|
|
|
|
|
PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory",
|
|
|
|
|
InetSocketAddress (Ipv4Address::GetAny (), sinkPort));
|
|
|
|
|
ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (1));
|
|
|
|
|
sinkApps.Start (Seconds (0.));
|
|
|
|
|
sinkApps.Stop (Seconds (20.));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This should all be familiar, with the exception of,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory",
|
|
|
|
|
InetSocketAddress (Ipv4Address::GetAny (), sinkPort));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This code instantiates a @code{PacketSinkHelper} and tells it to create sockets
|
|
|
|
|
using the class @code{ns3::TcpSocketFactory}. This class implements a design
|
|
|
|
|
pattern called ``object factory'' which is a commonly used mechanism for
|
|
|
|
|
specifying a class used to create objects in an abstract way. Here, instead of
|
|
|
|
|
having to create the objects themselves, you provide the @code{ PacketSinkHelper}
|
|
|
|
|
a string that specifies a @code{TypeId} string used to create an object which
|
|
|
|
|
can then be used, in turn, to create instances of the Objects created by the
|
|
|
|
|
factory.
|
|
|
|
|
|
|
|
|
|
The remaining parameter tells the @code{Application} which address and port it
|
2009-10-19 18:47:01 -07:00
|
|
|
should @code{Bind} to.
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
The next two lines of code will create the socket and connect the trace source.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<Socket> ns3TcpSocket = Socket::CreateSocket (nodes.Get (0),
|
|
|
|
|
TcpSocketFactory::GetTypeId ());
|
|
|
|
|
ns3TcpSocket->TraceConnectWithoutContext (``CongestionWindow'',
|
|
|
|
|
MakeCallback (&CwndChange));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2009-10-19 18:47:01 -07:00
|
|
|
The first statement calls the static member function @code{Socket::CreateSocket}
|
2009-10-19 16:44:41 -07:00
|
|
|
and provides a @code{Node} and an explicit @code{TypeId} for the object factory
|
|
|
|
|
used to create the socket. This is a slightly lower level call than the
|
|
|
|
|
@code{PacketSinkHelper} call above, and uses an explicit C++ type instead of
|
|
|
|
|
one referred to by a string. Otherwise, it is conceptually the same thing.
|
|
|
|
|
|
|
|
|
|
Once the @code{TcpSocket} is created and attached to the @code{Node}, we can
|
|
|
|
|
use @code{TraceConnectWithoutContext} to connect the CongestionWindow trace
|
|
|
|
|
source to our trace sink.
|
|
|
|
|
|
|
|
|
|
Recall that we coded an @code{Application} so we could take that @code{Socket}
|
|
|
|
|
we just made (during configuration time) and use it in simulation time. We now
|
|
|
|
|
have to instantiate that @code{Application}. We didn't go to any trouble to
|
|
|
|
|
create a helper to manage the @code{Application} so we are going to have to
|
2010-01-27 17:21:36 -08:00
|
|
|
create and install it ``manually''. This is actually quite easy:
|
2009-10-19 16:44:41 -07:00
|
|
|
|
|
|
|
|
@verbatim
|
2009-10-19 17:35:02 -07:00
|
|
|
Ptr<MyApp> app = CreateObject<MyApp> ();
|
2009-10-19 16:44:41 -07:00
|
|
|
app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps"));
|
|
|
|
|
nodes.Get (0)->AddApplication (app);
|
|
|
|
|
app->Start (Seconds (1.));
|
|
|
|
|
app->Stop (Seconds (20.));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2009-10-19 17:35:02 -07:00
|
|
|
The first line creates an @code{Object} of type @code{MyApp} -- our
|
2009-10-19 16:44:41 -07:00
|
|
|
@code{Application}. The second line tells the @code{Application} what
|
|
|
|
|
@code{Socket} to use, what address to connect to, how much data to send
|
|
|
|
|
at each send event, how many send events to generate and the rate at which
|
|
|
|
|
to produce data from those events.
|
|
|
|
|
|
2009-10-19 17:35:02 -07:00
|
|
|
Next, we manually add the @code{MyApp Application} to the source node
|
2009-10-19 16:44:41 -07:00
|
|
|
and explicitly call the @code{Start} and @code{Stop} methods on the
|
|
|
|
|
@code{Application} to tell it when to start and stop doing its thing.
|
|
|
|
|
|
|
|
|
|
We need to actually do the connect from the receiver point-to-point @code{NetDevice}
|
|
|
|
|
to our callback now.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeCallback (&RxDrop));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
It should now be obvious that we are getting a reference to the receiving
|
|
|
|
|
@code{Node NetDevice} from its container and connecting the trace source defined
|
|
|
|
|
by the attribute ``PhyRxDrop'' on that device to the trace sink @code{RxDrop}.
|
|
|
|
|
|
|
|
|
|
Finally, we tell the simulator to override any @code{Applications} and just
|
|
|
|
|
stop processing events at 20 seconds into the simulation.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Simulator::Stop (Seconds(20));
|
|
|
|
|
Simulator::Run ();
|
|
|
|
|
Simulator::Destroy ();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Recall that as soon as @code{Simulator::Run} is called, configuration time
|
|
|
|
|
ends, and simulation time begins. All of the work we orchestrated by
|
|
|
|
|
creating the @code{Application} and teaching it how to connect and send
|
|
|
|
|
data actually happens during this function call.
|
|
|
|
|
|
|
|
|
|
As soon as @code{Simulator::Run} returns, the simulation is complete and
|
|
|
|
|
we enter the teardown phase. In this case, @code{Simulator::Destroy} takes
|
|
|
|
|
care of the gory details and we just return a success code after it completes.
|
|
|
|
|
|
|
|
|
|
@subsection Running fifth.cc
|
|
|
|
|
|
|
|
|
|
Since we have provided the file @code{fifth.cc} for you, if you have built
|
|
|
|
|
your distribution (in debug mode since it uses NS_LOG -- recall that optimized
|
|
|
|
|
builds optimize out NS_LOGs) it will be waiting for you to run.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
./waf --run fifth
|
2009-10-20 10:45:12 -07:00
|
|
|
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build
|
2009-10-19 16:44:41 -07:00
|
|
|
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build'
|
2009-10-20 10:45:12 -07:00
|
|
|
'build' finished successfully (0.684s)
|
|
|
|
|
1.20919 1072
|
|
|
|
|
1.21511 1608
|
|
|
|
|
1.22103 2144
|
2009-10-19 16:44:41 -07:00
|
|
|
...
|
2009-10-20 10:45:12 -07:00
|
|
|
1.2471 8040
|
|
|
|
|
1.24895 8576
|
|
|
|
|
1.2508 9112
|
|
|
|
|
RxDrop at 1.25151
|
2009-10-19 16:44:41 -07:00
|
|
|
...
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can probably see immediately a downside of using prints of any kind in your
|
|
|
|
|
traces. We get those extraneous waf messages printed all over our interesting
|
|
|
|
|
information along with those RxDrop messages. We will remedy that soon, but I'm
|
|
|
|
|
sure you can't wait to see the results of all of this work. Let's redirect that
|
|
|
|
|
output to a file called @code{cwnd.dat}:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
./waf --run fifth > cwnd.dat 2>&1
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Now edit up ``cwnd.dat'' in your favorite editor and remove the waf build status
|
|
|
|
|
and drop lines, leaving only the traced data (you could also comment out the
|
|
|
|
|
@code{TraceConnectWithoutContext("PhyRxDrop", MakeCallback (&RxDrop));} in the
|
|
|
|
|
script to get rid of the drop prints just as easily.
|
|
|
|
|
|
|
|
|
|
You can now run gnuplot (if you have it installed) and tell it to generate some
|
|
|
|
|
pretty pictures:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2009-10-20 10:45:12 -07:00
|
|
|
gnuplot> set terminal png size 640,480
|
2009-10-19 16:44:41 -07:00
|
|
|
gnuplot> set output "cwnd.png"
|
|
|
|
|
gnuplot> plot "cwnd.dat" using 1:2 title 'Congestion Window' with linespoints
|
|
|
|
|
gnuplot> exit
|
|
|
|
|
@end verbatim
|
2009-10-08 13:59:26 -07:00
|
|
|
|
2009-10-19 16:44:41 -07:00
|
|
|
You should now have a graph of the congestion window versus time sitting in the
|
|
|
|
|
file ``cwnd.png'' in all of its glory, that looks like:
|
2009-10-08 13:59:26 -07:00
|
|
|
|
2009-10-19 16:44:41 -07:00
|
|
|
@sp 1
|
|
|
|
|
@center @image{figures/cwnd,,,,png}
|
2009-10-08 13:59:26 -07:00
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@subsection Using Mid-Level Helpers
|
2010-01-25 16:09:07 -08:00
|
|
|
|
|
|
|
|
In the previous section, we showed how to hook a trace source and get hopefully
|
|
|
|
|
interesting information out of a simulation. Perhaps you will recall that we
|
|
|
|
|
called logging to the standard output using @code{std::cout} a ``Blunt Instrument''
|
|
|
|
|
much earlier in this chapter. We also wrote about how it was a problem having
|
|
|
|
|
to parse the log output in order to isolate interesting information. It may
|
|
|
|
|
have occurred to you that we just spent a lot of time implementing an example
|
|
|
|
|
that exhibits all of the problems we purport to fix with the @code{ns-3} tracing
|
|
|
|
|
system! You would be correct. But, bear with us. We're not done yet.
|
|
|
|
|
|
|
|
|
|
One of the most important things we want to do is to is to have the ability to
|
|
|
|
|
easily control the amount of output coming out of the simulation; and we also
|
|
|
|
|
want to save those data to a file so we can refer back to it later. We can use
|
|
|
|
|
the mid-level trace helpers provided in @code{ns-3} to do just that and complete
|
|
|
|
|
the picture.
|
|
|
|
|
|
2010-01-25 17:07:33 -08:00
|
|
|
We provide a script that writes the cwnd change and drop events developed in
|
|
|
|
|
the example @code{fifth.cc } to disk in separate files. The cwnd changes are
|
|
|
|
|
stored as a tab-separated ASCII file and the drop events are stored in a pcap
|
|
|
|
|
file. The changes to make this happen are quite small.
|
2010-01-25 16:09:07 -08:00
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@subsubsection A sixth.cc Walkthrough
|
2010-01-25 16:09:07 -08:00
|
|
|
|
2010-01-25 17:07:33 -08:00
|
|
|
Let's take a look at the changes required to go from @code{fifth.cc} to
|
|
|
|
|
@code{sixth.cc}. Open @code{examples/tutorial/fifth.cc} in your favorite
|
2010-01-28 17:21:04 -08:00
|
|
|
editor. You can see the first change by searching for CwndChange. You will
|
|
|
|
|
find that we have changed the signatures for the trace sinks and have added
|
|
|
|
|
a single line to each sink that writes the traced information to a stream
|
|
|
|
|
representing a file.
|
2010-01-25 17:07:33 -08:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
static void
|
2010-02-23 10:30:38 -08:00
|
|
|
CwndChange (Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd)
|
2010-01-25 17:07:33 -08:00
|
|
|
{
|
|
|
|
|
NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd);
|
|
|
|
|
*stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-02-23 10:04:31 -08:00
|
|
|
RxDrop (Ptr<PcapFileWrapper> file, Ptr<const Packet> p)
|
2010-01-25 17:07:33 -08:00
|
|
|
{
|
|
|
|
|
NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ());
|
|
|
|
|
file->Write(Simulator::Now(), p);
|
|
|
|
|
}
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
We have added a ``stream'' parameter to the @code{CwndChange} trace sink.
|
2010-02-12 17:31:11 -08:00
|
|
|
This is an object that holds (keeps safely alive) a C++ output stream. It
|
|
|
|
|
turns out that this is a very simple object, but one that manages lifetime
|
|
|
|
|
issues for the stream and solves a problem that even experienced C++ users
|
|
|
|
|
run into. It turns out that the copy constructor for ostream is marked
|
|
|
|
|
private. This means that ostreams do not obey value semantics and cannot
|
|
|
|
|
be used in any mechanism that requires the stream to be copied. This includes
|
|
|
|
|
the @command{ns-3} callback system, which as you may recall, requires objects
|
|
|
|
|
that obey value semantics. Further notice that we have added the following
|
|
|
|
|
line in the @code{CwndChange} trace sink implementation:
|
2010-01-25 17:07:33 -08:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
*stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would be very familiar code if you replaced @code{*stream->GetStream ()}
|
|
|
|
|
with @code{std::cout}, as in:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
std::cout << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2010-02-23 10:30:38 -08:00
|
|
|
This illustrates that the @code{Ptr<OutputStreamWrapper>} is really just
|
2010-01-25 17:07:33 -08:00
|
|
|
carrying around a @code{std::ofstream} for you, and you can use it here like
|
|
|
|
|
any other output stream.
|
|
|
|
|
|
|
|
|
|
A similar situation happens in @code{RxDrop} except that the object being
|
2010-02-23 10:04:31 -08:00
|
|
|
passed around (a @code{Ptr<PcapFileWrapper>}) represents a pcap file. There
|
2010-01-25 17:07:33 -08:00
|
|
|
is a one-liner in the trace sink to write a timestamp and the contents of the
|
|
|
|
|
packet being dropped to the pcap file:
|
|
|
|
|
|
|
|
|
|
@end verbatim
|
|
|
|
|
file->Write(Simulator::Now(), p);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Of course, if we have objects representing the two files, we need to create
|
|
|
|
|
them somewhere and also cause them to be passed to the trace sinks. If you
|
|
|
|
|
look in the @code{main} function, you will find new code to do just that:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
AsciiTraceHelper asciiTraceHelper;
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd");
|
2010-01-25 17:07:33 -08:00
|
|
|
ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream));
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
PcapHelper pcapHelper;
|
2010-02-23 10:04:31 -08:00
|
|
|
Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", "w", PcapHelper::DLT_PPP);
|
2010-01-25 17:07:33 -08:00
|
|
|
devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
In the first section of the code snippet above, we are creating the ASCII
|
|
|
|
|
trace file, creating an object responsible for managing it and using a
|
|
|
|
|
variant of the callback creation function to arrange for the object to be
|
2010-01-28 17:21:04 -08:00
|
|
|
passed to the sink. Our ASCII trace helpers provide a rich set of
|
|
|
|
|
functions to make using text (ASCII) files easy. We are just going to
|
|
|
|
|
illustrate the use of the file stream creation function here.
|
2010-01-25 17:07:33 -08:00
|
|
|
|
|
|
|
|
The @code{CreateFileStream{}} function is basically going to instantiate
|
|
|
|
|
a std::ofstream object and create a new file (or truncate an existing file).
|
|
|
|
|
This ofstream is packaged up in an @code{ns-3} object for lifetime management
|
|
|
|
|
and copy constructor issue resolution.
|
|
|
|
|
|
|
|
|
|
We then take this @code{ns-3} object representing the file and pass it to
|
|
|
|
|
@code{MakeBoundCallback()}. This function creates a callback just like
|
|
|
|
|
@code{MakeCallback()}, but it ``binds'' a new value to the callback. This
|
2010-01-28 17:21:04 -08:00
|
|
|
value is added to the callback before it is called.
|
|
|
|
|
|
|
|
|
|
Essentially, @code{MakeBoundCallback(&CwndChange, stream)} causes the trace
|
|
|
|
|
source to add the additional ``stream'' parameter to the front of the formal
|
|
|
|
|
parameter list before invoking the callback. This changes the required
|
|
|
|
|
signature of the @code{CwndChange} sink to match the one shown above, which
|
2010-02-23 10:30:38 -08:00
|
|
|
includes the ``extra'' parameter @code{Ptr<OutputStreamWrapper> stream}.
|
2010-01-25 17:07:33 -08:00
|
|
|
|
|
|
|
|
In the second section of code in the snippet above, we instantiate a
|
|
|
|
|
@code{PcapHelper} to do the same thing for our pcap trace file that we did
|
|
|
|
|
with the @code{AsciiTraceHelper}. The line of code,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-02-23 10:04:31 -08:00
|
|
|
Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", "w", PcapHelper::DLT_PPP);
|
2010-01-25 17:07:33 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
creates a pcap file named ``sixth.pcap'' with file mode ``w''. This means that
|
|
|
|
|
the new file is to truncated if an existing file with that name is found. The
|
|
|
|
|
final parameter is the ``data link type'' of the new pcap file. These are
|
|
|
|
|
the same as the pcap library data link types defined in @code{bpf.h} if you are
|
|
|
|
|
familar with pcap. In this case, @code{DLT_PPP} indicates that the pcap file
|
|
|
|
|
is going to contain packets prefixed with point to point headers. This is true
|
|
|
|
|
since the packets are coming from our point-to-point device driver. Other
|
|
|
|
|
common data link types are DLT_EN10MB (10 MB Ethernet) appropriate for csma
|
2010-01-28 17:21:04 -08:00
|
|
|
devices and DLT_IEEE802_11 (IEEE 802.11) appropriate for wifi devices. These
|
|
|
|
|
are defined in @code{src/helper/trace-helper.h"} if you are interested in seeing
|
|
|
|
|
the list. The entries in the list match those in @code{bpf.h} but we duplicate
|
|
|
|
|
them to avoid a pcap source dependence.
|
2010-01-25 16:09:07 -08:00
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
A @code{ns-3} object representing the pcap file is returned from @code{CreateFile}
|
|
|
|
|
and used in a bound callback exactly as it was in the ascii case.
|
2010-01-25 17:07:33 -08:00
|
|
|
|
2010-02-12 17:31:11 -08:00
|
|
|
An important detour: It is important to notice that even though both of these
|
|
|
|
|
objects are declared in very similar ways,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-02-23 10:04:31 -08:00
|
|
|
Ptr<PcapFileWrapper> file ...
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream ...
|
2010-02-12 17:31:11 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The underlying objects are entirely different. For example, the
|
2010-02-23 10:04:31 -08:00
|
|
|
Ptr<PcapFileWrapper> is a smart pointer to an @command{ns-3} Object that is a
|
2010-02-12 17:31:11 -08:00
|
|
|
fairly heaviweight thing that supports @code{Attributes} and is integrated into
|
2010-02-23 10:30:38 -08:00
|
|
|
the config system. The Ptr<OutputStreamWrapper>, on the other hand, is a smart
|
2010-02-12 17:31:11 -08:00
|
|
|
pointer to a reference counted object that is a very lightweight thing.
|
|
|
|
|
Remember to always look at the object you are referencing before making any
|
|
|
|
|
assumptions about the ``powers'' that object may have.
|
|
|
|
|
|
|
|
|
|
For example, take a look at @code{src/common/pcap-file-object.h} in the
|
|
|
|
|
distribution and notice,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-02-23 10:04:31 -08:00
|
|
|
class PcapFileWrapper : public Object
|
2010-02-12 17:31:11 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
2010-02-23 10:04:31 -08:00
|
|
|
that class @code{PcapFileWrapper} is an @command{ns-3} Object by virtue of
|
2010-02-23 10:30:38 -08:00
|
|
|
its inheritance. Then look at @code{src/common/output-stream-wrapper.h} and
|
2010-02-12 17:31:11 -08:00
|
|
|
notice,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-02-23 10:30:38 -08:00
|
|
|
class OutputStreamWrapper : public SimpleRefCount<OutputStreamWrapper>
|
2010-02-12 17:31:11 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
that this object is not an @command{ns-3} Object at all, it is ``merely'' a
|
|
|
|
|
C++ object that happens to support intrusive reference counting.
|
|
|
|
|
|
|
|
|
|
The point here is that just because you read Ptr<something> it does not necessarily
|
|
|
|
|
mean that ``something'' is an @command{ns-3} Object on which you can hang @command{ns-3}
|
|
|
|
|
@code{Attributes}, for example.
|
|
|
|
|
|
|
|
|
|
Now, back to the example. If you now build and run this example,
|
2010-01-25 17:07:33 -08:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
./waf --run sixth
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
you will see the same messages appear as when you ran ``fifth'', but two new
|
|
|
|
|
files will appear in the top-level directory of your @code{ns-3} distribution.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
sixth.cwnd sixth.pcap
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Since ``sixth.cwnd'' is an ASCII text file, you can view it with @code{cat}
|
|
|
|
|
or your favorite file viewer.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
1.20919 536 1072
|
|
|
|
|
1.21511 1072 1608
|
|
|
|
|
...
|
|
|
|
|
9.30922 8893 8925
|
|
|
|
|
9.31754 8925 8957
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You have a tab separated file with a timestamp, an old congestion window and a
|
|
|
|
|
new congestion window suitable for directly importing into your plot program.
|
|
|
|
|
There are no extraneous prints in the file, no parsing or editing is required.
|
|
|
|
|
|
|
|
|
|
Since ``sixth.pcap'' is a pcap file, you can fiew it with @code{tcpdump}.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
reading from file ../../sixth.pcap, link-type PPP (PPP)
|
|
|
|
|
1.251507 IP 10.1.1.1.49153 > 10.1.1.2.8080: . 17689:18225(536) ack 1 win 65535
|
|
|
|
|
1.411478 IP 10.1.1.1.49153 > 10.1.1.2.8080: . 33808:34312(504) ack 1 win 65535
|
|
|
|
|
...
|
|
|
|
|
7.393557 IP 10.1.1.1.49153 > 10.1.1.2.8080: . 781568:782072(504) ack 1 win 65535
|
|
|
|
|
8.141483 IP 10.1.1.1.49153 > 10.1.1.2.8080: . 874632:875168(536) ack 1 win 65535
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You have a pcap file with the packets that were dropped in the simulation. There
|
|
|
|
|
are no other packets present in the file and there is nothing else present to
|
|
|
|
|
make life difficult.
|
|
|
|
|
|
|
|
|
|
It's been a long journey, but we are now at a point where we can appreciate the
|
|
|
|
|
@code{ns-3} tracing system. We have pulled important events out of the middle
|
|
|
|
|
of a TCP implementation and a device driver. We stored those events directly in
|
|
|
|
|
files usable with commonly known tools. We did this without modifying any of the
|
|
|
|
|
core code involved, and we did this in only 18 lines of code:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
static void
|
2010-02-23 10:30:38 -08:00
|
|
|
CwndChange (Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd)
|
2010-01-25 17:07:33 -08:00
|
|
|
{
|
|
|
|
|
NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd);
|
|
|
|
|
*stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
AsciiTraceHelper asciiTraceHelper;
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd");
|
2010-01-25 17:07:33 -08:00
|
|
|
ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream));
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
static void
|
2010-02-23 10:04:31 -08:00
|
|
|
RxDrop (Ptr<PcapFileWrapper> file, Ptr<const Packet> p)
|
2010-01-25 17:07:33 -08:00
|
|
|
{
|
|
|
|
|
NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ());
|
|
|
|
|
file->Write(Simulator::Now(), p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
PcapHelper pcapHelper;
|
2010-02-23 10:04:31 -08:00
|
|
|
Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", "w", PcapHelper::DLT_PPP);
|
2010-01-25 17:07:33 -08:00
|
|
|
devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file));
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
@c ============================================================================
|
|
|
|
|
@c Using Trace Helpers
|
|
|
|
|
@c ============================================================================
|
|
|
|
|
@node Using Trace Helpers
|
|
|
|
|
@section Using Trace Helpers
|
|
|
|
|
|
|
|
|
|
The @code{ns-3} trace helpers provide a rich environment for configuring and
|
|
|
|
|
selecting different trace events and writing them to files. In previous
|
|
|
|
|
sections, primarily ``Building Topologies,'' we have seen several varieties
|
|
|
|
|
of the trace helper methods designed for use inside other (device) helpers.
|
|
|
|
|
|
|
|
|
|
Perhaps you will recall seeing some of these variations:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
pointToPoint.EnablePcapAll ("second");
|
|
|
|
|
pointToPoint.EnablePcap ("second", p2pNodes.Get (0)->GetId (), 0);
|
|
|
|
|
csma.EnablePcap ("third", csmaDevices.Get (0), true);
|
|
|
|
|
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
What may not be obvious, though, is that there is a consistent model for all of
|
|
|
|
|
the trace-related methods found in the system. We will now take a little time
|
2010-01-28 17:21:04 -08:00
|
|
|
and take a look at the ``big picture''.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
There are currently two primary use cases of the tracing helpers in @code{ns-3}:
|
|
|
|
|
Device helpers and protocol helpers. Device helpers look at the problem
|
|
|
|
|
of specifying which traces should be enabled through a node, device pair. For
|
|
|
|
|
example, you may want to specify that pcap tracing should be enabled on a
|
|
|
|
|
particular device on a specific node. This follows from the @code{ns-3} device
|
|
|
|
|
conceptual model, and also the conceptual models of the various device helpers.
|
|
|
|
|
Following naturallyu from this, the files created follow a
|
|
|
|
|
<prefix>-<node>-<device> naming convention.
|
|
|
|
|
|
|
|
|
|
Protocol helpers look at the problem of specifying which traces should be
|
|
|
|
|
enabled through a protocol and interface pair. This follows from the @code{ns-3}
|
|
|
|
|
protocol stack conceptual model, and also the conceptual models of internet
|
|
|
|
|
stack helpers. Naturally, the trace files should follow a
|
|
|
|
|
<prefix>-<protocol>-<interface> naming convention.
|
|
|
|
|
|
|
|
|
|
The trace helpers therefore fall naturally into a two-dimensional taxonomy.
|
|
|
|
|
There are subtleties that prevent all four classes from behaving identically,
|
|
|
|
|
but we do strive to make them all work as similarly as possible; and whenever
|
|
|
|
|
possible there are analogs for all methods in all classes.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
| pcap | ascii |
|
|
|
|
|
-----------------+------+-------|
|
|
|
|
|
Device Helper | | |
|
|
|
|
|
-----------------+------+-------|
|
|
|
|
|
Protocol Helper | | |
|
|
|
|
|
-----------------+------+-------|
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
We use an approach called a @code{mixin} to add tracing functionality to our
|
|
|
|
|
helper classes. A @code{mixin} is a class that provides functionality to that
|
|
|
|
|
is inherited by a subclass. Inheriting from a mixin is not considered a form
|
|
|
|
|
of specialization but is really a way to collect functionality.
|
|
|
|
|
|
|
|
|
|
Let's take a quick look at all four of these cases and their respective
|
|
|
|
|
@code{mixins}.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
@subsection Pcap Tracing Device Helpers
|
|
|
|
|
|
|
|
|
|
The goal of these helpers is to make it easy to add a consistent pcap trace
|
|
|
|
|
facility to an @code{ns-3} device. We want all of the various flavors of
|
|
|
|
|
pcap tracing to work the same across all devices, so the methods of these
|
|
|
|
|
helpers are inherited by device helpers. Take a look at
|
2010-01-28 17:21:04 -08:00
|
|
|
@code{src/helper/trace-helper.h} if you want to follow the discussion while
|
2010-01-27 17:21:36 -08:00
|
|
|
looking at real code.
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
The class @code{PcapHelperForDevice} is a @code{mixin} provides the high level
|
|
|
|
|
functionality for using pcap tracing in an @code{ns-3} device. Every device
|
|
|
|
|
must implement a single virtual method inherited from this class.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
virtual void EnablePcapInternal (std::string prefix, Ptr<NetDevice> nd, bool promiscuous, bool explicitFilename) = 0;
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The signature of this method reflects the device-centric view of the situation
|
|
|
|
|
at this level. All of the public methods inherited from class
|
|
|
|
|
2@code{PcapUserHelperForDevice} reduce to calling this single device-dependent
|
|
|
|
|
implementation method. For example, the lowest level pcap method,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 15:14:42 -08:00
|
|
|
void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
|
2010-02-24 14:40:09 -08:00
|
|
|
@end verbatim
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
will call the device implementation of @code{EnablePcapInternal} directly. All
|
|
|
|
|
other public pcap tracing methods build on this implementation to provide
|
|
|
|
|
additional user-level functionality. What this means to the user is that all
|
|
|
|
|
device helpers in the system will have all of the pcap trace methods available;
|
|
|
|
|
and these methods will all work in the same way across devices if the device
|
|
|
|
|
implements @code{EnablePcapInternal} correctly.
|
|
|
|
|
|
|
|
|
|
@subsubsection Pcap Tracing Device Helper Methods
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 15:14:42 -08:00
|
|
|
void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
|
2010-01-27 17:21:36 -08:00
|
|
|
void EnablePcap (std::string prefix, NetDeviceContainer d, bool promiscuous = false);
|
|
|
|
|
void EnablePcap (std::string prefix, NodeContainer n, bool promiscuous = false);
|
|
|
|
|
void EnablePcap (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool promiscuous = false);
|
|
|
|
|
void EnablePcapAll (std::string prefix, bool promiscuous = false);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
In each of the methods shown above, there is a default parameter called
|
|
|
|
|
@code{promiscuous} that defaults to false. This parameter indicates that the
|
|
|
|
|
trace should not be gathered in promiscuous mode. If you do want your traces
|
|
|
|
|
to include all traffic seen by the device (and if the device supports a
|
|
|
|
|
promiscuous mode) simply add a true parameter to any of the calls above. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<NetDevice> nd;
|
|
|
|
|
...
|
|
|
|
|
helper.EnablePcap ("prefix", nd, true);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
will enable promiscuous mode captures on the @code{NetDevice} specified by @code{nd}.
|
|
|
|
|
|
|
|
|
|
The first two methods also include a default parameter called @code{explicitFilename}
|
|
|
|
|
that will be discussed below.
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
You are encouraged to peruse the Doxygen for class @code{PcapHelperForDevice}
|
2010-01-27 17:21:36 -08:00
|
|
|
to find the details of these methods; but to summarize ...
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on a particular node/net-device pair by providing a
|
|
|
|
|
@code{Ptr<NetDevice>} to an @code{EnablePcap} method. The @code{Ptr<Node>} is
|
|
|
|
|
implicit since the net device must belong to exactly one @code{Node}.
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<NetDevice> nd;
|
|
|
|
|
...
|
|
|
|
|
helper.EnablePcap ("prefix", nd);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on a particular node/net-device pair by providing a
|
|
|
|
|
@code{std::string} representing an object name service string to an
|
|
|
|
|
@code{EnablePcap} method. The @code{Ptr<NetDevice>} is looked up from the name
|
|
|
|
|
string. Again, the @code<Node> is implicit since the named net device must
|
|
|
|
|
belong to exactly one @code{Node}. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Names::Add ("server" ...);
|
|
|
|
|
Names::Add ("server/eth0" ...);
|
|
|
|
|
...
|
|
|
|
|
helper.EnablePcap ("prefix", "server/ath0");
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on a collection of node/net-device pairs by
|
|
|
|
|
providing a @code{NetDeviceContainer}. For each @code{NetDevice} in the container
|
|
|
|
|
the type is checked. For each device of the proper type (the same type as is
|
|
|
|
|
managed by the device helper), tracing is enabled. Again, the @code<Node> is
|
|
|
|
|
implicit since the found net device must belong to exactly one @code{Node}.
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NetDeviceContainer d = ...;
|
|
|
|
|
...
|
|
|
|
|
helper.EnablePcap ("prefix", d);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on a collection of node/net-device pairs by
|
|
|
|
|
providing a @code{NodeContainer}. For each @code{Node} in the @code{NodeContainer}
|
|
|
|
|
its attached @code{NetDevices} are iterated. For each @code{NetDevice} attached
|
|
|
|
|
to each node in the container, the type of that device is checked. For each
|
|
|
|
|
device of the proper type (the same type as is managed by the device helper),
|
|
|
|
|
tracing is enabled.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NodeContainer n;
|
|
|
|
|
...
|
|
|
|
|
helper.EnablePcap ("prefix", n);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on the basis of node ID and device ID as well as
|
|
|
|
|
with explicit @code{Ptr}. Each @code{Node} in the system has an integer node ID
|
|
|
|
|
and each device connected to a node has an integer device ID.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
helper.EnablePcap ("prefix", 21, 1);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Finally, you can enable pcap tracing for all devices in the system, with the
|
|
|
|
|
same type as that managed by the device helper.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
helper.EnablePcapAll ("prefix");
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
@subsubsection Pcap Tracing Device Helper Filename Selection
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
Implicit in the method descriptions above is the construction of a complete
|
|
|
|
|
filename by the implementation method. By convention, pcap traces in the
|
|
|
|
|
@code{ns-3} system are of the form ``<prefix>-<node id>-<device id>.pcap''
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
As previously mentioned, every node in the system will have a system-assigned
|
|
|
|
|
node id; and every device will have an interface index (also called a device id)
|
|
|
|
|
relative to its node. By default, then, a pcap trace file created as a result
|
|
|
|
|
of enabling tracing on the first device of node 21 using the prefix ``prefix''
|
|
|
|
|
would be ``prefix-21-1.pcap''.
|
|
|
|
|
|
|
|
|
|
You can always use the @code{ns-3} object name service to make this more clear.
|
|
|
|
|
For example, if you use the object name service to assign the name ``server''
|
|
|
|
|
to node 21, the resulting pcap trace file name will automatically become,
|
|
|
|
|
``prefix-server-1.pcap'' and if you also assign the name ``eth0'' to the
|
|
|
|
|
device, your pcap file name will automatically pick this up and be called
|
|
|
|
|
``prefix-server-eth0.pcap''.
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
Finally, two of the methods shown above,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
|
|
|
|
|
void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilenaqme = false);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
have a default parameter called @code{explicitFilename}. When set to true,
|
|
|
|
|
this parameter disables the automatic filename completion mechanism and allows
|
|
|
|
|
you to create an explicit filename. This option is only available in the
|
|
|
|
|
methods which enable pcap tracing on a single device.
|
|
|
|
|
|
|
|
|
|
For example, in order to arrange for a device helper to create a single
|
|
|
|
|
promiscuous pcap capture file of a specific name (``my-pcap-file.pcap'') on a
|
|
|
|
|
given device, one could:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<NetDevice> nd;
|
|
|
|
|
...
|
|
|
|
|
helper.EnablePcap ("my-pcap-file.pcap", nd, true, true);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The first @code{true} parameter enables promiscuous mode traces and the second
|
|
|
|
|
tells the helper to interpret the @code{prefix} parameter as a complete filename.
|
|
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@subsection Ascii Tracing Device Helpers
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
The behavior of the ascii trace helper @code{mixin} is substantially similar to
|
|
|
|
|
the pcap version. Take a look at @code{src/helper/trace-helper.h} if you want to
|
2010-01-27 17:21:36 -08:00
|
|
|
follow the discussion while looking at real code.
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
The class @code{AsciiTraceHelperForDevice} adds the high level functionality for
|
|
|
|
|
using ascii tracing to a device helper class. As in the pcap case, every device
|
|
|
|
|
must implement a single virtual method inherited from the ascii trace @code{mixin}.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
virtual void EnableAsciiInternal (Ptr<OutputStreamWrapper> stream,
|
|
|
|
|
std::string prefix,
|
|
|
|
|
Ptr<NetDevice> nd,
|
|
|
|
|
bool explicitFilename) = 0;
|
|
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The signature of this method reflects the device-centric view of the situation
|
|
|
|
|
at this level; and also the fact that the helper may be writing to a shared
|
2010-01-28 17:21:04 -08:00
|
|
|
output stream. All of the public ascii-trace-related methods inherited from
|
2010-03-01 15:14:42 -08:00
|
|
|
class @code{AsciiTraceHelperForDevice} reduce to calling this single device-
|
2010-01-27 17:21:36 -08:00
|
|
|
dependent implementation method. For example, the lowest level ascii trace
|
|
|
|
|
methods,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnableAscii (std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
|
2010-01-27 17:21:36 -08:00
|
|
|
@verbatim
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
will call the device implementation of @code{EnableAsciiInternal} directly,
|
|
|
|
|
providing either a valid prefix or stream. All other public ascii tracing
|
2010-01-27 17:21:36 -08:00
|
|
|
methods will build on these low-level functions to provide additional user-level
|
|
|
|
|
functionality. What this means to the user is that all device helpers in the
|
|
|
|
|
system will have all of the ascii trace methods available; and these methods
|
|
|
|
|
will all work in the same way across devices if the devices implement
|
|
|
|
|
@code{EnablAsciiInternal} correctly.
|
|
|
|
|
|
|
|
|
|
@subsubsection Ascii Tracing Device Helper Methods
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnableAscii (std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);
|
2010-01-27 17:21:36 -08:00
|
|
|
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnableAscii (std::string prefix, std::string ndName, bool explicitFilename = false);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAscii (Ptr<OutputStreamWrapper> stream, std::string ndName);
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
void EnableAscii (std::string prefix, NetDeviceContainer d);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAscii (Ptr<OutputStreamWrapper> stream, NetDeviceContainer d);
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
void EnableAscii (std::string prefix, NodeContainer n);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAscii (Ptr<OutputStreamWrapper> stream, NodeContainer n);
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
void EnableAsciiAll (std::string prefix);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAsciiAll (Ptr<OutputStreamWrapper> stream);
|
2010-03-01 18:15:36 -08:00
|
|
|
|
|
|
|
|
void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename);
|
|
|
|
|
void EnableAscii (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t deviceid);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
You are encouraged to peruse the Doxygen for class @code{TraceHelperForDevice}
|
2010-01-27 17:21:36 -08:00
|
|
|
to find the details of these methods; but to summarize ...
|
|
|
|
|
|
|
|
|
|
There are twice as many methods available for ascii tracing as there were for
|
|
|
|
|
pcap tracing. This is because, in addition to the pcap-style model where traces
|
|
|
|
|
from each unique node/device pair are written to a unique file, we support a model
|
|
|
|
|
in which trace information for many node/device pairs is written to a common file.
|
|
|
|
|
This means that the <prefix>-<node>-<device> file name generation mechanism is
|
|
|
|
|
replaced by a mechanism to refer to a common file; and the number of API methods
|
|
|
|
|
is doubled to allow all combinations.
|
|
|
|
|
|
|
|
|
|
Just as in pcap tracing, you can enable ascii tracing on a particular
|
|
|
|
|
node/net-device pair by providing a @code{Ptr<NetDevice>} to an @code{EnableAscii}
|
|
|
|
|
method. The @code{Ptr<Node>} is implicit since the net device must belong to
|
|
|
|
|
exactly one @code{Node}. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<NetDevice> nd;
|
|
|
|
|
...
|
|
|
|
|
helper.EnableAscii ("prefix", nd);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
2010-03-01 18:15:36 -08:00
|
|
|
The first four methods also include a default parameter called @code{explicitFilename}
|
|
|
|
|
that operate similar to equivalent parameters in the pcap case.
|
|
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
In this case, no trace contexts are written to the ascii trace file since they
|
|
|
|
|
would be redundant. The system will pick the file name to be created using
|
|
|
|
|
the same rules as described in the pcap section, except that the file will
|
|
|
|
|
have the suffix ``.tr'' instead of ``.pcap''.
|
|
|
|
|
|
|
|
|
|
If you want to enable ascii tracing on more than one net device and have all
|
|
|
|
|
traces sent to a single file, you can do that as well by using an object to
|
|
|
|
|
refer to a single file. We have already seen this in the ``cwnd'' example
|
|
|
|
|
above:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<NetDevice> nd1;
|
|
|
|
|
Ptr<NetDevice> nd2;
|
|
|
|
|
...
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
|
2010-01-27 17:21:36 -08:00
|
|
|
...
|
|
|
|
|
helper.EnableAscii (stream, nd1);
|
|
|
|
|
helper.EnableAscii (stream, nd2);
|
|
|
|
|
@verbatim
|
|
|
|
|
|
|
|
|
|
In this case, trace contexts are written to the ascii trace file since they
|
|
|
|
|
are required to disambiguate traces from the two devices. Note that since the
|
|
|
|
|
user is completely specifying the file name, the string should include the ``,tr''
|
|
|
|
|
for consistency.
|
|
|
|
|
|
|
|
|
|
You can enable ascii tracing on a particular node/net-device pair by providing a
|
|
|
|
|
@code{std::string} representing an object name service string to an
|
|
|
|
|
@code{EnablePcap} method. The @code{Ptr<NetDevice>} is looked up from the name
|
|
|
|
|
string. Again, the @code<Node> is implicit since the named net device must
|
|
|
|
|
belong to exactly one @code{Node}. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Names::Add ("client" ...);
|
|
|
|
|
Names::Add ("client/eth0" ...);
|
|
|
|
|
Names::Add ("server" ...);
|
|
|
|
|
Names::Add ("server/eth0" ...);
|
|
|
|
|
...
|
|
|
|
|
helper.EnableAscii ("prefix", "client/eth0");
|
|
|
|
|
helper.EnableAscii ("prefix", "server/eth0");
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in two files named ``prefix-client-eth0.tr'' and
|
|
|
|
|
``prefix-server-eth0.tr'' with traces for each device in the respective trace
|
2010-02-23 10:30:38 -08:00
|
|
|
file. Since all of the EnableAscii functions are overloaded to take a stream wrapper,
|
2010-01-27 17:21:36 -08:00
|
|
|
you can use that form as well:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Names::Add ("client" ...);
|
|
|
|
|
Names::Add ("client/eth0" ...);
|
|
|
|
|
Names::Add ("server" ...);
|
|
|
|
|
Names::Add ("server/eth0" ...);
|
|
|
|
|
...
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
|
2010-01-27 17:21:36 -08:00
|
|
|
...
|
|
|
|
|
helper.EnableAscii (stream, "client/eth0");
|
|
|
|
|
helper.EnableAscii (stream, "server/eth0");
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in a single trace file called ``trace-file-name.tr'' that
|
|
|
|
|
contains all of the trace events for both devices. The events would be
|
|
|
|
|
disambiguated by trace context strings.
|
|
|
|
|
|
|
|
|
|
You can enable ascii tracing on a collection of node/net-device pairs by
|
|
|
|
|
providing a @code{NetDeviceContainer}. For each @code{NetDevice} in the container
|
|
|
|
|
the type is checked. For each device of the proper type (the same type as is
|
|
|
|
|
managed by the device helper), tracing is enabled. Again, the @code<Node> is
|
|
|
|
|
implicit since the found net device must belong to exactly one @code{Node}.
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NetDeviceContainer d = ...;
|
|
|
|
|
...
|
|
|
|
|
helper.EnableAscii ("prefix", d);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in a number of ascii trace files being created, each of which
|
|
|
|
|
follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the
|
|
|
|
|
traces into a single file is accomplished similarly to the examples above:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NetDeviceContainer d = ...;
|
|
|
|
|
...
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
|
2010-01-27 17:21:36 -08:00
|
|
|
...
|
|
|
|
|
helper.EnableAscii (stream, d);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable ascii tracing on a collection of node/net-device pairs by
|
|
|
|
|
providing a @code{NodeContainer}. For each @code{Node} in the @code{NodeContainer}
|
|
|
|
|
its attached @code{NetDevices} are iterated. For each @code{NetDevice} attached
|
|
|
|
|
to each node in the container, the type of that device is checked. For each
|
|
|
|
|
device of the proper type (the same type as is managed by the device helper),
|
|
|
|
|
tracing is enabled.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NodeContainer n;
|
|
|
|
|
...
|
|
|
|
|
helper.EnableAscii ("prefix", n);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in a number of ascii trace files being created, each of which
|
|
|
|
|
follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the
|
|
|
|
|
traces into a single file is accomplished similarly to the examples above:
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on the basis of node ID and device ID as well as
|
|
|
|
|
with explicit @code{Ptr}. Each @code{Node} in the system has an integer node ID
|
|
|
|
|
and each device connected to a node has an integer device ID.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
helper.EnableAscii ("prefix", 21, 1);
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Of course, the traces can be combined into a single file as shown above.
|
|
|
|
|
|
|
|
|
|
Finally, you can enable pcap tracing for all devices in the system, with the
|
|
|
|
|
same type as that managed by the device helper.
|
2010-01-25 16:09:07 -08:00
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@verbatim
|
|
|
|
|
helper.EnableAsciiAll ("prefix");
|
|
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in a number of ascii trace files being created, one for
|
|
|
|
|
every device in the system of the type managed by the helper. All of these
|
|
|
|
|
files will follow the <prefix>-<node id>-<device id>.tr convention. Combining
|
|
|
|
|
all of the traces into a single file is accomplished similarly to the examples
|
|
|
|
|
above.
|
|
|
|
|
|
|
|
|
|
@subsubsection Ascii Tracing Device Helper Filename Selection
|
|
|
|
|
|
|
|
|
|
Implicit in the prefix-style method descriptions above is the construction of the
|
|
|
|
|
complete filenames by the implementation method. By convention, ascii traces
|
|
|
|
|
in the @code{ns-3} system are of the form ``<prefix>-<node id>-<device id>.tr''
|
|
|
|
|
|
|
|
|
|
As previously mentioned, every node in the system will have a system-assigned
|
|
|
|
|
node id; and every device will have an interface index (also called a device id)
|
|
|
|
|
relative to its node. By default, then, an ascii trace file created as a result
|
|
|
|
|
of enabling tracing on the first device of node 21, using the prefix ``prefix'',
|
|
|
|
|
would be ``prefix-21-1.tr''.
|
|
|
|
|
|
|
|
|
|
You can always use the @code{ns-3} object name service to make this more clear.
|
|
|
|
|
For example, if you use the object name service to assign the name ``server''
|
|
|
|
|
to node 21, the resulting ascii trace file name will automatically become,
|
|
|
|
|
``prefix-server-1.tr'' and if you also assign the name ``eth0'' to the
|
|
|
|
|
device, your ascii trace file name will automatically pick this up and be called
|
|
|
|
|
``prefix-server-eth0.tr''.
|
|
|
|
|
|
2010-03-01 18:15:36 -08:00
|
|
|
Several of the methods have a default parameter called @code{explicitFilename}.
|
|
|
|
|
When set to true, this parameter disables the automatic filename completion
|
|
|
|
|
mechanism and allows you to create an explicit filename. This option is only
|
|
|
|
|
available in the methods which take a prefix and enable tracing on a single device.
|
|
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@subsection Pcap Tracing Protocol Helpers
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
The goal of these @code{mixins} is to make it easy to add a consistent pcap trace
|
2010-01-27 17:21:36 -08:00
|
|
|
facility to protocols. We want all of the various flavors of pcap tracing to
|
|
|
|
|
work the same across all protocols, so the methods of these helpers are
|
2010-01-28 17:21:04 -08:00
|
|
|
inherited by stack helpers. Take a look at @code{src/helper/trace-helper.h}
|
2010-01-27 17:21:36 -08:00
|
|
|
if you want to follow the discussion while looking at real code.
|
|
|
|
|
|
|
|
|
|
In this section we will be illustrating the methods as applied to the protocol
|
|
|
|
|
@code{Ipv4}. To specify traces in similar protocols, just substitute the
|
|
|
|
|
appropriate type. For example, use a @code{Ptr<Ipv6>} instead of a
|
2010-01-28 17:21:04 -08:00
|
|
|
@code{Ptr<Ipv4>} and call @code{EnablePcapIpv6} instead of @code{EnablePcapIpv4}.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
The class @code{PcapHelperForIpv4} provides the high level functionality for
|
2010-03-01 15:14:42 -08:00
|
|
|
using pcap tracing in the @code{Ipv4} protocol. Each protocol helper enabling these
|
2010-01-27 17:21:36 -08:00
|
|
|
methods must implement a single virtual method inherited from this class. There
|
2010-01-28 17:21:04 -08:00
|
|
|
will be a separate implementation for @code{Ipv6}, for example, but the only
|
|
|
|
|
difference will be in the method names and signatures. Different method names
|
|
|
|
|
are required to disambiguate class @code{Ipv4} from @coe{Ipv6} which are both
|
|
|
|
|
derived from class @code{Object}, and methods that share the same signature.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
virtual void EnablePcapIpv4Internal (std::string prefix,
|
|
|
|
|
Ptr<Ipv4> ipv4,
|
|
|
|
|
uint32_t interface,
|
|
|
|
|
bool explicitFilename) = 0;
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The signature of this method reflects the protocol and interface-centric view
|
|
|
|
|
of the situation at this level. All of the public methods inherited from class
|
2010-01-28 17:21:04 -08:00
|
|
|
@code{PcapHelperForIpv4} reduce to calling this single device-dependent
|
2010-01-27 17:21:36 -08:00
|
|
|
implementation method. For example, the lowest level pcap method,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
|
2010-01-27 17:21:36 -08:00
|
|
|
@verbatim
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
will call the device implementation of @code{EnablePcapIpv4Internal} directly.
|
|
|
|
|
All other public pcap tracing methods build on this implementation to provide
|
2010-01-27 17:21:36 -08:00
|
|
|
additional user-level functionality. What this means to the user is that all
|
|
|
|
|
protocol helpers in the system will have all of the pcap trace methods
|
|
|
|
|
available; and these methods will all work in the same way across
|
2010-01-28 17:21:04 -08:00
|
|
|
protocols if the helper implements @code{EnablePcapIpv4Internal} correctly.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
@subsubsection Pcap Tracing Protocol Helper Methods
|
|
|
|
|
|
|
|
|
|
These methods are designed to be in one-to-one correspondence with the @code{Node}-
|
|
|
|
|
and @code{NetDevice}- centric versions of the device versions. Instead of
|
|
|
|
|
@code{Node} and @code{NetDevice} pair constraints, we use protocol and interface
|
|
|
|
|
constraints.
|
|
|
|
|
|
|
|
|
|
Note that just like in the device version, there are six methods:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
|
|
|
|
|
void EnablePcapIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false);
|
2010-01-28 17:21:04 -08:00
|
|
|
void EnablePcapIpv4 (std::string prefix, Ipv4InterfaceContainer c);
|
|
|
|
|
void EnablePcapIpv4 (std::string prefix, NodeContainer n);
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnablePcapIpv4 (std::string prefix, uint32_t nodeid, uint32_t interface, bool explicitFilename);
|
2010-01-28 17:21:04 -08:00
|
|
|
void EnablePcapIpv4All (std::string prefix);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
You are encouraged to peruse the Doxygen for class @code{PcapHelperForIpv4}
|
2010-01-27 17:21:36 -08:00
|
|
|
to find the details of these methods; but to summarize ...
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on a particular protocol/interface pair by providing a
|
|
|
|
|
@code{Ptr<Ipv4>} and @code{interface} to an @code{EnablePcap} method. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
|
|
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnablePcapIpv4 ("prefix", ipv4, 0);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on a particular node/net-device pair by providing a
|
|
|
|
|
@code{std::string} representing an object name service string to an
|
|
|
|
|
@code{EnablePcap} method. The @code{Ptr<Ipv4>} is looked up from the name
|
|
|
|
|
string. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Names::Add ("serverIPv4" ...);
|
|
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnablePcapIpv4 ("prefix", "serverIpv4", 1);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on a collection of protocol/interface pairs by
|
|
|
|
|
providing an @code{Ipv4InterfaceContainer}. For each @code{Ipv4} / interface
|
|
|
|
|
pair in the container the protocol type is checked. For each protocol of the
|
|
|
|
|
proper type (the same type as is managed by the device helper), tracing is
|
|
|
|
|
enabled for the corresponding interface. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NodeContainer nodes;
|
|
|
|
|
...
|
|
|
|
|
NetDeviceContainer devices = deviceHelper.Install (nodes);
|
|
|
|
|
...
|
|
|
|
|
Ipv4AddressHelper ipv4;
|
|
|
|
|
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
|
|
|
|
|
Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
|
|
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnablePcapIpv4 ("prefix", interfaces);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on a collection of protocol/interface pairs by
|
|
|
|
|
providing a @code{NodeContainer}. For each @code{Node} in the @code{NodeContainer}
|
|
|
|
|
the appropriate protocol is found. For each protocol, its interfaces are
|
|
|
|
|
enumerated and tracing is enabled on the resulting pairs. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NodeContainer n;
|
|
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnablePcapIpv4 ("prefix", n);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on the basis of node ID and interface as well. In
|
|
|
|
|
this case, the node-id is translated to a @code{Ptr{Node} and the appropriate
|
|
|
|
|
protocol is looked up in the node. The resulting protocol and interface are
|
|
|
|
|
used to specify the resulting trace source.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnablePcapIpv4 ("prefix", 21, 1);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Finally, you can enable pcap tracing for all interfaces in the system, with
|
|
|
|
|
associated protocol being the same type as that managed by the device helper.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnablePcapIpv4All ("prefix");
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
@subsubsection Pcap Tracing Protocol Helper Filename Selection
|
|
|
|
|
|
|
|
|
|
Implicit in all of the method descriptions above is the construction of the
|
|
|
|
|
complete filenames by the implementation method. By convention, pcap traces
|
|
|
|
|
taken for devices in the @code{ns-3} system are of the form
|
|
|
|
|
``<prefix>-<node id>-<device id>.pcap''. In the case of protocol traces,
|
|
|
|
|
there is a one-to-one correspondence between protocols and @code{Nodes}.
|
|
|
|
|
This is because protocol @code{Objects} are aggregated to @code{Node Objects}.
|
|
|
|
|
Since there is no global protocol id in the system, we use the corresponding
|
|
|
|
|
node id in file naming. Threfore there is a possibility for file name
|
|
|
|
|
collisions in automatically chosen trace file names. For this reason, the
|
|
|
|
|
file name convention is changed for protocol traces.
|
|
|
|
|
|
|
|
|
|
As previously mentioned, every node in the system will have a system-assigned
|
|
|
|
|
node id. Since there is a one-to-one correspondence between protocol instances
|
|
|
|
|
and node instances we use the node id. Each interface has an interface id
|
|
|
|
|
relative to its protocol. We use the convention
|
|
|
|
|
"<prefix>-n<node id>-i<interface id>.pcap" for trace file naming in protocol
|
|
|
|
|
helpers.
|
|
|
|
|
|
|
|
|
|
Therefore, by default, a pcap trace file created as a result of enabling tracing
|
|
|
|
|
on interface 1 of the Ipv4 protocol of node 21 using the prefix ``prefix''
|
|
|
|
|
would be ``prefix-n21-i1.pcap''.
|
|
|
|
|
|
|
|
|
|
You can always use the @code{ns-3} object name service to make this more clear.
|
|
|
|
|
For example, if you use the object name service to assign the name ``serverIpv4''
|
|
|
|
|
to the Ptr<Ipv4> on node 21, the resulting pcap trace file name will
|
|
|
|
|
automatically become, ``prefix-nserverIpv4-i1.pcap''.
|
|
|
|
|
|
2010-03-01 18:15:36 -08:00
|
|
|
Several of the methods have a default parameter called @code{explicitFilename}.
|
|
|
|
|
When set to true, this parameter disables the automatic filename completion
|
|
|
|
|
mechanism and allows you to create an explicit filename. This option is only
|
|
|
|
|
available in the methods which take a prefix and enable tracing on a single device.
|
|
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@subsection Ascii Tracing Protocol Helpers
|
|
|
|
|
|
|
|
|
|
The behavior of the ascii trace helpers is substantially similar to the pcap
|
2010-01-28 17:21:04 -08:00
|
|
|
case. Take a look at @code{src/helper/trace-helper.h} if you want to
|
2010-01-27 17:21:36 -08:00
|
|
|
follow the discussion while looking at real code.
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
In this section we will be illustrating the methods as applied to the protocol
|
|
|
|
|
@code{Ipv4}. To specify traces in similar protocols, just substitute the
|
|
|
|
|
appropriate type. For example, use a @code{Ptr<Ipv6>} instead of a
|
|
|
|
|
@code{Ptr<Ipv4>} and call @code{EnableAsciiIpv6} instead of @code{EnableAsciiIpv4}.
|
|
|
|
|
|
2010-03-01 15:14:42 -08:00
|
|
|
The class @code{AsciiTraceHelperForIpv4} adds the high level functionality
|
|
|
|
|
for using ascii tracing to a protocol helper. Each protocol that enables these
|
|
|
|
|
methods must implement a single virtual method inherited from this class.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
virtual void EnableAsciiIpv4Internal (Ptr<OutputStreamWrapper> stream,
|
|
|
|
|
std::string prefix,
|
|
|
|
|
Ptr<Ipv4> ipv4,
|
|
|
|
|
uint32_t interface,
|
|
|
|
|
bool explicitFilename) = 0;
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
The signature of this method reflects the protocol- and interface-centric view
|
|
|
|
|
of the situation at this level; and also the fact that the helper may be writing
|
|
|
|
|
to a shared output stream. All of the public methods inherited from class
|
2010-01-28 17:21:04 -08:00
|
|
|
@code{PcapAndAsciiTraceHelperForIpv4} reduce to calling this single device-
|
2010-01-27 17:21:36 -08:00
|
|
|
dependent implementation method. For example, the lowest level ascii trace
|
|
|
|
|
methods,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
|
2010-01-27 17:21:36 -08:00
|
|
|
@verbatim
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
will call the device implementation of @code{EnableAsciiIpv4Internal} directly,
|
2010-01-27 17:21:36 -08:00
|
|
|
providing either the prefix or the stream. All other public ascii tracing
|
|
|
|
|
methods will build on these low-level functions to provide additional user-level
|
|
|
|
|
functionality. What this means to the user is that all device helpers in the
|
|
|
|
|
system will have all of the ascii trace methods available; and these methods
|
|
|
|
|
will all work in the same way across protocols if the protocols implement
|
2010-01-28 17:21:04 -08:00
|
|
|
@code{EnablAsciiIpv4Internal} correctly.
|
2010-01-27 17:21:36 -08:00
|
|
|
|
2010-03-01 18:15:36 -08:00
|
|
|
@subsubsection Ascii Tracing Protocol Helper Methods
|
2009-10-08 13:59:26 -07:00
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@verbatim
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);
|
2010-01-27 17:21:36 -08:00
|
|
|
|
2010-03-01 18:15:36 -08:00
|
|
|
void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, std::string ipv4Name, uint32_t interface);
|
2010-01-27 17:21:36 -08:00
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
void EnableAsciiIpv4 (std::string prefix, Ipv4InterfaceContainer c);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ipv4InterfaceContainer c);
|
2010-01-27 17:21:36 -08:00
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
void EnableAsciiIpv4 (std::string prefix, NodeContainer n);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, NodeContainer n);
|
2010-01-27 17:21:36 -08:00
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
void EnableAsciiIpv4All (std::string prefix);
|
2010-02-23 10:30:38 -08:00
|
|
|
void EnableAsciiIpv4All (Ptr<OutputStreamWrapper> stream);
|
2010-03-01 18:15:36 -08:00
|
|
|
|
|
|
|
|
void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename);
|
|
|
|
|
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t interface);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
2010-01-28 17:21:04 -08:00
|
|
|
You are encouraged to peruse the Doxygen for class @code{PcapAndAsciiHelperForIpv4}
|
2010-01-27 17:21:36 -08:00
|
|
|
to find the details of these methods; but to summarize ...
|
|
|
|
|
|
|
|
|
|
There are twice as many methods available for ascii tracing as there were for
|
|
|
|
|
pcap tracing. This is because, in addition to the pcap-style model where traces
|
|
|
|
|
from each unique protocol/interface pair are written to a unique file, we
|
|
|
|
|
support a model in which trace information for many protocol/interface pairs is
|
|
|
|
|
written to a common file. This means that the <prefix>-n<node id>-<interface>
|
|
|
|
|
file name generation mechanism is replaced by a mechanism to refer to a common
|
|
|
|
|
file; and the number of API methods is doubled to allow all combinations.
|
|
|
|
|
|
|
|
|
|
Just as in pcap tracing, you can enable ascii tracing on a particular
|
|
|
|
|
protocol/interface pair by providing a @code{Ptr<Ipv4>} and an @code{interface}
|
|
|
|
|
to an @code{EnableAscii} method.
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<Ipv4> ipv4;
|
|
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4 ("prefix", ipv4, 1);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
In this case, no trace contexts are written to the ascii trace file since they
|
|
|
|
|
would be redundant. The system will pick the file name to be created using
|
|
|
|
|
the same rules as described in the pcap section, except that the file will
|
|
|
|
|
have the suffix ``.tr'' instead of ``.pcap''.
|
|
|
|
|
|
|
|
|
|
If you want to enable ascii tracing on more than one interface and have all
|
|
|
|
|
traces sent to a single file, you can do that as well by using an object to
|
|
|
|
|
refer to a single file. We have already something similar to this in the
|
|
|
|
|
``cwnd'' example above:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Ptr<Ipv4> protocol1 = node1->GetObject<Ipv4> ();
|
|
|
|
|
Ptr<Ipv4> protocol2 = node2->GetObject<Ipv4> ();
|
|
|
|
|
...
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
|
2010-01-27 17:21:36 -08:00
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4 (stream, protocol1, 1);
|
|
|
|
|
helper.EnableAsciiIpv4 (stream, protocol2, 1);
|
2010-01-27 17:21:36 -08:00
|
|
|
@verbatim
|
|
|
|
|
|
|
|
|
|
In this case, trace contexts are written to the ascii trace file since they
|
|
|
|
|
are required to disambiguate traces from the two interfaces. Note that since
|
|
|
|
|
the user is completely specifying the file name, the string should include the
|
|
|
|
|
``,tr'' for consistency.
|
|
|
|
|
|
|
|
|
|
You can enable ascii tracing on a particular protocol by providing a
|
|
|
|
|
@code{std::string} representing an object name service string to an
|
|
|
|
|
@code{EnablePcap} method. The @code{Ptr<Ipv4>} is looked up from the name
|
|
|
|
|
string. The @code<Node> in the resulting filenames is implicit since there
|
|
|
|
|
is a one-to-one correspondence between protocol instances and nodes,
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Names::Add ("node1Ipv4" ...);
|
|
|
|
|
Names::Add ("node2Ipv4" ...);
|
|
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4 ("prefix", "node1Ipv4", 1);
|
|
|
|
|
helper.EnableAsciiIpv4 ("prefix", "node2Ipv4", 1);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in two files named ``prefix-nnode1Ipv4-i1.tr'' and
|
|
|
|
|
``prefix-nnode2Ipv4-i1.tr'' with traces for each interface in the respective
|
|
|
|
|
trace file. Since all of the EnableAscii functions are overloaded to take a
|
2010-02-23 10:30:38 -08:00
|
|
|
stream wrapper, you can use that form as well:
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
Names::Add ("node1Ipv4" ...);
|
|
|
|
|
Names::Add ("node2Ipv4" ...);
|
|
|
|
|
...
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
|
2010-01-27 17:21:36 -08:00
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4 (stream, "node1Ipv4", 1);
|
|
|
|
|
helper.EnableAsciiIpv4 (stream, "node2Ipv4", 1);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in a single trace file called ``trace-file-name.tr'' that
|
|
|
|
|
contains all of the trace events for both interfaces. The events would be
|
|
|
|
|
disambiguated by trace context strings.
|
|
|
|
|
|
|
|
|
|
You can enable ascii tracing on a collection of protocol/interface pairs by
|
|
|
|
|
providing an @code{Ipv4InterfaceContainer}. For each protocol of the proper
|
|
|
|
|
type (the same type as is managed by the device helper), tracing is enabled
|
|
|
|
|
for the corresponding interface. Again, the @code<Node> is implicit since
|
|
|
|
|
there is a one-to-one correspondence between each protocol and its node.
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NodeContainer nodes;
|
|
|
|
|
...
|
|
|
|
|
NetDeviceContainer devices = deviceHelper.Install (nodes);
|
|
|
|
|
...
|
|
|
|
|
Ipv4AddressHelper ipv4;
|
|
|
|
|
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
|
|
|
|
|
Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
|
|
|
|
|
...
|
|
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4 ("prefix", interfaces);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in a number of ascii trace files being created, each of which
|
|
|
|
|
follows the <prefix>-n<node id>-i<interface>.tr convention. Combining all of the
|
|
|
|
|
traces into a single file is accomplished similarly to the examples above:
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NodeContainer nodes;
|
|
|
|
|
...
|
|
|
|
|
NetDeviceContainer devices = deviceHelper.Install (nodes);
|
|
|
|
|
...
|
|
|
|
|
Ipv4AddressHelper ipv4;
|
|
|
|
|
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
|
|
|
|
|
Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
|
|
|
|
|
...
|
2010-02-23 10:30:38 -08:00
|
|
|
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
|
2010-01-27 17:21:36 -08:00
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4 (stream, interfaces);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
You can enable ascii tracing on a collection of protocol/interface pairs by
|
|
|
|
|
providing a @code{NodeContainer}. For each @code{Node} in the @code{NodeContainer}
|
|
|
|
|
the appropriate protocol is found. For each protocol, its interfaces are
|
|
|
|
|
enumerated and tracing is enabled on the resulting pairs. For example,
|
|
|
|
|
|
|
|
|
|
@verbatim
|
|
|
|
|
NodeContainer n;
|
|
|
|
|
...
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4 ("prefix", n);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in a number of ascii trace files being created, each of which
|
|
|
|
|
follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the
|
|
|
|
|
traces into a single file is accomplished similarly to the examples above:
|
|
|
|
|
|
|
|
|
|
You can enable pcap tracing on the basis of node ID and device ID as well. In
|
|
|
|
|
this case, the node-id is translated to a @code{Ptr{Node} and the appropriate
|
|
|
|
|
protocol is looked up in the node. The resulting protocol and interface are
|
|
|
|
|
used to specify the resulting trace source.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4 ("prefix", 21, 1);
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
Of course, the traces can be combined into a single file as shown above.
|
|
|
|
|
|
|
|
|
|
Finally, you can enable ascii tracing for all interfaces in the system, with
|
|
|
|
|
associated protocol being the same type as that managed by the device helper.
|
|
|
|
|
|
|
|
|
|
@verbatim
|
2010-01-28 17:21:04 -08:00
|
|
|
helper.EnableAsciiIpv4All ("prefix");
|
2010-01-27 17:21:36 -08:00
|
|
|
@end verbatim
|
|
|
|
|
|
|
|
|
|
This would result in a number of ascii trace files being created, one for
|
|
|
|
|
every interface in the system related to a protocol of the type managed by the
|
|
|
|
|
helper. All of these files will follow the <prefix>-n<node id>-i<interface.tr
|
|
|
|
|
convention. Combining all of the traces into a single file is accomplished
|
|
|
|
|
similarly to the examples above.
|
|
|
|
|
|
2010-03-01 18:15:36 -08:00
|
|
|
@subsubsection Ascii Tracing Protocol Helper Filename Selection
|
2010-01-27 17:21:36 -08:00
|
|
|
|
|
|
|
|
Implicit in the prefix-style method descriptions above is the construction of the
|
|
|
|
|
complete filenames by the implementation method. By convention, ascii traces
|
|
|
|
|
in the @code{ns-3} system are of the form ``<prefix>-<node id>-<device id>.tr''
|
|
|
|
|
|
|
|
|
|
As previously mentioned, every node in the system will have a system-assigned
|
|
|
|
|
node id. Since there is a one-to-one correspondence between protocols and nodes
|
|
|
|
|
we use to node-id to identify the protocol identity. Every interface on a
|
|
|
|
|
given rotocol will have an interface index (also called simply an interface)
|
|
|
|
|
relative to its protocol. By default, then, an ascii trace file created as a result
|
|
|
|
|
of enabling tracing on the first device of node 21, using the prefix ``prefix'',
|
|
|
|
|
would be ``prefix-n21-i1.tr''. Use the prefix to disambiguate multiple protocols
|
|
|
|
|
per node.
|
|
|
|
|
|
|
|
|
|
You can always use the @code{ns-3} object name service to make this more clear.
|
|
|
|
|
For example, if you use the object name service to assign the name ``serverIpv4''
|
|
|
|
|
to the protocol on node 21, and also specify interface one, the resulting ascii
|
|
|
|
|
trace file name will automatically become, ``prefix-nserverIpv4-1.tr''.
|
|
|
|
|
|
2010-03-01 18:15:36 -08:00
|
|
|
Several of the methods have a default parameter called @code{explicitFilename}.
|
|
|
|
|
When set to true, this parameter disables the automatic filename completion
|
|
|
|
|
mechanism and allows you to create an explicit filename. This option is only
|
|
|
|
|
available in the methods which take a prefix and enable tracing on a single device.
|
|
|
|
|
|
2010-01-27 17:21:36 -08:00
|
|
|
@c ============================================================================
|
|
|
|
|
@c Summary
|
|
|
|
|
@c ============================================================================
|
|
|
|
|
@node Summary
|
|
|
|
|
@section Summary
|
|
|
|
|
|
|
|
|
|
@code{ns-3} includes an extremely rich environment allowing users at several
|
|
|
|
|
levels to customize the kinds of information that can be extracted from
|
|
|
|
|
simulations.
|
|
|
|
|
|
|
|
|
|
There are high-level helper functions that allow users to simply control the
|
|
|
|
|
collection of pre-defined outputs to a fine granularity. There are mid-level
|
|
|
|
|
helper functions to allow more sophisticated users to customize how information
|
|
|
|
|
is extracted and saved; and there are low-level core functions to allow expert
|
|
|
|
|
users to alter the system to present new and previously unexported information
|
|
|
|
|
in a way that will be immediatly accessible to users at higher levels.
|
|
|
|
|
|
|
|
|
|
This is a very comprehensive system, and we realize that it is a lot to
|
|
|
|
|
digest, especially for new users or those not intimately familiar with C++
|
|
|
|
|
and its idioms. We do consider the tracing system a very important part of
|
|
|
|
|
@code{ns-3} and so recommend becoming as familiar as possible with it. It is
|
|
|
|
|
probably the case that understanding the rest of the @code{ns-3} system will
|
|
|
|
|
be quite simple once you have mastered the tracing system
|