From eeaea6f03b578666775d64d00fe660085cb38524 Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Fri, 21 Mar 2008 12:14:07 -0700 Subject: [PATCH] tutorial updates for attributes --- doc/tutorial/attributes.texi | 535 +++++++++++++++++++++++++++++++++ doc/tutorial/introduction.texi | 2 + doc/tutorial/log.texi | 24 ++ doc/tutorial/other.texi | 2 +- doc/tutorial/output.texi | 3 - doc/tutorial/tutorial.texi | 17 +- 6 files changed, 575 insertions(+), 8 deletions(-) create mode 100644 doc/tutorial/attributes.texi create mode 100644 doc/tutorial/log.texi diff --git a/doc/tutorial/attributes.texi b/doc/tutorial/attributes.texi new file mode 100644 index 000000000..192d6a8fe --- /dev/null +++ b/doc/tutorial/attributes.texi @@ -0,0 +1,535 @@ +@node ns-3 Attributes +@chapter ns-3 Attributes +@anchor{chap:Attributes} + +In ns-3 simulations, there are two main aspects to configuration: +@itemize @bullet +@item the simulation topology and how objects are connected +@item the values used by the models instantiated in the topology +@end itemize + +This chapter focuses on the second item above: how the many values +in use in ns-3 are organized, documented, and modifiable by ns-3 users. +The ns-3 attribute system is also the underpinning of how traces +and statistics are gathered in the simulator. + +Before delving into details of the attribute value system, +it will help to review some basic properties of @code{class ns3::Object}. + +@node Object Overview +@section Object Overview + +ns-3 is fundamentally a C++ object-based system. By this we mean that +new C++ classes (types) can be declared, defined, and subclassed +as usual. + +Many ns-3 objects inherit from the @code{ns3::Object} base class. These +objects have some additional properties that we exploit for +organizing the system and improving the memory management +of our objects: + +@itemize @bullet +@item a "metadata" system that links the class name to a lot of +meta-information about the object, including the base class of the subclass, +the set of accessible constructors in the subclass, and the set of +"attributes" of the subclass +@item a reference counting smart pointer implementation, for memory +management. +@end itemize + +ns-3 objects that use the attribute system derive from either +@code{ns3::Object} or @code{ns3::ObjectBase}. Most ns-3 objects +we will discuss derive from @code{ns3::Object}, but a few that +are outside the smart pointer memory management framework derive +from @code{ns3::ObjectBase}. + +Let's review a couple of properties of these objects. + +@node Smart pointers +@subsection Smart pointers + +As introduced above in @ref{Smart Pointers 101}, ns-3 objects +are memory managed by a +@uref{http://en.wikipedia.org/wiki/Smart_pointer,,reference counting smart pointer implementation}, @code{class ns3::Ptr}. + +Smart pointers are used extensively in the ns-3 APIs, to avoid passing +references to heap-allocated objects that may cause memory leaks. +For most basic usage (syntax), treat a smart pointer like a regular pointer: +@verbatim + Ptr nd = ...; + nd->CallSomeFunction (); + // etc. +@end verbatim + +@node CreateObject +@subsection CreateObject + +As we discussed above in @ref{Object Creation}, +at the lowest-level API, objects of type @code{ns3::Object} are +not instantiated using @code{operator new} as usual but instead by +a templated function called @code{CreateObject()}. + +A typical way to create such an object is as follows: +@verbatim + Ptr nd = CreateObject (); +@end verbatim + +You can think of this as being functionally equivalent to: +@verbatim + WifiNetDevice* nd = new WifiNetDevice (); +@end verbatim + +Objects that derive from @code{ns3::Object} must be allocated +on the heap using CreateObject(). Those deriving from +@code{ns3::ObjectBase}, such as ns-3 helper functions and packet +headers and trailers, can be allocated on the stack. + +In some scripts, you may not see a lot of CreateObject() calls +in the code; +this is because there are some helper objects in effect that +are doing the CreateObject()s for you. + +@node TypeId +@subsection TypeId + +ns-3 classes that derive from class ns3::Object can include +a metadata class called @code{TypeId} that records meta-information +about the class, for use in the object aggregation and component +manager systems: +@itemize @bullet + @item a unique string identifying the class + @item the base class of the subclass, within the metadata system + @item the set of accessible constructors in the subclass +@end itemize + +@node Object Summary +@subsection Object Summary + +Putting all of these concepts together, let's look at a specific +example: @code{class ns3::Node}. + +The public header file node.h has a declaration that includes +a static GetTypeId function call: +@verbatim +class Node : public Object +{ +public: + static TypeId GetTypeId (void); + ... +@end verbatim + +This is defined in the node.cc file as follows: +@verbatim +TypeId +Node::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::Node") + .SetParent () + return tid; +} +@end verbatim +Finally, when users want to create Nodes, they call: +@verbatim + Ptr n = CreateObject n; +@end verbatim + +We next discuss how attributes (values associated with member variables +or functions of the class) are plumbed into the above TypeId. + +@node Attribute Overview +@section Attribute Overview + +The goal of the attribute system is to organize the access of +internal member objects of a simulation. This goal arises because, +typically in simulation, users will cut and paste/modify existing +simulation scripts, or will use higher-level simulation constructs, +but often will be interested in studying or tracing particular +internal variables. For instance, use cases such as: +@itemize @bullet +@item "I want to trace the packets on the wireless interface only on +the first access point" +@item "I want to trace the value of the TCP congestion window (every +time it changes) on a particular TCP socket" +@item "I want a dump of all values that were used in my simulation." +@end itemize + +Similarly, users may want fine-grained access to internal +variables in the simulation, or may want to broadly change the +initial value used for a particular parameter in all subsequently +created objects. Finally, users may wish to know what variables +are settable and retrievable in a simulation configuration. This +is not just for direct simulation interaction on the command line; +consider also a (future) graphical user interface +that would like to be able to provide a feature whereby a user +might right-click on an node on the canvas and see a hierarchical, +organized list of parameters that are settable on the node and its +constituent member objects, and help text and default values for +each parameter. + +@node Functional overview +@subsection Functional overview + +We provide a way for users to access values deep in the system, without +having to plumb accessors (pointers) through the system and walk +pointer chains to get to them. Consider a class DropTailQueue that +has a member variable that is an unsigned integer @code{m_maxPackets}; +this member variable controls the depth of the queue. + +If we look at the declaration of DropTailQueue, we see the following: +@verbatim +class DropTailQueue : public Queue { +public: + static TypeId GetTypeId (void); + ... + +private: + std::queue > m_packets; + uint32_t m_maxPackets; +}; +@end verbatim + +Let's consider things that a user may want to do with the value of +m_maxPackets: + +@itemize @bullet +@item Set a default value for the system, such that whenever a new +DropTailQueue is created, this member is initialized to that default. +@item Set or get the value on an already instantiated queue. +@end itemize + +The above things typically require providing Set() and Get() functions, +and some type of global default value. + +In the ns-3 attribute system, these value definitions and accessor +functions are moved into the TypeId class; e.g.: +@verbatim +TypeId DropTailQueue::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::DropTailQueue") + .SetParent () + .AddConstructor () + .AddAttribute ("MaxPackets", "The maximum number of packets accepted by this DropTailQueue.", + Uinteger (100), + MakeUintegerAccessor (&DropTailQueue::m_maxPackets), + MakeUintegerChecker ()) + ; + + return tid; +} +@end verbatim + +The AddAttribute() method is performing a number of things with this +value: +@itemize @bullet +@item Binding the variable m_maxPackets to a string "MaxPackets" +@item Providing a default value (100 packets) +@item Providing some help text defining the value +@item Providing a "checker" (not used in this example) that can be used to set +bounds on the allowable range of values +@end itemize + +The key point is that now the value of this variable and its default +value are accessible in the attribute namespace, which is based on +strings such as "MaxPackets" and TypeId strings. In the next +section, we will provide an example script that shows how users +may manipulate these values. + +@node Basic usage +@subsection Basic usage + +Let's look at how a user script might access these values. +This is based on the script found at @code{samples/main-attribute-value.cc}, +with some details stripped out. +@verbatim +// +// This is a basic example of how to use the attribute system to +// set and get a value in the underlying system; namely, an unsigned +// integer of the maximum number of packets in a queue +// + +int +main (int argc, char *argv[]) +{ + + // By default, the MaxPackets attribute has a value of 100 packets + // (this default can be observed in the function DropTailQueue::GetTypeId) + // + // Here, we set it to 80 packets. We could use one of two value types: + // a string-based value or a Uinteger value + Config::SetDefault ("ns3::DropTailQueue::MaxPackets", String ("80")); + // The below function call is redundant + Config::SetDefault ("ns3::DropTailQueue::MaxPackets", Uinteger(80)); + + // Allow the user to override any of the defaults and the above + // SetDefaults() at run-time, via command-line arguments + CommandLine cmd; + cmd.Parse (argc, argv); +@end verbatim + +The main thing to notice in the above are the two calls to +@code{Config::SetDefault}. This is how we set the default value +for all subsequently instantiated DropTailQueues. We illustrate +that two types of Value classes, a String and a Uinteger class, +can be used to assign the value to the attribute named by +"ns3::DropTailQueue::MaxPackets". + +Now, we will create a few objects using the low-level API; here, +our newly created queues will not have a m_maxPackets initialized to +100 packets but to 80 packets, because of what we did above with +default values. +@verbatim + Ptr n0 = CreateObject (); + + Ptr net0 = CreateObject (); + n0->AddDevice (net0); + + Ptr q = CreateObject (); + net0->AddQueue(q); +@end verbatim + +At this point, we have created a single node (Node 0) and a +single PointToPointNetDevice (NetDevice 0) and added a +DropTailQueue to it. + +Now, we can manipulate the MaxPackets value of the already +instantiated DropTailQueue. Here are various ways to do that. + +@subsubsection Pointer-based access + +We assume that a smart pointer (Ptr) to a relevant network device is +in hand; here, it is the net0 pointer. + +One way to change the value is to access a pointer to the +underlying queue and modify its attribute. + +First, we observe that we can get a pointer to the (base class) +queue via the PointToPointNetDevice attributes, where it is called +TxQueue +@verbatim + Ptr txQueue = net0->GetAttribute ("TxQueue"); +@end verbatim + +Using the GetObject function, we can perform a safe downcast +to a DropTailQueue, where MaxPackets is a member +@verbatim + Ptr dtq = txQueue->GetObject (); + NS_ASSERT (dtq); +@end verbatim + +Next, we can get the value of an attribute on this queue +We have introduced wrapper "Value" classes for the underlying +data types, similar to Java wrappers around these types, since +the attribute system stores values and not disparate types. +Here, the attribute value is assigned to a Uinteger, and +the Get() method on this value produces the (unwrapped) uint32_t. +@verbatim + Uinteger limit = dtq->GetAttribute ("MaxPackets"); + NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " packets"); +@end verbatim + +Note that the above downcast is not really needed; we could have +done the same using the Ptr even though the attribute +is a member of the subclass +@verbatim + limit = txQueue->GetAttribute ("MaxPackets"); + NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " packets"); +@end verbatim + +Now, let's set it to another value (60 packets) +@verbatim + txQueue->SetAttribute("MaxPackets", Uinteger (60)); + limit = txQueue->GetAttribute ("MaxPackets"); + NS_LOG_INFO ("3. txQueue limit changed: " << limit.Get () << " packets"); +@end verbatim + +@subsubsection Namespace-based access + +An alternative way to get at the attribute is to use the configuration +namespace. Here, this attribute resides on a known path in this +namespace; this approach is useful if one doesn't have access to +the underlying pointers and would like to configure a specific +attribute with a single statement. +@verbatim + Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets", Uinteger (25)); + limit = txQueue->GetAttribute ("MaxPackets"); + NS_LOG_INFO ("4. txQueue limit changed through namespace: " << + limit.Get () << " packets"); +@end verbatim + +We could have also used wildcards to set this value for all nodes +and all net devices (which in this simple example has the same +effect as the previous Set()) +@verbatim + Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets", Uinteger (15)); + limit = txQueue->GetAttribute ("MaxPackets"); + NS_LOG_INFO ("5. txQueue limit changed through wildcarded namespace: " << + limit.Get () << " packets"); +@end verbatim + +@node Setting through constructors and helper classes +@subsection Setting through constructors helper classes + +Arbitrary combinations of attributes can be set and fetched from +the helper and low-level APIs; either from the constructors themselves: +@verbatim +Ptr p = CreateObject ("n1", v1, "n2", v2, ...); +@end verbatim +or from the higher-level helper APIs, such as: +@verbatim + mobility.SetPositionAllocator ("GridPositionAllocator", + "MinX", FpValue (-100.0), + "MinY", FpValue (-100.0), + "DeltaX", FpValue (5.0), + "DeltaY", FpValue (20.0), + "GridWidth", UintValue (20), + "LayoutType", "RowFirst"); +@end verbatim + +@node Value classes +@subsection Value classes +Readers will note the new Value classes. These can be thought of as +an intermediate class that can be used to convert from raw types to the +Values that are used by the system. Recall that this database is holding +objects of many types with a single generic type. Conversions to this +type can either be done using an intermediate class (IntValue, FpValue for +"floating point") or via strings. Direct implicit conversion of types +to Value is not really practical. So in the above, users have a choice +of using strings or values: +@verbatim +p->Set ("cwnd", "100"); // string-based setter +p->Set ("cwnd", IntValue(100)); // value-based setter +@end verbatim + +The system provides some macros that help users declare and define +new Value subclasses for new types that they want to introduce into +the attribute system. + +@node Extending attributes +@section Extending attributes + +The ns-3 system will place a number of internal values under the +attribute system, but undoubtedly users will want to extend this +to pick up ones we have missed, or to add their own classes to this. + +@subsection Adding an existing internal variable to the metadata system + +// XXX revise me + +Consider this variable in class TcpSocket: +@verbatim + uint32_t m_cWnd; // Congestion window +@end verbatim + +Suppose that someone working with Tcp wanted to get or set the +value of that variable using the metadata system. If it were not +already provided by ns-3, the user could declare the following addition +in the metadata system (to the TypeId declaration for TcpSocket): +@verbatim + .AddParameter ("Congestion window", + "Tcp congestion window (bytes)", + MakeUIntParamSpec (&TcpSocket::m_cWnd, 1)); + +@end verbatim + +Now, the user with a pointer to the TcpSocket can perform operations +such as setting and getting the value, without having to add these +functions explicitly. Furthermore, access controls can be applied, such +as allowing the parameter to be read and not written, or bounds +checking on the permissible values can be applied. + +@subsection Adding a new TypeId + +Here, we discuss the impact on a user who wants to add a new class to +ns-3; what additional things must be done to hook it into this system. + +We've already introduced what a TypeId definition looks like: +@verbatim +TypeId +RandomWalk2dMobilityModel::GetTypeId (void) +{ + static TypeId tid = TypeId ("RandomWalkMobilityModel") + .SetParent () + .SetGroupName ("Mobility") + .AddConstructor () + // followed by a number of Parameters + .AddParameter ("bounds", + "Bounds of the area to cruise.", + MakeRectangleParamSpec (&RandomWalk2dMobilityModel::m_bounds, Rectangle (0.0, 0.0, 100.0, 100.0))) + .AddParameter ("time", + "Change current direction and speed after moving for this delay.", + MakeTimeParamSpec (&RandomWalk2dMobilityModel::m_modeTime, + Seconds (1.0))) + + // etc (more parameters). +@end verbatim + +The declaration for this in the class declaration is one-line public +member method: +@verbatim + public: + static TypeId GetTypeId (void); +@end verbatim + +@section Adding new class type to the Value system + +From the perspective of the user who writes a new class in the system and +wants to hook it in to the attribute system, there is mainly the matter +of writing +the conversions to/from strings and Values. Most of this can be +copy/pasted with macro-ized code. For instance, consider class +Rectangle in the @code{src/mobility/} directory: + +One line is added to the class declaration: +@verbatim +/** + * \brief a 2d rectangle + */ +class Rectangle +{ +... + + VALUE_HELPER_HEADER_1 (Rectangle); +}; +@end verbatim + +One templatized declaration, and two operators, are added below the +class declaration: + +@verbatim +std::ostream &operator << (std::ostream &os, const Rectangle &rectangle); +std::istream &operator >> (std::istream &is, Rectangle &rectangle); + +VALUE_HELPER_HEADER_2 (Rectangle); +@end verbatim + +In the class definition, the code looks like this: + +@verbatim +VALUE_HELPER_CPP (Rectangle); + +std::ostream & +operator << (std::ostream &os, const Rectangle &rectangle) +{ + os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|" << rectangle.yMax; + return os; +} +std::istream & +operator >> (std::istream &is, Rectangle &rectangle) + { + char c1, c2, c3; + is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3 >> rectangle.yMax; + if (c1 != '|' || + c2 != '|' || + c3 != '|') + { + is.setstate (std::ios_base::failbit); + } + return is; +} +@end verbatim + +These stream operators simply convert from a string representation of the +Rectangle ("xMin|xMax|yMin|yMax") to the underlying Rectangle, and the +modeler must specify these operators and the string syntactical representation +of an instance of the new class. + diff --git a/doc/tutorial/introduction.texi b/doc/tutorial/introduction.texi index a1459f6fe..7d654a2c5 100644 --- a/doc/tutorial/introduction.texi +++ b/doc/tutorial/introduction.texi @@ -932,6 +932,7 @@ Similarly to the function template case, the compiler knows that it has to automatically generate code to fill out the class and method declarations and definitions using the appropriate type specified by @code{}. +@node Smart Pointers 101 @subsection Smart Pointers 101 If you are familiar with C++ smart pointers, you may skip this section as it is just a cursory introduction to smart pointers and intrusive reference @@ -1066,6 +1067,7 @@ you use smart pointers just as if they were built-in pointers: p->method (); @end verbatim +@node Object Creation @subsection Object Creation @cindex CreateObject On the right hand side of the line of code we're examining (reproduced below diff --git a/doc/tutorial/log.texi b/doc/tutorial/log.texi new file mode 100644 index 000000000..2f39efe59 --- /dev/null +++ b/doc/tutorial/log.texi @@ -0,0 +1,24 @@ +@node Logging +@chapter Logging +@anchor{chap:Logging} + +This chapter is the first in a series of chapters discussing things that +one can do to modify the input or output of existing ns-3 scripts. + +Examples: +@itemize @bullet +@item Enable or disable the generation of log messages, with fine granularity +@item Set default values for configuration values in the system +@item Generate a report of all configuration values used during a simulation +run (not yet implemented) +@item Set or get values of member variables on objects already instantiated +@item Customizing the tracing output of the script +@item Generate statistics on (not yet implemented) +@item Perform a large number of independent runs of the same simulation +@end itemize + +@node Logging Basics +@section Logging Basics + +@node Enabling Log Output +@section Enabling Log Output diff --git a/doc/tutorial/other.texi b/doc/tutorial/other.texi index 535c11822..efa83a083 100644 --- a/doc/tutorial/other.texi +++ b/doc/tutorial/other.texi @@ -1832,7 +1832,7 @@ container. In the case of a Node, for example, it is quite natural to think of the Node as being the container which contains protocol stacks, internet routing, etc. So, lets start thinking about a real example by calling the container @code{Object Node} instead of @code{A} as we have been. The -creation of this @code is found all over our example programs. For +creation of this @code{Object} is found all over our example programs. For example, you will find code like the following in @code{samples/simple-point-to-point.cc}: diff --git a/doc/tutorial/output.texi b/doc/tutorial/output.texi index 5c18281ca..aa836b5cd 100644 --- a/doc/tutorial/output.texi +++ b/doc/tutorial/output.texi @@ -457,9 +457,6 @@ wrapper, so you will not see the ARP exchanges that were logged when using the ASCII trace wrapper. You are encouraged to take a look at the contents of these pcap files using your favorite pcap software (or Wireshark). -@node Logging -@section Logging - @node Statistics @section Statistics diff --git a/doc/tutorial/tutorial.texi b/doc/tutorial/tutorial.texi index 2eae508f5..d71fe8cf3 100644 --- a/doc/tutorial/tutorial.texi +++ b/doc/tutorial/tutorial.texi @@ -83,12 +83,19 @@ Part 1: Getting Started with ns-3 * Resources:: * Downloading and Compiling:: * Some-Prerequisites:: +Part 2: Reading ns-3 Programs * A-First-ns-3-Script:: -Part 2-: Details -* ns-3 Callbacks:: -* Simulation Output:: -* ns-3 routing overview:: +Part 3: Reconfiguring Existing ns-3 Scripts +* Logging:: +* ns-3 Attributes:: +* Tracing:: +* Statistics:: +Part 4: Creating New or Revised Topologies +* Helper Functions:: * Other-network-topologies:: +Part 5: Extending ns-3 +* ns-3 Callbacks:: +* ns-3 routing overview:: * Nonlinear-Thinking:: * Summary:: * Object-Model:: @@ -100,6 +107,8 @@ Part 2-: Details @end menu @include introduction.texi +@include log.texi +@include attributes.texi @include callbacks.texi @include output.texi @include routing.texi