From 023fa2e8743f3864c908be5db7faa003a20b295a Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Mon, 21 Sep 2009 10:59:21 +0100 Subject: [PATCH 01/63] Upgrade to upstream WAF 1.5.9 plus a bunch of 'waf tools' layered on top. --- waf | Bin 88110 -> 86338 bytes waf-tools/cflags.py | 192 +++++++++++++++++++++++ waf-tools/command.py | 134 ++++++++++++++++ waf-tools/pkgconfig.py | 71 +++++++++ waf-tools/shellcmd.py | 345 +++++++++++++++++++++++++++++++++++++++++ wscript | 2 + 6 files changed, 744 insertions(+) mode change 100755 => 100644 waf create mode 100644 waf-tools/cflags.py create mode 100644 waf-tools/command.py create mode 100644 waf-tools/pkgconfig.py create mode 100644 waf-tools/shellcmd.py diff --git a/waf b/waf old mode 100755 new mode 100644 index ca4d42c188b318018b51571e8c3016b174878f64..65ab0a8e387f84b3765ad21b1e37c7657490182c GIT binary patch delta 83178 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 L{D2B0JUu-M(v86i delta 84964 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 z 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 list of cflags""" + raise NotImplementedError + + def get_optimization_flags(self, level): + """get_optimization_flags(level) -> list of cflags""" + raise NotImplementedError + + def get_debug_flags(self, level): + """get_debug_flags(level) -> (list of cflags, list of cppdefines)""" + raise NotImplementedError + + +class GccTraits(CompilerTraits): + def __init__(self): + super(GccTraits, self).__init__() + # cumulative list of warnings per level + self.warnings_flags = [['-Wall'], ['-Werror'], ['-Wextra']] + + def get_warnings_flags(self, level): + warnings = [] + for l in range(level): + if l < len(self.warnings_flags): + warnings.extend(self.warnings_flags[l]) + else: + break + return warnings + + def get_optimization_flags(self, level): + if level == 0: + return ['-O0'] + elif level == 1: + return ['-O'] + elif level == 2: + return ['-O2'] + elif level == 3: + return ['-O3'] + + def get_debug_flags(self, level): + if level == 0: + return (['-g0'], ['NDEBUG']) + elif level == 1: + return (['-g'], []) + elif level >= 2: + return (['-ggdb', '-g3'], ['_DEBUG']) + + +class IccTraits(CompilerTraits): + def __init__(self): + super(IccTraits, self).__init__() + # cumulative list of warnings per level + # icc is _very_ verbose with -Wall, -Werror is barely achievable + self.warnings_flags = [[], [], ['-Wall']] + + def get_warnings_flags(self, level): + warnings = [] + for l in range(level): + if l < len(self.warnings_flags): + warnings.extend(self.warnings_flags[l]) + else: + break + return warnings + + def get_optimization_flags(self, level): + if level == 0: + return ['-O0'] + elif level == 1: + return ['-O'] + elif level == 2: + return ['-O2'] + elif level == 3: + return ['-O3'] + + def get_debug_flags(self, level): + if level == 0: + return (['-g0'], ['NDEBUG']) + elif level == 1: + return (['-g'], []) + elif level >= 2: + return (['-ggdb', '-g3'], ['_DEBUG']) + + + +class MsvcTraits(CompilerTraits): + def __init__(self): + super(MsvcTraits, self).__init__() + # cumulative list of warnings per level + self.warnings_flags = [['/W2'], ['/WX'], ['/Wall']] + + def get_warnings_flags(self, level): + warnings = [] + for l in range(level): + if l < len(self.warnings_flags): + warnings.extend(self.warnings_flags[l]) + else: + break + return warnings + + def get_optimization_flags(self, level): + if level == 0: + return ['/Od'] + elif level == 1: + return [] + elif level == 2: + return ['/O2'] + elif level == 3: + return ['/Ox'] + + def get_debug_flags(self, level): + if level == 0: + return ([], ['NDEBUG']) + elif level == 1: + return (['/ZI', '/RTC1'], []) + elif level >= 2: + return (['/ZI', '/RTC1'], ['_DEBUG']) + + + +gcc = GccTraits() +icc = IccTraits() +msvc = MsvcTraits() + +# how to map env['COMPILER_CC'] or env['COMPILER_CXX'] into a traits object +compiler_mapping = { + 'gcc': gcc, + 'g++': gcc, + 'msvc': msvc, + 'icc': icc, + 'icpc': icc, +} + +profiles = { + # profile name: [optimization_level, warnings_level, debug_level] + 'default': [2, 1, 1], + 'debug': [0, 2, 3], + 'release': [3, 1, 0], + } + +default_profile = 'default' + +def set_options(opt): + assert default_profile in profiles + opt.add_option('-d', '--build-profile', + action='store', + default=default_profile, + help=("Specify the build profile. " + "Build profiles control the default compilation flags" + " used for C/C++ programs, if CCFLAGS/CXXFLAGS are not" + " set set in the environment. [Allowed Values: %s]" + % ", ".join([repr(p) for p in profiles.keys()])), + choices=profiles.keys(), + dest='build_profile') + +def detect(conf): + cc = conf.env['COMPILER_CC'] or None + cxx = conf.env['COMPILER_CXX'] or None + if not (cc or cxx): + raise Utils.WafError("neither COMPILER_CC nor COMPILER_CXX are defined; " + "maybe the compiler_cc or compiler_cxx tool has not been configured yet?") + + try: + compiler = compiler_mapping[cc] + except KeyError: + try: + compiler = compiler_mapping[cxx] + except KeyError: + Logs.warn("No compiler flags support for compiler %r or %r" + % (cc, cxx)) + return + + opt_level, warn_level, dbg_level = profiles[Options.options.build_profile] + + optimizations = compiler.get_optimization_flags(opt_level) + debug, debug_defs = compiler.get_debug_flags(dbg_level) + warnings = compiler.get_warnings_flags(warn_level) + + if cc and not conf.env['CCFLAGS']: + conf.env.append_value('CCFLAGS', optimizations) + conf.env.append_value('CCFLAGS', debug) + conf.env.append_value('CCFLAGS', warnings) + conf.env.append_value('CCDEFINES', debug_defs) + if cxx and not conf.env['CXXFLAGS']: + conf.env.append_value('CXXFLAGS', optimizations) + conf.env.append_value('CXXFLAGS', debug) + conf.env.append_value('CXXFLAGS', warnings) + conf.env.append_value('CXXDEFINES', debug_defs) diff --git a/waf-tools/command.py b/waf-tools/command.py new file mode 100644 index 000000000..5034acf3c --- /dev/null +++ b/waf-tools/command.py @@ -0,0 +1,134 @@ +from TaskGen import feature, taskgen, before, task_gen +import Node, Task, Utils, Build, pproc, Constants +import Options + +import shellcmd +shellcmd.subprocess = pproc # the WAF version of the subprocess module is supposedly less buggy + +from Logs import debug, error +shellcmd.debug = debug + +import Task + +import re + + +arg_rx = re.compile(r"(?P\$\$)|(?P\$\{(?P\w+)(?P.*?)\})", re.M) + +class command_task(Task.Task): + color = "BLUE" + def __init__(self, env, generator): + Task.Task.__init__(self, env, normal=1, generator=generator) + + def __str__(self): + "string to display to the user" + env = self.env + src_str = ' '.join([a.nice_path(env) for a in self.inputs]) + tgt_str = ' '.join([a.nice_path(env) for a in self.outputs]) + if self.outputs: + sep = ' -> ' + else: + sep = '' + + pipeline = shellcmd.Pipeline() + pipeline.parse(self.generator.command) + cmd = pipeline.get_abbreviated_command() + + return 'command (%s): %s%s%s\n' % (cmd, src_str, sep, tgt_str) + + def _subst_arg(self, arg, direction, namespace): + """ + @param arg: the command argument (or stdin/stdout/stderr) to substitute + @param direction: direction of the argument: 'in', 'out', or None + """ + def repl(match): + if match.group('dollar'): + return "$" + elif match.group('subst'): + var = match.group('var') + code = match.group('code') + result = eval(var+code, namespace) + if isinstance(result, Node.Node): + if var == 'TGT': + return result.bldpath(self.env) + elif var == 'SRC': + return result.srcpath(self.env) + else: + raise ValueError("Bad subst variable %r" % var) + elif result is self.inputs: + if len(self.inputs) == 1: + return result[0].srcpath(self.env) + else: + raise ValueError("${SRC} requested but have multiple sources; which one?") + elif result is self.outputs: + if len(self.outputs) == 1: + return result[0].bldpath(self.env) + else: + raise ValueError("${TGT} requested but have multiple targets; which one?") + else: + return result + return None + + return arg_rx.sub(repl, arg) + + def run(self): + pipeline = shellcmd.Pipeline() + pipeline.parse(self.generator.command) + namespace = self.env.get_merged_dict() + if self.generator.variables is not None: + namespace.update(self.generator.variables) + namespace.update(env=self.env, SRC=self.inputs, TGT=self.outputs) + for cmd in pipeline.pipeline: + if isinstance(cmd, shellcmd.Command): + if isinstance(cmd.stdin, basestring): + cmd.stdin = self._subst_arg(cmd.stdin, 'in', namespace) + if isinstance(cmd.stdout, basestring): + cmd.stdout = self._subst_arg(cmd.stdout, 'out', namespace) + if isinstance(cmd.stderr, basestring): + cmd.stderr = self._subst_arg(cmd.stderr, 'out', namespace) + for argI in xrange(len(cmd.argv)): + cmd.argv[argI] = self._subst_arg(cmd.argv[argI], None, namespace) + if cmd.env_vars is not None: + env_vars = dict() + for name, value in cmd.env_vars.iteritems(): + env_vars[name] = self._subst_arg(value, None, namespace) + cmd.env_vars = env_vars + elif isinstance(cmd, shellcmd.Chdir): + cmd.dir = self._subst_arg(cmd.dir, None, namespace) + + return pipeline.run(verbose=(Options.options.verbose > 0)) + +@taskgen +@feature('command') +def init_command(self): + Utils.def_attrs(self, + # other variables that can be used in the command: ${VARIABLE} + variables = None) + + + +@taskgen +@feature('command') +@before('apply_core') +def apply_command(self): + self.meths.remove('apply_core') + # create the task + task = self.create_task('command') + setattr(task, "dep_vars", getattr(self, "dep_vars", None)) + # process the sources + inputs = [] + for src in self.to_list(self.source): + node = self.path.find_resource(src) + if node is None: + raise Utils.WafError("source %s not found" % src) + inputs.append(node) + task.set_inputs(inputs) + task.set_outputs([self.path.find_or_declare(tgt) for tgt in self.to_list(self.target)]) + #Task.file_deps = Task.extract_deps + + + +class command_taskgen(task_gen): + def __init__(self, *k, **kw): + task_gen.__init__(self, *k, **kw) + self.features.append('command') diff --git a/waf-tools/pkgconfig.py b/waf-tools/pkgconfig.py new file mode 100644 index 000000000..95d7cf587 --- /dev/null +++ b/waf-tools/pkgconfig.py @@ -0,0 +1,71 @@ +# -*- mode: python; encoding: utf-8 -*- +# Gustavo Carneiro (gjamc) 2008 + +import Options +import Configure +import pproc as subprocess +import config_c + +def detect(conf): + pkg_config = conf.find_program('pkg-config', var='PKG_CONFIG') + if not pkg_config: return + +@Configure.conf +def pkg_check_modules(conf, uselib_name, expression, mandatory=True): + pkg_config = conf.env['PKG_CONFIG'] + if not pkg_config: + if mandatory: + conf.fatal("pkg-config is not available") + else: + return False + + argv = [pkg_config, '--cflags', '--libs', expression] + cmd = subprocess.Popen(argv, stdout=subprocess.PIPE) + out, dummy = cmd.communicate() + retval = cmd.wait() + + msg_checking = ("pkg-config flags for %s" % (uselib_name,)) + if Options.options.verbose: + if retval == 0: + conf.check_message_custom(msg_checking, + ('(%s)' % expression), out) + else: + conf.check_message(msg_checking, ('(%s)' % expression), False) + else: + conf.check_message(msg_checking, '', (retval == 0), '') + conf.log.write('%r: %r (exit code %i)\n' % (argv, out, retval)) + + if retval == 0: + + config_c.parse_flags(out, uselib_name, conf.env) + conf.env[uselib_name] = True + return True + + else: + + conf.env[uselib_name] = False + if mandatory: + raise Configure.ConfigurationError('pkg-config check failed') + else: + return False + +@Configure.conf +def pkg_check_module_variable(conf, module, variable): + pkg_config = conf.env['PKG_CONFIG'] + if not pkg_config: + conf.fatal("pkg-config is not available") + + argv = [pkg_config, '--variable', variable, module] + cmd = subprocess.Popen(argv, stdout=subprocess.PIPE) + out, dummy = cmd.communicate() + retval = cmd.wait() + out = out.rstrip() # strip the trailing newline + + msg_checking = ("pkg-config variable %r in %s" % (variable, module,)) + conf.check_message_custom(msg_checking, '', out) + conf.log.write('%r: %r (exit code %i)\n' % (argv, out, retval)) + + if retval == 0: + return out + else: + raise Configure.ConfigurationError('pkg-config check failed') diff --git a/waf-tools/shellcmd.py b/waf-tools/shellcmd.py new file mode 100644 index 000000000..776593528 --- /dev/null +++ b/waf-tools/shellcmd.py @@ -0,0 +1,345 @@ +# Copyright (C) 2008 Gustavo J. A. M. Carneiro + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import shlex +import subprocess +import sys +import re +import os + +env_var_rx = re.compile(r"^([a-zA-Z0-9_]+)=(\S+)$") + +def debug(message): + print >> sys.stderr, message + + +if sys.platform == 'win32': + dev_null = open("NUL:", "w") +else: + dev_null = open("/dev/null", "w") + +def _open_out_file(filename): + if filename in ['NUL:', '/dev/null']: + return dev_null + else: + return open(filename, 'wb') + + +class Node(object): + pass + +class Op(Node): + pass + +class Pipe(Op): + pass + +class And(Op): + pass + +class Or(Op): + pass + +class Command(Node): + class PIPE(object): + pass # PIPE is a constant + class STDOUT(object): + pass # PIPE is a constant + + def __init__(self, name): + super(Command, self).__init__() + self.name = name # command name + self.argv = [name] # command argv + self.stdin = None + self.stdout = None + self.stderr = None + self.env_vars = None + + def __repr__(self): + return "Command(%r, argv=%r, stdin=%r, stdout=%r, stderr=%r)" \ + % (self.name, self.argv, self.stdin, self.stdout, self.stderr) + +class Chdir(Node): + def __init__(self): + super(Chdir, self).__init__() + self.dir = None + + def __repr__(self): + return "Chdir(%r)" \ + % (self.dir) + +class Pipeline(object): + def __init__(self): + self.current_command = None + self.pipeline = [] + + def _commit_command(self): + assert self.current_command is not None + self.pipeline.append(self.current_command) + self.current_command = None + + def get_abbreviated_command(self): + l = [] + for node in self.pipeline: + if isinstance(node, Command): + l.append(node.name) + if isinstance(node, Chdir): + l.append('cd %s' % node.dir) + elif isinstance(node, Pipe): + l.append('|') + elif isinstance(node, And): + l.append('&&') + elif isinstance(node, And): + l.append('||') + return ' '.join(l) + + def parse(self, command): + self.current_command = None + self.pipeline = [] + + if isinstance(command, list): + tokens = list(command) + else: + tokens = shlex.split(command) + debug("command: shlex: %r" % (tokens,)) + + BEGIN, COMMAND, CHDIR, STDERR, STDOUT, STDIN = range(6) + state = BEGIN + self.current_command = None + env_vars = dict() + + while tokens: + token = tokens.pop(0) + if state == BEGIN: + env_var_match = env_var_rx.match(token) + if env_var_match is not None: + env_vars[env_var_match.group(1)] = env_var_match.group(2) + else: + assert self.current_command is None + if token == 'cd': + self.current_command = Chdir() + assert not env_vars + state = CHDIR + else: + self.current_command = Command(token) + if env_vars: + self.current_command.env_vars = env_vars + env_vars = dict() + state = COMMAND + elif state == COMMAND: + if token == '>': + state = STDOUT + elif token == '2>': + state = STDERR + elif token == '2>&1': + assert self.current_command.stderr is None + self.current_command.stderr = Command.STDOUT + elif token == '<': + state = STDIN + elif token == '|': + assert self.current_command.stdout is None + self.current_command.stdout = Command.PIPE + self._commit_command() + self.pipeline.append(Pipe()) + state = BEGIN + elif token == '&&': + self._commit_command() + self.pipeline.append(And()) + state = BEGIN + elif token == '||': + self._commit_command() + self.pipeline.append(Or()) + state = BEGIN + else: + self.current_command.argv.append(token) + elif state == CHDIR: + if token == '&&': + self._commit_command() + self.pipeline.append(And()) + state = BEGIN + else: + assert self.current_command.dir is None + self.current_command.dir = token + elif state == STDOUT: + assert self.current_command.stdout is None + self.current_command.stdout = token + state = COMMAND + elif state == STDERR: + assert self.current_command.stderr is None + self.current_command.stderr = token + state = COMMAND + elif state == STDIN: + assert self.current_command.stdin is None + self.current_command.stdin = token + state = COMMAND + self._commit_command() + return self.pipeline + + def _exec_piped_commands(self, commands): + retvals = [] + for cmd in commands: + retvals.append(cmd.wait()) + retval = 0 + for r in retvals: + if r: + retval = retvals[-1] + break + return retval + + def run(self, verbose=False): + pipeline = list(self.pipeline) + files_to_close = [] + piped_commands = [] + piped_commands_display = [] + BEGIN, PIPE = range(2) + state = BEGIN + cwd = '.' + while pipeline: + node = pipeline.pop(0) + + if isinstance(node, Chdir): + next_op = pipeline.pop(0) + assert isinstance(next_op, And) + cwd = os.path.join(cwd, node.dir) + if verbose: + piped_commands_display.append("cd %s &&" % node.dir) + continue + + assert isinstance(node, (Command, Chdir)) + cmd = node + if verbose: + if cmd.env_vars: + env_vars_str = ' '.join(['%s=%s' % (key, val) for key, val in cmd.env_vars.iteritems()]) + piped_commands_display.append("%s %s" % (env_vars_str, ' '.join(cmd.argv))) + else: + piped_commands_display.append(' '.join(cmd.argv)) + + if state == PIPE: + stdin = piped_commands[-1].stdout + elif cmd.stdin is not None: + stdin = open(cmd.stdin, "r") + if verbose: + piped_commands_display.append('< %s' % cmd.stdin) + files_to_close.append(stdin) + else: + stdin = None + + if cmd.stdout is None: + stdout = None + elif cmd.stdout is Command.PIPE: + stdout = subprocess.PIPE + else: + stdout = _open_out_file(cmd.stdout) + files_to_close.append(stdout) + if verbose: + piped_commands_display.append('> %s' % cmd.stdout) + + if cmd.stderr is None: + stderr = None + elif cmd.stderr is Command.PIPE: + stderr = subprocess.PIPE + elif cmd.stderr is Command.STDOUT: + stderr = subprocess.STDOUT + if verbose: + piped_commands_display.append('2>&1') + else: + stderr = _open_out_file(cmd.stderr) + files_to_close.append(stderr) + if verbose: + piped_commands_display.append('2> %s' % cmd.stderr) + + if cmd.env_vars: + env = dict(os.environ) + env.update(cmd.env_vars) + else: + env = None + + if cwd == '.': + proc_cwd = None + else: + proc_cwd = cwd + + debug("command: subprocess.Popen(argv=%r, stdin=%r, stdout=%r, stderr=%r, env_vars=%r, cwd=%r)" + % (cmd.argv, stdin, stdout, stderr, cmd.env_vars, proc_cwd)) + proc = subprocess.Popen(cmd.argv, stdin=stdin, stdout=stdout, stderr=stderr, env=env, cwd=proc_cwd) + del stdin, stdout, stderr + piped_commands.append(proc) + + try: + next_node = pipeline.pop(0) + except IndexError: + try: + retval = self._exec_piped_commands(piped_commands) + if verbose: + print "%s: exit code %i" % (' '.join(piped_commands_display), retval) + finally: + for f in files_to_close: + if f is not dev_null: + f.close() + files_to_close = [] + return retval + else: + + if isinstance(next_node, Pipe): + state = PIPE + piped_commands_display.append('|') + + elif isinstance(next_node, Or): + try: + this_retval = self._exec_piped_commands(piped_commands) + finally: + for f in files_to_close: + if f is not dev_null: + f.close() + files_to_close = [] + if this_retval == 0: + if verbose: + print "%s: exit code %i (|| is short-circuited)" % (' '.join(piped_commands_display), retval) + return this_retval + if verbose: + print "%s: exit code %i (|| proceeds)" % (' '.join(piped_commands_display), retval) + state = BEGIN + piped_commands = [] + piped_commands_display = [] + + elif isinstance(next_node, And): + try: + this_retval = self._exec_piped_commands(piped_commands) + finally: + for f in files_to_close: + if f is not dev_null: + f.close() + files_to_close = [] + if this_retval != 0: + if verbose: + print "%s: exit code %i (&& is short-circuited)" % (' '.join(piped_commands_display), retval) + return this_retval + if verbose: + print "%s: exit code %i (&& proceeds)" % (' '.join(piped_commands_display), retval) + state = BEGIN + piped_commands = [] + piped_commands_display = [] + + + +def _main(): + pipeline = Pipeline() + pipeline.parse('./foo.py 2>&1 < xxx | cat && ls') + print pipeline.run() + +if __name__ == '__main__': + _main() + diff --git a/wscript b/wscript index bf37bdeff..00e7e43c7 100644 --- a/wscript +++ b/wscript @@ -27,6 +27,8 @@ import Build import Configure import Scripting +sys.path.insert(0, os.path.abspath('waf-tools')) + import cflags # override the build profiles from waf cflags.profiles = { # profile name: [optimization_level, warnings_level, debug_level] From a233d593022cd1d7cbf3fcea55e382b6f737e995 Mon Sep 17 00:00:00 2001 From: Sebastien Vincent Date: Thu, 8 Oct 2009 17:31:58 +0200 Subject: [PATCH 02/63] Fix valgrind error for icmpv6-redirect example. --- src/internet-stack/icmpv6-l4-protocol.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/internet-stack/icmpv6-l4-protocol.cc b/src/internet-stack/icmpv6-l4-protocol.cc index 943fe1b41..a1beff326 100644 --- a/src/internet-stack/icmpv6-l4-protocol.cc +++ b/src/internet-stack/icmpv6-l4-protocol.cc @@ -962,7 +962,8 @@ void Icmpv6L4Protocol::SendRedirection (Ptr redirectedPacket, Ipv6Addres if ((redirectedPacketSize % 8) != 0) { - redirectedPacket->AddPaddingAtEnd (8 - (redirectedPacketSize % 8)); + Ptr pad = Create(8 - (redirectedPacketSize % 8)); + redirectedPacket->AddAtEnd (pad); } if (redirHardwareTarget.GetLength ()) From 33b3bb4c401c56b842b7227f97b501d577756e42 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 8 Oct 2009 10:36:26 -0700 Subject: [PATCH 03/63] add tracing tutorial piece --- doc/tutorial/tracing.texi | 614 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 614 insertions(+) create mode 100644 doc/tutorial/tracing.texi diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi new file mode 100644 index 000000000..317b82ea8 --- /dev/null +++ b/doc/tutorial/tracing.texi @@ -0,0 +1,614 @@ + +@c ============================================================================ +@c Begin document body here +@c ============================================================================ + +@c ============================================================================ +@c PART: The Tracing System +@c ============================================================================ +@c The below chapters are under the major heading "The Tracing System" +@c This is similar to the Latex \part command +@c +@c ============================================================================ +@c The Tracing System +@c ============================================================================ +@node The Tracing System +@chapter The Tracing System + +@menu +* Background:: +@end menu + +@c ============================================================================ +@c Background +@c ============================================================================ +@node Background +@section Background + +As mentioned in the Using the Tracing System section, the whole point of running +an @code{ns-3} simulation is to generate output for study. One has two basic s +trategies to work with in @code{ns-3}: using generic pre-defined output +mechanisms and parsing their content to extract interesting information; or +developing your own output mechanisms that convey exactly (and perhaps only) +the information you want. + +Using pre-defined output mechansims has the advantage of not requiring any +changes to @code{ns-3}, but does require programming. Often, pcap or NS_LOG +output messages are gathered and run through scripts that use grep, sed or awk +to parse the messages and reduce and transform the data to a manageable form. +You do have to write programs to do the transformation, so this does not come +for free. Of course, if the information you are interested in does not exist +in any of the pre-defined output mechanisms, you are stuck. + +If you develop your own output mechanisms, you trade the programming in grep, +sed or awk scripts for programming the use of some form of output in ns-3. This +has several important advantages. First, you can reduce the amount of data +you have to manage and wade through by only tracing interesting events. +Perhaps the most important reason for becoming familiar with this method is +that you can add the ability to trace any piece of information you want. You +will no longer be limited by what the authors of a given model felt was +important to trace. For this reason, we believe that the tracing subsystem +is one of the most important mechansisms to understand in @command{ns-3}. + +@subsection Blunt Instruments +There are many ways to get information out of a program. The most +straightforward way is to just directly print the information to the standard +output, as in, + +@verbatim + #include + ... + void + SomeFunction (void) + { + uint32_t x = SOME_INTERESTING_VALUE; + ... + std::cout << "The value of x is " << x << std::endl; + ... + } +@end verbatim + +Nobody is going to prevent you from going deep into the core of @code{ns-3} and +adding print statements. After all, you have complete control of your own +@node{ns-3} branch. This will probably not turn out to be very satisfactory in +the long term, though. + +As the number of print statements increases in your programs, the task of +dealing with the large number of outputs will become more and more complicated. +Eventually, you may feel the need to control what information is being printed +in some way; perhaps by turning on and off certain categories of prints, or +increasing or decreasing the amount of information you want. If you continue +down this path you may discover that you have re-implemented the @code{NS_LOG} +mechanism. In order to avoid that, one of the first things you might consider +is using @code{NS_LOG} itself. + +We mentioned above that one way to get information out of @code{ns-3} is to +parse existing NS_LOG output for interesting information. However, what happens +if you discover that some tidbit of information you need is not present in +existing log output. You could edit the core of @code{ns-3} and simply add to the +output. This is certainly better than adding print statements since it +follows @code{ns-3} coding conventions and could potentially be useful to other +people as a patch to the existing core. + +If, for example, you wanted to add more logging to the @code{ns-3} TCP socket +(@code{tcp-socket-impl.cc}) you could just add a new message down in the +implementation. Notice that in TcpSocketImpl::ProcessAction() there is no log +message for the @code{ACK_TX} case. You could simply add one, changing the code +from: + +@verbatim + bool TcpSocketImpl::ProcessAction (Actions_t a) + { // These actions do not require a packet or any TCP Headers + NS_LOG_FUNCTION (this << a); + switch (a) + { + case NO_ACT: + NS_LOG_LOGIC ("TcpSocketImpl " << this <<" Action: NO_ACT"); + break; + case ACK_TX: + SendEmptyPacket (TcpHeader::ACK); + break; + ... +@end verbatim + +to add a new @code{NS_LOG_LOGIC} in the appropriate @code{case} statement: + +@verbatim + bool TcpSocketImpl::ProcessAction (Actions_t a) + { // These actions do not require a packet or any TCP Headers + NS_LOG_FUNCTION (this << a); + switch (a) + { + case NO_ACT: + NS_LOG_LOGIC ("TcpSocketImpl " << this << " Action: NO_ACT"); + break; + case ACK_TX: + NS_LOG_LOGIC ("TcpSocketImpl " << this << " Action: ACK_TX"); + SendEmptyPacket (TcpHeader::ACK); + break; + ... +@end verbatim + +This may seem fairly satisfying at first glance, but somthing to consider is that +you will be writing code to add the @code{NS_LOG} statement and you will also +have to write code (as in grep, sed or awk scripts) to parse the log output in +order to isolate and format your information. This is because even though you +have some control over what is output by the logging system, you only have control +down to the log component level. + +If you are adding code to an existing module, you will also have to live with the +output that every other developer has found interesting. You may find that in +order to get the small amount of information you need, you may have to wade +through huge amounts of extraneous messages that are of no interest to you. You +may be forced to save huge log files to disk and process them down to a few lines +whenever you want to do anything. + +We consider prints to @code{std::cout} and NS_LOG messages simple ways to get +more information out of @code{ns-3}, but they are really quite blunt instruments. + +It is desirable to have a facility that allows one to reach into the core system +and only get the information required without having to change and recompile the +core system. Even better would be a system that notified the user when an item +of interest changed or an interesting event happened. + +The @command{ns-3} tracing system is designed to work along those lines and is +well-integrated with the Attribute and Config substems allowing for relatively +simple use scenarios. + +@node Overview +@section Overview + +The ns-3 tracing system is built on the concepts of independent tracing sources +and tracing sinks; along with a uniform mechanism for connecting sources to sinks. + +Trace sources are entities that can signal events that happen in a simulation and +provide access to interesting underlying data. For example, a trace source could +indicate when a packet is received by a net device and provide access to the +packet contents for interested trace sinks. A trace source might also indicate +when an iteresting state change happens in a model. For example, the congestion +window of a TCP model is a prime candidate for a trace source. + +Trace sources are not useful by themselves; they must be connected to other pieces +of code that actually do something useful with the information provided by the source. +The entities that consume trace information are called trace sinks. Trace sources +are generators of events and trace sinks are consumers. This explicit division +allows for large numbers of trace sources to be scattered around the system in +places which model authors believe might be useful. + +There can be zero or more consumers of trace events generated by a trace source. +One can think of a trace source as a kind of point-to-multipoint information link. +Your code looking for trace events from a particular piece of core code could +happily coexist with other code doing something entirely different from the same +information. + +Unless a user connects a trace sink to one of these sources, nothing is output. By +using the tracing system, both you and other people at the same trace source are +getting exactly what they want and only what they want out of the system. Neither +of you are impacting any other user by changing what information is output by the +system. If you happen to add a trace source, your work as a good open-source +citizen may allow other users to provide new utilities that are perhaps very useful +overall, without making any changes to the @code{ns-3} core. + +@node A Simple Low-Level Example +@subsection A Simple Low-Level Example + +Let's take a few minutes and walk through a simple tracing example. We are going +to need a little background on Callbacks to understand what is happening in the +example, so we have to take a small detour right away. + +@node Callbacks +@subsubsection Callbacks + +The goal of the Callback system in @code{ns-3} is to allow one piece of code to +call a function (or method in C++) without any specific inter-module dependency. +This ultimately means you need some kind of indirection -- you treat the address +of the called function as a variable. This variable is called a pointer-to-function +variable. The relationship between function and pointer-to-function pointer is +really no different that that of object and pointer-to-object. + +In C the canonical example of a pointer-to-function is a +pointer-to-function-returning-integer (PFI). For a PFI taking one int parameter, +this could be declared like, + +@verbatim + int (*pfi)(int arg) = 0; +@end verbatim + +What you get from this is a variable named simply ``pfi'' that is initialized +to the value 0. If you want to initialize this pointer to something meaningful, +you have to have a function with a matching signature. In this case, you could +provide a function that looks like, + +@verbatim + int MyFunction (int arg) {} +@end verbatim + +If you have this target, you can initialize the variable to point to your +function: + +@verbatim + pfi = MyFunction; +@end verbatim + +You can then call MyFunction indirectly using the more suggestive form of +the call, + +@verbatim + int result = (*pfi) (1234); +@end verbatim + +This is suggestive since it looks like you are dereferencing the function +pointer just like you would dereference any pointer. Typically, however, +people take advantage of the fact that the compiler knows what is going on +and will just use a shorter form, + +@verbatim + int result = pfi (1234); +@end verbatim + +This looks like you are calling a function named ``pfi,'' but the compiler is +smart enough to know to call through the variable @code{pfi} indirectly to +the function @code{MyFunction}. + +Conceptually, this is almost exactly how the tracing system will work. +Basically, a trace source @emph{is} a callback. When a trace sink expresses +interest in receiving trace events, it adds a Callback to a list of Callbacks +internally held by the trace source. When an interesting event happens, the +trace source invokes its @code{operator()} providing zero or more parameters. +The @code{operator()} eventually wanders down into the system and does something +remarkably like the indirect call you just saw. It provides zero or more +parameters (the call to ``pfi'' above passed one parameter to the target function +@code{MyFunction}. + +The important difference that the tracing system adds is that for each trace +source there is an internal list of Callbacks. Instead of just making one +indirect call, a trace source may invoke any numbr of Callbacks. When a trace +sink expresses interest in notifications from a trace source, it basically just +arranges to add its own fuction to the callback list. + +If you are interested in more details about how this is actually arranged in +@code{ns-3}, feel free to peruse the Callback section of the manual. + +@node Example Code +@subsubsection Example Code + +We have provided some code to implement what is really the simplest example +of tracing that can be assembled. You can find this code in the tutorial +directory as @code{fourth.cc}. Let's walk through it. + +@verbatim + /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ + /* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include "ns3/object.h" + #include "ns3/uinteger.h" + #include "ns3/traced-value.h" + #include "ns3/trace-source-accessor.h" + + #include + + using namespace ns3; +@end verbatim + +Most of this code should be quite familiar to you. As mentioned above, the +trace system makes heavy use of the Object and Attribute systems, so you will +need to include them. The first two includes above bring in the declarations +for those systems explicitly. You could use the core module header, but this +illustrates how simple this all really is. + +The file, @code{traced-value.h} brings in the required declarations for tracing +of data that obeys value semantics. In general, value semantics just means that +you can pass the object around, not an address. In order to use value semantics +at all you have to have an object with an associated copy constructor and +assignment operator available. We extend the requirements to talk about the set +of operators that are pre-defined for plain-old-data (POD) types. Operator=, +operator++, operator--, operator+, operator==, etc. + +What this all really means is that you will be able to trace changes to a C++ +object made using those operators. + +Since the tracing system is integrated with Attributes, and Attributes work +with Objects, there must be an @command{ns-3} @code{Object} for the trace source +to live in. The next code snipped declares and defines a simple Object we can +work with. + +@verbatim + class MyObject : public Object + { + public: + static TypeId GetTypeId (void) + { + static TypeId tid = TypeId ("MyObject") + .SetParent (Object::GetTypeId ()) + .AddConstructor () + .AddTraceSource ("MyInteger", + "An integer value to trace.", + MakeTraceSourceAccessor (&MyObject::m_myInt)) + ; + return tid; + } + + MyObject () {} + TracedValue m_myInt; + }; +@end verbatim + +The two important lines of code, above, with respect to tracing are the +@code{.AddTraceSource} and the @code{TracedValue} declaration of @code{m_myInt}. + +The @code{.AddTraceSource} provides the ``hooks'' used for connecting the trace +source to the outside world through the config system. The @code{TracedValue} +declaration provides the infrastructure that overloads the operators mentioned +above and drives the callback process. + +@verbatim + void + IntTrace (int32_t oldValue, int32_t newValue) + { + std::cout << "Traced " << oldValue << " to " << newValue << std::endl; + } +@end verbatim + +This is the definition of the trace sink. It corresponds directly to a callback +function. This function will be called whenever one of the operators of the +@code{TracedValue} is executed. + +We have now seen the trace source and the trace sink. What remains is code to +connect the source to the sink. + +@verbatim + int + main (int argc, char *argv[]) + { + Ptr myObject = CreateObject (); + myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace)); + + myObject->m_myInt = 1234; + } +@end verbatim + +Here we first create the Object in which the trace source lives. + +The next step, the @code{TraceConnectWithoutContext}, forms the connection +between the trace source and the trace sink. Notice the @code{MakeCallback} +template function. This function does the magic required to create the +underlying callback function. It will make sure that an overloaded +@code{operator()} is there which can be used to ``fire'' the callback. This +is essentially arranging for something that looks just like the @code{pfi()} +example above. The declaration of the @code{TracedValue m_myInt;} +in the Object itself performs the magic needed to provide the overloaded +operators (++, --, etc.) will use this @code{operator()} to actually invoke +the callback. + +The @code{TraceConnectWithoutContext}, takes a string parameter that provides +the name of the Attribute assigned to the trace source. Let's ignore the bit +about context for now since it is not important yet. + +Finally, the line, + +@verbatim + myObject->m_myInt = 1234; +@end verbatim + +should be interpreted as an invocation of @code{operator=} on the member +variable @code{m_myInt} with the integer @code{1234} passed as a parameter. +It turns out that this operator is defined (by @code{TracedValue}) to execute +a callback that returns void and takes two integer values as parameters -- +an old value and a new value for the integer in question. That is exactly +the function signature for the callback function we provided -- @code{IntTrace}. + +To summarize, a trace source is, in essence, a variable that holds a list of +callbacks. A trace sink is a function used as the target of a callback. The +Attribute and object type information systems are used to provide a way to +connect trace sources to trace sinks. The act of ``hitting'' a trace source +is executing an operator on the trace source which fires callbacks. This +results in the trace sink callbacks registering interest in the source being +called with the parameters provided by the source. + +If you now build and run this example, + +@verbatim + ./waf --run fourth +@end verbatim + +you will see the output from the @code{IntTrace} function execute as soon as the +trace source is hit: + +@verbatim + Traced 0 to 1234 +@end verbatim + +When we executed the code, @code{myObject->m_myInt = 1234;}, the trace source +fired and automatically provided the before and after values to the trace sink. +The function @code{IntTrace} then printed this to the standard output. No +problem. + +@subsection Using the Config Subsystem to Connect to Trace Sources + +The @code{TraceConnectWithoutContext} call shown above in the simple example is +actually very rarely used in the system. More typically, the @code{Config} +subsystem is used to allow selecting a trace source in the system using what is +called a @emph{config path}. We saw an example of this in the previous section +where we hooked the ``CourseChange'' event when we were playing with +@code{third.cc}. + +Recall that we defined a trace sink to print course change information from the +mobility models of our simulation. It should now be a lot more clear to you +what this function is doing. + +@verbatim + void + CourseChange (std::string context, Ptr model) + { + Vector position = model->GetPosition (); + NS_LOG_UNCOND (context << + " x = " << position.x << ", y = " << position.y); + } +@end verbatim + +When we connected the ``CourseChange'' trace source to the above trace sink, +we used what is clled a ``Config Path'' to specify the source when we +aranged a connection between the pre-defined trace source and the new trace +sink: + +@verbatim + std::ostringstream oss; + oss << + "/NodeList/" << wifiStaNodes.Get (nWifi - 1)->GetId () << + "/$ns3::MobilityModel/CourseChange"; + + Config::Connect (oss.str (), MakeCallback (&CourseChange)); +@end verbatim + +Let's try and make some sense of what is sometimes considered relatively +mysterious code. For the purposes of discussion, assume that the node +number returned by the @code{GetId()} is ``7''. In this case, the path +above turns out to be, + +@verbatim + "/NodeList/7/$ns3::MobilityModel/CourseChange" +@end verbatim + +The last segment of a config path must be an @code{Attribute} of an +@code{Object}. In fact, if you had a pointer to the @code{Object} that has the +``CourseChange'' @code{Attribute} handy, you could write this just like we did +in the previous example. You know by now that we typically store pointers to +our nodes in a NodeContainer. In the @code{third.cc} example, the Nodes of +interest are stored in the @code{wifiStaNodes} NodeContainer. In fact, while +putting the path together, we used this container to get a Ptr which we +used to call GetId() on. We could have used this Ptr directly to call +a connect method directly: + +@verbatim + Ptr theObject = wifiStaNodes.Get (nWifi - 1); + theObject->TraceConnectWithoutContext ("CourseChange", MakeCallback (&CourseChange)); +@end verbatim + +In the @code{third.cc} example, we actually want an additional ``context'' to +be delivered along with the Callback paramters (which will be explained below) so we +could actually use the following equivalent code, + +@verbatim + Ptr theObject = wifiStaNodes.Get (nWifi - 1); + theObject->TraceConnect ("CourseChange", MakeCallback (&CourseChange)); +@end verbatim + +It turns out that the internal code for @code{Config::ConnectWithoutContext} and +@code{Config::Connect} actually do find a Ptr and call the appropriate +TraceConnect method at the lowest level. + +The @code{Config} functions take a path that represents a chain of @code{Object} +pointers. Each segment of a path corresponds to an Object Attribute. The last +segment is the Attribute of interest, and prior segments must be typed to contain +or find Objects. The @code{Config} code parses and ``walks'' this path until it +gets to the final segment of the path. It then interprets the last segment as +an @code{Attribute} on the last Object it found while walking the path. The +@code{Config} functions then call the appropriate @code{TraceConnect} or +@code{TraceConnectWithoutContext} method on the final Object. Let's see what +happens in a bit more detail when the above path is walked. + +The leading ``/'' character in the path refers to a so-called namespace. One +of the predefined namespaces in the config system is ``NodeList'' which is a +list of all of the nodes in the simulation. Items in the list are referred to +by indices into the list, so ``/NodeList/7'' refers to the eighth node in the +list of nodes created during the simulation. This reference is actually a +@code{Ptr} and so is a subclass of an @code{ns3::Object}. + +As described in the Object Model section of the @code{ns-3} manual, we support +Object Aggregation. This allows us to form an association between different +Objects without any programming. Each Object in an Aggregation can be reached +from the other Objects. + +The next path segment being walked begins with the ``$'' character. This +indicates to the config system that a @code{GetObject} call should be made +looking for the type that follows. It turns out that the MobilityHelper used in +@code{third.cc} arranges to Aggregate, or associate, a mobility model to each of +the wireless Nodes. When you add the ``$'' you are asking for another Object that +has presumably been previously aggregated. You can think of this as switching +pointers from the original Ptr as specified by ``/NodeList/7'' to its +associated mobility model --- which is of type ``$ns3::MobilityModel''. If you +are familiar with @code{GetObject}, we have asked the system to do the following: + +@verbatim + Ptr mobilityModel = node->GetObject () +@end verbatim + +We are now at the last Object in the path, so we turn our attention to the +Attributes of that Object. The @code{MobilityModel} class defines an Attribute +called ``CourseChange.'' You can see this by looking at the source code in +@code{src/mobility/mobility-model.cc} and searching for ``CourseChange'' in your +favorite editor. You should find, + +@verbatim + .AddTraceSource (``CourseChange'', + ``The value of the position and/or velocity vector changed'', + MakeTraceSourceAccessor (&MobilityModel::m_courseChangeTrace)) +@end verbatim + +which should look very familiar at this point. + +If you look for the corresponding declaration of the underlying traced variable +in @code{mobility-model.h} you will find + +@verbatim + TracedCallback > m_courseChangeTrace; +@end verbatim + +The type declaration @code{TracedCallback} identifies @code{m_courseChangeTrace} +as a special list of Callbacks that can be hooked using the Config functions +described above. + +The @code{MobilityModel} class is designed to be a base class providing a common +interface for all of the specific subclasses. If you search down to the end of +the file, you will see a method defined called @code{NotifyCourseChange()}: + +@verbatim + void + MobilityModel::NotifyCourseChange (void) const + { + m_courseChangeTrace(this); + } +@end verbatim + +Derived classes will call into this method whenever they do a course change to +support tracing. This method invokes @code{operator()} on the underlying +@code{m_courseChangeTrace}, which will, in turn, invoke all of the registered +Callbacks, calling all of the trace sinks that have registered interest in the +trace source by calling a Config function. + +So, in the @code{third.cc} example we looked at, whenever a course change is +made in one of the @code{RandomWalk2dMobilityModel} instances installed, there +will be a @code{NotifyCourseChange()} call which calls up into the +@code{MobilityModel} base class. As seen above, this invokes @code{operator()} +on @code{m_courseChangeTrace}, which in turn, calls any registered trace sinks. +In the example, the only code registering an interest was the code that provided +the config path. Therefore, the @code{CourseChange} function that was hooked +from Node number seven will be the only Callback called. + +The final piece of the puzzle is the ``context.'' Recall that we saw an output +looking something like the following from @code{third.cc}: + +@verbatim + /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.27897, y = 2.22677 +@end verbatim + +The first part of the output is the context. It is simply the path through +which the config code located the trace source. In the case we have been looking at +there can be any number of trace sources in the system corresponding to any number +of nodes with mobility models. There needs to be some way to identify which trace +source is actually the one that fired the Callback. An easy way is to request a +trace context when you @code{Config::Connect}. + From 94c2d097f37d2a28c82208383f006182154e3986 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 8 Oct 2009 11:37:29 -0700 Subject: [PATCH 04/63] add fourth.cc to example tests and wordsmith tracing tutorial a bit --- doc/tutorial/tracing.texi | 106 ++++++++++++++++++++++---------------- test.py | 1 + 2 files changed, 62 insertions(+), 45 deletions(-) diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index 317b82ea8..7bea37682 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -26,29 +26,36 @@ @section Background As mentioned in the Using the Tracing System section, the whole point of running -an @code{ns-3} simulation is to generate output for study. One has two basic s -trategies to work with in @code{ns-3}: using generic pre-defined output +an @code{ns-3} simulation is to generate output for study. You have two basic +strategies to work with in @code{ns-3}: using generic pre-defined bulk output mechanisms and parsing their content to extract interesting information; or -developing your own output mechanisms that convey exactly (and perhaps only) -the information you want. +somehow developing an output mechanism that conveys exactly (and perhaps only) +the information wanted. -Using pre-defined output mechansims has the advantage of not requiring any -changes to @code{ns-3}, but does require programming. Often, pcap or NS_LOG -output messages are gathered and run through scripts that use grep, sed or awk -to parse the messages and reduce and transform the data to a manageable form. -You do have to write programs to do the transformation, so this does not come -for free. Of course, if the information you are interested in does not exist -in any of the pre-defined output mechanisms, you are stuck. +Using pre-defined bulk output mechansims has the advantage of not requiring any +changes to @code{ns-3}, but it does require programming. Often, pcap or NS_LOG +output messages are gathered during simulation runs and separately run through +scripts that use grep, sed or awk to parse the messages and reduce and transform +the data to a manageable form. Programs must be written to do the +transformation, so this does not come for free. Of course, if the information +of interest in does not exist in any of the pre-defined output mechanisms, +this approach fails. -If you develop your own output mechanisms, you trade the programming in grep, -sed or awk scripts for programming the use of some form of output in ns-3. This -has several important advantages. First, you can reduce the amount of data -you have to manage and wade through by only tracing interesting events. -Perhaps the most important reason for becoming familiar with this method is -that you can add the ability to trace any piece of information you want. You -will no longer be limited by what the authors of a given model felt was -important to trace. For this reason, we believe that the tracing subsystem -is one of the most important mechansisms to understand in @command{ns-3}. +If you need to add some tidbit of inforamtion to the pre-defined bulk mechanisms, +this can certainly be done; and if you use one of the @code{ns-3} mechanisms, +you may get your code added as a contribution. + +@code{ns-3} provides another mechanism, called Tracing, that avoids some of the +problems inherent in the bulk output mechanisms. It has several important +advantages. First, you can reduce the amount of data you have to manage by only +tracing the events of interest to you. Second, if you use this method, you can +control the format of the output directly so you avoid the postprocessing step +with sed or awk script. If you desire, your output can be formatted directly into +a form acceptable by gnuplot, for example. You can add hooks in the core which +can then be accessed by other users, but which will produce no information unless +explicitly asked to do so. For these reasons, we believe that the @code{ns-3> +Tracing system is the best way to get information out of a simulation and is also +therefore one of the most important mechansisms to understand in @command{ns-3}. @subsection Blunt Instruments There are many ways to get information out of a program. The most @@ -69,9 +76,9 @@ output, as in, @end verbatim Nobody is going to prevent you from going deep into the core of @code{ns-3} and -adding print statements. After all, you have complete control of your own -@node{ns-3} branch. This will probably not turn out to be very satisfactory in -the long term, though. +adding print statements. This is insanely easy to do and, after all, you have +complete control of your own @node{ns-3} branch. This will probably not turn +out to be very satisfactory in the long term, though. As the number of print statements increases in your programs, the task of dealing with the large number of outputs will become more and more complicated. @@ -83,18 +90,18 @@ mechanism. In order to avoid that, one of the first things you might consider is using @code{NS_LOG} itself. We mentioned above that one way to get information out of @code{ns-3} is to -parse existing NS_LOG output for interesting information. However, what happens -if you discover that some tidbit of information you need is not present in -existing log output. You could edit the core of @code{ns-3} and simply add to the -output. This is certainly better than adding print statements since it -follows @code{ns-3} coding conventions and could potentially be useful to other -people as a patch to the existing core. +parse existing NS_LOG output for interesting information. If you discover that +some tidbit of information you need is not present in existing log output, you +could edit the core of @code{ns-3} and simply add your interesting information +to the output stream. Now, this is certainly better than adding your own +print statements since it follows @code{ns-3} coding conventions and could +potentially be useful to other people as a patch to the existing core. -If, for example, you wanted to add more logging to the @code{ns-3} TCP socket -(@code{tcp-socket-impl.cc}) you could just add a new message down in the -implementation. Notice that in TcpSocketImpl::ProcessAction() there is no log -message for the @code{ACK_TX} case. You could simply add one, changing the code -from: +Let's pick a random example. If you wanted to add more logging to the +@code{ns-3} TCP socket (@code{tcp-socket-impl.cc}) you could just add a new +message down in the implementation. Notice that in TcpSocketImpl::ProcessAction() +there is no log message for the @code{ACK_TX} case. You could simply add one, +changing the code from: @verbatim bool TcpSocketImpl::ProcessAction (Actions_t a) @@ -129,12 +136,12 @@ to add a new @code{NS_LOG_LOGIC} in the appropriate @code{case} statement: ... @end verbatim -This may seem fairly satisfying at first glance, but somthing to consider is that -you will be writing code to add the @code{NS_LOG} statement and you will also -have to write code (as in grep, sed or awk scripts) to parse the log output in -order to isolate and format your information. This is because even though you -have some control over what is output by the logging system, you only have control -down to the log component level. +This may seem fairly simple and satisfying at first glance, but somthing to +consider is that you will be writing code to add the @code{NS_LOG} statement +and you will also have to write code (as in grep, sed or awk scripts) to parse +the log output in order to isolate your information. This is because even +though you have some control over what is output by the logging system, you +only have control down to the log component level. If you are adding code to an existing module, you will also have to live with the output that every other developer has found interesting. You may find that in @@ -143,13 +150,22 @@ through huge amounts of extraneous messages that are of no interest to you. You may be forced to save huge log files to disk and process them down to a few lines whenever you want to do anything. -We consider prints to @code{std::cout} and NS_LOG messages simple ways to get -more information out of @code{ns-3}, but they are really quite blunt instruments. +Since there are no guarantees in @code{ns-3} about the stability of @code{NS_LOG} +messages, you may also discover that pieces of log output on which you depend +disappear or change between releases. If you depend on the structure of the +output, you may find other messages being added or deleted which may affect your +parsing code. -It is desirable to have a facility that allows one to reach into the core system -and only get the information required without having to change and recompile the +For these reasons, we consider prints to @code{std::cout} and NS_LOG messages +simple ways to get more information out of @code{ns-3}, but they are really +unstable and quite blunt instruments. + +It is desirable to have a stable facility using stable APIs that allow one to +reach into the core system and only get the information required. It is +desirable to be able to do this without having to change and recompile the core system. Even better would be a system that notified the user when an item -of interest changed or an interesting event happened. +of interest changed or an interesting event happened so the user doesn't have +to actively go poke around in the system looking for things. The @command{ns-3} tracing system is designed to work along those lines and is well-integrated with the Attribute and Config substems allowing for relatively diff --git a/test.py b/test.py index accacd289..1edcd0453 100755 --- a/test.py +++ b/test.py @@ -119,6 +119,7 @@ example_tests = [ ("tutorial/hello-simulator", "True"), ("tutorial/second", "True"), ("tutorial/third", "True"), + ("tutorial/fourth", "True"), ("udp/udp-echo", "True"), From 4a6d30fbd52763503d60d47ebab6d3e478b59560 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 8 Oct 2009 13:59:26 -0700 Subject: [PATCH 05/63] tracing tutorial stuff --- doc/tutorial/tracing.texi | 180 ++++++++++++++++++++++++++++++++++--- doc/tutorial/tutorial.texi | 1 + 2 files changed, 167 insertions(+), 14 deletions(-) diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index 7bea37682..d0788c1a5 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -77,7 +77,7 @@ output, as in, Nobody is going to prevent you from going deep into the core of @code{ns-3} and adding print statements. This is insanely easy to do and, after all, you have -complete control of your own @node{ns-3} branch. This will probably not turn +complete control of your own @ncde{ns-3} branch. This will probably not turn out to be very satisfactory in the long term, though. As the number of print statements increases in your programs, the task of @@ -338,7 +338,7 @@ object made using those operators. Since the tracing system is integrated with Attributes, and Attributes work with Objects, there must be an @command{ns-3} @code{Object} for the trace source -to live in. The next code snipped declares and defines a simple Object we can +to live in. The next code snippet declares and defines a simple Object we can work with. @verbatim @@ -379,8 +379,8 @@ above and drives the callback process. @end verbatim This is the definition of the trace sink. It corresponds directly to a callback -function. This function will be called whenever one of the operators of the -@code{TracedValue} is executed. +function. Once it is connected, this function will be called whenever one of the +overloaded operators of the @code{TracedValue} is executed. We have now seen the trace source and the trace sink. What remains is code to connect the source to the sink. @@ -401,17 +401,24 @@ Here we first create the Object in which the trace source lives. The next step, the @code{TraceConnectWithoutContext}, forms the connection between the trace source and the trace sink. Notice the @code{MakeCallback} template function. This function does the magic required to create the -underlying callback function. It will make sure that an overloaded -@code{operator()} is there which can be used to ``fire'' the callback. This -is essentially arranging for something that looks just like the @code{pfi()} -example above. The declaration of the @code{TracedValue m_myInt;} -in the Object itself performs the magic needed to provide the overloaded -operators (++, --, etc.) will use this @code{operator()} to actually invoke -the callback. +underlying @code{ns-3} Callback object and associate it with the function +@code{IntTrace}. TraceConnect with make the association between your provided +function and the overloaded @code{operator()} in the traced variable referred +to by the ``MyInteger'' Attribute. After this association is made, the trace +source will ``fire'' your provided callback function. -The @code{TraceConnectWithoutContext}, takes a string parameter that provides -the name of the Attribute assigned to the trace source. Let's ignore the bit -about context for now since it is not important yet. +The code to make all of this happen is, of course, non-trivial, but the essence +is that you are arranging for something that looks just like the @code{pfi()} +example above to be called by the trace source. The declaration of the +@code{TracedValue m_myInt;} in the Object itself performs the magic +needed to provide the overloaded operators (++, --, etc.) that will use the +@code{operator()} to actually invoke the Callback with the desired parameters. +The @code{.AddTraceSource} performs the magic to connect the Callback to the +Config system, and @code{TraceConnectWithoutContext} performs the magic to +connect your function to the trace source, which is specified by Attribute +name. + +Let's ignore the bit about context for now. Finally, the line, @@ -421,6 +428,7 @@ Finally, the line, should be interpreted as an invocation of @code{operator=} on the member variable @code{m_myInt} with the integer @code{1234} passed as a parameter. + It turns out that this operator is defined (by @code{TracedValue}) to execute a callback that returns void and takes two integer values as parameters -- an old value and a new value for the integer in question. That is exactly @@ -628,3 +636,147 @@ of nodes with mobility models. There needs to be some way to identify which tra source is actually the one that fired the Callback. An easy way is to request a trace context when you @code{Config::Connect}. +@subsection How to Find and Connect Trace Sources, and Discover Callback Signatures + +The first question that inevitably comes up for new users of the Tracing system is, +``okay, I know that there must be trace sources in the simulation core, but how do +I find out what trace sources are available to me''? + +The second question is, ``okay, I found a trace source, how do I figure out the +config path to use when I connect to it''? + +The third question is, ``okay, I found a trace source, how do I figure out what +the return type and formal arguments of my callback function need to be''? + +The fourth question is, ``okay, I typed that all in and got this incredibly bizarre +error message, what in the world does it mean''? + +@subsection What Trace Sources are Available? + +The answer to this question is found in the @code{ns-3} Doxygen. Go to the +@code{ns-3} web site @uref{http://www.nsnam.org/getting_started.html,,``here''} +and select the ``Doxygen (stable)'' link ``Documentation'' on the navigation +bar to the left side of the page. Expand the ``Modules'' book in the NS-3 +documentation tree a the upper left by clicking the ``+'' box. Now, expand +the ``Core'' book in the tree by clicking its ``+'' box. You should now +see three extremely useful links: + +@itemize @bullet +@item The list of all trace sources +@item The list of all attributes +@item The list of all global values +@end itemize + +The list of interest to us here is ``the list of all trace sources.'' Go +ahead and select that link. You will see, perhaps not too surprisingly, a +list of all of the trace sources available in the @code{ns-3} core. + +As an example, scroll down to @code{ns3::MobilityModel}. You will find +an entry for + +@verbatim + CourseChange: The value of the position and/or velocity vector changed +@end verbatim + +You should recognize this as the trace source we used in the @code{third.cc} +example. Perusing this list will be helpful. + +@subsection What String do I use to Connect? + +The key to this question is found in the @code{ns-3} Doxygen as well. It will +be simplest just to walk through the ``CourseChanged'' example. + +Let's assume that you have just found the ``CourseChanged'' trace source in +``The list of all trace sources'' and you want to figure out how to connect to +it. You know that you are using (again, from the @code{third.cc} example) an +@code{ns3::RandomWalk2dMobilityModel}. So open the ``Class List'' book in +the NS-3 documentation tree by clicking its ``+'' box. You will now see a +list of all of the classes in @code{ns-3}. Scroll down until you see the +entry for @code{ns3::RandomWalk2dMobilityModel} and follow that link. +You should now be looking at the ``ns3::RandomWalk2dMobilityModel Class +Reference.'' + +If you now scroll down to the ``Member Function Documentation'' section, you +will see documentation for the @code{GetTypeId} function. You constructed one +of these in the simple tracing example above: + +@verbatim + static TypeId GetTypeId (void) + { + static TypeId tid = TypeId ("MyObject") + .SetParent (Object::GetTypeId ()) + .AddConstructor () + .AddTraceSource ("MyInteger", + "An integer value to trace.", + MakeTraceSourceAccessor (&MyObject::m_myInt)) + ; + return tid; + } +@end verbatim + +As mentioned above, this is the bit of code that connected the Config +and Attribute systems to the underlying trace source. This is also the +place where you should start looking for information about the way to +connect. + +You are looking at the same information for the RandomWalk2dMobilityModel; and +the information you want is now right there in front of you in the Doxygen: + +@verbatim + This object is accessible through the following paths with Config::Set and Config::Connect: + + /NodeList/[i]/$ns3::MobilityModel/$ns3::RandomWalk2dMobilityModel +@end verbatim + +The documentation tells you how to get to the @code{RandomWalk2dMobilityModel} +Object. Compare the string above with the string we actually used in the +example code: + +@verbatim + "/NodeList/7/$ns3::MobilityModel" +@end verbatim + +The difference is due to the fact that two @code{GetObject} calls are implied +in the string found in the documentation. The first, for @code{$ns3::MobilityModel} +will query the aggregation for the base class. The second implied +@code{GetObject} call, for @code{$ns3::RandomWalk2dMobilityModel}, is used to ``cast'' +the base class to the concrete imlementation class. The documentation shows +both of these operations for you. It turns out that the actual Attribute you are +going to be looking for is found in the base class as we have seen. + +Look further down in the @code{GetTypeId} doxygen. You will find, + +@verbatim + No TraceSources defined for this type. + TraceSources defined in parent class ns3::MobilityModel: + + CourseChange: The value of the position and/or velocity vector changed + Reimplemented from ns3::MobilityModel +@end verbatim + +This is exactly what you need to know. The trace source of interest is found in +@code{ns3::MobilityModel} (which you knew anyway). The interesting thing this +bit of Doxygen teslls you is that you don't need that extra cast in the config +path above to get to the concrete class, since the trace source is actually in +the base class. Therefore the additional @code{GetObject} is not required and +you simply use the path: + +@verbatim + /NodeList/[i]/$ns3::MobilityModel +@end verbatim + +which perfectly matches the example path: + +@verbatim + /NodeList/7/$ns3::MobilityModel +@end verbatim + +@subsection What Return Value and Formal Arguments? + +This is a bit more involved. There are two ways you can figure this out. + + + + + + diff --git a/doc/tutorial/tutorial.texi b/doc/tutorial/tutorial.texi index 80bae6a6a..e445fed1e 100644 --- a/doc/tutorial/tutorial.texi +++ b/doc/tutorial/tutorial.texi @@ -84,6 +84,7 @@ see @uref{http://www.nsnam.org/docs/tutorial.pdf}. * Conceptual Overview:: * Tweaking ns-3:: * Building Topologies:: +* The Tracing System:: @end menu @include introduction.texi From 1acc1b5a65e1e32305bc84e1d5500f5b4c33600d Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 8 Oct 2009 17:57:06 -0700 Subject: [PATCH 06/63] remind waf how to generate introspected doxygen --- doc/tutorial/tracing.texi | 53 +++++++++++- wscript | 169 +++++--------------------------------- 2 files changed, 71 insertions(+), 151 deletions(-) diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index d0788c1a5..97cd08341 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -683,8 +683,26 @@ example. Perusing this list will be helpful. @subsection What String do I use to Connect? -The key to this question is found in the @code{ns-3} Doxygen as well. It will -be simplest just to walk through the ``CourseChanged'' example. +The easiest way to do this is to grep around in the @ns-3 codebase for someone +who has already figured it out, You should always try to copy someone else's +working code before you start to write your own. Try something like: + +@verbatim + find . -name '*.cc' | xargs grep CourseChange | grep Connect +@end verbatim + +and you may find your answer along with working code. For example, in this +case, @code{./ns-3-dev/examples/wireless/mixed-wireless.cc} has something +just waiting for you to use: + +@verbatim + Config::Connect (``/NodeList/*/$ns3::MobilityModel/CourseChange'', + MakeCallback (&CourseChangeCallback)); +@end verbatim + +If you cannot find any examples in the distribution, you can find this out +from the @code{ns-3} Doxygen. It will probably be simplest just to walk +through the ``CourseChanged'' example. Let's assume that you have just found the ``CourseChanged'' trace source in ``The list of all trace sources'' and you want to figure out how to connect to @@ -773,7 +791,36 @@ which perfectly matches the example path: @subsection What Return Value and Formal Arguments? -This is a bit more involved. There are two ways you can figure this out. +The easiest way to do this is to grep around in the @ns-3 codebase for someone +who has already figured it out, You should always try to copy someone else's +working code. Try something like: + +@verbatim + find . -name '*.cc' | xargs grep CourseChange | grep Connect +@end verbatim + +and you may find your answer along with working code. For example, in this +case, @code{./ns-3-dev/examples/wireless/mixed-wireless.cc} has something +just waiting for you to use. You will find + +@verbatim + Config::Connect (``/NodeList/*/$ns3::MobilityModel/CourseChange'', + MakeCallback (&CourseChangeCallback)); +@end verbatim + +as a result of your grep. The @code{MakeCallback} should indicate to you that +there is a callback function there which you can use. Sure enough, there is: + +@verbatim + static void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +If there are no examples to work from, this can be a bit more challenging. + diff --git a/wscript b/wscript index d299625be..1d18b155c 100644 --- a/wscript +++ b/wscript @@ -137,16 +137,6 @@ def set_options(opt): action="store_true", default=False, dest='lcov_report') - opt.add_option('--doxygen', - help=('Run doxygen to generate html documentation from source comments'), - action="store_true", default=False, - dest='doxygen') - opt.add_option('--doxygen-no-build', - help=('Run doxygen to generate html documentation from source comments, ' - 'but do not wait for ns-3 to finish the full build.'), - action="store_true", default=False, - dest='doxygen_no_build') - opt.add_option('--run', help=('Run a locally built program; argument can be a program name,' ' or a command starting with the program name.'), @@ -590,25 +580,12 @@ def build(bld): raise Utils.WafError("Cannot run regression tests: building the ns-3 examples is not enabled" " (regression tests are based on examples)") -# if Options.options.check: -# Options.options.compile_targets += ',run-tests' -# if env['ENABLE_PYTHON_BINDINGS']: -# Options.options.compile_targets += ',ns3module,pybindgen-command' -# _run_check(bld) - - if Options.options.doxygen_no_build: - doxygen() - raise SystemExit(0) - def shutdown(ctx): bld = wutils.bld if wutils.bld is None: return env = bld.env - #if Options.commands['check']: - # _run_waf_check() - if Options.options.lcov_report: lcov_report() @@ -626,25 +603,14 @@ def shutdown(ctx): if Options.options.check: raise Utils.WafError("Please run `./test.py' now, instead of `./waf --check'") - if Options.options.doxygen: - doxygen() - raise SystemExit(0) - check_shell(bld) - if Options.options.doxygen: - doxygen() - raise SystemExit(0) - - check_context = Build.BuildContext def check(bld): """run the equivalent of the old ns-3 unit tests using test.py""" - bld = wutils.bld - env = bld.env - wutils.run_python_program("test.py -c core", env) - + env = wutils.bld.env + wutils.run_python_program("test.py -n -c core", env) class print_introspected_doxygen_task(Task.TaskBase): after = 'cc cxx cc_link cxx_link' @@ -696,111 +662,6 @@ class run_python_unit_tests_task(Task.TaskBase): wutils.run_argv([self.bld.env['PYTHON'], os.path.join("..", "utils", "python-unit-tests.py")], self.bld.env, proc_env, force_no_valgrind=True) -#class run_a_unit_test_task(Task.TaskBase): -# after = 'cc cxx cc_link cxx_link' -# color = 'BLUE' -# -# def __init__(self, bld, name_of_test): -# self.bld = bld -# super(run_a_unit_test_task, self).__init__(generator=self) -# self.name_of_test = name_of_test -# try: -# program_obj = wutils.find_program("run-tests", self.bld.env) -# except ValueError, ex: -# raise Utils.WafError(str(ex)) -# program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)) -# self.program_path = program_node.abspath(self.bld.env) -# -# def __str__(self): -# return 'run-unit-test(%s)\n' % self.name_of_test -# -# def runnable_status(self): -# return Task.RUN_ME -# -# def run(self): -# #print repr([self.program_path, self.name_of_test]) -# try: -# self.retval = wutils.run_argv([self.program_path, self.name_of_test], self.bld.env) -# except Utils.WafError: -# self.retval = 1 -# #print "running test %s: exit with %i" % (self.name_of_test, retval) -# return 0 -# -#class get_list_of_unit_tests_task(Task.TaskBase): -# after = 'cc cxx cc_link cxx_link' -# color = 'BLUE' -# -# def __init__(self, bld): -# self.bld = bld -# super(get_list_of_unit_tests_task, self).__init__(generator=self) -# self.tests = [] -# -# def __str__(self): -# return 'get-unit-tests-list\n' -# -# def runnable_status(self): -# return Task.RUN_ME -# -# def run(self): -# try: -# program_obj = wutils.find_program("run-tests", self.bld.env) -# except ValueError, ex: -# raise Utils.WafError(str(ex)) -# program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)) -# program_path = program_node.abspath(self.bld.env) -# proc = subprocess.Popen([program_path, "--ListTests"], stdout=subprocess.PIPE, -# env=wutils.get_proc_env()) -# self.tests = [l.rstrip() for l in proc.stdout.readlines()] -# retval = proc.wait() -# if retval: -# return retval -# test_tasks = [] -# for name_of_test in self.tests: -# test_tasks.append(run_a_unit_test_task(self.bld, name_of_test)) -# collector = collect_unit_test_results_task(self.bld, list(test_tasks)) -# collector.run_after = list(test_tasks) -# self.more_tasks = [collector] + test_tasks -# -# -#class collect_unit_test_results_task(Task.TaskBase): -# after = 'run_a_unit_test_task' -# color = 'BLUE' -# -# def __init__(self, bld, test_tasks): -# self.bld = bld -# super(collect_unit_test_results_task, self).__init__(generator=self) -# self.test_tasks = test_tasks -# -# def __str__(self): -# return 'collect-unit-tests-results\n' -# -# def runnable_status(self): -# for t in self.run_after: -# if not t.hasrun: -# return Task.ASK_LATER -# return Task.RUN_ME -# -# def run(self): -# failed_tasks = [] -# for task in self.test_tasks: -# if task.retval: -# failed_tasks.append(task) -# if failed_tasks: -# print "C++ UNIT TESTS: %i tests passed, %i failed (%s)." % \ -# (len(self.test_tasks) - len(failed_tasks), len(failed_tasks), -# ', '.join(t.name_of_test for t in failed_tasks)) -# return 1 -# else: -# print "C++ UNIT TESTS: all %i tests passed." % (len(self.test_tasks),) -# return 0 -# -# -#def _run_check(bld): -# task = get_list_of_unit_tests_task(bld) -# print_introspected_doxygen_task(bld) -# if bld.env['ENABLE_PYTHON_BINDINGS']: -# run_python_unit_tests_task(bld) - def check_shell(bld): if 'NS3_MODULE_PATH' not in os.environ: return @@ -835,11 +696,26 @@ def shell(ctx): env = wutils.bld.env wutils.run_argv([shell], env, {'NS3_MODULE_PATH': os.pathsep.join(env['NS3_MODULE_PATH'])}) -def doxygen(): - if not os.path.exists('doc/introspected-doxygen.h'): - Logs.warn("doc/introspected-doxygen.h does not exist; run waf check to generate it.") +def doxygen(bld): + """do a full build, generate the introspected doxygen and then the doxygen""" + Scripting.build(bld) + env = wutils.bld.env + proc_env = wutils.get_proc_env() + + try: + program_obj = wutils.find_program('print-introspected-doxygen', env) + except ValueError: + Logs.warn("print-introspected-doxygen does not exist") + raise SystemExit(1) + return + + prog = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)).abspath(env) + out = open(os.path.join('doc', 'introspected-doxygen.h'), 'w') + + if subprocess.Popen([prog], stdout=out, env=proc_env).wait(): + raise SystemExit(1) + out.close() - ## run doxygen doxygen_config = os.path.join('doc', 'doxygen.conf') if subprocess.Popen(['doxygen', doxygen_config]).wait(): raise SystemExit(1) @@ -876,9 +752,6 @@ def lcov_report(): finally: os.chdir("..") - - - ## ## The default WAF DistDir implementation is rather slow, because it ## first copies everything and only later removes unwanted files and From 7aa6cb832a1978201f0ec424ce9f88e8057052e0 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 8 Oct 2009 18:02:44 -0700 Subject: [PATCH 07/63] update doc/main.h to say ./waf doxygen builds documentation --- doc/main.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/main.h b/doc/main.h index ce9d233e0..260438f8c 100644 --- a/doc/main.h +++ b/doc/main.h @@ -27,9 +27,7 @@ * ns-3 requires Doxygen version 1.5.4 or greater to fully build all items, * although earlier versions of Doxygen will mostly work. * - * Type "./waf --check" followed by "./waf --doxygen" to build the documentation. - * There is a program that runs during "./waf --check" that builds pieces of - * the documentation through introspection. The doc/ directory contains + * Type "./waf doxygen" to build the documentation. The doc/ directory contains * configuration for Doxygen (doxygen.conf and main.txt). The Doxygen * build process puts html files into the doc/html/ directory, and latex * filex into the doc/latex/ directory. From a56b60f6ff707f5dccce3373f2ccb910c9e16f84 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 8 Oct 2009 19:20:28 -0700 Subject: [PATCH 08/63] more tracing tutorial words --- doc/tutorial/tracing.texi | 278 +++++++++++++++++++++++++++++++++++++- 1 file changed, 277 insertions(+), 1 deletion(-) diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index 97cd08341..2bdcaee2b 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -819,7 +819,283 @@ there is a callback function there which you can use. Sure enough, there is: } @end verbatim -If there are no examples to work from, this can be a bit more challenging. +@subsubsection Take my Word for It + +If there are no examples to work from, this can be, well, challenging to +actually figure out from the source code. + +Before embarking on a walkthrough of the code, I'll be kind and just tell you +a simple way to figure this out: The return value of your callback will always +be void. The formal parameter list for a @code{TracedCallback} can be found +from the template parameter list in the declaration. Recall that for our +current example, this is in @code{mobility-model.h}, where we have previously +found: + +@verbatim + TracedCallback > m_courseChangeTrace; +@end verbatim + +There is a one-to-one correspondence between the template parameter list in +the declaration and the formal arguments of the callback function. Here, +there is one template parameter, which is a @code{Ptr}. +This tells you that you need a function that returns void and takes a +a @code{Ptr}. For example, + +@verbatim + void + CourseChangeCallback (Ptr model) + { + ... + } +@end verbatim + +That's all you need if you want to @code{Config::ConnectWithoutContext}. If +you want a context, you need to @code{Config::Connect} and use a Callback +function that takes a string context, then the required argument. + +@verbatim + void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +If you want to ensure that your @code{CourseChangeCallback} is only visible +in your local file, you can add the keyword @code{static} and come up with: + +@verbatim + static void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +which is exactly what we used in the @code{third.c} example. + +@subsubsection The Hard Way + +This section is entirely optional. It is going to be a bumpy ride, especially +for those unfamiliar with the details of templates. However, if you get through +this, you will have a very good handle on a lot of the @code{ns-3} low level +idioms. + +So, again, let's figure out what signature of callback function is required for +the ``CourseChange'' Attribute. This is going to be painful, but you only need +to do this once. After you get through this, you will be able to just look at +a @code{TracedCallback} and understand it. + +The first thing we need to look at is the declaration of the trace source. +Recall that this is in @code{mobility-model.h}, where we have previously +found: + +@verbatim + TracedCallback > m_courseChangeTrace; +@end verbatim + +This declaration is for a template. The template parameter is inside the +angle-brackets, so we are really interested in finding out what that +@code{TracedCallback<>} is. If you have absolutely no idea where this might +be found, grep is your friend. + +We are probably going to be interested in some kind of declaration in the +@code{ns-3} source, so first change into the @code{src} directory. Then, +we know this declaration is going to have to be in some kind of header file, +so just grep for it using: + +@verbatim + find . -name '*.h' | xargs grep TracedCallback +@end verbatim + +You'll see 124 lines fly by (I piped this through wc to see how bad it was). +Although thatmay seem like it, that's not really a lot. Just pipe the output +through more and start scanning through it. On the first page, you will see +some very suspiciously template-looking stuff. + +@verbatim + TracedCallback::TracedCallback () + TracedCallback::ConnectWithoutContext (c ... + TracedCallback::Connect (const CallbackB ... + TracedCallback::DisconnectWithoutContext ... + TracedCallback::Disconnect (const Callba ... + TracedCallback::operator() (void) const ... + TracedCallback::operator() (T1 a1) const ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... + TracedCallback::operator() (T1 a1, T2 a2 ... +@end verbatim + +It turns out that all of this comes from the header file +@code{traced-callback.h} which sounds very promising. You can then take a +look at @code{mobility-model.h} and see that there is a line which confirms +this hunch: + +@verbatim + #include "ns3/traced-callback.h" +@endi verbatim + +Of course, you could have gone at this from the other direction and started +by looking at the includes in @code{mobility-model.h} and noticing the +include of @code{traced-callback} and inferring that this must be the file +you want. + +In either case, the next step is to take a look at @code{src/core/traced-callback.h} +in your favorite editor to see what is happening. + +You will see a comment at the top of the file that should be comforting: + +@verbatim + An ns3::TracedCallback has almost exactly the same API as a normal ns3::Callback but + instead of forwarding calls to a single function (as an ns3::Callback normally does), + it forwards calls to a chain of ns3::Callback. +@end verbatim + +This should sound very familiar and let you know you are on the right track. + +Just after this comment, you will find, + +@verbatim + template + class TracedCallback + { + ... +@end verbatim + +This tells you that TracedCallback is a templated class. It has eight possible +type parameters with default values. Go back and compare this with the +declaration you are trying to understand: + +@verbatim + TracedCallback > m_courseChangeTrace; +@end verbatim + +The @code{typename T1} in the templated class declaration corresponds to the +@code{Ptr} in the declaration above. All of the other +type parameters are left as defaults. Looking at the constructor really +doesn't tell you much. The one place where you have seen a connection made +between your Callback function and the tracing system is in the @code{Connect} +and @code{ConnectWithoutContext} functions. If you scroll down, you will see +a @code{ConnectWithoutContext} method here: + +@verbatim + template + void + TracedCallback::ConnectWithoutContext ... + { + Callback cb; + cb.Assign (callback); + m_callbackList.push_back (cb); + } +@end verbatim + +You are now in the belly of the beast. When the template is instantiated for +the declaration above, the compiler will replace @code{T1} with +@code{Ptr}. + +@verbatim + void + TracedCallback::ConnectWithoutContext ... cb + { + Callback > cb; + cb.Assign (callback); + m_callbackList.push_back (cb); + } +@end verbatim + +You can now see the implementation of everything we've been talking about. The +code creates a Callback of the right type and assigns your function to it. This +is the equivalent of the @code{pfi = MyFunction} we discussed at the start of +this section. The code then adds the Callback to the list of Callbacks for +this source. The only thing left is to look at the definition of Callback. +Using the same grep trick as we used to find @code{TracedCallback}, you will be +able to find that the file ./core/callback.h is the one we need to look at. + +If you look down through the file, you will see a lot of probably almost +incomprehensible template code. You will eventually come to some Doxygen for +the Callback template class, though. Fortunately, there is some English: + +@verbatim + This class template implements the Functor Design Pattern. + It is used to declare the type of a Callback: + - the first non-optional template argument represents + the return type of the callback. + - the second optional template argument represents + the type of the first argument to the callback. + - the third optional template argument represents + the type of the second argument to the callback. + - the fourth optional template argument represents + the type of the third argument to the callback. + - the fifth optional template argument represents + the type of the fourth argument to the callback. + - the sixth optional template argument represents + the type of the fifth argument to the callback. +@end verbatim + +We are trying to figure out what the + +@verbatim + Callback > cb; +@end verbatim + +declaration means. Now we are in a position to understand that the first +(non-optional) parameter, @code{void}, represents the return type of the +Callback. The second (non-optional) parameter, @code{Ptr} +represents the first argument to the callback. + +The Callback in question is your function to receive the trace events. From +this you can infer that you need a function that returns void and takes a +a @code{Ptr}. For example, + +@verbatim + void + CourseChangeCallback (Ptr model) + { + ... + } +@end verbatim + +That's all you need if you want to @code{Config::ConnectWithoutContext}. If +you want a context, you need to @code{Config::Connect} and use a Callback +function that takes a string context. This is because the @code{Connect} +function will provide the context for you. You'll need: + +@verbatim + void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +If you want to ensure that your @code{CourseChangeCallback} is only visible +in your local file, you can add the keyword @code{static} and come up with: + +@verbatim + static void + CourseChangeCallback (std::string path, Ptr model) + { + ... + } +@end verbatim + +which is exactly what we used in the @code{third.c} example. Perhaps you +should now go back and reread the previous section (Take My Word for It). + +If you are interested in more details regarding the implementation of +Callbacks, feel free to take a look at the @code{ns-3} manual. They are one +of the most frequently used constructs in the low-level parts of @code{ns-3}. +It is, in my opinion, a quite elegant thing. + From 482ed05b77efad02a265c050d86e0fb9d7262471 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 8 Oct 2009 20:32:00 -0700 Subject: [PATCH 09/63] valgrind complains about uninitialized buffer even if we don't care --- src/common/pcap-file-test-suite.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/pcap-file-test-suite.cc b/src/common/pcap-file-test-suite.cc index c96f7135a..24967158d 100644 --- a/src/common/pcap-file-test-suite.cc +++ b/src/common/pcap-file-test-suite.cc @@ -175,6 +175,7 @@ WriteModeCreateTestCase::DoRun (void) // data. // uint8_t buffer[128]; + memset(buffer, 0, sizeof(buffer)); err = f.Write (0, 0, buffer, 128); NS_TEST_ASSERT_MSG_EQ (err, false, "Write (write-only-file " << m_testFilename << ") returns error"); @@ -370,6 +371,7 @@ AppendModeCreateTestCase::DoRun (void) // We should be able to write to it since it was opened in "a" mode. // uint8_t buffer[128]; + memset(buffer, 0, sizeof(buffer)); err = f.Write (0, 0, buffer, 128); NS_TEST_ASSERT_MSG_EQ (err, false, "Write (append-mode-file " << m_testFilename << ") returns error"); From d43166d21bdc39007648cb03fdfd3a9ca27f38ca Mon Sep 17 00:00:00 2001 From: Andrey Mazo Date: Fri, 9 Oct 2009 12:05:36 +0400 Subject: [PATCH 10/63] memset () wants #include . --- src/common/pcap-file-test-suite.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/pcap-file-test-suite.cc b/src/common/pcap-file-test-suite.cc index 24967158d..6a9e3903f 100644 --- a/src/common/pcap-file-test-suite.cc +++ b/src/common/pcap-file-test-suite.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "ns3/test.h" #include "ns3/pcap-file.h" From a5614b58622c34c4bca6bd527182944ead004cab Mon Sep 17 00:00:00 2001 From: Andrey Mazo Date: Fri, 9 Oct 2009 15:45:17 +0400 Subject: [PATCH 11/63] Fix typos in tutorial. --- doc/tutorial/tracing.texi | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index 2bdcaee2b..210794066 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -41,7 +41,7 @@ transformation, so this does not come for free. Of course, if the information of interest in does not exist in any of the pre-defined output mechanisms, this approach fails. -If you need to add some tidbit of inforamtion to the pre-defined bulk mechanisms, +If you need to add some tidbit of information to the pre-defined bulk mechanisms, this can certainly be done; and if you use one of the @code{ns-3} mechanisms, you may get your code added as a contribution. @@ -53,9 +53,9 @@ control the format of the output directly so you avoid the postprocessing step with sed or awk script. If you desire, your output can be formatted directly into a form acceptable by gnuplot, for example. You can add hooks in the core which can then be accessed by other users, but which will produce no information unless -explicitly asked to do so. For these reasons, we believe that the @code{ns-3> +explicitly asked to do so. For these reasons, we believe that the @code{ns-3} Tracing system is the best way to get information out of a simulation and is also -therefore one of the most important mechansisms to understand in @command{ns-3}. +therefore one of the most important mechanisms to understand in @command{ns-3}. @subsection Blunt Instruments There are many ways to get information out of a program. The most @@ -68,7 +68,7 @@ output, as in, void SomeFunction (void) { - uint32_t x = SOME_INTERESTING_VALUE; + uint32_t x = SOME_INTERESTING_VALUE; ... std::cout << "The value of x is " << x << std::endl; ... @@ -77,7 +77,7 @@ output, as in, Nobody is going to prevent you from going deep into the core of @code{ns-3} and adding print statements. This is insanely easy to do and, after all, you have -complete control of your own @ncde{ns-3} branch. This will probably not turn +complete control of your own @code{ns-3} branch. This will probably not turn out to be very satisfactory in the long term, though. As the number of print statements increases in your programs, the task of @@ -136,7 +136,7 @@ to add a new @code{NS_LOG_LOGIC} in the appropriate @code{case} statement: ... @end verbatim -This may seem fairly simple and satisfying at first glance, but somthing to +This may seem fairly simple and satisfying at first glance, but something to consider is that you will be writing code to add the @code{NS_LOG} statement and you will also have to write code (as in grep, sed or awk scripts) to parse the log output in order to isolate your information. This is because even @@ -168,7 +168,7 @@ of interest changed or an interesting event happened so the user doesn't have to actively go poke around in the system looking for things. The @command{ns-3} tracing system is designed to work along those lines and is -well-integrated with the Attribute and Config substems allowing for relatively +well-integrated with the Attribute and Config subsystems allowing for relatively simple use scenarios. @node Overview @@ -278,9 +278,9 @@ parameters (the call to ``pfi'' above passed one parameter to the target functio The important difference that the tracing system adds is that for each trace source there is an internal list of Callbacks. Instead of just making one -indirect call, a trace source may invoke any numbr of Callbacks. When a trace +indirect call, a trace source may invoke any number of Callbacks. When a trace sink expresses interest in notifications from a trace source, it basically just -arranges to add its own fuction to the callback list. +arranges to add its own function to the callback list. If you are interested in more details about how this is actually arranged in @code{ns-3}, feel free to peruse the Callback section of the manual. @@ -331,7 +331,7 @@ you can pass the object around, not an address. In order to use value semantics at all you have to have an object with an associated copy constructor and assignment operator available. We extend the requirements to talk about the set of operators that are pre-defined for plain-old-data (POD) types. Operator=, -operator++, operator--, operator+, operator==, etc. +operator++, operator---, operator+, operator==, etc. What this all really means is that you will be able to trace changes to a C++ object made using those operators. @@ -402,7 +402,7 @@ The next step, the @code{TraceConnectWithoutContext}, forms the connection between the trace source and the trace sink. Notice the @code{MakeCallback} template function. This function does the magic required to create the underlying @code{ns-3} Callback object and associate it with the function -@code{IntTrace}. TraceConnect with make the association between your provided +@code{IntTrace}. TraceConnect makes the association between your provided function and the overloaded @code{operator()} in the traced variable referred to by the ``MyInteger'' Attribute. After this association is made, the trace source will ``fire'' your provided callback function. @@ -411,7 +411,7 @@ The code to make all of this happen is, of course, non-trivial, but the essence is that you are arranging for something that looks just like the @code{pfi()} example above to be called by the trace source. The declaration of the @code{TracedValue m_myInt;} in the Object itself performs the magic -needed to provide the overloaded operators (++, --, etc.) that will use the +needed to provide the overloaded operators (++, ---, etc.) that will use the @code{operator()} to actually invoke the Callback with the desired parameters. The @code{.AddTraceSource} performs the magic to connect the Callback to the Config system, and @code{TraceConnectWithoutContext} performs the magic to @@ -430,9 +430,9 @@ should be interpreted as an invocation of @code{operator=} on the member variable @code{m_myInt} with the integer @code{1234} passed as a parameter. It turns out that this operator is defined (by @code{TracedValue}) to execute -a callback that returns void and takes two integer values as parameters -- +a callback that returns void and takes two integer values as parameters --- an old value and a new value for the integer in question. That is exactly -the function signature for the callback function we provided -- @code{IntTrace}. +the function signature for the callback function we provided --- @code{IntTrace}. To summarize, a trace source is, in essence, a variable that holds a list of callbacks. A trace sink is a function used as the target of a callback. The @@ -484,8 +484,8 @@ what this function is doing. @end verbatim When we connected the ``CourseChange'' trace source to the above trace sink, -we used what is clled a ``Config Path'' to specify the source when we -aranged a connection between the pre-defined trace source and the new trace +we used what is called a ``Config Path'' to specify the source when we +arranged a connection between the pre-defined trace source and the new trace sink: @verbatim @@ -522,7 +522,7 @@ a connect method directly: @end verbatim In the @code{third.cc} example, we actually want an additional ``context'' to -be delivered along with the Callback paramters (which will be explained below) so we +be delivered along with the Callback parameters (which will be explained below) so we could actually use the following equivalent code, @verbatim @@ -683,7 +683,7 @@ example. Perusing this list will be helpful. @subsection What String do I use to Connect? -The easiest way to do this is to grep around in the @ns-3 codebase for someone +The easiest way to do this is to grep around in the @code{ns-3} codebase for someone who has already figured it out, You should always try to copy someone else's working code before you start to write your own. Try something like: @@ -774,7 +774,7 @@ Look further down in the @code{GetTypeId} doxygen. You will find, This is exactly what you need to know. The trace source of interest is found in @code{ns3::MobilityModel} (which you knew anyway). The interesting thing this -bit of Doxygen teslls you is that you don't need that extra cast in the config +bit of Doxygen tells you is that you don't need that extra cast in the config path above to get to the concrete class, since the trace source is actually in the base class. Therefore the additional @code{GetObject} is not required and you simply use the path: @@ -791,7 +791,7 @@ which perfectly matches the example path: @subsection What Return Value and Formal Arguments? -The easiest way to do this is to grep around in the @ns-3 codebase for someone +The easiest way to do this is to grep around in the @code{ns-3} codebase for someone who has already figured it out, You should always try to copy someone else's working code. Try something like: @@ -872,7 +872,7 @@ in your local file, you can add the keyword @code{static} and come up with: } @end verbatim -which is exactly what we used in the @code{third.c} example. +which is exactly what we used in the @code{third.cc} example. @subsubsection The Hard Way @@ -909,7 +909,7 @@ so just grep for it using: @end verbatim You'll see 124 lines fly by (I piped this through wc to see how bad it was). -Although thatmay seem like it, that's not really a lot. Just pipe the output +Although that may seem like it, that's not really a lot. Just pipe the output through more and start scanning through it. On the first page, you will see some very suspiciously template-looking stuff. @@ -936,11 +936,11 @@ this hunch: @verbatim #include "ns3/traced-callback.h" -@endi verbatim +@end verbatim Of course, you could have gone at this from the other direction and started by looking at the includes in @code{mobility-model.h} and noticing the -include of @code{traced-callback} and inferring that this must be the file +include of @code{traced-callback.h} and inferring that this must be the file you want. In either case, the next step is to take a look at @code{src/core/traced-callback.h} @@ -1018,7 +1018,7 @@ is the equivalent of the @code{pfi = MyFunction} we discussed at the start of this section. The code then adds the Callback to the list of Callbacks for this source. The only thing left is to look at the definition of Callback. Using the same grep trick as we used to find @code{TracedCallback}, you will be -able to find that the file ./core/callback.h is the one we need to look at. +able to find that the file @code{./core/callback.h} is the one we need to look at. If you look down through the file, you will see a lot of probably almost incomprehensible template code. You will eventually come to some Doxygen for @@ -1053,7 +1053,7 @@ Callback. The second (non-optional) parameter, @code{Ptr} represents the first argument to the callback. The Callback in question is your function to receive the trace events. From -this you can infer that you need a function that returns void and takes a +this you can infer that you need a function that returns @code{void} and takes a @code{Ptr}. For example, @verbatim @@ -1088,7 +1088,7 @@ in your local file, you can add the keyword @code{static} and come up with: } @end verbatim -which is exactly what we used in the @code{third.c} example. Perhaps you +which is exactly what we used in the @code{third.cc} example. Perhaps you should now go back and reread the previous section (Take My Word for It). If you are interested in more details regarding the implementation of From 6e7d010e527449338d7f1af5f7aa8354ee5da38f Mon Sep 17 00:00:00 2001 From: fmoatamr Date: Fri, 9 Oct 2009 16:07:20 +0200 Subject: [PATCH 12/63] Fix the compiling error: \"Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.)\" under Cygwin --- src/simulator/cairo-wideint-private.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/simulator/cairo-wideint-private.h b/src/simulator/cairo-wideint-private.h index 6c8358b9f..bbbca4fd7 100644 --- a/src/simulator/cairo-wideint-private.h +++ b/src/simulator/cairo-wideint-private.h @@ -36,10 +36,12 @@ #define cairo_private #define HAVE_UINT64_T 1 -/*for compatibility with MacOS*/ +/*for compatibility with MacOS and Cygwin*/ #ifndef HAVE_STDINT_H #ifdef __APPLE__ #define HAVE_STDINT_H 1 +#elif defined(WIN32) +#define HAVE_STDINT_H 1 #endif #endif From 204e61b9925e3dd56d7115967831d5c9d0c1776e Mon Sep 17 00:00:00 2001 From: Andrey Mazo Date: Fri, 9 Oct 2009 18:36:39 +0400 Subject: [PATCH 13/63] fix most of doxygen warnings --- src/helper/application-container.h | 6 +++--- src/helper/dot11s-installer.h | 2 +- src/helper/flame-installer.h | 2 +- src/helper/flow-monitor-helper.h | 2 +- src/helper/ipv4-interface-container.h | 5 ++--- src/helper/net-device-container.h | 6 +++--- src/helper/node-container.h | 4 ++-- src/helper/udp-echo-helper.h | 8 ++++---- 8 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/helper/application-container.h b/src/helper/application-container.h index a2cebe6ae..04a20e4e8 100644 --- a/src/helper/application-container.h +++ b/src/helper/application-container.h @@ -157,7 +157,7 @@ public: * \brief Append the contents of another ApplicationContainer to the end of * this container. * - * \param The ApplicationContainer to append. + * \param other The ApplicationContainer to append. */ void Add (ApplicationContainer other); @@ -202,9 +202,9 @@ public: * down and stop doing their thing (Stop) at a common time. * * This method simply iterates through the contained Applications and calls - * their Start() methods with the provided Time. + * their Stop() methods with the provided Time. * - * \param start The Time at which each of the applications should start. + * \param stop The Time at which each of the applications should stop. */ void Stop (Time stop); diff --git a/src/helper/dot11s-installer.h b/src/helper/dot11s-installer.h index a142591da..3c92eec1e 100644 --- a/src/helper/dot11s-installer.h +++ b/src/helper/dot11s-installer.h @@ -54,7 +54,7 @@ public: /** * \brief Install an 802.11s stack. - * \param The Ptr to use when setting up the PMP. + * \param mp The Ptr to use when setting up the PMP. */ bool InstallStack (Ptr mp); diff --git a/src/helper/flame-installer.h b/src/helper/flame-installer.h index b7d93b0f2..efeb4d4d4 100644 --- a/src/helper/flame-installer.h +++ b/src/helper/flame-installer.h @@ -58,7 +58,7 @@ public: /** * \brief Install a flame stack on the given MeshPointDevice - * \param The Ptr to use. + * \param mp The Ptr to use. */ bool InstallStack (Ptr mp); diff --git a/src/helper/flow-monitor-helper.h b/src/helper/flow-monitor-helper.h index 4786a195e..409872d08 100644 --- a/src/helper/flow-monitor-helper.h +++ b/src/helper/flow-monitor-helper.h @@ -46,7 +46,7 @@ public: /// \param nodes A NodeContainer holding the set of nodes to work with. Ptr Install (NodeContainer nodes); /// \brief Enable flow monitoring on a single node - /// \param nodes A Ptr to the node on which to enable flow monitoring. + /// \param node A Ptr to the node on which to enable flow monitoring. Ptr Install (Ptr node); /// \brief Enable flow monitoring on all nodes Ptr InstallAll (); diff --git a/src/helper/ipv4-interface-container.h b/src/helper/ipv4-interface-container.h index 1e4e74562..703702a33 100644 --- a/src/helper/ipv4-interface-container.h +++ b/src/helper/ipv4-interface-container.h @@ -79,12 +79,11 @@ public: * Manually add an entry to the container consisting of a previously composed * entry std::pair. * - * \param ipv4 pointer to Ipv4 object - * \param interface interface index of the Ipv4Interface to add to the container + * \param ipInterfacePair the pair of a pointer to Ipv4 object and interface index of the Ipv4Interface to add to the container * * @see Ipv4InterfaceContainer */ - void Add (std::pair, uint32_t>); + void Add (std::pair, uint32_t> ipInterfacePair); /** * Manually add an entry to the container consisting of the individual parts diff --git a/src/helper/net-device-container.h b/src/helper/net-device-container.h index 79db52daf..1940d28d5 100644 --- a/src/helper/net-device-container.h +++ b/src/helper/net-device-container.h @@ -175,14 +175,14 @@ public: * \brief Append the contents of another NetDeviceContainer to the end of * this container. * - * \param The NetDeviceContainer to append. + * \param other The NetDeviceContainer to append. */ void Add (NetDeviceContainer other); /** * \brief Append a single Ptr to this container. * - * \param application The Ptr to append. + * \param device The Ptr to append. */ void Add (Ptr device); @@ -190,7 +190,7 @@ public: * \brief Append to this container the single Ptr referred to * via its object name service registered name. * - * \param name The name of the NetDevice Object to add to the container. + * \param deviceName The name of the NetDevice Object to add to the container. */ void Add (std::string deviceName); diff --git a/src/helper/node-container.h b/src/helper/node-container.h index 0e2634be5..7a07f5e0b 100644 --- a/src/helper/node-container.h +++ b/src/helper/node-container.h @@ -58,7 +58,7 @@ public: * instantiated and assigned a name using the Object Name Service. This * Node is then specified by its assigned name. * - * \param name The name of the Node Object to add to the container. + * \param nodeName The name of the Node Object to add to the container. */ NodeContainer (std::string nodeName); @@ -240,7 +240,7 @@ public: * \brief Append the contents of another NodeContainer to the end of * this container. * - * \param The NodeContainer to append. + * \param other The NodeContainer to append. */ void Add (NodeContainer other); diff --git a/src/helper/udp-echo-helper.h b/src/helper/udp-echo-helper.h index c554359f4..bc24dd041 100644 --- a/src/helper/udp-echo-helper.h +++ b/src/helper/udp-echo-helper.h @@ -121,7 +121,7 @@ public: * packet (what is sent as data to the server) to the contents of the fill * string (including the trailing zero terminator). * - * \warn The size of resulting echo packets will be automatically adjusted + * \warning The size of resulting echo packets will be automatically adjusted * to reflect the size of the fill string -- this means that the PacketSize * attribute may be changed as a result of this call. * @@ -137,7 +137,7 @@ public: * * The fill byte will be used to initialize the contents of the data packet. * - * \warn The size of resulting echo packets will be automatically adjusted + * \warning The size of resulting echo packets will be automatically adjusted * to reflect the dataLength parameter -- this means that the PacketSize * attribute may be changed as a result of this call. * @@ -156,7 +156,7 @@ public: * by providing a complete buffer with fillLength set to your desired * dataLength * - * \warn The size of resulting echo packets will be automatically adjusted + * \warning The size of resulting echo packets will be automatically adjusted * to reflect the dataLength parameter -- this means that the PacketSize * attribute of the Application may be changed as a result of this call. * @@ -183,7 +183,7 @@ public: * is provided as a string name of a Node that has been previously * associated using the Object Name Service. * - * \param node The name of the node on which to create the UdpEchoClientApplication + * \param nodeName The name of the node on which to create the UdpEchoClientApplication * * \returns An ApplicationContainer that holds a Ptr to the * application created From 6cdc60baf026175ba533f5df82fd51ece1b921b9 Mon Sep 17 00:00:00 2001 From: Sebastien Vincent Date: Fri, 9 Oct 2009 19:31:39 +0200 Subject: [PATCH 14/63] Fix valgrind errors for simple-wifi-frame-aggregation example. --- src/devices/wifi/msdu-standard-aggregator.cc | 3 ++- src/internet-stack/icmpv6-l4-protocol.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/devices/wifi/msdu-standard-aggregator.cc b/src/devices/wifi/msdu-standard-aggregator.cc index c8ea0c9e3..aaac7ca9f 100644 --- a/src/devices/wifi/msdu-standard-aggregator.cc +++ b/src/devices/wifi/msdu-standard-aggregator.cc @@ -64,7 +64,8 @@ MsduStandardAggregator::Aggregate (Ptr packet, Ptr aggrega { if (padding) { - aggregatedPacket->AddPaddingAtEnd (padding); + Ptr pad = Create (padding); + aggregatedPacket->AddAtEnd (pad); } currentHdr.SetDestinationAddr (dest); currentHdr.SetSourceAddr (src); diff --git a/src/internet-stack/icmpv6-l4-protocol.cc b/src/internet-stack/icmpv6-l4-protocol.cc index a1beff326..0ecf2b035 100644 --- a/src/internet-stack/icmpv6-l4-protocol.cc +++ b/src/internet-stack/icmpv6-l4-protocol.cc @@ -962,7 +962,7 @@ void Icmpv6L4Protocol::SendRedirection (Ptr redirectedPacket, Ipv6Addres if ((redirectedPacketSize % 8) != 0) { - Ptr pad = Create(8 - (redirectedPacketSize % 8)); + Ptr pad = Create (8 - (redirectedPacketSize % 8)); redirectedPacket->AddAtEnd (pad); } From 4ce4288ecb8009d53372b5e0ba9ed20ac4475791 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 12:54:54 -0700 Subject: [PATCH 15/63] teach test.py to skip selected tests --- test.py | 290 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 181 insertions(+), 109 deletions(-) diff --git a/test.py b/test.py index 1edcd0453..9438f85da 100755 --- a/test.py +++ b/test.py @@ -53,87 +53,99 @@ ENABLE_EXAMPLES = True # # If the user has constrained us to run certain kinds of tests, we can tell waf # to only build +# core_kinds = ["bvt", "core", "system", "unit"] +# +# There are some special cases for test suites that kill valgrind. This is +# because NSC causes illegal instruction crashes when run under valgrind. +# +core_valgrind_skip_tests = [ + "ns3-tcp-cwnd", + "ns3-tcp-interoperability", +] + # # A list of examples to run as smoke tests just to ensure that they remain # buildable and runnable over time. Also a condition under which to run -# the example (from the waf configuration). +# the example (from the waf configuration), and a condition under which to +# run the example under valgrind. This is because NSC causes illegal +# instruction crashes when run under valgrind. # # XXX Should this not be read from a configuration file somewhere and not # hardcoded. # example_tests = [ - ("csma/csma-bridge", "True"), - ("csma/csma-bridge-one-hop", "True"), - ("csma/csma-broadcast", "True"), - ("csma/csma-multicast", "True"), - ("csma/csma-one-subnet", "True"), - ("csma/csma-packet-socket", "True"), - ("csma/csma-ping", "True"), - ("csma/csma-raw-ip-socket", "True"), - ("csma/csma-star", "True"), + ("csma/csma-bridge", "True", "True"), + ("csma/csma-bridge-one-hop", "True", "True"), + ("csma/csma-broadcast", "True", "True"), + ("csma/csma-multicast", "True", "True"), + ("csma/csma-one-subnet", "True", "True"), + ("csma/csma-packet-socket", "True", "True"), + ("csma/csma-ping", "True", "True"), + ("csma/csma-raw-ip-socket", "True", "True"), + ("csma/csma-star", "True", "True"), - ("emulation/emu-ping", "False"), - ("emulation/emu-udp-echo", "False"), + ("emulation/emu-ping", "False", "True"), + ("emulation/emu-udp-echo", "False", "True"), - ("error-model/simple-error-model", "True"), + ("error-model/simple-error-model", "True", "True"), - ("ipv6/icmpv6-redirect", "True"), - ("ipv6/ping6", "True"), - ("ipv6/radvd", "True"), - ("ipv6/radvd-two-prefix", "True"), - ("ipv6/test-ipv6", "True"), + ("ipv6/icmpv6-redirect", "True", "True"), + ("ipv6/ping6", "True", "True"), + ("ipv6/radvd", "True", "True"), + ("ipv6/radvd-two-prefix", "True", "True"), + ("ipv6/test-ipv6", "True", "True"), - ("mesh/mesh", "True"), + ("mesh/mesh", "True", "True"), - ("naming/object-names", "True"), + ("naming/object-names", "True", "True"), - ("realtime/realtime-udp-echo", "ENABLE_REAL_TIME == True"), + ("realtime/realtime-udp-echo", "ENABLE_REAL_TIME == True", "True"), - ("routing/dynamic-global-routing", "True"), - ("routing/global-injection-slash32", "True"), - ("routing/global-routing-slash32", "True"), - ("routing/mixed-global-routing", "True"), - ("routing/nix-simple", "True"), - ("routing/nms-p2p-nix", "False"), # Takes too long to run - ("routing/simple-alternate-routing", "True"), - ("routing/simple-global-routing", "True"), - ("routing/simple-point-to-point-olsr", "True"), - ("routing/simple-routing-ping6", "True"), - ("routing/static-routing-slash32", "True"), + ("routing/dynamic-global-routing", "True", "True"), + ("routing/global-injection-slash32", "True", "True"), + ("routing/global-routing-slash32", "True", "True"), + ("routing/mixed-global-routing", "True", "True"), + ("routing/nix-simple", "True", "True"), + ("routing/nms-p2p-nix", "False", "True"), # Takes too long to run + ("routing/simple-alternate-routing", "True", "True"), + ("routing/simple-global-routing", "True", "True"), + ("routing/simple-point-to-point-olsr", "True", "True"), + ("routing/simple-routing-ping6", "True", "True"), + ("routing/static-routing-slash32", "True", "True"), - ("stats/wifi-example-sim", "True"), + ("stats/wifi-example-sim", "True", "True"), - ("tap/tap-wifi-dumbbell", "False"), # Requires manual configuration + ("tap/tap-wifi-dumbbell", "False", "True"), # Requires manual configuration - ("tcp/star", "True"), - ("tcp/tcp-large-transfer", "True"), - ("tcp/tcp-nsc-lfn", "ENABLE_NSC == True"), - ("tcp/tcp-nsc-zoo", "ENABLE_NSC == True"), - ("tcp/tcp-star-server", "True"), + ("tcp/star", "True", "True"), + ("tcp/tcp-large-transfer", "True", "True"), + ("tcp/tcp-nsc-lfn", "ENABLE_NSC == True", "True"), + ("tcp/tcp-nsc-zoo", "ENABLE_NSC == True", "True"), + ("tcp/tcp-star-server", "True", "True"), - ("tunneling/virtual-net-device", "True"), + ("tunneling/virtual-net-device", "True", "True"), - ("tutorial/first", "True"), - ("tutorial/hello-simulator", "True"), - ("tutorial/second", "True"), - ("tutorial/third", "True"), - ("tutorial/fourth", "True"), + ("tutorial/first", "True", "True"), + ("tutorial/hello-simulator", "True", "True"), + ("tutorial/second", "True", "True"), + ("tutorial/third", "True", "True"), + ("tutorial/fourth", "True", "True"), - ("udp/udp-echo", "True"), + ("udp/udp-echo", "True", "True"), - ("wireless/mixed-wireless", "True"), - ("wireless/multirate", "False"), # Takes too long to run - ("wireless/simple-wifi-frame-aggregation", "True"), - ("wireless/wifi-adhoc", "False"), # Takes too long to run - ("wireless/wifi-ap --verbose=0", "True"), # Don't let it spew to stdout - ("wireless/wifi-clear-channel-cmu", "False"), # Requires specific hardware - ("wireless/wifi-simple-adhoc", "True"), - ("wireless/wifi-simple-adhoc-grid", "True"), - ("wireless/wifi-simple-infra", "True"), - ("wireless/wifi-simple-interference", "True"), - ("wireless/wifi-wired-bridging", "True"), + ("wireless/mixed-wireless", "True", "True"), + ("wireless/multirate", "False", "True"), # Takes too long to run + ("wireless/simple-wifi-frame-aggregation", "True", "True"), + ("wireless/wifi-adhoc", "False", "True"), # Takes too long to run + ("wireless/wifi-ap --verbose=0", "True", "True"), # Don't let it spew to stdout + ("wireless/wifi-clear-channel-cmu", "False", "True"), # Requires specific hardware + ("wireless/wifi-simple-adhoc", "True", "True"), + ("wireless/wifi-simple-adhoc-grid", "True", "True"), + ("wireless/wifi-simple-infra", "True", "True"), + ("wireless/wifi-simple-interference", "True", "True"), + ("wireless/wifi-wired-bridging", "True", "True"), ] # @@ -230,12 +242,15 @@ def translate_to_html(results_file, html_file): time = get_node_text(suite.getElementsByTagName("SuiteTime")[0]) # - # Print a level three header in green with the result, name and time. - # If the test suite passed, the header is printed in green, otherwise - # it is printed in red. + # Print a level three header with the result, name and time. If the + # test suite passed, the header is printed in green. If the suite was + # skipped, print it in orange, otherwise assume something bad happened + # and print in red. # if result == "PASS": f.write("

%s: %s (%s)

\n" % (result, name, time)) + elif result == "SKIP": + f.write("

%s: %s (%s)

\n" % (result, name, time)) else: f.write("

%s: %s (%s)

\n" % (result, name, time)) @@ -250,8 +265,8 @@ def translate_to_html(results_file, html_file): f.write(" Result \n") # - # If the suite crashed, there is no further information, so just - # delare a new table row with the result (CRASH) in it. Looks like: + # If the suite crashed or is skipped, there is no further information, so just + # delare a new table row with the result (CRASH or SKIP) in it. Looks like: # # +--------+ # | Result | @@ -259,11 +274,14 @@ def translate_to_html(results_file, html_file): # | CRASH | # +--------+ # - # Then go on to the next test suite. Valgrind errors look the same. + # Then go on to the next test suite. Valgrind and skipped errors look the same. # - if result in ["CRASH", "VALGR"]: + if result in ["CRASH", "SKIP", "VALGR"]: f.write("\n") - f.write("%s\n" % result) + if result == "SKIP": + f.write("%s\n" % result) + else: + f.write("%s\n" % result) f.write("\n") f.write("\n") continue @@ -423,11 +441,13 @@ def translate_to_html(results_file, html_file): # if result == "PASS": f.write("%s\n" % result) + elif result == "SKIP": + f.write("%s\n" % result) else: f.write("%s\n" % result) # - # Write the example name as a new tagle data. + # Write the example name as a new tag data. # f.write("%s\n" % name) @@ -556,9 +576,11 @@ def run_job_synchronously(shell_command, directory, valgrind): class Job(): def __init__(self): self.is_break = False + self.is_skip = False self.is_example = False self.shell_command = "" self.display_name = "" + self.basedir = "" self.cwd = "" self.tmp_file_name = "" self.returncode = False @@ -571,6 +593,13 @@ class Job(): def set_is_break(self, is_break): self.is_break = is_break + # + # If a job is to be skipped, we actually run it through the worker threads + # to keep the PASS, FAIL, CRASH and SKIP processing all in one place. + # + def set_is_skip(self, is_skip): + self.is_skip = is_skip + # # Examples are treated differently than standard test suites. This is # mostly because they are completely unaware that they are being run as @@ -666,6 +695,17 @@ class worker_thread(threading.Thread): job.set_is_break(True) self.output_queue.put(job) continue + + # + # If we are actually supposed to skip this job, do so. Note that + # if is_skip is true, returncode is undefined. + # + if job.is_skip: + if options.verbose: + print "Skip %s" % job.shell_command + self.output_queue.put(job) + return + # # Otherwise go about the business of running tests as normal. # @@ -758,8 +798,8 @@ def run_tests(): # # We communicate results in two ways. First, a simple message relating - # PASS, FAIL, or SKIP is always written to the standard output. It is - # expected that this will be one of the main use cases. A developer can + # PASS, FAIL, CRASH or SKIP is always written to the standard output. It + # is expected that this will be one of the main use cases. A developer can # just run test.py with no options and see that all of the tests still # pass. # @@ -868,6 +908,12 @@ def run_tests(): threads.append(thread) thread.start() + # + # Keep track of some summary statistics + # + total_tests = 0 + skipped_tests = 0 + # # We now have worker threads spun up, and a list of work to do. So, run # through the list of test suites and dispatch a job to run each one. @@ -875,7 +921,9 @@ def run_tests(): # Dispatching will run with unlimited speed and the worker threads will # execute as fast as possible from the queue. # - total_tests = 0 + # Note that we actually dispatch tests to be skipped, so all of the + # PASS, FAIL, CRASH and SKIP processing is done in the same place. + # for test in suite_list: if len(test): job = Job() @@ -891,6 +939,9 @@ def run_tests(): job.set_shell_command("utils/test-runner --suite='%s'%s" % (test, multiple)) + if options.valgrind and test in core_valgrind_skip_tests: + job.set_is_skip(True) + if options.verbose: print "Queue %s" % test @@ -942,8 +993,8 @@ def run_tests(): if len(options.suite) == 0 and len(options.example) == 0: if len(options.constrain) == 0 or options.constrain == "example": if ENABLE_EXAMPLES: - for test, condition in example_tests: - if eval(condition) == True: + for test, do_run, do_valgrind_run in example_tests: + if eval(do_run): job = Job() job.set_is_example(True) job.set_display_name(test) @@ -952,6 +1003,9 @@ def run_tests(): job.set_basedir(os.getcwd()) job.set_shell_command("examples/%s" % test) + if options.valgrind and not eval(do_valgrind_run): + job.set_is_skip (True) + if options.verbose: print "Queue %s" % test @@ -1012,18 +1066,22 @@ def run_tests(): else: kind = "TestSuite" - if job.returncode == 0: - status = "PASS" - passed_tests = passed_tests + 1 - elif job.returncode == 1: - failed_tests = failed_tests + 1 - status = "FAIL" - elif job.returncode == 2: - valgrind_errors = valgrind_errors + 1 - status = "VALGR" + if job.is_skip: + status = "SKIP" + skipped_tests = skipped_tests + 1 else: - crashed_tests = crashed_tests + 1 - status = "CRASH" + if job.returncode == 0: + status = "PASS" + passed_tests = passed_tests + 1 + elif job.returncode == 1: + failed_tests = failed_tests + 1 + status = "FAIL" + elif job.returncode == 2: + valgrind_errors = valgrind_errors + 1 + status = "VALGR" + else: + crashed_tests = crashed_tests + 1 + status = "CRASH" print "%s: %s %s" % (status, kind, job.display_name) @@ -1042,12 +1100,14 @@ def run_tests(): example_name = " %s\n" % job.display_name f.write(example_name) - if job.returncode == 0: + if status == "PASS": f.write(' PASS\n') - elif job.returncode == 1: + elif status == "FAIL": f.write(' FAIL\n') - elif job.returncode == 2: + elif status == "VALGR": f.write(' VALGR\n') + elif status == "SKIP": + f.write(' SKIP\n') else: f.write(' CRASH\n') @@ -1099,31 +1159,43 @@ def run_tests(): # fails valgrind, we'll see the PASS entry for the working TestSuite # followed by a VALGR failing test suite of the same name. # - if job.returncode == 0 or job.returncode == 1 or job.returncode == 2: - f_to = open(xml_results_file, 'a') - f_from = open(job.tmp_file_name, 'r') - f_to.write(f_from.read()) - f_to.close() - f_from.close() + if job.is_skip: + f = open(xml_results_file, 'a') + f.write("\n") + f.write(" %s\n" % job.display_name) + f.write(' SKIP\n') + f.write(' Execution times not available\n') + f.write("\n") + f.close() else: - f = open(xml_results_file, 'a') - f.write("\n") - f.write(" %s\n" % job.display_name) - f.write(' CRASH\n') - f.write(' Execution times not available\n') - f.write("\n") - f.close() + if job.returncode == 0 or job.returncode == 1 or job.returncode == 2: + f_to = open(xml_results_file, 'a') + f_from = open(job.tmp_file_name, 'r') + f_to.write(f_from.read()) + f_to.close() + f_from.close() + else: + f = open(xml_results_file, 'a') + f.write("\n") + f.write(" %s\n" % job.display_name) + f.write(' CRASH\n') + f.write(' Execution times not available\n') + f.write("\n") + f.close() - if job.returncode == 2: - f = open(xml_results_file, 'a') - f.write("\n") - f.write(" %s\n" % job.display_name) - f.write(' VALGR\n') - f.write(' Execution times not available\n') - f.write("\n") - f.close() + if job.returncode == 2: + f = open(xml_results_file, 'a') + f.write("\n") + f.write(" %s\n" % job.display_name) + f.write(' VALGR\n') + f.write(' Execution times not available\n') + f.write("\n") + f.close() - os.remove(job.tmp_file_name) + try: + os.remove(job.tmp_file_name) + except: + pass # # We have all of the tests run and the results written out. One final @@ -1146,8 +1218,8 @@ def run_tests(): # # Print a quick summary of events # - print "%d of %d tests passed (%d passed, %d failed, %d crashed, %d valgrind errors)" % (passed_tests, total_tests, - passed_tests, failed_tests, crashed_tests, valgrind_errors) + print "%d of %d tests passed (%d passed, %d skipped, %d failed, %d crashed, %d valgrind errors)" % (passed_tests, + total_tests, passed_tests, skipped_tests, failed_tests, crashed_tests, valgrind_errors) # # The last things to do are to translate the XML results file to "human # readable form" if the user asked for it (or make an XML file somewhere) From dafb0530ccf900986cb1b549a1fe66955afe15ed Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 15:52:01 -0700 Subject: [PATCH 16/63] fix ns3-tcp-cwnd unit test --- src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc | 17 ++++++----------- test.py | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc b/src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc index 97b76fa1b..25cd26619 100644 --- a/src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc +++ b/src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc @@ -345,25 +345,20 @@ Ns3TcpCwndTestCase1::DoRun (void) // reflecting the change from LARGEST_CWND back to MSS // const uint32_t MSS = 536; - const uint32_t N_EVENTS = 21; - const uint32_t LARGEST_CWND = MSS * N_EVENTS; + const uint32_t N_EVENTS = 20; CwndEvent event; NS_TEST_ASSERT_MSG_EQ (m_responses.GetN (), N_EVENTS, "Unexpectedly low number of cwnd change events"); - for (uint32_t i = 0, from = 536, to = 1072; i < N_EVENTS - 1; ++i, from += 536, to += 536) + for (uint32_t i = 0, from = MSS, to = MSS * 2; i < N_EVENTS; ++i, from += MSS, to += MSS) { event = m_responses.Get (i); NS_TEST_ASSERT_MSG_EQ (event.m_oldCwnd, from, "Wrong old cwnd value in cwnd change event " << i); NS_TEST_ASSERT_MSG_EQ (event.m_newCwnd, to, "Wrong new cwnd value in cwnd change event " << i); } - event = m_responses.Get (N_EVENTS - 1); - NS_TEST_ASSERT_MSG_EQ (event.m_oldCwnd, LARGEST_CWND, "Wrong old cwnd value in cwnd change event " << N_EVENTS - 1); - NS_TEST_ASSERT_MSG_EQ (event.m_newCwnd, MSS, "Wrong new cwnd value in cwnd change event " << N_EVENTS - 1); - return GetErrorStatus (); } @@ -523,19 +518,19 @@ Ns3TcpCwndTestCase2::DoRun (void) NS_TEST_ASSERT_MSG_EQ (m_responses.GetN (), 31, "Unexpected number of cwnd change events"); - for (uint32_t i = 0, from = 536, to = 1072; i < 9; ++i, from += 536, to += 536) + for (uint32_t i = 0, from = MSS, to = MSS * 2; i < 9; ++i, from += MSS, to += MSS) { event = m_responses.Get (i); NS_TEST_ASSERT_MSG_EQ (event.m_oldCwnd, from, "Wrong old cwnd value in cwnd change event " << i); NS_TEST_ASSERT_MSG_EQ (event.m_newCwnd, to, "Wrong new cwnd value in cwnd change event " << i); } - // Cwnd should be back to 536 + // Cwnd should be back to MSS event = m_responses.Get (9); NS_TEST_ASSERT_MSG_EQ (event.m_newCwnd, MSS, "Wrong new cwnd value in cwnd change event " << 9); // Another round of slow start - for (uint32_t i = 10, from = 536, to = 1072; i < 14; ++i, from += 536, to += 536) + for (uint32_t i = 10, from = MSS, to = MSS * 2; i < 14; ++i, from += MSS, to += MSS) { event = m_responses.Get (i); NS_TEST_ASSERT_MSG_EQ (event.m_oldCwnd, from, "Wrong old cwnd value in cwnd change event " << i); @@ -556,7 +551,7 @@ Ns3TcpCwndTestCase2::DoRun (void) << i); } - // Cwnd should be back to 536 + // Cwnd should be back to MSS event = m_responses.Get (29); NS_TEST_ASSERT_MSG_EQ (event.m_newCwnd, MSS, "Wrong new cwnd value in cwnd change event " << 29); diff --git a/test.py b/test.py index 9438f85da..e6d64fc65 100755 --- a/test.py +++ b/test.py @@ -1233,7 +1233,7 @@ def run_tests(): if len(options.xml): shutil.copyfile(xml_results_file, options.xml) - if passed_tests == total_tests: + if passed_tests + skipped_tests == total_tests: return 0 # success else: return 1 # catchall for general errors From cabdaadcf8994ff25be4fe4b376dc68bdc148a44 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 20:50:17 -0700 Subject: [PATCH 17/63] Update tutorial typos --- doc/tutorial/building-topologies.texi | 16 +++++----- doc/tutorial/conceptual-overview.texi | 8 ++--- doc/tutorial/getting-started.texi | 20 ++++++------ doc/tutorial/tweaking.texi | 44 +++++++++++++-------------- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/doc/tutorial/building-topologies.texi b/doc/tutorial/building-topologies.texi index 44585005d..8d6bbd0c0 100644 --- a/doc/tutorial/building-topologies.texi +++ b/doc/tutorial/building-topologies.texi @@ -380,8 +380,8 @@ Since we have set up the UDP echo applications to log just as we did in @code{first.cc}, you will see similar output when you run the script. @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.415s) Sent 1024 bytes to 10.1.2.4 Received 1024 bytes from 10.1.1.1 @@ -560,8 +560,8 @@ devices set to four: You should now see, @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.405s) Sent 1024 bytes to 10.1.2.5 Received 1024 bytes from 10.1.1.1 @@ -634,8 +634,8 @@ If you build the new script and run the simulation setting @code{nCsma} to 100, you will see the following output: @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.407s) Sent 1024 bytes to 10.1.2.101 Received 1024 bytes from 10.1.1.1 @@ -1117,8 +1117,8 @@ Again, since we have set up the UDP echo applications just as we did in the @code{second.cc} script, you will see similar output. @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.407s) Sent 1024 bytes to 10.1.2.4 Received 1024 bytes from 10.1.3.3 diff --git a/doc/tutorial/conceptual-overview.texi b/doc/tutorial/conceptual-overview.texi index 76d61bdec..39100f058 100644 --- a/doc/tutorial/conceptual-overview.texi +++ b/doc/tutorial/conceptual-overview.texi @@ -750,10 +750,10 @@ You should see messages reporting that your @code{myfirst} example was built successfully. @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' [614/708] cxx: scratch/myfirst.cc -> build/debug/scratch/myfirst_3.o [706/708] cxx_link: build/debug/scratch/myfirst_3.o -> build/debug/scratch/myfirst - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (2.357s) @end verbatim @@ -767,8 +767,8 @@ directory you must run it out of the scratch directory): You should see some output: @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.418s) Sent 1024 bytes to 10.1.1.2 Received 1024 bytes from 10.1.1.1 diff --git a/doc/tutorial/getting-started.texi b/doc/tutorial/getting-started.texi index 238d93047..106b9cb18 100644 --- a/doc/tutorial/getting-started.texi +++ b/doc/tutorial/getting-started.texi @@ -126,8 +126,8 @@ introduced features. Since the release numbers are going to be changing, I will stick with the more constant ns-3-dev here in the tutorial, but you can replace the -string ``ns-3-dev'' with your choice of release (e.g., ns-3.5 and -ns-3.5-ref-traces) in the text below. You can find the latest version of the +string ``ns-3-dev'' with your choice of release (e.g., ns-3.6 and +ns-3.6-ref-traces) in the text below. You can find the latest version of the code either by inspection of the repository list or by going to the @uref{http://www.nsnam.org/getting_started.html,,``Getting Started''} web page and looking for the latest release identifier. @@ -138,7 +138,7 @@ script to pull down the various pieces of @command{ns-3} you will be using. Go ahead and type the following into your shell (remember you can substitute the name of your chosen release number instead of @code{ns-3-dev} -- like -@code{"ns-3.5"} and @code{"ns-3.5-ref-traces"} if you want to work with a +@code{"ns-3.6"} and @code{"ns-3.6-ref-traces"} if you want to work with a stable release). @verbatim @@ -267,16 +267,16 @@ get a copy of a release by typing the following into your Linux shell cd mkdir tarballs cd tarballs - wget http://www.nsnam.org/releases/ns-allinone-3.5.tar.bz2 - tar xjf ns-allinone-3.5.tar.bz2 + wget http://www.nsnam.org/releases/ns-allinone-3.6.tar.bz2 + tar xjf ns-allinone-3.6.tar.bz2 @end verbatim -If you change into the directory @code{ns-allinone-3.5} you should see a +If you change into the directory @code{ns-allinone-3.6} you should see a number of files: @verbatim -build.py* ns-3.5/ nsc-0.5.0/ README -constants.py ns-3.5-ref-traces/ pybindgen-0.10.0.640/ util.py +build.py* ns-3.6/ nsc-0.5.1/ README +constants.py ns-3.6-ref-traces/ pybindgen-0.12.0.700/ util.py @end verbatim You are now ready to build the @command{ns-3} distribution. @@ -298,7 +298,7 @@ Change into the directory you created in the download section above. If you downloaded using Mercurial you should have a directory called @code{ns-3-allinone} under your @code{~/repos} directory. If you downloaded using a tarball you should have a directory called something like -@code{ns-allinone-3.5} under your @code{~/tarballs} directory. Take a deep +@code{ns-allinone-3.6} under your @code{~/tarballs} directory. Take a deep breath and type the following: @verbatim @@ -310,7 +310,7 @@ script builds the various pieces you downloaded. Eventually you should see the following magic words: @verbatim - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (2m30.586s) @end verbatim diff --git a/doc/tutorial/tweaking.texi b/doc/tutorial/tweaking.texi index 9524a2022..2da297b1d 100644 --- a/doc/tutorial/tweaking.texi +++ b/doc/tutorial/tweaking.texi @@ -100,8 +100,8 @@ You should see the now familiar output of the first @command{ns-3} example program @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.413s) Sent 1024 bytes to 10.1.1.2 Received 1024 bytes from 10.1.1.1 @@ -150,8 +150,8 @@ you run the script with NS_LOG set this way, the @command{ns-3} logging system will pick up the change and you should see the following output: @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.404s) UdpEchoClientApplication:UdpEchoClient() UdpEchoClientApplication:SetDataSize(1024) @@ -205,8 +205,8 @@ that every message from the given log component is prefixed with the component name. @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.417s) UdpEchoClientApplication:UdpEchoClient() UdpEchoClientApplication:SetDataSize(1024) @@ -242,8 +242,8 @@ echo client and server applications. You may see that this can be very useful in debugging problems. @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.406s) UdpEchoServerApplication:UdpEchoServer() UdpEchoClientApplication:UdpEchoClient() @@ -277,8 +277,8 @@ Again, you will have to remove the newline above. If you run the script now, you should see the following output: @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.418s) 0s UdpEchoServerApplication:UdpEchoServer() 0s UdpEchoClientApplication:UdpEchoClient() @@ -410,8 +410,8 @@ If you now run the script you will see your new ``Creating Topology'' log message, @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.404s) Creating Topology Sent 1024 bytes to 10.1.1.2 @@ -465,8 +465,8 @@ sort out which program gets which argument. The command line parser will now see the @code{--PrintHelp} argument and respond with, @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.413s) TcpL4Protocol:TcpStateMachine() CommandLine:HandleArgument(): Handle arg name=PrintHelp value= @@ -542,8 +542,8 @@ time prefix. If you run the script, you should now see the following output, @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.405s) 0s UdpEchoServerApplication:UdpEchoServer() 1s UdpEchoServerApplication:StartApplication() @@ -606,8 +606,8 @@ in which case we recover the timing we had when we explicitly set the @code{DataRate} and @code{Delay} in the script: @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/bu - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/bui + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.417s) 0s UdpEchoServerApplication:UdpEchoServer() 1s UdpEchoServerApplication:StartApplication() @@ -684,8 +684,8 @@ Try, @end verbatim @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.403s) --PrintHelp: Print this help message. --PrintGroups: Print the list of groups. @@ -707,8 +707,8 @@ setting the @code{--nPackets} argument in the command line, You should now see @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (0.404s) 0s UdpEchoServerApplication:UdpEchoServer() 1s UdpEchoServerApplication:StartApplication() From 13826cf9045dc7ef438bde65d904525fedaad59a Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 20:50:49 -0700 Subject: [PATCH 18/63] update tutorial paths for new examples directory --- doc/tutorial/building-topologies.texi | 10 ++--- doc/tutorial/conceptual-overview.texi | 8 ++-- doc/tutorial/getting-started.texi | 58 ++++++++++++--------------- 3 files changed, 35 insertions(+), 41 deletions(-) diff --git a/doc/tutorial/building-topologies.texi b/doc/tutorial/building-topologies.texi index 8d6bbd0c0..60fc4e24a 100644 --- a/doc/tutorial/building-topologies.texi +++ b/doc/tutorial/building-topologies.texi @@ -43,10 +43,10 @@ point-to-point topologies, we will see equivalent CSMA topology helpers in this section. The appearance and operation of these helpers should look quite familiar to you. -We provide an example script in our @code{examples} directory. This script +We provide an example script in our @code{examples/tutorial} directory. This script builds on the @code{first.cc} script and adds a CSMA network to the point-to-point simulation we've already considered. Go ahead and open -@code{examples/second.cc} in your favorite editor. You will have already seen +@code{examples/tutorial/second.cc} in your favorite editor. You will have already seen enough @command{ns-3} code to understand most of what is going on in this example, but we will go over the entire script and examine some of the output. @@ -356,7 +356,7 @@ the @code{first.cc} example. If you are in the top-level directory of the repository you just type, @verbatim - cp examples/second.cc scratch/mysecond.cc + cp examples/tutorial/second.cc scratch/mysecond.cc ./waf @end verbatim @@ -712,9 +712,9 @@ constructing point-to-point topologies, we will see equivalent @code{Wifi} topology helpers in this section. The appearance and operation of these helpers should look quite familiar to you. -We provide an example script in our @code{examples} directory. This script +We provide an example script in our @code{examples/tutorial} directory. This script builds on the @code{second.cc} script and adds a Wifi network. Go ahead and -open @code{examples/third.cc} in your favorite editor. You will have already +open @code{examples/tutorial/third.cc} in your favorite editor. You will have already seen enough @command{ns-3} code to understand most of what is going on in this example, but there are a few new things, so we will go over the entire script and examine some of the output. diff --git a/doc/tutorial/conceptual-overview.texi b/doc/tutorial/conceptual-overview.texi index 39100f058..322ab689a 100644 --- a/doc/tutorial/conceptual-overview.texi +++ b/doc/tutorial/conceptual-overview.texi @@ -181,7 +181,7 @@ directory structure something like the following: @end verbatim @cindex first.cc -Change into the examples directory. You should see a file named +Change into the @code{examples/tutorial} directory. You should see a file named @code{first.cc} located there. This is a script that will create a simple point-to-point link between two nodes and echo a single packet between the nodes. Let's take a look at that script line by line, so go ahead and open @@ -277,7 +277,7 @@ done a @end verbatim to build the project. So now if you look in the directory -@code{../build/debug/ns3} you will find the four module include files shown +@code{../../build/debug/ns3} you will find the four module include files shown above. You can take a look at the contents of these files and find that they do include all of the public include files in their respective modules. @@ -732,12 +732,12 @@ took care of the hard part for you. The remaining lines of our first @subsection Building Your Script We have made it trivial to build your simple scripts. All you have to do is to drop your script into the scratch directory and it will automatically be -built if you run Waf. Let's try it. Copy @code{examples/first.cc} into +built if you run Waf. Let's try it. Copy @code{examples/tutorial/first.cc} into the @code{scratch} directory after changing back into the top level directory. @verbatim cd .. - cp examples/first.cc scratch/myfirst.cc + cp examples/tutorial/first.cc scratch/myfirst.cc @end verbatim Now build your first example script using waf: diff --git a/doc/tutorial/getting-started.texi b/doc/tutorial/getting-started.texi index 106b9cb18..5fc36d3a9 100644 --- a/doc/tutorial/getting-started.texi +++ b/doc/tutorial/getting-started.texi @@ -405,6 +405,7 @@ output that looks similar to the following, Python Bindings : enabled Python API Scanning Support : enabled Use sudo to set suid bit : not enabled (option --enable-sudo not selected) + Build examples and samples : enabled Static build : not enabled (option --enable-static not selected) 'configure' finished successfully (2.870s) @end verbatim @@ -462,51 +463,44 @@ but now you know how to change the configuration and build optimized code. @cindex unit tests You can run the unit tests of the @command{ns-3} distribution by running the -``--check'' option, +``./test.py -c core'' script, @verbatim - ./waf --check + ./test.py -c core @end verbatim -These tests are run in parallel by waf, so the summary, ``Ran n tests'' will -appear as soon as all of the tasks are launched, but you should eventually +These tests are run in parallel by waf. You should eventually see a report saying that, @verbatim - C++ UNIT TESTS: all 33 tests passed. + 47 of 47 tests passed (47 passed, 0 failed, 0 crashed, 0 valgrind errors) @end verbatim This is the important message. -You will also see output from the test runner and waf task sequence numbers -the output will actually look something like, +You will also see output from the test runner and the output will actually look something like, @verbatim - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - [707/709] get-unit-tests-list - [708/709] run-python-unit-tests - [709/709] print-introspected-doxygen - [710/743] run-unit-test(AddressHelper) - [711/743] run-unit-test(Wifi) - ........... - ---------------------------------------------------------------------- - Ran 11 tests in 0.003s - - OK - [712/743] run-unit-test(DcfManager) - [713/743] run-unit-test(MacRxMiddle) - [714/743] run-unit-test(Ipv4ListRouting) + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' + 'build' finished successfully (1.799s) + PASS: TestSuite ns3-wifi-interference + PASS: TestSuite histogram + PASS: TestSuite sample + PASS: TestSuite ipv4-address-helper + PASS: TestSuite devices-wifi + PASS: TestSuite propagation-loss-model ... - [739/743] run-unit-test(RandomVariable) - [740/743] run-unit-test(Object) - [741/743] run-unit-test(Ptr) - [742/743] run-unit-test(Callback) - [743/743] collect-unit-tests-results - C++ UNIT TESTS: all 33 tests passed. - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' - 'build' finished successfully (1.799s) + PASS: TestSuite attributes + PASS: TestSuite config + PASS: TestSuite global-value + PASS: TestSuite command-line + PASS: TestSuite basic-random-number + PASS: TestSuite object + PASS: TestSuite random-number-generators + 47 of 47 tests passed (47 passed, 0 failed, 0 crashed, 0 valgrind errors) @end verbatim This command is typically run by @code{users} to quickly verify that an @@ -519,7 +513,7 @@ known-good reference output files. You downloaded these reference traces to your machine during the @code{./download.py} process above. (Warning: The @code{ns-3.2} and @code{ns-3.3} releases do not use the @code{ns-3-allinone} environment and require you to be online when you run regression tests because -hey dynamically synchronize the reference traces directory with an online +they dynamically synchronize the reference traces directory with an online repository immediately prior to the run). During regression testing Waf will run a number of tests that generate what we @@ -547,7 +541,7 @@ You should see messages indicating that many tests are being run and are passing. @verbatim - Entering directory `repos/ns-3-allinone/ns-3-dev/build' + Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' [647/669] regression-test (test-csma-bridge) [648/669] regression-test (test-csma-broadcast) [649/669] regression-test (test-csma-multicast) @@ -558,7 +552,7 @@ passing. ... Regression testing summary: PASS: 22 of 22 tests passed - Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-3.5-tutorial/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build' finished successfully (25.826s) @end verbatim From 87d0e09bc7705cd5dd621b36ebe33c1e6504e675 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 20:52:19 -0700 Subject: [PATCH 19/63] Separate the index --- doc/tutorial/tutorial.texi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/tutorial/tutorial.texi b/doc/tutorial/tutorial.texi index e445fed1e..6678f207f 100644 --- a/doc/tutorial/tutorial.texi +++ b/doc/tutorial/tutorial.texi @@ -85,6 +85,7 @@ see @uref{http://www.nsnam.org/docs/tutorial.pdf}. * Tweaking ns-3:: * Building Topologies:: * The Tracing System:: +* Index:: @end menu @include introduction.texi @@ -94,6 +95,8 @@ see @uref{http://www.nsnam.org/docs/tutorial.pdf}. @include building-topologies.texi @include tracing.texi +@node Index +@unnumbered Index @printindex cp @bye From ea723b250363b5ba60b132d3e17f50044702c2a4 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 21:18:24 -0700 Subject: [PATCH 20/63] update CHANGES.html and RELEASE_NOTES for release --- CHANGES.html | 15 +++++++++++++++ RELEASE_NOTES | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/CHANGES.html b/CHANGES.html index c8ae3b493..fb086506d 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -48,6 +48,13 @@ us a note on ns-developers mailing list.

Changes to build system:

    +
  • A new test framework is provided with ns-3.6 that primarilay runs outside waf +

    "./waf check" now runs the new unit tests of the core part of ns-3.6. +In order to run the complete test package, use "./test.py" which is +documented in a new manual -- find it in ./doc/testing. "./waf check" +no longer generates the introspected Doxygen. Now use "./waf doxygen" +to do this and generate the Doxygen documentation in one step. +

New API:

@@ -124,6 +131,14 @@ router solicitation, DAD

+
  • New Test Framework +

    Add TestCase, TestSuite classes +

      +
    • examples: src/core/names-test-suite.cc, src/core/random-number-test-suite.cc, src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc +
    +

    +
  • +

    Changes to existing API:

    diff --git a/RELEASE_NOTES b/RELEASE_NOTES index bf7d4bb52..8138cd2ee 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -63,6 +63,11 @@ New user-visible features - Ipv4NixVectorHelper - Examples (nix-simple, nms-p2p-nix) + e) New Test Framework + - Use test.py instead of ./waf check or ./waf --regression + - Previous unit tests have been ported to new framework. + - Examples are tested for run-ability. + API changes from ns-3.5 ----------------------- API changes for this release are documented in the file CHANGES.html. From 2d135d4f85a88d2d268debed4aab39b0fca9e433 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 21:28:50 -0700 Subject: [PATCH 21/63] Update release steps --- doc/release_steps.txt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/release_steps.txt b/doc/release_steps.txt index d46761fe6..a10ebfc83 100644 --- a/doc/release_steps.txt +++ b/doc/release_steps.txt @@ -1,13 +1,18 @@ Steps in doing an ns-3 release 1. check out a clean ns-3-dev somewhere + - hg clone http://code.nsnam.org/ns-3-allinone + - ./download.py + - ./build.py + - confirm that the release builds cleanly. + - ensure that tests pass (./test.py) + - ensure no regressions (./waf --regression) 2. prepare the source files - revise and check in AUTHORS, if needed - revise and check in RELEASE_NOTES - DO NOT change VERSION at this time - confirm that Doxygen builds cleanly and without warnings - (./waf --check; ./waf --doxygen), and check in any necessary changes - - ensure no regressions (./waf --regression) + (./waf doxygen), and check in any necessary changes 3. ./waf configure; ./waf dist - this will create an ns-3-dev.tar.bz2 tarball - this will also create a ns-3-dev-ref-traces.tar.bz2 tarball @@ -34,13 +39,17 @@ Steps in doing an ns-3 release for the regression tests to work. - hg commit - hg push -9. Run the regression tests on the new release (debug and optimized) +9. Run the tests on the new release (debug and optimized) - ./waf -d debug configure - ./waf + - ./test.py + - ./test.py -g - ./waf --regression - ./waf --valgrind --regression (for valgrind version) - ./waf -d optimized configure - ./waf + - ./test.py + - ./test.py -g - ./waf --regression - ./waf --valgrind --regression (for valgrind version) - There should be no regression errors at this time From 9911779ab67dc12a4166d288173e50c80e8fb31d Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 21:29:30 -0700 Subject: [PATCH 22/63] Added tag ns-3.6-RC1 for changeset 549243b47311 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 164114709..e48fa8b35 100644 --- a/.hgtags +++ b/.hgtags @@ -36,3 +36,4 @@ dfd0bc16dc991313896f351530a3dc5a25f62e15 ns-3.3-RC4 8562a42accf6f715d312c037326ec7da48095e13 ns-3.5-rc2 a600c11ff8d40a40e88c2d692acad6512dde70c8 ns-3.5-rc3 c975274c9707b1f07d94cc51f205c351122131a5 ns-3.5 +549243b47311211975b388cd64fcb9111caa2fc2 ns-3.6-RC1 From 96003f4dd8e6af375f73cc194c7104028d0389c4 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 21:49:47 -0700 Subject: [PATCH 23/63] release steps tweaks --- doc/release_steps.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/release_steps.txt b/doc/release_steps.txt index a10ebfc83..64f374956 100644 --- a/doc/release_steps.txt +++ b/doc/release_steps.txt @@ -39,7 +39,10 @@ Steps in doing an ns-3 release for the regression tests to work. - hg commit - hg push -9. Run the tests on the new release (debug and optimized) +9. Run the tests on the new release (debug and optimized) like a user would + - hg clone http://code.nsnam.org/ns-3-allinone-ns-3.x + - ./download.py -n ns-3.x -r ns-3.x-ref-traces + - ./build.py - ./waf -d debug configure - ./waf - ./test.py From f516e4b664bbd6a870a53eeb6e788427e84d84d8 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Fri, 9 Oct 2009 23:51:23 -0700 Subject: [PATCH 24/63] tweak release steps --- doc/release_steps.txt | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/doc/release_steps.txt b/doc/release_steps.txt index 64f374956..1b2018726 100644 --- a/doc/release_steps.txt +++ b/doc/release_steps.txt @@ -35,14 +35,16 @@ Steps in doing an ns-3 release 7. check out a clean version of the new release (ns-3.x) somewhere 8. Update the VERSION for this new release - change the string 3-dev in the VERSION file to the real version - (e.g. 3.2) This must agree with the version name you chose in the clone + (e.g. 3.7 or 3.7-RC1) This must agree with the version name you chose in the clone for the regression tests to work. - hg commit - hg push 9. Run the tests on the new release (debug and optimized) like a user would + You need to use ns-3-allinone since you will use that to make the distro - hg clone http://code.nsnam.org/ns-3-allinone-ns-3.x - ./download.py -n ns-3.x -r ns-3.x-ref-traces - ./build.py + - cd ns-3.x - ./waf -d debug configure - ./waf - ./test.py @@ -57,15 +59,19 @@ Steps in doing an ns-3 release - ./waf --valgrind --regression (for valgrind version) - There should be no regression errors at this time 10. Create final tarballs + - cd .. + - ./dist.py - ./waf configure; ./waf dist - - this will create an ns-3.x.tar.bz2 tarball - - this will also create a ns-3.x-ref-traces.tar.bz2 tarball -11. upload "ns-3.x.tar.bz2" to the /var/www/html/releases/ directory on + - this will create an ns-allinone-3.x.tar.bz2 tarball +11. upload "ns-allinone-3.x.tar.bz2" to the /var/www/html/releases/ directory on the www.nsnam.org server - - give it 644 file permissions, and user/group = apache -12. upload "ns-3.x-ref-traces.tar.bz2" to the /var/www/html/releases/ - directory on the www.nsnam.org server - - give it 644 file permissions, and user/group = apache + - scp ns-allinone-3.x.tar.bz2 www.nsnam.org:~ + - ssh www.nsnam.org + - sudo cp ns-allinone-3.x.tar.bz2 /var/www/html/releases + - cd !$ +12. give it 644 file permissions, and user/group = apache if it is not already + - sudo chown apache:apache ns-allinone-3.x.tar.bz2 + - sudo chmod 644 ns-allinone-3.x.tar.bz2 13. update web pages on www.nsnam.org (source is in the www/ module) - clone the source repo (hg clone http://code.nsnam.org/www) - update index.html From 5516d489094dc3c84f345d0b1cc739e2336c1dee Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Mon, 12 Oct 2009 14:01:36 -0700 Subject: [PATCH 25/63] refalgamize temporary files in test.py --- .hgignore | 1 + doc/testing/testing-framework.texi | 43 +++++++++++++++++++ test.py | 66 +++++++++++++++--------------- 3 files changed, 78 insertions(+), 32 deletions(-) diff --git a/.hgignore b/.hgignore index f36485021..abf6afe19 100644 --- a/.hgignore +++ b/.hgignore @@ -4,6 +4,7 @@ ~$ ^build-dir ^build +^testpy-output ^doc/html ^doc/latex ^\.lock-wscript diff --git a/doc/testing/testing-framework.texi b/doc/testing/testing-framework.texi index ce954331b..b7a98e0bc 100644 --- a/doc/testing/testing-framework.texi +++ b/doc/testing/testing-framework.texi @@ -107,6 +107,37 @@ This mode is indented to be used by users who are interested in determining if their distribution is working correctly, and by developers who are interested in determining if changes they have made have caused any regressions. +There are a number of options available to control the behavir of @code{test.py}. +if you run @code{test.py --help} you should see a command summary like: + +@verbatim + Usage: test.py [options] + + Options: + -h, --help show this help message and exit + -c KIND, --constrain=KIND + constrain the test-runner by kind of test + -e EXAMPLE, --example=EXAMPLE + specify a single example to run + -g, --grind run the test suites and examples using valgrind + -k, --kinds print the kinds of tests available + -l, --list print the list of known tests + -m, --multiple report multiple failures from test suites and test + cases + -n, --nowaf do not run waf before starting testing + -s TEST-SUITE, --suite=TEST-SUITE + specify a single test suite to run + -v, --verbose print progress and informational messages + -w HTML-FILE, --web=HTML-FILE, --html=HTML-FILE + write detailed test results into HTML-FILE.html + -r, --retain retain all temporary files (which are normally + deleted) + -t TEXT-FILE, --text=TEXT-FILE + write detailed test results into TEXT-FILE.txt + -x XML-FILE, --xml=XML-FILE + write detailed test results into XML-FILE.xml +@end verbatim + If one specifies an optional output style, one can generate detailed descriptions of the tests and status. Available styles are @command{text} and @command{HTML}. The buildbots will select the HTML option to generate HTML test reports for the @@ -324,6 +355,18 @@ leaks. This can be selected by using the @code{--grind} option. ./test.py --grind @end verbatim +As it runs, @code{test.py} and the programs that it runs indirectly, generate large +numbers of temporary files. Usually, the content of these files is not interesting, +however in some cases it can be useful (for debugging purposes) to view these files. +@code{test.py} provides a @command{--retain} option which will cause these temporary +files to be kept after the run is completed. The files are saved in a directory +named @code{testpy} under a subdirectory named according to the current Coordinated +Universal Time (also known as Greenwich Mean Time). + +@verbatim + ./test.py --retain +@end verbatim + Finally, @code{test.py} provides a @command{--verbose} option which will print large amounts of information about its progress. It is not expected that this will be terribly useful unless there is an error. In this case, you can get diff --git a/test.py b/test.py index e6d64fc65..d8f2577fe 100755 --- a/test.py +++ b/test.py @@ -19,12 +19,12 @@ import os import sys +import time import optparse import subprocess import threading import Queue import signal -import random import xml.dom.minidom import shutil @@ -148,13 +148,6 @@ example_tests = [ ("wireless/wifi-wired-bridging", "True", "True"), ] -# -# Most of the examples produce gangs of trace files, so we want to find -# somewhere to put them that won't pollute the current directory. One -# obvious place is somewhere in /tmp. -# -TMP_TRACES_DIR = "/tmp/unchecked-traces" - # # The test suites are going to want to output status. They are running # concurrently. This means that unless we are careful, the output of @@ -162,9 +155,12 @@ TMP_TRACES_DIR = "/tmp/unchecked-traces" # file that could unintentionally start serializing execution, we ask # the tests to write their output to a temporary directory and then # put together the final output file when we "join" the test tasks back -# to the main thread. +# to the main thread. In addition to this issue, the example programs +# often write lots and lots of trace files which we will just ignore. +# We put all of them into the temp directory as well, so they can be +# easily deleted. # -TMP_OUTPUT_DIR = "/tmp/testpy" +TMP_OUTPUT_DIR = "testpy-output" def get_node_text(node): for child in node.childNodes: @@ -652,10 +648,7 @@ class Job(): # This is the temporary results file name that will be given to an executing # test as it is being run. We will be running all of our tests in parallel # so there must be multiple temporary output files. These will be collected - # into a single XML file at the end and then be deleted. The file names are - # just giant random numbers, for example - # - # "/tmp/testpy/5437925246732857" + # into a single XML file at the end and then be deleted. # def set_tmp_file_name(self, tmp_file_name): self.tmp_file_name = tmp_file_name @@ -808,26 +801,30 @@ def run_tests(): # finds a problem, or HTML for nightly builds. In these cases, an # XML file is written containing the status messages from the test suites. # This file is then read and translated into text or HTML. It is expected - # that nobody will really be interested in the XML, so we write it to - # somewhere in /tmp with a random name to avoid collisions. Just in case - # some strange once-in-a-lifetime error occurs, we always write the info - # so it can be found, we just may not use it. + # that nobody will really be interested in the XML, so we write it somewhere + # with a unique name (time) to avoid collisions. In case an error happens, we + # provide a runtime option to retain the temporary files. # # When we run examples as smoke tests, they are going to want to create # lots and lots of trace files. We aren't really interested in the contents - # of the trace files, so we also just stash them off in /tmp somewhere. + # of the trace files, so we also just stash them off in the temporary dir. + # The retain option also causes these unchecked trace files to be kept. # + date_and_time = time.strftime("%Y-%m-%d-%H-%M-%S-CUT", time.gmtime()) + if not os.path.exists(TMP_OUTPUT_DIR): os.makedirs(TMP_OUTPUT_DIR) - if not os.path.exists(TMP_TRACES_DIR): - os.makedirs(TMP_TRACES_DIR) + testpy_output_dir = os.path.join(TMP_OUTPUT_DIR, date_and_time); + + if not os.path.exists(testpy_output_dir): + os.makedirs(testpy_output_dir) # # Create the main output file and start filling it with XML. We need to # do this since the tests will just append individual results to this file. # - xml_results_file = TMP_OUTPUT_DIR + "%d.xml" % random.randint(0, sys.maxint) + xml_results_file = os.path.join(testpy_output_dir, "results.xml") f = open(xml_results_file, 'w') f.write('\n') f.write('\n') @@ -929,7 +926,7 @@ def run_tests(): job = Job() job.set_is_example(False) job.set_display_name(test) - job.set_tmp_file_name(TMP_OUTPUT_DIR + "%d" % random.randint(0, sys.maxint)) + job.set_tmp_file_name(os.path.join(testpy_output_dir, "%s.xml" % test)) job.set_cwd(os.getcwd()) job.set_basedir(os.getcwd()) if (options.multiple): @@ -999,7 +996,7 @@ def run_tests(): job.set_is_example(True) job.set_display_name(test) job.set_tmp_file_name("") - job.set_cwd(TMP_TRACES_DIR) + job.set_cwd(testpy_output_dir) job.set_basedir(os.getcwd()) job.set_shell_command("examples/%s" % test) @@ -1022,7 +1019,7 @@ def run_tests(): job.set_is_example(True) job.set_display_name(options.example) job.set_tmp_file_name("") - job.set_cwd(TMP_TRACES_DIR) + job.set_cwd(testpy_output_dir) job.set_basedir(os.getcwd()) job.set_shell_command("examples/%s" % options.example) @@ -1192,11 +1189,6 @@ def run_tests(): f.write("\n") f.close() - try: - os.remove(job.tmp_file_name) - except: - pass - # # We have all of the tests run and the results written out. One final # bit of housekeeping is to wait for all of the threads to close down @@ -1233,14 +1225,21 @@ def run_tests(): if len(options.xml): shutil.copyfile(xml_results_file, options.xml) + # + # If we have been asked to retain all of the little temporary files, we + # don't delete tm. If we do delete the temporary files, delete only the + # directory we just created. We don't want to happily delete any retained + # directories, which will probably surprise the user. + # + if not options.retain: + shutil.rmtree(testpy_output_dir) + if passed_tests + skipped_tests == total_tests: return 0 # success else: return 1 # catchall for general errors def main(argv): - random.seed() - parser = optparse.OptionParser() parser.add_option("-c", "--constrain", action="store", type="string", dest="constrain", default="", metavar="KIND", @@ -1276,6 +1275,9 @@ def main(argv): metavar="HTML-FILE", help="write detailed test results into HTML-FILE.html") + parser.add_option("-r", "--retain", action="store_true", dest="retain", default=False, + help="retain all temporary files (which are normally deleted)") + parser.add_option("-t", "--text", action="store", type="string", dest="text", default="", metavar="TEXT-FILE", help="write detailed test results into TEXT-FILE.txt") From 878ce49230bbb462d76c0d4e10c70dcb7f751c01 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Mon, 12 Oct 2009 23:31:58 -0700 Subject: [PATCH 26/63] fix valgrind complaints (bug 711) --- src/devices/mesh/dot11s/hwmp-protocol-mac.cc | 7 ++++++- src/devices/mesh/dot11s/hwmp-protocol.cc | 7 ++++++- src/devices/wifi/interference-helper.cc | 16 +++++++++++++++- src/devices/wifi/interference-helper.h | 2 ++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/devices/mesh/dot11s/hwmp-protocol-mac.cc b/src/devices/mesh/dot11s/hwmp-protocol-mac.cc index 3434c84c9..ca65e5ed8 100644 --- a/src/devices/mesh/dot11s/hwmp-protocol-mac.cc +++ b/src/devices/mesh/dot11s/hwmp-protocol-mac.cc @@ -357,7 +357,12 @@ HwmpProtocolMac::ForwardPerr (std::vector faile //Send Management frame for (std::vector::const_iterator i = receivers.begin (); i != receivers.end (); i++) { - hdr.SetAddr1 (*i); + // + // 64-bit Intel valgrind complains about hdr.SetAddr1 (*i). It likes this + // just fine. + // + Mac48Address address = *i; + hdr.SetAddr1 (address); m_stats.txPerr++; m_stats.txMgt++; m_stats.txMgtBytes += packet->GetSize (); diff --git a/src/devices/mesh/dot11s/hwmp-protocol.cc b/src/devices/mesh/dot11s/hwmp-protocol.cc index 2187a9cc0..4a9012d61 100644 --- a/src/devices/mesh/dot11s/hwmp-protocol.cc +++ b/src/devices/mesh/dot11s/hwmp-protocol.cc @@ -274,7 +274,12 @@ HwmpProtocol::RequestRoute ( for (std::vector::const_iterator i = receivers.begin (); i != receivers.end (); i ++) { Ptr packetCopy = packet->Copy(); - tag.SetAddress (*i); + // + // 64-bit Intel valgrind complains about tag.SetAddress (*i). It + // likes this just fine. + // + Mac48Address address = *i; + tag.SetAddress (address); packetCopy->AddPacketTag (tag); routeReply (true, packetCopy, source, destination, protocolType, plugin->first); } diff --git a/src/devices/wifi/interference-helper.cc b/src/devices/wifi/interference-helper.cc index 1a41e588b..221dad65b 100644 --- a/src/devices/wifi/interference-helper.cc +++ b/src/devices/wifi/interference-helper.cc @@ -428,7 +428,7 @@ InterferenceHelper::AppendEvent (Ptr event) { i++; } - m_events.erase (m_events.begin (), i); + EraseEvents (m_events.begin (), i); } m_events.push_back (event); } @@ -606,7 +606,21 @@ InterferenceHelper::CalculateSnrPer (Ptr event) void InterferenceHelper::EraseEvents (void) { + for (Events::iterator i = m_events.begin (); i != m_events.end (); ++i) + { + *i = 0; + } m_events.erase (m_events.begin (), m_events.end ()); } +void +InterferenceHelper::EraseEvents (Events::iterator start, Events::iterator end) +{ + for (Events::iterator i = start; i != end; ++i) + { + *i = 0; + } + m_events.erase (start, end); +} + } // namespace ns3 diff --git a/src/devices/wifi/interference-helper.h b/src/devices/wifi/interference-helper.h index 0ae37b280..20caa6472 100644 --- a/src/devices/wifi/interference-helper.h +++ b/src/devices/wifi/interference-helper.h @@ -110,6 +110,8 @@ private: typedef std::vector NiChanges; typedef std::list > Events; + void EraseEvents (Events::iterator start, Events::iterator end); + InterferenceHelper (const InterferenceHelper &o); InterferenceHelper &operator = (const InterferenceHelper &o); void AppendEvent (Ptr event); From 7473705ef041cdfb235944d671509d57c622ee8a Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Tue, 13 Oct 2009 00:21:39 -0700 Subject: [PATCH 27/63] add leak-check=full to test.py to get more info (bug 711) --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index d8f2577fe..35b83ce7d 100755 --- a/test.py +++ b/test.py @@ -554,7 +554,7 @@ def make_library_path(): def run_job_synchronously(shell_command, directory, valgrind): if valgrind: - cmd = "%s valgrind --error-exitcode=2 %s/%s/%s" % (LIBRARY_PATH, NS3_BUILDDIR, NS3_ACTIVE_VARIANT, shell_command) + cmd = "%s valgrind --leak-check=full --error-exitcode=2 %s/%s/%s" % (LIBRARY_PATH, NS3_BUILDDIR, NS3_ACTIVE_VARIANT, shell_command) else: cmd = "%s %s/%s/%s" % (LIBRARY_PATH, NS3_BUILDDIR, NS3_ACTIVE_VARIANT, shell_command) From 255b29974a53da4fbe2c55bbebba154a97938ace Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Tue, 13 Oct 2009 10:23:21 -0700 Subject: [PATCH 28/63] Stray () in test.py (bug 718) --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index 35b83ce7d..ddfd39d09 100755 --- a/test.py +++ b/test.py @@ -569,7 +569,7 @@ def run_job_synchronously(shell_command, directory, valgrind): # This class defines a unit of testing work. It will typically refer to # a test suite to run using the test-runner, or an example to run directly. # -class Job(): +class Job: def __init__(self): self.is_break = False self.is_skip = False From 88cfec21eb8b1ab3acbc5125aa4587ba99243d50 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Tue, 13 Oct 2009 12:51:46 -0700 Subject: [PATCH 29/63] release Ptrs in mesh-point-device --- src/devices/mesh/mesh-point-device.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/devices/mesh/mesh-point-device.cc b/src/devices/mesh/mesh-point-device.cc index 5c4dba64f..e25bc60b5 100644 --- a/src/devices/mesh/mesh-point-device.cc +++ b/src/devices/mesh/mesh-point-device.cc @@ -59,6 +59,9 @@ MeshPointDevice::MeshPointDevice () : MeshPointDevice::~MeshPointDevice () { NS_LOG_FUNCTION_NOARGS (); + m_node = 0; + m_channel = 0; + m_routingProtocol = 0; } void @@ -70,7 +73,6 @@ MeshPointDevice::DoDispose () *iter = 0; } m_ifaces.clear (); - m_node = 0; NetDevice::DoDispose (); } From 64450e03cd750326029cb681bd4b2dfb2e49ac1d Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Tue, 13 Oct 2009 22:41:30 -0700 Subject: [PATCH 30/63] plug leaks (bug 711) --- src/devices/bridge/bridge-channel.cc | 8 ++++++++ src/devices/mesh/dot11s/peer-management-protocol.cc | 1 + src/devices/mesh/mesh-l2-routing-protocol.cc | 1 + src/devices/mesh/mesh-wifi-interface-mac.cc | 4 ++++ src/devices/mesh/wifi-information-element-vector.cc | 5 +++++ src/devices/wifi/interference-helper.cc | 1 + src/helper/mesh-helper.cc | 4 ++++ src/helper/mesh-helper.h | 5 +++++ 8 files changed, 29 insertions(+) diff --git a/src/devices/bridge/bridge-channel.cc b/src/devices/bridge/bridge-channel.cc index 26ec993c9..7029c5251 100644 --- a/src/devices/bridge/bridge-channel.cc +++ b/src/devices/bridge/bridge-channel.cc @@ -44,8 +44,16 @@ BridgeChannel::BridgeChannel () BridgeChannel::~BridgeChannel () { NS_LOG_FUNCTION_NOARGS (); + + for (std::vector< Ptr >::iterator iter = m_bridgedChannels.begin (); + iter != m_bridgedChannels.end (); iter++) + { + *iter = 0; + } + m_bridgedChannels.clear (); } + void BridgeChannel::AddChannel (Ptr bridgedChannel) { diff --git a/src/devices/mesh/dot11s/peer-management-protocol.cc b/src/devices/mesh/dot11s/peer-management-protocol.cc index cff61506a..415fa8f69 100644 --- a/src/devices/mesh/dot11s/peer-management-protocol.cc +++ b/src/devices/mesh/dot11s/peer-management-protocol.cc @@ -71,6 +71,7 @@ PeerManagementProtocol::PeerManagementProtocol () : } PeerManagementProtocol::~PeerManagementProtocol () { + m_meshId = 0; } void PeerManagementProtocol::DoDispose () diff --git a/src/devices/mesh/mesh-l2-routing-protocol.cc b/src/devices/mesh/mesh-l2-routing-protocol.cc index e4f242fbf..176ea222c 100644 --- a/src/devices/mesh/mesh-l2-routing-protocol.cc +++ b/src/devices/mesh/mesh-l2-routing-protocol.cc @@ -39,6 +39,7 @@ MeshL2RoutingProtocol::GetTypeId (void) MeshL2RoutingProtocol::~MeshL2RoutingProtocol () { + m_mp = 0; } void diff --git a/src/devices/mesh/mesh-wifi-interface-mac.cc b/src/devices/mesh/mesh-wifi-interface-mac.cc index de97fd912..247947318 100644 --- a/src/devices/mesh/mesh-wifi-interface-mac.cc +++ b/src/devices/mesh/mesh-wifi-interface-mac.cc @@ -109,6 +109,10 @@ MeshWifiInterfaceMac::MeshWifiInterfaceMac () MeshWifiInterfaceMac::~MeshWifiInterfaceMac () { NS_LOG_FUNCTION (this); + m_beaconDca = 0; + m_stationManager = 0; + m_phy = 0; + m_low = 0; } //----------------------------------------------------------------------------- // WifiMac inherited diff --git a/src/devices/mesh/wifi-information-element-vector.cc b/src/devices/mesh/wifi-information-element-vector.cc index f44e0e646..9c3dbb641 100644 --- a/src/devices/mesh/wifi-information-element-vector.cc +++ b/src/devices/mesh/wifi-information-element-vector.cc @@ -48,6 +48,11 @@ WifiInformationElementVector::WifiInformationElementVector () : } WifiInformationElementVector::~WifiInformationElementVector () { + for (IE_VECTOR::iterator i = m_elements.begin (); i != m_elements.end (); i++) + { + *i = 0; + } + m_elements.clear (); } TypeId WifiInformationElementVector::GetTypeId () diff --git a/src/devices/wifi/interference-helper.cc b/src/devices/wifi/interference-helper.cc index 221dad65b..b85183f1d 100644 --- a/src/devices/wifi/interference-helper.cc +++ b/src/devices/wifi/interference-helper.cc @@ -128,6 +128,7 @@ InterferenceHelper::InterferenceHelper () {} InterferenceHelper::~InterferenceHelper () { + EraseEvents (); m_errorRateModel = 0; } diff --git a/src/helper/mesh-helper.cc b/src/helper/mesh-helper.cc index 876afb00f..2c4aa7a5d 100644 --- a/src/helper/mesh-helper.cc +++ b/src/helper/mesh-helper.cc @@ -32,6 +32,10 @@ MeshHelper::MeshHelper () : m_standard (WIFI_PHY_STANDARD_80211a) { } +MeshHelper::~MeshHelper () +{ + m_stack = 0; +} void MeshHelper::SetSpreadInterfaceChannels (enum ChannelPolicy policy) { diff --git a/src/helper/mesh-helper.h b/src/helper/mesh-helper.h index 1a343988b..31aca274e 100644 --- a/src/helper/mesh-helper.h +++ b/src/helper/mesh-helper.h @@ -43,6 +43,11 @@ public: */ MeshHelper (); + /** + * Destroy a MeshHelper. + */ + ~MeshHelper (); + /** * \brief Set the helper to the default values for the MAC type, remote * station manager and channel policy. From 4fb0057dc7248416df34835775a9d29dbdb9c48c Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Wed, 14 Oct 2009 10:24:55 -0700 Subject: [PATCH 31/63] Added tag ns-3.6-RC2 for changeset 899604299046 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index e48fa8b35..a02094d83 100644 --- a/.hgtags +++ b/.hgtags @@ -37,3 +37,4 @@ dfd0bc16dc991313896f351530a3dc5a25f62e15 ns-3.3-RC4 a600c11ff8d40a40e88c2d692acad6512dde70c8 ns-3.5-rc3 c975274c9707b1f07d94cc51f205c351122131a5 ns-3.5 549243b47311211975b388cd64fcb9111caa2fc2 ns-3.6-RC1 +8996042990466b1eda718a848e1c02923c0add74 ns-3.6-RC2 From 4eba1f033e74508bbee1c638bc187b71cbcecb71 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Wed, 14 Oct 2009 11:20:49 -0700 Subject: [PATCH 32/63] tweak release steps --- doc/release_steps.txt | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/doc/release_steps.txt b/doc/release_steps.txt index 1b2018726..e7536fe7c 100644 --- a/doc/release_steps.txt +++ b/doc/release_steps.txt @@ -1,10 +1,11 @@ Steps in doing an ns-3 release -1. check out a clean ns-3-dev somewhere +1. check out a clean ns-3-dev somewhere using ns-3-allinone (you will need it) - hg clone http://code.nsnam.org/ns-3-allinone - ./download.py - ./build.py - confirm that the release builds cleanly. + - cd ns-3-dev - ensure that tests pass (./test.py) - ensure no regressions (./waf --regression) 2. prepare the source files @@ -13,19 +14,23 @@ Steps in doing an ns-3 release - DO NOT change VERSION at this time - confirm that Doxygen builds cleanly and without warnings (./waf doxygen), and check in any necessary changes -3. ./waf configure; ./waf dist - - this will create an ns-3-dev.tar.bz2 tarball - - this will also create a ns-3-dev-ref-traces.tar.bz2 tarball -4. test dev tarball on release platforms (waf --check and maybe some other - scripts) +3. build an ns-3-allinone distribution + - change back into the allinone directory + - ./dist.py + - this will create an ns-allinone-dev.tar.bz2 tarball +4. test dev tarball on release platforms + - ./test.py + - ./waf --regression + - other scripts you can think of 5. once you are happy with the tarball, tag ns-3-dev and ns-3-dev-ref-traces + - cd into ns-3-dev - hg tag "ns-3.x" - hg push - - cd into regression/ns-3-dev-ref-traces + - cd into ns-3-dev-ref-traces - hg tag "ns-3.x" - hg push 6. clone the tagged ns-3-dev and place it on the repository - - ssh code.nsnam.org; sudo tcsh; su code; + - ssh code.nsnam.org; sudo bash; su code; - cp -r /home/code/repos/ns-3-dev /home/code/repos/ns-3.1x - cd /home/code/repos/ns-3.x/.hg and edit the hgrc appropriately: "description = ns-3.x release @@ -59,9 +64,8 @@ Steps in doing an ns-3 release - ./waf --valgrind --regression (for valgrind version) - There should be no regression errors at this time 10. Create final tarballs - - cd .. + - cd into ns-3-allinone level - ./dist.py - - ./waf configure; ./waf dist - this will create an ns-allinone-3.x.tar.bz2 tarball 11. upload "ns-allinone-3.x.tar.bz2" to the /var/www/html/releases/ directory on the www.nsnam.org server @@ -74,23 +78,25 @@ Steps in doing an ns-3 release - sudo chmod 644 ns-allinone-3.x.tar.bz2 13. update web pages on www.nsnam.org (source is in the www/ module) - clone the source repo (hg clone http://code.nsnam.org/www) - - update index.html - - add link to news.html - - update getting_started.html - - update documents.html + - update references to releases in html_src + (consider "grep 'ns-3\.' *.html" for a new release) + (consider "grep 'RCx' *.html" for a new RC) + - update references to releases in scripts/ + - update roadmap on wiki - commit and push changes +14. update the server - build and update HTML directory on the server - -- ssh www.nsnam.org; sudo tcsh; su nsnam; + -- ssh www.nsnam.org; sudo bash; su nsnam; -- run ~/bin/update-html - build and update Doxygen directory on the server -- edit ~/bin/update-doxygen-release file and change RELEASE variable to the right version number -- run ~/bin/update-doxygen-release -14. Final checks +15. Final checks - download tarball from web, build and run regression tests for as many targets as you can - download release from mercurial, build and run regression tests for as many targets as you can - test and verify until you're confident the release is solid. -15. announce to ns-developers, with summary of release notes +16. announce to ns-developers, with summary of release notes From ac9dc12e945c48860e9fa1abe4a88ebd91b0d830 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 15 Oct 2009 10:53:31 -0700 Subject: [PATCH 33/63] add flowmon to RELEASE_NOTES --- RELEASE_NOTES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 8138cd2ee..6eed4584b 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -68,6 +68,10 @@ New user-visible features - Previous unit tests have been ported to new framework. - Examples are tested for run-ability. + f) A new Flow Monitor module + - To very easily measure flow metrics in a simulation + - No need to use trace callbacks or parsing trace files + API changes from ns-3.5 ----------------------- API changes for this release are documented in the file CHANGES.html. From af27a1388c4e71c33263a3f5e133775f98072709 Mon Sep 17 00:00:00 2001 From: Andrey Mazo Date: Thu, 15 Oct 2009 13:49:42 +0400 Subject: [PATCH 34/63] add NS_LOG_FUNCTION to several constructors/destructors/DoDisposes --- src/devices/mesh/dot11s/hwmp-protocol.cc | 3 +++ src/devices/wifi/wifi-net-device.cc | 12 ++++++++++-- src/devices/wifi/yans-wifi-channel.cc | 1 + src/node/net-device.cc | 4 +++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/devices/mesh/dot11s/hwmp-protocol.cc b/src/devices/mesh/dot11s/hwmp-protocol.cc index 4a9012d61..f1ef24f43 100644 --- a/src/devices/mesh/dot11s/hwmp-protocol.cc +++ b/src/devices/mesh/dot11s/hwmp-protocol.cc @@ -184,6 +184,7 @@ HwmpProtocol::HwmpProtocol (): m_doFlag (false), m_rfFlag (false) { + NS_LOG_FUNCTION_NOARGS (); if (m_isRoot) { @@ -193,11 +194,13 @@ HwmpProtocol::HwmpProtocol (): HwmpProtocol::~HwmpProtocol () { + NS_LOG_FUNCTION_NOARGS (); } void HwmpProtocol::DoDispose () { + NS_LOG_FUNCTION_NOARGS (); for (std::map::iterator i = m_preqTimeouts.begin (); i != m_preqTimeouts.end (); i ++) { i->second.Cancel (); diff --git a/src/devices/wifi/wifi-net-device.cc b/src/devices/wifi/wifi-net-device.cc index e155ce752..8d34f5b6c 100644 --- a/src/devices/wifi/wifi-net-device.cc +++ b/src/devices/wifi/wifi-net-device.cc @@ -28,6 +28,9 @@ #include "ns3/pointer.h" #include "ns3/node.h" #include "ns3/trace-source-accessor.h" +#include "ns3/log.h" + +NS_LOG_COMPONENT_DEFINE ("WifiNetDevice"); namespace ns3 { @@ -65,13 +68,18 @@ WifiNetDevice::GetTypeId (void) WifiNetDevice::WifiNetDevice () : m_mtu (0), m_configComplete (false) -{} +{ + NS_LOG_FUNCTION_NOARGS (); +} WifiNetDevice::~WifiNetDevice () -{} +{ + NS_LOG_FUNCTION_NOARGS (); +} void WifiNetDevice::DoDispose (void) { + NS_LOG_FUNCTION_NOARGS (); m_node = 0; m_mac->Dispose (); m_phy->Dispose (); diff --git a/src/devices/wifi/yans-wifi-channel.cc b/src/devices/wifi/yans-wifi-channel.cc index 52ce25527..fee768abb 100644 --- a/src/devices/wifi/yans-wifi-channel.cc +++ b/src/devices/wifi/yans-wifi-channel.cc @@ -56,6 +56,7 @@ YansWifiChannel::YansWifiChannel () {} YansWifiChannel::~YansWifiChannel () { + NS_LOG_FUNCTION_NOARGS (); m_phyList.clear (); } diff --git a/src/node/net-device.cc b/src/node/net-device.cc index 431a5e5bc..df53b5d21 100644 --- a/src/node/net-device.cc +++ b/src/node/net-device.cc @@ -45,6 +45,8 @@ TypeId NetDevice::GetTypeId (void) } NetDevice::~NetDevice () -{} +{ + NS_LOG_FUNCTION_NOARGS (); +} } // namespace ns3 From b252f68f9dfe500b946f5d624a4878530ccaed54 Mon Sep 17 00:00:00 2001 From: Andrey Mazo Date: Thu, 15 Oct 2009 14:10:02 +0400 Subject: [PATCH 35/63] bug 711: example mesh/mesh fails valgrind. also fix several possible leaks. --- src/devices/mesh/dot11s/hwmp-protocol.cc | 2 ++ src/devices/mesh/dot11s/peer-management-protocol.cc | 2 ++ src/devices/mesh/mesh-point-device.cc | 3 +++ src/devices/mesh/mesh-wifi-interface-mac.cc | 2 ++ src/devices/wifi/yans-wifi-phy.cc | 2 ++ src/helper/dot11s-installer.cc | 5 +++-- src/node/node.cc | 1 + 7 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/devices/mesh/dot11s/hwmp-protocol.cc b/src/devices/mesh/dot11s/hwmp-protocol.cc index f1ef24f43..9b30de4c6 100644 --- a/src/devices/mesh/dot11s/hwmp-protocol.cc +++ b/src/devices/mesh/dot11s/hwmp-protocol.cc @@ -209,8 +209,10 @@ HwmpProtocol::DoDispose () m_preqTimeouts.clear (); m_lastDataSeqno.clear (); m_lastHwmpSeqno.clear (); + m_interfaces.clear (); m_rqueue.clear (); m_rtable = 0; + m_mp = 0; } bool diff --git a/src/devices/mesh/dot11s/peer-management-protocol.cc b/src/devices/mesh/dot11s/peer-management-protocol.cc index 415fa8f69..7445965d0 100644 --- a/src/devices/mesh/dot11s/peer-management-protocol.cc +++ b/src/devices/mesh/dot11s/peer-management-protocol.cc @@ -93,6 +93,8 @@ PeerManagementProtocol::DoDispose () i->second.clear (); } m_neighbourBeacons.clear (); + + m_plugins.clear (); } bool diff --git a/src/devices/mesh/mesh-point-device.cc b/src/devices/mesh/mesh-point-device.cc index e25bc60b5..50b4e9268 100644 --- a/src/devices/mesh/mesh-point-device.cc +++ b/src/devices/mesh/mesh-point-device.cc @@ -73,6 +73,9 @@ MeshPointDevice::DoDispose () *iter = 0; } m_ifaces.clear (); + m_node = 0; + m_channel = 0; + m_routingProtocol = 0; NetDevice::DoDispose (); } diff --git a/src/devices/mesh/mesh-wifi-interface-mac.cc b/src/devices/mesh/mesh-wifi-interface-mac.cc index 247947318..d11f02690 100644 --- a/src/devices/mesh/mesh-wifi-interface-mac.cc +++ b/src/devices/mesh/mesh-wifi-interface-mac.cc @@ -282,8 +282,10 @@ MeshWifiInterfaceMac::DoDispose () m_rxMiddle = 0; m_low = 0; m_dcfManager = 0; + m_stationManager = 0; m_phy = 0; m_queues.clear (); + m_plugins.clear (); m_beaconSendEvent.Cancel (); m_beaconDca = 0; diff --git a/src/devices/wifi/yans-wifi-phy.cc b/src/devices/wifi/yans-wifi-phy.cc index 7efbf6739..82b0f34da 100644 --- a/src/devices/wifi/yans-wifi-phy.cc +++ b/src/devices/wifi/yans-wifi-phy.cc @@ -139,6 +139,8 @@ YansWifiPhy::DoDispose (void) m_channel = 0; m_modes.clear (); m_device = 0; + m_mobility = 0; + m_state = 0; } void diff --git a/src/helper/dot11s-installer.cc b/src/helper/dot11s-installer.cc index 9ed804b0d..fb85888e6 100644 --- a/src/helper/dot11s-installer.cc +++ b/src/helper/dot11s-installer.cc @@ -73,8 +73,9 @@ Dot11sStack::InstallStack (Ptr mp) hwmp->SetRoot (); } //Install interaction between HWMP and Peer management protocol: - pmp->SetPeerLinkStatusCallback (MakeCallback (&HwmpProtocol::PeerLinkStatus, hwmp)); - hwmp->SetNeighboursCallback (MakeCallback (&PeerManagementProtocol::GetActiveLinks, pmp)); + //PeekPointer()'s to avoid circular Ptr references + pmp->SetPeerLinkStatusCallback (MakeCallback (&HwmpProtocol::PeerLinkStatus, PeekPointer (hwmp))); + hwmp->SetNeighboursCallback (MakeCallback (&PeerManagementProtocol::GetActiveLinks, PeekPointer (pmp))); return true; } void diff --git a/src/node/node.cc b/src/node/node.cc index 180f7a060..d1265ec7a 100644 --- a/src/node/node.cc +++ b/src/node/node.cc @@ -148,6 +148,7 @@ Node::GetNApplications (void) const void Node::DoDispose() { + m_handlers.clear (); for (std::vector >::iterator i = m_devices.begin (); i != m_devices.end (); i++) { From c0d01261d094026c474cdbea1a2205b1eedfdc43 Mon Sep 17 00:00:00 2001 From: Andrey Mazo Date: Thu, 15 Oct 2009 14:32:19 +0400 Subject: [PATCH 36/63] remove some redundant clean ups, includes --- src/devices/mesh/dot11s/ie-dot11s-beacon-timing.cc | 8 -------- src/devices/mesh/dot11s/ie-dot11s-preq.cc | 5 ----- src/devices/mesh/mesh-wifi-beacon.h | 2 -- src/devices/mesh/wifi-information-element-vector.cc | 2 +- src/devices/wifi/interference-helper.cc | 2 +- 5 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.cc b/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.cc index a02053ab7..0cde19382 100644 --- a/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.cc +++ b/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.cc @@ -118,19 +118,11 @@ IeBeaconTiming::DelNeighboursTimingElementUnit (uint16_t aid, Time last_beacon, void IeBeaconTiming::ClearTimingElement () { - uint16_t to_delete = 0; - uint16_t i; for (NeighboursTimingUnitsList::iterator j = m_neighbours.begin (); j != m_neighbours.end (); j++) { - to_delete++; (*j) = 0; } - for (i = 0; i < to_delete; i++) - { - m_neighbours.pop_back (); - } m_neighbours.clear (); - } uint8_t IeBeaconTiming::GetInformationSize () const diff --git a/src/devices/mesh/dot11s/ie-dot11s-preq.cc b/src/devices/mesh/dot11s/ie-dot11s-preq.cc index c5d2c1ff9..40e749f06 100644 --- a/src/devices/mesh/dot11s/ie-dot11s-preq.cc +++ b/src/devices/mesh/dot11s/ie-dot11s-preq.cc @@ -373,16 +373,11 @@ IePreq::DelDestinationAddressElement (Mac48Address dest_address) void IePreq::ClearDestinationAddressElements () { - int i; for (std::vector >::iterator j = m_destinations.begin (); j != m_destinations.end (); j++) { (*j) = 0; } - for (i = 0; i < m_destCount; i++) - { - m_destinations.pop_back (); - } m_destinations.clear (); m_destCount = 0; } diff --git a/src/devices/mesh/mesh-wifi-beacon.h b/src/devices/mesh/mesh-wifi-beacon.h index 4ac8afed7..f05d9f483 100644 --- a/src/devices/mesh/mesh-wifi-beacon.h +++ b/src/devices/mesh/mesh-wifi-beacon.h @@ -27,8 +27,6 @@ #include "ns3/wifi-mac-header.h" #include "ns3/wifi-information-element-vector.h" -#include - namespace ns3 { /** diff --git a/src/devices/mesh/wifi-information-element-vector.cc b/src/devices/mesh/wifi-information-element-vector.cc index 9c3dbb641..3531b4b22 100644 --- a/src/devices/mesh/wifi-information-element-vector.cc +++ b/src/devices/mesh/wifi-information-element-vector.cc @@ -74,7 +74,7 @@ WifiInformationElementVector::GetSerializedSize () const void WifiInformationElementVector::Serialize (Buffer::Iterator start) const { - for(std::vector >::const_iterator i = m_elements.begin (); i != m_elements.end (); i ++) + for(IE_VECTOR::const_iterator i = m_elements.begin (); i != m_elements.end (); i ++) { start.WriteU8((*i)->ElementId ()); start.WriteU8 ((*i)->GetInformationSize ()); diff --git a/src/devices/wifi/interference-helper.cc b/src/devices/wifi/interference-helper.cc index b85183f1d..b6a565091 100644 --- a/src/devices/wifi/interference-helper.cc +++ b/src/devices/wifi/interference-helper.cc @@ -611,7 +611,7 @@ InterferenceHelper::EraseEvents (void) { *i = 0; } - m_events.erase (m_events.begin (), m_events.end ()); + m_events.clear (); } void From d0ffc407acaf2f6c82b25e04932ee5b2800c57e0 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 15 Oct 2009 22:44:41 -0700 Subject: [PATCH 37/63] Added tag ns-3.6-RC3 for changeset 79ff6ad1adbb --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index a02094d83..4ee27e8a1 100644 --- a/.hgtags +++ b/.hgtags @@ -38,3 +38,4 @@ a600c11ff8d40a40e88c2d692acad6512dde70c8 ns-3.5-rc3 c975274c9707b1f07d94cc51f205c351122131a5 ns-3.5 549243b47311211975b388cd64fcb9111caa2fc2 ns-3.6-RC1 8996042990466b1eda718a848e1c02923c0add74 ns-3.6-RC2 +79ff6ad1adbb7b4677759ddf52028b68b0515168 ns-3.6-RC3 From 117fa7f3c27a7cee1608f335a8da364169167c07 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Thu, 15 Oct 2009 23:55:06 -0700 Subject: [PATCH 38/63] release steps tweaks --- doc/release_steps.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/release_steps.txt b/doc/release_steps.txt index e7536fe7c..ac329ccb1 100644 --- a/doc/release_steps.txt +++ b/doc/release_steps.txt @@ -38,20 +38,21 @@ Steps in doing an ns-3 release - clone the ns-3-dev-ref-traces and place it on the repository as above but use the name ns-3.x-ref-traces and edit the hgrc appropriately 7. check out a clean version of the new release (ns-3.x) somewhere + - hg clone http://code.nsnam.org/ns-3.x 8. Update the VERSION for this new release - change the string 3-dev in the VERSION file to the real version (e.g. 3.7 or 3.7-RC1) This must agree with the version name you chose in the clone for the regression tests to work. - - hg commit - - hg push + - hg commit -m "update VERSION to ns-3.x" + - hg push ssh://code@code.nsnam.org//home/code/repos/ns-3.x + 9. Run the tests on the new release (debug and optimized) like a user would You need to use ns-3-allinone since you will use that to make the distro - - hg clone http://code.nsnam.org/ns-3-allinone-ns-3.x + - hg clone http://code.nsnam.org/ns-3-allinone ns-3-allinone-3.x + - cd !$ - ./download.py -n ns-3.x -r ns-3.x-ref-traces - ./build.py - cd ns-3.x - - ./waf -d debug configure - - ./waf - ./test.py - ./test.py -g - ./waf --regression From c237cbe114904e34003f23e921d2d71ef170c02c Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Fri, 16 Oct 2009 07:13:05 -0700 Subject: [PATCH 39/63] Manual organization --- doc/manual/animation.texi | 6 + doc/manual/applications.texi | 6 + doc/manual/bridge.texi | 6 + doc/manual/callbacks.texi | 8 +- doc/manual/emu.texi | 384 ++++++++++++++++++++++------------- doc/manual/emulation.texi | 315 ---------------------------- doc/manual/flow-monitor.texi | 6 + doc/manual/helpers.texi | 42 ++++ doc/manual/internet.texi | 229 +++++++++++++++++++++ doc/manual/ipv4.texi | 6 + doc/manual/ipv6.texi | 6 + doc/manual/log.texi | 25 +-- doc/manual/manual.texi | 92 +++++++-- doc/manual/mesh.texi | 6 + doc/manual/names.texi | 6 + doc/manual/node.texi | 231 +-------------------- doc/manual/organization.texi | 50 +++++ doc/manual/python.texi | 6 + doc/manual/simple.texi | 6 + doc/manual/statistics.texi | 9 +- doc/manual/tap.texi | 7 + doc/manual/tracing.texi | 8 +- 22 files changed, 729 insertions(+), 731 deletions(-) create mode 100644 doc/manual/animation.texi create mode 100644 doc/manual/applications.texi create mode 100644 doc/manual/bridge.texi create mode 100644 doc/manual/flow-monitor.texi create mode 100644 doc/manual/helpers.texi create mode 100644 doc/manual/internet.texi create mode 100644 doc/manual/ipv4.texi create mode 100644 doc/manual/ipv6.texi create mode 100644 doc/manual/mesh.texi create mode 100644 doc/manual/names.texi create mode 100644 doc/manual/organization.texi create mode 100644 doc/manual/python.texi create mode 100644 doc/manual/simple.texi create mode 100644 doc/manual/tap.texi diff --git a/doc/manual/animation.texi b/doc/manual/animation.texi new file mode 100644 index 000000000..c7da1668b --- /dev/null +++ b/doc/manual/animation.texi @@ -0,0 +1,6 @@ +@node Animation +@chapter Animation + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/applications.texi b/doc/manual/applications.texi new file mode 100644 index 000000000..07b431620 --- /dev/null +++ b/doc/manual/applications.texi @@ -0,0 +1,6 @@ +@node Applications +@chapter Applications + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/bridge.texi b/doc/manual/bridge.texi new file mode 100644 index 000000000..dc5fdf7aa --- /dev/null +++ b/doc/manual/bridge.texi @@ -0,0 +1,6 @@ +@node Bridge NetDevice +@chapter Bridge NetDevice + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/callbacks.texi b/doc/manual/callbacks.texi index 1abcedddc..72cec0266 100644 --- a/doc/manual/callbacks.texi +++ b/doc/manual/callbacks.texi @@ -7,15 +7,15 @@ chapter provides some motivation on the callback, guidance on how to use it, and details on its implementation. @menu -* Motivation:: -* Background:: +* Callbacks Motivation:: +* Callbacks Background:: * Using the Callback API:: * Bound Callbacks:: * Callback locations in ns-3:: * Implementation details:: @end menu -@node Motivation +@node Callbacks Motivation @section Motivation Consider that you have two simulation models A and B, and you wish @@ -91,7 +91,7 @@ to a transport protocol above, the user may be forced to hack the system to get the desired interconnections, This is clearly not an optimal way to design a generic simulator. -@node Background +@node Callbacks Background @section Background The basic mechanism that allows one to address the problem above is known as diff --git a/doc/manual/emu.texi b/doc/manual/emu.texi index 2e59ddb52..39f0c6c44 100644 --- a/doc/manual/emu.texi +++ b/doc/manual/emu.texi @@ -1,183 +1,293 @@ @node Emu NetDevice @chapter Emu NetDevice -This is the introduction to Emu NetDevice chapter, to complement the -Emu model doxygen. +@section Behavior -@menu -* Overview of the model:: -* Using the EmuNetDevice:: -* Emu Tracing:: -@end menu +The @code{Emu} net device allows a simulation node to send and receive packets +over a real network. The emulated net device relies on a specified interface +being in promiscuous mode. It opens a raw socket and binds to that interface. +We perform MAC spoofing to separate simulation network traffic from other +network traffic that may be flowing to and from the host. -@node Overview of the model -@section Overview of the model +Normally, the use case for emulated net devices is in collections of small +simulations that connect to the outside world through specific interfaces. +For example, one could construct a number of virtual machines and connect them +via a host-only network. To use the emulated net device, you would need to +set all of the host-only interfaces in promiscuous mode and provide an +appropriate device name, "eth1" for example. -The emulated net device allows a simulation node to send and receive packets -a real network. +One could also use the @code{Emu} net device in a testbed situation where the +host on which the simulation is running has a specific interface of interest +which drives the testbed hardware. You would also need to set this specific +interface into promiscuous mode and provide an appropriate device name to the +ns-3 emulated net device. An example of this environment is the ORBIT testbed +as described above. -The Emu net device is not a complete net device and channel combination as is -typical in ns-3. The Emu device can be thought of as a proxy for a real -device that resides in an ns-3 simulation. The Emu net device talks to that -real device using raw sockets and binds to the device via the Linux interface. -There is no related Emu channel since other devices will most likely reside on -different computers running entirely separate simulations. - -The Emu net device relies on a specified interface (``eth1, for example) being -in promiscuous mode. It opens a raw socket and binds to that interface. We -perform MAC spoofing to separate simulation network traffic from other network -traffic that may be flowing to and from the host. - -Normally, the use case for emulated net devices is in collections of -small simulations that connect to the outside world through specific -interfaces. For example, one could construct a number of virtual -machines and connect them via a host-only network. To use the emulated -net device, you would need to set all of the host-only interfaces in -promiscuous mode and provide an appropriate device name, "eth1" for example. - -One could also use the emulated net device in a testbed situation -where the host on which the simulation is running has a specific interface -of interest which drives the testbed hardware. You would also need to set -this specific interface into promiscuous mode and provide an appropriate -device name to the ns-3 emulated net device. - -The emulated net device only works if the underlying interface is up in -promiscuous mode. We could just turn it on, but the situation is that we -expect the other considerations listed above to have been dealt with. -To verify that these issues are dealt with, we just make sure that the end -result of that process has taken place and that the specified interface is -in promiscuous mode. - -@subsection Address Concerns - -Packets will be sent out over the device, but as mentioned, we use MAC spoofing. -By default in the simulation, the MAC addresses will be generated using the +The @code{Emu} net device only works if the underlying interface is up and in +promiscuous mode. Packets will be sent out over the device, but we use MAC +spoofing. The MAC addresses will be generated (by default) using the Organizationally Unique Identifier (OUI) 00:00:00 as a base. This vendor code is not assigned to any organization and so should not conflict with any real hardware. -It is always up to you to determine that using these MAC addresses is +It is always up to the user to determine that using these MAC addresses is okay on your network and won't conflict with anything else (including another -simulation using emu devices) on your network. If you are using the +simulation using @code{Emu} devices) on your network. If you are using the emulated net device in separate simulations you must consider global MAC address assignment issues and ensure that MAC addresses are unique across all simulations. The emulated net device respects the MAC address provided -in the SetAddress method so you can do this manually. For larger simulations, -you may want to set the OUI in the MAC address allocation function. +in the @code{SetAddress} method so you can do this manually. For larger +simulations, you may want to set the OUI in the MAC address allocation function. IP addresses corresponding to the emulated net devices are the addresses generated in the simulation, which are generated in the usual way via helper -functions. - -@subsection Attributes - -The Emu network device appears to the ns-3 system just as any other device and -can be controlled through the attribute system, and traced through conventional -trace hooks. The EmuNetDevice provides following Attributes: - -@itemize @bullet -@item Address: The Mac48Address of the device; -@item DeviceName: The name of the underlying real device (e.g., ``eth1''); -@item Start: The simulation time at which to enable the underlying socket; -@item Stop: The simulation time at which to stop receiving from the underlying socket; -@item TxQueue: The transmit queue used by the device; -@item InterframeGap: The optional time to wait between "frames"; -@item Rx: A trace source for received packets; -@end itemize - -Packets sent over the EmuNetDevice are always routed through the -transmit queue to provide a trace hook for packets sent out over the -network. This transmit queue can be set (via attribute) to model different -queuing strategies. - -@node Using the EmuNetDevice -@section Using the EmuNetDevice +functions. Since we are using MAC spoofing, there will not be a conflict +between ns-3 network stacks and any native network stacks. The emulated net device comes with a helper function as all ns-3 devices do. One unique aspect is that there is no channel associated with the underlying -medium. We really have no idea what this medium is, and so have not made an -effort to model it abstractly. The primary thing to be aware of is the +medium. We really have no idea what this external medium is, and so have not +made an effort to model it abstractly. The primary thing to be aware of is the implication this has for static global routing. The global router module attempts to walk the channels looking for adjacent networks. Since there -is no channel, the global router will be unable to do this. +is no channel, the global router will be unable to do this and you must then +use a dynamic routing protocol such as OLSR to include routing in +@code{Emu}-based networks. -The Emu net devices are typically created and configured using the associated -@code{EmuHelper} object. The various ns3 device helpers generally work in a -similar way, and their use is seen in many of our example programs. - -The conceptual model of interest is that of a bare computer ``husk'' into which -you plug net devices. The bare computers are created using a @code{NodeContainer} -helper. You just ask this helper to create as many computers (we call them -@code{Nodes}) as you need on your network: +@section Usage +Any mixing of ns-3 objects with real objects will typically require that +ns-3 compute checksums in its protocols. By default, checksums are not +computed by ns-3. To enable checksums (e.g. UDP, TCP, IP), users must set +the attribute @code{ChecksumEnabled} to true, such as follows: @verbatim - NodeContainer nodes; - nodes.Create (nEmuNodes); +GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true)); @end verbatim -Once you have your nodes, you need to instantiate a @code{EmuHelper} and set -any attributes you may want to change. +The usage of the @code{Emu} net device is straightforward once the network of +simulations has been configured. Since most of the work involved in working +with this device is in network configuration before even starting a simulation, +you may want to take a moment to review a couple of HOWTO pages on the ns-3 wiki +that describe how to set up a virtual test network using VMware and how to run +a set of example (client server) simulations that use @code{Emu} net devices. + +@uref{http://www.nsnam.org/wiki/index.php/HOWTO_use_VMware_to_set_up_virtual_networks_(Windows)} +@uref{http://www.nsnam.org/wiki/index.php/HOWTO_use_ns-3_scripts_to_drive_real_hardware_(experimental)} + +Once you are over the configuration hurdle, the script changes required to use +an @code{Emu} device are trivial. The main structural difference is that you +will need to create an ns-3 simulation script for each node. In the case of +the HOWTOs above, there is one client script and one server script. The only +``challenge'' is to get the addresses set correctly. + +Just as with all other ns-3 net devices, we provide a helper class for the +@code{Emu} net device. The following code snippet illustrates how one would +declare an EmuHelper and use it to set the ``DeviceName'' attribute to ``eth1'' +and install @code{Emu} devices on a group of nodes. You would do this on both +the client and server side in the case of the HOWTO seen above. @verbatim EmuHelper emu; - csma.SetAttribute ("DeviceName", StringValue ("eth1")); + emu.SetAttribute ("DeviceName", StringValue ("eth1")); + NetDeviceContainer d = emu.Install (n); @end verbatim - -Once the attributes are set, all that remains is to create the devices -and install them on the required nodes. When we create the net devices, -we add them to a container to allow you to use them in the future. This -all takes just one line of code. + +The only other change that may be required is to make sure that the address +spaces (MAC and IP) on the client and server simulations are compatible. First +the MAC address is set to a unique well-known value in both places (illustrated +here for one side). @verbatim - NetDeviceContainer emuDevices = emu.Install (nodes); + // + // We've got the devices in place. Since we're using MAC address + // spoofing under the sheets, we need to make sure that the MAC addresses + // we have assigned to our devices are unique. Ns-3 will happily + // automatically assign the same MAC addresses to the devices in both halves + // of our two-script pair, so let's go ahead and just manually change them + // to something we ensure is unique. + // + Ptr nd = d.Get (0); + Ptr ed = nd->GetObject (); + ed->SetAddress ("00:00:00:00:00:02"); @end verbatim -@node Emu Tracing -@section Emu Tracing +And then the IP address of the client or server is set in the usual way using +helpers. -Like all ns-3 devices, the Emu Model provides a number of trace sources. -These trace sources can be hooked using your own custom trace code, or you -can use our helper functions to arrange for tracing to be enabled on devices -you specify. +@verbatim + // + // We've got the "hardware" in place. Now we need to add IP addresses. + // This is the server half of a two-script pair. We need to make sure + // that the addressing in both of these applications is consistent, so + // we use provide an initial address in both cases. Here, the client + // will reside on one machine running ns-3 with one node having ns-3 + // with IP address "10.1.1.2" and talk to a server script running in + // another ns-3 on another computer that has an ns-3 node with IP + // address "10.1.1.3" + // + Ipv4AddressHelper ipv4; + ipv4.SetBase ("10.1.1.0", "255.255.255.0", "0.0.0.2"); + Ipv4InterfaceContainer i = ipv4.Assign (d); +@end verbatim -@subsection Upper-Level (MAC) Hooks +You will use application helpers to generate traffic exactly as you do in any +ns-3 simulation script. Note that the server address shown below in a snippet +from the client, must correspond to the IP address assigned to the server node +similarly to the snippet above. -From the point of view of tracing in the net device, there are several -interesting points to insert trace hooks. A convention inherited from other -simulators is that packets destined for transmission onto attached networks -pass through a single "transmit queue" in the net device. We provide trace -hooks at this point in packet flow, which corresponds (abstractly) only to a -transition from the network to data link layer, and call them collectively -the device MAC hooks. +@verbatim + uint32_t packetSize = 1024; + uint32_t maxPacketCount = 2000; + Time interPacketInterval = Seconds (0.001); + UdpEchoClientHelper client ("10.1.1.3", 9); + client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount)); + client.SetAttribute ("Interval", TimeValue (interPacketInterval)); + client.SetAttribute ("PacketSize", UintegerValue (packetSize)); + ApplicationContainer apps = client.Install (n.Get (0)); + apps.Start (Seconds (1.0)); + apps.Stop (Seconds (2.0)); +@end verbatim -When a packet is sent to the Emu net device for transmission it always -passes through the transmit queue. The transmit queue in the -EmuNetDevice inherits from Queue, and therefore inherits three -trace sources: +The @code{Emu} net device and helper provide access to ASCII and pcap tracing +functionality just as other ns-3 net devices to. You enable tracing similarly +to these other net devices: -@itemize @bullet -@item An Enqueue operation source (see Queue::m_traceEnqueue); -@item A Dequeue operation source (see Queue::m_traceDequeue); -@item A Drop operation source (see Queue::m_traceDrop). -@end itemize +@verbatim + EmuHelper::EnablePcapAll ("emu-udp-echo-client"); +@end verbatim -The upper-level (MAC) trace hooks for the EmuNetDevice are, in fact, -exactly these three trace sources on the single transmit queue of the device. +To see an example of a client script using the @code{Emu} net device, see +@code{examples/emu-udp-echo-client.cc} and @code{examples/emu-udp-echo-server.cc} +in the repository @uref{http://code.nsnam.org/craigdo/ns-3-emu/}. -The m_traceEnqueue event is triggered when a packet is placed on the transmit -queue. This happens at the time that EmuNetDevice::Send or -EmuNetDevice::SendFrom is called by a higher layer to queue a packet for -transmission. +@section Implementation -The m_traceDequeue event is triggered when a packet is removed from the -transmit queue. Dequeues from the transmit queue happen immediately after -the Enqueue event and just prior to the packet being sent to the underlying -socket. This means that the transmit queue really only exists to fire on -enqueue and dequeue operations so the Emu device behaves like other ns-3 -devices in this respect. +Perhaps the most unusual part of the @code{Emu} and @code{Tap} device +implementation relates to the requirement for executing some of the code +with super-user permissions. Rather than force the user to execute the entire +simulation as root, we provide a small ``creator'' program that runs as root +and does any required high-permission sockets work. -@subsection Lower-Level (PHY) Hooks +We do a similar thing for both the @code{Emu} and the @code{Tap} devices. +The high-level view is that the @code{CreateSocket} method creates a local +interprocess (Unix) socket, forks, and executes the small creation program. +The small program, which runs as suid root, creates a raw socket and sends +back the raw socket file descriptor over the Unix socket that is passed to +it as a parameter. The raw socket is passed as a control message (sometimes +called ancillary data) of type SCM_RIGHTS. + +The @code{Emu} net device uses the ns-3 threading and multithreaded real-time +scheduler extensions. The interesting work in the @code{Emu} device is done +when the net device is started (@code{EmuNetDevice::StartDevice ()}). An +attribute (``Start'') provides a simulation time at which to spin up the +net device. At this specified time (which defaults to t=0), the socket +creation function is called and executes as described above. You may also +specify a time at which to stop the device using the ``Stop'' attribute. + +Once the (promiscuous mode) socket is created, we bind it to an interface name +also provided as an attribute (``DeviceName'') that is stored internally as +@code{m_deviceName}: + +@verbatim + struct ifreq ifr; + bzero (&ifr, sizeof(ifr)); + strncpy ((char *)ifr.ifr_name, m_deviceName.c_str (), IFNAMSIZ); + + int32_t rc = ioctl (m_sock, SIOCGIFINDEX, &ifr); + + struct sockaddr_ll ll; + bzero (&ll, sizeof(ll)); + + ll.sll_family = AF_PACKET; + ll.sll_ifindex = m_sll_ifindex; + ll.sll_protocol = htons(ETH_P_ALL); + + rc = bind (m_sock, (struct sockaddr *)&ll, sizeof (ll)); +@end verbatim + +After the promiscuous raw socket is set up, a separate thread is spawned to do +reads from that socket and the link state is set to @code{Up}. + +@verbatim + m_readThread = Create ( + MakeCallback (&EmuNetDevice::ReadThread, this)); + m_readThread->Start (); + + NotifyLinkUp (); +@end verbatim + +The @code{EmuNetDevice::ReadThread} function basically just sits in an infinite +loop reading from the promiscuous mode raw socket and scheduling packet +receptions using the real-time simulator extensions. + +@verbatim + for (;;) + { + ... + + len = recvfrom (m_sock, buf, bufferSize, 0, (struct sockaddr *)&addr, + &addrSize); + + ... + + DynamicCast (Simulator::GetImplementation ())-> + ScheduleRealtimeNow ( + MakeEvent (&EmuNetDevice::ForwardUp, this, buf, len)); + + ... + } +@end verbatim + +The line starting with our templated DynamicCast function probably deserves a +comment. It gains access to the simulator implementation object using +the @code{Simulator::GetImplementation} method and then casts to the real-time +simulator implementation to use the real-time schedule method +@code{ScheduleRealtimeNow}. This function will cause a handler for the newly +received packet to be scheduled for execution at the current real time clock +value. This will, in turn cause the simulation clock to be advanced to that +real time value when the scheduled event (@code{EmuNetDevice::ForwardUp}) is +fired. + +The @code{ForwardUp} function operates as most other similar ns-3 net device +methods do. The packet is first filtered based on the destination address. In +the case of the @code{Emu} device, the MAC destination address will be the +address of the @code{Emu} device and not the hardware address of the real +device. Headers are then stripped off and the trace hooks are hit. Finally, +the packet is passed up the ns-3 protocol stack using the receive callback +function of the net device. + +Sending a packet is equally straightforward as shown below. The first thing +we do is to add the ethernet header and trailer to the ns-3 @code{Packet} we +are sending. The source address corresponds to the address of the @code{Emu} +device and not the underlying native device MAC address. This is where the +MAC address spoofing is done. The trailer is added and we enqueue and dequeue +the packet from the net device queue to hit the trace hooks. + +@verbatim + header.SetSource (source); + header.SetDestination (destination); + header.SetLengthType (packet->GetSize ()); + packet->AddHeader (header); + + EthernetTrailer trailer; + trailer.CalcFcs (packet); + packet->AddTrailer (trailer); + + m_queue->Enqueue (packet); + packet = m_queue->Dequeue (); + + struct sockaddr_ll ll; + bzero (&ll, sizeof (ll)); + + ll.sll_family = AF_PACKET; + ll.sll_ifindex = m_sll_ifindex; + ll.sll_protocol = htons(ETH_P_ALL); + + rc = sendto (m_sock, packet->PeekData (), packet->GetSize (), 0, + reinterpret_cast (&ll), sizeof (ll)); +@end verbatim + + +Finally, we simply send the packet to the raw socket which puts it out on the +real network. -There are no lower level trace hooks implemented in the Emu net device since -we rely on the underlying OS implementation of the raw socket to perform -the low level operations required to send and receive packets. diff --git a/doc/manual/emulation.texi b/doc/manual/emulation.texi index 5cc9080c2..a7c7a9868 100644 --- a/doc/manual/emulation.texi +++ b/doc/manual/emulation.texi @@ -1,6 +1,5 @@ @node Emulation @chapter Emulation -@anchor{chap:Emulation} ns-3 has been designed for integration into testbed and virtual machine environments. We have addressed this need by providing two kinds of @@ -68,317 +67,3 @@ We expect the typical use case for this environment will be to analyze the behavior of native applications and protocol suites in the presence of large simulated ns-3 networks. -@section Behavior - -@subsection Emu Net Device - -The @code{Emu} net device allows a simulation node to send and receive packets -over a real network. The emulated net device relies on a specified interface -being in promiscuous mode. It opens a raw socket and binds to that interface. -We perform MAC spoofing to separate simulation network traffic from other -network traffic that may be flowing to and from the host. - -Normally, the use case for emulated net devices is in collections of small -simulations that connect to the outside world through specific interfaces. -For example, one could construct a number of virtual machines and connect them -via a host-only network. To use the emulated net device, you would need to -set all of the host-only interfaces in promiscuous mode and provide an -appropriate device name, "eth1" for example. - -One could also use the @code{Emu} net device in a testbed situation where the -host on which the simulation is running has a specific interface of interest -which drives the testbed hardware. You would also need to set this specific -interface into promiscuous mode and provide an appropriate device name to the -ns-3 emulated net device. An example of this environment is the ORBIT testbed -as described above. - -The @code{Emu} net device only works if the underlying interface is up and in -promiscuous mode. Packets will be sent out over the device, but we use MAC -spoofing. The MAC addresses will be generated (by default) using the -Organizationally Unique Identifier (OUI) 00:00:00 as a base. This vendor code -is not assigned to any organization and so should not conflict with any real -hardware. - -It is always up to the user to determine that using these MAC addresses is -okay on your network and won't conflict with anything else (including another -simulation using @code{Emu} devices) on your network. If you are using the -emulated net device in separate simulations you must consider global MAC -address assignment issues and ensure that MAC addresses are unique across -all simulations. The emulated net device respects the MAC address provided -in the @code{SetAddress} method so you can do this manually. For larger -simulations, you may want to set the OUI in the MAC address allocation function. - -IP addresses corresponding to the emulated net devices are the addresses -generated in the simulation, which are generated in the usual way via helper -functions. Since we are using MAC spoofing, there will not be a conflict -between ns-3 network stacks and any native network stacks. - -The emulated net device comes with a helper function as all ns-3 devices do. -One unique aspect is that there is no channel associated with the underlying -medium. We really have no idea what this external medium is, and so have not -made an effort to model it abstractly. The primary thing to be aware of is the -implication this has for static global routing. The global router module -attempts to walk the channels looking for adjacent networks. Since there -is no channel, the global router will be unable to do this and you must then -use a dynamic routing protocol such as OLSR to include routing in -@code{Emu}-based networks. - -@subsection Tap Net Device - -The @code{Tap} Net Device is scheduled for inclusion in ns-3.4 at the writing -of this section. We will include details as soon as the @code{Tap} device is -merged. - -@section Usage - -Any mixing of ns-3 objects with real objects will typically require that -ns-3 compute checksums in its protocols. By default, checksums are not -computed by ns-3. To enable checksums (e.g. UDP, TCP, IP), users must set -the attribute @code{ChecksumEnabled} to true, such as follows: -@verbatim -GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true)); -@end verbatim - -@subsection Emu Net Device - -The usage of the @code{Emu} net device is straightforward once the network of -simulations has been configured. Since most of the work involved in working -with this device is in network configuration before even starting a simulation, -you may want to take a moment to review a couple of HOWTO pages on the ns-3 wiki -that describe how to set up a virtual test network using VMware and how to run -a set of example (client server) simulations that use @code{Emu} net devices. - -@uref{http://www.nsnam.org/wiki/index.php/HOWTO_use_VMware_to_set_up_virtual_networks_(Windows)} -@uref{http://www.nsnam.org/wiki/index.php/HOWTO_use_ns-3_scripts_to_drive_real_hardware_(experimental)} - -Once you are over the configuration hurdle, the script changes required to use -an @code{Emu} device are trivial. The main structural difference is that you -will need to create an ns-3 simulation script for each node. In the case of -the HOWTOs above, there is one client script and one server script. The only -``challenge'' is to get the addresses set correctly. - -Just as with all other ns-3 net devices, we provide a helper class for the -@code{Emu} net device. The following code snippet illustrates how one would -declare an EmuHelper and use it to set the ``DeviceName'' attribute to ``eth1'' -and install @code{Emu} devices on a group of nodes. You would do this on both -the client and server side in the case of the HOWTO seen above. - -@verbatim - EmuHelper emu; - emu.SetAttribute ("DeviceName", StringValue ("eth1")); - NetDeviceContainer d = emu.Install (n); -@end verbatim - -The only other change that may be required is to make sure that the address -spaces (MAC and IP) on the client and server simulations are compatible. First -the MAC address is set to a unique well-known value in both places (illustrated -here for one side). - -@verbatim - // - // We've got the devices in place. Since we're using MAC address - // spoofing under the sheets, we need to make sure that the MAC addresses - // we have assigned to our devices are unique. Ns-3 will happily - // automatically assign the same MAC addresses to the devices in both halves - // of our two-script pair, so let's go ahead and just manually change them - // to something we ensure is unique. - // - Ptr nd = d.Get (0); - Ptr ed = nd->GetObject (); - ed->SetAddress ("00:00:00:00:00:02"); -@end verbatim - -And then the IP address of the client or server is set in the usual way using -helpers. - -@verbatim - // - // We've got the "hardware" in place. Now we need to add IP addresses. - // This is the server half of a two-script pair. We need to make sure - // that the addressing in both of these applications is consistent, so - // we use provide an initial address in both cases. Here, the client - // will reside on one machine running ns-3 with one node having ns-3 - // with IP address "10.1.1.2" and talk to a server script running in - // another ns-3 on another computer that has an ns-3 node with IP - // address "10.1.1.3" - // - Ipv4AddressHelper ipv4; - ipv4.SetBase ("10.1.1.0", "255.255.255.0", "0.0.0.2"); - Ipv4InterfaceContainer i = ipv4.Assign (d); -@end verbatim - -You will use application helpers to generate traffic exactly as you do in any -ns-3 simulation script. Note that the server address shown below in a snippet -from the client, must correspond to the IP address assigned to the server node -similarly to the snippet above. - -@verbatim - uint32_t packetSize = 1024; - uint32_t maxPacketCount = 2000; - Time interPacketInterval = Seconds (0.001); - UdpEchoClientHelper client ("10.1.1.3", 9); - client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount)); - client.SetAttribute ("Interval", TimeValue (interPacketInterval)); - client.SetAttribute ("PacketSize", UintegerValue (packetSize)); - ApplicationContainer apps = client.Install (n.Get (0)); - apps.Start (Seconds (1.0)); - apps.Stop (Seconds (2.0)); -@end verbatim - -The @code{Emu} net device and helper provide access to ASCII and pcap tracing -functionality just as other ns-3 net devices to. You enable tracing similarly -to these other net devices: - -@verbatim - EmuHelper::EnablePcapAll ("emu-udp-echo-client"); -@end verbatim - -To see an example of a client script using the @code{Emu} net device, see -@code{examples/emu-udp-echo-client.cc} and @code{examples/emu-udp-echo-server.cc} -in the repository @uref{http://code.nsnam.org/craigdo/ns-3-emu/}. - -@subsection Tap Net Device - -The @code{Tap} Net Device is scheduled for inclusion in ns-3.4 at the writing -of this section. We will include details as soon as the @code{Tap} device is -merged. - -@section Implementation - -Perhaps the most unusual part of the @code{Emu} and @code{Tap} device -implementation relates to the requirement for executing some of the code -with super-user permissions. Rather than force the user to execute the entire -simulation as root, we provide a small ``creator'' program that runs as root -and does any required high-permission sockets work. - -We do a similar thing for both the @code{Emu} and the @code{Tap} devices. -The high-level view is that the @code{CreateSocket} method creates a local -interprocess (Unix) socket, forks, and executes the small creation program. -The small program, which runs as suid root, creates a raw socket and sends -back the raw socket file descriptor over the Unix socket that is passed to -it as a parameter. The raw socket is passed as a control message (sometimes -called ancillary data) of type SCM_RIGHTS. - -@subsection Emu Net Device - -The @code{Emu} net device uses the ns-3 threading and multithreaded real-time -scheduler extensions. The interesting work in the @code{Emu} device is done -when the net device is started (@code{EmuNetDevice::StartDevice ()}). An -attribute (``Start'') provides a simulation time at which to spin up the -net device. At this specified time (which defaults to t=0), the socket -creation function is called and executes as described above. You may also -specify a time at which to stop the device using the ``Stop'' attribute. - -Once the (promiscuous mode) socket is created, we bind it to an interface name -also provided as an attribute (``DeviceName'') that is stored internally as -@code{m_deviceName}: - -@verbatim - struct ifreq ifr; - bzero (&ifr, sizeof(ifr)); - strncpy ((char *)ifr.ifr_name, m_deviceName.c_str (), IFNAMSIZ); - - int32_t rc = ioctl (m_sock, SIOCGIFINDEX, &ifr); - - struct sockaddr_ll ll; - bzero (&ll, sizeof(ll)); - - ll.sll_family = AF_PACKET; - ll.sll_ifindex = m_sll_ifindex; - ll.sll_protocol = htons(ETH_P_ALL); - - rc = bind (m_sock, (struct sockaddr *)&ll, sizeof (ll)); -@end verbatim - -After the promiscuous raw socket is set up, a separate thread is spawned to do -reads from that socket and the link state is set to @code{Up}. - -@verbatim - m_readThread = Create ( - MakeCallback (&EmuNetDevice::ReadThread, this)); - m_readThread->Start (); - - NotifyLinkUp (); -@end verbatim - -The @code{EmuNetDevice::ReadThread} function basically just sits in an infinite -loop reading from the promiscuous mode raw socket and scheduling packet -receptions using the real-time simulator extensions. - -@verbatim - for (;;) - { - ... - - len = recvfrom (m_sock, buf, bufferSize, 0, (struct sockaddr *)&addr, - &addrSize); - - ... - - DynamicCast (Simulator::GetImplementation ())-> - ScheduleRealtimeNow ( - MakeEvent (&EmuNetDevice::ForwardUp, this, buf, len)); - - ... - } -@end verbatim - -The line starting with our templated DynamicCast function probably deserves a -comment. It gains access to the simulator implementation object using -the @code{Simulator::GetImplementation} method and then casts to the real-time -simulator implementation to use the real-time schedule method -@code{ScheduleRealtimeNow}. This function will cause a handler for the newly -received packet to be scheduled for execution at the current real time clock -value. This will, in turn cause the simulation clock to be advanced to that -real time value when the scheduled event (@code{EmuNetDevice::ForwardUp}) is -fired. - -The @code{ForwardUp} function operates as most other similar ns-3 net device -methods do. The packet is first filtered based on the destination address. In -the case of the @code{Emu} device, the MAC destination address will be the -address of the @code{Emu} device and not the hardware address of the real -device. Headers are then stripped off and the trace hooks are hit. Finally, -the packet is passed up the ns-3 protocol stack using the receive callback -function of the net device. - -Sending a packet is equally straightforward as shown below. The first thing -we do is to add the ethernet header and trailer to the ns-3 @code{Packet} we -are sending. The source address corresponds to the address of the @code{Emu} -device and not the underlying native device MAC address. This is where the -MAC address spoofing is done. The trailer is added and we enqueue and dequeue -the packet from the net device queue to hit the trace hooks. - -@verbatim - header.SetSource (source); - header.SetDestination (destination); - header.SetLengthType (packet->GetSize ()); - packet->AddHeader (header); - - EthernetTrailer trailer; - trailer.CalcFcs (packet); - packet->AddTrailer (trailer); - - m_queue->Enqueue (packet); - packet = m_queue->Dequeue (); - - struct sockaddr_ll ll; - bzero (&ll, sizeof (ll)); - - ll.sll_family = AF_PACKET; - ll.sll_ifindex = m_sll_ifindex; - ll.sll_protocol = htons(ETH_P_ALL); - - rc = sendto (m_sock, packet->PeekData (), packet->GetSize (), 0, - reinterpret_cast (&ll), sizeof (ll)); -@end verbatim - - -Finally, we simply send the packet to the raw socket which puts it out on the -real network. - -@subsection Tap Net Device - -The @code{Tap} Net Device is scheduled for inclusion in ns-3.4 at the writing -of this section. We will include details as soon as the @code{Tap} device is -merged. - diff --git a/doc/manual/flow-monitor.texi b/doc/manual/flow-monitor.texi new file mode 100644 index 000000000..d01ba63b5 --- /dev/null +++ b/doc/manual/flow-monitor.texi @@ -0,0 +1,6 @@ +@node Flow Monitor +@chapter Flow Monitor + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/helpers.texi b/doc/manual/helpers.texi new file mode 100644 index 000000000..4d25ec8e3 --- /dev/null +++ b/doc/manual/helpers.texi @@ -0,0 +1,42 @@ +@node Helpers +@chapter Helpers + +The above chapters introduced you to various ns-3 programming concepts +such as smart pointers for reference-counted memory management, attributes, +namespaces, callbacks, etc. Users who work at this low-level API +can interconnect ns-3 objects with fine granulariy. However, a +simulation program written entirely using the low-level API would +be quite long and tedious to code. For this reason, a separate so-called +``helper API'' has been overlaid on the core ns-3 API. If you have read +the ns-3 tutorial, you will already be familiar with the helper API, +since it is the API that new users are typically introduced to first. +In this chapter, we introduce the design philosophy of the helper +API and contrast it to the low-level API. If you become a heavy +user of ns-3, you will likely move back and forth between these +APIs even in the same program. + +The helper API has a few goals: +@enumerate +@item the rest of @code{src/} has no dependencies on the helper API; +anything that can be done with the helper API can be coded also at +the low-level API +@item @strong{Containers:} Often simulations will need to do +a number of identical actions to groups of objects. The helper +API makes heavy use of containers of similar objects to which similar +or identical operations can be performed. +@item The helper API is not generic; it does not strive to maximize +code reuse. So, programming constructs such as polymorphism and +templates that achieve code reuse are not as prevalent. For instance, +there are separate CsmaNetDevice helpers and PointToPointNetDevice +helpers but they do not derive from a common NetDevice base class. +@item The helper API typically works with stack-allocated (vs. +heap-allocated) objects. For some programs, ns-3 users may not +need to worry about any low level Object Create or Ptr handling; +they can make do with containers of objects and stack-allocated helpers +that operate on them. +@end enumerate + +The helper API is really all about making ns-3 programs easier to +write and read, without taking away the power of the low-level +interface. The rest of this chapter provides some examples of +the programming conventions of the helper API. diff --git a/doc/manual/internet.texi b/doc/manual/internet.texi new file mode 100644 index 000000000..6d9e40952 --- /dev/null +++ b/doc/manual/internet.texi @@ -0,0 +1,229 @@ +@node Internet Stack +@chapter Internet Stack + +@section Internet stack aggregation + +The above @code{class Node} is not very useful as-is; other objects +must be aggregated to it to provide useful node functionality. + +The ns-3 source code directory @code{src/internet-stack} provides +implementation of TCP/IPv4-related components. These include IPv4, +ARP, UDP, TCP, and other related protocols. + +Internet Nodes are not subclasses of class Node; they are simply Nodes +that have had a bunch of IPv4-related +objects aggregated to them. They can be put together by hand, or +via a helper function @code{InternetStackHelper::Install ()} which does the +following to all nodes passed in as arguments: +@verbatim +void +InternetStackHelper::Install (Ptr node) const +{ + if (node->GetObject () != 0) + { + NS_FATAL_ERROR ("InternetStackHelper::Install(): Aggregating " + "an InternetStack to a node with an existing Ipv4 object"); + return; + } + + CreateAndAggregateObjectFromTypeId (node, "ns3::ArpL3Protocol"); + CreateAndAggregateObjectFromTypeId (node, "ns3::Ipv4L3Protocol"); + CreateAndAggregateObjectFromTypeId (node, "ns3::Icmpv4L4Protocol"); + CreateAndAggregateObjectFromTypeId (node, "ns3::UdpL4Protocol"); + node->AggregateObject (m_tcpFactory.Create ()); + Ptr factory = CreateObject (); + node->AggregateObject (factory); + // Set routing + Ptr ipv4 = node->GetObject (); + Ptr ipv4Routing = m_routing->Create (node); + ipv4->SetRoutingProtocol (ipv4Routing); +} +@end verbatim + +Note that the Ipv4 routing protocol is configured and set outside this +function. By default, the following protocols are added to Ipv4: +@verbatim +InternetStackHelper::InternetStackHelper () +{ + SetTcp ("ns3::TcpL4Protocol"); + static Ipv4StaticRoutingHelper staticRouting; + static Ipv4GlobalRoutingHelper globalRouting; + static Ipv4ListRoutingHelper listRouting; + listRouting.Add (staticRouting, 0); + listRouting.Add (globalRouting, -10); + SetRoutingHelper (listRouting); +} +@end verbatim + +@subsection Internet Node structure + +An IPv4-capable Node (an ns-3 Node augmented by aggregation to have one or more +IP stacks) has the following internal structure. + +@subsubsection Layer-3 protocols +At the lowest layer, sitting above the NetDevices, are the "layer 3" +protocols, including IPv4, IPv6 (in the future), and ARP. The +@code{class Ipv4L3Protocol} is an +implementation class whose public interface is +typically @code{class Ipv4} (found in src/node directory), but the +Ipv4L3Protocol public API is also used internally in the +src/internet-stack directory at present. + +In class Ipv4L3Protocol, one method described below is @code{Receive ()}: +@verbatim + /** + * Lower layer calls this method after calling L3Demux::Lookup + * The ARP subclass needs to know from which NetDevice this + * packet is coming to: + * - implement a per-NetDevice ARP cache + * - send back arp replies on the right device + */ + void Receive( Ptr device, Ptr p, uint16_t protocol, const Address &from, + const Address &to, NetDevice::PacketType packetType); +@end verbatim + +First, note that the @code{Receive ()} function has a matching signature +to the ReceiveCallback in the @code{class Node}. This function pointer +is inserted into the Node's protocol handler when +@code{AddInterface ()} is called. The actual registration is done +with a statement such as: +follows: +@verbatim + RegisterProtocolHandler ( MakeCallback (&Ipv4Protocol::Receive, ipv4), + Ipv4L3Protocol::PROT_NUMBER, 0); +@end verbatim + +The Ipv4L3Protocol object is aggregated to the Node; there is only one +such Ipv4L3Protocol object. Higher-layer protocols that have a packet +to send down to the Ipv4L3Protocol object can call +@code{GetObject ()} to obtain a pointer, as follows: +@verbatim + Ptr ipv4 = m_node->GetObject (); + if (ipv4 != 0) + { + ipv4->Send (packet, saddr, daddr, PROT_NUMBER); + } +@end verbatim + +This class nicely demonstrates two techniques we exploit in +ns-3 to bind objects together: callbacks, and object aggregation. + +Once IPv4 routing has determined that a packet is for the local node, it +forwards it up the stack. This is done with the following function: + +@verbatim +void +Ipv4L3Protocol::LocalDeliver (Ptr packet, Ipv4Header const&ip, uint32_t iif) +@end verbatim + +The first step is to find the right Ipv4L4Protocol object , based on IP protocol +number. For instance, TCP is registered in the demux as protocol number 6. +Finally, the @code{Receive()} function on the Ipv4L4Protocol (such as +@code{TcpL4Protocol::Receive} is called. + +We have not yet introduced the class Ipv4Interface. Basically, +each NetDevice is paired with an IPv4 representation of such device. +In Linux, this @code{class Ipv4Interface} roughly corresponds to +the @code{struct in_device}; the main purpose is to provide +address-family specific information (addresses) about an interface. + +@subsubsection Layer-4 protocols and sockets + +We next describe how the transport protocols, sockets, and applications +tie together. In summary, each transport protocol implementation is +a socket factory. An application that needs a new socket + +For instance, to create a UDP socket, an application would use a code +snippet such as the following: +@verbatim + Ptr udpSocketFactory = GetNode ()->GetObject (); + Ptr m_socket = socketFactory->CreateSocket (); + m_socket->Bind (m_local_address); + ... +@end verbatim +The above will query the node to get a pointer to its UDP socket +factory, will create one such socket, and will use the socket with +an API similar to the C-based sockets API, such as @code{Connect ()} +and @code{Send ()}. See the chapter on ns-3 sockets for more information. + +We have described so far a socket factory (e.g. @code{class Udp}) and +a socket, which may be specialized (e.g., @code{class UdpSocket}). +There are a few more key objects that relate to the specialized +task of demultiplexing a packet to one or more receiving sockets. +The key object in this task is @code{class Ipv4EndPointDemux}. +This demultiplexer stores objects of @code{class Ipv4EndPoint}. +This class holds the addressing/port tuple (local port, local address, +destination port, destination address) associated with the socket, +and a receive callback. This receive callback has a receive +function registered by the socket. The @code{Lookup ()} function to +Ipv4EndPointDemux returns a list of Ipv4EndPoint objects (there may +be a list since more than one socket may match the packet). The +layer-4 protocol copies the packet to each Ipv4EndPoint and calls +its @code{ForwardUp ()} method, which then calls the @code{Receive ()} +function registered by the socket. + +An issue that arises when working with the sockets API on real +systems is the need to manage the reading from a socket, using +some type of I/O (e.g., blocking, non-blocking, asynchronous, ...). +ns-3 implements an asynchronous model for socket I/O; the application +sets a callback to be notified of received data ready to be read, and the +callback is invoked by the transport protocol when data is available. +This callback is specified as follows: +@verbatim + void Socket::SetRecvCallback (Callback, + Ptr, const Address&> receivedData); +@end verbatim +The data being received is conveyed in the Packet data buffer. An example +usage is in @code{class PacketSink}: +@verbatim + m_socket->SetRecvCallback (MakeCallback(&PacketSink::HandleRead, this)); +@end verbatim + +To summarize, internally, the UDP implementation is organized as follows: +@itemize @bullet +@item a @code{UdpImpl} class that implements the UDP socket factory +functionality +@item a @code{UdpL4Protocol} class that implements the protocol logic +that is socket-independent +@item a @code{UdpSocketImpl} class that implements socket-specific aspects +of UDP +@item a class called @code{Ipv4EndPoint} that stores the +addressing tuple (local port, local address, destination port, destination +address) associated with the socket, and a receive callback for the socket. +@end itemize + +@subsection Ipv4-capable node interfaces + +Many of the implementation details, or internal objects themselves, +of Ipv4-capable Node objects are not exposed at the simulator public +API. This allows for different implementations; for instance, +replacing the native ns-3 models with ported TCP/IP stack code. + +The C++ public APIs of all of these objects is found in the +@code{src/node} directory, including principally: +@itemize @bullet +@item @code{socket.h} +@item @code{tcp.h} +@item @code{udp.h} +@item @code{ipv4.h} +@end itemize +These are typically base class objects that implement the default +values used in the implementation, implement access methods to get/set +state variables, host attributes, and implement publicly-available methods +exposed to clients such as @code{CreateSocket}. + +@subsection Example path of a packet + +These two figures show an example stack trace of how packets flow +through the Internet Node objects. + +@float Figure,fig:internet-node-send +@caption{Send path of a packet.} +@image{figures/internet-node-send,5in} +@end float + +@float Figure,fig:internet-node-recv +@caption{Receive path of a packet.} +@image{figures/internet-node-recv,5in} +@end float + diff --git a/doc/manual/ipv4.texi b/doc/manual/ipv4.texi new file mode 100644 index 000000000..9e336780b --- /dev/null +++ b/doc/manual/ipv4.texi @@ -0,0 +1,6 @@ +@node IPv4 +@chapter IPv4 + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/ipv6.texi b/doc/manual/ipv6.texi new file mode 100644 index 000000000..8f2aa9103 --- /dev/null +++ b/doc/manual/ipv6.texi @@ -0,0 +1,6 @@ +@node IPv6 +@chapter IPv6 + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/log.texi b/doc/manual/log.texi index 2f39efe59..88a90f862 100644 --- a/doc/manual/log.texi +++ b/doc/manual/log.texi @@ -1,24 +1,7 @@ @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 +@cartouche +This chapter not yet written. For now, the ns-3 tutorial contains logging +information. +@end cartouche diff --git a/doc/manual/manual.texi b/doc/manual/manual.texi index 2029549e3..99cb74568 100644 --- a/doc/manual/manual.texi +++ b/doc/manual/manual.texi @@ -2,7 +2,6 @@ @c %**start of header @setfilename ns-3.info @settitle ns-3 manual -@c @setchapternewpage odd @c %**end of header @ifinfo @@ -61,7 +60,6 @@ along with this program. If not, see @uref{http://www.gnu.org/licenses/}. @include VERSION @today{} -@c @page @vskip 0pt plus 1filll @insertcopying @end titlepage @@ -86,42 +84,102 @@ Simulator version: @end ifnottex @menu +* Organization:: * Random variables:: * Callbacks:: * Object model:: * Attributes:: +* Object names:: +* Logging:: * Tracing:: * RealTime:: -* Emulation:: * Packets:: -* Sockets APIs:: -* Node and Internet Stack:: -* TCP:: -* Routing overview:: -* Wifi NetDevice:: -* CSMA NetDevice:: +* Helpers:: +* Python:: +* Node and NetDevices:: +* Simple NetDevice:: * PointToPoint NetDevice:: +* CSMA NetDevice:: +* Wifi NetDevice:: +* Mesh NetDevice:: +* Bridge NetDevice:: +* Emulation:: +* Emu NetDevice:: +* Tap NetDevice:: +* Sockets APIs:: +* Internet Stack:: +* IPv4:: +* IPv6:: +* Routing overview:: +* TCP:: +* Applications:: +* Flow Monitor:: +* Animation:: +* Statistics:: * Creating a new ns-3 model:: * Troubleshooting:: @end menu +@setchapternewpage odd +@headings off +@everyheading @thischapter @| @| ns-3 manual +@everyfooting ns-3.6 @| @thispage @| @today +@include organization.texi + +@unnumbered Part 1: ns-3 core +@setchapternewpage off @include random.texi +@setchapternewpage odd @include callbacks.texi @include objects.texi @include attributes.texi +@include names.texi +@include log.texi @include tracing.texi @include realtime.texi -@include emulation.texi @include packets.texi -@include sockets.texi +@include helpers.texi +@include python.texi + +@unnumbered Part 2: Nodes and NetDevices +@setchapternewpage off @include node.texi -@c @include output.texi -@include tcp.texi -@include routing.texi -@include wifi.texi -@include csma.texi +@setchapternewpage odd +@include simple.texi @include point-to-point.texi -@c @include other.texi +@include csma.texi +@include wifi.texi +@include mesh.texi +@include bridge.texi + +@unnumbered Part 3: Emulation +@setchapternewpage off +@include emulation.texi +@include emu.texi +@include tap.texi +@setchapternewpage odd + +@unnumbered Part 4: Internet Models +@setchapternewpage off +@include sockets.texi +@setchapternewpage odd +@include internet.texi +@include ipv4.texi +@include ipv6.texi +@include routing.texi +@include tcp.texi + +@unnumbered Part 5: Applications +@setchapternewpage off +@include applications.texi +@setchapternewpage odd + +@unnumbered Part 6: Support +@setchapternewpage off +@include flow-monitor.texi +@setchapternewpage odd +@include animation.texi +@include statistics.texi @include new-models.texi @include troubleshoot.texi diff --git a/doc/manual/mesh.texi b/doc/manual/mesh.texi new file mode 100644 index 000000000..9ef91152d --- /dev/null +++ b/doc/manual/mesh.texi @@ -0,0 +1,6 @@ +@node Mesh NetDevice +@chapter Mesh NetDevice + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/names.texi b/doc/manual/names.texi new file mode 100644 index 000000000..ddccb41e0 --- /dev/null +++ b/doc/manual/names.texi @@ -0,0 +1,6 @@ +@node Object names +@chapter Object names + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/node.texi b/doc/manual/node.texi index 6c018d603..0195138ba 100644 --- a/doc/manual/node.texi +++ b/doc/manual/node.texi @@ -1,5 +1,5 @@ -@node Node and Internet Stack -@chapter Node and Internet Stack +@node Node and NetDevices +@chapter Node and NetDevices @anchor{chap:Node} This chapter describes how ns-3 nodes are put together, and provides @@ -109,229 +109,6 @@ The NodeList class provides an @code{Add()} method and C++ iterators to allow one to walk the node list or fetch a Node pointer by its integer identifier. -@section Internet stack aggregation - -The above @code{class Node} is not very useful as-is; other objects -must be aggregated to it to provide useful node functionality. - -The ns-3 source code directory @code{src/internet-stack} provides -implementation of TCP/IPv4-related components. These include IPv4, -ARP, UDP, TCP, and other related protocols. - -Internet Nodes are not subclasses of class Node; they are simply Nodes -that have had a bunch of IPv4-related -objects aggregated to them. They can be put together by hand, or -via a helper function @code{InternetStackHelper::Install ()} which does the -following to all nodes passed in as arguments: -@verbatim -void -InternetStackHelper::Install (Ptr node) const -{ - if (node->GetObject () != 0) - { - NS_FATAL_ERROR ("InternetStackHelper::Install(): Aggregating " - "an InternetStack to a node with an existing Ipv4 object"); - return; - } - - CreateAndAggregateObjectFromTypeId (node, "ns3::ArpL3Protocol"); - CreateAndAggregateObjectFromTypeId (node, "ns3::Ipv4L3Protocol"); - CreateAndAggregateObjectFromTypeId (node, "ns3::Icmpv4L4Protocol"); - CreateAndAggregateObjectFromTypeId (node, "ns3::UdpL4Protocol"); - node->AggregateObject (m_tcpFactory.Create ()); - Ptr factory = CreateObject (); - node->AggregateObject (factory); - // Set routing - Ptr ipv4 = node->GetObject (); - Ptr ipv4Routing = m_routing->Create (node); - ipv4->SetRoutingProtocol (ipv4Routing); -} -@end verbatim - -Note that the Ipv4 routing protocol is configured and set outside this -function. By default, the following protocols are added to Ipv4: -@verbatim -InternetStackHelper::InternetStackHelper () -{ - SetTcp ("ns3::TcpL4Protocol"); - static Ipv4StaticRoutingHelper staticRouting; - static Ipv4GlobalRoutingHelper globalRouting; - static Ipv4ListRoutingHelper listRouting; - listRouting.Add (staticRouting, 0); - listRouting.Add (globalRouting, -10); - SetRoutingHelper (listRouting); -} -@end verbatim - -@subsection Internet Node structure - -An IPv4-capable Node (an ns-3 Node augmented by aggregation to have one or more -IP stacks) has the following internal structure. - -@subsubsection Layer-3 protocols -At the lowest layer, sitting above the NetDevices, are the "layer 3" -protocols, including IPv4, IPv6 (in the future), and ARP. The -@code{class Ipv4L3Protocol} is an -implementation class whose public interface is -typically @code{class Ipv4} (found in src/node directory), but the -Ipv4L3Protocol public API is also used internally in the -src/internet-stack directory at present. - -In class Ipv4L3Protocol, one method described below is @code{Receive ()}: -@verbatim - /** - * Lower layer calls this method after calling L3Demux::Lookup - * The ARP subclass needs to know from which NetDevice this - * packet is coming to: - * - implement a per-NetDevice ARP cache - * - send back arp replies on the right device - */ - void Receive( Ptr device, Ptr p, uint16_t protocol, const Address &from, - const Address &to, NetDevice::PacketType packetType); -@end verbatim - -First, note that the @code{Receive ()} function has a matching signature -to the ReceiveCallback in the @code{class Node}. This function pointer -is inserted into the Node's protocol handler when -@code{AddInterface ()} is called. The actual registration is done -with a statement such as: -follows: -@verbatim - RegisterProtocolHandler ( MakeCallback (&Ipv4Protocol::Receive, ipv4), - Ipv4L3Protocol::PROT_NUMBER, 0); -@end verbatim - -The Ipv4L3Protocol object is aggregated to the Node; there is only one -such Ipv4L3Protocol object. Higher-layer protocols that have a packet -to send down to the Ipv4L3Protocol object can call -@code{GetObject ()} to obtain a pointer, as follows: -@verbatim - Ptr ipv4 = m_node->GetObject (); - if (ipv4 != 0) - { - ipv4->Send (packet, saddr, daddr, PROT_NUMBER); - } -@end verbatim - -This class nicely demonstrates two techniques we exploit in -ns-3 to bind objects together: callbacks, and object aggregation. - -Once IPv4 routing has determined that a packet is for the local node, it -forwards it up the stack. This is done with the following function: - -@verbatim -void -Ipv4L3Protocol::LocalDeliver (Ptr packet, Ipv4Header const&ip, uint32_t iif) -@end verbatim - -The first step is to find the right Ipv4L4Protocol object , based on IP protocol -number. For instance, TCP is registered in the demux as protocol number 6. -Finally, the @code{Receive()} function on the Ipv4L4Protocol (such as -@code{TcpL4Protocol::Receive} is called. - -We have not yet introduced the class Ipv4Interface. Basically, -each NetDevice is paired with an IPv4 representation of such device. -In Linux, this @code{class Ipv4Interface} roughly corresponds to -the @code{struct in_device}; the main purpose is to provide -address-family specific information (addresses) about an interface. - -@subsubsection Layer-4 protocols and sockets - -We next describe how the transport protocols, sockets, and applications -tie together. In summary, each transport protocol implementation is -a socket factory. An application that needs a new socket - -For instance, to create a UDP socket, an application would use a code -snippet such as the following: -@verbatim - Ptr udpSocketFactory = GetNode ()->GetObject (); - Ptr m_socket = socketFactory->CreateSocket (); - m_socket->Bind (m_local_address); - ... -@end verbatim -The above will query the node to get a pointer to its UDP socket -factory, will create one such socket, and will use the socket with -an API similar to the C-based sockets API, such as @code{Connect ()} -and @code{Send ()}. See the chapter on ns-3 sockets for more information. - -We have described so far a socket factory (e.g. @code{class Udp}) and -a socket, which may be specialized (e.g., @code{class UdpSocket}). -There are a few more key objects that relate to the specialized -task of demultiplexing a packet to one or more receiving sockets. -The key object in this task is @code{class Ipv4EndPointDemux}. -This demultiplexer stores objects of @code{class Ipv4EndPoint}. -This class holds the addressing/port tuple (local port, local address, -destination port, destination address) associated with the socket, -and a receive callback. This receive callback has a receive -function registered by the socket. The @code{Lookup ()} function to -Ipv4EndPointDemux returns a list of Ipv4EndPoint objects (there may -be a list since more than one socket may match the packet). The -layer-4 protocol copies the packet to each Ipv4EndPoint and calls -its @code{ForwardUp ()} method, which then calls the @code{Receive ()} -function registered by the socket. - -An issue that arises when working with the sockets API on real -systems is the need to manage the reading from a socket, using -some type of I/O (e.g., blocking, non-blocking, asynchronous, ...). -ns-3 implements an asynchronous model for socket I/O; the application -sets a callback to be notified of received data ready to be read, and the -callback is invoked by the transport protocol when data is available. -This callback is specified as follows: -@verbatim - void Socket::SetRecvCallback (Callback, - Ptr, const Address&> receivedData); -@end verbatim -The data being received is conveyed in the Packet data buffer. An example -usage is in @code{class PacketSink}: -@verbatim - m_socket->SetRecvCallback (MakeCallback(&PacketSink::HandleRead, this)); -@end verbatim - -To summarize, internally, the UDP implementation is organized as follows: -@itemize @bullet -@item a @code{UdpImpl} class that implements the UDP socket factory -functionality -@item a @code{UdpL4Protocol} class that implements the protocol logic -that is socket-independent -@item a @code{UdpSocketImpl} class that implements socket-specific aspects -of UDP -@item a class called @code{Ipv4EndPoint} that stores the -addressing tuple (local port, local address, destination port, destination -address) associated with the socket, and a receive callback for the socket. -@end itemize - -@subsection Ipv4-capable node interfaces - -Many of the implementation details, or internal objects themselves, -of Ipv4-capable Node objects are not exposed at the simulator public -API. This allows for different implementations; for instance, -replacing the native ns-3 models with ported TCP/IP stack code. - -The C++ public APIs of all of these objects is found in the -@code{src/node} directory, including principally: -@itemize @bullet -@item @code{socket.h} -@item @code{tcp.h} -@item @code{udp.h} -@item @code{ipv4.h} -@end itemize -These are typically base class objects that implement the default -values used in the implementation, implement access methods to get/set -state variables, host attributes, and implement publicly-available methods -exposed to clients such as @code{CreateSocket}. - -@subsection Example path of a packet - -These two figures show an example stack trace of how packets flow -through the Internet Node objects. - -@float Figure,fig:internet-node-send -@caption{Send path of a packet.} -@image{figures/internet-node-send,5in} -@end float - -@float Figure,fig:internet-node-recv -@caption{Receive path of a packet.} -@image{figures/internet-node-recv,5in} -@end float +The following chapters provide reference information on the available +NetDevices in ns-3. diff --git a/doc/manual/organization.texi b/doc/manual/organization.texi new file mode 100644 index 000000000..78a2ade05 --- /dev/null +++ b/doc/manual/organization.texi @@ -0,0 +1,50 @@ +@node Organization +@chapter Organization + +This manual is organized into several parts with several chapters per part. +This chapter describes the overall software organization and the +corresponding organization of this manual. + +ns-3 is a discrete-event network simulator in which the simulation core +and models are implemented in C++. ns-3 is built as a library which +may be statically or dynamically linked to a C++ main program that +defines the simulation topology and starts the simulator. ns-3 also +exports nearly all of its API to Python, allowing Python programs to +import an "ns3" module in much the same way as in C++. + +The source code for ns-3 is mostly organized in the @code{src/} +directory and can be described by the following block diagram. +We will work our way from the bottom up; in general, modules +only have dependencies on modules beneath them in the figure. + +We first describe Part 1 of the manual. +The simulation core is implemented in @code{src/core}, and the core is +used to build the simulation engine @code{src/simulator}. Packets are +fundamental objects in a network simulator and are implemented in +@code{src/packet}. These three simulation modules by themselves +are intended to comprise a generic simulation core that can be used +by different kinds of networks, not just Internet-based networks. +The above modules of ns-3 are independent of specific network and +device models, which are covered in later parts of this manual. + +In addition to the above ns-3 core, we describe also in Part 1 two +other modules that supplement the core C++-based API. ns-3 programs +may access all of the API directly or may make use of a so-called +``helper API'' that provides convenient wrappers or encapsulation of +low-level API calls. The fact that ns-3 programs can be written to +two APIs (or a combination thereof) is a fundamental aspect of the +simulator and is also covered in Part 1. We also describe how +Python is supported in ns-3 as the last chapter of Part 1. + +The remainder of the manual is focused on documenting the models +and supporting capabilities. Part 2 focuses on two fundamental +objects in ns-3: the @code{Node} and @code{NetDevice}. Two +special NetDevice types are designed to support network emulation +use cases, and emulation is described in Part 3. +Part 4 is devoted to Internet-related models, including the sockets +API used by Internet applications. Part 5 covers applications, and +Part 6 describes additional support for simulation, such as animators. + +The project maintains a separate manual devoted to testing and +validation of ns-3 code (see the +@uref{http://www.nsnam.org/docs,, ns-3 Testing and Validation manual})j diff --git a/doc/manual/python.texi b/doc/manual/python.texi new file mode 100644 index 000000000..8f748b141 --- /dev/null +++ b/doc/manual/python.texi @@ -0,0 +1,6 @@ +@node Python +@chapter Python + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/simple.texi b/doc/manual/simple.texi new file mode 100644 index 000000000..550c913e6 --- /dev/null +++ b/doc/manual/simple.texi @@ -0,0 +1,6 @@ +@node Simple NetDevice +@chapter Simple NetDevice + +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/statistics.texi b/doc/manual/statistics.texi index 094e91a4b..63476c6e0 100644 --- a/doc/manual/statistics.texi +++ b/doc/manual/statistics.texi @@ -1,9 +1,6 @@ @node Statistics @chapter Statistics -@anchor{chap:Statistics} -ns-3 does not presently have support for statistics (automatically generated -statistical output). This is planned -for development later in 2008. If you are interested in contributing, -please see @uref{http://www.nsnam.org/wiki/index.php/Suggested_Projects,,our suggested projects page} or contact the ns-developers -list. +@cartouche +Placeholder chapter +@end cartouche diff --git a/doc/manual/tap.texi b/doc/manual/tap.texi new file mode 100644 index 000000000..24efd2e7d --- /dev/null +++ b/doc/manual/tap.texi @@ -0,0 +1,7 @@ +@node Tap NetDevice +@chapter Tap NetDevice + +@cartouche +Placeholder chapter +@end cartouche + diff --git a/doc/manual/tracing.texi b/doc/manual/tracing.texi index d98ff8fb5..817831ef5 100644 --- a/doc/manual/tracing.texi +++ b/doc/manual/tracing.texi @@ -13,13 +13,13 @@ further study. In @command{ns-3}, the subsystem that enables a researcher to do this is the tracing subsystem. @menu -* Motivation:: +* Tracing Motivation:: * Overview:: * Using the Tracing API:: -* Implementation details:: +* Tracing implementation details:: @end menu -@node Motivation +@node Tracing Motivation @section Motivation There are many ways to get information out of a program. The most straightforward @@ -324,5 +324,5 @@ core of the simulator; sinks. @end itemize -@node Implementation details +@node Tracing implementation details @section Implementation details From 1a742a3c0243db7e6b15c0cb59bdbe24d5751c66 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sat, 17 Oct 2009 00:30:07 +0100 Subject: [PATCH 40/63] Upgrade pybindgen to fix bug #723 --- bindings/python/wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/wscript b/bindings/python/wscript index 94918f376..792e0029f 100644 --- a/bindings/python/wscript +++ b/bindings/python/wscript @@ -15,7 +15,7 @@ import Build import Utils ## https://launchpad.net/pybindgen/ -REQUIRED_PYBINDGEN_VERSION = (0, 12, 0, 700) +REQUIRED_PYBINDGEN_VERSION = (0, 12, 0, 703) REQUIRED_PYGCCXML_VERSION = (0, 9, 5) From ae2b92f3305b994f52efa190de8dab1859a147fc Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Fri, 16 Oct 2009 17:17:06 -0700 Subject: [PATCH 41/63] a few nits in the release notes --- RELEASE_NOTES | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 6eed4584b..fc8d765f3 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -17,7 +17,7 @@ http://www.nsnam.org/releases/ns-allinone-3.6.tar.bz2 Supported platforms ------------------- ns-3.6 has been tested on the following platforms: - - linux x86 gcc 4.2, 4.1, and, 3.4.6. + - linux x86 gcc 4.4.1, 4.2, 4.1, and, 3.4.6. - linux x86_64 gcc 4.4.0, 4.3.2, 4.2.3, 4.2.1, 4.1.3, 3.4.6 - MacOS X ppc and x86 (gcc 4.0.x and 4.2.x) - cygwin gcc 3.4.4 (debug only), gcc 4.3.2 (debug and optimized) @@ -75,7 +75,6 @@ New user-visible features API changes from ns-3.5 ----------------------- API changes for this release are documented in the file CHANGES.html. -XXX Known issues ------------ @@ -84,10 +83,6 @@ ns-3 build is known to fail on the following platforms: - optimized builds on gcc 3.4.4 and 3.4.5 - optimized builds on linux x86 gcc 4.0.x -Future releases ---------------- -XXX - Release 3.5 =========== From 41c62223ee852b2eb96dad8abee5c9ade330f980 Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Thu, 15 Oct 2009 15:59:52 -0700 Subject: [PATCH 42/63] add figure for manual overview --- doc/manual/Makefile | 1 + doc/manual/organization.texi | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/manual/Makefile b/doc/manual/Makefile index 2f27bfb86..04e7cb439 100644 --- a/doc/manual/Makefile +++ b/doc/manual/Makefile @@ -17,6 +17,7 @@ IMAGES_EPS = \ $(FIGURES)/node.eps \ $(FIGURES)/buffer.eps \ $(FIGURES)/sockets-overview.eps \ + $(FIGURES)/software-organization.eps \ $(FIGURES)/routing.eps \ $(FIGURES)/routing-specialization.eps \ $(FIGURES)/testbed.eps \ diff --git a/doc/manual/organization.texi b/doc/manual/organization.texi index 78a2ade05..b62c4d83d 100644 --- a/doc/manual/organization.texi +++ b/doc/manual/organization.texi @@ -12,8 +12,13 @@ defines the simulation topology and starts the simulator. ns-3 also exports nearly all of its API to Python, allowing Python programs to import an "ns3" module in much the same way as in C++. +@float Figure,fig:organization +@caption{Software organization of ns-3} +@center @image{figures/software-organization, 5in} +@end float + The source code for ns-3 is mostly organized in the @code{src/} -directory and can be described by the following block diagram. +directory and can be described by the diagram in @ref{fig:organization}. We will work our way from the bottom up; in general, modules only have dependencies on modules beneath them in the figure. @@ -47,4 +52,4 @@ Part 6 describes additional support for simulation, such as animators. The project maintains a separate manual devoted to testing and validation of ns-3 code (see the -@uref{http://www.nsnam.org/docs,, ns-3 Testing and Validation manual})j +@uref{http://www.nsnam.org/tutorials.html,, ns-3 Testing and Validation manual})j From 6a6de8bbfa961e7f34b08d4985460b440a9bfe54 Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Sat, 17 Oct 2009 16:02:25 -0700 Subject: [PATCH 43/63] add new dia source --- doc/manual/figures/software-organization.dia | Bin 0 -> 2990 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/manual/figures/software-organization.dia diff --git a/doc/manual/figures/software-organization.dia b/doc/manual/figures/software-organization.dia new file mode 100644 index 0000000000000000000000000000000000000000..18a4c73b6f2902bdd4020d29e9dc3fab2157e484 GIT binary patch literal 2990 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 Date: Sun, 18 Oct 2009 22:11:29 -0700 Subject: [PATCH 44/63] some cleanup of part 1 of manual --- doc/manual/attributes.texi | 149 +++++++++++++++++++++++------------ doc/manual/callbacks.texi | 40 +++++++--- doc/manual/emulation.texi | 4 +- doc/manual/manual.texi | 5 +- doc/manual/objects.texi | 8 +- doc/manual/organization.texi | 2 +- doc/manual/packets.texi | 32 +++++--- doc/manual/random.texi | 71 +++++++---------- doc/manual/tracing.texi | 61 ++++++++------ 9 files changed, 226 insertions(+), 146 deletions(-) diff --git a/doc/manual/attributes.texi b/doc/manual/attributes.texi index b610338bd..a7bea4fca 100644 --- a/doc/manual/attributes.texi +++ b/doc/manual/attributes.texi @@ -125,7 +125,8 @@ public: This is defined in the node.cc file as follows: -@verbatim +@smallformat +@example TypeId Node::GetTypeId (void) { @@ -148,15 +149,16 @@ Node::GetTypeId (void) ; return tid; } -@end verbatim +@end example +@end smallformat -Look at the TypeId of an ns-3 @code{Object} class as an extended form of run -time type information (RTTI). The C++ language includes simple kind of RTTI +Consider the TypeId of an ns-3 @code{Object} class as an extended form of run +time type information (RTTI). The C++ language includes a simple kind of RTTI in order to support @code{dynamic_cast} and @code{typeid} operators. The ``@code{.SetParent ()}'' call in the declaration above is used in conjunction with our object aggregation mechanisms to allow safe up- and -down-casing in inheritance trees during @code{GetObject}. +down-casting in inheritance trees during @code{GetObject}. The ``@code{.AddConstructor ()}'' call is used in conjunction with our abstract object factory mechanisms to allow us to construct C++ objects without @@ -183,7 +185,7 @@ without even knowing the concrete C++ type @verbatim ObjectFactory factory; const std::string typeId = "ns3::Node''; - factory.SetTypeId(typeId); + factory.SetTypeId (typeId); Ptr node = factory.Create (); @end verbatim @@ -259,7 +261,8 @@ 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 +@smallformat +@example NS_OBJECT_ENSURE_REGISTERED (DropTailQueue); TypeId DropTailQueue::GetTypeId (void) @@ -276,7 +279,8 @@ TypeId DropTailQueue::GetTypeId (void) return tid; } -@end verbatim +@end example +@end smallformat The AddAttribute() method is performing a number of things with this value: @@ -295,11 +299,19 @@ section, we will provide an example script that shows how users may manipulate these values. Note that initialization of the attribute relies on the macro -NS_OBJECT_ENSURE_REGISTERED (DropTailQueue) being called; if you leave +@code{NS_OBJECT_ENSURE_REGISTERED} (DropTailQueue) being called; if you leave this out of your new class implementation, your attributes will not be initialized correctly. -@subsection Basic usage +While we have described how to create attributes, we still haven't +described how to access and manage these values. For instance, there is no +@code{globals.h} header file where these are stored; attributes are +stored with their classes. Questions that naturally arise are how +do users easily learn about all of the attributes of their models, and +how does a user access these attributes, or document their values +as part of the record of their simulation? + +@subsection Default values and command-line arguments 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}, @@ -341,7 +353,8 @@ 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 +@smallformat +@example Ptr n0 = CreateObject (); Ptr net0 = CreateObject (); @@ -349,7 +362,8 @@ default values. Ptr q = CreateObject (); net0->AddQueue(q); -@end verbatim +@end example +@end smallformat At this point, we have created a single node (Node 0) and a single PointToPointNetDevice (NetDevice 0) and added a @@ -358,10 +372,10 @@ 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 +@subsection Pointer-based access We assume that a smart pointer (Ptr) to a relevant network device is -in hand; here, it is the net0 pointer. +in hand; in the current example, it is the @code{net0} pointer. One way to change the value is to access a pointer to the underlying queue and modify its attribute. @@ -369,11 +383,11 @@ 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 +@example PointerValue tmp; net0->GetAttribute ("TxQueue", tmp); Ptr txQueue = tmp.GetObject (); -@end verbatim +@end example Using the GetObject function, we can perform a safe downcast to a DropTailQueue, where MaxPackets is a member @@ -409,44 +423,52 @@ Now, let's set it to another value (60 packets) NS_LOG_INFO ("3. txQueue limit changed: " << limit.Get () << " packets"); @end verbatim -@subsubsection Namespace-based access +@subsection 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 +@smallformat +@example Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets", UintegerValue (25)); txQueue->GetAttribute ("MaxPackets", limit); NS_LOG_INFO ("4. txQueue limit changed through namespace: " << limit.Get () << " packets"); -@end verbatim +@end example +@end smallformat 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 +@smallformat +@example Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets", UintegerValue (15)); txQueue->GetAttribute ("MaxPackets", limit); NS_LOG_INFO ("5. txQueue limit changed through wildcarded namespace: " << limit.Get () << " packets"); -@end verbatim +@end example +@end smallformat -@subsubsection Object Name Service-based access +@subsection Object Name Service-based access Another way to get at the attribute is to use the object name service facility. Here, this attribute is found using a name string. This approach is useful if one doesn't have access to the underlying pointers and it is difficult to determine the required concrete configuration namespaced path. -@verbatim +@smallformat +@example Names::Add ("server", serverNode); Names::Add ("server/eth0", serverDevice); ... Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25)); -@end verbatim +@end example +@end smallformat + +@xref{Object names} for a fuller treatment of the ns-3 configuration namespace. @subsection Setting through constructors helper classes @@ -466,7 +488,8 @@ or from the higher-level helper APIs, such as: "LayoutType", StringValue ("RowFirst")); @end verbatim -@subsection Value classes +@subsection Implementation details +@subsubsection Value classes Readers will note the new FooValue classes which are subclasses of the AttributeValue base class. These can be thought of as an intermediate class that can be used to convert from raw types to the @@ -489,14 +512,14 @@ the attribute system: @item ATTRIBUTE_HELPER_CPP @end itemize -@subsection Initialization order +@subsubsection Initialization order In general, the attribute code to assign values to the underlying class member variables is executed after an object is constructed. But what if you need the values assigned before the constructor body executes, because you need them in the logic of the constructor? There is a way to do this, used for example in the class -@code{ns3::ConfigStore}: call @code{ObjectBase::ConstructSelf()} +@code{ns3::ConfigStore}: call @code{ObjectBase::ConstructSelf ()} as follows: @verbatim @@ -546,7 +569,8 @@ 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 +@smallformat +@example TypeId RandomWalk2dMobilityModel::GetTypeId (void) { @@ -568,7 +592,8 @@ RandomWalk2dMobilityModel::GetTypeId (void) ; return tid; } -@end verbatim +@end example +@end smallformat The declaration for this in the class declaration is one-line public member method: @@ -599,7 +624,9 @@ the conversions to/from strings and attribute values. Most of this can be copy/pasted with macro-ized code. For instance, consider class declaration for Rectangle in the @code{src/mobility/} directory: -@verbatim +@subsection Header file +@smallformat +@example /** * \brief a 2d rectangle */ @@ -612,35 +639,42 @@ class Rectangle double yMin; double yMax; }; -@end verbatim +@end example +@end smallformat One macro call and two operators, must be added below the class declaration in order to turn a Rectangle into a value usable by the @code{Attribute} system: -@verbatim +@smallformat +@example std::ostream &operator << (std::ostream &os, const Rectangle &rectangle); std::istream &operator >> (std::istream &is, Rectangle &rectangle); ATTRIBUTE_HELPER_HEADER (Rectangle); -@end verbatim +@end example +@end smallformat +@subsection Implementation file In the class definition (@code{.cc} file), the code looks like this: -@verbatim +@smallformat +@example ATTRIBUTE_HELPER_CPP (Rectangle); std::ostream & operator << (std::ostream &os, const Rectangle &rectangle) { - os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|" << rectangle.yMax; + 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; + is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3 + >> rectangle.yMax; if (c1 != '|' || c2 != '|' || c3 != '|') @@ -649,7 +683,8 @@ operator >> (std::istream &is, Rectangle &rectangle) } return is; } -@end verbatim +@end example +@end smallformat These stream operators simply convert from a string representation of the Rectangle ("xMin|xMax|yMin|yMax") to the underlying Rectangle, and the @@ -679,7 +714,8 @@ file to the scratch directory: Let's edit it to add the ConfigStore feature. First, add an include statement to include the contrib module, and then add these lines: -@verbatim +@smallformat +@example #include "contrib-module.h" ... int main (...) @@ -693,7 +729,8 @@ int main (...) Simulator::Run (); } -@end verbatim +@end example +@end smallformat There are three attributes that govern the behavior of the ConfigStore: "Mode", "Filename", and "FileFormat". The Mode (default "None") configures @@ -705,12 +742,16 @@ the ConfigStore format is Xml or RawText format. So, using the above modified program, try executing the following waf command and -@verbatim -./waf --command-template="%s --ns3::ConfigStore::Filename=csma-bridge-config.xml --ns3::ConfigStore::Mode=Save --ns3::ConfigStore::FileFormat=Xml" --run scratch/csma-bridge -@end verbatim +@smallformat +@example +./waf --command-template="%s --ns3::ConfigStore::Filename=csma-bridge-config.xml +--ns3::ConfigStore::Mode=Save --ns3::ConfigStore::FileFormat=Xml" --run scratch/csma-bridge +@end example +@end smallformat After running, you can open the csma-bridge-config.xml file and it will display the configuration that was applied to your simulation; e.g. -@verbatim +@smallformat +@example @@ -723,7 +764,9 @@ display the configuration that was applied to your simulation; e.g. ... -@end verbatim +@end example +@end smallformat + This file can be archived with your simulation script and output data. While it is possible to generate a sample config file and lightly @@ -749,7 +792,8 @@ separate file called "output-attributes.xml". (Note-- to get this input xml file to begin with, it is sometimes helpful to run the program to generate an output xml file first, then hand-edit that file and re-input it for the next simulation run). -@verbatim +@smallformat +@example #include "contrib-module.h" ... int main (...) @@ -778,7 +822,8 @@ int main (...) outputConfig.ConfigureAttributes (); Simulator::Run (); } -@end verbatim +@end example +@end smallformat @subsection GTK-based ConfigStore @@ -794,24 +839,28 @@ sudo apt-get install libgtk2.0-0 libgtk2.0-dev @end verbatim To check whether it is configured or not, check the output of the ./waf configure step: -@verbatim +@smallformat +@example ---- Summary of optional NS-3 features: Threading Primitives : enabled Real Time Simulator : enabled GtkConfigStore : not enabled (library 'gtk+-2.0 >= 2.12' not found) -@end verbatim +@end example +@end smallformat In the above example, it was not enabled, so it cannot be used until a suitable version is installed and ./waf configure; ./waf is rerun. Usage is almost the same as the non-GTK-based version, but there are no ConfigStore attributes involved: -@verbatim +@smallformat +@example // Invoke just before entering Simulator::Run () GtkConfigStore config; config.ConfigureDefaults (); config.ConfigureAttributes (); -@end verbatim +@end example +@end smallformat Now, when you run the script, a GUI should pop up, allowing you to open menus of attributes on different nodes/objects, and then launch the diff --git a/doc/manual/callbacks.texi b/doc/manual/callbacks.texi index 72cec0266..b355ee0b4 100644 --- a/doc/manual/callbacks.texi +++ b/doc/manual/callbacks.texi @@ -12,6 +12,7 @@ it, and details on its implementation. * Using the Callback API:: * Bound Callbacks:: * Callback locations in ns-3:: +* Traced Callbacks:: * Implementation details:: @end menu @@ -35,7 +36,6 @@ public: class B { public: - void ReceiveInput ( // parameters); void DoSomething (void); ... @@ -94,6 +94,10 @@ optimal way to design a generic simulator. @node Callbacks Background @section Background +@cartouche +Readers familiar with programming callbacks may skip this tutorial section. +@end cartouche + The basic mechanism that allows one to address the problem above is known as a @emph{callback}. The ultimate goal is to allow one piece of code to call a function (or method in C++) without any specific inter-module dependency. @@ -358,16 +362,19 @@ Consider also the following main program snippet: @end verbatim This is an example of a C-style callback -- one which does not include or need -a @code{this} pointer. The funtion template @code{Callback} is esentially the +a @code{this} pointer. The function template @code{Callback} is esentially the declaration of the variable containing the pointer-to-function. In the example above, we explicitly showed a pointer to a function that returned an integer and took a single integer as a parameter, The @code{Callback} template function is a generic version of that -- it is used to declare the type of a callback. +@strong{Note1:} Readers unfamiliar with C++ templates may consult +@uref{http://www.cplusplus.com/doc/tutorial/templates/,,this reference}. + The @code{Callback} template requires one mandatory argument (the return type of the function to be assigned to this callback) and up to five optional arguments, which each specify the type of the arguments (if your particular -callback function has more than five arguments, then this can be easily handled +callback function has more than five arguments, then this can be handled by extending the callback implementation). So in the above example, we have a declared a callback named "one" that will @@ -451,7 +458,7 @@ invoked. Consider this example, also from main-callback.cc: @end verbatim Here, we pass an additional object pointer to the @code{MakeCallback<>} function. -Recall from the example above that @code{Operator()} will use the pointer to +Recall from the background section above that @code{Operator()} will use the pointer to member syntax when it executes on an object: @verbatim @@ -505,7 +512,7 @@ later -- when the @code{Callback} is called via @code{operator()}. All of the parameters are provided by the calling function. What if it is desired to allow the client function (the one that provides the -callback) to provide some of the parameters? Alexandrescu calls the process of +callback) to provide some of the parameters? @uref{http://erdani.com/book/main.html,,Alexandrescu} calls the process of allowing a client to specify one of the parameters @emph{binding}. One of the parameters of @code{operator()} has been bound (fixed) by the client. @@ -539,7 +546,7 @@ takes the parameters to be bound. In the case of the example above, MakeBoundCallback (&CsmaHelper::SniffEvent, pcap)); @end verbatim -Will create a specific callback implementation that knows to add in the extra +will create a specific callback implementation that knows to add in the extra bound arguments. Conceptually, it extends the specific functor described above with one or more bound arguments @@ -581,16 +588,24 @@ function call: (*m_p.*m_pmi)(m_boundArg, arg); @end verbatim +@node Traced Callbacks +@section Traced Callbacks +@cartouche +Placeholder subsection +@end cartouche +@section Callback locations in @command{ns-3} @node Callback locations in ns-3 @section Callback locations in @command{ns-3} Where are callbacks frequently used in @command{ns-3}? Here are some of the more visible ones to typical users: -@subsection Socket API -@subsection Layer-2/Layer-3 API -@subsection Tracing subsystem -@subsection Routing +@itemize @bullet +@item Socket API +@item Layer-2/Layer-3 API +@item Tracing subsystem +@item API between IP and routing subsystems +@end itemize @node Implementation details @section Implementation details @@ -600,8 +615,9 @@ itself. The actual Callback code is quite complicated and very template-intense a deep understanding of the code is not required. If interested, expert users may find the following useful: -The code was originally written based on the techniques described -@uref{http://www.codeproject.com/cpp/TTLFunction.asp,,here}. +The code was originally written based on the techniques described in +@uref{http://www.codeproject.com/cpp/TTLFunction.asp,, +http://www.codeproject.com/cpp/TTLFunction.asp}. It was subsequently rewritten to follow the architecture outlined in @uref{http://www.amazon.com/Modern-C\%2B\%2B-Design-Programming-Patterns/dp/0201704315/ref=pd_bbs_sr_1/102-0157303-1900156?ie=UTF8\&s=books\&qid=1187982662\&sr=1-1,,Modern C++ Design: Generic Programming and Design Patterns Applied-- Alexandrescu}, chapter 5, "Generalized Functors". diff --git a/doc/manual/emulation.texi b/doc/manual/emulation.texi index a7c7a9868..f551f6897 100644 --- a/doc/manual/emulation.texi +++ b/doc/manual/emulation.texi @@ -23,7 +23,7 @@ for details on the ORBIT testbed. A simulation of this kind is shown in the following figure: @float Figure,fig:testbed -@center @caption{Example Implementation of Testbed Emulation.} +@caption{Example Implementation of Testbed Emulation.} @center @image{figures/testbed, 5in} @end float @@ -44,7 +44,7 @@ is shown in the following figure: @float Figure,fig:emulated-channel @caption{Implementation overview of emulated channel.} -@image{figures/emulated-channel, 5in} +@image{figures/emulated-channel, 6in} @end float Here, you will see that there is a single host with a number of virtual machines diff --git a/doc/manual/manual.texi b/doc/manual/manual.texi index 99cb74568..f8a0b7e20 100644 --- a/doc/manual/manual.texi +++ b/doc/manual/manual.texi @@ -24,11 +24,12 @@ the document should be discussed on the ns-developers@@isi.edu mailing list. This is an @command{ns-3} reference manual. Primary documentation for the @command{ns-3} project is available in -four forms: +five forms: @itemize @bullet @item @uref{http://www.nsnam.org/docs/tutorial/tutorial.html,,ns-3 Tutorial} @item @uref{http://www.nsnam.org/doxygen/index.html,,ns-3 Doxygen}: Documentation of the public APIs of the simulator @item Reference Manual (this document) +@item @uref{http://www.nsnam.org/tutorials.html,, ns-3 Testing and Validation manual} @item @uref{http://www.nsnam.org/wiki/index.php,, ns-3 wiki} @end itemize @@ -62,6 +63,8 @@ along with this program. If not, see @uref{http://www.gnu.org/licenses/}. @vskip 0pt plus 1filll @insertcopying +@page +@center This page is intentionally blank. @end titlepage @c So the toc is printed at the start. diff --git a/doc/manual/objects.texi b/doc/manual/objects.texi index 5db0b34aa..38f4c3228 100644 --- a/doc/manual/objects.texi +++ b/doc/manual/objects.texi @@ -98,7 +98,7 @@ When the reference count falls to zero, the object is deleted. @itemize @bullet @item When the client code obtains a pointer from the object itself -through object creation, or via QueryInterface, it does not have +through object creation, or via GetObject, it does not have to increment the reference count. @item When client code obtains a pointer from another source (e.g., copying a pointer) it must call @code{Ref()} to increment the @@ -154,7 +154,9 @@ using @code{CreateObject()} instead. For objects deriving from @code{class RefCountBase}, or other objects that support usage of the smart pointer class -(in particular, the ns-3 Packet class), +(in particular, the ns-3 Packet class does not derive from RefCountBase +in order to avoid a vtable, but separately implements @code{Ref ()} and +@code{Unref ()}), a templated helper function is available and recommended to be used: @verbatim Ptr b = Create (); @@ -241,7 +243,7 @@ now use the Ptr to the Ipv4 object that was previously aggregated to the node. Another example of how one might use aggregation is to add optional -models to objects. For in +models to objects. For instance, an existing Node object may have an ``Energy Model'' object aggregated to it at run time (without modifying and recompiling the node class). An existing model (such as a wireless diff --git a/doc/manual/organization.texi b/doc/manual/organization.texi index b62c4d83d..34ded045a 100644 --- a/doc/manual/organization.texi +++ b/doc/manual/organization.texi @@ -52,4 +52,4 @@ Part 6 describes additional support for simulation, such as animators. The project maintains a separate manual devoted to testing and validation of ns-3 code (see the -@uref{http://www.nsnam.org/tutorials.html,, ns-3 Testing and Validation manual})j +@uref{http://www.nsnam.org/tutorials.html,, ns-3 Testing and Validation manual}). diff --git a/doc/manual/packets.texi b/doc/manual/packets.texi index 14837af12..7c4e3a4be 100644 --- a/doc/manual/packets.texi +++ b/doc/manual/packets.texi @@ -99,7 +99,7 @@ things that you wouldn't find in the bits on the wire). @float Figure,fig:packets @caption{Implementation overview of Packet class.} -@image{figures/packet} +@image{figures/packet, 4in} @end float Figure @ref{fig:packets} is a high-level overview of the Packet @@ -222,9 +222,11 @@ applied is: Packet (uint8_t const *buffer, uint32_t size); @end verbatim Here is an example: -@verbatim +@smallformat +@example Ptr pkt1 = Create (reinterpret_cast ("hello"), 5); -@end verbatim +@end example +@end smallformat Packets are freed when there are no more references to them, as with all ns-3 objects referenced by the Ptr class. @@ -258,7 +260,8 @@ examples within the source code. Once you have a header (or you have a preexisting header), the following Packet API can be used to add or remove such headers. -@verbatim +@smallformat +@example /** * Add header to this packet. This method invokes the * Header::GetSerializedSize and Header::Serialize @@ -284,11 +287,13 @@ Packet API can be used to add or remove such headers. * \returns the number of bytes read from the packet. */ uint32_t PeekHeader (Header &header) const; -@end verbatim +@end example +@end smallformat For instance, here are the typical operations to add and remove a UDP header. -@verbatim +@smallformat +@example // add header Ptr packet = Create (); UdpHeader udpHeader; @@ -299,7 +304,8 @@ For instance, here are the typical operations to add and remove a UDP header. UdpHeader udpHeader; packet->RemoveHeader (udpHeader); // Read udpHeader fields as needed -@end verbatim +@end example +@end smallformat @subsection Adding and removing Tags @@ -355,7 +361,8 @@ An example is the UdpEchoServer class, which takes the received packet and "turns it around" to send back to the echo client. The Packet API for byte tags is given below. -@verbatim +@smallformat +@example /** * \param tag the new tag to add to this packet * @@ -399,10 +406,12 @@ The Packet API for byte tags is given below. * invoke the Print method of each tag stored in the packet. */ void PrintByteTags (std::ostream &os) const; -@end verbatim +@end example +@end smallformat The Packet API for packet tags is given below. -@verbatim +@smallformat +@example /** * \param tag the tag to store in this packet * @@ -451,7 +460,8 @@ The Packet API for packet tags is given below. * packet tags. */ PacketTagIterator GetPacketTagIterator (void) const; -@end verbatim +@end example +@end smallformat Here is a simple example illustrating the use of tags from the code in @code{src/internet-stack/udp-socket-impl.cc}: diff --git a/doc/manual/random.texi b/doc/manual/random.texi index 797750075..42789b663 100644 --- a/doc/manual/random.texi +++ b/doc/manual/random.texi @@ -35,7 +35,7 @@ default; this marks a change in policy starting with ns-3.4} @end itemize @item to obtain randomness across multiple simulation runs, you must either set the seed differently or set the run number differently. To set a seed, call -@code{SeedManager::SetSeed(uint32_t)} at the beginning of the program; +@code{SeedManager::SetSeed (uint32_t)} at the beginning of the program; to set a run number with the same seed, call @code{SeedManager::SetRun (uint32_t)} at the beginning of the program; @xref{Seeding and independent replications} @@ -53,7 +53,7 @@ ns-3. @node Background @section Background -Simulations use a lot of random numbers; the study in [cite] +Simulations use a lot of random numbers; one study found that most network simulations spend as much as 50% of the CPU generating random numbers. Simulation users need to be concerned with the quality of the (pseudo) random numbers and @@ -62,7 +62,7 @@ the independence between different streams of random numbers. Users need to be concerned with a few issues, such as: @itemize @bullet @item the seeding of the random number generator and whether a -simulation run is deterministic or not, +simulation outcome is deterministic or not, @item how to acquire different streams of random numbers that are independent from one another, and @item how long it takes for streams to cycle @@ -96,8 +96,7 @@ streams of random numbers, each of which consists of 2.3x10^15 substreams. Each substream has a period (@emph{i.e.}, the number of random numbers before overlap) of 7.6x10^22. The period of the entire generator is -3.1x10^57. Figure ref-streams provides a graphical idea of -how the streams and substreams fit together. +3.1x10^57. Class @code{ns3::RandomVariable} is the public interface to this underlying random number generator. When users create new @@ -167,7 +166,7 @@ from within the program; the user can set the @end verbatim Another way to control this is by passing a command-line argument; since -this is an ns3 GlobalValue instance, it is equivalently done such as follows: +this is an ns-3 GlobalValue instance, it is equivalently done such as follows: @verbatim ./waf --command-template="%s --RngRun=3" --run program-name @end verbatim @@ -198,42 +197,25 @@ numbers before overlapping. @section Base class public API Below are excerpted a few public methods of @code{class RandomVariable} -that deal with the global configuration and state of the RNG. -@verbatim +that access the next value in the substream. +@smallformat +@example /** - * \brief Set seeding behavior - * - * Specify whether the POSIX device /dev/random is to - * be used for seeding. When this is used, the underlying - * generator is seeded with data from /dev/random instead of - * being seeded based upon the time of day. Defaults to true. + * \brief Returns a random double from the underlying distribution + * \return A floating point random value */ - static void UseDevRandom(bool udr = true); - - /** - * \brief Use the global seed to force precisely reproducible results. - */ - static void UseGlobalSeed(uint32_t s0, uint32_t s1, uint32_t s2, - uint32_t s3, uint32_t s4, uint32_t s5); - + double GetValue (void) const; + /** - * \brief Set the run number of this simulation + * \brief Returns a random integer integer from the underlying distribution + * \return Integer cast of ::GetValue() */ - static void SetRunNumber(uint32_t n); + uint32_t GetInteger (void) const; +@end example +@end smallformat - /** - * \brief Get the internal state of the RNG - * - * This function is for power users who understand the inner workings - * of the underlying RngStream method used. It returns the internal - * state of the RNG via the input parameter. - * \param seed Output parameter; gets overwritten with the internal state - * of the RNG. - */ - void GetSeed(uint32_t seed[6]) const; -@end verbatim - -We have already described the seeding configuration above. +We have already described the seeding configuration above. Different +RandomVariable subclasses may have additional API. @node Types of RandomVariables @section Types of RandomVariables @@ -255,6 +237,9 @@ class RandomVariable. @item @code{class DeterministicVariable } @item @code{class LogNormalVariable } @item @code{class TriangularVariable } +@item @code{class GammaVariable } +@item @code{class ErlangVariable } +@item @code{class ZipfVariable } @end itemize @node Semantics of RandomVariable objects @@ -270,7 +255,8 @@ any heap-allocated RandomVariables. RandomVariable objects can also be used in ns-3 attributes, which means that values can be set for them through the ns-3 attribute system. An example is in the propagation models for WifiNetDevice: -@verbatim +@smallformat +@example TypeId RandomPropagationDelayModel::GetTypeId (void) { @@ -285,7 +271,8 @@ RandomPropagationDelayModel::GetTypeId (void) ; return tid; } -@end verbatim +@end example +@end smallformat Here, the ns-3 user can change the default random variable for this delay model (which is a UniformVariable ranging from 0 to 1) through the attribute system. @@ -328,14 +315,12 @@ Let's review what things you should do when creating a simulation. @itemize @bullet @item Decide whether you are running with a fixed seed or random seed; -a random seed is the default, +a fixed seed is the default, @item Decide how you are going to manage independent replications, if applicable, @item Convince yourself that you are not drawing more random values -than the cycle length, if you are running a long simulation, and +than the cycle length, if you are running a very long simulation, and @item When you publish, follow the guidelines above about documenting your use of the random number generator. @end itemize -The program @emph{samples/main-random.cc} has some examples of usage. - diff --git a/doc/manual/tracing.texi b/doc/manual/tracing.texi index 817831ef5..d36f402ce 100644 --- a/doc/manual/tracing.texi +++ b/doc/manual/tracing.texi @@ -1,12 +1,12 @@ @node Tracing @chapter Tracing -The tracing subsystem is one of the most important mechansisms to understand in +The tracing subsystem is one of the most important mechanisms to understand in @command{ns-3}. In most cases, @command{ns-3} users will have a brilliant idea for some new and improved networking feature. In order to verify that this idea works, the researcher will make changes to an existing system and then run -experiments to see how the new feature behaves by gathering some form of statistic -that captures the behavior of the feature. +experiments to see how the new feature behaves by gathering statistics +that capture the behavior of the feature. In other words, the whole point of running a simulation is to generate output for further study. In @command{ns-3}, the subsystem that enables a researcher to do @@ -37,7 +37,7 @@ way is to just directly print the information to the standard output, as in, @end verbatim This is workable in small environments, but as your simulations get more and more -compliated, you end up with more and more prints and the task of parsing and +complicated, you end up with more and more prints and the task of parsing and performing computations on the output begins to get harder and harder. Another thing to consider is that every time a new tidbit is needed, the software @@ -74,7 +74,7 @@ Trace sources are entities that can signal events that happen in a simulation an provide access to interesting underlying data. For example, a trace source could indicate when a packet is received by a net device and provide access to the packet contents for interested trace sinks. A trace source might also indicate -when an iteresting state change happens in a model. For example, the congestion +when an interesting state change happens in a model. For example, the congestion window of a TCP model is a prime candidate for a trace source. Trace sources are not useful by themselves; they must be connected to other pieces @@ -92,7 +92,7 @@ or models of the simulator. There can be zero or more consumers of trace events generated by a trace source. One can think of a trace source as a kind of point-to-multipoint information link. -The ``transport protocol'' for this conceptual point-to-multipoint link as an +The ``transport protocol'' for this conceptual point-to-multipoint link is an @code{ns-3} @code{Callback}. Recall from the Callback Section that callback facility is a way to allow two @@ -100,7 +100,8 @@ modules in the system to communicate via function calls while at the same time decoupling the calling function from the called class completely. This is the same requirement as outlined above for the tracing system. -Basically, a trace source @emph{is} a callback. When a trace sink expresses +Basically, a trace source @emph{is} a callback to which multiple +functions may be registered. When a trace sink expresses interest in receiving trace events, it adds a callback to a list of callbacks held by the trace source. When an interesting event happens, the trace source invokes its @code{operator()} providing zero or more parameters. This tells @@ -112,7 +113,8 @@ functions. It will be useful to go walk a quick example just to reinforce what we've said. -@verbatim +@smallformat +@example #include ``ns3/object.h'' #include ``ns3/uinteger.h'' #include ``ns3/traced-value.h'' @@ -121,7 +123,8 @@ It will be useful to go walk a quick example just to reinforce what we've said. #include using namespace ns3; -@end verbatim +@end example +@end smallformat The first thing to do is include the required files. As mentioned above, the trace system makes heavy use of the Object and Attribute systems. The first @@ -139,7 +142,8 @@ operator--, operator+, operator==, etc. What this all means is that you will be able to trace changes to an object made using those operators. -@verbatim +@smallformat +@example class MyObject : public Object { public: @@ -158,7 +162,8 @@ made using those operators. MyObject () {} TracedValue m_myInt; }; -@end verbatim +@end example +@end smallformat Since the tracing system is integrated with Attributes, and Attributes work with Objects, there must be an @command{ns-3} @code{Object} for the trace source @@ -170,29 +175,33 @@ source to the outside world. The @code{TracedValue} declaration provides the infrastructure that overloads the operators mentioned above and drives the callback process. -@verbatim +@smallformat +@example void IntTrace (Int oldValue, Int newValue) { std::cout << ``Traced `` << oldValue << `` to `` << newValue << std::endl; } -@end verbatim +@end example +@end smallformat This is the definition of the trace sink. It corresponds directly to a callback function. This function will be called whenever one of the operators of the @code{TracedValue} is executed. -@verbatim +@smallformat +@example int main (int argc, char *argv[]) { Ptr myObject = CreateObject (); - myObject->TraceConnectWithoutContext ("MyInt", MakeCallback(&IntTrace)); + myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace)); myObject->m_myInt = 1234; } -@end verbatim +@end example +@end smallformat In this snippet, the first thing that needs to be done is to create the object in which the trace source lives. @@ -238,7 +247,8 @@ called a @emph{config path}. For example, one might find something that looks like the following in the system (taken from @code{examples/tcp-large-transfer.cc}) -@verbatim +@smallformat +@example void CwndTracer (uint32_t oldval, uint32_t newval) {} ... @@ -246,7 +256,8 @@ For example, one might find something that looks like the following in the syste Config::ConnectWithoutContext ( "/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow", MakeCallback (&CwndTracer)); -@end verbatim +@end example +@end smallformat This should look very familiar. It is the same thing as the previous example, except that a static member function of class @code{Config} is being called instead @@ -258,13 +269,15 @@ must be an @code{Attribute} of an @code{Object}. In fact, if you had a pointer the @code{Object} that has the ``CongestionWindow'' @code{Attribute} handy (call it @code{theObject}), you could write this just like the previous example: -@verbatim +@smallformat +@example void CwndTracer (uint32_t oldval, uint32_t newval) {} ... theObject->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer)); -@end verbatim +@end example +@end smallformat It turns out that the code for @code{Config::ConnectWithoutContext} does exactly that. This function takes a path that represents a chain of @code{Object} pointers and follows @@ -297,16 +310,18 @@ This socket, the type of which turns out to be an @code{ns3::TcpSocketImpl} defi an attribute called ``CongestionWindow'' which is a @code{TracedValue}. The @code{Config::ConnectWithoutContext} now does a, -@verbatim +@smallformat +@example object->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer)); -@end verbatim +@end example +@end smallformat using the object pointer from ``SocketList/0'' which makes the connection between the trace source defined in the socket to the callback -- @code{CwndTracer}. Now, whenever a change is made to the @code{TracedValue} representing the congestion window in the TCP socket, the registered callback will be executed and -the function @code{Cwndtracer} will be called printing out the old and new values +the function @code{CwndTracer} will be called printing out the old and new values of the TCP congestion window. @node Using the Tracing API From c381234e6293346ccfd5820b8e514737391da5f2 Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Mon, 19 Oct 2009 07:54:31 -0700 Subject: [PATCH 45/63] additional manual cleanup --- doc/manual/Makefile | 4 ++- doc/manual/animation.texi | 7 ++++ doc/manual/bridge.texi | 3 ++ doc/manual/emu.texi | 19 ++++------ doc/manual/flow-monitor.texi | 4 +++ doc/manual/internet.texi | 38 +++++++++++++------- doc/manual/ipv6.texi | 4 +++ doc/manual/manual.texi | 2 +- doc/manual/mesh.texi | 5 +++ doc/manual/new-models.texi | 45 ++++++++++++++--------- doc/manual/node.texi | 4 +-- doc/manual/python.texi | 3 ++ doc/manual/routing.texi | 15 +++++--- doc/manual/sockets.texi | 67 +++++++--------------------------- doc/manual/statistics.texi | 4 +++ doc/manual/tap.texi | 4 +++ doc/manual/tcp.texi | 70 ++++++++++++------------------------ doc/manual/troubleshoot.texi | 31 ++++++++++------ doc/manual/wifi.texi | 63 ++++++++++++++++++++------------ 19 files changed, 209 insertions(+), 183 deletions(-) diff --git a/doc/manual/Makefile b/doc/manual/Makefile index 04e7cb439..5fb001472 100644 --- a/doc/manual/Makefile +++ b/doc/manual/Makefile @@ -72,6 +72,8 @@ figures-clean: version: echo -n "ns-" > VERSION-PREFIX; cat VERSION-PREFIX ../../VERSION > VERSION; rm -rf VERSION-PREFIX -clean: figures-clean +texi-clean: rm -rf manual.aux manual.cp manual.cps manual.fn manual.ky manual.pg manual.tp rm -rf manual.vr manual.toc manual.log manual.pdf manual.html manual/ VERSION + +clean: figures-clean texi-clean diff --git a/doc/manual/animation.texi b/doc/manual/animation.texi index c7da1668b..efe702ed8 100644 --- a/doc/manual/animation.texi +++ b/doc/manual/animation.texi @@ -4,3 +4,10 @@ @cartouche Placeholder chapter @end cartouche + +This wiki page: @* +@uref{http://www.nsnam.org/wiki/index.php/NetAnim,,http://www.nsnam.org/wiki/index.php/NetAnim} +contains information about the animator support that has been added to ns-3.6. + +Another Python-based animator is available (ns-3-pyviz) but is not +documented. diff --git a/doc/manual/bridge.texi b/doc/manual/bridge.texi index dc5fdf7aa..f6a5228a9 100644 --- a/doc/manual/bridge.texi +++ b/doc/manual/bridge.texi @@ -4,3 +4,6 @@ @cartouche Placeholder chapter @end cartouche + +Some examples of the use of Bridge NetDevice can be found in +@code{examples/csma/} directory. diff --git a/doc/manual/emu.texi b/doc/manual/emu.texi index 39f0c6c44..0c51405c1 100644 --- a/doc/manual/emu.texi +++ b/doc/manual/emu.texi @@ -9,14 +9,7 @@ being in promiscuous mode. It opens a raw socket and binds to that interface. We perform MAC spoofing to separate simulation network traffic from other network traffic that may be flowing to and from the host. -Normally, the use case for emulated net devices is in collections of small -simulations that connect to the outside world through specific interfaces. -For example, one could construct a number of virtual machines and connect them -via a host-only network. To use the emulated net device, you would need to -set all of the host-only interfaces in promiscuous mode and provide an -appropriate device name, "eth1" for example. - -One could also use the @code{Emu} net device in a testbed situation where the +One can use the @code{Emu} net device in a testbed situation where the host on which the simulation is running has a specific interface of interest which drives the testbed hardware. You would also need to set this specific interface into promiscuous mode and provide an appropriate device name to the @@ -48,7 +41,7 @@ The emulated net device comes with a helper function as all ns-3 devices do. One unique aspect is that there is no channel associated with the underlying medium. We really have no idea what this external medium is, and so have not made an effort to model it abstractly. The primary thing to be aware of is the -implication this has for static global routing. The global router module +implication this has for IPv4 global routing. The global router module attempts to walk the channels looking for adjacent networks. Since there is no channel, the global router will be unable to do this and you must then use a dynamic routing protocol such as OLSR to include routing in @@ -71,8 +64,10 @@ you may want to take a moment to review a couple of HOWTO pages on the ns-3 wiki that describe how to set up a virtual test network using VMware and how to run a set of example (client server) simulations that use @code{Emu} net devices. -@uref{http://www.nsnam.org/wiki/index.php/HOWTO_use_VMware_to_set_up_virtual_networks_(Windows)} -@uref{http://www.nsnam.org/wiki/index.php/HOWTO_use_ns-3_scripts_to_drive_real_hardware_(experimental)} +@itemize @bullet +@item @uref{http://www.nsnam.org/wiki/index.php/HOWTO_use_VMware_to_set_up_virtual_networks_(Windows)} +@item @uref{http://www.nsnam.org/wiki/index.php/HOWTO_use_ns-3_scripts_to_drive_real_hardware_(experimental)} +@end itemize Once you are over the configuration hurdle, the script changes required to use an @code{Emu} device are trivial. The main structural difference is that you @@ -102,7 +97,7 @@ here for one side). // We've got the devices in place. Since we're using MAC address // spoofing under the sheets, we need to make sure that the MAC addresses // we have assigned to our devices are unique. Ns-3 will happily - // automatically assign the same MAC addresses to the devices in both halves + // automatically assign the same MAC address to the devices in both halves // of our two-script pair, so let's go ahead and just manually change them // to something we ensure is unique. // diff --git a/doc/manual/flow-monitor.texi b/doc/manual/flow-monitor.texi index d01ba63b5..b27c4a327 100644 --- a/doc/manual/flow-monitor.texi +++ b/doc/manual/flow-monitor.texi @@ -4,3 +4,7 @@ @cartouche Placeholder chapter @end cartouche + +This feature was added as contributed code (@code{src/contrib}) in ns-3.6. +A paper on this feature is published in the proceedings of NSTools: @* +@uref{http://www.nstools.org/techprog.shtml,,http://www.nstools.org/techprog.shtml} diff --git a/doc/manual/internet.texi b/doc/manual/internet.texi index 6d9e40952..405cda9ee 100644 --- a/doc/manual/internet.texi +++ b/doc/manual/internet.texi @@ -3,19 +3,20 @@ @section Internet stack aggregation -The above @code{class Node} is not very useful as-is; other objects +A bare @code{class Node} is not very useful as-is; other objects must be aggregated to it to provide useful node functionality. The ns-3 source code directory @code{src/internet-stack} provides -implementation of TCP/IPv4-related components. These include IPv4, -ARP, UDP, TCP, and other related protocols. +implementation of TCP/IPv4- and IPv6-related components. These include IPv4, +ARP, UDP, TCP, IPv6, Neighbor Discovery, and other related protocols. Internet Nodes are not subclasses of class Node; they are simply Nodes that have had a bunch of IPv4-related objects aggregated to them. They can be put together by hand, or via a helper function @code{InternetStackHelper::Install ()} which does the following to all nodes passed in as arguments: -@verbatim +@smallformat +@example void InternetStackHelper::Install (Ptr node) const { @@ -38,9 +39,14 @@ InternetStackHelper::Install (Ptr node) const Ptr ipv4Routing = m_routing->Create (node); ipv4->SetRoutingProtocol (ipv4Routing); } -@end verbatim +@end example +@end smallformat -Note that the Ipv4 routing protocol is configured and set outside this +Where multiple implementations exist in ns-3 (TCP, IP routing), these +objects are added by a factory object (TCP) or by a routing helper +(m_routing). + +Note that the routing protocol is configured and set outside this function. By default, the following protocols are added to Ipv4: @verbatim InternetStackHelper::InternetStackHelper () @@ -55,6 +61,8 @@ InternetStackHelper::InternetStackHelper () } @end verbatim +By default, IPv4 and IPv6 are enabled. + @subsection Internet Node structure An IPv4-capable Node (an ns-3 Node augmented by aggregation to have one or more @@ -70,7 +78,8 @@ Ipv4L3Protocol public API is also used internally in the src/internet-stack directory at present. In class Ipv4L3Protocol, one method described below is @code{Receive ()}: -@verbatim +@smallformat +@example /** * Lower layer calls this method after calling L3Demux::Lookup * The ARP subclass needs to know from which NetDevice this @@ -78,9 +87,10 @@ In class Ipv4L3Protocol, one method described below is @code{Receive ()}: * - implement a per-NetDevice ARP cache * - send back arp replies on the right device */ - void Receive( Ptr device, Ptr p, uint16_t protocol, const Address &from, - const Address &to, NetDevice::PacketType packetType); -@end verbatim + void Receive( Ptr device, Ptr p, uint16_t protocol, +const Address &from, const Address &to, NetDevice::PacketType packetType); +@end example +@end smallformat First, note that the @code{Receive ()} function has a matching signature to the ReceiveCallback in the @code{class Node}. This function pointer @@ -111,10 +121,12 @@ ns-3 to bind objects together: callbacks, and object aggregation. Once IPv4 routing has determined that a packet is for the local node, it forwards it up the stack. This is done with the following function: -@verbatim +@smallformat +@example void Ipv4L3Protocol::LocalDeliver (Ptr packet, Ipv4Header const&ip, uint32_t iif) -@end verbatim +@end example +@end smallformat The first step is to find the right Ipv4L4Protocol object , based on IP protocol number. For instance, TCP is registered in the demux as protocol number 6. @@ -127,6 +139,8 @@ In Linux, this @code{class Ipv4Interface} roughly corresponds to the @code{struct in_device}; the main purpose is to provide address-family specific information (addresses) about an interface. +The IPv6 implementation follows a similar architecture. + @subsubsection Layer-4 protocols and sockets We next describe how the transport protocols, sockets, and applications diff --git a/doc/manual/ipv6.texi b/doc/manual/ipv6.texi index 8f2aa9103..e391e5fc7 100644 --- a/doc/manual/ipv6.texi +++ b/doc/manual/ipv6.texi @@ -4,3 +4,7 @@ @cartouche Placeholder chapter @end cartouche +IPv6 models are being added to ns-3. A paper on the IPv6 models was +published in WNS2 2008: @* +@uref{http://lsiit.u-strasbg.fr/Publications/2008/VMM08/,,http://lsiit.u-strasbg.fr/Publications/2008/VMM08/} + diff --git a/doc/manual/manual.texi b/doc/manual/manual.texi index f8a0b7e20..3edd848d5 100644 --- a/doc/manual/manual.texi +++ b/doc/manual/manual.texi @@ -158,9 +158,9 @@ Simulator version: @unnumbered Part 3: Emulation @setchapternewpage off @include emulation.texi +@setchapternewpage odd @include emu.texi @include tap.texi -@setchapternewpage odd @unnumbered Part 4: Internet Models @setchapternewpage off diff --git a/doc/manual/mesh.texi b/doc/manual/mesh.texi index 9ef91152d..7770bc699 100644 --- a/doc/manual/mesh.texi +++ b/doc/manual/mesh.texi @@ -4,3 +4,8 @@ @cartouche Placeholder chapter @end cartouche + +The Mesh NetDevice based on 802.11s was added in ns-3.6. +An overview presentation by Kirill Andreev was published at the wns-3 workshop +in 2009: @* +@uref{http://www.nsnam.org/wiki/index.php/Wns3-2009,,http://www.nsnam.org/wiki/index.php/Wns3-2009} diff --git a/doc/manual/new-models.texi b/doc/manual/new-models.texi index 71f75c506..cdd57d64d 100644 --- a/doc/manual/new-models.texi +++ b/doc/manual/new-models.texi @@ -395,8 +395,10 @@ The @ref{Object model} chapter discusses this in more detail. @subsection how to include files from elsewhere @subsection log component -Here, write a bit about adding ns-3 logging macros. Note that +@cartouche +Here, write a bit about adding ns-3 logging macros. Note that @* LOG_COMPONENT_DEFINE is done outside the namespace ns3 +@end cartouche @subsection constructor, empty function prototypes @@ -407,7 +409,8 @@ LOG_COMPONENT_DEFINE is done outside the namespace ns3 @subsection Object Framework -@verbatim +@smallformat +@example static const ClassId cid; @@ -416,8 +419,8 @@ const InterfaceId ErrorModel::iid = const ClassId ErrorModel::cid = MakeClassId ("ErrorModel", ErrorModel::iid); -@end verbatim - +@end example +@end smallformat @node Adding-a-sample-script @section Adding a sample script @@ -431,7 +434,8 @@ of the ErrorModels themselves. @subsection Add basic support in the class -@verbatim +@smallformat +@example point-to-point-net-device.h class ErrorModel; @@ -440,12 +444,13 @@ point-to-point-net-device.h */ Ptr m_receiveErrorModel; -@end verbatim +@end example +@end smallformat @subsection Add Accessor -@verbatim - +@smallformat +@example void PointToPointNetDevice::SetReceiveErrorModel (Ptr em) { @@ -458,11 +463,13 @@ PointToPointNetDevice::SetReceiveErrorModel (Ptr em) PointerValue (), MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel), MakePointerChecker ()) -@end verbatim +@end example +@end smallformat @subsection Plumb into the system -@verbatim +@smallformat +@example void PointToPointNetDevice::Receive (Ptr packet) { NS_LOG_FUNCTION (this << packet); @@ -486,15 +493,18 @@ void PointToPointNetDevice::Receive (Ptr packet) ProcessHeader(packet, protocol); m_rxCallback (this, packet, protocol, GetRemote ()); if (!m_promiscCallback.IsNull ()) - { m_promiscCallback (this, packet, protocol, GetRemote (), GetAddress (), NetDevice::PACKET_HOST); + { m_promiscCallback (this, packet, protocol, GetRemote (), + GetAddress (), NetDevice::PACKET_HOST); } } } -@end verbatim +@end example +@end smallformat @subsection Create null functional script -@verbatim +@smallformat +@example simple-error-model.cc // Error model @@ -514,7 +524,8 @@ ErrorModel::DoCorrupt (Packet& p) NS_LOG_UNCOND("Corrupt!"); return false; } -@end verbatim +@end example +@end smallformat At this point, we can run the program with our trivial ErrorModel plumbed into the receive path of the PointToPointNetDevice. It @@ -550,7 +561,8 @@ the above unit of granularity. We declare BasicErrorModel to be a subclass of ErrorModel as follows, -@verbatim +@smallformat +@example class BasicErrorModel : public ErrorModel { public: @@ -562,7 +574,8 @@ private: virtual bool DoReset (void); ... } -@end verbatim +@end example +@end smallformat and configure the subclass GetTypeId function by setting a unique TypeId string and setting the Parent to ErrorModel: diff --git a/doc/manual/node.texi b/doc/manual/node.texi index 0195138ba..e2b4de144 100644 --- a/doc/manual/node.texi +++ b/doc/manual/node.texi @@ -20,8 +20,8 @@ to which one may add NetDevices (cards) and other innards including the protocols and applications. @ref{fig:node} illustrates that Node objects contain a list of Applications (initially, the list is empty), a list of NetDevices (initially, the list -is empty), a unique integer ID, and a system ID (for -distributed simulation). +is empty), a list of ProtocolHandlers, a unique integer ID, and +a system ID (for distributed simulation). The design tries to avoid putting too many dependencies on the base class Node, Application, or NetDevice for the following: diff --git a/doc/manual/python.texi b/doc/manual/python.texi index 8f748b141..f8c21bbcb 100644 --- a/doc/manual/python.texi +++ b/doc/manual/python.texi @@ -4,3 +4,6 @@ @cartouche Placeholder chapter @end cartouche + +For now, please see the Python wiki page at @* +@uref{http://www.nsnam.org/wiki/index.php/NS-3_Python_Bindings,,http://www.nsnam.org/wiki/index.php/NS-3_Python_Bindings}. diff --git a/doc/manual/routing.texi b/doc/manual/routing.texi index 323a81e24..8b03f1f7f 100644 --- a/doc/manual/routing.texi +++ b/doc/manual/routing.texi @@ -25,7 +25,7 @@ are described in @ref{Unicast routing}. Multicast routing is documented in @image{figures/routing, 6in} @end float -Figure 11-1 shows the overall routing architecture for Ipv4. The key objects +@ref{fig:routing} shows the overall routing architecture for Ipv4. The key objects are Ipv4L3Protocol, Ipv4RoutingProtocol(s) (a class to which all routing/forwarding has been delegated from Ipv4L3Protocol), and Ipv4Route(s). @@ -143,7 +143,8 @@ and rebuilds the routes. For instance, this scheduling call will cause the tables to be rebuilt at time 5 seconds: @verbatim - Simulator::Schedule (Seconds (5),&Ipv4GlobalRoutingHelper::RecomputeRoutingTables); + Simulator::Schedule (Seconds (5), + &Ipv4GlobalRoutingHelper::RecomputeRoutingTables); @end verbatim @subsection Global Routing Implementation @@ -199,15 +200,21 @@ is finally used to populate the routes themselves. @node Unicast routing @section Unicast routing -There are presently four routing protocols defined: +There are presently five unicast routing protocols defined for IPv4 and +two for IPv6: @itemize @bullet @item class Ipv4StaticRouting (covering both unicast and multicast) -@item Optimized Link State Routing (a MANET protocol defined in +@item IPv4 Optimized Link State Routing (a MANET protocol defined in @uref{http://www.ietf.org/rfc/rfc3626.txt,,RFC 3626}) @item class Ipv4ListRouting (used to store a prioritized list of routing protocols) @item class Ipv4GlobalRouting (used to store routes computed by the global route manager, if that is used) +@item class Ipv4NixVectorRouting (a more efficient version of global routing +that stores source routes in a packet header field) +@item class Ipv6ListRouting (used to store a prioritized list of routing +protocols) +@item class Ipv6StaticRouting @end itemize In the future, this architecture should also allow someone to implement diff --git a/doc/manual/sockets.texi b/doc/manual/sockets.texi index 2ba10441c..c0fe95a67 100644 --- a/doc/manual/sockets.texi +++ b/doc/manual/sockets.texi @@ -42,8 +42,8 @@ one of asynchronous I/O, which is not typically found in real systems @item the API is not a complete sockets API, such as supporting all socket options or all function variants; @item many calls use @code{ns3::Packet} class to transfer data -between application and socket. This may seem a little funny to -people to pass ``Packets'' across a stream socket API, but think +between application and socket. This may seem peculiar to +pass ``Packets'' across a stream socket API, but think of these packets as just fancy byte buffers at this level (more on this also below). @end itemize @@ -58,7 +58,7 @@ on this also below). @subsubsection Creating sockets An application that wants to use sockets must first create one. -On real systems, this is accomplished by calling socket(): +On real systems using a C-based API, this is accomplished by calling socket(): @verbatim int socket(int domain, int type, int protocol); @@ -79,11 +79,13 @@ Examples of TypeIds to pass to this method are @code{TcpSocketFactory}, This method returns a smart pointer to a Socket object. Here is an example: -@verbatim +@smallformat +@example Ptr n0; // Do some stuff to build up the Node's internet stack Ptr localSocket = Socket::CreateSocket (n0, TcpSocketFactory::GetTypeId ()); -@end verbatim +@end example +@end smallformat In some ns-3 code, sockets will not be explicitly created by user's main programs, if an ns-3 application does it. For instance, for @@ -149,7 +151,7 @@ There are two basic variants of @code{Send()} and @code{Recv()} supported: int Recv (uint8_t* buf, uint32_t size); @end verbatim -The non-Packet variants are left for legacy API reasons. When calling +The non-Packet variants are provided for legacy API reasons. When calling the raw buffer variant of @code{Send()}, the buffer is immediately written into a Packet and the @code{Send (Ptr p)} is invoked. @@ -160,7 +162,7 @@ the Packet variants are more likely to be preferred in ns-3: @itemize @bullet @item Users can use the Tags facility of packets to, for example, encode -a flow ID or other helper data. +a flow ID or other helper data at the application layer. @item Users can exploit the copy-on-write implementation to avoid memory copies (on the receive side, the conversion back to a @code{uint8_t* buf} may sometimes incur an additional copy). @@ -193,51 +195,6 @@ a real (zeroed) buffer on the spot, and the efficiency will be lost there. @section POSIX-like sockets API -@emph{this capability is under development and is scheduled for -inclusion in the ns-3.5 releasetimeframe; see the repository -http://code.nsnam.org/mathieu/ns-3-simu for details} - -The below is excerpted from Mathieu's post to ns-developers list -on April 4, 2008. - -"To summarize, the goal is that the full posix/socket API is defined in -src/process/simu.h: each posix type and function is re-defined there -with a simu_ or SIMU_ prefix to avoid ugly name clashes and collisions -(feel free to come up with a better prefix). - -Each process is created with a call to ProcessManager::Create and is -attached to that ProcessManager instance. So, if the ProcessManager -(which is aggregated to a Node in src/helper/process-helper.cc) is -killed when the simulation ends, the system will automatically reclaim -all the resources of each process associated to each manager. The same -happens when an application "exits" from its main function. - -The example application defines two posix "processes": the function -ClientProgram creates a udp socket on the localhost port 2000 and the -function ServerProgram creates a udp socket on the localhost port 2000. -The code does not work right now because I did not get the details of -simu_read right yet but, I do plan to make this work at some point. - -I really think that this approach is worthwhile for many reasons, a few -of which are outlined below: -@itemize @bullet -@item makes porting real world application code _much_ easier - -@item makes write applications for new users much easier because they can -read the bsd socket api reference and documentation and write code -directly. - -@item can be used to write applications which work in both simulation and -in the real world at the same time. To do this, all you have to do is -write your application to use the simu_ API, and, then, you can chose at -compile-time which implementation of that API you want to use: you can -pick one implementation which forwards all calls to the system BSD -socket API or another one which forwards all calls to the attached -ProcessManager. Arguably, I did not implement the version which forwards -to system BSD sockets but, that should be pretty trivial. -@end itemize - -So, anyway, comments about the overall API would be welcome. Students -interested in the gsoc project for real-world code integration should -consider looking at this also." - +@cartouche +Under development in the http://code.nsnam.org/mathieu/ns-3-simu repository. +@end cartouche diff --git a/doc/manual/statistics.texi b/doc/manual/statistics.texi index 63476c6e0..0dc3fca87 100644 --- a/doc/manual/statistics.texi +++ b/doc/manual/statistics.texi @@ -4,3 +4,7 @@ @cartouche Placeholder chapter @end cartouche +This wiki page: @* +@uref{http://www.nsnam.org/wiki/index.php/Statistical_Framework_for_Network_Simulation,,http://www.nsnam.org/wiki/index.php/Statistical_Framework_for_Network_Simulation} +contains information about the proposed statistical framework that is +located in @code{src/contrib} directory. diff --git a/doc/manual/tap.texi b/doc/manual/tap.texi index 24efd2e7d..badfb9c69 100644 --- a/doc/manual/tap.texi +++ b/doc/manual/tap.texi @@ -5,3 +5,7 @@ Placeholder chapter @end cartouche +The Tap NetDevice can be used to allow a host system or virtual machines +to interact with a simulation. See @* +@code{examples/tap/tap-wifi-dumbbell.cc} for an example. + diff --git a/doc/manual/tcp.texi b/doc/manual/tcp.texi index c1ba17292..3b99a0451 100644 --- a/doc/manual/tcp.texi +++ b/doc/manual/tcp.texi @@ -105,10 +105,7 @@ for @code{class TcpSocket}. @subsection Current limitations @itemize @bullet @item Only Tahoe congestion control is presently supported. -@item Only IPv4 is supported (IPv6 support will start to be added in ns-3.3). -@item @uref{http://www.nsnam.org/bugzilla/show_bug.cgi?id=198,,Bug 198}: TcpSocketImpl doesn't send acks with data packets in two-way transfers -@item @uref{http://www.nsnam.org/bugzilla/show_bug.cgi?id=250,,Bug 250}: Tcp breaks if you set the DelAckCount parameter to be greater than 2 -@item @uref{http://www.nsnam.org/bugzilla/show_bug.cgi?id=311,,Bug 311}: Tcp socket close returns -1 but does not set errno. +@item Only IPv4 is supported (IPv6 support will start to be added after ns-3.6). @end itemize @section Network Simulation Cradle @@ -132,43 +129,17 @@ how to use it. @subsection Prerequisites Presently, NSC has been tested and shown to work on these platforms: -Linux i386 and Linux x86-64. NSC does not support powerpc at the moment. +Linux i386 and Linux x86-64. NSC does not support powerpc. -NSC requires the packages mercurial, flex, and bison. +Building NSC requires the packages flex and bison. @subsection Configuring and Downloading -NSC is disabled by default and must be explicitly configured in. To try -this, type +Using the @code{build.py} script in ns-3-allinone directory, NSC will be +enabled by default unless the platform does not support it. To disable +it when building ns-3, type: @verbatim -./waf configure --enable-nsc -@end verbatim -the output of the configuration will show something like: -@verbatim -Checking for NSC supported architecture x86_64 : ok -Pulling nsc updates from https://secure.wand.net.nz/mercurial/nsc -pulling from https://secure.wand.net.nz/mercurial/nsc -searching for changes -no changes found ----- Summary of optional NS-3 features: -... -Network Simulation Cradle : enabled -... -@end verbatim -if successful. Note that the configure script pulls a recent copy of -NSC from a mercurial repository. This download will not work if you are not -online. - -If everything went OK, you will see a directory called "nsc" in the top-level -directory, with contents like this: -@verbatim -audit.sh linux-2.6/ openbsd3/ scons-time.py* -ChangeLog linux-2.6.18/ README SConstruct -config.log linux-2.6.26/ sconsign.py* sim/ -freebsd5/ lwip-1.3.0/ scons-LICENSE test/ -globaliser/ lwip-HEAD/ scons-local-1.0.1/ -INSTALL ns/ scons.py* -LICENSE omnetpp/ scons-README +./waf configure --disable-nsc @end verbatim @subsection Building and validating @@ -233,12 +204,18 @@ Additionally, NSC TCP exports a lot of configuration variables into the ns-3 @ref{Attributes} system, via a @uref{http://en.wikipedia.org/wiki/Sysctl,, sysctl}-like interface. In the @code{examples/tcp-nsc-zoo} example, you can see the following configuration: -@verbatim - // this disables TCP SACK, wscale and timestamps on node 1 (the attributes represent sysctl-values). - Config::Set ("/NodeList/1/$ns3::Ns3NscStack/net.ipv4.tcp_sack", StringValue ("0")); - Config::Set ("/NodeList/1/$ns3::Ns3NscStack/net.ipv4.tcp_timestamps", StringValue ("0")); - Config::Set ("/NodeList/1/$ns3::Ns3NscStack/net.ipv4.tcp_window_scaling", StringValue ("0")); -@end verbatim +@smallformat +@example + // this disables TCP SACK, wscale and timestamps on node 1 (the attributes +represent sysctl-values). + Config::Set ("/NodeList/1/$ns3::Ns3NscStack/net.ipv4.tcp_sack", +StringValue ("0")); + Config::Set ("/NodeList/1/$ns3::Ns3NscStack/net.ipv4.tcp_timestamps", +StringValue ("0")); + Config::Set ("/NodeList/1/$ns3::Ns3NscStack/net.ipv4.tcp_window_scaling", +StringValue ("0")); +@end example +@end smallformat These additional configuration variables are not available to native ns-3 TCP. @@ -331,11 +308,10 @@ nsc-tcp-sockets of a node when its wakeup callback is invoked by nsc. @itemize @bullet @item NSC only works on single-interface nodes; attempting to run it on a multi-interface node will cause a program error. This limitation should -be fixed by ns-3.3. -@item Cygwin and OS X PPC are not presently supported -@item The non-Linux stacks of NSC are not supported -@item NSC's integration into the build system presently requires on-line -access and mercurial, and is a slow download. +be fixed by ns-3.7. +@item Cygwin and OS X PPC are not supported +@item The non-Linux stacks of NSC are not supported in ns-3 +@item Not all socket API callbacks are supported @end itemize For more information, see diff --git a/doc/manual/troubleshoot.texi b/doc/manual/troubleshoot.texi index 623420ae3..2338af4c9 100644 --- a/doc/manual/troubleshoot.texi +++ b/doc/manual/troubleshoot.texi @@ -9,7 +9,9 @@ or running ns-3 programs. * Run-time errors:: @end menu -Please note that the wiki (@uref{http://www.nsnam.org/wiki/index.php/Troubleshooting}) may have contributed items. +Please note that the wiki @* +(@uref{http://www.nsnam.org/wiki/index.php/Troubleshooting}) +may have contributed items. @node Build errors @section Build errors @@ -23,18 +25,21 @@ pointer values are unexpectedly null. Here is an example of what might occur: -@verbatim +@smallformat +@example ns-old:~/ns-3-nsc$ ./waf --run tcp-point-to-point Entering directory `/home/tomh/ns-3-nsc/build' Compilation finished successfully Command ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] exited with code -11 -@end verbatim +@end example +@end smallformat The error message says that the program terminated unsuccessfully, but it is not clear from this information what might be wrong. To examine more closely, try running it under the @uref{http://sources.redhat.com/gdb/,,gdb debugger}: -@verbatim +@smallformat +@example ns-old:~/ns-3-nsc$ ./waf --run tcp-point-to-point --command-template="gdb %s" Entering directory `/home/tomh/ns-3-nsc/build' Compilation finished successfully @@ -44,7 +49,8 @@ GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. -This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1". +This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db +library "/lib/libthread_db.so.1". (gdb) run Starting program: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point @@ -61,7 +67,8 @@ $1 = {m_ptr = 0x3c5d65} $2 = {m_ptr = 0x0} (gdb) quit The program is running. Exit anyway? (y or n) y -@end verbatim +@end example +@end smallformat Note first the way the program was invoked-- pass the command to run as an argument to the command template "gdb %s". @@ -70,11 +77,13 @@ This tells us that there was an attempt to dereference a null pointer socketFactory. Let's look around line 136 of tcp-point-to-point, as gdb suggests: -@verbatim +@smallformat +@example Ptr socketFactory = n2->GetObject (Tcp::iid); Ptr localSocket = socketFactory->CreateSocket (); localSocket->Bind (); -@end verbatim +@end example +@end smallformat The culprit here is that the return value of GetObject is not being checked and may be null. @@ -82,6 +91,8 @@ checked and may be null. Sometimes you may need to use the @uref{http://valgrind.org,,valgrind memory checker} for more subtle errors. Again, you invoke the use of valgrind similarly: -@verbatim +@smallformat +@example ns-old:~/ns-3-nsc$ ./waf --run tcp-point-to-point --command-template="valgrind %s" -@end verbatim +@end example +@end smallformat diff --git a/doc/manual/wifi.texi b/doc/manual/wifi.texi index a1d09828c..467cfc8da 100644 --- a/doc/manual/wifi.texi +++ b/doc/manual/wifi.texi @@ -28,9 +28,8 @@ ns-3 provides models for these aspects of 802.11: @item QoS-based EDCA and queueing extensions of @strong{802.11e} @item various propagation loss models including @strong{Nakagami, Rayleigh, Friis, LogDistance, FixedRss, Random} @item two propagation delay models, a distance-based and random model -@item various rate control algorithms including @strong{Aarf, Arf, Cara, Onoe, Rraa, and ConstantRate} -@item @emph{(under development)} 802.11s (mesh) -@item @emph{(under development)} Minstrel rate control +@item various rate control algorithms including @strong{Aarf, Arf, Cara, Onoe, Rraa, ConstantRate, and Minstrel} +@item 802.11s (mesh), described in another chapter @end itemize The set of 802.11 models provided in ns-3 attempts to provide @@ -159,10 +158,12 @@ and a propagation loss based on a log distance model with a reference loss of 46.6777 dB at reference distance of 1m. Users will typically type code such as: -@verbatim +@smallformat +@example YansWifiChannelHelper wifiChannelHelper = YansWifiChannelHelper::Default (); Ptr wifiChannel = wifiChannelHelper.Create (); -@end verbatim +@end example +@end smallformat to get the defaults. Note the distinction above in creating a helper object vs. an actual simulation object. In ns-3, helper objects (used at the helper API only) are created on the @@ -203,35 +204,45 @@ configure MAC parameters like type of MAC, values of contention windows and so o Setting up a non-QoS MAC layers the object we use is @code{ns3::NqosWifiMacHelper}. For example the following user code configures a non-QoS MAC sta and changes its default values for contention window and Aifsn: -@verbatim +@smallformat +@example NqosWifiMacHelper wifiMacHelper = NqosWifiMacHelper::Default (); Ssid ssid = Ssid ("ns-3-ssid"); - wifiMacHelper.SetType ("ns3::NqstaWifiMac", "Ssid", SsidValue (ssid), "ActiveProbing", BooleanValue (false)); - wifiMacHelper.SetDcaParameters ("MinCw", UintegerValue (20), "Aifsn", UintegerValue (3)); -@end verbatim + wifiMacHelper.SetType ("ns3::NqstaWifiMac", "Ssid", SsidValue (ssid), +"ActiveProbing", BooleanValue (false)); + wifiMacHelper.SetDcaParameters ("MinCw", UintegerValue (20), "Aifsn", +UintegerValue (3)); +@end example +@end smallformat Setting up a QoS MACs we use a @code{ns3::QosWifiMacHelper} instead. This object could be also used to change default EDCA parameters, and to set a possible MSDU aggregator for a particular access class in order to use 802.11n MSDU aggregation feature. A possible user code: -@verbatim +@smallformat +@example QosWifiMacHelper wifiMacHelper = QosWifiMacHelper::Default (); - wifiMacHelper.SetType ("ns3::QapWifiMac", "Ssid", SsidValue (ssid), "BeaconGeneration", BooleanValue (true), + wifiMacHelper.SetType ("ns3::QapWifiMac", "Ssid", SsidValue (ssid), +"BeaconGeneration", BooleanValue (true), "BeaconInterval", TimeValue (Seconds (2.5))); wifiMacHelper.SetEdcaParametersForAc (AC_VO, "MinCw", UintegerValue (2)); - wifiMacHelper.SetMsduAggregatorForAc (AC_VO, "ns3::MsduStandardAggregator", "MaxAmsduSize", UintegerValue (3839)); -@end verbatim + wifiMacHelper.SetMsduAggregatorForAc (AC_VO, "ns3::MsduStandardAggregator", +"MaxAmsduSize", UintegerValue (3839)); +@end example +@end smallformat Call to QosWifiMacHelper::Default () is needed in order to set default EDCA parameters properly for all access classes. Otherwise we should set them one by one: -@verbatim +@smallformat +@example QosWifiMacHelper wifiMacHelper; - wifiMacHelper.SetEdcaParametersForAc (AC_VO, "MinCw", UintegerValue (2), "MaxCw", UintegerValue (7), - "Aifsn", UintegerValue (2)); - wifiMacHelper.SetEdcaParametersForAc (AC_VI, "MinCw", UintegerValue (7), "MaxCw", UintegerValue (15), - "Aifsn", UintegerValue (2)); + wifiMacHelper.SetEdcaParametersForAc (AC_VO, "MinCw", UintegerValue (2), +"MaxCw", UintegerValue (7), "Aifsn", UintegerValue (2)); + wifiMacHelper.SetEdcaParametersForAc (AC_VI, "MinCw", UintegerValue (7), +"MaxCw", UintegerValue (15), "Aifsn", UintegerValue (2)); ... -@end verbatim +@end example +@end smallformat @subsection WifiHelper @@ -244,9 +255,11 @@ What does this do? It sets the RemoteStationManager to @code{ns3::ArfWifiManager}. Now, let's use the wifiPhyHelper and wifiMacHelper created above to install WifiNetDevices on a set of nodes in a NodeContainer "c": -@verbatim +@smallformat +@example NetDeviceContainer wifiContainer = WifiHelper::Install (wifiPhyHelper, wifiMacHelper, c); -@end verbatim +@end example +@end smallformat This creates the WifiNetDevice which includes also a WifiRemoteStationManager, a WifiMac, and a WifiPhy (connected to the matching WifiChannel). @@ -257,12 +270,16 @@ show how to do some of this reconfiguration. @subsection AdHoc WifiNetDevice configuration This is a typical example of how a user might configure an adhoc network. -@emph{Write me} +@cartouche +To be completed +@end cartouche @subsection Infrastructure (Access Point and clients) WifiNetDevice configuration This is a typical example of how a user might configure an access point and a set of clients. -@emph{Write me} +@cartouche +To be completed +@end cartouche @node The WifiChannel and WifiPhy models @section The WifiChannel and WifiPhy models From 0b559a7c8c082196458098b6ef0e551c38a57384 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Mon, 19 Oct 2009 16:44:41 -0700 Subject: [PATCH 46/63] fill out tutorial tracing section with semi-real plot --- doc/tutorial/figures/cwnd.png | Bin 0 -> 10703 bytes doc/tutorial/tracing.texi | 809 +++++++++++++++++++++++++++++++++- examples/tutorial/fifth.cc | 224 ++++++++++ examples/tutorial/wscript | 3 + 4 files changed, 1030 insertions(+), 6 deletions(-) create mode 100644 doc/tutorial/figures/cwnd.png create mode 100644 examples/tutorial/fifth.cc diff --git a/doc/tutorial/figures/cwnd.png b/doc/tutorial/figures/cwnd.png new file mode 100644 index 0000000000000000000000000000000000000000..b573d9dbacab3af0b558b00d8ed893e6583984e0 GIT binary patch literal 10703 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 bQpr9w%&+J$d1kh*gIzbHSr``?(a-!3*B8(4 literal 0 HcmV?d00001 diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index 210794066..e1f78ccb6 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -1,4 +1,3 @@ - @c ============================================================================ @c Begin document body here @c ============================================================================ @@ -17,6 +16,7 @@ @menu * Background:: +* Overview:: @end menu @c ============================================================================ @@ -1096,10 +1096,807 @@ Callbacks, feel free to take a look at the @code{ns-3} manual. They are one of the most frequently used constructs in the low-level parts of @code{ns-3}. It is, in my opinion, a quite elegant thing. - - - - - +@subsection What About TracedValue? + +Earlier in this section, we presented a simple piece of code that used a +@code{TracedValue} to demonstrate the basics of the tracing code. +We just glossed over the way to find the return type and formal arguments +for the @code{TracedValue}. Rather than go through the whole exercise, we +will just point you at the correct file, @code{src/core/traced-value.h} and +to the important piece of code: + +@verbatim + template + class TracedValue + { + public: + ... + void Set (const T &v) { + if (m_v != v) + { + m_cb (m_v, v); + m_v = v; + } + } + ... + private: + T m_v; + TracedCallback m_cb; + }; +@end verbatim + +Here you see that the @code{TracedValue} is templated, of course. In the simple +example case at the start of the section, the typename is int32_t. This means +that the member variable being traced (@code>m_v} in the private section of the +class) will be an @code{int32_t m_v}. The @code{Set} method will take a +@code{const uint32_t &v} as a parameter. You should now be able to understand +that the @code{Set} code will fire the @code{m_cb} callback with two parameters: +the first being the current value of the @code{TracedValue}; and the second +being the new value being set. + +The callback, @code{m_cb} is declared as a @code{TracedCallback} which +will correspond to a @code{TracedCallback} when the class is +instantiated. + +Recall that the callback target of a TracedCallback always returns @code{void}. +Further recall that there is a one-to-one correspondence between the template +parameter list in the declaration and the formal arguments of the callback +function. Therefore the callback will need to have a function signature that +looks like: + +@verbatim + void + MyCallback (uint32_t oldValue, uint32_t newValue) + { + ... + } +@end verbatim + +It probably won't surprise you that this is exactly what we provided in that +simple example we covered so long ago: + +@verbatim + void + IntTrace (int32_t oldValue, int32_t newValue) + { + std::cout << "Traced " << oldValue << " to " << newValue << std::endl; + } +@end verbatim + +@c ============================================================================ +@c A Real Example +@c ============================================================================ +@node A Real Example +@section A Real Example + +Let's do an example taken from one of the best-known books on TCP around. +``TCP/IP Illustrated, Volume 1: The Protocols,'' by W. Richard Stevens is a +classic. I just flipped the book open and ran across a nice plot of both the +congestion window and sequence numbers versus time on page 366. Stevens calls +this, ``Figure 21.10. Value of cwnd and send sequence number while data is being +transmitted.'' Let's just recreate the cwnd part of that plot in @command{ns-3} +using the tracing system and @code{gnuplot}. + +@subsection Are There Trace Sources Available? + +The first thing to think about is how we want to get the data out. What is it +that we need to trace? The first thing to do is to consult ``The list of all +trace sources'' to see what we have to work with. Recall that this is found +in the @command{ns-3} Doxygen in the ``Core'' Module section. If you scroll +through the list, you will eventually find: + +@verbatim + ns3::TcpSocketImpl + CongestionWindow: The TCP connection's congestion window +@end verbatim + +It turns out that the @command{ns-3} TCP implementation lives (mostly) in the +file @code{src/internet-stack/tcp-socket-impl.cc}. If you don't know this a +priori, you can use the recursive grep trick: + +@vervatim + find . -name '*.cc' | xargs grep -i tcp +@end verbatim + +You will find page after page of instances of tcp pointing you to that file. + +If you open @code{src/internet-stack/tcp-socket-impl.cc} in your favorite +editor, you will see right up at the top of the file, the following declarations: + +@verbatim + TypeId + TcpSocketImpl::GetTypeId () + { + static TypeId tid = TypeId(``ns3::TcpSocketImpl'') + .SetParent () + .AddTraceSource (``CongestionWindow'', + ``The TCP connection's congestion window'', + MakeTraceSourceAccessor (&TcpSocketImpl::m_cWnd)) + ; + return tid; + } +@end verbatim + +This should tell you to look for the declaration of @code{m_cWnd} in the header +file @code{src/internet-stack/tcp-socket-impl.h}. If you open this file in your +favorite editor, you will find: + +@verbatim + TracedValue m_cWnd; //Congestion window +@end verbatim + +You should now understand this code completely. If we have a pointer to the +@code{TcpSocketImpl}, we can @code{TraceConnect} to the ``CongestionWindow'' trace +source if we provide an appropriate callback target. This is the same kind of +trace source that we saw in the simple example at the start of this section, +except that we are talking about @code{uint32_t} instead of @code{int32_t}. + +We now know that we need to provide a callback that returns void and takes +two @code{uint32_t} parameters, the first being the old value and the second +being the new value: + +@verbatim + void + CwndTrace (uint32_t oldValue, uint32_t newValue) + { + ... + } +@end verbatim + +@subsection What Script to Use? + +It's always best to try and find working code laying around that you can +modify, rather than starting from scratch. So the fist order of business now +is to find some code that already hooks the ``CongestionWindow'' trace source +and see if we can modify it. As usual, grep is your friend: + +@verbatim + find . -name '*.cc' | xargs grep CongestionWindow +@end verbatim + +This will point out a couple of promising candidates: +@code{examples/tcp/tcp-large-transfer.cc} and +@code{src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc}. + +We haven't visited any of the test code yet, so let's take a look there. You +will typically find that test code is fairly minimal, so this is probably a +very good bet. Open @code{src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc} in your +favorite editor and search for ``CongestionWindow.'' You will find, + +@verbatim + ns3TcpSocket->TraceConnectWithoutContext (``CongestionWindow'', + MakeCallback (&Ns3TcpCwndTestCase1::CwndChange, this)); +@end verbatim + +This should look very familiar to you. We mentioned above that if we had a +pointer to the @code{TcpSocketImpl}, we could @code{TraceConnect} to the +``CongestionWindow'' trace source. That's exactly what we have here; so it +turns out that this line of code does exactly what we want. Let's go ahead +and extract the code we need from this function +(@code{Ns3TcpCwndTestCase1::DoRun (void)}). If you look at this function, +you will find that it looks just like an @code{ns-3} script. It turns out that +is exactly what it is. It is a script run by the test framework, so we can just +pull it out and wrap it in @code{main} instead of in @code{DoRun}. Rather than +walk through this step, by step, we have provided the file that results from +porting this test back to a native @code{ns-3} script. + +Rather than go through this extraction process, we'll just provide the results +in a file named @code{examples/tutorial/fifth.cc}. + +@subsection A Common Problem and Solution + +The @code{fifth.cc} example demonstrates an extremely important rule that you +must understand before using any kind of @code{Attribute}: you must ensure +that the target of a @code{Config} command exists before trying to use it. +This is no different than saying an object must be instantiated before trying +to call it, Although this may seem obvious when stated this way, it does +trip up many people trying to use the system for the first time. + +Let's return to basics for a moment. There are three basic time periods that +exist in any @command{ns-3} script. The first time period is sometimes called +``Configuration Time'' or ``Setup Time,'' and is in force during the period +when the @code{main} function of your script is running, but before +@code{Simulator::Run} is called. The second time period is sometimes called +``Simulation Time'' and is in force during the time period when +@code{Simulator::Run} is actively executing its events. After it completes +executing the simulation, @code{Simulator::Run} will return control back to +the @code{main} function. When this happens, the script enters what can be +called ``Teardown Time,'' which is when the structures and objects created +during setup and taken apart and released, + +Perhaps the most common mistake made in trying to use the tracing system is +assuming that entities constructed dynamically during simulation time are +available during configuration time. In particular, an @command{ns-3} +@code{Socket} is a dynamic object often created by @code{Applications} to +communicate between @code{Nodes}. An @command{ns-3} @code{Application} +always has a ``Start Time'' and a ``Stop Time'' associated with it. In the +vast majority of cases, an @code{Application} will not attempt to create +a dynamic object until its @code{StartApplication} method is called. This +is to ensure that the simulation is completely configured before the app +tries to do anything (what would happen if it tried to connect to a node +that didn't exist yet during configuration time). The answer to this +issue is to 1) create a simulator event that is run after the dynamic object +is created and hook the trace when that event is executedl or 2) create the +dynamic object at configuration time, hook it then, and give the object to +the system to use during simulation time. We took the second approach in +the @code{fifth.cc} example. This decision required us to create the +@code{SimpleSource} @code{Application}, the entire purpose of which is to +take a @code{Socket} as a parameter. + +@subsection A fifth.cc Walkthrough + +Now, let's take a look at the example program we constructed by disecting +the congestion window test. Open @code{examples/tutorial/fifth.cc} in your +favorite editor. You should see some familiar looking code: + +@verbatim + /* -*- Mode:C++; c-file-style:''gnu''; indent-tabs-mode:nil; -*- */ + /* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Include., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include ``ns3/core-module.h'' + #include ``ns3/common-module.h'' + #include ``ns3/simulator-module.h'' + #include ``ns3/node-module.h'' + #include ``ns3/helper-module.h'' + + using namespace ns3; + + NS_LOG_COMPONENT_DEFINE (``FifthScriptExample''); +@end verbatim + +This has all been covered, so we won't discuss it. The next lines of source are +the network illustration and a comment addressing the problem described above +with @code{Socket}. + +@verbatim + // =========================================================================== + // + // node 0 node 1 + // +----------------+ +----------------+ + // | ns-3 TCP | | ns-3 TCP | + // +----------------+ +----------------+ + // | 10.1.1.1 | | 10.1.1.2 | + // +----------------+ +----------------+ + // | point-to-point | | point-to-point | + // +----------------+ +----------------+ + // | | + // +---------------------+ + // 5 Mbps, 2 ms + // + // + // We want to look at changes in the ns-3 TCP congestion window. We need + // to crank up a flow and hook the CongestionWindow attribute on the socket + // of the sender. Normally one would use an on-off application to generate a + // flow, but this has a couple of problems. First, the socket of the on-off + // application is not created until Application Start time, so we wouldn't be + // able to hook the socket (now) at configuration time. Second, even if we + // could arrange a call after start time, the socket is not public so we + // couldn't get at it. + // + // So, we can cook up a simple version of the on-off application that does what + // we want. On the plus side we don't need all of the complexity of the on-off + // application. On the minus side, we don't have a helper, so we have to get + // a little more involved in the details, but this is trivial. + // + // So first, we create a socket and do the trace connect on it; then we pass + // this socket into the constructor of our simple application which we then + // install in the source node. + // =========================================================================== + // +@end verbatim + +This should also be self-explanatory. + +The next part is the declaration of the @code{SimpleSource} @code{Application} that +we put together to allow the @code{Socket} to be created at configuration time. + +@verbatim + class SimpleSource : public Application + { + public: + + SimpleSource (); + virtual ~SimpleSource(); + + void Setup (Ptr socket, Address address, uint32_t packetSize, + uint32_t nPackets, DataRate dataRate); + + private: + virtual void StartApplication (void); + virtual void StopApplication (void); + + void ScheduleTx (void); + void SendPacket (void); + + Ptr m_socket; + Address m_peer; + uint32_t m_packetSize; + uint32_t m_nPackets; + DataRate m_dataRate; + EventId m_sendEvent; + bool m_running; + uint32_t m_packetsSent; + }; +@end verbatim + +You can see that this class inherits from the @command{ns-3} @code{Application} +class. Take a look at @code{src/node/application.h} if you are interested in +what is inherited. This class is obligated to override the +@code{StartApplication} and @code{StopApplication} methods. These methods are +called when the corresponding @code{Start} and @code{Stop} methods are called +during simulation time. + +@subsubsection How Applications are Started and Stopped + +It is worthwhile to spend a bit of time explaining how events actually get +started in the system. The most common way to start pumping events is to start +an @code{Application}. This is done as the result of the following (hopefully) +familar lines of an @sommand{ns-3} script: + +@verbatim + ApplicationContainer apps = ... + apps.Start (Seconds (1.0)); + apps.Stop (Seconds (10.0)); +@end verbatim + +The application container code (see @code{src/helper/application-container.h} if +you are interested) loops through its contained applications and calls, + +@verbatim + app->Start (startTime); + app->Stop (stopTime); +@end verbatim + +on each of them. The @code{Start} method of an @code{Application} calls +@code{Application::ScheduleStart} (see @code{src/helper/application-container.cc}) +which, in turn, schedules an event to start the @code{Application}: + +@verbatim + Simulator::Schedule (startTime, &Application::StartApplication, this); +@end verbatim + +Since @code{SimpleSource} inherits from @code{Application} and overrides +@code{StartApplication}, this bit of code causes the simulator to execute +something that is effectively like, + +@verbatim + this->StartApplication (startTime); +@end verbatim + +where the @code{this} pointer, if you have kept it all straight, is the pointer +to the @code{Application} in the container. It is then expected that another +event will be scheduled in the overridden @code{StartApplication} that will +begin doing some application-specific function, like sending packets. + +@code{StopApplication} operates in a similar manner and tells the @code{Application} +to stop generating events. + +@subsubsection The SimpleSource Application + +The @code{SimpleSource} @code{Application} needs a constructor and a destructor, +of course: + +@verbatim + SimpleSource::SimpleSource () + : m_socket (0), + m_peer (), + m_packetSize (0), + m_nPackets (0), + m_dataRate (0), + m_sendEvent (), + m_running (false), + m_packetsSent (0) + { + } + + SimpleSource::~SimpleSource() + { + m_socket = 0; + } +@end verbatim + +The existence of the next bit of code is the whole reason why we wrote this +@code{Application} in the first place. + +@verbatim +void +SimpleSource::Setup (Ptr socket, Address address, uint32_t packetSize, + uint32_t nPackets, DataRate dataRate) +{ + m_socket = socket; + m_peer = address; + m_packetSize = packetSize; + m_nPackets = nPackets; + m_dataRate = dataRate; +} +@end verbatim + +This code should be pretty self-explanatory. We are just initializing member +variables. The important one from the perspective of tracing is the +@code{Ptr socket} which we needed to provide to the application +during configuration time. Recall that we are going to create the @code{Socket} +as a @code{TcpSocket} (which is implemented by @code{TcpSocketImpl}) and hook +its ``CongestionWindow'' trace source before passing it to the @code{Setup} +method. + +@verbatim + void + SimpleSource::StartApplication (void) + { + m_running = true; + m_packetsSent = 0; + m_socket->Bind (); + m_socket->Connect (m_peer); + SendPacket (); + } +@end verbatim + +The above code is the overridden implementation @code{Application::StartApplication} +that will be automatically called by the simulator to start our @code{Application} +running. You can see that it does a @code{Socket} @code{Bind} operation. If +you are familiar with Berkeley Sockets this shouldn't be a surprise. It performs +the required work on the local side of the connection just as you might expect. +The following @code{Connect} will do what is required to establish a connection +with the TCP at @code{Address} m_peer. The @code{Application} then starts creating +simulation events by calling @code{SendPacket}. + +The next bit of code explains to the @code{Application} how to stop creating +simulation events. + +@verbatim + void + SimpleSource::StopApplication (void) + { + m_running = false; + + if (m_sendEvent.IsRunning ()) + { + Simulator::Cancel (m_sendEvent); + } + + if (m_socket) + { + m_socket->Close (); + } + } +@end verbatim + +Every time a simulation event is scheduled, an @code{Event} is created. If the +@code{Event} is pending execution or executing, its method @code{IsRunning} will +return @code{true}. In this code, if @code{IsRunning()} returns true, we +@code{Cancel} the event which removes it from the simulator event queue. By +doing this, we break the chain of events that the @code{Application} is using to +keep sending its @code{Packets} and the event goes quiet. After we quiet the +@code{Application} we @code{Close} the socket which tears down the TCP connection. + +The socket is actually deleted in the destructor when the @code{m_socket = 0} is +executed. This removes the last reference to the underlying Ptr which +causes the destructor of that Object to be called. + +Recall that @code{StartApplication} called @code{SendPacket} to start the +chain of events that describes the @code{Application} behavior. + +@verbatim + void + SimpleSource::SendPacket (void) + { + Ptr packet = Create (m_packetSize); + m_socket->Send (packet); + + if (++m_packetsSent < m_nPackets) + { + ScheduleTx (); + } + } +@end verbatim + +Here, you see that @code{SendPacket} does just that. It creates a @code{Packet} +and then does a @code{Send} which, if you know Berkeley Sockets, is probably +just what you expected to see. + +It is the responsibility of the @code{Application} to keep scheduling the +chain of events, so the next lines call @code{ScheduleTx} to schedule another +transmit event (a @code{SendPacket}) until the @code{Application} decides it +has sent enough. + +@verbatim + void + SimpleSource::ScheduleTx (void) + { + if (m_running) + { + Time tNext (Seconds (m_packetSize * 8 / static_cast (m_dataRate.GetBitRate ()))); + m_sendEvent = Simulator::Schedule (tNext, &SimpleSource::SendPacket, this); + } + } +@end verbatim + +Here, you see that @code{ScheduleTx} does exactly that. If the @code{Applciation} +is running (if @code{StopApplication}) has not been called) it will schedule a +new event, which calls @code{SendPacket} again. The alert reader will spot +something that also trips up new users. The data rate of an @code{Application} is +just that. It has nothing to do with the data rate of an underlying @code{Channel}. +This is the rate at which the @code{Application} produces bits. It does not take +into account any overhead for the various protocols or channels that it uses to +transport the data. If you set the data rate of an @code{Application} to the same +data rate as your underlying @code{Channel} you will eventually get a buffer overflow. + +@subsubsection The Trace Sinks + +The whole point of this exercise is to get trace callbacks. The next piece of +code implements that callback: + +@verbatim + static void + CwndChange (uint32_t oldCwnd, uint32_t newCwnd) + { + NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << ``\t'' << newCwnd); + } +@end verbatim + +This should be very familiar to you now, so we won't dwell on the details. This +function just logs the current simulation time and the new value of the +congestion window every time it is changed. You can probably imagine that you +could load the resulting output into a graphics program (gnuplot or Excel) and +immediately see a nice graph of the congestion window behavior over time. + +We added a new trace sink to show where are dropped. We are going to add an +error model to this code also, so we wanted to demonstrate this working. + +@verbatim + static void + RxDrop (Ptr p) + { + NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ()); + } +@end verbatim + +This trace sink will be connected to the ``PhyRxDrop'' trace source of the +point-to-point NetDevice. If you take a small detour there you will see +(in @code{src/devices/point-to-point/point-to-point-net-device.cc}) that +this trace source refers to @code{PointToPointNetDevice::m_phyRxDropTrace}. +If you then look in @code{src/devices/point-to-point/point-to-point-net-device.h} +for this member variable, you will find that it is declared as a +@code{TracedCallback >}. This should tell you that the +callback target should be a function that returns void and takes a single +parameter which is a @code{Ptr} -- just what we have above. + +@subsubsection The Main Program + +The following code should be very familiar to you by now: + +@end verbatim + int + main (int argc, char *argv[]) + { + NodeContainer nodes; + nodes.Create (2); + + PointToPointHelper pointToPoint; + pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); + pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); + + NetDeviceContainer devices; + devices = pointToPoint.Install (nodes); +@end verbatim + +This creates two nodes with a point-to-point channel between them, just as +shown in the illustration at the start of the file. + +The next few lines of code show somthing new. If we trace a connection that +behaves perfectly, we will end up with a monotonically increasing congestion +window. To see any interesting behavior, we really want to introduce link +errors which will drop packets, cause duplicate ACKs and trigger the more +interesting behaviors of the congestion window. + +@command{ns-3} provides @code{ErrorModel} objects which can be attached to +@code{Channels}. We are using the @code{RateErrorModel} which allows us +to introduce errors into a @code{Channel} at a given @emph{rate}. + +@verbatim + Ptr em = CreateObjectWithAttributes ( + "RanVar", RandomVariableValue (UniformVariable (0., 1.)), + "ErrorRate", DoubleValue (0.01)); + devices.Get (0)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); +@end verbatim + +The above code instantiates a @code{RateErrorModel} Object. Rather than +using the two-step process of instantiating it and then setting Attributes, +we use the convenience function @code{CreateObjectWithAttributes} which +allows us to do both at the same time. We set the ``RanVar'' +@code{Attribute} to a random variable that generates a uniform distribution +from 0 to 1. We also set the ``ErrorRate'' @code{Attribute} to the rate 0.01. +We then set the resulting instantiated @code{RateErrorModel} as the error +model used by the point-to-point @code{NetDevice}. This will result in +about one of every one hundred packets being dropped by the net device +and give us those retransmissions we want to make our plot a little more +interesting. + +@verbatim + InternetStackHelper stack; + stack.Install (nodes); + + Ipv4AddressHelper address; + address.SetBase (``10.1.1.0'', ``255.255.255.252''); + Ipv4InterfaceContainer interfaces = address.Assign (devices); +@end verbatim + +The above code should be familiar. It installs internet stacks on our two +nodes and creates interfaces and assigns IP addresses for the point-to-point +devices. + +Since we are using TCP, we need something on the destination node to receive +TCP connections and data. The @code{PacketSink} @code{Application} is commonly +used in @command{ns-3} for that purpose. + +@verbatim + uint16_t sinkPort = 8080; + Address sinkAddress (InetSocketAddress(interfaces.GetAddress (1), sinkPort)); + PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", + InetSocketAddress (Ipv4Address::GetAny (), sinkPort)); + ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (1)); + sinkApps.Start (Seconds (0.)); + sinkApps.Stop (Seconds (20.)); +@end verbatim + +This should all be familiar, with the exception of, + +@verbatim + PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", + InetSocketAddress (Ipv4Address::GetAny (), sinkPort)); +@end verbatim + +This code instantiates a @code{PacketSinkHelper} and tells it to create sockets +using the class @code{ns3::TcpSocketFactory}. This class implements a design +pattern called ``object factory'' which is a commonly used mechanism for +specifying a class used to create objects in an abstract way. Here, instead of +having to create the objects themselves, you provide the @code{ PacketSinkHelper} +a string that specifies a @code{TypeId} string used to create an object which +can then be used, in turn, to create instances of the Objects created by the +factory. + +The remaining parameter tells the @code{Application} which address and port it +should @cod{Bind} to. + +The next two lines of code will create the socket and connect the trace source. + +@verbatim + Ptr ns3TcpSocket = Socket::CreateSocket (nodes.Get (0), + TcpSocketFactory::GetTypeId ()); + ns3TcpSocket->TraceConnectWithoutContext (``CongestionWindow'', + MakeCallback (&CwndChange)); +@end verbatim + +The first statement calls the static member function @code{Socket::CreateSocket) +and provides a @code{Node} and an explicit @code{TypeId} for the object factory +used to create the socket. This is a slightly lower level call than the +@code{PacketSinkHelper} call above, and uses an explicit C++ type instead of +one referred to by a string. Otherwise, it is conceptually the same thing. + +Once the @code{TcpSocket} is created and attached to the @code{Node}, we can +use @code{TraceConnectWithoutContext} to connect the CongestionWindow trace +source to our trace sink. + +Recall that we coded an @code{Application} so we could take that @code{Socket} +we just made (during configuration time) and use it in simulation time. We now +have to instantiate that @code{Application}. We didn't go to any trouble to +create a helper to manage the @code{Application} so we are going to have to +create and install it ``manually.'' This is actually quite easy: + +@verbatim + Ptr app = CreateObject (); + app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps")); + nodes.Get (0)->AddApplication (app); + app->Start (Seconds (1.)); + app->Stop (Seconds (20.)); +@end verbatim + +The first line creates an @code{Object} of type @code{SimpleSource} -- our +@code{Application}. The second line tells the @code{Application} what +@code{Socket} to use, what address to connect to, how much data to send +at each send event, how many send events to generate and the rate at which +to produce data from those events. + +Next, we manually add the @code{SimpleSource Application} to the source node +and explicitly call the @code{Start} and @code{Stop} methods on the +@code{Application} to tell it when to start and stop doing its thing. + +We need to actually do the connect from the receiver point-to-point @code{NetDevice} +to our callback now. + +@verbatim + devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeCallback (&RxDrop)); +@end verbatim + +It should now be obvious that we are getting a reference to the receiving +@code{Node NetDevice} from its container and connecting the trace source defined +by the attribute ``PhyRxDrop'' on that device to the trace sink @code{RxDrop}. + +Finally, we tell the simulator to override any @code{Applications} and just +stop processing events at 20 seconds into the simulation. + +@verbatim + Simulator::Stop (Seconds(20)); + Simulator::Run (); + Simulator::Destroy (); + + return 0; + } +@end verbatim + +Recall that as soon as @code{Simulator::Run} is called, configuration time +ends, and simulation time begins. All of the work we orchestrated by +creating the @code{Application} and teaching it how to connect and send +data actually happens during this function call. + +As soon as @code{Simulator::Run} returns, the simulation is complete and +we enter the teardown phase. In this case, @code{Simulator::Destroy} takes +care of the gory details and we just return a success code after it completes. + +@subsection Running fifth.cc + +Since we have provided the file @code{fifth.cc} for you, if you have built +your distribution (in debug mode since it uses NS_LOG -- recall that optimized +builds optimize out NS_LOGs) it will be waiting for you to run. + +@verbatim + ./waf --run fifth + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build' + Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build' + 'build' finished successfully (0.525s) + 1.21397 1072 + 1.22755 1608 + 1.24114 2144 + ... + 1.35211 8576 + 1.36136 9112 + 1.37061 9648 + RxDrop at 1.3729 + ... +@end verbatim + +You can probably see immediately a downside of using prints of any kind in your +traces. We get those extraneous waf messages printed all over our interesting +information along with those RxDrop messages. We will remedy that soon, but I'm +sure you can't wait to see the results of all of this work. Let's redirect that +output to a file called @code{cwnd.dat}: + +@verbatim + ./waf --run fifth > cwnd.dat 2>&1 +@end verbatim + +Now edit up ``cwnd.dat'' in your favorite editor and remove the waf build status +and drop lines, leaving only the traced data (you could also comment out the +@code{TraceConnectWithoutContext("PhyRxDrop", MakeCallback (&RxDrop));} in the +script to get rid of the drop prints just as easily. + +You can now run gnuplot (if you have it installed) and tell it to generate some +pretty pictures: + +@verbatim + gnuplot> set terminal png size 1024,768 + gnuplot> set output "cwnd.png" + gnuplot> plot "cwnd.dat" using 1:2 title 'Congestion Window' with linespoints + gnuplot> exit +@end verbatim + +You should now have a graph of the congestion window versus time sitting in the +file ``cwnd.png'' in all of its glory, that looks like: + +@sp 1 +@center @image{figures/cwnd,,,,png} diff --git a/examples/tutorial/fifth.cc b/examples/tutorial/fifth.cc new file mode 100644 index 000000000..980547cff --- /dev/null +++ b/examples/tutorial/fifth.cc @@ -0,0 +1,224 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "ns3/core-module.h" +#include "ns3/common-module.h" +#include "ns3/simulator-module.h" +#include "ns3/node-module.h" +#include "ns3/helper-module.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("FifthScriptExample"); + +// =========================================================================== +// +// node 0 node 1 +// +----------------+ +----------------+ +// | ns-3 TCP | | ns-3 TCP | +// +----------------+ +----------------+ +// | 10.1.1.1 | | 10.1.1.2 | +// +----------------+ +----------------+ +// | point-to-point | | point-to-point | +// +----------------+ +----------------+ +// | | +// +---------------------+ +// 5 Mbps, 2 ms +// +// +// We want to look at changes in the ns-3 TCP congestion window. We need +// to crank up a flow and hook the CongestionWindow attribute on the socket +// of the sender. Normally one would use an on-off application to generate a +// flow, but this has a couple of problems. First, the socket of the on-off +// application is not created until Application Start time, so we wouldn't be +// able to hook the socket (now) at configuration time. Second, even if we +// could arrange a call after start time, the socket is not public so we +// couldn't get at it. +// +// So, we can cook up a simple version of the on-off application that does what +// we want. On the plus side we don't need all of the complexity of the on-off +// application. On the minus side, we don't have a helper, so we have to get +// a little more involved in the details, but this is trivial. +// +// So first, we create a socket and do the trace connect on it; then we pass +// this socket into the constructor of our simple application which we then +// install in the source node. +// =========================================================================== +// +class SimpleSource : public Application +{ +public: + + SimpleSource (); + virtual ~SimpleSource(); + + void Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate); + +private: + virtual void StartApplication (void); + virtual void StopApplication (void); + + void ScheduleTx (void); + void SendPacket (void); + + Ptr m_socket; + Address m_peer; + uint32_t m_packetSize; + uint32_t m_nPackets; + DataRate m_dataRate; + EventId m_sendEvent; + bool m_running; + uint32_t m_packetsSent; +}; + +SimpleSource::SimpleSource () + : m_socket (0), + m_peer (), + m_packetSize (0), + m_nPackets (0), + m_dataRate (0), + m_sendEvent (), + m_running (false), + m_packetsSent (0) +{ +} + +SimpleSource::~SimpleSource() +{ + m_socket = 0; +} + +void +SimpleSource::Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate) +{ + m_socket = socket; + m_peer = address; + m_packetSize = packetSize; + m_nPackets = nPackets; + m_dataRate = dataRate; +} + +void +SimpleSource::StartApplication (void) +{ + m_running = true; + m_packetsSent = 0; + m_socket->Bind (); + m_socket->Connect (m_peer); + SendPacket (); +} + +void +SimpleSource::StopApplication (void) +{ + m_running = false; + + if (m_sendEvent.IsRunning ()) + { + Simulator::Cancel (m_sendEvent); + } + + if (m_socket) + { + m_socket->Close (); + } +} + +void +SimpleSource::SendPacket (void) +{ + Ptr packet = Create (m_packetSize); + m_socket->Send (packet); + + if (++m_packetsSent < m_nPackets) + { + ScheduleTx (); + } +} + +void +SimpleSource::ScheduleTx (void) +{ + if (m_running) + { + Time tNext (Seconds (m_packetSize * 8 / static_cast (m_dataRate.GetBitRate ()))); + m_sendEvent = Simulator::Schedule (tNext, &SimpleSource::SendPacket, this); + } +} + +static void +CwndChange (uint32_t oldCwnd, uint32_t newCwnd) +{ + NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd); +} + +static void +RxDrop (Ptr p) +{ + NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ()); +} + +int +main (int argc, char *argv[]) +{ + NodeContainer nodes; + nodes.Create (2); + + PointToPointHelper pointToPoint; + pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("1Mbps")); + pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); + + NetDeviceContainer devices; + devices = pointToPoint.Install (nodes); + + Ptr em = CreateObjectWithAttributes ( + "RanVar", RandomVariableValue (UniformVariable (0., 1.)), + "ErrorRate", DoubleValue (0.00001)); + devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); + + InternetStackHelper stack; + stack.Install (nodes); + + Ipv4AddressHelper address; + address.SetBase ("10.1.1.0", "255.255.255.252"); + Ipv4InterfaceContainer interfaces = address.Assign (devices); + + uint16_t sinkPort = 8080; + Address sinkAddress (InetSocketAddress(interfaces.GetAddress (1), sinkPort)); + PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), sinkPort)); + ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (1)); + sinkApps.Start (Seconds (0.)); + sinkApps.Stop (Seconds (20.)); + + Ptr ns3TcpSocket = Socket::CreateSocket (nodes.Get (0), TcpSocketFactory::GetTypeId ()); + ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndChange)); + + Ptr app = CreateObject (); + app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("5Mbps")); + nodes.Get (0)->AddApplication (app); + app->Start (Seconds (1.)); + app->Stop (Seconds (20.)); + + devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeCallback (&RxDrop)); + + Simulator::Stop (Seconds(20)); + Simulator::Run (); + Simulator::Destroy (); + + return 0; +} + diff --git a/examples/tutorial/wscript b/examples/tutorial/wscript index 9966647c6..1c340ab6d 100644 --- a/examples/tutorial/wscript +++ b/examples/tutorial/wscript @@ -15,3 +15,6 @@ def build(bld): obj = bld.create_ns3_program('fourth', ['core']) obj.source = 'fourth.cc' + + obj = bld.create_ns3_program('fifth', ['core', 'simulator', 'point-to-point', 'internet-stack']) + obj.source = 'fifth.cc' From 30dac288f564e36301cf0672223b9f8d520011d3 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Mon, 19 Oct 2009 17:35:02 -0700 Subject: [PATCH 47/63] Rename SimpleSource to MyApp --- doc/tutorial/tracing.texi | 38 +++++++++++++++++++------------------- examples/tutorial/fifth.cc | 24 ++++++++++++------------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index e1f78ccb6..7b3be4519 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -1320,7 +1320,7 @@ is created and hook the trace when that event is executedl or 2) create the dynamic object at configuration time, hook it then, and give the object to the system to use during simulation time. We took the second approach in the @code{fifth.cc} example. This decision required us to create the -@code{SimpleSource} @code{Application}, the entire purpose of which is to +@code{MyApp} @code{Application}, the entire purpose of which is to take a @code{Socket} as a parameter. @subsection A fifth.cc Walkthrough @@ -1400,16 +1400,16 @@ with @code{Socket}. This should also be self-explanatory. -The next part is the declaration of the @code{SimpleSource} @code{Application} that +The next part is the declaration of the @code{MyApp} @code{Application} that we put together to allow the @code{Socket} to be created at configuration time. @verbatim - class SimpleSource : public Application + class MyApp : public Application { public: - SimpleSource (); - virtual ~SimpleSource(); + MyApp (); + virtual ~MyApp(); void Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate); @@ -1468,7 +1468,7 @@ which, in turn, schedules an event to start the @code{Application}: Simulator::Schedule (startTime, &Application::StartApplication, this); @end verbatim -Since @code{SimpleSource} inherits from @code{Application} and overrides +Since @code{MyApp} inherits from @code{Application} and overrides @code{StartApplication}, this bit of code causes the simulator to execute something that is effectively like, @@ -1484,13 +1484,13 @@ begin doing some application-specific function, like sending packets. @code{StopApplication} operates in a similar manner and tells the @code{Application} to stop generating events. -@subsubsection The SimpleSource Application +@subsubsection The MyApp Application -The @code{SimpleSource} @code{Application} needs a constructor and a destructor, +The @code{MyApp} @code{Application} needs a constructor and a destructor, of course: @verbatim - SimpleSource::SimpleSource () + MyApp::MyApp () : m_socket (0), m_peer (), m_packetSize (0), @@ -1502,7 +1502,7 @@ of course: { } - SimpleSource::~SimpleSource() + MyApp::~MyApp() { m_socket = 0; } @@ -1513,7 +1513,7 @@ The existence of the next bit of code is the whole reason why we wrote this @verbatim void -SimpleSource::Setup (Ptr socket, Address address, uint32_t packetSize, +MyApp::Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate) { m_socket = socket; @@ -1534,7 +1534,7 @@ method. @verbatim void - SimpleSource::StartApplication (void) + MyApp::StartApplication (void) { m_running = true; m_packetsSent = 0; @@ -1558,7 +1558,7 @@ simulation events. @verbatim void - SimpleSource::StopApplication (void) + MyApp::StopApplication (void) { m_running = false; @@ -1591,7 +1591,7 @@ chain of events that describes the @code{Application} behavior. @verbatim void - SimpleSource::SendPacket (void) + MyApp::SendPacket (void) { Ptr packet = Create (m_packetSize); m_socket->Send (packet); @@ -1614,12 +1614,12 @@ has sent enough. @verbatim void - SimpleSource::ScheduleTx (void) + MyApp::ScheduleTx (void) { if (m_running) { Time tNext (Seconds (m_packetSize * 8 / static_cast (m_dataRate.GetBitRate ()))); - m_sendEvent = Simulator::Schedule (tNext, &SimpleSource::SendPacket, this); + m_sendEvent = Simulator::Schedule (tNext, &MyApp::SendPacket, this); } } @end verbatim @@ -1797,20 +1797,20 @@ create a helper to manage the @code{Application} so we are going to have to create and install it ``manually.'' This is actually quite easy: @verbatim - Ptr app = CreateObject (); + Ptr app = CreateObject (); app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps")); nodes.Get (0)->AddApplication (app); app->Start (Seconds (1.)); app->Stop (Seconds (20.)); @end verbatim -The first line creates an @code{Object} of type @code{SimpleSource} -- our +The first line creates an @code{Object} of type @code{MyApp} -- our @code{Application}. The second line tells the @code{Application} what @code{Socket} to use, what address to connect to, how much data to send at each send event, how many send events to generate and the rate at which to produce data from those events. -Next, we manually add the @code{SimpleSource Application} to the source node +Next, we manually add the @code{MyApp Application} to the source node and explicitly call the @code{Start} and @code{Stop} methods on the @code{Application} to tell it when to start and stop doing its thing. diff --git a/examples/tutorial/fifth.cc b/examples/tutorial/fifth.cc index 980547cff..0067a36a1 100644 --- a/examples/tutorial/fifth.cc +++ b/examples/tutorial/fifth.cc @@ -59,12 +59,12 @@ NS_LOG_COMPONENT_DEFINE ("FifthScriptExample"); // install in the source node. // =========================================================================== // -class SimpleSource : public Application +class MyApp : public Application { public: - SimpleSource (); - virtual ~SimpleSource(); + MyApp (); + virtual ~MyApp(); void Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate); @@ -85,7 +85,7 @@ private: uint32_t m_packetsSent; }; -SimpleSource::SimpleSource () +MyApp::MyApp () : m_socket (0), m_peer (), m_packetSize (0), @@ -97,13 +97,13 @@ SimpleSource::SimpleSource () { } -SimpleSource::~SimpleSource() +MyApp::~MyApp() { m_socket = 0; } void -SimpleSource::Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate) +MyApp::Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate) { m_socket = socket; m_peer = address; @@ -113,7 +113,7 @@ SimpleSource::Setup (Ptr socket, Address address, uint32_t packetSize, u } void -SimpleSource::StartApplication (void) +MyApp::StartApplication (void) { m_running = true; m_packetsSent = 0; @@ -123,7 +123,7 @@ SimpleSource::StartApplication (void) } void -SimpleSource::StopApplication (void) +MyApp::StopApplication (void) { m_running = false; @@ -139,7 +139,7 @@ SimpleSource::StopApplication (void) } void -SimpleSource::SendPacket (void) +MyApp::SendPacket (void) { Ptr packet = Create (m_packetSize); m_socket->Send (packet); @@ -151,12 +151,12 @@ SimpleSource::SendPacket (void) } void -SimpleSource::ScheduleTx (void) +MyApp::ScheduleTx (void) { if (m_running) { Time tNext (Seconds (m_packetSize * 8 / static_cast (m_dataRate.GetBitRate ()))); - m_sendEvent = Simulator::Schedule (tNext, &SimpleSource::SendPacket, this); + m_sendEvent = Simulator::Schedule (tNext, &MyApp::SendPacket, this); } } @@ -207,7 +207,7 @@ main (int argc, char *argv[]) Ptr ns3TcpSocket = Socket::CreateSocket (nodes.Get (0), TcpSocketFactory::GetTypeId ()); ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndChange)); - Ptr app = CreateObject (); + Ptr app = CreateObject (); app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("5Mbps")); nodes.Get (0)->AddApplication (app); app->Start (Seconds (1.)); From 6450db55f20783215e7819a85223577937ee45d3 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Mon, 19 Oct 2009 18:47:01 -0700 Subject: [PATCH 48/63] tutorial nits --- doc/tutorial/tracing.texi | 70 ++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index 7b3be4519..67a6d039d 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -1194,7 +1194,7 @@ It turns out that the @command{ns-3} TCP implementation lives (mostly) in the file @code{src/internet-stack/tcp-socket-impl.cc}. If you don't know this a priori, you can use the recursive grep trick: -@vervatim +@verbatim find . -name '*.cc' | xargs grep -i tcp @end verbatim @@ -1278,10 +1278,8 @@ you will find that it looks just like an @code{ns-3} script. It turns out that is exactly what it is. It is a script run by the test framework, so we can just pull it out and wrap it in @code{main} instead of in @code{DoRun}. Rather than walk through this step, by step, we have provided the file that results from -porting this test back to a native @code{ns-3} script. - -Rather than go through this extraction process, we'll just provide the results -in a file named @code{examples/tutorial/fifth.cc}. +porting this test back to a native @code{ns-3} script -- +@code{examples/tutorial/fifth.cc}. @subsection A Common Problem and Solution @@ -1346,18 +1344,19 @@ favorite editor. You should see some familiar looking code: * Foundation, Include., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include ``ns3/core-module.h'' - #include ``ns3/common-module.h'' - #include ``ns3/simulator-module.h'' - #include ``ns3/node-module.h'' - #include ``ns3/helper-module.h'' + #include + #include "ns3/core-module.h" + #include "ns3/common-module.h" + #include "ns3/simulator-module.h" + #include "ns3/node-module.h" + #include "ns3/helper-module.h" using namespace ns3; - NS_LOG_COMPONENT_DEFINE (``FifthScriptExample''); + NS_LOG_COMPONENT_DEFINE ("FifthScriptExample"); @end verbatim -This has all been covered, so we won't discuss it. The next lines of source are +This has all been covered, so we won't rehash it. The next lines of source are the network illustration and a comment addressing the problem described above with @code{Socket}. @@ -1434,17 +1433,17 @@ we put together to allow the @code{Socket} to be created at configuration time. You can see that this class inherits from the @command{ns-3} @code{Application} class. Take a look at @code{src/node/application.h} if you are interested in -what is inherited. This class is obligated to override the +what is inherited. The @code{MyApp} class is obligated to override the @code{StartApplication} and @code{StopApplication} methods. These methods are -called when the corresponding @code{Start} and @code{Stop} methods are called -during simulation time. +called when the corresponding base class @code{Start} and @code{Stop} methods are +called during simulation time. @subsubsection How Applications are Started and Stopped It is worthwhile to spend a bit of time explaining how events actually get started in the system. The most common way to start pumping events is to start an @code{Application}. This is done as the result of the following (hopefully) -familar lines of an @sommand{ns-3} script: +familar lines of an @command{ns-3} script: @verbatim ApplicationContainer apps = ... @@ -1550,8 +1549,10 @@ running. You can see that it does a @code{Socket} @code{Bind} operation. If you are familiar with Berkeley Sockets this shouldn't be a surprise. It performs the required work on the local side of the connection just as you might expect. The following @code{Connect} will do what is required to establish a connection -with the TCP at @code{Address} m_peer. The @code{Application} then starts creating -simulation events by calling @code{SendPacket}. +with the TCP at @code{Address} m_peer. It should now be clear why we need to defer +a lot of this to simulation time, since the @code{Connect} is going to need a fully +functioning network to comlete. After the @code{Connect}, the @code{Application} +then starts creating simulation events by calling @code{SendPacket}. The next bit of code explains to the @code{Application} how to stop creating simulation events. @@ -1579,8 +1580,9 @@ Every time a simulation event is scheduled, an @code{Event} is created. If the return @code{true}. In this code, if @code{IsRunning()} returns true, we @code{Cancel} the event which removes it from the simulator event queue. By doing this, we break the chain of events that the @code{Application} is using to -keep sending its @code{Packets} and the event goes quiet. After we quiet the -@code{Application} we @code{Close} the socket which tears down the TCP connection. +keep sending its @code{Packets} and the @code{Application} goes quiet. After we +quiet the @code{Application} we @code{Close} the socket which tears down the TCP +connection. The socket is actually deleted in the destructor when the @code{m_socket = 0} is executed. This removes the last reference to the underlying Ptr which @@ -1636,8 +1638,9 @@ data rate as your underlying @code{Channel} you will eventually get a buffer ove @subsubsection The Trace Sinks -The whole point of this exercise is to get trace callbacks. The next piece of -code implements that callback: +The whole point of this exercise is to get trace callbacks from TCP indicating the +congestion window has been updated. The next piece of code implements the +corresponding trace sink: @verbatim static void @@ -1665,9 +1668,10 @@ error model to this code also, so we wanted to demonstrate this working. @end verbatim This trace sink will be connected to the ``PhyRxDrop'' trace source of the -point-to-point NetDevice. If you take a small detour there you will see -(in @code{src/devices/point-to-point/point-to-point-net-device.cc}) that -this trace source refers to @code{PointToPointNetDevice::m_phyRxDropTrace}. +point-to-point NetDevice. This trace source fires when a packet is dropped +by the physical layer of a @code{NetDevice}. If you take a small detour to the +source (@code{src/devices/point-to-point/point-to-point-net-device.cc}) you will +see that this trace source refers to @code{PointToPointNetDevice::m_phyRxDropTrace}. If you then look in @code{src/devices/point-to-point/point-to-point-net-device.h} for this member variable, you will find that it is declared as a @code{TracedCallback >}. This should tell you that the @@ -1678,7 +1682,7 @@ parameter which is a @code{Ptr} -- just what we have above. The following code should be very familiar to you by now: -@end verbatim +@verbatim int main (int argc, char *argv[]) { @@ -1709,7 +1713,7 @@ to introduce errors into a @code{Channel} at a given @emph{rate}. @verbatim Ptr em = CreateObjectWithAttributes ( "RanVar", RandomVariableValue (UniformVariable (0., 1.)), - "ErrorRate", DoubleValue (0.01)); + "ErrorRate", DoubleValue (0.00001)); devices.Get (0)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); @end verbatim @@ -1718,12 +1722,10 @@ using the two-step process of instantiating it and then setting Attributes, we use the convenience function @code{CreateObjectWithAttributes} which allows us to do both at the same time. We set the ``RanVar'' @code{Attribute} to a random variable that generates a uniform distribution -from 0 to 1. We also set the ``ErrorRate'' @code{Attribute} to the rate 0.01. +from 0 to 1. We also set the ``ErrorRate'' @code{Attribute}. We then set the resulting instantiated @code{RateErrorModel} as the error -model used by the point-to-point @code{NetDevice}. This will result in -about one of every one hundred packets being dropped by the net device -and give us those retransmissions we want to make our plot a little more -interesting. +model used by the point-to-point @code{NetDevice}. This will give us some +retransmissions and make our plot a little more interesting. @verbatim InternetStackHelper stack; @@ -1769,7 +1771,7 @@ can then be used, in turn, to create instances of the Objects created by the factory. The remaining parameter tells the @code{Application} which address and port it -should @cod{Bind} to. +should @code{Bind} to. The next two lines of code will create the socket and connect the trace source. @@ -1780,7 +1782,7 @@ The next two lines of code will create the socket and connect the trace source. MakeCallback (&CwndChange)); @end verbatim -The first statement calls the static member function @code{Socket::CreateSocket) +The first statement calls the static member function @code{Socket::CreateSocket} and provides a @code{Node} and an explicit @code{TypeId} for the object factory used to create the socket. This is a slightly lower level call than the @code{PacketSinkHelper} call above, and uses an explicit C++ type instead of From 7946c74a8033695d7040697204106c98939e941f Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Mon, 19 Oct 2009 22:21:22 -0700 Subject: [PATCH 49/63] Added tag ns-3.6-RC4 for changeset 39a82d7a0d66 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 4ee27e8a1..79381de90 100644 --- a/.hgtags +++ b/.hgtags @@ -39,3 +39,4 @@ c975274c9707b1f07d94cc51f205c351122131a5 ns-3.5 549243b47311211975b388cd64fcb9111caa2fc2 ns-3.6-RC1 8996042990466b1eda718a848e1c02923c0add74 ns-3.6-RC2 79ff6ad1adbb7b4677759ddf52028b68b0515168 ns-3.6-RC3 +39a82d7a0d661febe42e75f26ada79424258e330 ns-3.6-RC4 From 48ee52fffbfba7ad37b4952c99a2fb2050433574 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Tue, 20 Oct 2009 10:05:57 -0700 Subject: [PATCH 50/63] Make tests work with MinGW (make win32-system-wall-clock-ms work) --- src/core/system-wall-clock-ms.h | 27 ++++++++-- src/core/test.cc | 38 +++++--------- src/core/test.h | 14 +++--- src/core/unix-system-wall-clock-ms.cc | 69 ++++++++++++++++++++++---- src/core/win32-system-wall-clock-ms.cc | 18 +++++++ 5 files changed, 120 insertions(+), 46 deletions(-) diff --git a/src/core/system-wall-clock-ms.h b/src/core/system-wall-clock-ms.h index 0c512d3ad..9cca3ca38 100644 --- a/src/core/system-wall-clock-ms.h +++ b/src/core/system-wall-clock-ms.h @@ -25,6 +25,9 @@ namespace ns3 { /** * \brief measure wall-clock time in milliseconds + + * \todo This class exists also in non-unix systems but is not + * implemented and always return zero as measurd time. */ class SystemWallClockMs { public: @@ -36,13 +39,31 @@ public: */ void Start (void); /** - * \returns the measured elapsed wall clock time since - * ns3::SystemWallClockMs::start was invoked. + * \brief Stop measuring the time since Start() was called. + * \returns the measured elapsed wall clock time (in milliseconds) since + * ns3::SystemWallClockMs::Start was invoked. * - * It is possible to start a new measurement with ns3::SystemWallClockMs::start + * It is possible to start a new measurement with ns3::SystemWallClockMs::Start * after this method returns. */ unsigned long long End (void); + + /** + * \returns the measured elapsed wall clock time (in milliseconds) since + * ns3::SystemWallClockMs::Start was invoked. + */ + double GetElapsedReal (void) const; + /** + * \returns the measured elapsed 'user' wall clock time (in milliseconds) since + * ns3::SystemWallClockMs::Start was invoked. + */ + double GetElapsedUser (void) const; + /** + * \returns the measured elapsed 'system' wall clock time (in milliseconds) since + * ns3::SystemWallClockMs::Start was invoked. + */ + double GetElapsedSystem (void) const; + private: class SystemWallClockMsPrivate *m_priv; }; diff --git a/src/core/test.cc b/src/core/test.cc index 57d285ad0..3b3a2db24 100644 --- a/src/core/test.cc +++ b/src/core/test.cc @@ -262,7 +262,7 @@ TestCase::ContinueOnFailure (void) void TestCase::DoReportStart (void) { - m_startTime = times (&m_startTimes); + m_clock.Start (); if (m_ofs == 0) { @@ -319,26 +319,18 @@ TestCase::DoReportTestFailure ( void TestCase::DoReportEnd (void) { - static long ticksPerSecond = sysconf (_SC_CLK_TCK); - + m_clock.End (); if (m_ofs == 0) { return; } - struct tms endTimes; - clock_t endTime = times (&endTimes); - - clock_t elapsed = endTime - m_startTime; - clock_t elapsedUsr = endTimes.tms_utime - m_startTimes.tms_utime; - clock_t elapsedSys = endTimes.tms_stime - m_startTimes.tms_stime; - (*m_ofs).precision (2); *m_ofs << std::fixed; - *m_ofs << " " << "real " << static_cast (elapsed) / ticksPerSecond - << " user " << static_cast (elapsedUsr) / ticksPerSecond - << " system " << static_cast (elapsedSys) / ticksPerSecond + *m_ofs << " " << "real " << m_clock.GetElapsedReal () * 1e-3 + << " user " << m_clock.GetElapsedUser () * 1e-3 + << " system " << m_clock.GetElapsedSystem () * 1e-3 << "" << std::endl; *m_ofs << " " << std::endl; @@ -523,8 +515,8 @@ TestSuite::ContinueOnFailure (void) void TestSuite::DoReportStart (void) { - m_startTime = times (&m_startTimes); - + m_clock.Start (); + if (m_ofs == 0) { return; @@ -556,25 +548,19 @@ TestSuite::DoReportSuccess (void) void TestSuite::DoReportEnd (void) { - static long ticksPerSecond = sysconf (_SC_CLK_TCK); - + m_clock.End (); + if (m_ofs == 0) { return; } - struct tms endTimes; - clock_t endTime = times (&endTimes); - - clock_t elapsed = endTime - m_startTime; - clock_t elapsedUsr = endTimes.tms_utime - m_startTimes.tms_utime; - clock_t elapsedSys = endTimes.tms_stime - m_startTimes.tms_stime; (*m_ofs).precision (2); *m_ofs << std::fixed; - *m_ofs << " " << "real " << static_cast (elapsed) / ticksPerSecond - << " user " << static_cast (elapsedUsr) / ticksPerSecond - << " system " << static_cast (elapsedSys) / ticksPerSecond + *m_ofs << " " << "real " << m_clock.GetElapsedReal () * 1e-3 + << " user " << m_clock.GetElapsedUser () * 1e-3 + << " system " << m_clock.GetElapsedSystem () * 1e-3 << "" << std::endl; *m_ofs << "" << std::endl; diff --git a/src/core/test.h b/src/core/test.h index 58db025f9..1a6e77950 100644 --- a/src/core/test.h +++ b/src/core/test.h @@ -27,7 +27,10 @@ #include #include #include -#include + +#include "ns3/system-wall-clock-ms.h" + + // // Note on below macros: // @@ -821,6 +824,7 @@ private: TestCase (TestCase& tc); TestCase& operator= (TestCase& tc); + SystemWallClockMs m_clock; std::string m_name; bool m_verbose; bool m_continueOnFailure; @@ -828,8 +832,6 @@ private: std::string m_basedir; std::ofstream *m_ofs; bool m_error; - clock_t m_startTime; - struct tms m_startTimes; }; /** @@ -1057,6 +1059,7 @@ private: TestSuite (TestSuite& ts); TestSuite& operator= (TestSuite& ts); + SystemWallClockMs m_clock; std::string m_name; bool m_verbose; bool m_continueOnFailure; @@ -1064,10 +1067,7 @@ private: std::ofstream *m_ofs; bool m_error; TestType m_type; - - clock_t m_startTime; - struct tms m_startTimes; - + typedef std::vector TestCaseVector_t; TestCaseVector_t m_tests; }; diff --git a/src/core/unix-system-wall-clock-ms.cc b/src/core/unix-system-wall-clock-ms.cc index 14f1fbb5b..cfe53932f 100644 --- a/src/core/unix-system-wall-clock-ms.cc +++ b/src/core/unix-system-wall-clock-ms.cc @@ -19,7 +19,9 @@ */ #include "system-wall-clock-ms.h" -#include +#include +#include +#include namespace ns3 { @@ -27,28 +29,57 @@ class SystemWallClockMsPrivate { public: void Start (void); unsigned long long End (void); + double GetElapsedReal (void) const; + double GetElapsedUser (void) const; + double GetElapsedSystem (void) const; + private: - struct timeval m_startTv; - struct timeval m_endTv; + struct tms m_startTimes; + clock_t m_startTime; + double m_elapsedReal; + double m_elapsedUser; + double m_elapsedSystem; }; void SystemWallClockMsPrivate::Start (void) { - struct timezone tz; - gettimeofday (&m_startTv, &tz); + m_startTime = times (&m_startTimes); } unsigned long long SystemWallClockMsPrivate::End (void) { - struct timezone tz; - gettimeofday (&m_endTv, &tz); - unsigned long long end = m_endTv.tv_sec *1000 + m_endTv.tv_usec / 1000; - unsigned long long start = m_startTv.tv_sec *1000 + m_startTv.tv_usec / 1000; - return end - start; + static long ticksPerSecond = sysconf (_SC_CLK_TCK); + + struct tms endTimes; + clock_t endTime = times (&endTimes); + + m_elapsedReal = 1e3 * static_cast (endTime - m_startTime) / ticksPerSecond; + m_elapsedUser = 1e3 * static_cast (endTimes.tms_utime - m_startTimes.tms_utime) / ticksPerSecond; + m_elapsedSystem = 1e3 * static_cast (endTimes.tms_stime - m_startTimes.tms_stime) / ticksPerSecond; + + return m_elapsedReal; } +double +SystemWallClockMsPrivate::GetElapsedReal (void) const +{ + return m_elapsedReal; +} + +double +SystemWallClockMsPrivate::GetElapsedUser (void) const +{ + return m_elapsedUser; +} + +double +SystemWallClockMsPrivate::GetElapsedSystem (void) const +{ + return m_elapsedSystem; +} + SystemWallClockMs::SystemWallClockMs () : m_priv (new SystemWallClockMsPrivate ()) {} @@ -70,4 +101,22 @@ SystemWallClockMs::End (void) return m_priv->End (); } +double +SystemWallClockMs::GetElapsedReal (void) const +{ + return m_priv->GetElapsedReal (); +} + +double +SystemWallClockMs::GetElapsedUser (void) const +{ + return m_priv->GetElapsedUser (); +} + +double +SystemWallClockMs::GetElapsedSystem (void) const +{ + return m_priv->GetElapsedSystem (); +} + }; // namespace ns3 diff --git a/src/core/win32-system-wall-clock-ms.cc b/src/core/win32-system-wall-clock-ms.cc index 1cc9fc2c5..c02f0f268 100644 --- a/src/core/win32-system-wall-clock-ms.cc +++ b/src/core/win32-system-wall-clock-ms.cc @@ -61,4 +61,22 @@ SystemWallClockMs::End (void) return m_priv->End (); } +double +SystemWallClockMs::GetElapsedReal (void) const +{ + return 0; +} + +double +SystemWallClockMs::GetElapsedUser (void) const +{ + return 0; +} + +double +SystemWallClockMs::GetElapsedSystem (void) const +{ + return 0; +} + }; // namespace ns3 From 1f8d1c854d96105b4566e6519378e96c47a3f5a8 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Tue, 20 Oct 2009 10:45:12 -0700 Subject: [PATCH 51/63] tracing tutorial changes --- doc/tutorial/figures/cwnd.png | Bin 10703 -> 5958 bytes doc/tutorial/tracing.texi | 50 +++++++++++++++++----------------- examples/tutorial/fifth.cc | 4 +-- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/tutorial/figures/cwnd.png b/doc/tutorial/figures/cwnd.png index b573d9dbacab3af0b558b00d8ed893e6583984e0..363e053b1bbfe634140f3efcbb8fb6c2d6aed14b 100644 GIT binary patch literal 5958 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 bQpr9w%&+J$d1kh*gIzbHSr``?(a-!3*B8(4 diff --git a/doc/tutorial/tracing.texi b/doc/tutorial/tracing.texi index 67a6d039d..869e561bb 100644 --- a/doc/tutorial/tracing.texi +++ b/doc/tutorial/tracing.texi @@ -1127,9 +1127,9 @@ to the important piece of code: Here you see that the @code{TracedValue} is templated, of course. In the simple example case at the start of the section, the typename is int32_t. This means -that the member variable being traced (@code>m_v} in the private section of the +that the member variable being traced (@code{m_v} in the private section of the class) will be an @code{int32_t m_v}. The @code{Set} method will take a -@code{const uint32_t &v} as a parameter. You should now be able to understand +@code{const int32_t &v} as a parameter. You should now be able to understand that the @code{Set} code will fire the @code{m_cb} callback with two parameters: the first being the current value of the @code{TracedValue}; and the second being the new value being set. @@ -1146,7 +1146,7 @@ looks like: @verbatim void - MyCallback (uint32_t oldValue, uint32_t newValue) + MyCallback (int32_t oldValue, int32_t newValue) { ... } @@ -1246,7 +1246,7 @@ being the new value: @subsection What Script to Use? It's always best to try and find working code laying around that you can -modify, rather than starting from scratch. So the fist order of business now +modify, rather than starting from scratch. So the first order of business now is to find some code that already hooks the ``CongestionWindow'' trace source and see if we can modify it. As usual, grep is your friend: @@ -1277,7 +1277,7 @@ and extract the code we need from this function you will find that it looks just like an @code{ns-3} script. It turns out that is exactly what it is. It is a script run by the test framework, so we can just pull it out and wrap it in @code{main} instead of in @code{DoRun}. Rather than -walk through this step, by step, we have provided the file that results from +walk through this, step, by step, we have provided the file that results from porting this test back to a native @code{ns-3} script -- @code{examples/tutorial/fifth.cc}. @@ -1287,7 +1287,7 @@ The @code{fifth.cc} example demonstrates an extremely important rule that you must understand before using any kind of @code{Attribute}: you must ensure that the target of a @code{Config} command exists before trying to use it. This is no different than saying an object must be instantiated before trying -to call it, Although this may seem obvious when stated this way, it does +to call it. Although this may seem obvious when stated this way, it does trip up many people trying to use the system for the first time. Let's return to basics for a moment. There are three basic time periods that @@ -1300,7 +1300,7 @@ when the @code{main} function of your script is running, but before executing the simulation, @code{Simulator::Run} will return control back to the @code{main} function. When this happens, the script enters what can be called ``Teardown Time,'' which is when the structures and objects created -during setup and taken apart and released, +during setup and taken apart and released. Perhaps the most common mistake made in trying to use the tracing system is assuming that entities constructed dynamically during simulation time are @@ -1314,7 +1314,7 @@ is to ensure that the simulation is completely configured before the app tries to do anything (what would happen if it tried to connect to a node that didn't exist yet during configuration time). The answer to this issue is to 1) create a simulator event that is run after the dynamic object -is created and hook the trace when that event is executedl or 2) create the +is created and hook the trace when that event is executed; or 2) create the dynamic object at configuration time, hook it then, and give the object to the system to use during simulation time. We took the second approach in the @code{fifth.cc} example. This decision required us to create the @@ -1323,7 +1323,7 @@ take a @code{Socket} as a parameter. @subsection A fifth.cc Walkthrough -Now, let's take a look at the example program we constructed by disecting +Now, let's take a look at the example program we constructed by dissecting the congestion window test. Open @code{examples/tutorial/fifth.cc} in your favorite editor. You should see some familiar looking code: @@ -1551,7 +1551,7 @@ the required work on the local side of the connection just as you might expect. The following @code{Connect} will do what is required to establish a connection with the TCP at @code{Address} m_peer. It should now be clear why we need to defer a lot of this to simulation time, since the @code{Connect} is going to need a fully -functioning network to comlete. After the @code{Connect}, the @code{Application} +functioning network to complete. After the @code{Connect}, the @code{Application} then starts creating simulation events by calling @code{SendPacket}. The next bit of code explains to the @code{Application} how to stop creating @@ -1627,7 +1627,7 @@ has sent enough. @end verbatim Here, you see that @code{ScheduleTx} does exactly that. If the @code{Applciation} -is running (if @code{StopApplication}) has not been called) it will schedule a +is running (if @code{StopApplication} has not been called) it will schedule a new event, which calls @code{SendPacket} again. The alert reader will spot something that also trips up new users. The data rate of an @code{Application} is just that. It has nothing to do with the data rate of an underlying @code{Channel}. @@ -1656,8 +1656,8 @@ congestion window every time it is changed. You can probably imagine that you could load the resulting output into a graphics program (gnuplot or Excel) and immediately see a nice graph of the congestion window behavior over time. -We added a new trace sink to show where are dropped. We are going to add an -error model to this code also, so we wanted to demonstrate this working. +We added a new trace sink to show where packets are dropped. We are going to +add an error model to this code also, so we wanted to demonstrate this working. @verbatim static void @@ -1700,7 +1700,7 @@ The following code should be very familiar to you by now: This creates two nodes with a point-to-point channel between them, just as shown in the illustration at the start of the file. -The next few lines of code show somthing new. If we trace a connection that +The next few lines of code show something new. If we trace a connection that behaves perfectly, we will end up with a monotonically increasing congestion window. To see any interesting behavior, we really want to introduce link errors which will drop packets, cause duplicate ACKs and trigger the more @@ -1714,7 +1714,7 @@ to introduce errors into a @code{Channel} at a given @emph{rate}. Ptr em = CreateObjectWithAttributes ( "RanVar", RandomVariableValue (UniformVariable (0., 1.)), "ErrorRate", DoubleValue (0.00001)); - devices.Get (0)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); + devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); @end verbatim The above code instantiates a @code{RateErrorModel} Object. Rather than @@ -1856,17 +1856,17 @@ builds optimize out NS_LOGs) it will be waiting for you to run. @verbatim ./waf --run fifth - Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build' + Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-dev/ns-3-dev/build' - 'build' finished successfully (0.525s) - 1.21397 1072 - 1.22755 1608 - 1.24114 2144 + 'build' finished successfully (0.684s) + 1.20919 1072 + 1.21511 1608 + 1.22103 2144 ... - 1.35211 8576 - 1.36136 9112 - 1.37061 9648 - RxDrop at 1.3729 + 1.2471 8040 + 1.24895 8576 + 1.2508 9112 + RxDrop at 1.25151 ... @end verbatim @@ -1889,7 +1889,7 @@ You can now run gnuplot (if you have it installed) and tell it to generate some pretty pictures: @verbatim - gnuplot> set terminal png size 1024,768 + gnuplot> set terminal png size 640,480 gnuplot> set output "cwnd.png" gnuplot> plot "cwnd.dat" using 1:2 title 'Congestion Window' with linespoints gnuplot> exit diff --git a/examples/tutorial/fifth.cc b/examples/tutorial/fifth.cc index 0067a36a1..75e8f48b0 100644 --- a/examples/tutorial/fifth.cc +++ b/examples/tutorial/fifth.cc @@ -179,7 +179,7 @@ main (int argc, char *argv[]) nodes.Create (2); PointToPointHelper pointToPoint; - pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("1Mbps")); + pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); NetDeviceContainer devices; @@ -208,7 +208,7 @@ main (int argc, char *argv[]) ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndChange)); Ptr app = CreateObject (); - app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("5Mbps")); + app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps")); nodes.Get (0)->AddApplication (app); app->Start (Seconds (1.)); app->Stop (Seconds (20.)); From dba4030746e683c91b2a646790a3881d2b0d7980 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Tue, 20 Oct 2009 11:13:22 -0700 Subject: [PATCH 52/63] add closing section to tutorial --- doc/tutorial/conclusion.texi | 60 ++++++++++++++++++++++++++++++++++++ doc/tutorial/tutorial.texi | 2 ++ 2 files changed, 62 insertions(+) create mode 100644 doc/tutorial/conclusion.texi diff --git a/doc/tutorial/conclusion.texi b/doc/tutorial/conclusion.texi new file mode 100644 index 000000000..e0a67b9a7 --- /dev/null +++ b/doc/tutorial/conclusion.texi @@ -0,0 +1,60 @@ +@c ============================================================================ +@c Begin document body here +@c ============================================================================ + +@c ============================================================================ +@c PART: Closing Remarks +@c ============================================================================ +@c The below chapters are under the major heading "Closing Remarks" +@c This is similar to the Latex \part command +@c +@c ============================================================================ +@c Closing Remarks +@c ============================================================================ +@node Closing Remarks +@chapter Closing Remarks + +@menu +* Futures:: +* Closing:: +@end menu + +@c ============================================================================ +@c Futures +@c ============================================================================ +@node +@section Futures + +This document is a work in process. We hope and expect it to grow over time +to cover more and more of the nuts and bolts of @command{ns-3}. + +We hope to add the following chapters over the next few releases: + +@itemize @bullet +@item The Callback System +@item The Object System and Memory Management +@item The Routing System +@item Adding a New NetDevice and Channel +@item Adding a New Protocol +@item Working with Real Networks and Hosts +@end itemize + +Writing manual and tutorial chapters is not something we all get excited about, +but it is very important to the project. If you are an expert in one of these +areas, please consider contributing to @command{ns-3} by providing one of these +chapters; or any other chapter you may think is important. + +@c ============================================================================ +@c Closing +@c ============================================================================ +@node +@section Closing + +@code{ns-3} is a large and complicated system. It is impossible to cover all +of the things you will need to know in one small tutorial. + +We have really just scratched the surface of @command{ns-3} in this tutorial, +but we hope to have covered enough to get you started doing useful networking +research using our favorite simulator. + +-- The @command{ns-3} development team. diff --git a/doc/tutorial/tutorial.texi b/doc/tutorial/tutorial.texi index 6678f207f..958262e0b 100644 --- a/doc/tutorial/tutorial.texi +++ b/doc/tutorial/tutorial.texi @@ -85,6 +85,7 @@ see @uref{http://www.nsnam.org/docs/tutorial.pdf}. * Tweaking ns-3:: * Building Topologies:: * The Tracing System:: +* Closing Remarks:: * Index:: @end menu @@ -94,6 +95,7 @@ see @uref{http://www.nsnam.org/docs/tutorial.pdf}. @include tweaking.texi @include building-topologies.texi @include tracing.texi +@include conclusion.texi @node Index @unnumbered Index From b2dbe4454e8f9dedaccddb8b8185176b0a8ca3a6 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Tue, 20 Oct 2009 12:04:20 -0700 Subject: [PATCH 53/63] Backed out changeset 04cc3ffe0202 --- src/core/system-wall-clock-ms.h | 27 ++-------- src/core/test.cc | 38 +++++++++----- src/core/test.h | 14 +++--- src/core/unix-system-wall-clock-ms.cc | 69 ++++---------------------- src/core/win32-system-wall-clock-ms.cc | 18 ------- 5 files changed, 46 insertions(+), 120 deletions(-) diff --git a/src/core/system-wall-clock-ms.h b/src/core/system-wall-clock-ms.h index 9cca3ca38..0c512d3ad 100644 --- a/src/core/system-wall-clock-ms.h +++ b/src/core/system-wall-clock-ms.h @@ -25,9 +25,6 @@ namespace ns3 { /** * \brief measure wall-clock time in milliseconds - - * \todo This class exists also in non-unix systems but is not - * implemented and always return zero as measurd time. */ class SystemWallClockMs { public: @@ -39,31 +36,13 @@ public: */ void Start (void); /** - * \brief Stop measuring the time since Start() was called. - * \returns the measured elapsed wall clock time (in milliseconds) since - * ns3::SystemWallClockMs::Start was invoked. + * \returns the measured elapsed wall clock time since + * ns3::SystemWallClockMs::start was invoked. * - * It is possible to start a new measurement with ns3::SystemWallClockMs::Start + * It is possible to start a new measurement with ns3::SystemWallClockMs::start * after this method returns. */ unsigned long long End (void); - - /** - * \returns the measured elapsed wall clock time (in milliseconds) since - * ns3::SystemWallClockMs::Start was invoked. - */ - double GetElapsedReal (void) const; - /** - * \returns the measured elapsed 'user' wall clock time (in milliseconds) since - * ns3::SystemWallClockMs::Start was invoked. - */ - double GetElapsedUser (void) const; - /** - * \returns the measured elapsed 'system' wall clock time (in milliseconds) since - * ns3::SystemWallClockMs::Start was invoked. - */ - double GetElapsedSystem (void) const; - private: class SystemWallClockMsPrivate *m_priv; }; diff --git a/src/core/test.cc b/src/core/test.cc index 3b3a2db24..57d285ad0 100644 --- a/src/core/test.cc +++ b/src/core/test.cc @@ -262,7 +262,7 @@ TestCase::ContinueOnFailure (void) void TestCase::DoReportStart (void) { - m_clock.Start (); + m_startTime = times (&m_startTimes); if (m_ofs == 0) { @@ -319,18 +319,26 @@ TestCase::DoReportTestFailure ( void TestCase::DoReportEnd (void) { - m_clock.End (); + static long ticksPerSecond = sysconf (_SC_CLK_TCK); + if (m_ofs == 0) { return; } + struct tms endTimes; + clock_t endTime = times (&endTimes); + + clock_t elapsed = endTime - m_startTime; + clock_t elapsedUsr = endTimes.tms_utime - m_startTimes.tms_utime; + clock_t elapsedSys = endTimes.tms_stime - m_startTimes.tms_stime; + (*m_ofs).precision (2); *m_ofs << std::fixed; - *m_ofs << " " << "real " << m_clock.GetElapsedReal () * 1e-3 - << " user " << m_clock.GetElapsedUser () * 1e-3 - << " system " << m_clock.GetElapsedSystem () * 1e-3 + *m_ofs << " " << "real " << static_cast (elapsed) / ticksPerSecond + << " user " << static_cast (elapsedUsr) / ticksPerSecond + << " system " << static_cast (elapsedSys) / ticksPerSecond << "" << std::endl; *m_ofs << " " << std::endl; @@ -515,8 +523,8 @@ TestSuite::ContinueOnFailure (void) void TestSuite::DoReportStart (void) { - m_clock.Start (); - + m_startTime = times (&m_startTimes); + if (m_ofs == 0) { return; @@ -548,19 +556,25 @@ TestSuite::DoReportSuccess (void) void TestSuite::DoReportEnd (void) { - m_clock.End (); - + static long ticksPerSecond = sysconf (_SC_CLK_TCK); + if (m_ofs == 0) { return; } + struct tms endTimes; + clock_t endTime = times (&endTimes); + + clock_t elapsed = endTime - m_startTime; + clock_t elapsedUsr = endTimes.tms_utime - m_startTimes.tms_utime; + clock_t elapsedSys = endTimes.tms_stime - m_startTimes.tms_stime; (*m_ofs).precision (2); *m_ofs << std::fixed; - *m_ofs << " " << "real " << m_clock.GetElapsedReal () * 1e-3 - << " user " << m_clock.GetElapsedUser () * 1e-3 - << " system " << m_clock.GetElapsedSystem () * 1e-3 + *m_ofs << " " << "real " << static_cast (elapsed) / ticksPerSecond + << " user " << static_cast (elapsedUsr) / ticksPerSecond + << " system " << static_cast (elapsedSys) / ticksPerSecond << "" << std::endl; *m_ofs << "" << std::endl; diff --git a/src/core/test.h b/src/core/test.h index 1a6e77950..58db025f9 100644 --- a/src/core/test.h +++ b/src/core/test.h @@ -27,10 +27,7 @@ #include #include #include - -#include "ns3/system-wall-clock-ms.h" - - +#include // // Note on below macros: // @@ -824,7 +821,6 @@ private: TestCase (TestCase& tc); TestCase& operator= (TestCase& tc); - SystemWallClockMs m_clock; std::string m_name; bool m_verbose; bool m_continueOnFailure; @@ -832,6 +828,8 @@ private: std::string m_basedir; std::ofstream *m_ofs; bool m_error; + clock_t m_startTime; + struct tms m_startTimes; }; /** @@ -1059,7 +1057,6 @@ private: TestSuite (TestSuite& ts); TestSuite& operator= (TestSuite& ts); - SystemWallClockMs m_clock; std::string m_name; bool m_verbose; bool m_continueOnFailure; @@ -1067,7 +1064,10 @@ private: std::ofstream *m_ofs; bool m_error; TestType m_type; - + + clock_t m_startTime; + struct tms m_startTimes; + typedef std::vector TestCaseVector_t; TestCaseVector_t m_tests; }; diff --git a/src/core/unix-system-wall-clock-ms.cc b/src/core/unix-system-wall-clock-ms.cc index cfe53932f..14f1fbb5b 100644 --- a/src/core/unix-system-wall-clock-ms.cc +++ b/src/core/unix-system-wall-clock-ms.cc @@ -19,9 +19,7 @@ */ #include "system-wall-clock-ms.h" -#include -#include -#include +#include namespace ns3 { @@ -29,57 +27,28 @@ class SystemWallClockMsPrivate { public: void Start (void); unsigned long long End (void); - double GetElapsedReal (void) const; - double GetElapsedUser (void) const; - double GetElapsedSystem (void) const; - private: - struct tms m_startTimes; - clock_t m_startTime; - double m_elapsedReal; - double m_elapsedUser; - double m_elapsedSystem; + struct timeval m_startTv; + struct timeval m_endTv; }; void SystemWallClockMsPrivate::Start (void) { - m_startTime = times (&m_startTimes); + struct timezone tz; + gettimeofday (&m_startTv, &tz); } unsigned long long SystemWallClockMsPrivate::End (void) { - static long ticksPerSecond = sysconf (_SC_CLK_TCK); - - struct tms endTimes; - clock_t endTime = times (&endTimes); - - m_elapsedReal = 1e3 * static_cast (endTime - m_startTime) / ticksPerSecond; - m_elapsedUser = 1e3 * static_cast (endTimes.tms_utime - m_startTimes.tms_utime) / ticksPerSecond; - m_elapsedSystem = 1e3 * static_cast (endTimes.tms_stime - m_startTimes.tms_stime) / ticksPerSecond; - - return m_elapsedReal; + struct timezone tz; + gettimeofday (&m_endTv, &tz); + unsigned long long end = m_endTv.tv_sec *1000 + m_endTv.tv_usec / 1000; + unsigned long long start = m_startTv.tv_sec *1000 + m_startTv.tv_usec / 1000; + return end - start; } -double -SystemWallClockMsPrivate::GetElapsedReal (void) const -{ - return m_elapsedReal; -} - -double -SystemWallClockMsPrivate::GetElapsedUser (void) const -{ - return m_elapsedUser; -} - -double -SystemWallClockMsPrivate::GetElapsedSystem (void) const -{ - return m_elapsedSystem; -} - SystemWallClockMs::SystemWallClockMs () : m_priv (new SystemWallClockMsPrivate ()) {} @@ -101,22 +70,4 @@ SystemWallClockMs::End (void) return m_priv->End (); } -double -SystemWallClockMs::GetElapsedReal (void) const -{ - return m_priv->GetElapsedReal (); -} - -double -SystemWallClockMs::GetElapsedUser (void) const -{ - return m_priv->GetElapsedUser (); -} - -double -SystemWallClockMs::GetElapsedSystem (void) const -{ - return m_priv->GetElapsedSystem (); -} - }; // namespace ns3 diff --git a/src/core/win32-system-wall-clock-ms.cc b/src/core/win32-system-wall-clock-ms.cc index c02f0f268..1cc9fc2c5 100644 --- a/src/core/win32-system-wall-clock-ms.cc +++ b/src/core/win32-system-wall-clock-ms.cc @@ -61,22 +61,4 @@ SystemWallClockMs::End (void) return m_priv->End (); } -double -SystemWallClockMs::GetElapsedReal (void) const -{ - return 0; -} - -double -SystemWallClockMs::GetElapsedUser (void) const -{ - return 0; -} - -double -SystemWallClockMs::GetElapsedSystem (void) const -{ - return 0; -} - }; // namespace ns3 From f8e7971e6fc353eee484888c34878c62865669b7 Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Mon, 19 Oct 2009 20:55:52 -0700 Subject: [PATCH 54/63] Remove SetEdcaParameterForAc() from manual --- doc/manual/wifi.texi | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/doc/manual/wifi.texi b/doc/manual/wifi.texi index 467cfc8da..6b8a648fc 100644 --- a/doc/manual/wifi.texi +++ b/doc/manual/wifi.texi @@ -222,27 +222,17 @@ A possible user code: @smallformat @example QosWifiMacHelper wifiMacHelper = QosWifiMacHelper::Default (); - wifiMacHelper.SetType ("ns3::QapWifiMac", "Ssid", SsidValue (ssid), -"BeaconGeneration", BooleanValue (true), - "BeaconInterval", TimeValue (Seconds (2.5))); - wifiMacHelper.SetEdcaParametersForAc (AC_VO, "MinCw", UintegerValue (2)); + wifiMacHelper.SetType ("ns3::QapWifiMac", + "Ssid", SsidValue (ssid), + "BeaconGeneration", BooleanValue (true), + "BeaconInterval", TimeValue (Seconds (2.5))); wifiMacHelper.SetMsduAggregatorForAc (AC_VO, "ns3::MsduStandardAggregator", -"MaxAmsduSize", UintegerValue (3839)); + "MaxAmsduSize", UintegerValue (3839)); @end example @end smallformat Call to QosWifiMacHelper::Default () is needed in order to set default EDCA parameters properly for all -access classes. Otherwise we should set them one by one: -@smallformat -@example - QosWifiMacHelper wifiMacHelper; - wifiMacHelper.SetEdcaParametersForAc (AC_VO, "MinCw", UintegerValue (2), -"MaxCw", UintegerValue (7), "Aifsn", UintegerValue (2)); - wifiMacHelper.SetEdcaParametersForAc (AC_VI, "MinCw", UintegerValue (7), -"MaxCw", UintegerValue (15), "Aifsn", UintegerValue (2)); - ... -@end example -@end smallformat +access classes; otherwise we should set them one by one. @subsection WifiHelper From 970e4b6d2d57078447cdf44c837abdc4866abc3f Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Wed, 21 Oct 2009 00:17:14 -0700 Subject: [PATCH 55/63] remove comment referencing non-working use case --- examples/tap/tap-wifi-dumbbell.cc | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/examples/tap/tap-wifi-dumbbell.cc b/examples/tap/tap-wifi-dumbbell.cc index cf268ede5..df2883ebf 100644 --- a/examples/tap/tap-wifi-dumbbell.cc +++ b/examples/tap/tap-wifi-dumbbell.cc @@ -81,17 +81,7 @@ // sudo route add -net 10.1.3.0 netmask 255.255.255.0 dev thetap gw 10.1.1.2 // ping 10.1.3.4 // -// 4) Try to run this in UseLocal mode. This allows you to provide an existing -// pre-configured tap device to the simulation. The IP address and MAC -// address in this mode do not have to match those of the ns-3 device. -// -// sudo tunctl -t mytap -// sudo ifconfig mytap hw ether 08:00:2e:00:00:01 -// sudo ifconfig mytap 10.1.1.1 netmask 255.255.255.0 up -// ./waf --run "tap-wifi-dumbbell --mode=UseLocal --tapName=mytap"& -// ping 10.1.1.3 -// -// 5) Try to run this in UseBridge mode. This allows you to bridge an ns-3 +// 4) Try to run this in UseBridge mode. This allows you to bridge an ns-3 // simulation to an existing pre-configured bridge. This uses tap devices // just for illustration, you can create your own bridge if you want. // From bdb28bbea624d393a571c72bc0d5e32146118272 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Wed, 21 Oct 2009 17:19:03 -0700 Subject: [PATCH 56/63] Added tag ns-3.6 for changeset d55c479666ac --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 79381de90..1394c82d5 100644 --- a/.hgtags +++ b/.hgtags @@ -40,3 +40,4 @@ c975274c9707b1f07d94cc51f205c351122131a5 ns-3.5 8996042990466b1eda718a848e1c02923c0add74 ns-3.6-RC2 79ff6ad1adbb7b4677759ddf52028b68b0515168 ns-3.6-RC3 39a82d7a0d661febe42e75f26ada79424258e330 ns-3.6-RC4 +d55c479666ac6be0575fac695ddf355c0530e0dd ns-3.6 From 84e1fc122da0e0eb72bde7fb55f8105ede7d535a Mon Sep 17 00:00:00 2001 From: Kirill Andreev Date: Fri, 23 Oct 2009 15:34:14 +0400 Subject: [PATCH 57/63] Mesh: fixed HWMP sequence and metric filtering, added route discovery time trace source, fixed beacon collision avoidanceadded ability to enable/disable BCA --- src/devices/mesh/dot11s/airtime-metric.cc | 6 +- src/devices/mesh/dot11s/airtime-metric.h | 8 +- src/devices/mesh/dot11s/hwmp-protocol.cc | 81 +++--- src/devices/mesh/dot11s/hwmp-protocol.h | 17 +- src/devices/mesh/dot11s/peer-link.cc | 10 + src/devices/mesh/dot11s/peer-link.h | 4 +- .../dot11s/peer-management-protocol-mac.cc | 28 +- .../dot11s/peer-management-protocol-mac.h | 9 +- .../mesh/dot11s/peer-management-protocol.cc | 273 ++++++++---------- .../mesh/dot11s/peer-management-protocol.h | 70 ++--- src/devices/mesh/mesh-wifi-beacon.cc | 5 + src/devices/mesh/mesh-wifi-beacon.h | 2 + src/helper/dot11s-installer.cc | 2 +- 13 files changed, 241 insertions(+), 274 deletions(-) diff --git a/src/devices/mesh/dot11s/airtime-metric.cc b/src/devices/mesh/dot11s/airtime-metric.cc index cf6c27574..b1b6e2863 100644 --- a/src/devices/mesh/dot11s/airtime-metric.cc +++ b/src/devices/mesh/dot11s/airtime-metric.cc @@ -31,7 +31,7 @@ AirtimeLinkMetricCalculator::GetTypeId () .SetParent () .AddConstructor () .AddAttribute ( "OverheadNanosec", - "Overhead expressed in nanoseconds:DIFS+ 2* SIFS + 2* PREAMBLE + 2* ACK", + "Overhead expressed in nanoseconds:DIFS+ SIFS + 2 * PREAMBLE + ACK", UintegerValue (108000), MakeUintegerAccessor (&AirtimeLinkMetricCalculator::m_overheadNanosec), MakeUintegerChecker (1) @@ -80,9 +80,9 @@ AirtimeLinkMetricCalculator::CalculateMetric (Mac48Address peerAddress, PtrGetStationManager ()->Lookup (peerAddress); NS_ASSERT (station != 0); - Ptr test_frame = Create (m_testLength + m_headerLength + m_meshHeaderLength); + Ptr test_frame = Create (m_testLength + m_meshHeaderLength); uint32_t rate = - station->GetDataMode (test_frame, m_testLength + m_headerLength + m_meshHeaderLength).GetDataRate (); + station->GetDataMode (test_frame, m_testLength + m_meshHeaderLength).GetDataRate (); uint32_t payload_nanosec = (uint32_t) ( (double) ((m_testLength + m_meshHeaderLength) * 8 /*octets -> bits*/) * sec2ns / ((double) rate)); uint32_t header_nanosec = (uint32_t) ((double) (m_headerLength * 8 /*octets -> bits*/* sec2ns) diff --git a/src/devices/mesh/dot11s/airtime-metric.h b/src/devices/mesh/dot11s/airtime-metric.h index 6755ac6b5..0f98cc480 100644 --- a/src/devices/mesh/dot11s/airtime-metric.h +++ b/src/devices/mesh/dot11s/airtime-metric.h @@ -45,13 +45,13 @@ public: static TypeId GetTypeId (); uint32_t CalculateMetric (Mac48Address peerAddress, Ptr mac); private: - //\brief Overhead expressed in nanoseconds:DIFS+ 2* SIFS + 2*PREAMBLE + 2* ACK + /// Overhead expressed in nanoseconds:DIFS + SIFS + 2 * PREAMBLE + ACK uint32_t m_overheadNanosec; - ///\brief Bt value + /// Bt value uint32_t m_testLength; - ///\brief header length (used in overhead) + /// header length (used in overhead) uint16_t m_headerLength; - ///\brief meshHeader length (6 octets usually) + /// meshHeader length (minimum 6 octets) uint16_t m_meshHeaderLength; }; } //namespace dot11s diff --git a/src/devices/mesh/dot11s/hwmp-protocol.cc b/src/devices/mesh/dot11s/hwmp-protocol.cc index 9b30de4c6..b333e5801 100644 --- a/src/devices/mesh/dot11s/hwmp-protocol.cc +++ b/src/devices/mesh/dot11s/hwmp-protocol.cc @@ -33,6 +33,7 @@ #include "airtime-metric.h" #include "ie-dot11s-preq.h" #include "ie-dot11s-prep.h" +#include "ns3/trace-source-accessor.h" #include "ie-dot11s-perr.h" NS_LOG_COMPONENT_DEFINE ("HwmpProtocol"); @@ -157,7 +158,13 @@ HwmpProtocol::GetTypeId () MakeBooleanAccessor ( &HwmpProtocol::m_rfFlag), MakeBooleanChecker () - ); + ) + .AddTraceSource ( "RouteDiscoveryTime", + "The time of route discovery procedure", + MakeTraceSourceAccessor ( + &HwmpProtocol::m_routeDiscoveryTimeCallback) + ) + ; return tid; } @@ -201,14 +208,14 @@ void HwmpProtocol::DoDispose () { NS_LOG_FUNCTION_NOARGS (); - for (std::map::iterator i = m_preqTimeouts.begin (); i != m_preqTimeouts.end (); i ++) + for (std::map::iterator i = m_preqTimeouts.begin (); i != m_preqTimeouts.end (); i ++) { - i->second.Cancel (); + i->second.preqTimeout.Cancel (); } m_proactivePreqTimer.Cancel(); m_preqTimeouts.clear (); m_lastDataSeqno.clear (); - m_lastHwmpSeqno.clear (); + m_hwmpSeqnoMetricDatabase.clear (); m_interfaces.clear (); m_rqueue.clear (); m_rtable = 0; @@ -392,31 +399,24 @@ HwmpProtocol::ReceivePreq (IePreq preq, Mac48Address from, uint32_t interface, M { preq.IncrementMetric (metric); //acceptance cretirea: - std::map::const_iterator i = m_lastHwmpSeqno.find (preq.GetOriginatorAddress()); - if (i == m_lastHwmpSeqno.end ()) + std::map >::const_iterator i = m_hwmpSeqnoMetricDatabase.find ( + preq.GetOriginatorAddress ()); + if (i != m_hwmpSeqnoMetricDatabase.end ()) { - m_lastHwmpSeqno[preq.GetOriginatorAddress ()] = preq.GetOriginatorSeqNumber (); - m_lastHwmpMetric[preq.GetOriginatorAddress ()] = preq.GetMetric (); - } - else - { - if ((int32_t)(i->second - preq.GetOriginatorSeqNumber ()) > 0) + if ((int32_t)(i->second.first - preq.GetOriginatorSeqNumber ()) > 0) { return; } - if (i->second == preq.GetOriginatorSeqNumber ()) + if (i->second.first == preq.GetOriginatorSeqNumber ()) { - //find metric - std::map::const_iterator j = m_lastHwmpMetric.find (preq.GetOriginatorAddress()); - NS_ASSERT (j != m_lastHwmpSeqno.end ()); - if (j->second <= preq.GetMetric ()) + if (i->second.second <= preq.GetMetric ()) { return; } } - m_lastHwmpSeqno[preq.GetOriginatorAddress ()] = preq.GetOriginatorSeqNumber (); - m_lastHwmpMetric[preq.GetOriginatorAddress ()] = preq.GetMetric (); } + m_hwmpSeqnoMetricDatabase[preq.GetOriginatorAddress ()] = std::make_pair (preq.GetOriginatorSeqNumber (), preq.GetMetric ()); + NS_LOG_DEBUG("I am " << GetAddress () << "Accepted preq from address" << from << ", preq:" << preq); std::vector > destinations = preq.GetDestinationList (); //Add reactive path to originator: @@ -559,25 +559,16 @@ HwmpProtocol::ReceivePrep (IePrep prep, Mac48Address from, uint32_t interface, M { prep.IncrementMetric (metric); //acceptance cretirea: - std::map::const_iterator i = m_lastHwmpSeqno.find (prep.GetOriginatorAddress ()); - if (i == m_lastHwmpSeqno.end ()) + std::map >::const_iterator i = m_hwmpSeqnoMetricDatabase.find ( + prep.GetOriginatorAddress ()); + if ((i != m_hwmpSeqnoMetricDatabase.end ()) && ((int32_t)(i->second.first - prep.GetOriginatorSeqNumber ()) > 0)) { - m_lastHwmpSeqno[prep.GetOriginatorAddress ()] = prep.GetOriginatorSeqNumber (); + return; } - else - { - if ((int32_t)(i->second - prep.GetOriginatorSeqNumber ()) > 0) - { - return; - } - else - { - m_lastHwmpSeqno[prep.GetOriginatorAddress ()] = prep.GetOriginatorSeqNumber (); - } - } + m_hwmpSeqnoMetricDatabase[prep.GetOriginatorAddress ()] = std::make_pair (prep.GetOriginatorSeqNumber (), prep.GetMetric ()); //update routing info //Now add a path to destination and add precursor to source - NS_LOG_DEBUG("I am " << GetAddress () << ", received prep from " << prep.GetOriginatorAddress () << ", receiver was:" << from); + NS_LOG_DEBUG ("I am " << GetAddress () << ", received prep from " << prep.GetOriginatorAddress () << ", receiver was:" << from); HwmpRtable::LookupResult result = m_rtable->LookupReactive (prep.GetDestinationAddress ()); //Add a reactive path only if it is better than existing: if ( @@ -904,6 +895,12 @@ HwmpProtocol::DequeueFirstPacket () void HwmpProtocol::ReactivePathResolved (Mac48Address dst) { + std::map::iterator i = m_preqTimeouts.find (dst); + if (i != m_preqTimeouts.end ()) + { + m_routeDiscoveryTimeCallback (Simulator::Now () - i->second.whenScheduled); + } + HwmpRtable::LookupResult result = m_rtable->LookupReactive (dst); NS_ASSERT(result.retransmitter != Mac48Address::GetBroadcast ()); //Send all packets stored for this destination @@ -950,12 +947,13 @@ HwmpProtocol::ProactivePathResolved () bool HwmpProtocol::ShouldSendPreq (Mac48Address dst) { - std::map::const_iterator i = m_preqTimeouts.find (dst); + std::map::const_iterator i = m_preqTimeouts.find (dst); if (i == m_preqTimeouts.end ()) { - m_preqTimeouts[dst] = Simulator::Schedule ( + m_preqTimeouts[dst].preqTimeout = Simulator::Schedule ( m_dot11MeshHWMPnetDiameterTraversalTime * Scalar (2), &HwmpProtocol::RetryPathDiscovery, this, dst, 1); + m_preqTimeouts[dst].whenScheduled = Simulator::Now (); return true; } return false; @@ -970,8 +968,8 @@ HwmpProtocol::RetryPathDiscovery (Mac48Address dst, uint8_t numOfRetry) } if (result.retransmitter != Mac48Address::GetBroadcast ()) { - std::map::iterator i = m_preqTimeouts.find (dst); - NS_ASSERT (i != m_preqTimeouts.end ()); + std::map::iterator i = m_preqTimeouts.find (dst); + NS_ASSERT (i != m_preqTimeouts.end ()); m_preqTimeouts.erase (i); return; } @@ -986,8 +984,9 @@ HwmpProtocol::RetryPathDiscovery (Mac48Address dst, uint8_t numOfRetry) packet.reply (false, packet.pkt, packet.src, packet.dst, packet.protocol, HwmpRtable::MAX_METRIC); packet = DequeueFirstPacketByDst (dst); } - std::map::iterator i = m_preqTimeouts.find (dst); + std::map::iterator i = m_preqTimeouts.find (dst); NS_ASSERT (i != m_preqTimeouts.end ()); + m_routeDiscoveryTimeCallback (Simulator::Now () - i->second.whenScheduled); m_preqTimeouts.erase (i); return; } @@ -997,7 +996,7 @@ HwmpProtocol::RetryPathDiscovery (Mac48Address dst, uint8_t numOfRetry) { i->second->RequestDestination (dst, originator_seqno, dst_seqno); } - m_preqTimeouts[dst] = Simulator::Schedule ( + m_preqTimeouts[dst].preqTimeout = Simulator::Schedule ( Scalar (2 * (numOfRetry + 1)) * m_dot11MeshHWMPnetDiameterTraversalTime, &HwmpProtocol::RetryPathDiscovery, this, dst, numOfRetry); } @@ -1112,7 +1111,7 @@ void HwmpProtocol::Statistics::Print (std::ostream & os) const "totalDropped=\"" << totalDropped << "\" " "initiatedPreq=\"" << initiatedPreq << "\" " "initiatedPrep=\"" << initiatedPrep << "\" " - "initiatedPerr=\"" << initiatedPerr << "\"" << std::endl; + "initiatedPerr=\"" << initiatedPerr << "\"/>" << std::endl; } void HwmpProtocol::Report (std::ostream & os) const diff --git a/src/devices/mesh/dot11s/hwmp-protocol.h b/src/devices/mesh/dot11s/hwmp-protocol.h index 98f56f45a..7218178b7 100644 --- a/src/devices/mesh/dot11s/hwmp-protocol.h +++ b/src/devices/mesh/dot11s/hwmp-protocol.h @@ -24,6 +24,7 @@ #include "ns3/mesh-l2-routing-protocol.h" #include "ns3/nstime.h" #include "ns3/event-id.h" +#include "ns3/traced-value.h" #include #include @@ -156,6 +157,8 @@ private: */ bool DropDataFrame (uint32_t seqno, Mac48Address source); //\} + /// Route discovery time: + TracedCallback