author | Mitch Watrous |
Thu, 16 Aug 2012 09:13:36 -0700 | |
changeset 8986 | 3391f6a7fb3b |
parent 8906 | 11398e7abe77 |
child 9018 | 73773563c94c |
permissions | -rw-r--r-- |
6754 | 1 |
.. include:: replace.txt |
2 |
||
3 |
||
4 |
Tracing |
|
5 |
------- |
|
6 |
||
7 |
Background |
|
8 |
********** |
|
9 |
||
10 |
As mentioned in the Using the Tracing System section, the whole point of running |
|
11 |
an |ns3| simulation is to generate output for study. You have two basic |
|
12 |
strategies to work with in |ns3|: using generic pre-defined bulk output |
|
13 |
mechanisms and parsing their content to extract interesting information; or |
|
14 |
somehow developing an output mechanism that conveys exactly (and perhaps only) |
|
15 |
the information wanted. |
|
16 |
||
17 |
Using pre-defined bulk output mechanisms has the advantage of not requiring any |
|
18 |
changes to |ns3|, but it does require programming. Often, pcap or NS_LOG |
|
19 |
output messages are gathered during simulation runs and separately run through |
|
20 |
scripts that use grep, sed or awk to parse the messages and reduce and transform |
|
21 |
the data to a manageable form. Programs must be written to do the |
|
22 |
transformation, so this does not come for free. Of course, if the information |
|
23 |
of interest in does not exist in any of the pre-defined output mechanisms, |
|
24 |
this approach fails. |
|
25 |
||
26 |
If you need to add some tidbit of information to the pre-defined bulk mechanisms, |
|
27 |
this can certainly be done; and if you use one of the |ns3| mechanisms, |
|
28 |
you may get your code added as a contribution. |
|
29 |
||
30 |
|ns3| provides another mechanism, called Tracing, that avoids some of the |
|
31 |
problems inherent in the bulk output mechanisms. It has several important |
|
32 |
advantages. First, you can reduce the amount of data you have to manage by only |
|
33 |
tracing the events of interest to you (for large simulations, dumping everything |
|
34 |
to disk for post-processing can create I/O bottlenecks). Second, if you use this |
|
35 |
method, you can control the format of the output directly so you avoid the |
|
36 |
postprocessing step with sed or awk script. If you desire, your output can be |
|
37 |
formatted directly into a form acceptable by gnuplot, for example. You can add |
|
38 |
hooks in the core which can then be accessed by other users, but which will |
|
39 |
produce no information unless explicitly asked to do so. For these reasons, we |
|
40 |
believe that the |ns3| tracing system is the best way to get information |
|
41 |
out of a simulation and is also therefore one of the most important mechanisms |
|
42 |
to understand in |ns3|. |
|
43 |
||
44 |
Blunt Instruments |
|
45 |
+++++++++++++++++ |
|
46 |
There are many ways to get information out of a program. The most |
|
47 |
straightforward way is to just directly print the information to the standard |
|
48 |
output, as in, |
|
49 |
||
50 |
:: |
|
51 |
||
52 |
#include <iostream> |
|
53 |
... |
|
54 |
void |
|
55 |
SomeFunction (void) |
|
56 |
{ |
|
57 |
uint32_t x = SOME_INTERESTING_VALUE; |
|
58 |
... |
|
59 |
std::cout << "The value of x is " << x << std::endl; |
|
60 |
... |
|
61 |
} |
|
62 |
||
63 |
Nobody is going to prevent you from going deep into the core of |ns3| and |
|
64 |
adding print statements. This is insanely easy to do and, after all, you have |
|
65 |
complete control of your own |ns3| branch. This will probably not turn |
|
66 |
out to be very satisfactory in the long term, though. |
|
67 |
||
68 |
As the number of print statements increases in your programs, the task of |
|
69 |
dealing with the large number of outputs will become more and more complicated. |
|
70 |
Eventually, you may feel the need to control what information is being printed |
|
71 |
in some way; perhaps by turning on and off certain categories of prints, or |
|
72 |
increasing or decreasing the amount of information you want. If you continue |
|
73 |
down this path you may discover that you have re-implemented the ``NS_LOG`` |
|
74 |
mechanism. In order to avoid that, one of the first things you might consider |
|
75 |
is using ``NS_LOG`` itself. |
|
76 |
||
77 |
We mentioned above that one way to get information out of |ns3| is to |
|
78 |
parse existing NS_LOG output for interesting information. If you discover that |
|
79 |
some tidbit of information you need is not present in existing log output, you |
|
80 |
could edit the core of |ns3| and simply add your interesting information |
|
81 |
to the output stream. Now, this is certainly better than adding your own |
|
82 |
print statements since it follows |ns3| coding conventions and could |
|
83 |
potentially be useful to other people as a patch to the existing core. |
|
84 |
||
85 |
Let's pick a random example. If you wanted to add more logging to the |
|
86 |
|ns3| TCP socket (``tcp-socket-base.cc``) you could just add a new |
|
87 |
message down in the implementation. Notice that in TcpSocketBase::ReceivedAck() |
|
8906
11398e7abe77
Sphinx doc bug fixes
Peter D. Barnes, Jr. <barnes26@llnl.gov>
parents:
8772
diff
changeset
|
88 |
there is no log message for the no ACK case. You could simply add one, |
6754 | 89 |
changing the code from: |
90 |
||
91 |
:: |
|
92 |
||
93 |
/** Process the newly received ACK */ |
|
94 |
void |
|
95 |
TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader) |
|
96 |
{ |
|
97 |
NS_LOG_FUNCTION (this << tcpHeader); |
|
98 |
||
99 |
// Received ACK. Compare the ACK number against highest unacked seqno |
|
100 |
if (0 == (tcpHeader.GetFlags () & TcpHeader::ACK)) |
|
101 |
{ // Ignore if no ACK flag |
|
102 |
} |
|
103 |
... |
|
104 |
||
105 |
to add a new ``NS_LOG_LOGIC`` in the appropriate statement: |
|
106 |
||
107 |
:: |
|
108 |
||
109 |
/** Process the newly received ACK */ |
|
110 |
void |
|
111 |
TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader) |
|
112 |
{ |
|
113 |
NS_LOG_FUNCTION (this << tcpHeader); |
|
114 |
||
115 |
// Received ACK. Compare the ACK number against highest unacked seqno |
|
116 |
if (0 == (tcpHeader.GetFlags () & TcpHeader::ACK)) |
|
117 |
{ // Ignore if no ACK flag |
|
118 |
NS_LOG_LOGIC ("TcpSocketBase " << this << " no ACK flag"); |
|
119 |
} |
|
120 |
... |
|
121 |
||
122 |
This may seem fairly simple and satisfying at first glance, but something to |
|
123 |
consider is that you will be writing code to add the ``NS_LOG`` statement |
|
124 |
and you will also have to write code (as in grep, sed or awk scripts) to parse |
|
125 |
the log output in order to isolate your information. This is because even |
|
126 |
though you have some control over what is output by the logging system, you |
|
127 |
only have control down to the log component level. |
|
128 |
||
129 |
If you are adding code to an existing module, you will also have to live with the |
|
130 |
output that every other developer has found interesting. You may find that in |
|
131 |
order to get the small amount of information you need, you may have to wade |
|
132 |
through huge amounts of extraneous messages that are of no interest to you. You |
|
133 |
may be forced to save huge log files to disk and process them down to a few lines |
|
134 |
whenever you want to do anything. |
|
135 |
||
136 |
Since there are no guarantees in |ns3| about the stability of ``NS_LOG`` |
|
137 |
output, you may also discover that pieces of log output on which you depend |
|
138 |
disappear or change between releases. If you depend on the structure of the |
|
139 |
output, you may find other messages being added or deleted which may affect your |
|
140 |
parsing code. |
|
141 |
||
142 |
For these reasons, we consider prints to ``std::cout`` and NS_LOG messages |
|
143 |
to be quick and dirty ways to get more information out of |ns3|. |
|
144 |
||
145 |
It is desirable to have a stable facility using stable APIs that allow one to |
|
146 |
reach into the core system and only get the information required. It is |
|
147 |
desirable to be able to do this without having to change and recompile the |
|
148 |
core system. Even better would be a system that notified the user when an item |
|
149 |
of interest changed or an interesting event happened so the user doesn't have |
|
150 |
to actively poke around in the system looking for things. |
|
151 |
||
152 |
The |ns3| tracing system is designed to work along those lines and is |
|
153 |
well-integrated with the Attribute and Config subsystems allowing for relatively |
|
154 |
simple use scenarios. |
|
155 |
||
156 |
Overview |
|
157 |
******** |
|
158 |
||
159 |
The ns-3 tracing system is built on the concepts of independent tracing sources |
|
160 |
and tracing sinks; along with a uniform mechanism for connecting sources to sinks. |
|
161 |
||
162 |
Trace sources are entities that can signal events that happen in a simulation and |
|
163 |
provide access to interesting underlying data. For example, a trace source could |
|
164 |
indicate when a packet is received by a net device and provide access to the |
|
165 |
packet contents for interested trace sinks. A trace source might also indicate |
|
166 |
when an interesting state change happens in a model. For example, the congestion |
|
167 |
window of a TCP model is a prime candidate for a trace source. |
|
168 |
||
169 |
Trace sources are not useful by themselves; they must be connected to other pieces |
|
170 |
of code that actually do something useful with the information provided by the source. |
|
171 |
The entities that consume trace information are called trace sinks. Trace sources |
|
172 |
are generators of events and trace sinks are consumers. This explicit division |
|
173 |
allows for large numbers of trace sources to be scattered around the system in |
|
174 |
places which model authors believe might be useful. |
|
175 |
||
176 |
There can be zero or more consumers of trace events generated by a trace source. |
|
177 |
One can think of a trace source as a kind of point-to-multipoint information link. |
|
178 |
Your code looking for trace events from a particular piece of core code could |
|
179 |
happily coexist with other code doing something entirely different from the same |
|
180 |
information. |
|
181 |
||
182 |
Unless a user connects a trace sink to one of these sources, nothing is output. By |
|
183 |
using the tracing system, both you and other people at the same trace source are |
|
184 |
getting exactly what they want and only what they want out of the system. Neither |
|
185 |
of you are impacting any other user by changing what information is output by the |
|
186 |
system. If you happen to add a trace source, your work as a good open-source |
|
187 |
citizen may allow other users to provide new utilities that are perhaps very useful |
|
188 |
overall, without making any changes to the |ns3| core. |
|
189 |
||
190 |
A Simple Low-Level Example |
|
191 |
++++++++++++++++++++++++++ |
|
192 |
||
193 |
Let's take a few minutes and walk through a simple tracing example. We are going |
|
194 |
to need a little background on Callbacks to understand what is happening in the |
|
195 |
example, so we have to take a small detour right away. |
|
196 |
||
197 |
Callbacks |
|
198 |
~~~~~~~~~ |
|
199 |
||
200 |
The goal of the Callback system in |ns3| is to allow one piece of code to |
|
201 |
call a function (or method in C++) without any specific inter-module dependency. |
|
202 |
This ultimately means you need some kind of indirection -- you treat the address |
|
203 |
of the called function as a variable. This variable is called a pointer-to-function |
|
204 |
variable. The relationship between function and pointer-to-function pointer is |
|
205 |
really no different that that of object and pointer-to-object. |
|
206 |
||
207 |
In C the canonical example of a pointer-to-function is a |
|
208 |
pointer-to-function-returning-integer (PFI). For a PFI taking one int parameter, |
|
209 |
this could be declared like, |
|
210 |
||
211 |
:: |
|
212 |
||
213 |
int (*pfi)(int arg) = 0; |
|
214 |
||
215 |
What you get from this is a variable named simply "pfi" that is initialized |
|
216 |
to the value 0. If you want to initialize this pointer to something meaningful, |
|
217 |
you have to have a function with a matching signature. In this case, you could |
|
218 |
provide a function that looks like, |
|
219 |
||
220 |
:: |
|
221 |
||
222 |
int MyFunction (int arg) {} |
|
223 |
||
224 |
If you have this target, you can initialize the variable to point to your |
|
225 |
function: |
|
226 |
||
227 |
:: |
|
228 |
||
229 |
pfi = MyFunction; |
|
230 |
||
231 |
You can then call MyFunction indirectly using the more suggestive form of |
|
232 |
the call, |
|
233 |
||
234 |
:: |
|
235 |
||
236 |
int result = (*pfi) (1234); |
|
237 |
||
238 |
This is suggestive since it looks like you are dereferencing the function |
|
239 |
pointer just like you would dereference any pointer. Typically, however, |
|
240 |
people take advantage of the fact that the compiler knows what is going on |
|
241 |
and will just use a shorter form, |
|
242 |
||
243 |
:: |
|
244 |
||
245 |
int result = pfi (1234); |
|
246 |
||
247 |
This looks like you are calling a function named "pfi," but the compiler is |
|
248 |
smart enough to know to call through the variable ``pfi`` indirectly to |
|
249 |
the function ``MyFunction``. |
|
250 |
||
251 |
Conceptually, this is almost exactly how the tracing system will work. |
|
252 |
Basically, a trace source *is* a callback. When a trace sink expresses |
|
253 |
interest in receiving trace events, it adds a Callback to a list of Callbacks |
|
254 |
internally held by the trace source. When an interesting event happens, the |
|
255 |
trace source invokes its ``operator()`` providing zero or more parameters. |
|
256 |
The ``operator()`` eventually wanders down into the system and does something |
|
257 |
remarkably like the indirect call you just saw. It provides zero or more |
|
258 |
parameters (the call to "pfi" above passed one parameter to the target function |
|
259 |
``MyFunction``. |
|
260 |
||
261 |
The important difference that the tracing system adds is that for each trace |
|
262 |
source there is an internal list of Callbacks. Instead of just making one |
|
263 |
indirect call, a trace source may invoke any number of Callbacks. When a trace |
|
264 |
sink expresses interest in notifications from a trace source, it basically just |
|
265 |
arranges to add its own function to the callback list. |
|
266 |
||
267 |
If you are interested in more details about how this is actually arranged in |
|
268 |
|ns3|, feel free to peruse the Callback section of the manual. |
|
269 |
||
270 |
Example Code |
|
271 |
~~~~~~~~~~~~ |
|
272 |
||
273 |
We have provided some code to implement what is really the simplest example |
|
274 |
of tracing that can be assembled. You can find this code in the tutorial |
|
275 |
directory as ``fourth.cc``. Let's walk through it. |
|
276 |
||
277 |
:: |
|
278 |
||
279 |
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
|
280 |
/* |
|
281 |
* This program is free software; you can redistribute it and/or modify |
|
282 |
* it under the terms of the GNU General Public License version 2 as |
|
283 |
* published by the Free Software Foundation; |
|
284 |
* |
|
285 |
* This program is distributed in the hope that it will be useful, |
|
286 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
287 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
288 |
* GNU General Public License for more details. |
|
289 |
* |
|
290 |
* You should have received a copy of the GNU General Public License |
|
291 |
* along with this program; if not, write to the Free Software |
|
292 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
293 |
*/ |
|
294 |
||
295 |
#include "ns3/object.h" |
|
296 |
#include "ns3/uinteger.h" |
|
297 |
#include "ns3/traced-value.h" |
|
298 |
#include "ns3/trace-source-accessor.h" |
|
299 |
||
300 |
#include <iostream> |
|
301 |
||
302 |
using namespace ns3; |
|
303 |
||
304 |
Most of this code should be quite familiar to you. As mentioned above, the |
|
305 |
trace system makes heavy use of the Object and Attribute systems, so you will |
|
306 |
need to include them. The first two includes above bring in the declarations |
|
307 |
for those systems explicitly. You could use the core module header, but this |
|
308 |
illustrates how simple this all really is. |
|
309 |
||
310 |
The file, ``traced-value.h`` brings in the required declarations for tracing |
|
311 |
of data that obeys value semantics. In general, value semantics just means that |
|
312 |
you can pass the object around, not an address. In order to use value semantics |
|
313 |
at all you have to have an object with an associated copy constructor and |
|
314 |
assignment operator available. We extend the requirements to talk about the set |
|
315 |
of operators that are pre-defined for plain-old-data (POD) types. Operator=, |
|
316 |
operator++, operator---, operator+, operator==, etc. |
|
317 |
||
318 |
What this all really means is that you will be able to trace changes to a C++ |
|
319 |
object made using those operators. |
|
320 |
||
321 |
Since the tracing system is integrated with Attributes, and Attributes work |
|
322 |
with Objects, there must be an |ns3| ``Object`` for the trace source |
|
323 |
to live in. The next code snippet declares and defines a simple Object we can |
|
324 |
work with. |
|
325 |
||
326 |
:: |
|
327 |
||
328 |
class MyObject : public Object |
|
329 |
{ |
|
330 |
public: |
|
331 |
static TypeId GetTypeId (void) |
|
332 |
{ |
|
333 |
static TypeId tid = TypeId ("MyObject") |
|
334 |
.SetParent (Object::GetTypeId ()) |
|
335 |
.AddConstructor<MyObject> () |
|
336 |
.AddTraceSource ("MyInteger", |
|
337 |
"An integer value to trace.", |
|
338 |
MakeTraceSourceAccessor (&MyObject::m_myInt)) |
|
339 |
; |
|
340 |
return tid; |
|
341 |
} |
|
342 |
||
343 |
MyObject () {} |
|
344 |
TracedValue<int32_t> m_myInt; |
|
345 |
}; |
|
346 |
||
347 |
The two important lines of code, above, with respect to tracing are the |
|
348 |
``.AddTraceSource`` and the ``TracedValue`` declaration of ``m_myInt``. |
|
349 |
||
350 |
The ``.AddTraceSource`` provides the "hooks" used for connecting the trace |
|
351 |
source to the outside world through the config system. The ``TracedValue`` |
|
352 |
declaration provides the infrastructure that overloads the operators mentioned |
|
353 |
above and drives the callback process. |
|
354 |
||
355 |
:: |
|
356 |
||
357 |
void |
|
358 |
IntTrace (int32_t oldValue, int32_t newValue) |
|
359 |
{ |
|
360 |
std::cout << "Traced " << oldValue << " to " << newValue << std::endl; |
|
361 |
} |
|
362 |
||
363 |
This is the definition of the trace sink. It corresponds directly to a callback |
|
364 |
function. Once it is connected, this function will be called whenever one of the |
|
365 |
overloaded operators of the ``TracedValue`` is executed. |
|
366 |
||
367 |
We have now seen the trace source and the trace sink. What remains is code to |
|
368 |
connect the source to the sink. |
|
369 |
||
370 |
:: |
|
371 |
||
372 |
int |
|
373 |
main (int argc, char *argv[]) |
|
374 |
{ |
|
375 |
Ptr<MyObject> myObject = CreateObject<MyObject> (); |
|
376 |
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace)); |
|
377 |
||
378 |
myObject->m_myInt = 1234; |
|
379 |
} |
|
380 |
||
381 |
Here we first create the Object in which the trace source lives. |
|
382 |
||
383 |
The next step, the ``TraceConnectWithoutContext``, forms the connection |
|
384 |
between the trace source and the trace sink. Notice the ``MakeCallback`` |
|
385 |
template function. This function does the magic required to create the |
|
386 |
underlying |ns3| Callback object and associate it with the function |
|
387 |
``IntTrace``. TraceConnect makes the association between your provided |
|
388 |
function and the overloaded ``operator()`` in the traced variable referred |
|
389 |
to by the "MyInteger" Attribute. After this association is made, the trace |
|
390 |
source will "fire" your provided callback function. |
|
391 |
||
392 |
The code to make all of this happen is, of course, non-trivial, but the essence |
|
393 |
is that you are arranging for something that looks just like the ``pfi()`` |
|
394 |
example above to be called by the trace source. The declaration of the |
|
395 |
``TracedValue<int32_t> m_myInt;`` in the Object itself performs the magic |
|
396 |
needed to provide the overloaded operators (++, ---, etc.) that will use the |
|
397 |
``operator()`` to actually invoke the Callback with the desired parameters. |
|
398 |
The ``.AddTraceSource`` performs the magic to connect the Callback to the |
|
399 |
Config system, and ``TraceConnectWithoutContext`` performs the magic to |
|
400 |
connect your function to the trace source, which is specified by Attribute |
|
401 |
name. |
|
402 |
||
403 |
Let's ignore the bit about context for now. |
|
404 |
||
405 |
Finally, the line, |
|
406 |
||
407 |
:: |
|
408 |
||
409 |
myObject->m_myInt = 1234; |
|
410 |
||
411 |
should be interpreted as an invocation of ``operator=`` on the member |
|
412 |
variable ``m_myInt`` with the integer ``1234`` passed as a parameter. |
|
413 |
||
414 |
It turns out that this operator is defined (by ``TracedValue``) to execute |
|
415 |
a callback that returns void and takes two integer values as parameters --- |
|
416 |
an old value and a new value for the integer in question. That is exactly |
|
417 |
the function signature for the callback function we provided --- ``IntTrace``. |
|
418 |
||
419 |
To summarize, a trace source is, in essence, a variable that holds a list of |
|
420 |
callbacks. A trace sink is a function used as the target of a callback. The |
|
421 |
Attribute and object type information systems are used to provide a way to |
|
422 |
connect trace sources to trace sinks. The act of "hitting" a trace source |
|
423 |
is executing an operator on the trace source which fires callbacks. This |
|
424 |
results in the trace sink callbacks registering interest in the source being |
|
425 |
called with the parameters provided by the source. |
|
426 |
||
427 |
If you now build and run this example, |
|
428 |
||
429 |
:: |
|
430 |
||
431 |
./waf --run fourth |
|
432 |
||
433 |
you will see the output from the ``IntTrace`` function execute as soon as the |
|
434 |
trace source is hit: |
|
435 |
||
436 |
:: |
|
437 |
||
438 |
Traced 0 to 1234 |
|
439 |
||
440 |
When we executed the code, ``myObject->m_myInt = 1234;``, the trace source |
|
441 |
fired and automatically provided the before and after values to the trace sink. |
|
442 |
The function ``IntTrace`` then printed this to the standard output. No |
|
443 |
problem. |
|
444 |
||
445 |
Using the Config Subsystem to Connect to Trace Sources |
|
446 |
++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|
447 |
||
448 |
The ``TraceConnectWithoutContext`` call shown above in the simple example is |
|
449 |
actually very rarely used in the system. More typically, the ``Config`` |
|
450 |
subsystem is used to allow selecting a trace source in the system using what is |
|
451 |
called a *config path*. We saw an example of this in the previous section |
|
452 |
where we hooked the "CourseChange" event when we were playing with |
|
453 |
``third.cc``. |
|
454 |
||
455 |
Recall that we defined a trace sink to print course change information from the |
|
456 |
mobility models of our simulation. It should now be a lot more clear to you |
|
457 |
what this function is doing. |
|
458 |
||
459 |
:: |
|
460 |
||
461 |
void |
|
462 |
CourseChange (std::string context, Ptr<const MobilityModel> model) |
|
463 |
{ |
|
464 |
Vector position = model->GetPosition (); |
|
465 |
NS_LOG_UNCOND (context << |
|
466 |
" x = " << position.x << ", y = " << position.y); |
|
467 |
} |
|
468 |
||
469 |
When we connected the "CourseChange" trace source to the above trace sink, |
|
470 |
we used what is called a "Config Path" to specify the source when we |
|
471 |
arranged a connection between the pre-defined trace source and the new trace |
|
472 |
sink: |
|
473 |
||
474 |
:: |
|
475 |
||
476 |
std::ostringstream oss; |
|
477 |
oss << |
|
478 |
"/NodeList/" << wifiStaNodes.Get (nWifi - 1)->GetId () << |
|
479 |
"/$ns3::MobilityModel/CourseChange"; |
|
480 |
||
481 |
Config::Connect (oss.str (), MakeCallback (&CourseChange)); |
|
482 |
||
483 |
Let's try and make some sense of what is sometimes considered relatively |
|
484 |
mysterious code. For the purposes of discussion, assume that the node |
|
485 |
number returned by the ``GetId()`` is "7". In this case, the path |
|
486 |
above turns out to be, |
|
487 |
||
488 |
:: |
|
489 |
||
490 |
"/NodeList/7/$ns3::MobilityModel/CourseChange" |
|
491 |
||
492 |
The last segment of a config path must be an ``Attribute`` of an |
|
493 |
``Object``. In fact, if you had a pointer to the ``Object`` that has the |
|
494 |
"CourseChange" ``Attribute`` handy, you could write this just like we did |
|
495 |
in the previous example. You know by now that we typically store pointers to |
|
496 |
our nodes in a NodeContainer. In the ``third.cc`` example, the Nodes of |
|
497 |
interest are stored in the ``wifiStaNodes`` NodeContainer. In fact, while |
|
498 |
putting the path together, we used this container to get a Ptr<Node> which we |
|
499 |
used to call GetId() on. We could have used this Ptr<Node> directly to call |
|
500 |
a connect method directly: |
|
501 |
||
502 |
:: |
|
503 |
||
504 |
Ptr<Object> theObject = wifiStaNodes.Get (nWifi - 1); |
|
505 |
theObject->TraceConnectWithoutContext ("CourseChange", MakeCallback (&CourseChange)); |
|
506 |
||
507 |
In the ``third.cc`` example, we actually want an additional "context" to |
|
508 |
be delivered along with the Callback parameters (which will be explained below) so we |
|
509 |
could actually use the following equivalent code, |
|
510 |
||
511 |
:: |
|
512 |
||
513 |
Ptr<Object> theObject = wifiStaNodes.Get (nWifi - 1); |
|
514 |
theObject->TraceConnect ("CourseChange", MakeCallback (&CourseChange)); |
|
515 |
||
516 |
It turns out that the internal code for ``Config::ConnectWithoutContext`` and |
|
517 |
``Config::Connect`` actually do find a Ptr<Object> and call the appropriate |
|
518 |
TraceConnect method at the lowest level. |
|
519 |
||
520 |
The ``Config`` functions take a path that represents a chain of ``Object`` |
|
521 |
pointers. Each segment of a path corresponds to an Object Attribute. The last |
|
522 |
segment is the Attribute of interest, and prior segments must be typed to contain |
|
523 |
or find Objects. The ``Config`` code parses and "walks" this path until it |
|
524 |
gets to the final segment of the path. It then interprets the last segment as |
|
525 |
an ``Attribute`` on the last Object it found while walking the path. The |
|
526 |
``Config`` functions then call the appropriate ``TraceConnect`` or |
|
527 |
``TraceConnectWithoutContext`` method on the final Object. Let's see what |
|
528 |
happens in a bit more detail when the above path is walked. |
|
529 |
||
530 |
The leading "/" character in the path refers to a so-called namespace. One |
|
531 |
of the predefined namespaces in the config system is "NodeList" which is a |
|
532 |
list of all of the nodes in the simulation. Items in the list are referred to |
|
533 |
by indices into the list, so "/NodeList/7" refers to the eighth node in the |
|
534 |
list of nodes created during the simulation. This reference is actually a |
|
535 |
``Ptr<Node>`` and so is a subclass of an ``ns3::Object``. |
|
536 |
||
537 |
As described in the Object Model section of the |ns3| manual, we support |
|
538 |
Object Aggregation. This allows us to form an association between different |
|
539 |
Objects without any programming. Each Object in an Aggregation can be reached |
|
540 |
from the other Objects. |
|
541 |
||
542 |
The next path segment being walked begins with the "$" character. This |
|
543 |
indicates to the config system that a ``GetObject`` call should be made |
|
544 |
looking for the type that follows. It turns out that the MobilityHelper used in |
|
545 |
``third.cc`` arranges to Aggregate, or associate, a mobility model to each of |
|
546 |
the wireless Nodes. When you add the "$" you are asking for another Object that |
|
547 |
has presumably been previously aggregated. You can think of this as switching |
|
548 |
pointers from the original Ptr<Node> as specified by "/NodeList/7" to its |
|
549 |
associated mobility model --- which is of type "$ns3::MobilityModel". If you |
|
550 |
are familiar with ``GetObject``, we have asked the system to do the following: |
|
551 |
||
552 |
:: |
|
553 |
||
554 |
Ptr<MobilityModel> mobilityModel = node->GetObject<MobilityModel> () |
|
555 |
||
556 |
We are now at the last Object in the path, so we turn our attention to the |
|
557 |
Attributes of that Object. The ``MobilityModel`` class defines an Attribute |
|
558 |
called "CourseChange". You can see this by looking at the source code in |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
559 |
``src/mobility/model/mobility-model.cc`` and searching for "CourseChange" in your |
6754 | 560 |
favorite editor. You should find, |
561 |
||
562 |
:: |
|
563 |
||
564 |
.AddTraceSource ("CourseChange", |
|
565 |
"The value of the position and/or velocity vector changed", |
|
566 |
MakeTraceSourceAccessor (&MobilityModel::m_courseChangeTrace)) |
|
567 |
||
568 |
which should look very familiar at this point. |
|
569 |
||
570 |
If you look for the corresponding declaration of the underlying traced variable |
|
571 |
in ``mobility-model.h`` you will find |
|
572 |
||
573 |
:: |
|
574 |
||
575 |
TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace; |
|
576 |
||
577 |
The type declaration ``TracedCallback`` identifies ``m_courseChangeTrace`` |
|
578 |
as a special list of Callbacks that can be hooked using the Config functions |
|
579 |
described above. |
|
580 |
||
581 |
The ``MobilityModel`` class is designed to be a base class providing a common |
|
582 |
interface for all of the specific subclasses. If you search down to the end of |
|
583 |
the file, you will see a method defined called ``NotifyCourseChange()``: |
|
584 |
||
585 |
:: |
|
586 |
||
587 |
void |
|
588 |
MobilityModel::NotifyCourseChange (void) const |
|
589 |
{ |
|
590 |
m_courseChangeTrace(this); |
|
591 |
} |
|
592 |
||
593 |
Derived classes will call into this method whenever they do a course change to |
|
594 |
support tracing. This method invokes ``operator()`` on the underlying |
|
595 |
``m_courseChangeTrace``, which will, in turn, invoke all of the registered |
|
596 |
Callbacks, calling all of the trace sinks that have registered interest in the |
|
597 |
trace source by calling a Config function. |
|
598 |
||
599 |
So, in the ``third.cc`` example we looked at, whenever a course change is |
|
600 |
made in one of the ``RandomWalk2dMobilityModel`` instances installed, there |
|
601 |
will be a ``NotifyCourseChange()`` call which calls up into the |
|
602 |
``MobilityModel`` base class. As seen above, this invokes ``operator()`` |
|
603 |
on ``m_courseChangeTrace``, which in turn, calls any registered trace sinks. |
|
604 |
In the example, the only code registering an interest was the code that provided |
|
605 |
the config path. Therefore, the ``CourseChange`` function that was hooked |
|
606 |
from Node number seven will be the only Callback called. |
|
607 |
||
608 |
The final piece of the puzzle is the "context". Recall that we saw an output |
|
609 |
looking something like the following from ``third.cc``: |
|
610 |
||
611 |
:: |
|
612 |
||
613 |
/NodeList/7/$ns3::MobilityModel/CourseChange x = 7.27897, y = 2.22677 |
|
614 |
||
615 |
The first part of the output is the context. It is simply the path through |
|
616 |
which the config code located the trace source. In the case we have been looking at |
|
617 |
there can be any number of trace sources in the system corresponding to any number |
|
618 |
of nodes with mobility models. There needs to be some way to identify which trace |
|
619 |
source is actually the one that fired the Callback. An easy way is to request a |
|
620 |
trace context when you ``Config::Connect``. |
|
621 |
||
622 |
How to Find and Connect Trace Sources, and Discover Callback Signatures |
|
623 |
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|
624 |
||
625 |
The first question that inevitably comes up for new users of the Tracing system is, |
|
626 |
"okay, I know that there must be trace sources in the simulation core, but how do |
|
627 |
I find out what trace sources are available to me"? |
|
628 |
||
629 |
The second question is, "okay, I found a trace source, how do I figure out the |
|
630 |
config path to use when I connect to it"? |
|
631 |
||
632 |
The third question is, "okay, I found a trace source, how do I figure out what |
|
633 |
the return type and formal arguments of my callback function need to be"? |
|
634 |
||
635 |
The fourth question is, "okay, I typed that all in and got this incredibly bizarre |
|
636 |
error message, what in the world does it mean"? |
|
637 |
||
638 |
What Trace Sources are Available? |
|
639 |
+++++++++++++++++++++++++++++++++ |
|
640 |
||
7480
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
641 |
The answer to this question is found in the |ns3| Doxygen. If you go to |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
642 |
the project web site, |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
643 |
`ns-3 project |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
644 |
<http://www.nsnam.org>`_, you will find a link to "Documentation" in the navigation bar. If you select this link, you will be |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
645 |
taken to our documentation page. There |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
646 |
is a link to "Latest Release" that will take you to the documentation |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
647 |
for the latest stable release of |ns3|. |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
648 |
If you select the "API Documentation" link, you will be |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
649 |
taken to the |ns3| API documentation page. |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
650 |
|
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
651 |
Expand the "Modules" book in the NS-3 |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
652 |
documentation tree by clicking the "+" box. Now, expand |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
653 |
the "C++ Constructs Used by All Modules" book in the tree by clicking its "+" box. You should now |
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
654 |
see four extremely useful links: |
6754 | 655 |
|
656 |
* The list of all trace sources |
|
657 |
* The list of all attributes |
|
658 |
* The list of all global values |
|
7480
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
659 |
* Debugging |
6754 | 660 |
|
661 |
The list of interest to us here is "the list of all trace sources". Go |
|
662 |
ahead and select that link. You will see, perhaps not too surprisingly, a |
|
663 |
list of all of the trace sources available in the |ns3| core. |
|
664 |
||
665 |
As an example, scroll down to ``ns3::MobilityModel``. You will find |
|
666 |
an entry for |
|
667 |
||
668 |
:: |
|
669 |
||
670 |
CourseChange: The value of the position and/or velocity vector changed |
|
671 |
||
672 |
You should recognize this as the trace source we used in the ``third.cc`` |
|
673 |
example. Perusing this list will be helpful. |
|
674 |
||
675 |
What String do I use to Connect? |
|
676 |
++++++++++++++++++++++++++++++++ |
|
677 |
||
678 |
The easiest way to do this is to grep around in the |ns3| codebase for someone |
|
679 |
who has already figured it out, You should always try to copy someone else's |
|
680 |
working code before you start to write your own. Try something like: |
|
681 |
||
682 |
:: |
|
683 |
||
684 |
find . -name '*.cc' | xargs grep CourseChange | grep Connect |
|
685 |
||
686 |
and you may find your answer along with working code. For example, in this |
|
687 |
case, ``./ns-3-dev/examples/wireless/mixed-wireless.cc`` has something |
|
688 |
just waiting for you to use: |
|
689 |
||
690 |
:: |
|
691 |
||
692 |
Config::Connect ("/NodeList/*/$ns3::MobilityModel/CourseChange", |
|
693 |
MakeCallback (&CourseChangeCallback)); |
|
694 |
||
695 |
If you cannot find any examples in the distribution, you can find this out |
|
696 |
from the |ns3| Doxygen. It will probably be simplest just to walk |
|
697 |
through the "CourseChanged" example. |
|
698 |
||
699 |
Let's assume that you have just found the "CourseChanged" trace source in |
|
700 |
"The list of all trace sources" and you want to figure out how to connect to |
|
701 |
it. You know that you are using (again, from the ``third.cc`` example) an |
|
702 |
``ns3::RandomWalk2dMobilityModel``. So open the "Class List" book in |
|
703 |
the NS-3 documentation tree by clicking its "+" box. You will now see a |
|
704 |
list of all of the classes in |ns3|. Scroll down until you see the |
|
705 |
entry for ``ns3::RandomWalk2dMobilityModel`` and follow that link. |
|
706 |
You should now be looking at the "ns3::RandomWalk2dMobilityModel Class |
|
707 |
Reference". |
|
708 |
||
709 |
If you now scroll down to the "Member Function Documentation" section, you |
|
710 |
will see documentation for the ``GetTypeId`` function. You constructed one |
|
711 |
of these in the simple tracing example above: |
|
712 |
||
713 |
:: |
|
714 |
||
715 |
static TypeId GetTypeId (void) |
|
716 |
{ |
|
717 |
static TypeId tid = TypeId ("MyObject") |
|
718 |
.SetParent (Object::GetTypeId ()) |
|
719 |
.AddConstructor<MyObject> () |
|
720 |
.AddTraceSource ("MyInteger", |
|
721 |
"An integer value to trace.", |
|
722 |
MakeTraceSourceAccessor (&MyObject::m_myInt)) |
|
723 |
; |
|
724 |
return tid; |
|
725 |
} |
|
726 |
||
727 |
As mentioned above, this is the bit of code that connected the Config |
|
728 |
and Attribute systems to the underlying trace source. This is also the |
|
729 |
place where you should start looking for information about the way to |
|
730 |
connect. |
|
731 |
||
732 |
You are looking at the same information for the RandomWalk2dMobilityModel; and |
|
733 |
the information you want is now right there in front of you in the Doxygen: |
|
734 |
||
735 |
:: |
|
736 |
||
737 |
This object is accessible through the following paths with Config::Set and Config::Connect: |
|
738 |
||
739 |
/NodeList/[i]/$ns3::MobilityModel/$ns3::RandomWalk2dMobilityModel |
|
740 |
||
741 |
The documentation tells you how to get to the ``RandomWalk2dMobilityModel`` |
|
742 |
Object. Compare the string above with the string we actually used in the |
|
743 |
example code: |
|
744 |
||
745 |
:: |
|
746 |
||
747 |
"/NodeList/7/$ns3::MobilityModel" |
|
748 |
||
749 |
The difference is due to the fact that two ``GetObject`` calls are implied |
|
750 |
in the string found in the documentation. The first, for ``$ns3::MobilityModel`` |
|
751 |
will query the aggregation for the base class. The second implied |
|
752 |
``GetObject`` call, for ``$ns3::RandomWalk2dMobilityModel``, is used to "cast" |
|
753 |
the base class to the concrete implementation class. The documentation shows |
|
754 |
both of these operations for you. It turns out that the actual Attribute you are |
|
755 |
going to be looking for is found in the base class as we have seen. |
|
756 |
||
757 |
Look further down in the ``GetTypeId`` doxygen. You will find, |
|
758 |
||
759 |
:: |
|
760 |
||
761 |
No TraceSources defined for this type. |
|
762 |
TraceSources defined in parent class ns3::MobilityModel: |
|
763 |
||
764 |
CourseChange: The value of the position and/or velocity vector changed |
|
765 |
Reimplemented from ns3::MobilityModel |
|
766 |
||
767 |
This is exactly what you need to know. The trace source of interest is found in |
|
768 |
``ns3::MobilityModel`` (which you knew anyway). The interesting thing this |
|
769 |
bit of Doxygen tells you is that you don't need that extra cast in the config |
|
770 |
path above to get to the concrete class, since the trace source is actually in |
|
771 |
the base class. Therefore the additional ``GetObject`` is not required and |
|
772 |
you simply use the path: |
|
773 |
||
774 |
:: |
|
775 |
||
776 |
/NodeList/[i]/$ns3::MobilityModel |
|
777 |
||
778 |
which perfectly matches the example path: |
|
779 |
||
780 |
:: |
|
781 |
||
782 |
/NodeList/7/$ns3::MobilityModel |
|
783 |
||
784 |
What Return Value and Formal Arguments? |
|
785 |
+++++++++++++++++++++++++++++++++++++++ |
|
786 |
||
787 |
The easiest way to do this is to grep around in the |ns3| codebase for someone |
|
788 |
who has already figured it out, You should always try to copy someone else's |
|
789 |
working code. Try something like: |
|
790 |
||
791 |
:: |
|
792 |
||
793 |
find . -name '*.cc' | xargs grep CourseChange | grep Connect |
|
794 |
||
795 |
and you may find your answer along with working code. For example, in this |
|
796 |
case, ``./ns-3-dev/examples/wireless/mixed-wireless.cc`` has something |
|
797 |
just waiting for you to use. You will find |
|
798 |
||
799 |
:: |
|
800 |
||
801 |
Config::Connect ("/NodeList/*/$ns3::MobilityModel/CourseChange", |
|
802 |
MakeCallback (&CourseChangeCallback)); |
|
803 |
||
804 |
as a result of your grep. The ``MakeCallback`` should indicate to you that |
|
805 |
there is a callback function there which you can use. Sure enough, there is: |
|
806 |
||
807 |
:: |
|
808 |
||
809 |
static void |
|
810 |
CourseChangeCallback (std::string path, Ptr<const MobilityModel> model) |
|
811 |
{ |
|
812 |
... |
|
813 |
} |
|
814 |
||
815 |
Take my Word for It |
|
816 |
~~~~~~~~~~~~~~~~~~~ |
|
817 |
||
818 |
If there are no examples to work from, this can be, well, challenging to |
|
819 |
actually figure out from the source code. |
|
820 |
||
821 |
Before embarking on a walkthrough of the code, I'll be kind and just tell you |
|
822 |
a simple way to figure this out: The return value of your callback will always |
|
823 |
be void. The formal parameter list for a ``TracedCallback`` can be found |
|
824 |
from the template parameter list in the declaration. Recall that for our |
|
825 |
current example, this is in ``mobility-model.h``, where we have previously |
|
826 |
found: |
|
827 |
||
828 |
:: |
|
829 |
||
830 |
TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace; |
|
831 |
||
832 |
There is a one-to-one correspondence between the template parameter list in |
|
833 |
the declaration and the formal arguments of the callback function. Here, |
|
834 |
there is one template parameter, which is a ``Ptr<const MobilityModel>``. |
|
835 |
This tells you that you need a function that returns void and takes a |
|
836 |
a ``Ptr<const MobilityModel>``. For example, |
|
837 |
||
838 |
:: |
|
839 |
||
840 |
void |
|
841 |
CourseChangeCallback (Ptr<const MobilityModel> model) |
|
842 |
{ |
|
843 |
... |
|
844 |
} |
|
845 |
||
846 |
That's all you need if you want to ``Config::ConnectWithoutContext``. If |
|
847 |
you want a context, you need to ``Config::Connect`` and use a Callback |
|
848 |
function that takes a string context, then the required argument. |
|
849 |
||
850 |
:: |
|
851 |
||
852 |
void |
|
853 |
CourseChangeCallback (std::string path, Ptr<const MobilityModel> model) |
|
854 |
{ |
|
855 |
... |
|
856 |
} |
|
857 |
||
858 |
If you want to ensure that your ``CourseChangeCallback`` is only visible |
|
859 |
in your local file, you can add the keyword ``static`` and come up with: |
|
860 |
||
861 |
:: |
|
862 |
||
863 |
static void |
|
864 |
CourseChangeCallback (std::string path, Ptr<const MobilityModel> model) |
|
865 |
{ |
|
866 |
... |
|
867 |
} |
|
868 |
||
869 |
which is exactly what we used in the ``third.cc`` example. |
|
870 |
||
871 |
The Hard Way |
|
872 |
~~~~~~~~~~~~ |
|
873 |
||
874 |
This section is entirely optional. It is going to be a bumpy ride, especially |
|
875 |
for those unfamiliar with the details of templates. However, if you get through |
|
876 |
this, you will have a very good handle on a lot of the |ns3| low level |
|
877 |
idioms. |
|
878 |
||
879 |
So, again, let's figure out what signature of callback function is required for |
|
880 |
the "CourseChange" Attribute. This is going to be painful, but you only need |
|
881 |
to do this once. After you get through this, you will be able to just look at |
|
882 |
a ``TracedCallback`` and understand it. |
|
883 |
||
884 |
The first thing we need to look at is the declaration of the trace source. |
|
885 |
Recall that this is in ``mobility-model.h``, where we have previously |
|
886 |
found: |
|
887 |
||
888 |
:: |
|
889 |
||
890 |
TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace; |
|
891 |
||
892 |
This declaration is for a template. The template parameter is inside the |
|
893 |
angle-brackets, so we are really interested in finding out what that |
|
894 |
``TracedCallback<>`` is. If you have absolutely no idea where this might |
|
895 |
be found, grep is your friend. |
|
896 |
||
897 |
We are probably going to be interested in some kind of declaration in the |
|
898 |
|ns3| source, so first change into the ``src`` directory. Then, |
|
899 |
we know this declaration is going to have to be in some kind of header file, |
|
900 |
so just grep for it using: |
|
901 |
||
902 |
:: |
|
903 |
||
904 |
find . -name '*.h' | xargs grep TracedCallback |
|
905 |
||
906 |
You'll see 124 lines fly by (I piped this through wc to see how bad it was). |
|
907 |
Although that may seem like it, that's not really a lot. Just pipe the output |
|
908 |
through more and start scanning through it. On the first page, you will see |
|
909 |
some very suspiciously template-looking stuff. |
|
910 |
||
911 |
:: |
|
912 |
||
913 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::TracedCallback () |
|
914 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext (c ... |
|
915 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Connect (const CallbackB ... |
|
916 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::DisconnectWithoutContext ... |
|
917 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::Disconnect (const Callba ... |
|
918 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (void) const ... |
|
919 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1) const ... |
|
920 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ... |
|
921 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ... |
|
922 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ... |
|
923 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ... |
|
924 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ... |
|
925 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::operator() (T1 a1, T2 a2 ... |
|
926 |
||
927 |
It turns out that all of this comes from the header file |
|
928 |
``traced-callback.h`` which sounds very promising. You can then take a |
|
929 |
look at ``mobility-model.h`` and see that there is a line which confirms |
|
930 |
this hunch: |
|
931 |
||
932 |
:: |
|
933 |
||
934 |
#include "ns3/traced-callback.h" |
|
935 |
||
936 |
Of course, you could have gone at this from the other direction and started |
|
937 |
by looking at the includes in ``mobility-model.h`` and noticing the |
|
938 |
include of ``traced-callback.h`` and inferring that this must be the file |
|
939 |
you want. |
|
940 |
||
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
941 |
In either case, the next step is to take a look at ``src/core/model/traced-callback.h`` |
6754 | 942 |
in your favorite editor to see what is happening. |
943 |
||
944 |
You will see a comment at the top of the file that should be comforting: |
|
945 |
||
946 |
:: |
|
947 |
||
948 |
An ns3::TracedCallback has almost exactly the same API as a normal ns3::Callback but |
|
949 |
instead of forwarding calls to a single function (as an ns3::Callback normally does), |
|
950 |
it forwards calls to a chain of ns3::Callback. |
|
951 |
||
952 |
This should sound very familiar and let you know you are on the right track. |
|
953 |
||
954 |
Just after this comment, you will find, |
|
955 |
||
956 |
:: |
|
957 |
||
958 |
template<typename T1 = empty, typename T2 = empty, |
|
959 |
typename T3 = empty, typename T4 = empty, |
|
960 |
typename T5 = empty, typename T6 = empty, |
|
961 |
typename T7 = empty, typename T8 = empty> |
|
962 |
class TracedCallback |
|
963 |
{ |
|
964 |
... |
|
965 |
||
966 |
This tells you that TracedCallback is a templated class. It has eight possible |
|
967 |
type parameters with default values. Go back and compare this with the |
|
968 |
declaration you are trying to understand: |
|
969 |
||
970 |
:: |
|
971 |
||
972 |
TracedCallback<Ptr<const MobilityModel> > m_courseChangeTrace; |
|
973 |
||
974 |
The ``typename T1`` in the templated class declaration corresponds to the |
|
975 |
``Ptr<const MobilityModel>`` in the declaration above. All of the other |
|
976 |
type parameters are left as defaults. Looking at the constructor really |
|
977 |
doesn't tell you much. The one place where you have seen a connection made |
|
978 |
between your Callback function and the tracing system is in the ``Connect`` |
|
979 |
and ``ConnectWithoutContext`` functions. If you scroll down, you will see |
|
980 |
a ``ConnectWithoutContext`` method here: |
|
981 |
||
982 |
:: |
|
983 |
||
984 |
template<typename T1, typename T2, |
|
985 |
typename T3, typename T4, |
|
986 |
typename T5, typename T6, |
|
987 |
typename T7, typename T8> |
|
988 |
void |
|
989 |
TracedCallback<T1,T2,T3,T4,T5,T6,T7,T8>::ConnectWithoutContext ... |
|
990 |
{ |
|
991 |
Callback<void,T1,T2,T3,T4,T5,T6,T7,T8> cb; |
|
992 |
cb.Assign (callback); |
|
993 |
m_callbackList.push_back (cb); |
|
994 |
} |
|
995 |
||
996 |
You are now in the belly of the beast. When the template is instantiated for |
|
997 |
the declaration above, the compiler will replace ``T1`` with |
|
998 |
``Ptr<const MobilityModel>``. |
|
999 |
||
1000 |
:: |
|
1001 |
||
1002 |
void |
|
1003 |
TracedCallback<Ptr<const MobilityModel>::ConnectWithoutContext ... cb |
|
1004 |
{ |
|
1005 |
Callback<void, Ptr<const MobilityModel> > cb; |
|
1006 |
cb.Assign (callback); |
|
1007 |
m_callbackList.push_back (cb); |
|
1008 |
} |
|
1009 |
||
1010 |
You can now see the implementation of everything we've been talking about. The |
|
1011 |
code creates a Callback of the right type and assigns your function to it. This |
|
1012 |
is the equivalent of the ``pfi = MyFunction`` we discussed at the start of |
|
1013 |
this section. The code then adds the Callback to the list of Callbacks for |
|
1014 |
this source. The only thing left is to look at the definition of Callback. |
|
1015 |
Using the same grep trick as we used to find ``TracedCallback``, you will be |
|
1016 |
able to find that the file ``./core/callback.h`` is the one we need to look at. |
|
1017 |
||
1018 |
If you look down through the file, you will see a lot of probably almost |
|
1019 |
incomprehensible template code. You will eventually come to some Doxygen for |
|
1020 |
the Callback template class, though. Fortunately, there is some English: |
|
1021 |
||
1022 |
:: |
|
1023 |
||
1024 |
This class template implements the Functor Design Pattern. |
|
1025 |
It is used to declare the type of a Callback: |
|
1026 |
- the first non-optional template argument represents |
|
1027 |
the return type of the callback. |
|
1028 |
- the second optional template argument represents |
|
1029 |
the type of the first argument to the callback. |
|
1030 |
- the third optional template argument represents |
|
1031 |
the type of the second argument to the callback. |
|
1032 |
- the fourth optional template argument represents |
|
1033 |
the type of the third argument to the callback. |
|
1034 |
- the fifth optional template argument represents |
|
1035 |
the type of the fourth argument to the callback. |
|
1036 |
- the sixth optional template argument represents |
|
1037 |
the type of the fifth argument to the callback. |
|
1038 |
||
1039 |
We are trying to figure out what the |
|
1040 |
||
1041 |
:: |
|
1042 |
||
1043 |
Callback<void, Ptr<const MobilityModel> > cb; |
|
1044 |
||
1045 |
declaration means. Now we are in a position to understand that the first |
|
1046 |
(non-optional) parameter, ``void``, represents the return type of the |
|
1047 |
Callback. The second (non-optional) parameter, ``Ptr<const MobilityModel>`` |
|
1048 |
represents the first argument to the callback. |
|
1049 |
||
1050 |
The Callback in question is your function to receive the trace events. From |
|
1051 |
this you can infer that you need a function that returns ``void`` and takes |
|
1052 |
a ``Ptr<const MobilityModel>``. For example, |
|
1053 |
||
1054 |
:: |
|
1055 |
||
1056 |
void |
|
1057 |
CourseChangeCallback (Ptr<const MobilityModel> model) |
|
1058 |
{ |
|
1059 |
... |
|
1060 |
} |
|
1061 |
||
1062 |
That's all you need if you want to ``Config::ConnectWithoutContext``. If |
|
1063 |
you want a context, you need to ``Config::Connect`` and use a Callback |
|
1064 |
function that takes a string context. This is because the ``Connect`` |
|
1065 |
function will provide the context for you. You'll need: |
|
1066 |
||
1067 |
:: |
|
1068 |
||
1069 |
void |
|
1070 |
CourseChangeCallback (std::string path, Ptr<const MobilityModel> model) |
|
1071 |
{ |
|
1072 |
... |
|
1073 |
} |
|
1074 |
||
1075 |
If you want to ensure that your ``CourseChangeCallback`` is only visible |
|
1076 |
in your local file, you can add the keyword ``static`` and come up with: |
|
1077 |
||
1078 |
:: |
|
1079 |
||
1080 |
static void |
|
1081 |
CourseChangeCallback (std::string path, Ptr<const MobilityModel> model) |
|
1082 |
{ |
|
1083 |
... |
|
1084 |
} |
|
1085 |
||
1086 |
which is exactly what we used in the ``third.cc`` example. Perhaps you |
|
1087 |
should now go back and reread the previous section (Take My Word for It). |
|
1088 |
||
1089 |
If you are interested in more details regarding the implementation of |
|
1090 |
Callbacks, feel free to take a look at the |ns3| manual. They are one |
|
1091 |
of the most frequently used constructs in the low-level parts of |ns3|. |
|
1092 |
It is, in my opinion, a quite elegant thing. |
|
1093 |
||
1094 |
What About TracedValue? |
|
1095 |
+++++++++++++++++++++++ |
|
1096 |
||
1097 |
Earlier in this section, we presented a simple piece of code that used a |
|
1098 |
``TracedValue<int32_t>`` to demonstrate the basics of the tracing code. |
|
1099 |
We just glossed over the way to find the return type and formal arguments |
|
1100 |
for the ``TracedValue``. Rather than go through the whole exercise, we |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1101 |
will just point you at the correct file, ``src/core/model/traced-value.h`` and |
6754 | 1102 |
to the important piece of code: |
1103 |
||
1104 |
:: |
|
1105 |
||
1106 |
template <typename T> |
|
1107 |
class TracedValue |
|
1108 |
{ |
|
1109 |
public: |
|
1110 |
... |
|
1111 |
void Set (const T &v) { |
|
1112 |
if (m_v != v) |
|
1113 |
{ |
|
1114 |
m_cb (m_v, v); |
|
1115 |
m_v = v; |
|
1116 |
} |
|
1117 |
} |
|
1118 |
... |
|
1119 |
private: |
|
1120 |
T m_v; |
|
1121 |
TracedCallback<T,T> m_cb; |
|
1122 |
}; |
|
1123 |
||
1124 |
Here you see that the ``TracedValue`` is templated, of course. In the simple |
|
1125 |
example case at the start of the section, the typename is int32_t. This means |
|
1126 |
that the member variable being traced (``m_v`` in the private section of the |
|
1127 |
class) will be an ``int32_t m_v``. The ``Set`` method will take a |
|
1128 |
``const int32_t &v`` as a parameter. You should now be able to understand |
|
1129 |
that the ``Set`` code will fire the ``m_cb`` callback with two parameters: |
|
1130 |
the first being the current value of the ``TracedValue``; and the second |
|
1131 |
being the new value being set. |
|
1132 |
||
1133 |
The callback, ``m_cb`` is declared as a ``TracedCallback<T, T>`` which |
|
1134 |
will correspond to a ``TracedCallback<int32_t, int32_t>`` when the class is |
|
1135 |
instantiated. |
|
1136 |
||
1137 |
Recall that the callback target of a TracedCallback always returns ``void``. |
|
1138 |
Further recall that there is a one-to-one correspondence between the template |
|
1139 |
parameter list in the declaration and the formal arguments of the callback |
|
1140 |
function. Therefore the callback will need to have a function signature that |
|
1141 |
looks like: |
|
1142 |
||
1143 |
:: |
|
1144 |
||
1145 |
void |
|
1146 |
MyCallback (int32_t oldValue, int32_t newValue) |
|
1147 |
{ |
|
1148 |
... |
|
1149 |
} |
|
1150 |
||
1151 |
It probably won't surprise you that this is exactly what we provided in that |
|
1152 |
simple example we covered so long ago: |
|
1153 |
||
1154 |
:: |
|
1155 |
||
1156 |
void |
|
1157 |
IntTrace (int32_t oldValue, int32_t newValue) |
|
1158 |
{ |
|
1159 |
std::cout << "Traced " << oldValue << " to " << newValue << std::endl; |
|
1160 |
} |
|
1161 |
||
1162 |
A Real Example |
|
1163 |
************** |
|
1164 |
||
1165 |
Let's do an example taken from one of the best-known books on TCP around. |
|
1166 |
"TCP/IP Illustrated, Volume 1: The Protocols," by W. Richard Stevens is a |
|
1167 |
classic. I just flipped the book open and ran across a nice plot of both the |
|
1168 |
congestion window and sequence numbers versus time on page 366. Stevens calls |
|
1169 |
this, "Figure 21.10. Value of cwnd and send sequence number while data is being |
|
1170 |
transmitted." Let's just recreate the cwnd part of that plot in |ns3| |
|
1171 |
using the tracing system and ``gnuplot``. |
|
1172 |
||
1173 |
Are There Trace Sources Available? |
|
1174 |
++++++++++++++++++++++++++++++++++ |
|
1175 |
||
1176 |
The first thing to think about is how we want to get the data out. What is it |
|
1177 |
that we need to trace? The first thing to do is to consult "The list of all |
|
1178 |
trace sources" to see what we have to work with. Recall that this is found |
|
7480
af0f3525d3ec
Fix more references to old website in Tutorial
Mitch Watrous <watrous@u.washington.edu>
parents:
7148
diff
changeset
|
1179 |
in the |ns3| Doxygen in the "C++ Constructs Used by All Modules" Module section. If you scroll |
6754 | 1180 |
through the list, you will eventually find: |
1181 |
||
1182 |
:: |
|
1183 |
||
1184 |
ns3::TcpNewReno |
|
1185 |
CongestionWindow: The TCP connection's congestion window |
|
1186 |
||
1187 |
It turns out that the |ns3| TCP implementation lives (mostly) in the |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1188 |
file ``src/internet/model/tcp-socket-base.cc`` while congestion control |
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1189 |
variants are in files such as ``src/internet/model/tcp-newreno.cc``. |
6754 | 1190 |
If you don't know this a priori, you can use the recursive grep trick: |
1191 |
||
1192 |
:: |
|
1193 |
||
1194 |
find . -name '*.cc' | xargs grep -i tcp |
|
1195 |
||
1196 |
You will find page after page of instances of tcp pointing you to that file. |
|
1197 |
||
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1198 |
If you open ``src/internet/model/tcp-newreno.cc`` in your favorite |
6754 | 1199 |
editor, you will see right up at the top of the file, the following declarations: |
1200 |
||
1201 |
:: |
|
1202 |
||
1203 |
TypeId |
|
1204 |
TcpNewReno::GetTypeId () |
|
1205 |
{ |
|
1206 |
static TypeId tid = TypeId("ns3::TcpNewReno") |
|
1207 |
.SetParent<TcpSocketBase> () |
|
1208 |
.AddConstructor<TcpNewReno> () |
|
1209 |
.AddTraceSource ("CongestionWindow", |
|
1210 |
"The TCP connection's congestion window", |
|
1211 |
MakeTraceSourceAccessor (&TcpNewReno::m_cWnd)) |
|
1212 |
; |
|
1213 |
return tid; |
|
1214 |
} |
|
1215 |
||
1216 |
This should tell you to look for the declaration of ``m_cWnd`` in the header |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1217 |
file ``src/internet/model/tcp-newreno.h``. If you open this file in your |
6754 | 1218 |
favorite editor, you will find: |
1219 |
||
1220 |
:: |
|
1221 |
||
1222 |
TracedValue<uint32_t> m_cWnd; //Congestion window |
|
1223 |
||
1224 |
You should now understand this code completely. If we have a pointer to the |
|
1225 |
``TcpNewReno``, we can ``TraceConnect`` to the "CongestionWindow" trace |
|
1226 |
source if we provide an appropriate callback target. This is the same kind of |
|
1227 |
trace source that we saw in the simple example at the start of this section, |
|
1228 |
except that we are talking about ``uint32_t`` instead of ``int32_t``. |
|
1229 |
||
1230 |
We now know that we need to provide a callback that returns void and takes |
|
1231 |
two ``uint32_t`` parameters, the first being the old value and the second |
|
1232 |
being the new value: |
|
1233 |
||
1234 |
:: |
|
1235 |
||
1236 |
void |
|
1237 |
CwndTrace (uint32_t oldValue, uint32_t newValue) |
|
1238 |
{ |
|
1239 |
... |
|
1240 |
} |
|
1241 |
||
1242 |
What Script to Use? |
|
1243 |
+++++++++++++++++++ |
|
1244 |
||
1245 |
It's always best to try and find working code laying around that you can |
|
1246 |
modify, rather than starting from scratch. So the first order of business now |
|
1247 |
is to find some code that already hooks the "CongestionWindow" trace source |
|
1248 |
and see if we can modify it. As usual, grep is your friend: |
|
1249 |
||
1250 |
:: |
|
1251 |
||
1252 |
find . -name '*.cc' | xargs grep CongestionWindow |
|
1253 |
||
1254 |
This will point out a couple of promising candidates: |
|
1255 |
``examples/tcp/tcp-large-transfer.cc`` and |
|
1256 |
``src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc``. |
|
1257 |
||
1258 |
We haven't visited any of the test code yet, so let's take a look there. You |
|
1259 |
will typically find that test code is fairly minimal, so this is probably a |
|
1260 |
very good bet. Open ``src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc`` in your |
|
1261 |
favorite editor and search for "CongestionWindow". You will find, |
|
1262 |
||
1263 |
:: |
|
1264 |
||
1265 |
ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", |
|
1266 |
MakeCallback (&Ns3TcpCwndTestCase1::CwndChange, this)); |
|
1267 |
||
1268 |
This should look very familiar to you. We mentioned above that if we had a |
|
1269 |
pointer to the ``TcpNewReno``, we could ``TraceConnect`` to the |
|
1270 |
"CongestionWindow" trace source. That's exactly what we have here; so it |
|
1271 |
turns out that this line of code does exactly what we want. Let's go ahead |
|
1272 |
and extract the code we need from this function |
|
1273 |
(``Ns3TcpCwndTestCase1::DoRun (void)``). If you look at this function, |
|
1274 |
you will find that it looks just like an |ns3| script. It turns out that |
|
1275 |
is exactly what it is. It is a script run by the test framework, so we can just |
|
1276 |
pull it out and wrap it in ``main`` instead of in ``DoRun``. Rather than |
|
1277 |
walk through this, step, by step, we have provided the file that results from |
|
1278 |
porting this test back to a native |ns3| script -- |
|
1279 |
``examples/tutorial/fifth.cc``. |
|
1280 |
||
1281 |
A Common Problem and Solution |
|
1282 |
+++++++++++++++++++++++++++++ |
|
1283 |
||
1284 |
The ``fifth.cc`` example demonstrates an extremely important rule that you |
|
1285 |
must understand before using any kind of ``Attribute``: you must ensure |
|
1286 |
that the target of a ``Config`` command exists before trying to use it. |
|
1287 |
This is no different than saying an object must be instantiated before trying |
|
1288 |
to call it. Although this may seem obvious when stated this way, it does |
|
1289 |
trip up many people trying to use the system for the first time. |
|
1290 |
||
1291 |
Let's return to basics for a moment. There are three basic time periods that |
|
1292 |
exist in any |ns3| script. The first time period is sometimes called |
|
1293 |
"Configuration Time" or "Setup Time," and is in force during the period |
|
1294 |
when the ``main`` function of your script is running, but before |
|
1295 |
``Simulator::Run`` is called. The second time period is sometimes called |
|
1296 |
"Simulation Time" and is in force during the time period when |
|
1297 |
``Simulator::Run`` is actively executing its events. After it completes |
|
1298 |
executing the simulation, ``Simulator::Run`` will return control back to |
|
1299 |
the ``main`` function. When this happens, the script enters what can be |
|
1300 |
called "Teardown Time," which is when the structures and objects created |
|
1301 |
during setup and taken apart and released. |
|
1302 |
||
1303 |
Perhaps the most common mistake made in trying to use the tracing system is |
|
1304 |
assuming that entities constructed dynamically during simulation time are |
|
1305 |
available during configuration time. In particular, an |ns3| |
|
1306 |
``Socket`` is a dynamic object often created by ``Applications`` to |
|
1307 |
communicate between ``Nodes``. An |ns3| ``Application`` |
|
1308 |
always has a "Start Time" and a "Stop Time" associated with it. In the |
|
1309 |
vast majority of cases, an ``Application`` will not attempt to create |
|
1310 |
a dynamic object until its ``StartApplication`` method is called at some |
|
1311 |
"Start Time". This is to ensure that the simulation is completely |
|
1312 |
configured before the app tries to do anything (what would happen if it tried |
|
1313 |
to connect to a node that didn't exist yet during configuration time). The |
|
1314 |
answer to this issue is to 1) create a simulator event that is run after the |
|
1315 |
dynamic object is created and hook the trace when that event is executed; or |
|
1316 |
2) create the dynamic object at configuration time, hook it then, and give |
|
1317 |
the object to the system to use during simulation time. We took the second |
|
1318 |
approach in the ``fifth.cc`` example. This decision required us to create |
|
1319 |
the ``MyApp`` ``Application``, the entire purpose of which is to take |
|
1320 |
a ``Socket`` as a parameter. |
|
1321 |
||
1322 |
A fifth.cc Walkthrough |
|
1323 |
++++++++++++++++++++++ |
|
1324 |
||
1325 |
Now, let's take a look at the example program we constructed by dissecting |
|
1326 |
the congestion window test. Open ``examples/tutorial/fifth.cc`` in your |
|
1327 |
favorite editor. You should see some familiar looking code: |
|
1328 |
||
1329 |
:: |
|
1330 |
||
1331 |
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
|
1332 |
/* |
|
1333 |
* This program is free software; you can redistribute it and/or modify |
|
1334 |
* it under the terms of the GNU General Public License version 2 as |
|
1335 |
* published by the Free Software Foundation; |
|
1336 |
* |
|
1337 |
* This program is distributed in the hope that it will be useful, |
|
1338 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
1339 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
1340 |
* GNU General Public License for more details. |
|
1341 |
* |
|
1342 |
* You should have received a copy of the GNU General Public License |
|
1343 |
* along with this program; if not, write to the Free Software |
|
1344 |
* Foundation, Include., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
1345 |
*/ |
|
1346 |
||
1347 |
#include <fstream> |
|
1348 |
#include "ns3/core-module.h" |
|
7137
dbefbad7bee3
Fix module names in documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
6754
diff
changeset
|
1349 |
#include "ns3/network-module.h" |
dbefbad7bee3
Fix module names in documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
6754
diff
changeset
|
1350 |
#include "ns3/internet-module.h" |
dbefbad7bee3
Fix module names in documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
6754
diff
changeset
|
1351 |
#include "ns3/point-to-point-module.h" |
dbefbad7bee3
Fix module names in documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
6754
diff
changeset
|
1352 |
#include "ns3/applications-module.h" |
6754 | 1353 |
|
1354 |
using namespace ns3; |
|
1355 |
||
1356 |
NS_LOG_COMPONENT_DEFINE ("FifthScriptExample"); |
|
1357 |
||
1358 |
This has all been covered, so we won't rehash it. The next lines of source are |
|
1359 |
the network illustration and a comment addressing the problem described above |
|
1360 |
with ``Socket``. |
|
1361 |
||
1362 |
:: |
|
1363 |
||
1364 |
// =========================================================================== |
|
1365 |
// |
|
1366 |
// node 0 node 1 |
|
1367 |
// +----------------+ +----------------+ |
|
1368 |
// | ns-3 TCP | | ns-3 TCP | |
|
1369 |
// +----------------+ +----------------+ |
|
1370 |
// | 10.1.1.1 | | 10.1.1.2 | |
|
1371 |
// +----------------+ +----------------+ |
|
1372 |
// | point-to-point | | point-to-point | |
|
1373 |
// +----------------+ +----------------+ |
|
1374 |
// | | |
|
1375 |
// +---------------------+ |
|
1376 |
// 5 Mbps, 2 ms |
|
1377 |
// |
|
1378 |
// |
|
1379 |
// We want to look at changes in the ns-3 TCP congestion window. We need |
|
1380 |
// to crank up a flow and hook the CongestionWindow attribute on the socket |
|
1381 |
// of the sender. Normally one would use an on-off application to generate a |
|
1382 |
// flow, but this has a couple of problems. First, the socket of the on-off |
|
1383 |
// application is not created until Application Start time, so we wouldn't be |
|
1384 |
// able to hook the socket (now) at configuration time. Second, even if we |
|
1385 |
// could arrange a call after start time, the socket is not public so we |
|
1386 |
// couldn't get at it. |
|
1387 |
// |
|
1388 |
// So, we can cook up a simple version of the on-off application that does what |
|
1389 |
// we want. On the plus side we don't need all of the complexity of the on-off |
|
1390 |
// application. On the minus side, we don't have a helper, so we have to get |
|
1391 |
// a little more involved in the details, but this is trivial. |
|
1392 |
// |
|
1393 |
// So first, we create a socket and do the trace connect on it; then we pass |
|
1394 |
// this socket into the constructor of our simple application which we then |
|
1395 |
// install in the source node. |
|
1396 |
// =========================================================================== |
|
1397 |
// |
|
1398 |
||
1399 |
This should also be self-explanatory. |
|
1400 |
||
1401 |
The next part is the declaration of the ``MyApp`` ``Application`` that |
|
1402 |
we put together to allow the ``Socket`` to be created at configuration time. |
|
1403 |
||
1404 |
:: |
|
1405 |
||
1406 |
class MyApp : public Application |
|
1407 |
{ |
|
1408 |
public: |
|
1409 |
||
1410 |
MyApp (); |
|
1411 |
virtual ~MyApp(); |
|
1412 |
||
1413 |
void Setup (Ptr<Socket> socket, Address address, uint32_t packetSize, |
|
1414 |
uint32_t nPackets, DataRate dataRate); |
|
1415 |
||
1416 |
private: |
|
1417 |
virtual void StartApplication (void); |
|
1418 |
virtual void StopApplication (void); |
|
1419 |
||
1420 |
void ScheduleTx (void); |
|
1421 |
void SendPacket (void); |
|
1422 |
||
1423 |
Ptr<Socket> m_socket; |
|
1424 |
Address m_peer; |
|
1425 |
uint32_t m_packetSize; |
|
1426 |
uint32_t m_nPackets; |
|
1427 |
DataRate m_dataRate; |
|
1428 |
EventId m_sendEvent; |
|
1429 |
bool m_running; |
|
1430 |
uint32_t m_packetsSent; |
|
1431 |
}; |
|
1432 |
||
1433 |
You can see that this class inherits from the |ns3| ``Application`` |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1434 |
class. Take a look at ``src/network/model/application.h`` if you are interested in |
6754 | 1435 |
what is inherited. The ``MyApp`` class is obligated to override the |
1436 |
``StartApplication`` and ``StopApplication`` methods. These methods are |
|
1437 |
automatically called when ``MyApp`` is required to start and stop sending |
|
1438 |
data during the simulation. |
|
1439 |
||
1440 |
How Applications are Started and Stopped (optional) |
|
1441 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
1442 |
||
1443 |
It is worthwhile to spend a bit of time explaining how events actually get |
|
1444 |
started in the system. This is another fairly deep explanation, and can be |
|
1445 |
ignored if you aren't planning on venturing down into the guts of the system. |
|
1446 |
It is useful, however, in that the discussion touches on how some very important |
|
1447 |
parts of |ns3| work and exposes some important idioms. If you are |
|
1448 |
planning on implementing new models, you probably want to understand this |
|
1449 |
section. |
|
1450 |
||
1451 |
The most common way to start pumping events is to start an ``Application``. |
|
1452 |
This is done as the result of the following (hopefully) familar lines of an |
|
1453 |
|ns3| script: |
|
1454 |
||
1455 |
:: |
|
1456 |
||
1457 |
ApplicationContainer apps = ... |
|
1458 |
apps.Start (Seconds (1.0)); |
|
1459 |
apps.Stop (Seconds (10.0)); |
|
1460 |
||
7147
71adbc0422a5
Fix more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7145
diff
changeset
|
1461 |
The application container code (see ``src/network/helper/application-container.h`` if |
6754 | 1462 |
you are interested) loops through its contained applications and calls, |
1463 |
||
1464 |
:: |
|
1465 |
||
1466 |
app->SetStartTime (startTime); |
|
1467 |
||
1468 |
as a result of the ``apps.Start`` call and |
|
1469 |
||
1470 |
:: |
|
1471 |
||
1472 |
app->SetStopTime (stopTime); |
|
1473 |
||
1474 |
as a result of the ``apps.Stop`` call. |
|
1475 |
||
1476 |
The ultimate result of these calls is that we want to have the simulator |
|
1477 |
automatically make calls into our ``Applications`` to tell them when to |
|
1478 |
start and stop. In the case of ``MyApp``, it inherits from class |
|
1479 |
``Application`` and overrides ``StartApplication``, and |
|
1480 |
``StopApplication``. These are the functions that will be called by |
|
1481 |
the simulator at the appropriate time. In the case of ``MyApp`` you |
|
1482 |
will find that ``MyApp::StartApplication`` does the initial ``Bind``, |
|
1483 |
and ``Connect`` on the socket, and then starts data flowing by calling |
|
1484 |
``MyApp::SendPacket``. ``MyApp::StopApplication`` stops generating |
|
1485 |
packets by cancelling any pending send events and closing the socket. |
|
1486 |
||
1487 |
One of the nice things about |ns3| is that you can completely |
|
1488 |
ignore the implementation details of how your ``Application`` is |
|
1489 |
"automagically" called by the simulator at the correct time. But since |
|
1490 |
we have already ventured deep into |ns3| already, let's go for it. |
|
1491 |
||
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1492 |
If you look at ``src/network/model/application.cc`` you will find that the |
6754 | 1493 |
``SetStartTime`` method of an ``Application`` just sets the member |
1494 |
variable ``m_startTime`` and the ``SetStopTime`` method just sets |
|
1495 |
``m_stopTime``. From there, without some hints, the trail will probably |
|
1496 |
end. |
|
1497 |
||
1498 |
The key to picking up the trail again is to know that there is a global |
|
1499 |
list of all of the nodes in the system. Whenever you create a node in |
|
1500 |
a simulation, a pointer to that node is added to the global ``NodeList``. |
|
1501 |
||
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1502 |
Take a look at ``src/network/model/node-list.cc`` and search for |
6754 | 1503 |
``NodeList::Add``. The public static implementation calls into a private |
1504 |
implementation called ``NodeListPriv::Add``. This is a relatively common |
|
1505 |
idom in |ns3|. So, take a look at ``NodeListPriv::Add``. There |
|
1506 |
you will find, |
|
1507 |
||
1508 |
:: |
|
1509 |
||
1510 |
Simulator::ScheduleWithContext (index, TimeStep (0), &Node::Start, node); |
|
1511 |
||
1512 |
This tells you that whenever a ``Node`` is created in a simulation, as |
|
1513 |
a side-effect, a call to that node's ``Start`` method is scheduled for |
|
1514 |
you that happens at time zero. Don't read too much into that name, yet. |
|
1515 |
It doesn't mean that the node is going to start doing anything, it can be |
|
1516 |
interpreted as an informational call into the ``Node`` telling it that |
|
1517 |
the simulation has started, not a call for action telling the ``Node`` |
|
1518 |
to start doing something. |
|
1519 |
||
1520 |
So, ``NodeList::Add`` indirectly schedules a call to ``Node::Start`` |
|
1521 |
at time zero to advise a new node that the simulation has started. If you |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1522 |
look in ``src/network/model/node.h`` you will, however, not find a method called |
6754 | 1523 |
``Node::Start``. It turns out that the ``Start`` method is inherited |
1524 |
from class ``Object``. All objects in the system can be notified when |
|
1525 |
the simulation starts, and objects of class ``Node`` are just one kind |
|
1526 |
of those objects. |
|
1527 |
||
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1528 |
Take a look at ``src/core/model/object.cc`` next and search for ``Object::Start``. |
6754 | 1529 |
This code is not as straightforward as you might have expected since |
1530 |
|ns3| ``Objects`` support aggregation. The code in |
|
1531 |
``Object::Start`` then loops through all of the objects that have been |
|
1532 |
aggregated together and calls their ``DoStart`` method. This is another |
|
1533 |
idiom that is very common in |ns3|. There is a public API method, |
|
1534 |
that stays constant across implementations, that calls a private implementation |
|
1535 |
method that is inherited and implemented by subclasses. The names are typically |
|
1536 |
something like ``MethodName`` for the public API and ``DoMethodName`` for |
|
1537 |
the private API. |
|
1538 |
||
1539 |
This tells us that we should look for a ``Node::DoStart`` method in |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1540 |
``src/network/model/node.cc`` for the method that will continue our trail. If you |
6754 | 1541 |
locate the code, you will find a method that loops through all of the devices |
1542 |
in the node and then all of the applications in the node calling |
|
1543 |
``device->Start`` and ``application->Start`` respectively. |
|
1544 |
||
1545 |
You may already know that classes ``Device`` and ``Application`` both |
|
1546 |
inherit from class ``Object`` and so the next step will be to look at |
|
1547 |
what happens when ``Application::DoStart`` is called. Take a look at |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
1548 |
``src/network/model/application.cc`` and you will find: |
6754 | 1549 |
|
1550 |
:: |
|
1551 |
||
1552 |
void |
|
1553 |
Application::DoStart (void) |
|
1554 |
{ |
|
1555 |
m_startEvent = Simulator::Schedule (m_startTime, &Application::StartApplication, this); |
|
1556 |
if (m_stopTime != TimeStep (0)) |
|
1557 |
{ |
|
1558 |
m_stopEvent = Simulator::Schedule (m_stopTime, &Application::StopApplication, this); |
|
1559 |
} |
|
1560 |
Object::DoStart (); |
|
1561 |
} |
|
1562 |
||
1563 |
Here, we finally come to the end of the trail. If you have kept it all straight, |
|
1564 |
when you implement an |ns3| ``Application``, your new application |
|
1565 |
inherits from class ``Application``. You override the ``StartApplication`` |
|
1566 |
and ``StopApplication`` methods and provide mechanisms for starting and |
|
1567 |
stopping the flow of data out of your new ``Application``. When a ``Node`` |
|
1568 |
is created in the simulation, it is added to a global ``NodeList``. The act |
|
1569 |
of adding a node to this ``NodeList`` causes a simulator event to be scheduled |
|
1570 |
for time zero which calls the ``Node::Start`` method of the newly added |
|
1571 |
``Node`` to be called when the simulation starts. Since a ``Node`` inherits |
|
1572 |
from ``Object``, this calls the ``Object::Start`` method on the ``Node`` |
|
1573 |
which, in turn, calls the ``DoStart`` methods on all of the ``Objects`` |
|
1574 |
aggregated to the ``Node`` (think mobility models). Since the ``Node`` |
|
1575 |
``Object`` has overridden ``DoStart``, that method is called when the |
|
1576 |
simulation starts. The ``Node::DoStart`` method calls the ``Start`` methods |
|
1577 |
of all of the ``Applications`` on the node. Since ``Applications`` are |
|
1578 |
also ``Objects``, this causes ``Application::DoStart`` to be called. When |
|
1579 |
``Application::DoStart`` is called, it schedules events for the |
|
1580 |
``StartApplication`` and ``StopApplication`` calls on the ``Application``. |
|
1581 |
These calls are designed to start and stop the flow of data from the |
|
1582 |
``Application`` |
|
1583 |
||
1584 |
This has been another fairly long journey, but it only has to be made once, and |
|
1585 |
you now understand another very deep piece of |ns3|. |
|
1586 |
||
1587 |
The MyApp Application |
|
1588 |
~~~~~~~~~~~~~~~~~~~~~ |
|
1589 |
||
1590 |
The ``MyApp`` ``Application`` needs a constructor and a destructor, |
|
1591 |
of course: |
|
1592 |
||
1593 |
:: |
|
1594 |
||
1595 |
MyApp::MyApp () |
|
1596 |
: m_socket (0), |
|
1597 |
m_peer (), |
|
1598 |
m_packetSize (0), |
|
1599 |
m_nPackets (0), |
|
1600 |
m_dataRate (0), |
|
1601 |
m_sendEvent (), |
|
1602 |
m_running (false), |
|
1603 |
m_packetsSent (0) |
|
1604 |
{ |
|
1605 |
} |
|
1606 |
||
1607 |
MyApp::~MyApp() |
|
1608 |
{ |
|
1609 |
m_socket = 0; |
|
1610 |
} |
|
1611 |
||
1612 |
The existence of the next bit of code is the whole reason why we wrote this |
|
1613 |
``Application`` in the first place. |
|
1614 |
||
1615 |
:: |
|
1616 |
||
1617 |
void |
|
1618 |
MyApp::Setup (Ptr<Socket> socket, Address address, uint32_t packetSize, |
|
1619 |
uint32_t nPackets, DataRate dataRate) |
|
1620 |
{ |
|
1621 |
m_socket = socket; |
|
1622 |
m_peer = address; |
|
1623 |
m_packetSize = packetSize; |
|
1624 |
m_nPackets = nPackets; |
|
1625 |
m_dataRate = dataRate; |
|
1626 |
} |
|
1627 |
||
1628 |
This code should be pretty self-explanatory. We are just initializing member |
|
1629 |
variables. The important one from the perspective of tracing is the |
|
1630 |
``Ptr<Socket> socket`` which we needed to provide to the application |
|
1631 |
during configuration time. Recall that we are going to create the ``Socket`` |
|
1632 |
as a ``TcpSocket`` (which is implemented by ``TcpNewReno``) and hook |
|
1633 |
its "CongestionWindow" trace source before passing it to the ``Setup`` |
|
1634 |
method. |
|
1635 |
||
1636 |
:: |
|
1637 |
||
1638 |
void |
|
1639 |
MyApp::StartApplication (void) |
|
1640 |
{ |
|
1641 |
m_running = true; |
|
1642 |
m_packetsSent = 0; |
|
1643 |
m_socket->Bind (); |
|
1644 |
m_socket->Connect (m_peer); |
|
1645 |
SendPacket (); |
|
1646 |
} |
|
1647 |
||
1648 |
The above code is the overridden implementation ``Application::StartApplication`` |
|
1649 |
that will be automatically called by the simulator to start our ``Application`` |
|
1650 |
running at the appropriate time. You can see that it does a ``Socket`` ``Bind`` |
|
1651 |
operation. If you are familiar with Berkeley Sockets this shouldn't be a surprise. |
|
1652 |
It performs the required work on the local side of the connection just as you might |
|
1653 |
expect. The following ``Connect`` will do what is required to establish a connection |
|
1654 |
with the TCP at ``Address`` m_peer. It should now be clear why we need to defer |
|
1655 |
a lot of this to simulation time, since the ``Connect`` is going to need a fully |
|
1656 |
functioning network to complete. After the ``Connect``, the ``Application`` |
|
1657 |
then starts creating simulation events by calling ``SendPacket``. |
|
1658 |
||
1659 |
The next bit of code explains to the ``Application`` how to stop creating |
|
1660 |
simulation events. |
|
1661 |
||
1662 |
:: |
|
1663 |
||
1664 |
void |
|
1665 |
MyApp::StopApplication (void) |
|
1666 |
{ |
|
1667 |
m_running = false; |
|
1668 |
||
1669 |
if (m_sendEvent.IsRunning ()) |
|
1670 |
{ |
|
1671 |
Simulator::Cancel (m_sendEvent); |
|
1672 |
} |
|
1673 |
||
1674 |
if (m_socket) |
|
1675 |
{ |
|
1676 |
m_socket->Close (); |
|
1677 |
} |
|
1678 |
} |
|
1679 |
||
1680 |
Every time a simulation event is scheduled, an ``Event`` is created. If the |
|
1681 |
``Event`` is pending execution or executing, its method ``IsRunning`` will |
|
1682 |
return ``true``. In this code, if ``IsRunning()`` returns true, we |
|
1683 |
``Cancel`` the event which removes it from the simulator event queue. By |
|
1684 |
doing this, we break the chain of events that the ``Application`` is using to |
|
1685 |
keep sending its ``Packets`` and the ``Application`` goes quiet. After we |
|
1686 |
quiet the ``Application`` we ``Close`` the socket which tears down the TCP |
|
1687 |
connection. |
|
1688 |
||
1689 |
The socket is actually deleted in the destructor when the ``m_socket = 0`` is |
|
1690 |
executed. This removes the last reference to the underlying Ptr<Socket> which |
|
1691 |
causes the destructor of that Object to be called. |
|
1692 |
||
1693 |
Recall that ``StartApplication`` called ``SendPacket`` to start the |
|
1694 |
chain of events that describes the ``Application`` behavior. |
|
1695 |
||
1696 |
:: |
|
1697 |
||
1698 |
void |
|
1699 |
MyApp::SendPacket (void) |
|
1700 |
{ |
|
1701 |
Ptr<Packet> packet = Create<Packet> (m_packetSize); |
|
1702 |
m_socket->Send (packet); |
|
1703 |
||
1704 |
if (++m_packetsSent < m_nPackets) |
|
1705 |
{ |
|
1706 |
ScheduleTx (); |
|
1707 |
} |
|
1708 |
} |
|
1709 |
||
1710 |
Here, you see that ``SendPacket`` does just that. It creates a ``Packet`` |
|
1711 |
and then does a ``Send`` which, if you know Berkeley Sockets, is probably |
|
1712 |
just what you expected to see. |
|
1713 |
||
1714 |
It is the responsibility of the ``Application`` to keep scheduling the |
|
1715 |
chain of events, so the next lines call ``ScheduleTx`` to schedule another |
|
1716 |
transmit event (a ``SendPacket``) until the ``Application`` decides it |
|
1717 |
has sent enough. |
|
1718 |
||
1719 |
:: |
|
1720 |
||
1721 |
void |
|
1722 |
MyApp::ScheduleTx (void) |
|
1723 |
{ |
|
1724 |
if (m_running) |
|
1725 |
{ |
|
1726 |
Time tNext (Seconds (m_packetSize * 8 / static_cast<double> (m_dataRate.GetBitRate ()))); |
|
1727 |
m_sendEvent = Simulator::Schedule (tNext, &MyApp::SendPacket, this); |
|
1728 |
} |
|
1729 |
} |
|
1730 |
||
1731 |
Here, you see that ``ScheduleTx`` does exactly that. If the ``Application`` |
|
1732 |
is running (if ``StopApplication`` has not been called) it will schedule a |
|
1733 |
new event, which calls ``SendPacket`` again. The alert reader will spot |
|
1734 |
something that also trips up new users. The data rate of an ``Application`` is |
|
1735 |
just that. It has nothing to do with the data rate of an underlying ``Channel``. |
|
1736 |
This is the rate at which the ``Application`` produces bits. It does not take |
|
1737 |
into account any overhead for the various protocols or channels that it uses to |
|
1738 |
transport the data. If you set the data rate of an ``Application`` to the same |
|
1739 |
data rate as your underlying ``Channel`` you will eventually get a buffer overflow. |
|
1740 |
||
1741 |
The Trace Sinks |
|
1742 |
~~~~~~~~~~~~~~~ |
|
1743 |
||
1744 |
The whole point of this exercise is to get trace callbacks from TCP indicating the |
|
1745 |
congestion window has been updated. The next piece of code implements the |
|
1746 |
corresponding trace sink: |
|
1747 |
||
1748 |
:: |
|
1749 |
||
1750 |
static void |
|
1751 |
CwndChange (uint32_t oldCwnd, uint32_t newCwnd) |
|
1752 |
{ |
|
1753 |
NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd); |
|
1754 |
} |
|
1755 |
||
1756 |
This should be very familiar to you now, so we won't dwell on the details. This |
|
1757 |
function just logs the current simulation time and the new value of the |
|
1758 |
congestion window every time it is changed. You can probably imagine that you |
|
1759 |
could load the resulting output into a graphics program (gnuplot or Excel) and |
|
1760 |
immediately see a nice graph of the congestion window behavior over time. |
|
1761 |
||
1762 |
We added a new trace sink to show where packets are dropped. We are going to |
|
1763 |
add an error model to this code also, so we wanted to demonstrate this working. |
|
1764 |
||
1765 |
:: |
|
1766 |
||
1767 |
static void |
|
1768 |
RxDrop (Ptr<const Packet> p) |
|
1769 |
{ |
|
1770 |
NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ()); |
|
1771 |
} |
|
1772 |
||
1773 |
This trace sink will be connected to the "PhyRxDrop" trace source of the |
|
1774 |
point-to-point NetDevice. This trace source fires when a packet is dropped |
|
1775 |
by the physical layer of a ``NetDevice``. If you take a small detour to the |
|
7145
a925e518220b
Rescan wifi's bindings and fix some paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7137
diff
changeset
|
1776 |
source (``src/point-to-point/model/point-to-point-net-device.cc``) you will |
6754 | 1777 |
see that this trace source refers to ``PointToPointNetDevice::m_phyRxDropTrace``. |
7145
a925e518220b
Rescan wifi's bindings and fix some paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7137
diff
changeset
|
1778 |
If you then look in ``src/point-to-point/model/point-to-point-net-device.h`` |
6754 | 1779 |
for this member variable, you will find that it is declared as a |
1780 |
``TracedCallback<Ptr<const Packet> >``. This should tell you that the |
|
1781 |
callback target should be a function that returns void and takes a single |
|
1782 |
parameter which is a ``Ptr<const Packet>`` -- just what we have above. |
|
1783 |
||
1784 |
The Main Program |
|
1785 |
~~~~~~~~~~~~~~~~ |
|
1786 |
||
1787 |
The following code should be very familiar to you by now: |
|
1788 |
||
1789 |
:: |
|
1790 |
||
1791 |
int |
|
1792 |
main (int argc, char *argv[]) |
|
1793 |
{ |
|
1794 |
NodeContainer nodes; |
|
1795 |
nodes.Create (2); |
|
1796 |
||
1797 |
PointToPointHelper pointToPoint; |
|
1798 |
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); |
|
1799 |
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); |
|
1800 |
||
1801 |
NetDeviceContainer devices; |
|
1802 |
devices = pointToPoint.Install (nodes); |
|
1803 |
||
1804 |
This creates two nodes with a point-to-point channel between them, just as |
|
1805 |
shown in the illustration at the start of the file. |
|
1806 |
||
1807 |
The next few lines of code show something new. If we trace a connection that |
|
1808 |
behaves perfectly, we will end up with a monotonically increasing congestion |
|
1809 |
window. To see any interesting behavior, we really want to introduce link |
|
1810 |
errors which will drop packets, cause duplicate ACKs and trigger the more |
|
1811 |
interesting behaviors of the congestion window. |
|
1812 |
||
1813 |
|ns3| provides ``ErrorModel`` objects which can be attached to |
|
1814 |
``Channels``. We are using the ``RateErrorModel`` which allows us |
|
1815 |
to introduce errors into a ``Channel`` at a given *rate*. |
|
1816 |
||
1817 |
:: |
|
1818 |
||
8986
3391f6a7fb3b
Replace src/network usage of RandomVariable with RandomVariableStream
Mitch Watrous
parents:
8906
diff
changeset
|
1819 |
Ptr<RateErrorModel> em = CreateObject<RateErrorModel> (); |
3391f6a7fb3b
Replace src/network usage of RandomVariable with RandomVariableStream
Mitch Watrous
parents:
8906
diff
changeset
|
1820 |
em->SetAttribute ("ErrorRate", DoubleValue (0.00001)); |
6754 | 1821 |
devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); |
1822 |
||
8986
3391f6a7fb3b
Replace src/network usage of RandomVariable with RandomVariableStream
Mitch Watrous
parents:
8906
diff
changeset
|
1823 |
The above code instantiates a ``RateErrorModel`` Object, and |
3391f6a7fb3b
Replace src/network usage of RandomVariable with RandomVariableStream
Mitch Watrous
parents:
8906
diff
changeset
|
1824 |
we set the "ErrorRate" ``Attribute`` to the desired value. |
6754 | 1825 |
We then set the resulting instantiated ``RateErrorModel`` as the error |
1826 |
model used by the point-to-point ``NetDevice``. This will give us some |
|
1827 |
retransmissions and make our plot a little more interesting. |
|
1828 |
||
1829 |
:: |
|
1830 |
||
1831 |
InternetStackHelper stack; |
|
1832 |
stack.Install (nodes); |
|
1833 |
||
1834 |
Ipv4AddressHelper address; |
|
1835 |
address.SetBase ("10.1.1.0", "255.255.255.252"); |
|
1836 |
Ipv4InterfaceContainer interfaces = address.Assign (devices); |
|
1837 |
||
1838 |
The above code should be familiar. It installs internet stacks on our two |
|
1839 |
nodes and creates interfaces and assigns IP addresses for the point-to-point |
|
1840 |
devices. |
|
1841 |
||
1842 |
Since we are using TCP, we need something on the destination node to receive |
|
1843 |
TCP connections and data. The ``PacketSink`` ``Application`` is commonly |
|
1844 |
used in |ns3| for that purpose. |
|
1845 |
||
1846 |
:: |
|
1847 |
||
1848 |
uint16_t sinkPort = 8080; |
|
1849 |
Address sinkAddress (InetSocketAddress(interfaces.GetAddress (1), sinkPort)); |
|
1850 |
PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", |
|
1851 |
InetSocketAddress (Ipv4Address::GetAny (), sinkPort)); |
|
1852 |
ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (1)); |
|
1853 |
sinkApps.Start (Seconds (0.)); |
|
1854 |
sinkApps.Stop (Seconds (20.)); |
|
1855 |
||
1856 |
This should all be familiar, with the exception of, |
|
1857 |
||
1858 |
:: |
|
1859 |
||
1860 |
PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", |
|
1861 |
InetSocketAddress (Ipv4Address::GetAny (), sinkPort)); |
|
1862 |
||
1863 |
This code instantiates a ``PacketSinkHelper`` and tells it to create sockets |
|
1864 |
using the class ``ns3::TcpSocketFactory``. This class implements a design |
|
1865 |
pattern called "object factory" which is a commonly used mechanism for |
|
1866 |
specifying a class used to create objects in an abstract way. Here, instead of |
|
1867 |
having to create the objects themselves, you provide the ``PacketSinkHelper`` |
|
1868 |
a string that specifies a ``TypeId`` string used to create an object which |
|
1869 |
can then be used, in turn, to create instances of the Objects created by the |
|
1870 |
factory. |
|
1871 |
||
1872 |
The remaining parameter tells the ``Application`` which address and port it |
|
1873 |
should ``Bind`` to. |
|
1874 |
||
1875 |
The next two lines of code will create the socket and connect the trace source. |
|
1876 |
||
1877 |
:: |
|
1878 |
||
1879 |
Ptr<Socket> ns3TcpSocket = Socket::CreateSocket (nodes.Get (0), |
|
1880 |
TcpSocketFactory::GetTypeId ()); |
|
1881 |
ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", |
|
1882 |
MakeCallback (&CwndChange)); |
|
1883 |
||
1884 |
The first statement calls the static member function ``Socket::CreateSocket`` |
|
1885 |
and provides a ``Node`` and an explicit ``TypeId`` for the object factory |
|
1886 |
used to create the socket. This is a slightly lower level call than the |
|
1887 |
``PacketSinkHelper`` call above, and uses an explicit C++ type instead of |
|
1888 |
one referred to by a string. Otherwise, it is conceptually the same thing. |
|
1889 |
||
1890 |
Once the ``TcpSocket`` is created and attached to the ``Node``, we can |
|
1891 |
use ``TraceConnectWithoutContext`` to connect the CongestionWindow trace |
|
1892 |
source to our trace sink. |
|
1893 |
||
1894 |
Recall that we coded an ``Application`` so we could take that ``Socket`` |
|
1895 |
we just made (during configuration time) and use it in simulation time. We now |
|
1896 |
have to instantiate that ``Application``. We didn't go to any trouble to |
|
1897 |
create a helper to manage the ``Application`` so we are going to have to |
|
1898 |
create and install it "manually". This is actually quite easy: |
|
1899 |
||
1900 |
:: |
|
1901 |
||
1902 |
Ptr<MyApp> app = CreateObject<MyApp> (); |
|
1903 |
app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps")); |
|
1904 |
nodes.Get (0)->AddApplication (app); |
|
1905 |
app->Start (Seconds (1.)); |
|
1906 |
app->Stop (Seconds (20.)); |
|
1907 |
||
1908 |
The first line creates an ``Object`` of type ``MyApp`` -- our |
|
1909 |
``Application``. The second line tells the ``Application`` what |
|
1910 |
``Socket`` to use, what address to connect to, how much data to send |
|
1911 |
at each send event, how many send events to generate and the rate at which |
|
1912 |
to produce data from those events. |
|
1913 |
||
1914 |
Next, we manually add the ``MyApp Application`` to the source node |
|
1915 |
and explicitly call the ``Start`` and ``Stop`` methods on the |
|
1916 |
``Application`` to tell it when to start and stop doing its thing. |
|
1917 |
||
1918 |
We need to actually do the connect from the receiver point-to-point ``NetDevice`` |
|
1919 |
to our callback now. |
|
1920 |
||
1921 |
:: |
|
1922 |
||
1923 |
devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeCallback (&RxDrop)); |
|
1924 |
||
1925 |
It should now be obvious that we are getting a reference to the receiving |
|
1926 |
``Node NetDevice`` from its container and connecting the trace source defined |
|
1927 |
by the attribute "PhyRxDrop" on that device to the trace sink ``RxDrop``. |
|
1928 |
||
1929 |
Finally, we tell the simulator to override any ``Applications`` and just |
|
1930 |
stop processing events at 20 seconds into the simulation. |
|
1931 |
||
1932 |
:: |
|
1933 |
||
1934 |
Simulator::Stop (Seconds(20)); |
|
1935 |
Simulator::Run (); |
|
1936 |
Simulator::Destroy (); |
|
1937 |
||
1938 |
return 0; |
|
1939 |
} |
|
1940 |
||
1941 |
Recall that as soon as ``Simulator::Run`` is called, configuration time |
|
1942 |
ends, and simulation time begins. All of the work we orchestrated by |
|
1943 |
creating the ``Application`` and teaching it how to connect and send |
|
1944 |
data actually happens during this function call. |
|
1945 |
||
1946 |
As soon as ``Simulator::Run`` returns, the simulation is complete and |
|
1947 |
we enter the teardown phase. In this case, ``Simulator::Destroy`` takes |
|
1948 |
care of the gory details and we just return a success code after it completes. |
|
1949 |
||
1950 |
Running fifth.cc |
|
1951 |
++++++++++++++++ |
|
1952 |
||
1953 |
Since we have provided the file ``fifth.cc`` for you, if you have built |
|
1954 |
your distribution (in debug mode since it uses NS_LOG -- recall that optimized |
|
1955 |
builds optimize out NS_LOGs) it will be waiting for you to run. |
|
1956 |
||
1957 |
:: |
|
1958 |
||
1959 |
./waf --run fifth |
|
1960 |
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build |
|
1961 |
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build' |
|
1962 |
'build' finished successfully (0.684s) |
|
1963 |
1.20919 1072 |
|
1964 |
1.21511 1608 |
|
1965 |
1.22103 2144 |
|
1966 |
... |
|
1967 |
1.2471 8040 |
|
1968 |
1.24895 8576 |
|
1969 |
1.2508 9112 |
|
1970 |
RxDrop at 1.25151 |
|
1971 |
... |
|
1972 |
||
1973 |
You can probably see immediately a downside of using prints of any kind in your |
|
1974 |
traces. We get those extraneous waf messages printed all over our interesting |
|
1975 |
information along with those RxDrop messages. We will remedy that soon, but I'm |
|
1976 |
sure you can't wait to see the results of all of this work. Let's redirect that |
|
1977 |
output to a file called ``cwnd.dat``: |
|
1978 |
||
1979 |
:: |
|
1980 |
||
1981 |
./waf --run fifth > cwnd.dat 2>&1 |
|
1982 |
||
1983 |
Now edit up "cwnd.dat" in your favorite editor and remove the waf build status |
|
1984 |
and drop lines, leaving only the traced data (you could also comment out the |
|
1985 |
``TraceConnectWithoutContext("PhyRxDrop", MakeCallback (&RxDrop));`` in the |
|
1986 |
script to get rid of the drop prints just as easily. |
|
1987 |
||
1988 |
You can now run gnuplot (if you have it installed) and tell it to generate some |
|
1989 |
pretty pictures: |
|
1990 |
||
1991 |
:: |
|
1992 |
||
1993 |
gnuplot> set terminal png size 640,480 |
|
1994 |
gnuplot> set output "cwnd.png" |
|
1995 |
gnuplot> plot "cwnd.dat" using 1:2 title 'Congestion Window' with linespoints |
|
1996 |
gnuplot> exit |
|
1997 |
||
1998 |
You should now have a graph of the congestion window versus time sitting in the |
|
1999 |
file "cwnd.png" that looks like: |
|
2000 |
||
2001 |
.. figure:: figures/cwnd.png |
|
2002 |
||
2003 |
Using Mid-Level Helpers |
|
2004 |
+++++++++++++++++++++++ |
|
2005 |
||
2006 |
In the previous section, we showed how to hook a trace source and get hopefully |
|
2007 |
interesting information out of a simulation. Perhaps you will recall that we |
|
2008 |
called logging to the standard output using ``std::cout`` a "Blunt Instrument" |
|
2009 |
much earlier in this chapter. We also wrote about how it was a problem having |
|
2010 |
to parse the log output in order to isolate interesting information. It may |
|
2011 |
have occurred to you that we just spent a lot of time implementing an example |
|
2012 |
that exhibits all of the problems we purport to fix with the |ns3| tracing |
|
2013 |
system! You would be correct. But, bear with us. We're not done yet. |
|
2014 |
||
2015 |
One of the most important things we want to do is to is to have the ability to |
|
2016 |
easily control the amount of output coming out of the simulation; and we also |
|
2017 |
want to save those data to a file so we can refer back to it later. We can use |
|
2018 |
the mid-level trace helpers provided in |ns3| to do just that and complete |
|
2019 |
the picture. |
|
2020 |
||
2021 |
We provide a script that writes the cwnd change and drop events developed in |
|
2022 |
the example ``fifth.cc`` to disk in separate files. The cwnd changes are |
|
2023 |
stored as a tab-separated ASCII file and the drop events are stored in a pcap |
|
2024 |
file. The changes to make this happen are quite small. |
|
2025 |
||
2026 |
A sixth.cc Walkthrough |
|
2027 |
~~~~~~~~~~~~~~~~~~~~~~ |
|
2028 |
||
2029 |
Let's take a look at the changes required to go from ``fifth.cc`` to |
|
2030 |
``sixth.cc``. Open ``examples/tutorial/fifth.cc`` in your favorite |
|
2031 |
editor. You can see the first change by searching for CwndChange. You will |
|
2032 |
find that we have changed the signatures for the trace sinks and have added |
|
2033 |
a single line to each sink that writes the traced information to a stream |
|
2034 |
representing a file. |
|
2035 |
||
2036 |
:: |
|
2037 |
||
2038 |
static void |
|
2039 |
CwndChange (Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd) |
|
2040 |
{ |
|
2041 |
NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd); |
|
2042 |
*stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl; |
|
2043 |
} |
|
2044 |
||
2045 |
static void |
|
2046 |
RxDrop (Ptr<PcapFileWrapper> file, Ptr<const Packet> p) |
|
2047 |
{ |
|
2048 |
NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ()); |
|
2049 |
file->Write(Simulator::Now(), p); |
|
2050 |
} |
|
2051 |
||
2052 |
We have added a "stream" parameter to the ``CwndChange`` trace sink. |
|
2053 |
This is an object that holds (keeps safely alive) a C++ output stream. It |
|
2054 |
turns out that this is a very simple object, but one that manages lifetime |
|
2055 |
issues for the stream and solves a problem that even experienced C++ users |
|
2056 |
run into. It turns out that the copy constructor for ostream is marked |
|
2057 |
private. This means that ostreams do not obey value semantics and cannot |
|
2058 |
be used in any mechanism that requires the stream to be copied. This includes |
|
2059 |
the |ns3| callback system, which as you may recall, requires objects |
|
2060 |
that obey value semantics. Further notice that we have added the following |
|
2061 |
line in the ``CwndChange`` trace sink implementation: |
|
2062 |
||
2063 |
:: |
|
2064 |
||
2065 |
*stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl; |
|
2066 |
||
2067 |
This would be very familiar code if you replaced ``*stream->GetStream ()`` |
|
2068 |
with ``std::cout``, as in: |
|
2069 |
||
2070 |
:: |
|
2071 |
||
2072 |
std::cout << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl; |
|
2073 |
||
2074 |
This illustrates that the ``Ptr<OutputStreamWrapper>`` is really just |
|
2075 |
carrying around a ``std::ofstream`` for you, and you can use it here like |
|
2076 |
any other output stream. |
|
2077 |
||
2078 |
A similar situation happens in ``RxDrop`` except that the object being |
|
2079 |
passed around (a ``Ptr<PcapFileWrapper>``) represents a pcap file. There |
|
2080 |
is a one-liner in the trace sink to write a timestamp and the contents of the |
|
2081 |
packet being dropped to the pcap file: |
|
2082 |
||
2083 |
:: |
|
2084 |
||
2085 |
file->Write(Simulator::Now(), p); |
|
2086 |
||
2087 |
Of course, if we have objects representing the two files, we need to create |
|
2088 |
them somewhere and also cause them to be passed to the trace sinks. If you |
|
2089 |
look in the ``main`` function, you will find new code to do just that: |
|
2090 |
||
2091 |
:: |
|
2092 |
||
2093 |
AsciiTraceHelper asciiTraceHelper; |
|
2094 |
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd"); |
|
2095 |
ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream)); |
|
2096 |
||
2097 |
... |
|
2098 |
||
2099 |
PcapHelper pcapHelper; |
|
2100 |
Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", std::ios::out, PcapHelper::DLT_PPP); |
|
2101 |
devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file)); |
|
2102 |
||
2103 |
In the first section of the code snippet above, we are creating the ASCII |
|
2104 |
trace file, creating an object responsible for managing it and using a |
|
2105 |
variant of the callback creation function to arrange for the object to be |
|
2106 |
passed to the sink. Our ASCII trace helpers provide a rich set of |
|
2107 |
functions to make using text (ASCII) files easy. We are just going to |
|
2108 |
illustrate the use of the file stream creation function here. |
|
2109 |
||
2110 |
The ``CreateFileStream{}`` function is basically going to instantiate |
|
2111 |
a std::ofstream object and create a new file (or truncate an existing file). |
|
2112 |
This ofstream is packaged up in an |ns3| object for lifetime management |
|
2113 |
and copy constructor issue resolution. |
|
2114 |
||
2115 |
We then take this |ns3| object representing the file and pass it to |
|
2116 |
``MakeBoundCallback()``. This function creates a callback just like |
|
2117 |
``MakeCallback()``, but it "binds" a new value to the callback. This |
|
2118 |
value is added to the callback before it is called. |
|
2119 |
||
2120 |
Essentially, ``MakeBoundCallback(&CwndChange, stream)`` causes the trace |
|
2121 |
source to add the additional "stream" parameter to the front of the formal |
|
2122 |
parameter list before invoking the callback. This changes the required |
|
2123 |
signature of the ``CwndChange`` sink to match the one shown above, which |
|
2124 |
includes the "extra" parameter ``Ptr<OutputStreamWrapper> stream``. |
|
2125 |
||
2126 |
In the second section of code in the snippet above, we instantiate a |
|
2127 |
``PcapHelper`` to do the same thing for our pcap trace file that we did |
|
2128 |
with the ``AsciiTraceHelper``. The line of code, |
|
2129 |
||
2130 |
:: |
|
2131 |
||
2132 |
Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", "w", PcapHelper::DLT_PPP); |
|
2133 |
||
2134 |
creates a pcap file named "sixth.pcap" with file mode "w". This means that |
|
2135 |
the new file is to truncated if an existing file with that name is found. The |
|
2136 |
final parameter is the "data link type" of the new pcap file. These are |
|
2137 |
the same as the pcap library data link types defined in ``bpf.h`` if you are |
|
2138 |
familar with pcap. In this case, ``DLT_PPP`` indicates that the pcap file |
|
2139 |
is going to contain packets prefixed with point to point headers. This is true |
|
2140 |
since the packets are coming from our point-to-point device driver. Other |
|
2141 |
common data link types are DLT_EN10MB (10 MB Ethernet) appropriate for csma |
|
2142 |
devices and DLT_IEEE802_11 (IEEE 802.11) appropriate for wifi devices. These |
|
7147
71adbc0422a5
Fix more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7145
diff
changeset
|
2143 |
are defined in ``src/network/helper/trace-helper.h"`` if you are interested in seeing |
6754 | 2144 |
the list. The entries in the list match those in ``bpf.h`` but we duplicate |
2145 |
them to avoid a pcap source dependence. |
|
2146 |
||
2147 |
A |ns3| object representing the pcap file is returned from ``CreateFile`` |
|
2148 |
and used in a bound callback exactly as it was in the ascii case. |
|
2149 |
||
2150 |
An important detour: It is important to notice that even though both of these |
|
2151 |
objects are declared in very similar ways, |
|
2152 |
||
2153 |
:: |
|
2154 |
||
2155 |
Ptr<PcapFileWrapper> file ... |
|
2156 |
Ptr<OutputStreamWrapper> stream ... |
|
2157 |
||
2158 |
The underlying objects are entirely different. For example, the |
|
2159 |
Ptr<PcapFileWrapper> is a smart pointer to an |ns3| Object that is a |
|
2160 |
fairly heaviweight thing that supports ``Attributes`` and is integrated into |
|
2161 |
the config system. The Ptr<OutputStreamWrapper>, on the other hand, is a smart |
|
2162 |
pointer to a reference counted object that is a very lightweight thing. |
|
2163 |
Remember to always look at the object you are referencing before making any |
|
2164 |
assumptions about the "powers" that object may have. |
|
2165 |
||
8771 | 2166 |
For example, take a look at ``src/network/utils/pcap-file-wrapper.h`` in the |
6754 | 2167 |
distribution and notice, |
2168 |
||
2169 |
:: |
|
2170 |
||
2171 |
class PcapFileWrapper : public Object |
|
2172 |
||
2173 |
that class ``PcapFileWrapper`` is an |ns3| Object by virtue of |
|
7148
6fc89f1818e8
Fix even more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7147
diff
changeset
|
2174 |
its inheritance. Then look at ``src/network/model/output-stream-wrapper.h`` and |
6754 | 2175 |
notice, |
2176 |
||
2177 |
:: |
|
2178 |
||
2179 |
class OutputStreamWrapper : public SimpleRefCount<OutputStreamWrapper> |
|
2180 |
||
2181 |
that this object is not an |ns3| Object at all, it is "merely" a |
|
2182 |
C++ object that happens to support intrusive reference counting. |
|
2183 |
||
2184 |
The point here is that just because you read Ptr<something> it does not necessarily |
|
2185 |
mean that "something" is an |ns3| Object on which you can hang |ns3| |
|
2186 |
``Attributes``, for example. |
|
2187 |
||
2188 |
Now, back to the example. If you now build and run this example, |
|
2189 |
||
2190 |
:: |
|
2191 |
||
2192 |
./waf --run sixth |
|
2193 |
||
2194 |
you will see the same messages appear as when you ran "fifth", but two new |
|
2195 |
files will appear in the top-level directory of your |ns3| distribution. |
|
2196 |
||
2197 |
:: |
|
2198 |
||
2199 |
sixth.cwnd sixth.pcap |
|
2200 |
||
2201 |
Since "sixth.cwnd" is an ASCII text file, you can view it with ``cat`` |
|
2202 |
or your favorite file viewer. |
|
2203 |
||
2204 |
:: |
|
2205 |
||
2206 |
1.20919 536 1072 |
|
2207 |
1.21511 1072 1608 |
|
2208 |
... |
|
2209 |
9.30922 8893 8925 |
|
2210 |
9.31754 8925 8957 |
|
2211 |
||
2212 |
You have a tab separated file with a timestamp, an old congestion window and a |
|
2213 |
new congestion window suitable for directly importing into your plot program. |
|
2214 |
There are no extraneous prints in the file, no parsing or editing is required. |
|
2215 |
||
2216 |
Since "sixth.pcap" is a pcap file, you can fiew it with ``tcpdump``. |
|
2217 |
||
2218 |
:: |
|
2219 |
||
2220 |
reading from file ../../sixth.pcap, link-type PPP (PPP) |
|
2221 |
1.251507 IP 10.1.1.1.49153 > 10.1.1.2.8080: . 17689:18225(536) ack 1 win 65535 |
|
2222 |
1.411478 IP 10.1.1.1.49153 > 10.1.1.2.8080: . 33808:34312(504) ack 1 win 65535 |
|
2223 |
... |
|
2224 |
7.393557 IP 10.1.1.1.49153 > 10.1.1.2.8080: . 781568:782072(504) ack 1 win 65535 |
|
2225 |
8.141483 IP 10.1.1.1.49153 > 10.1.1.2.8080: . 874632:875168(536) ack 1 win 65535 |
|
2226 |
||
2227 |
You have a pcap file with the packets that were dropped in the simulation. There |
|
2228 |
are no other packets present in the file and there is nothing else present to |
|
2229 |
make life difficult. |
|
2230 |
||
2231 |
It's been a long journey, but we are now at a point where we can appreciate the |
|
2232 |
|ns3| tracing system. We have pulled important events out of the middle |
|
2233 |
of a TCP implementation and a device driver. We stored those events directly in |
|
2234 |
files usable with commonly known tools. We did this without modifying any of the |
|
2235 |
core code involved, and we did this in only 18 lines of code: |
|
2236 |
||
2237 |
:: |
|
2238 |
||
2239 |
static void |
|
2240 |
CwndChange (Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd) |
|
2241 |
{ |
|
2242 |
NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd); |
|
2243 |
*stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl; |
|
2244 |
} |
|
2245 |
||
2246 |
... |
|
2247 |
||
2248 |
AsciiTraceHelper asciiTraceHelper; |
|
2249 |
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd"); |
|
2250 |
ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream)); |
|
2251 |
||
2252 |
... |
|
2253 |
||
2254 |
static void |
|
2255 |
RxDrop (Ptr<PcapFileWrapper> file, Ptr<const Packet> p) |
|
2256 |
{ |
|
2257 |
NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ()); |
|
2258 |
file->Write(Simulator::Now(), p); |
|
2259 |
} |
|
2260 |
||
2261 |
... |
|
2262 |
||
2263 |
PcapHelper pcapHelper; |
|
2264 |
Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", "w", PcapHelper::DLT_PPP); |
|
2265 |
devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file)); |
|
2266 |
||
2267 |
Using Trace Helpers |
|
2268 |
******************* |
|
2269 |
||
2270 |
The |ns3| trace helpers provide a rich environment for configuring and |
|
2271 |
selecting different trace events and writing them to files. In previous |
|
2272 |
sections, primarily "Building Topologies," we have seen several varieties |
|
2273 |
of the trace helper methods designed for use inside other (device) helpers. |
|
2274 |
||
2275 |
Perhaps you will recall seeing some of these variations: |
|
2276 |
||
2277 |
:: |
|
2278 |
||
2279 |
pointToPoint.EnablePcapAll ("second"); |
|
2280 |
pointToPoint.EnablePcap ("second", p2pNodes.Get (0)->GetId (), 0); |
|
2281 |
csma.EnablePcap ("third", csmaDevices.Get (0), true); |
|
2282 |
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr")); |
|
2283 |
||
2284 |
What may not be obvious, though, is that there is a consistent model for all of |
|
2285 |
the trace-related methods found in the system. We will now take a little time |
|
2286 |
and take a look at the "big picture". |
|
2287 |
||
2288 |
There are currently two primary use cases of the tracing helpers in |ns3|: |
|
2289 |
Device helpers and protocol helpers. Device helpers look at the problem |
|
2290 |
of specifying which traces should be enabled through a node, device pair. For |
|
2291 |
example, you may want to specify that pcap tracing should be enabled on a |
|
2292 |
particular device on a specific node. This follows from the |ns3| device |
|
2293 |
conceptual model, and also the conceptual models of the various device helpers. |
|
2294 |
Following naturally from this, the files created follow a |
|
2295 |
<prefix>-<node>-<device> naming convention. |
|
2296 |
||
2297 |
Protocol helpers look at the problem of specifying which traces should be |
|
2298 |
enabled through a protocol and interface pair. This follows from the |ns3| |
|
2299 |
protocol stack conceptual model, and also the conceptual models of internet |
|
2300 |
stack helpers. Naturally, the trace files should follow a |
|
2301 |
<prefix>-<protocol>-<interface> naming convention. |
|
2302 |
||
2303 |
The trace helpers therefore fall naturally into a two-dimensional taxonomy. |
|
2304 |
There are subtleties that prevent all four classes from behaving identically, |
|
2305 |
but we do strive to make them all work as similarly as possible; and whenever |
|
2306 |
possible there are analogs for all methods in all classes. |
|
2307 |
||
2308 |
:: |
|
2309 |
||
2310 |
| pcap | ascii | |
|
2311 |
-----------------+------+-------| |
|
2312 |
Device Helper | | | |
|
2313 |
-----------------+------+-------| |
|
2314 |
Protocol Helper | | | |
|
2315 |
-----------------+------+-------| |
|
2316 |
||
2317 |
We use an approach called a ``mixin`` to add tracing functionality to our |
|
2318 |
helper classes. A ``mixin`` is a class that provides functionality to that |
|
2319 |
is inherited by a subclass. Inheriting from a mixin is not considered a form |
|
2320 |
of specialization but is really a way to collect functionality. |
|
2321 |
||
2322 |
Let's take a quick look at all four of these cases and their respective |
|
2323 |
``mixins``. |
|
2324 |
||
2325 |
Pcap Tracing Device Helpers |
|
2326 |
+++++++++++++++++++++++++++ |
|
2327 |
||
2328 |
The goal of these helpers is to make it easy to add a consistent pcap trace |
|
2329 |
facility to an |ns3| device. We want all of the various flavors of |
|
2330 |
pcap tracing to work the same across all devices, so the methods of these |
|
2331 |
helpers are inherited by device helpers. Take a look at |
|
7147
71adbc0422a5
Fix more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7145
diff
changeset
|
2332 |
``src/network/helper/trace-helper.h`` if you want to follow the discussion while |
6754 | 2333 |
looking at real code. |
2334 |
||
2335 |
The class ``PcapHelperForDevice`` is a ``mixin`` provides the high level |
|
2336 |
functionality for using pcap tracing in an |ns3| device. Every device |
|
2337 |
must implement a single virtual method inherited from this class. |
|
2338 |
||
2339 |
:: |
|
2340 |
||
2341 |
virtual void EnablePcapInternal (std::string prefix, Ptr<NetDevice> nd, bool promiscuous, bool explicitFilename) = 0; |
|
2342 |
||
2343 |
The signature of this method reflects the device-centric view of the situation |
|
2344 |
at this level. All of the public methods inherited from class |
|
2345 |
2``PcapUserHelperForDevice`` reduce to calling this single device-dependent |
|
2346 |
implementation method. For example, the lowest level pcap method, |
|
2347 |
||
2348 |
:: |
|
2349 |
||
2350 |
void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false); |
|
2351 |
||
2352 |
will call the device implementation of ``EnablePcapInternal`` directly. All |
|
2353 |
other public pcap tracing methods build on this implementation to provide |
|
2354 |
additional user-level functionality. What this means to the user is that all |
|
2355 |
device helpers in the system will have all of the pcap trace methods available; |
|
2356 |
and these methods will all work in the same way across devices if the device |
|
2357 |
implements ``EnablePcapInternal`` correctly. |
|
2358 |
||
2359 |
Pcap Tracing Device Helper Methods |
|
2360 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
2361 |
||
2362 |
:: |
|
2363 |
||
2364 |
void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false); |
|
2365 |
void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false); |
|
2366 |
void EnablePcap (std::string prefix, NetDeviceContainer d, bool promiscuous = false); |
|
2367 |
void EnablePcap (std::string prefix, NodeContainer n, bool promiscuous = false); |
|
2368 |
void EnablePcap (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool promiscuous = false); |
|
2369 |
void EnablePcapAll (std::string prefix, bool promiscuous = false); |
|
2370 |
||
2371 |
In each of the methods shown above, there is a default parameter called |
|
2372 |
``promiscuous`` that defaults to false. This parameter indicates that the |
|
2373 |
trace should not be gathered in promiscuous mode. If you do want your traces |
|
2374 |
to include all traffic seen by the device (and if the device supports a |
|
2375 |
promiscuous mode) simply add a true parameter to any of the calls above. For example, |
|
2376 |
||
2377 |
:: |
|
2378 |
||
2379 |
Ptr<NetDevice> nd; |
|
2380 |
... |
|
2381 |
helper.EnablePcap ("prefix", nd, true); |
|
2382 |
||
2383 |
will enable promiscuous mode captures on the ``NetDevice`` specified by ``nd``. |
|
2384 |
||
2385 |
The first two methods also include a default parameter called ``explicitFilename`` |
|
2386 |
that will be discussed below. |
|
2387 |
||
2388 |
You are encouraged to peruse the Doxygen for class ``PcapHelperForDevice`` |
|
2389 |
to find the details of these methods; but to summarize ... |
|
2390 |
||
2391 |
You can enable pcap tracing on a particular node/net-device pair by providing a |
|
2392 |
``Ptr<NetDevice>`` to an ``EnablePcap`` method. The ``Ptr<Node>`` is |
|
2393 |
implicit since the net device must belong to exactly one ``Node``. |
|
2394 |
For example, |
|
2395 |
||
2396 |
:: |
|
2397 |
||
2398 |
Ptr<NetDevice> nd; |
|
2399 |
... |
|
2400 |
helper.EnablePcap ("prefix", nd); |
|
2401 |
||
2402 |
You can enable pcap tracing on a particular node/net-device pair by providing a |
|
2403 |
``std::string`` representing an object name service string to an |
|
2404 |
``EnablePcap`` method. The ``Ptr<NetDevice>`` is looked up from the name |
|
2405 |
string. Again, the ``<Node>`` is implicit since the named net device must |
|
2406 |
belong to exactly one ``Node``. For example, |
|
2407 |
||
2408 |
:: |
|
2409 |
||
2410 |
Names::Add ("server" ...); |
|
2411 |
Names::Add ("server/eth0" ...); |
|
2412 |
... |
|
2413 |
helper.EnablePcap ("prefix", "server/ath0"); |
|
2414 |
||
2415 |
You can enable pcap tracing on a collection of node/net-device pairs by |
|
2416 |
providing a ``NetDeviceContainer``. For each ``NetDevice`` in the container |
|
2417 |
the type is checked. For each device of the proper type (the same type as is |
|
2418 |
managed by the device helper), tracing is enabled. Again, the ``<Node>`` is |
|
2419 |
implicit since the found net device must belong to exactly one ``Node``. |
|
2420 |
For example, |
|
2421 |
||
2422 |
:: |
|
2423 |
||
2424 |
NetDeviceContainer d = ...; |
|
2425 |
... |
|
2426 |
helper.EnablePcap ("prefix", d); |
|
2427 |
||
2428 |
You can enable pcap tracing on a collection of node/net-device pairs by |
|
2429 |
providing a ``NodeContainer``. For each ``Node`` in the ``NodeContainer`` |
|
2430 |
its attached ``NetDevices`` are iterated. For each ``NetDevice`` attached |
|
2431 |
to each node in the container, the type of that device is checked. For each |
|
2432 |
device of the proper type (the same type as is managed by the device helper), |
|
2433 |
tracing is enabled. |
|
2434 |
||
2435 |
:: |
|
2436 |
||
2437 |
NodeContainer n; |
|
2438 |
... |
|
2439 |
helper.EnablePcap ("prefix", n); |
|
2440 |
||
2441 |
You can enable pcap tracing on the basis of node ID and device ID as well as |
|
2442 |
with explicit ``Ptr``. Each ``Node`` in the system has an integer node ID |
|
2443 |
and each device connected to a node has an integer device ID. |
|
2444 |
||
2445 |
:: |
|
2446 |
||
2447 |
helper.EnablePcap ("prefix", 21, 1); |
|
2448 |
||
2449 |
Finally, you can enable pcap tracing for all devices in the system, with the |
|
2450 |
same type as that managed by the device helper. |
|
2451 |
||
2452 |
:: |
|
2453 |
||
2454 |
helper.EnablePcapAll ("prefix"); |
|
2455 |
||
2456 |
Pcap Tracing Device Helper Filename Selection |
|
2457 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
2458 |
||
2459 |
Implicit in the method descriptions above is the construction of a complete |
|
2460 |
filename by the implementation method. By convention, pcap traces in the |
|
2461 |
|ns3| system are of the form "<prefix>-<node id>-<device id>.pcap" |
|
2462 |
||
2463 |
As previously mentioned, every node in the system will have a system-assigned |
|
2464 |
node id; and every device will have an interface index (also called a device id) |
|
2465 |
relative to its node. By default, then, a pcap trace file created as a result |
|
2466 |
of enabling tracing on the first device of node 21 using the prefix "prefix" |
|
2467 |
would be "prefix-21-1.pcap". |
|
2468 |
||
2469 |
You can always use the |ns3| object name service to make this more clear. |
|
2470 |
For example, if you use the object name service to assign the name "server" |
|
2471 |
to node 21, the resulting pcap trace file name will automatically become, |
|
2472 |
"prefix-server-1.pcap" and if you also assign the name "eth0" to the |
|
2473 |
device, your pcap file name will automatically pick this up and be called |
|
2474 |
"prefix-server-eth0.pcap". |
|
2475 |
||
2476 |
Finally, two of the methods shown above, |
|
2477 |
||
2478 |
:: |
|
2479 |
||
2480 |
void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false); |
|
2481 |
void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false); |
|
2482 |
||
2483 |
have a default parameter called ``explicitFilename``. When set to true, |
|
2484 |
this parameter disables the automatic filename completion mechanism and allows |
|
2485 |
you to create an explicit filename. This option is only available in the |
|
2486 |
methods which enable pcap tracing on a single device. |
|
2487 |
||
2488 |
For example, in order to arrange for a device helper to create a single |
|
2489 |
promiscuous pcap capture file of a specific name ("my-pcap-file.pcap") on a |
|
2490 |
given device, one could: |
|
2491 |
||
2492 |
:: |
|
2493 |
||
2494 |
Ptr<NetDevice> nd; |
|
2495 |
... |
|
2496 |
helper.EnablePcap ("my-pcap-file.pcap", nd, true, true); |
|
2497 |
||
2498 |
The first ``true`` parameter enables promiscuous mode traces and the second |
|
2499 |
tells the helper to interpret the ``prefix`` parameter as a complete filename. |
|
2500 |
||
2501 |
Ascii Tracing Device Helpers |
|
2502 |
++++++++++++++++++++++++++++ |
|
2503 |
||
2504 |
The behavior of the ascii trace helper ``mixin`` is substantially similar to |
|
7147
71adbc0422a5
Fix more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7145
diff
changeset
|
2505 |
the pcap version. Take a look at ``src/network/helper/trace-helper.h`` if you want to |
6754 | 2506 |
follow the discussion while looking at real code. |
2507 |
||
2508 |
The class ``AsciiTraceHelperForDevice`` adds the high level functionality for |
|
2509 |
using ascii tracing to a device helper class. As in the pcap case, every device |
|
2510 |
must implement a single virtual method inherited from the ascii trace ``mixin``. |
|
2511 |
||
2512 |
:: |
|
2513 |
||
2514 |
virtual void EnableAsciiInternal (Ptr<OutputStreamWrapper> stream, |
|
2515 |
std::string prefix, |
|
2516 |
Ptr<NetDevice> nd, |
|
2517 |
bool explicitFilename) = 0; |
|
2518 |
||
2519 |
||
2520 |
The signature of this method reflects the device-centric view of the situation |
|
2521 |
at this level; and also the fact that the helper may be writing to a shared |
|
2522 |
output stream. All of the public ascii-trace-related methods inherited from |
|
2523 |
class ``AsciiTraceHelperForDevice`` reduce to calling this single device- |
|
2524 |
dependent implementation method. For example, the lowest level ascii trace |
|
2525 |
methods, |
|
2526 |
||
2527 |
:: |
|
2528 |
||
2529 |
void EnableAscii (std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false); |
|
2530 |
void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd); |
|
2531 |
||
2532 |
||
2533 |
will call the device implementation of ``EnableAsciiInternal`` directly, |
|
2534 |
providing either a valid prefix or stream. All other public ascii tracing |
|
2535 |
methods will build on these low-level functions to provide additional user-level |
|
2536 |
functionality. What this means to the user is that all device helpers in the |
|
2537 |
system will have all of the ascii trace methods available; and these methods |
|
2538 |
will all work in the same way across devices if the devices implement |
|
2539 |
``EnablAsciiInternal`` correctly. |
|
2540 |
||
2541 |
Ascii Tracing Device Helper Methods |
|
2542 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
2543 |
||
2544 |
:: |
|
2545 |
||
2546 |
void EnableAscii (std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false); |
|
2547 |
void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd); |
|
2548 |
||
2549 |
void EnableAscii (std::string prefix, std::string ndName, bool explicitFilename = false); |
|
2550 |
void EnableAscii (Ptr<OutputStreamWrapper> stream, std::string ndName); |
|
2551 |
||
2552 |
void EnableAscii (std::string prefix, NetDeviceContainer d); |
|
2553 |
void EnableAscii (Ptr<OutputStreamWrapper> stream, NetDeviceContainer d); |
|
2554 |
||
2555 |
void EnableAscii (std::string prefix, NodeContainer n); |
|
2556 |
void EnableAscii (Ptr<OutputStreamWrapper> stream, NodeContainer n); |
|
2557 |
||
2558 |
void EnableAsciiAll (std::string prefix); |
|
2559 |
void EnableAsciiAll (Ptr<OutputStreamWrapper> stream); |
|
2560 |
||
2561 |
void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename); |
|
2562 |
void EnableAscii (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t deviceid); |
|
2563 |
||
8772 | 2564 |
You are encouraged to peruse the Doxygen for class ``AsciiTraceHelperForDevice`` |
6754 | 2565 |
to find the details of these methods; but to summarize ... |
2566 |
||
2567 |
There are twice as many methods available for ascii tracing as there were for |
|
2568 |
pcap tracing. This is because, in addition to the pcap-style model where traces |
|
2569 |
from each unique node/device pair are written to a unique file, we support a model |
|
2570 |
in which trace information for many node/device pairs is written to a common file. |
|
2571 |
This means that the <prefix>-<node>-<device> file name generation mechanism is |
|
2572 |
replaced by a mechanism to refer to a common file; and the number of API methods |
|
2573 |
is doubled to allow all combinations. |
|
2574 |
||
2575 |
Just as in pcap tracing, you can enable ascii tracing on a particular |
|
2576 |
node/net-device pair by providing a ``Ptr<NetDevice>`` to an ``EnableAscii`` |
|
2577 |
method. The ``Ptr<Node>`` is implicit since the net device must belong to |
|
2578 |
exactly one ``Node``. For example, |
|
2579 |
||
2580 |
:: |
|
2581 |
||
2582 |
Ptr<NetDevice> nd; |
|
2583 |
... |
|
2584 |
helper.EnableAscii ("prefix", nd); |
|
2585 |
||
2586 |
The first four methods also include a default parameter called ``explicitFilename`` |
|
2587 |
that operate similar to equivalent parameters in the pcap case. |
|
2588 |
||
2589 |
In this case, no trace contexts are written to the ascii trace file since they |
|
2590 |
would be redundant. The system will pick the file name to be created using |
|
2591 |
the same rules as described in the pcap section, except that the file will |
|
2592 |
have the suffix ".tr" instead of ".pcap". |
|
2593 |
||
2594 |
If you want to enable ascii tracing on more than one net device and have all |
|
2595 |
traces sent to a single file, you can do that as well by using an object to |
|
2596 |
refer to a single file. We have already seen this in the "cwnd" example |
|
2597 |
above: |
|
2598 |
||
2599 |
:: |
|
2600 |
||
2601 |
Ptr<NetDevice> nd1; |
|
2602 |
Ptr<NetDevice> nd2; |
|
2603 |
... |
|
2604 |
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); |
|
2605 |
... |
|
2606 |
helper.EnableAscii (stream, nd1); |
|
2607 |
helper.EnableAscii (stream, nd2); |
|
2608 |
||
2609 |
||
2610 |
In this case, trace contexts are written to the ascii trace file since they |
|
2611 |
are required to disambiguate traces from the two devices. Note that since the |
|
2612 |
user is completely specifying the file name, the string should include the ",tr" |
|
2613 |
for consistency. |
|
2614 |
||
2615 |
You can enable ascii tracing on a particular node/net-device pair by providing a |
|
2616 |
``std::string`` representing an object name service string to an |
|
2617 |
``EnablePcap`` method. The ``Ptr<NetDevice>`` is looked up from the name |
|
2618 |
string. Again, the ``<Node>`` is implicit since the named net device must |
|
2619 |
belong to exactly one ``Node``. For example, |
|
2620 |
||
2621 |
:: |
|
2622 |
||
2623 |
Names::Add ("client" ...); |
|
2624 |
Names::Add ("client/eth0" ...); |
|
2625 |
Names::Add ("server" ...); |
|
2626 |
Names::Add ("server/eth0" ...); |
|
2627 |
... |
|
2628 |
helper.EnableAscii ("prefix", "client/eth0"); |
|
2629 |
helper.EnableAscii ("prefix", "server/eth0"); |
|
2630 |
||
2631 |
This would result in two files named "prefix-client-eth0.tr" and |
|
2632 |
"prefix-server-eth0.tr" with traces for each device in the respective trace |
|
2633 |
file. Since all of the EnableAscii functions are overloaded to take a stream wrapper, |
|
2634 |
you can use that form as well: |
|
2635 |
||
2636 |
:: |
|
2637 |
||
2638 |
Names::Add ("client" ...); |
|
2639 |
Names::Add ("client/eth0" ...); |
|
2640 |
Names::Add ("server" ...); |
|
2641 |
Names::Add ("server/eth0" ...); |
|
2642 |
... |
|
2643 |
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); |
|
2644 |
... |
|
2645 |
helper.EnableAscii (stream, "client/eth0"); |
|
2646 |
helper.EnableAscii (stream, "server/eth0"); |
|
2647 |
||
2648 |
This would result in a single trace file called "trace-file-name.tr" that |
|
2649 |
contains all of the trace events for both devices. The events would be |
|
2650 |
disambiguated by trace context strings. |
|
2651 |
||
2652 |
You can enable ascii tracing on a collection of node/net-device pairs by |
|
2653 |
providing a ``NetDeviceContainer``. For each ``NetDevice`` in the container |
|
2654 |
the type is checked. For each device of the proper type (the same type as is |
|
2655 |
managed by the device helper), tracing is enabled. Again, the ``<Node>`` is |
|
2656 |
implicit since the found net device must belong to exactly one ``Node``. |
|
2657 |
For example, |
|
2658 |
||
2659 |
:: |
|
2660 |
||
2661 |
NetDeviceContainer d = ...; |
|
2662 |
... |
|
2663 |
helper.EnableAscii ("prefix", d); |
|
2664 |
||
2665 |
This would result in a number of ascii trace files being created, each of which |
|
2666 |
follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the |
|
2667 |
traces into a single file is accomplished similarly to the examples above: |
|
2668 |
||
2669 |
:: |
|
2670 |
||
2671 |
NetDeviceContainer d = ...; |
|
2672 |
... |
|
2673 |
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); |
|
2674 |
... |
|
2675 |
helper.EnableAscii (stream, d); |
|
2676 |
||
2677 |
You can enable ascii tracing on a collection of node/net-device pairs by |
|
2678 |
providing a ``NodeContainer``. For each ``Node`` in the ``NodeContainer`` |
|
2679 |
its attached ``NetDevices`` are iterated. For each ``NetDevice`` attached |
|
2680 |
to each node in the container, the type of that device is checked. For each |
|
2681 |
device of the proper type (the same type as is managed by the device helper), |
|
2682 |
tracing is enabled. |
|
2683 |
||
2684 |
:: |
|
2685 |
||
2686 |
NodeContainer n; |
|
2687 |
... |
|
2688 |
helper.EnableAscii ("prefix", n); |
|
2689 |
||
2690 |
This would result in a number of ascii trace files being created, each of which |
|
2691 |
follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the |
|
2692 |
traces into a single file is accomplished similarly to the examples above: |
|
2693 |
||
2694 |
You can enable pcap tracing on the basis of node ID and device ID as well as |
|
2695 |
with explicit ``Ptr``. Each ``Node`` in the system has an integer node ID |
|
2696 |
and each device connected to a node has an integer device ID. |
|
2697 |
||
2698 |
:: |
|
2699 |
||
2700 |
helper.EnableAscii ("prefix", 21, 1); |
|
2701 |
||
2702 |
Of course, the traces can be combined into a single file as shown above. |
|
2703 |
||
2704 |
Finally, you can enable pcap tracing for all devices in the system, with the |
|
2705 |
same type as that managed by the device helper. |
|
2706 |
||
2707 |
:: |
|
2708 |
||
2709 |
helper.EnableAsciiAll ("prefix"); |
|
2710 |
||
2711 |
This would result in a number of ascii trace files being created, one for |
|
2712 |
every device in the system of the type managed by the helper. All of these |
|
2713 |
files will follow the <prefix>-<node id>-<device id>.tr convention. Combining |
|
2714 |
all of the traces into a single file is accomplished similarly to the examples |
|
2715 |
above. |
|
2716 |
||
2717 |
Ascii Tracing Device Helper Filename Selection |
|
2718 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
2719 |
||
2720 |
Implicit in the prefix-style method descriptions above is the construction of the |
|
2721 |
complete filenames by the implementation method. By convention, ascii traces |
|
2722 |
in the |ns3| system are of the form "<prefix>-<node id>-<device id>.tr" |
|
2723 |
||
2724 |
As previously mentioned, every node in the system will have a system-assigned |
|
2725 |
node id; and every device will have an interface index (also called a device id) |
|
2726 |
relative to its node. By default, then, an ascii trace file created as a result |
|
2727 |
of enabling tracing on the first device of node 21, using the prefix "prefix", |
|
2728 |
would be "prefix-21-1.tr". |
|
2729 |
||
2730 |
You can always use the |ns3| object name service to make this more clear. |
|
2731 |
For example, if you use the object name service to assign the name "server" |
|
2732 |
to node 21, the resulting ascii trace file name will automatically become, |
|
2733 |
"prefix-server-1.tr" and if you also assign the name "eth0" to the |
|
2734 |
device, your ascii trace file name will automatically pick this up and be called |
|
2735 |
"prefix-server-eth0.tr". |
|
2736 |
||
2737 |
Several of the methods have a default parameter called ``explicitFilename``. |
|
2738 |
When set to true, this parameter disables the automatic filename completion |
|
2739 |
mechanism and allows you to create an explicit filename. This option is only |
|
2740 |
available in the methods which take a prefix and enable tracing on a single device. |
|
2741 |
||
2742 |
Pcap Tracing Protocol Helpers |
|
2743 |
+++++++++++++++++++++++++++++ |
|
2744 |
||
2745 |
The goal of these ``mixins`` is to make it easy to add a consistent pcap trace |
|
2746 |
facility to protocols. We want all of the various flavors of pcap tracing to |
|
2747 |
work the same across all protocols, so the methods of these helpers are |
|
7147
71adbc0422a5
Fix more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7145
diff
changeset
|
2748 |
inherited by stack helpers. Take a look at ``src/network/helper/trace-helper.h`` |
6754 | 2749 |
if you want to follow the discussion while looking at real code. |
2750 |
||
2751 |
In this section we will be illustrating the methods as applied to the protocol |
|
2752 |
``Ipv4``. To specify traces in similar protocols, just substitute the |
|
2753 |
appropriate type. For example, use a ``Ptr<Ipv6>`` instead of a |
|
2754 |
``Ptr<Ipv4>`` and call ``EnablePcapIpv6`` instead of ``EnablePcapIpv4``. |
|
2755 |
||
2756 |
The class ``PcapHelperForIpv4`` provides the high level functionality for |
|
2757 |
using pcap tracing in the ``Ipv4`` protocol. Each protocol helper enabling these |
|
2758 |
methods must implement a single virtual method inherited from this class. There |
|
2759 |
will be a separate implementation for ``Ipv6``, for example, but the only |
|
2760 |
difference will be in the method names and signatures. Different method names |
|
2761 |
are required to disambiguate class ``Ipv4`` from ``Ipv6`` which are both |
|
2762 |
derived from class ``Object``, and methods that share the same signature. |
|
2763 |
||
2764 |
:: |
|
2765 |
||
2766 |
virtual void EnablePcapIpv4Internal (std::string prefix, |
|
2767 |
Ptr<Ipv4> ipv4, |
|
2768 |
uint32_t interface, |
|
2769 |
bool explicitFilename) = 0; |
|
2770 |
||
2771 |
The signature of this method reflects the protocol and interface-centric view |
|
2772 |
of the situation at this level. All of the public methods inherited from class |
|
2773 |
``PcapHelperForIpv4`` reduce to calling this single device-dependent |
|
2774 |
implementation method. For example, the lowest level pcap method, |
|
2775 |
||
2776 |
:: |
|
2777 |
||
2778 |
void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false); |
|
2779 |
||
2780 |
||
2781 |
will call the device implementation of ``EnablePcapIpv4Internal`` directly. |
|
2782 |
All other public pcap tracing methods build on this implementation to provide |
|
2783 |
additional user-level functionality. What this means to the user is that all |
|
2784 |
protocol helpers in the system will have all of the pcap trace methods |
|
2785 |
available; and these methods will all work in the same way across |
|
2786 |
protocols if the helper implements ``EnablePcapIpv4Internal`` correctly. |
|
2787 |
||
2788 |
Pcap Tracing Protocol Helper Methods |
|
2789 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
2790 |
||
2791 |
These methods are designed to be in one-to-one correspondence with the ``Node``- |
|
2792 |
and ``NetDevice``- centric versions of the device versions. Instead of |
|
2793 |
``Node`` and ``NetDevice`` pair constraints, we use protocol and interface |
|
2794 |
constraints. |
|
2795 |
||
2796 |
Note that just like in the device version, there are six methods: |
|
2797 |
||
2798 |
:: |
|
2799 |
||
2800 |
void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false); |
|
2801 |
void EnablePcapIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false); |
|
2802 |
void EnablePcapIpv4 (std::string prefix, Ipv4InterfaceContainer c); |
|
2803 |
void EnablePcapIpv4 (std::string prefix, NodeContainer n); |
|
2804 |
void EnablePcapIpv4 (std::string prefix, uint32_t nodeid, uint32_t interface, bool explicitFilename); |
|
2805 |
void EnablePcapIpv4All (std::string prefix); |
|
2806 |
||
2807 |
You are encouraged to peruse the Doxygen for class ``PcapHelperForIpv4`` |
|
2808 |
to find the details of these methods; but to summarize ... |
|
2809 |
||
2810 |
You can enable pcap tracing on a particular protocol/interface pair by providing a |
|
2811 |
``Ptr<Ipv4>`` and ``interface`` to an ``EnablePcap`` method. For example, |
|
2812 |
||
2813 |
:: |
|
2814 |
||
2815 |
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> (); |
|
2816 |
... |
|
2817 |
helper.EnablePcapIpv4 ("prefix", ipv4, 0); |
|
2818 |
||
2819 |
You can enable pcap tracing on a particular node/net-device pair by providing a |
|
2820 |
``std::string`` representing an object name service string to an |
|
2821 |
``EnablePcap`` method. The ``Ptr<Ipv4>`` is looked up from the name |
|
2822 |
string. For example, |
|
2823 |
||
2824 |
:: |
|
2825 |
||
2826 |
Names::Add ("serverIPv4" ...); |
|
2827 |
... |
|
2828 |
helper.EnablePcapIpv4 ("prefix", "serverIpv4", 1); |
|
2829 |
||
2830 |
You can enable pcap tracing on a collection of protocol/interface pairs by |
|
2831 |
providing an ``Ipv4InterfaceContainer``. For each ``Ipv4`` / interface |
|
2832 |
pair in the container the protocol type is checked. For each protocol of the |
|
2833 |
proper type (the same type as is managed by the device helper), tracing is |
|
2834 |
enabled for the corresponding interface. For example, |
|
2835 |
||
2836 |
:: |
|
2837 |
||
2838 |
NodeContainer nodes; |
|
2839 |
... |
|
2840 |
NetDeviceContainer devices = deviceHelper.Install (nodes); |
|
2841 |
... |
|
2842 |
Ipv4AddressHelper ipv4; |
|
2843 |
ipv4.SetBase ("10.1.1.0", "255.255.255.0"); |
|
2844 |
Ipv4InterfaceContainer interfaces = ipv4.Assign (devices); |
|
2845 |
... |
|
2846 |
helper.EnablePcapIpv4 ("prefix", interfaces); |
|
2847 |
||
2848 |
You can enable pcap tracing on a collection of protocol/interface pairs by |
|
2849 |
providing a ``NodeContainer``. For each ``Node`` in the ``NodeContainer`` |
|
2850 |
the appropriate protocol is found. For each protocol, its interfaces are |
|
2851 |
enumerated and tracing is enabled on the resulting pairs. For example, |
|
2852 |
||
2853 |
:: |
|
2854 |
||
2855 |
NodeContainer n; |
|
2856 |
... |
|
2857 |
helper.EnablePcapIpv4 ("prefix", n); |
|
2858 |
||
2859 |
You can enable pcap tracing on the basis of node ID and interface as well. In |
|
2860 |
this case, the node-id is translated to a ``Ptr<Node>`` and the appropriate |
|
2861 |
protocol is looked up in the node. The resulting protocol and interface are |
|
2862 |
used to specify the resulting trace source. |
|
2863 |
||
2864 |
:: |
|
2865 |
||
2866 |
helper.EnablePcapIpv4 ("prefix", 21, 1); |
|
2867 |
||
2868 |
Finally, you can enable pcap tracing for all interfaces in the system, with |
|
2869 |
associated protocol being the same type as that managed by the device helper. |
|
2870 |
||
2871 |
:: |
|
2872 |
||
2873 |
helper.EnablePcapIpv4All ("prefix"); |
|
2874 |
||
2875 |
Pcap Tracing Protocol Helper Filename Selection |
|
2876 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
2877 |
||
2878 |
Implicit in all of the method descriptions above is the construction of the |
|
2879 |
complete filenames by the implementation method. By convention, pcap traces |
|
2880 |
taken for devices in the |ns3| system are of the form |
|
2881 |
"<prefix>-<node id>-<device id>.pcap". In the case of protocol traces, |
|
2882 |
there is a one-to-one correspondence between protocols and ``Nodes``. |
|
2883 |
This is because protocol ``Objects`` are aggregated to ``Node Objects``. |
|
2884 |
Since there is no global protocol id in the system, we use the corresponding |
|
2885 |
node id in file naming. Therefore there is a possibility for file name |
|
2886 |
collisions in automatically chosen trace file names. For this reason, the |
|
2887 |
file name convention is changed for protocol traces. |
|
2888 |
||
2889 |
As previously mentioned, every node in the system will have a system-assigned |
|
2890 |
node id. Since there is a one-to-one correspondence between protocol instances |
|
2891 |
and node instances we use the node id. Each interface has an interface id |
|
2892 |
relative to its protocol. We use the convention |
|
2893 |
"<prefix>-n<node id>-i<interface id>.pcap" for trace file naming in protocol |
|
2894 |
helpers. |
|
2895 |
||
2896 |
Therefore, by default, a pcap trace file created as a result of enabling tracing |
|
2897 |
on interface 1 of the Ipv4 protocol of node 21 using the prefix "prefix" |
|
2898 |
would be "prefix-n21-i1.pcap". |
|
2899 |
||
2900 |
You can always use the |ns3| object name service to make this more clear. |
|
2901 |
For example, if you use the object name service to assign the name "serverIpv4" |
|
2902 |
to the Ptr<Ipv4> on node 21, the resulting pcap trace file name will |
|
2903 |
automatically become, "prefix-nserverIpv4-i1.pcap". |
|
2904 |
||
2905 |
Several of the methods have a default parameter called ``explicitFilename``. |
|
2906 |
When set to true, this parameter disables the automatic filename completion |
|
2907 |
mechanism and allows you to create an explicit filename. This option is only |
|
2908 |
available in the methods which take a prefix and enable tracing on a single device. |
|
2909 |
||
2910 |
Ascii Tracing Protocol Helpers |
|
2911 |
++++++++++++++++++++++++++++++ |
|
2912 |
||
2913 |
The behavior of the ascii trace helpers is substantially similar to the pcap |
|
7147
71adbc0422a5
Fix more paths in the documentation
Mitch Watrous <watrous@u.washington.edu>
parents:
7145
diff
changeset
|
2914 |
case. Take a look at ``src/network/helper/trace-helper.h`` if you want to |
6754 | 2915 |
follow the discussion while looking at real code. |
2916 |
||
2917 |
In this section we will be illustrating the methods as applied to the protocol |
|
2918 |
``Ipv4``. To specify traces in similar protocols, just substitute the |
|
2919 |
appropriate type. For example, use a ``Ptr<Ipv6>`` instead of a |
|
2920 |
``Ptr<Ipv4>`` and call ``EnableAsciiIpv6`` instead of ``EnableAsciiIpv4``. |
|
2921 |
||
2922 |
The class ``AsciiTraceHelperForIpv4`` adds the high level functionality |
|
2923 |
for using ascii tracing to a protocol helper. Each protocol that enables these |
|
2924 |
methods must implement a single virtual method inherited from this class. |
|
2925 |
||
2926 |
:: |
|
2927 |
||
2928 |
virtual void EnableAsciiIpv4Internal (Ptr<OutputStreamWrapper> stream, |
|
2929 |
std::string prefix, |
|
2930 |
Ptr<Ipv4> ipv4, |
|
2931 |
uint32_t interface, |
|
2932 |
bool explicitFilename) = 0; |
|
2933 |
||
2934 |
The signature of this method reflects the protocol- and interface-centric view |
|
2935 |
of the situation at this level; and also the fact that the helper may be writing |
|
2936 |
to a shared output stream. All of the public methods inherited from class |
|
2937 |
``PcapAndAsciiTraceHelperForIpv4`` reduce to calling this single device- |
|
2938 |
dependent implementation method. For example, the lowest level ascii trace |
|
2939 |
methods, |
|
2940 |
||
2941 |
:: |
|
2942 |
||
2943 |
void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false); |
|
2944 |
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface); |
|
2945 |
||
2946 |
||
2947 |
will call the device implementation of ``EnableAsciiIpv4Internal`` directly, |
|
2948 |
providing either the prefix or the stream. All other public ascii tracing |
|
2949 |
methods will build on these low-level functions to provide additional user-level |
|
2950 |
functionality. What this means to the user is that all device helpers in the |
|
2951 |
system will have all of the ascii trace methods available; and these methods |
|
2952 |
will all work in the same way across protocols if the protocols implement |
|
2953 |
``EnablAsciiIpv4Internal`` correctly. |
|
2954 |
||
2955 |
Ascii Tracing Protocol Helper Methods |
|
2956 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
2957 |
||
2958 |
:: |
|
2959 |
||
2960 |
void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false); |
|
2961 |
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface); |
|
2962 |
||
2963 |
void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false); |
|
2964 |
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, std::string ipv4Name, uint32_t interface); |
|
2965 |
||
2966 |
void EnableAsciiIpv4 (std::string prefix, Ipv4InterfaceContainer c); |
|
2967 |
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ipv4InterfaceContainer c); |
|
2968 |
||
2969 |
void EnableAsciiIpv4 (std::string prefix, NodeContainer n); |
|
2970 |
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, NodeContainer n); |
|
2971 |
||
2972 |
void EnableAsciiIpv4All (std::string prefix); |
|
2973 |
void EnableAsciiIpv4All (Ptr<OutputStreamWrapper> stream); |
|
2974 |
||
2975 |
void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename); |
|
2976 |
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t interface); |
|
2977 |
||
2978 |
You are encouraged to peruse the Doxygen for class ``PcapAndAsciiHelperForIpv4`` |
|
2979 |
to find the details of these methods; but to summarize ... |
|
2980 |
||
2981 |
There are twice as many methods available for ascii tracing as there were for |
|
2982 |
pcap tracing. This is because, in addition to the pcap-style model where traces |
|
2983 |
from each unique protocol/interface pair are written to a unique file, we |
|
2984 |
support a model in which trace information for many protocol/interface pairs is |
|
2985 |
written to a common file. This means that the <prefix>-n<node id>-<interface> |
|
2986 |
file name generation mechanism is replaced by a mechanism to refer to a common |
|
2987 |
file; and the number of API methods is doubled to allow all combinations. |
|
2988 |
||
2989 |
Just as in pcap tracing, you can enable ascii tracing on a particular |
|
2990 |
protocol/interface pair by providing a ``Ptr<Ipv4>`` and an ``interface`` |
|
2991 |
to an ``EnableAscii`` method. |
|
2992 |
For example, |
|
2993 |
||
2994 |
:: |
|
2995 |
||
2996 |
Ptr<Ipv4> ipv4; |
|
2997 |
... |
|
2998 |
helper.EnableAsciiIpv4 ("prefix", ipv4, 1); |
|
2999 |
||
3000 |
In this case, no trace contexts are written to the ascii trace file since they |
|
3001 |
would be redundant. The system will pick the file name to be created using |
|
3002 |
the same rules as described in the pcap section, except that the file will |
|
3003 |
have the suffix ".tr" instead of ".pcap". |
|
3004 |
||
3005 |
If you want to enable ascii tracing on more than one interface and have all |
|
3006 |
traces sent to a single file, you can do that as well by using an object to |
|
3007 |
refer to a single file. We have already something similar to this in the |
|
3008 |
"cwnd" example above: |
|
3009 |
||
3010 |
:: |
|
3011 |
||
3012 |
Ptr<Ipv4> protocol1 = node1->GetObject<Ipv4> (); |
|
3013 |
Ptr<Ipv4> protocol2 = node2->GetObject<Ipv4> (); |
|
3014 |
... |
|
3015 |
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); |
|
3016 |
... |
|
3017 |
helper.EnableAsciiIpv4 (stream, protocol1, 1); |
|
3018 |
helper.EnableAsciiIpv4 (stream, protocol2, 1); |
|
3019 |
||
3020 |
In this case, trace contexts are written to the ascii trace file since they |
|
3021 |
are required to disambiguate traces from the two interfaces. Note that since |
|
3022 |
the user is completely specifying the file name, the string should include the |
|
3023 |
",tr" for consistency. |
|
3024 |
||
3025 |
You can enable ascii tracing on a particular protocol by providing a |
|
3026 |
``std::string`` representing an object name service string to an |
|
3027 |
``EnablePcap`` method. The ``Ptr<Ipv4>`` is looked up from the name |
|
3028 |
string. The ``<Node>`` in the resulting filenames is implicit since there |
|
3029 |
is a one-to-one correspondence between protocol instances and nodes, |
|
3030 |
For example, |
|
3031 |
||
3032 |
:: |
|
3033 |
||
3034 |
Names::Add ("node1Ipv4" ...); |
|
3035 |
Names::Add ("node2Ipv4" ...); |
|
3036 |
... |
|
3037 |
helper.EnableAsciiIpv4 ("prefix", "node1Ipv4", 1); |
|
3038 |
helper.EnableAsciiIpv4 ("prefix", "node2Ipv4", 1); |
|
3039 |
||
3040 |
This would result in two files named "prefix-nnode1Ipv4-i1.tr" and |
|
3041 |
"prefix-nnode2Ipv4-i1.tr" with traces for each interface in the respective |
|
3042 |
trace file. Since all of the EnableAscii functions are overloaded to take a |
|
3043 |
stream wrapper, you can use that form as well: |
|
3044 |
||
3045 |
:: |
|
3046 |
||
3047 |
Names::Add ("node1Ipv4" ...); |
|
3048 |
Names::Add ("node2Ipv4" ...); |
|
3049 |
... |
|
3050 |
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); |
|
3051 |
... |
|
3052 |
helper.EnableAsciiIpv4 (stream, "node1Ipv4", 1); |
|
3053 |
helper.EnableAsciiIpv4 (stream, "node2Ipv4", 1); |
|
3054 |
||
3055 |
This would result in a single trace file called "trace-file-name.tr" that |
|
3056 |
contains all of the trace events for both interfaces. The events would be |
|
3057 |
disambiguated by trace context strings. |
|
3058 |
||
3059 |
You can enable ascii tracing on a collection of protocol/interface pairs by |
|
3060 |
providing an ``Ipv4InterfaceContainer``. For each protocol of the proper |
|
3061 |
type (the same type as is managed by the device helper), tracing is enabled |
|
3062 |
for the corresponding interface. Again, the ``<Node>`` is implicit since |
|
3063 |
there is a one-to-one correspondence between each protocol and its node. |
|
3064 |
For example, |
|
3065 |
||
3066 |
:: |
|
3067 |
||
3068 |
NodeContainer nodes; |
|
3069 |
... |
|
3070 |
NetDeviceContainer devices = deviceHelper.Install (nodes); |
|
3071 |
... |
|
3072 |
Ipv4AddressHelper ipv4; |
|
3073 |
ipv4.SetBase ("10.1.1.0", "255.255.255.0"); |
|
3074 |
Ipv4InterfaceContainer interfaces = ipv4.Assign (devices); |
|
3075 |
... |
|
3076 |
... |
|
3077 |
helper.EnableAsciiIpv4 ("prefix", interfaces); |
|
3078 |
||
3079 |
This would result in a number of ascii trace files being created, each of which |
|
3080 |
follows the <prefix>-n<node id>-i<interface>.tr convention. Combining all of the |
|
3081 |
traces into a single file is accomplished similarly to the examples above: |
|
3082 |
||
3083 |
:: |
|
3084 |
||
3085 |
NodeContainer nodes; |
|
3086 |
... |
|
3087 |
NetDeviceContainer devices = deviceHelper.Install (nodes); |
|
3088 |
... |
|
3089 |
Ipv4AddressHelper ipv4; |
|
3090 |
ipv4.SetBase ("10.1.1.0", "255.255.255.0"); |
|
3091 |
Ipv4InterfaceContainer interfaces = ipv4.Assign (devices); |
|
3092 |
... |
|
3093 |
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr"); |
|
3094 |
... |
|
3095 |
helper.EnableAsciiIpv4 (stream, interfaces); |
|
3096 |
||
3097 |
You can enable ascii tracing on a collection of protocol/interface pairs by |
|
3098 |
providing a ``NodeContainer``. For each ``Node`` in the ``NodeContainer`` |
|
3099 |
the appropriate protocol is found. For each protocol, its interfaces are |
|
3100 |
enumerated and tracing is enabled on the resulting pairs. For example, |
|
3101 |
||
3102 |
:: |
|
3103 |
||
3104 |
NodeContainer n; |
|
3105 |
... |
|
3106 |
helper.EnableAsciiIpv4 ("prefix", n); |
|
3107 |
||
3108 |
This would result in a number of ascii trace files being created, each of which |
|
3109 |
follows the <prefix>-<node id>-<device id>.tr convention. Combining all of the |
|
3110 |
traces into a single file is accomplished similarly to the examples above: |
|
3111 |
||
3112 |
You can enable pcap tracing on the basis of node ID and device ID as well. In |
|
3113 |
this case, the node-id is translated to a ``Ptr<Node>`` and the appropriate |
|
3114 |
protocol is looked up in the node. The resulting protocol and interface are |
|
3115 |
used to specify the resulting trace source. |
|
3116 |
||
3117 |
:: |
|
3118 |
||
3119 |
helper.EnableAsciiIpv4 ("prefix", 21, 1); |
|
3120 |
||
3121 |
Of course, the traces can be combined into a single file as shown above. |
|
3122 |
||
3123 |
Finally, you can enable ascii tracing for all interfaces in the system, with |
|
3124 |
associated protocol being the same type as that managed by the device helper. |
|
3125 |
||
3126 |
:: |
|
3127 |
||
3128 |
helper.EnableAsciiIpv4All ("prefix"); |
|
3129 |
||
3130 |
This would result in a number of ascii trace files being created, one for |
|
3131 |
every interface in the system related to a protocol of the type managed by the |
|
3132 |
helper. All of these files will follow the <prefix>-n<node id>-i<interface.tr |
|
3133 |
convention. Combining all of the traces into a single file is accomplished |
|
3134 |
similarly to the examples above. |
|
3135 |
||
3136 |
Ascii Tracing Protocol Helper Filename Selection |
|
3137 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
3138 |
||
3139 |
Implicit in the prefix-style method descriptions above is the construction of the |
|
3140 |
complete filenames by the implementation method. By convention, ascii traces |
|
3141 |
in the |ns3| system are of the form "<prefix>-<node id>-<device id>.tr" |
|
3142 |
||
3143 |
As previously mentioned, every node in the system will have a system-assigned |
|
3144 |
node id. Since there is a one-to-one correspondence between protocols and nodes |
|
3145 |
we use to node-id to identify the protocol identity. Every interface on a |
|
3146 |
given protocol will have an interface index (also called simply an interface) |
|
3147 |
relative to its protocol. By default, then, an ascii trace file created as a result |
|
3148 |
of enabling tracing on the first device of node 21, using the prefix "prefix", |
|
3149 |
would be "prefix-n21-i1.tr". Use the prefix to disambiguate multiple protocols |
|
3150 |
per node. |
|
3151 |
||
3152 |
You can always use the |ns3| object name service to make this more clear. |
|
3153 |
For example, if you use the object name service to assign the name "serverIpv4" |
|
3154 |
to the protocol on node 21, and also specify interface one, the resulting ascii |
|
3155 |
trace file name will automatically become, "prefix-nserverIpv4-1.tr". |
|
3156 |
||
3157 |
Several of the methods have a default parameter called ``explicitFilename``. |
|
3158 |
When set to true, this parameter disables the automatic filename completion |
|
3159 |
mechanism and allows you to create an explicit filename. This option is only |
|
3160 |
available in the methods which take a prefix and enable tracing on a single device. |
|
3161 |
||
3162 |
Summary |
|
3163 |
******* |
|
3164 |
||
3165 |
|ns3| includes an extremely rich environment allowing users at several |
|
3166 |
levels to customize the kinds of information that can be extracted from |
|
3167 |
simulations. |
|
3168 |
||
3169 |
There are high-level helper functions that allow users to simply control the |
|
3170 |
collection of pre-defined outputs to a fine granularity. There are mid-level |
|
3171 |
helper functions to allow more sophisticated users to customize how information |
|
3172 |
is extracted and saved; and there are low-level core functions to allow expert |
|
3173 |
users to alter the system to present new and previously unexported information |
|
3174 |
in a way that will be immediately accessible to users at higher levels. |
|
3175 |
||
3176 |
This is a very comprehensive system, and we realize that it is a lot to |
|
3177 |
digest, especially for new users or those not intimately familiar with C++ |
|
3178 |
and its idioms. We do consider the tracing system a very important part of |
|
3179 |
|ns3| and so recommend becoming as familiar as possible with it. It is |
|
3180 |
probably the case that understanding the rest of the |ns3| system will |
|
3181 |
be quite simple once you have mastered the tracing system |