
5 Configuração
5.1 Uso do módulo de registro
5.1.1 Visão geral do registro
5.1.2 Habilitação do registro
5.1.3 Adicionando registro ao seu código
5.2 Usando argumentos de linha de comando
5.2.1 Sobrescrevendo valores de atributos padrão
5.2.2 Capturando seus próprios comandos
5.3 Usando o sistema de rastreamento
5.3.1 Rastreamento ASCII
Análise de rastreamento ASCII
5.3.2 Rastreamento PCAP
Capítulo 5
Configuração
5.1 Uso do módulo de registro
Já discutimos brevemente o módulo de registro do ns-3, revisando o script first.cc. Neste capítulo, vamos examinar mais de perto as opções disponíveis na subsistema de registro.
5.1.1 Visão geral do registro
Muitos sistemas grandes suportam algum tipo de ferramenta de registro de mensagens, e o ns-3 não é uma exceção. Em alguns casos, apenas mensagens de erro são gravadas na 'consola do operador' (que normalmente é stderr em sistemas baseados em Unix). Em outros sistemas, mensagens de aviso e informações mais detalhadas também podem ser exibidas. Em algumas situações, as ferramentas de registro são usadas para exibir mensagens de depuração que podem rapidamente 'embaralhar' a saída.
A abordagem utilizada no ns-3 pressupõe que todos esses níveis de informatividade são úteis, e oferecemos uma abordagem seletiva e multilayer para o registro de mensagens. O registro pode ser completamente desativado, ativado para componentes individuais ou de forma global. Para isso, são utilizados níveis de informatividade configuráveis. O módulo de registro ns-3 fornece uma maneira relativamente fácil de obter informações úteis de sua simulação.
Você deve entender que oferecemos um mecanismo genérico - rastreamento - para extrair dados de seus modelos, que deve ser o método preferido para saída ao modelar (para obter mais informações sobre nosso sistema de rastreamento, ver seção do manual 5.3). O registro deve ser o método preferido para obter informações de depuração, avisos, mensagens de erro ou para uma rápida saída de mensagens de seus scripts ou modelos em qualquer momento.
Atualmente, o sistema define sete níveis (tipos) de mensagens de registro em ordem crescente de informatividade.
- LOG_ERROR — registro de mensagens de erro (macros relacionado: NS_LOG_ERROR);
- LOG_WARN — registro de mensagens de aviso (macros relacionado: NS_LOG_WARN);
- LOG_DEBUG — registro de mensagens de depuração relativamente raras e especiais (macros relacionado: NS_LOG_DEBUG);
- LOG_INFO — registro de mensagens informativas sobre o andamento do programa (macros relacionado: NS_LOG_INFO);
- LOG_FUNCTION — registro de mensagens que descrevem cada função chamada (duas macros relacionadas: NS_LOG_FUNCTION, usada para funções membro, e NS_LOG_FUNCTION_NOARGS, usada para funções estáticas);
- LOG_LOGIC — registro de mensagens que descrevem o fluxo lógico dentro da função (macros relacionado: NS_LOG_LOGIC);
- LOG_ALL — registro de tudo o que foi mencionado acima (nenhuma macro relacionada).
Para cada tipo (LOG_TYPE), também existe um tipo LOG_LEVEL_TYPE, que, se utilizado, permite registrar, além de seu próprio nível, todos os níveis acima dele. (Consequentemente, LOG_ERROR e LOG_LEVEL_ERROR, assim como LOG_ALL e LOG_LEVEL_ALL são funcionalmente equivalentes.) Por exemplo, ativar LOG_INFO permitirá somente mensagens fornecidas pela macro NS_LOG_INFO, enquanto LOG_LEVEL_INFO também incluirá mensagens fornecidas pelas macros NS_LOG_DEBUG, NS_LOG_WARN e NS_LOG_ERROR.
Também fornecemos uma macro de registro incondicional, que é exibida sempre, independentemente do nível de registro ou do componente selecionado.
- NS_LOG_UNCOND — registro incondicional da mensagem relacionada (sem nível de registro associado).
Cada nível pode ser solicitado separadamente ou cumulativamente. O registro pode ser configurado usando a variável de ambiente sh NS_LOG ou através da chamada de registro de uma função do sistema. Como mostrado anteriormente, o sistema de registro tem documentação Doxygen e agora é um bom momento para revisá-la, se ainda não o fez.
Agora que você leu a documentação em detalhes, vamos usar esse conhecimento para obter algumas informações interessantes de um exemplo de script scratch/myfirst.cc, que você já compilou.
5.1.2 Habilitação do registro
Vamos usar a variável de ambiente NS_LOG para executar alguns registros adicionais, mas primeiro, apenas para se orientar, execute o último script como fez anteriormente,
$ ./waf --run scratch/myfirstVocê deve ver a saída familiar do primeiro programa de exemplo ns-3
$ Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build'
finalizado com sucesso (0.413s)
Enviados 1024 bytes para 10.1.1.2
Recebidos 1024 bytes de 10.1.1.1
Recebidos 1024 bytes de 10.1.1.2Descobriu-se que as mensagens 'enviadas' e 'recebidas' que você vê acima são, na verdade, mensagens registradas de UdpEchoClientApplication e UdpEchoServerApplication. Por exemplo, podemos pedir ao aplicativo cliente para imprimir informações adicionais, configurando seu nível de registro através da variável de ambiente NS_LOG.
A partir deste ponto, vou assumir que você está usando um shell parecido com sh, que utiliza a sintaxe 'VARIABLE = value'. Se você estiver usando um shell tipo csh, então você precisará converter meus exemplos para a sintaxe 'setenv variável valor' usada por esses shells.
Neste momento, o aplicativo UDP echo client responde à seguinte linha de código em scratch/myfirst.cc,
LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);Isso ativa o nível de registro LOG_LEVEL_INFO. Quando passamos a flag de nível de registro, na verdade estamos ativando esse nível e todos os níveis mais baixos. Neste caso, ativamos NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN e NS_LOG_ERROR. Podemos elevar o nível de registro e obter mais informações sem alterar o script e recompilar, definindo a variável de ambiente NS_LOG da seguinte forma:
$ export NS_LOG=UdpEchoClientApplication=level_allAssim, estamos configurando o seguinte valor para a variável NS_LOG do shell sh,
UdpEchoClientApplication=level_allA parte à esquerda da atribuição é o nome do componente a ser registrado que queremos configurar, e a parte à direita é a flag que queremos aplicar a ele. Neste caso, vamos ativar todos os níveis de depuração no aplicativo. Se você executar o script com o NS_LOG configurado dessa forma, o sistema de registro ns-3 aceitará as alterações e você deve ver a seguinte saída:
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizado com sucesso (0.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Enviados 1024 bytes para 10.1.1.2
Recebidos 1024 bytes de 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Recebidos 1024 bytes de 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()Informações adicionais de depuração fornecidas pelo aplicativo agora estão em conformidade com o nível NS_LOG_FUNCTION. Ele mostra cada ocorrência de chamada de funções durante a execução do script. Geralmente, em funções-métodos, é preferível usar (no mínimo)NS_LOG_FUNCTION (this). Utilize NS_LOG_FUNCTION_NOARGS ()
apenas em funções estáticas. No entanto, observe que no sistema ns-3 não há requisitos para manter qualquer funcionalidade de registro. A decisão sobre quanta informação é registrada fica a critério do desenvolvedor do modelo. No caso de aplicativos de eco, há uma grande quantidade de dados de saída disponíveis para registro.
Agora você pode visualizar o log das chamadas de funções que foram feitas pelo aplicativo. Se você olhar de perto, notará um dois pontos entre a linha UdpEchoClientApplication e o nome do método, no lugar onde você poderia esperar ver o operador de escopo C++ (::). Isso é feito intencionalmente.
Na verdade, isso não é um nome de classe, mas sim um nome de componente de registro. Quando há uma correspondência entre o arquivo de origem e a classe, geralmente é o nome da classe, mas você deve entender que na verdade isso não é um nome de classe e que há um dois pontos em vez de dois dois pontos. Esta é uma maneira de ajudá-lo, de forma relativamente sutil, a conceitualmente separar o nome do componente de registro do nome da classe.
No entanto, em alguns casos, pode ser difícil determinar qual método realmente gera a mensagem de registro. Se você olhar para o texto acima, terá a pergunta de onde veio a linha "Recebido 1024 bytes de 10.1.1.2". Você pode resolver esse problema definindo o nível prefix_func na variável de ambiente NS_LOG. Tente fazer o seguinte,
$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'Observe que as aspas são necessárias, pois o símbolo vertical, que estamos usando para indicar a operação OU, também é um conectador de pipeline do Unix. Agora, se você executar o script, verá que o sistema de registro garante que cada mensagem desse log tenha um prefixo com o nome do componente.
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' concluído com sucesso (0.417s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Enviou 1024 bytes para 10.1.1.2
Recebido 1024 bytes de 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
UdpEchoClientApplication:HandleRead(): Recebeu 1024 bytes de 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()Agora você pode ver que todas as mensagens recebidas do aplicativo UDP echo client são identificadas como tal. A mensagem "Recebido 1024 bytes de 10.1.1.2" agora está claramente definida como oriunda do aplicativo echo client. A mensagem restante deve vir do aplicativo UDP echo server. Podemos incluir este componente, inserindo uma lista de componentes separados por dois pontos na variável de ambiente NS_LOG.
$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func:
UdpEchoServerApplication=level_all|prefix_func'Aviso: no exemplo de texto acima, você precisará remover o caractere de nova linha após os dois pontos (:), ele é usado para formatar o documento. Agora, se você executar o script, verá todas as mensagens de log dos aplicativos echo client e server. Você pode perceber que isso pode ser muito útil durante a depuração.
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' concluído com sucesso (0.406s)
UdpEchoServerApplication:UdpEchoServer()
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoServerApplication:StartApplication()
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Enviou 1024 bytes para 10.1.1.2
UdpEchoServerApplication:HandleRead(): Recebeu 1024 bytes de 10.1.1.1
UdpEchoServerApplication:HandleRead(): Ecoando o pacote
UdpEchoClientApplication:HandleRead(0x624920, 0x625160)
UdpEchoClientApplication:HandleRead(): Recebeu 1024 bytes de 10.1.1.2
UdpEchoServerApplication:StopApplication()
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()Também pode ser útil ver o tempo de simulação em que a mensagem de log foi gerada. Você pode fazer isso adicionando um BIT de prefixo prefix_time:
$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time: UdpEchoServerApplication=level_all|prefix_func|prefix_time'Novamente, você precisará remover o caractere de nova linha acima. Se agora você executar o script, deve ver a seguinte saída:
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' finalizado com sucesso (0,418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Enviou 1024 bytes para 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Recebeu 1024 bytes de 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Ecoando pacote
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Recebeu 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()Note que o construtor para UdpEchoServer foi chamado durante a simulação 0 segundos. Isso na verdade acontece antes do início da simulação, mas este tempo é exibido como zero segundos. O mesmo é verdadeiro para a mensagem do construtor UdpEchoClient.
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' finalizado com sucesso (0,418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Enviou 1024 bytes para 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Recebeu 1024 bytes de 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Ecoando pacote
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Recebeu 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()Vamos lembrar que o script scratch/first.cc iniciou o aplicativo de eco do servidor um segundo antes do início da simulação. Agora você pode ver que o método StartApplication do servidor é efetivamente chamado no primeiro segundo. Você também pode notar que o cliente de eco é iniciado no segundo segundo da simulação, como pedimos no script.
Agora você pode acompanhar o progresso da simulação chamando ScheduleTransmit no cliente, que chama o retorno de chamada Send do aplicativo de eco do servidor. Observe que o tempo decorrido para enviar o pacote através do link de dois pontos é de 3,69 milissegundos. É evidente que o eco-servidor registra a mensagem de que respondeu ao pacote e, em seguida, após a demora do canal, você verá que o eco-cliente recebe o pacote de eco em seu método HandleRead.
Nesta simulação, muita coisa acontece invisivelmente para você. Mas você pode facilmente rastrear todo o processo ativando todos os componentes de registro no sistema. Tente definir o seguinte valor na variável NS_LOG:
$ export 'NS_LOG=*=level_all|prefix_func|prefix_time'O asterisco acima é um caractere curinga do componente de registro. Isso ativará todos os registros em todos os componentes usados na simulação. Não vou reproduzir a saída aqui (na data da redação deste artigo, ela gera 1265 linhas de saída para um único pacote de eco), mas você pode redirecionar essas informações para um arquivo e visualizá-lo em seu editor favorito.
$ . /waf --run scratch/myfirst > log.out 2>&1Pessoalmente, uso esta versão extremamente verbosa do registro de logs quando enfrento um problema e não tenho ideia de onde as coisas deram errado. Consigo acompanhar a execução do código com facilidade, sem precisar definir pontos de interrupção e depurar linha por linha. Basta editar a saída no meu editor favorito e procurar o que espero ver, além de perceber o que não esperava. Quando tenho uma noção geral do que está errado, vou para o depurador para investigar a questão em detalhes. Esse tipo de saída pode ser particularmente útil quando seu script faz algo completamente inesperado. Se você se basear apenas no depurador, pode acabar perdendo uma reviravolta inesperada. Os logs tornam essas reviravoltas evidentes.
5.1.3 Adicionando registro ao seu código
Você pode adicionar novas entradas às suas simulações fazendo chamadas ao componente log de vários macros. Vamos fazer isso no script myfirst.cc, que temos no diretório "limpo". Lembre-se de que definimos neste script o componente de registro:
NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");Você sabe que pode habilitar o registro de todas as mensagens deste componente definindo a variável de ambiente NS_LOG em diferentes níveis. Vamos em frente e adicionar algumas entradas ao script. O macro usado para adicionar mensagens de nível informativo ao log é NS_LOG_INFO. Vamos adicionar uma mensagem (logo antes de começarmos a criar os nós) que diz que o script está na fase de criação de topologia ("Creating Topology"). Isso é feito no seguinte trecho de código,
Abra scratch/myfirst.cc no seu editor favorito e adicione a linha,
NS_LOG_INFO ("Creating Topology");
logo antes das linhas,
NodeContainer nodes;
nodes.Create (2);Agora compile o script usando waf, e limpe a variável NS_LOG para desativar o fluxo de registro que habilitamos anteriormente:
$ . /waf
$ export NS_LOG=
Agora, se você executar o script,
$ . /waf --run scratch/myfirst, não verá a nova mensagem, pois o componente de registro associado (FirstScriptExample) não foi habilitado. Para ver sua mensagem, você precisa habilitar o componente de registro FirstScriptExample com nível não inferior a NS_LOG_INFO. Se você apenas deseja ver esse nível específico de registro, pode habilitá-lo assim,
$ export NS_LOG=FirstScriptExample=infoSe você executar o script agora, verá a nova mensagem "Creating Topology".
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' concluído com sucesso (0.404s)
Criando Topologia
Enviados 1024 bytes para 10.1.1.2
Recebidos 1024 bytes de 10.1.1.1
Recebidos 1024 bytes de 10.1.1.25.2 Usando argumentos de linha de comando
5.2.1 Sobrescrevendo valores de atributos padrão
Outra maneira de alterar o comportamento dos scripts ns-3 sem editar e compilar é usando argumentos da linha de comando. Fornecemos um mecanismo para analisar argumentos da linha de comando e configurar automaticamente variáveis locais e globais com base nos resultados.
O primeiro passo para usar o sistema de argumentos da linha de comando é declarar o analisador sintático da linha de comando. Isso é bastante simples (na sua função principal), como no seguinte código:
int
main (int argc, char *argv[])
{
...
CommandLine cmd;
cmd.Parse (argc, argv);
...
}Esse simples trecho de duas linhas é na verdade muito útil por si só. Ele abre a porta para a variável global ns-3 e o sistema de atributos. Vamos adicionar duas linhas de código ao início da função principal do script. scratch/myfirst.ccEm seguida, compilamos o script e o executamos, fazendo uma solicitação de ajuda da seguinte maneira,
$ ./waf --run "scratch/myfirst --PrintHelp"Este comando pedirá ao Waf para executar o script scratch/myfirst e passar o argumento da linha de comando --PrintHelp. As aspas são necessárias para indicar para qual programa o argumento é destinado. O analisador da linha de comando detectará o argumento --PrintHelp e produzirá a resposta,
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' concluído com sucesso (0.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Handle arg name=PrintHelp value=
--PrintHelp: Imprime esta mensagem de ajuda.
--PrintGroups: Imprime a lista de grupos.
--PrintTypeIds: Imprime todos os TypeIds.
--PrintGroup=[group]: Imprime todos os TypeIds do grupo.
--PrintAttributes=[typeid]: Imprime todos os atributos do typeid.
--PrintGlobals: Imprime a lista de globais.Agora, vamos considerar a opção --PrintAttributes. Já mencionamos o sistema de atributos ns-3 ao estudar o script first.cc. Vimos as seguintes linhas de código,
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));e comentamos que DataRate é na verdade um atributo PointToPointNetDevice.Vamos usar o analisador sintático de argumentos da linha de comando para visualizar os atributos. PointToPointNetDevice.A lista de ajuda diz que devemos fornecer TypeId. Este é o nome da classe à qual pertencem os atributos de interesse. No nosso caso, será ns3::PointToPointNetDevice. Vamos continuar, insira
$ . /waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointNetDevice"O sistema imprimirá todos os atributos desse tipo de dispositivo de rede. Você verá que entre os atributos da lista está
--ns3::PointToPointNetDevice::DataRate=[32768bps]:
A taxa de dados padrão para links ponto a pontoEste é o valor padrão que será utilizado no sistema ao criar um objeto PointToPointNetDevice.. Vamos sobrescrever este valor padrão usando o parâmetro Atributo em PointToPointHelper acima. Vamos usar os valores padrão para dispositivos e canais ponto a ponto. Para isso, removeremos as chamadas SetDeviceAttribute e SetChannelAttribute do myfirst.cc, que temos no diretório limpo.
Seu script agora deve apenas declarar PointToPointHelper e não executar nenhuma operação de configuração, como mostrado no exemplo abaixo,
...
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
...Continue e crie um novo script com Waf (. /waf) e vamos voltar e incluir algum registro do aplicativo de eco UDP do servidor e incluir um prefixo de tempo.
$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'Se você executar o script, deverá ver a seguinte saída:
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizado com sucesso (0.405s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Enviou 1024 bytes para 10.1.1.2
2.25732s Recebeu 1024 bytes de 10.1.1.1
2.25732s Ecoando pacote
Recebeu 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()Lembramos que da última vez que olhamos para o tempo de simulação, o momento em que o pacote foi recebido pelo eco servidor foi em 2,00369 segundos.
2.00369s UdpEchoServerApplication:HandleRead(): Recebeu 1024 bytes de 10.1.1.1Agora ele recebe o pacote em 2.25732 segundos. Isso se deve ao fato de que simplesmente reduzimos a taxa de transferência do PointToPointNetDevice de cinco megabits por segundo para o valor padrão de 32768 bits por segundo. Se tivéssemos definido uma nova DataRate usando a linha de comando, poderíamos acelerar nosso modelo novamente. Faremos isso da seguinte maneira, conforme a fórmula implícita do elemento de ajuda:
$ . /waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"Como resultado, o valor do atributo DataRate por padrão será revertido para cinco megabits por segundo. Você está surpreso com o resultado? Acontece que, para retornar ao comportamento original do script, também precisamos definir a latência do canal correspondente à velocidade da luz. Podemos solicitar que o sistema de linha de comando imprima os atributos do canal, assim como fizemos para o dispositivo de rede:
$ .\/waf --run "scratch\/myfirst --PrintAttributes=ns3::PointToPointChannel"Descobriremos que o atributo de latência do canal está configurado da seguinte forma:
--ns3::PointToPointChannel::Delay=[0ns]:
Atraso de transmissão através do canalEm seguida, podemos definir esses dois valores padrão pelo sistema de linha de comando,
$ .\/waf --run "scratch\/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms"neste caso, estamos restaurando o tempo que tínhamos quando definimos explicitamente DataRate e Delay no script:
Waf: Entrando no diretório `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
Waf: Saindo do diretório `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'build' concluído com sucesso (0.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Enviados 1024 bytes para 10.1.1.2
2.00369s Recebido 1024 bytes de 10.1.1.1
2.00369s Ecoando pacote
Recebido 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()Note que o pacote foi recebido novamente pelo servidor em 2,00369 segundos. Poderíamos, na verdade, configurar qualquer um dos atributos utilizados no script desta forma. Em particular, poderíamos definir valores diferentes de um para os atributos MaxPackets UdpEchoClient.
Como você usaria isso? Experimente. Lembre-se de que você deve comentar o local onde estamos sobrescrevendo o valor do atributo padrão e defini-lo explicitamente MaxPackets no script. Em seguida, você deve recompilar o script. Você também pode obter ajuda sobre a sintaxe para definir um novo valor padrão do atributo via linha de comando. Uma vez que você tenha dominado isso, poderá controlar a quantidade de pacotes exibidos na linha de comando. Como somos pessoas dedicadas, nossa linha de comando deve parecer mais ou menos assim:
$ .\/waf --run "scratch\/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms
--ns3::UdpEchoClient::MaxPackets=2"A pergunta natural que surge neste ponto é como saber sobre a existência de todos esses atributos. Novamente, o sistema de linha de comando possui uma função de ajuda para isso. Se pedirmos ajuda ao sistema de linha de comando, devemos ver:
$ ./waf --run "scratch/myfirst --PrintHelp"
myfirst [Program Arguments] [General Arguments]
General Arguments:
--PrintGlobals: Imprima a lista de globais.
--PrintGroups: Imprima a lista de grupos.
--PrintGroup=[group]: Imprima todos os TypeIds do grupo.
--PrintTypeIds: Imprima todos os TypeIds.
--PrintAttributes=[typeid]: Imprima todos os atributos do typeid.
--PrintHelp: Imprima esta mensagem de ajuda.Se você escolher o argumento "PrintGroups", deverá ver uma lista de todos os grupos registrados. TypeId. Os nomes dos grupos correspondem aos nomes dos módulos no diretório de origem (embora com letra maiúscula). Imprimir todas as informações de uma vez será muito volumoso, por isso existe um filtro adicional para imprimir informações por grupos. Assim, novamente focando no módulo "ponto a ponto":
./waf --run "scratch/myfirst --PrintGroup=PointToPoint"
TypeIds no grupo PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeaderAqui você pode encontrar os nomes disponíveis de TypeId para buscar atributos, por exemplo, em
--PrintAttributes = ns3::PointToPointChannel, conforme mostrado acima.
Outra maneira de descobrir sobre os atributos é através do Doxygen ns-3. Há uma página que lista todos os atributos registrados no simulador.
5.2.2 Capturando seus próprios comandos
Você também pode adicionar seus próprios hooks por meio do sistema de linha de comando. Isso é feito de forma bastante simples com o método do parser de linha de comando. AddValue.
Vamos usar essa oportunidade para especificar o número de pacotes que devem ser exibidos de uma maneira completamente diferente. Vamos adicionar uma variável local chamada nPackets na função main. Vamos definir seu valor como um para corresponder ao nosso comportamento padrão anterior. Para permitir que o parser de linha de comando altere esse valor, precisamos capturar esse valor no parser. Fazemos isso adicionando uma chamada AddValue. Vá e altere o script scratch/myfirst.cc para que comece com o seguinte código,
int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Número de pacotes a serem ecoados", nPackets);
cmd.Parse (argc, argv);
...Role para baixo até o ponto no script onde definimos o atributo MaxPackets e altere-o para que ele seja configurado para a variável nPackets em vez da constante 1, como mostrado abaixo.
echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));Agora, se você executar o script e passar o argumento --PrintHelp, deverá ver um novo argumento do usuário listado na tela de ajuda. Digite,
$ ./waf --run "scratch/myfirst --PrintHelp"
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'A construção' foi concluída com sucesso (0.403s)
--PrintHelp: Mostra esta mensagem de ajuda.
--PrintGroups: Mostra a lista de grupos.
--PrintTypeIds: Mostra todos os TypeIds.
--PrintGroup=[grupo]: Mostra todos os TypeIds do grupo.
--PrintAttributes=[typeid]: Mostra todos os atributos de typeid.
--PrintGlobals: Mostra a lista de globais.
Argumentos do Usuário:
--nPackets: Número de pacotes a serem ecoadosSe você deseja alterar a quantidade de pacotes transmitidos, pode fazer isso definindo o argumento -nPackets na linha de comando.
$ ./waf --run "scratch/myfirst --nPackets=2"Agora você deve ver
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'A construção' foi concluída com sucesso (0.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Enviado 1024 bytes para 10.1.1.2
2.25732s Recebido 1024 bytes de 10.1.1.1
2.25732s Ecoando pacote
Recebido 1024 bytes de 10.1.1.2
Enviado 1024 bytes para 10.1.1.2
3.25732s Recebido 1024 bytes de 10.1.1.1
3.25732s Ecoando pacote
Recebido 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()Você enviou agora dois pacotes. Bastante simples, não é?
Você pode ver que como usuário do ns-3, pode usar o sistema de argumentos da linha de comando para gerenciar valores globais e atributos. Se você é o autor do modelo, pode adicionar novos atributos a seus objetos, e estes estarão automaticamente disponíveis para configuração por seus usuários através do sistema de linha de comando. Se você é o autor do script, pode adicionar novas variáveis aos seus scripts e conectá-las facilmente ao sistema de linha de comando.
5.3 Usando o sistema de rastreamento
Todo o propósito da modelagem é gerar saídas para estudo posterior, e o sistema de rastreamento do ns-3 é o principal mecanismo para isso. Como o ns-3 é um programa em C++, podem ser utilizados os meios padrão de geração de saídas de programas em C++:
#include <iostream>
...
int main ()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}Você até pode usar o módulo de registro para adicionar um pouco de estrutura à sua solução. Sabemos que muitas questões podem surgir com essa abordagem, e para resolver esses problemas, fornecemos um subsistema de rastreamento de eventos.
Os principais objetivos do sistema de rastreamento do ns-3:
Para tarefas básicas, o sistema de rastreamento deve permitir ao usuário gerar rastreamento padrão para fontes populares e escolher os objetos que geram rastreamento;
Os usuários intermediários devem ser capazes de expandir o sistema de rastreamento para alterar o formato de saída gerado ou para inserir novas fontes de rastreamento, sem modificar o núcleo do simulador;
Usuários experientes podem modificar o núcleo do simulador para adicionar novas fontes e receptores de rastreamento. O sistema de rastreamento ns-3 é construído sobre os princípios de fontes e receptores de rastreamento independentes, além de um mecanismo unificado para conectar as fontes aos consumidores.
O sistema de rastreamento ns-3 é construído sobre os princípios de fontes e receptores de rastreamento independentes, além de um mecanismo unificado para conectar fontes a receptores. Fontes de rastreamento são objetos que podem sinalizar eventos que ocorrem na simulação e fornecer acesso a dados básicos de interesse. Por exemplo, uma fonte de rastreamento pode indicar quando um dispositivo de rede recebeu um pacote e fornecer acesso ao conteúdo do pacote para receptores de rastreamento interessados.
As fontes de rastreamento são inúteis por si mesmas se não estiverem "conectadas" a outras partes do código que realmente utilizam as informações fornecidas pelo receptor de forma útil. Os rastreadores são consumidores de eventos e dados fornecidos pelas fontes de rastreamento. Por exemplo, pode-se criar um receptor de rastreamento que irá (ao se conectar à fonte de rastreamento do exemplo anterior) imprimir partes interessantes do pacote recebido.
Uma justificativa razoável para essa separação explícita é permitir que os usuários conectem novos tipos de receptores a fontes de rastreamento existentes sem a necessidade de editar e recompilar o núcleo do simulador. Assim, no exemplo acima, o usuário pode definir um novo rastreador em seu script e conectá-lo a uma fonte de rastreamento existente definida no núcleo da simulação, apenas editando o script do usuário.
Neste guia, vamos percorrer algumas fontes e receptores predefinidos e mostrar como configurá-los com o mínimo de esforço por parte do usuário. Consulte o Guia ns‑3 ou as seções de instruções para informações sobre configuração avançada de rastreamento, incluindo a extensão do espaço de nomes de rastreamento e a criação de novas fontes de rastreamento.
5.3.1 Rastreamento ASCII
O ns‑3 fornece funcionalidade auxiliar que oferece um sistema de rastreamento de baixo nível para ajudar você com os detalhes ao configurar rastreios de pacotes simples. Se você ativar essa função, verá a saída em arquivos ASCII. Para aqueles que estão familiarizados com a saída do ns-2, esse tipo de rastreamento é semelhante a out.tr, que é gerado por vários scripts.
Vamos ao que interessa e adicionar alguns resultados de rastreamento ASCII ao nosso script scratch/myfirst.cc. Logo antes da chamada Simulator::Run(), adicione as seguintes linhas de código:
AsciiTraceHelper ascii;
pointToPoint.EnableAsciiAll(ascii.CreateFileStream("myfirst.tr"));Como em muitas outras idiomáticas do ns‑3, esse código utiliza um objeto auxiliar para criar rastreamentos ASCII. A segunda linha contém duas chamadas de método aninhadas. O método 'interno' CreateFileStream() usa a idiomática de objeto anônimo para criar um objeto de fluxo de arquivo na pilha (sem nome de objeto) e o passa para o método chamado. No futuro, iremos nos aprofundar nesse assunto, mas tudo o que você precisa saber neste momento é que está criando um objeto representando um arquivo chamado myfirst.tr e o passando para o ns‑3. Nós confiamos ao ns‑3 o cuidado com o objeto criado durante toda a sua existência, durante a qual as questões causadas pela pouco conhecida (intencional) limitação relacionada a construtores de cópia de objetos de fluxo C++ são resolvidas.
A chamada externa EnableAsciiAll() informa ao auxiliar que você deseja incluir na sua simulação rastreamento ASCII para todas as conexões de dispositivo ponto-a-ponto e que você quer que os receptores de rastreamento (especificados) registrem informações sobre o movimento dos pacotes no formato ASCII.
Para aqueles que estão familiarizados com ns-2, os eventos rastreados são equivalentes aos conhecidos pontos de rastreamento que registram eventos '+' , '-' , 'd' e 'r'.
Agora você pode compilar o script e executá-lo na linha de comando:
$ ./waf --run scratch/myfirstQuantas vezes antes você verá várias mensagens do Waf e, em seguida, "'build' finished successfully" (construção concluída com sucesso) com algumas mensagens de um programa em execução.
Durante a execução, o programa criará um arquivo chamado myfirst.tr. Devido às características de operação Waf, por padrão o arquivo é criado não no diretório local, mas no diretório de nível superior do repositório. Se você deseja alterar o caminho onde as trilhas são salvas, pode usar o parâmetro --cwd. Se não fizemos isso, para visualizar o arquivo de rastreamento ASCII myfirst.tr em seu editor favorito, teremos que ir para o diretório de nível superior do nosso repositório.
Análise de rastreamento ASCII
Há muitas informações em uma forma bastante densa, mas a primeira coisa a se notar é que o arquivo consiste em linhas separadas. Isso se tornará mais evidente se você expandir a janela de visualização.
Cada linha do arquivo corresponde a um evento de rastreamento. Neste caso, estamos rastreando eventos na fila de transmissão, presente em cada dispositivo ponto a ponto na simulação. A fila de transmissão é a fila pela qual cada pacote deve passar para o canal ponto a ponto. Note que cada linha no arquivo de rastreamento começa com um único símbolo (e tem um espaço após ele). Este símbolo terá o seguinte significado:
+: ocorreu uma operação de enfileiramento na fila do dispositivo;
-: ocorreu uma operação de extração na fila do dispositivo;
d: o pacote foi descartado, geralmente porque a fila está cheia;
r: o pacote foi recebido pelo dispositivo de rede.
Vamos analisar mais detalhadamente a primeira linha do arquivo de rastreamento. Vou dividi-la em partes (com recuos para clareza) e numerar as linhas à esquerda:
0 +
1 2
2 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
3 ns3::PppHeader (
4 Protocolo Ponto a Ponto: IP (0x0021))
6 ns3::Ipv4Header (
7 tos 0x0 ttl 64 id 0 protocolo 17 offset 0 flags [nenhum]
8 comprimento: 1052 10.1.1.1 > 10.1.1.2)
9 ns3::UdpHeader (
10 comprimento: 1032 49153 > 9)
11 Payload (tamanho=1024)A primeira parte deste evento de rastreamento expandido (linha 0) é a operação. Temos aqui o símbolo +, que corresponde a uma operação de enfileiramento para transmissão. A segunda parte (linha 1) é o tempo de simulação, expresso em segundos. Você pode se lembrar de que pedimos UdpEchoClientApplication começar a enviar pacotes em dois segundos. Aqui vemos a confirmação de que isso realmente está acontecendo.
Na próxima seção do exemplo de rastreamento (a partir da linha 2), é mostrado qual origem de rastreamento gerou esse evento (a namespace de rastreamento é especificada). Você pode imaginar o namespace de rastreamento da mesma forma que o namespace do sistema de arquivos. A raiz do namespace é NodeList. Isso corresponde ao contêiner gerenciado principalmente no código do ns-3. Contém todos os nós que são criados no script. Assim como um sistema de arquivos pode ter diretórios na raiz, em NodeList nós podemos ter múltiplos nós. Assim, a linha /NodeList/0 refere-se ao nó zero no NodeList, que normalmente entendemos como "nó 0". Cada nó possui uma lista de dispositivos que foram instalados. Esta lista está localizada a seguir no namespace. Você pode ver que este evento de rastreamento se origina de DeviceList/0, que é o dispositivo zero instalado no nó.
A próxima substring, $ ns3::PointToPointNetDevice, informa qual dispositivo está na posição zero: a lista de dispositivos do nó zero. Lembre-se de que a operação +, localizada na linha 0, significava que houve uma adição de item na fila de transmissão do dispositivo. Isso é refletido nos últimos segmentos "do caminho do rastreamento": TxQueue/Enqueue.
As demais seções do rastreamento devem ser bastante intuitivas. As linhas 3-4 indicam que o pacote está encapsulado em um protocolo ponto a ponto. As linhas 5-7 mostram que o pacote tem um cabeçalho versão IP4 e originou-se no endereço IP 10.1.1.1 e se destina a 10.1.1.2. As linhas 8-9 mostram que este pacote contém um cabeçalho UDP e, finalmente, a linha 10 mostra que a carga útil - é esperada como 1024 bytes.
A próxima linha no arquivo de rastreamento mostra que o mesmo pacote foi extraído da fila de transmissão no mesmo nó.
A terceira linha no arquivo de rastreamento mostra que o pacote foi recebido pelo dispositivo de rede no nó com o servidor de eco. Eu reproduzi esse evento abaixo.
0 r
1 2.25732
2 /NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx
3 ns3::Ipv4Header (
4 tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
5 length: 1052 10.1.1.1 > 10.1.1.2)
6 ns3::UdpHeader (
7 length: 1032 49153 > 9)
8 Payload (size=1024)Observe que a operação de rastreamento agora é r, e o tempo de simulação aumentou para 2,25732 segundos. Se você seguiu as instruções do tutorial cuidadosamente, isso significa que você deixou a Taxa de Dados dos dispositivos de rede e a latência do canal em seus valores padrão. Esse tempo deve ser familiar, pois você já o viu na seção anterior.
A gravação do espaço de nomes da fonte de rastreamento (linha 2) foi alterada para refletir que este evento se origina do nó 1 (/NodeList/1) и пакет принят источником трассировки (/MacRx). Deve ser bastante fácil acompanhar o movimento do pacote pela topologia, analisando as informações restantes no arquivo de rastreamento.
5.3.2 Rastreamento PCAP
Os assistentes de dispositivos ns-3 também podem ser usados para gerar arquivos de rastreamento no formato .pcap. O acrônimo pcap geralmente escrito em letras minúsculas, refere-se à captura de pacotes e, na verdade, é uma API que inclui a definição do formato do arquivo .pcap. O programa mais popular que pode ler e exibir esse formato é Wireshark (anteriormente chamado de Ethereal). No entanto, existem muitos analisadores de rastreio de tráfego que utilizam esse formato de pacote. Recomendamos que os usuários utilizem várias ferramentas disponíveis para analisar rastreamentos pcap. Neste guia, nós nos concentraremos na visualização de rastreamentos pcap utilizando tcpdump.
A ativação do rastreamento pcap é feita com uma única linha de código.
pointToPoint.EnablePcapAll ("myfirst");Insira esta linha de código após o código de rastreamento ASCII que acabamos de adicionar em scratch/myfirst.cc. Observe que passamos apenas a string “myfirst”, e não “myfirst.pcap” ou algo semelhante. Isso ocorre porque o parâmetro é um prefixo, não o nome completo do arquivo. Durante a simulação, o assistente irá realmente criar um arquivo de rastreamento para cada dispositivo ponto a ponto. Os nomes dos arquivos serão construídos utilizando o prefixo, o número do nó, o número do dispositivo e o sufixo “.pcap».
Para o nosso exemplo de script, acabaremos vendo arquivos chamados “myfirst-0-0.pcap” e “myfirst-1-0.pcap”, que são rastreamentos pcap para o nó 0-dispositivo 0 e o nó 1-dispositivo 0, respectivamente. Depois de adicionar a linha de código para ativar o rastreamento pcap, você pode executar o script normalmente:
$ ./waf --run scratch/myfirstSe você olhar no diretório de nível superior da sua distribuição, deverá ver três arquivos: o arquivo de rastreamento ASCII myfirst.tr, que analisamos anteriormente, os arquivos myfirst-0-0.pcap e myfirst-1-0.pcap — novos arquivos pcap que acabamos de gerar.
Lendo a saída com tcpdump
Atualmente, para visualizar arquivos pcap, a forma mais simples é usar tcpdump.
$ tcpdump -nn -tt -r myfirst-0-0.pcap
lendo do arquivo myfirst-0-0.pcap, tipo de link PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, comprimento 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, comprimento 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
lendo do arquivo myfirst-1-0.pcap, tipo de link PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, comprimento 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, comprimento 1024No dump myfirst-0-0.pcap ( dispositivo cliente ) você pode ver que o pacote de eco é enviado após 2 segundos de simulação. Se você olhar para o segundo dump (myfirst-1-0.pcap), você verá que o pacote é recebido no momento 2,257324 segundos. Você verá no segundo dump que o pacote retorna no momento 2,257324 segundos e, finalmente, que o pacote foi recebido pelo cliente de volta no primeiro dump no momento 2,514648 segundos.
Lendo a saída com Wireshark
Se você não está familiarizado com Wireshark, há um site de onde você pode baixar os programas e a documentação: . Wireshark — é uma interface gráfica do usuário que pode ser usada para exibir esses arquivos de rastreamento. Se você tem o Wireshark, você pode abrir qualquer um dos arquivos de rastreamento e exibir o conteúdo como se você tivesse capturado pacotes usando um analisador de pacotes.
Fonte: habr.com
