Compartilhar via


WinDbg - Linhas de tempo

Logotipo WinDbg com lupa inspecionando bits.

A TTD (Depuração de Viagem no Tempo) permite que os usuários gravem rastreamentos, que são gravações da execução de um programa. As linhas do tempo são uma representação visual dos eventos que ocorrem durante a execução. Esses eventos podem ser locais de: pontos de interrupção, leitura/gravação de memória, chamadas e retornos de função e exceções.

Linha do tempo no depurador mostrando exceções, acesso à memória, pontos de interrupção e chamadas de função.

Use a janela de linhas do tempo para ver rapidamente eventos importantes, entender a posição relativa e pular facilmente para o local deles no arquivo de rastreamento TTD. Use várias linhas do tempo para explorar visualmente eventos no rastreamento de viagem no tempo e conhecer a correlação de eventos.

A janela de Linhas do tempo é exibida ao abrir um arquivo de rastreamento TTD e mostra eventos importantes sem que você precise criar manualmente consultas de modelo de dados. Ao mesmo tempo, todos os objetos de viagem no tempo ficam disponíveis para permitir consultas de dados mais complexas.

Para mais informações sobre como criar e trabalhar com arquivos de rastreamento de viagem no tempo, consulte Depuração de viagem no tempo - Visão geral.

Tipos de linhas de tempo

A janela Linhas do tempo pode exibir os seguintes eventos:

  • Exceções (você pode filtrar em um código de exceção específico)
  • Pontos de interrupção
  • Chamadas de função (busca na forma de module!function)
  • Acessos a memória (leitura/gravação/execução entre dois endereços de memória)

Passe o mouse sobre cada evento para ver mais informações por meio da dica de ferramenta. Clicar em um evento irá executar a consulta para o evento e exibir mais informações. Clicar duas vezes em um evento irá saltar até esse local no arquivo de rastreamento TTD.

Exceções

Quando você carrega um arquivo de rastreamento e a linha do tempo está ativa, ele exibe todas as exceções na gravação automaticamente.

Quando você passa o mouse sobre um ponto de interrupção, informações como o tipo de exceção e o código de exceção aparecem.

Linha do tempo no depurador mostrando exceções com informações sobre um código de exceção específico.

Você pode filtrar ainda mais um código de exceção específico com o campo de código de exceção opcional.

Caixa de diálogo de exceção do depurador de Linha do tempo com tipo de linha do tempo definido como exceção e código de exceção definido como 0xC0000004.

Você também pode adicionar outra linha do tempo para um tipo de exceção específico.

Pontos de interrupção

Depois de adicionar um ponto de interrupção, você pode exibir as posições de quando esse ponto de interrupção é atingido na linha do tempo. Isso pode ser feito, por exemplo, usando o comando bp Set Breakpoint. Quando você passa o mouse em um ponto de interrupção, o endereço e o ponteiro de instrução associado ao ponto de interrupção aparecem.

Linha do tempo no depurador exibindo cerca de 30 indicadores de ponto de interrupção.

Quando o ponto de interrupção é limpo, a linha do tempo do ponto de interrupção relacionado é removida automaticamente.

Chamadas de função

Você pode ver as posições das chamadas de função na linha do tempo. Para isso, forneça a pesquisa na forma de module!function, por exemplo TimelineTestCode!multiplyTwo. Você também pode especificar os curingas, por exemplo TimelineTestCode!m*.

Adicionar uma linha do tempo no depurador com o nome da chamada de função inserido.

Quando você passa o mouse em uma chamada de função, o nome da função, os parâmetros de entrada, seus valores e o valor de retorno são exibidos. Este exemplo mostra buffer e size, já que esses são os parâmetros para DisplayGreeting!GetCppConGreeting.

Linha do tempo no depurador mostrando as chamadas de função e janela de registros.

Acesso à memória

Use a linha do tempo de acesso à memória para mostrar quando um intervalo específico de memória foi lido ou gravado, ou onde ocorreu a execução do código. É usado um endereço inicial e de parada para definir um intervalo entre dois endereços de memória.

Adicionar caixa de diálogo de acesso à memória da linha do tempo com o botão

Quando você passa o mouse em um item de acesso à memória, o valor e o ponteiro de instrução aparecem.

Linha do tempo no depurador mostrando eventos de acesso à memória.

Trabalhar com linhas do tempo

Uma linha cinza vertical segue o cursor ao passar o mouse na linha do tempo. A linha azul vertical mostra a posição atual no rastreamento.

Clique nos ícones de lupa para aumentar e diminuir a linha do tempo.

Na área superior de controle da linha do tempo, use o retângulo para deslocar a exibição da linha do tempo. Arraste os delimitadores externos do retângulo para redimensionar a exibição atual da linha do tempo.

Linha do tempo no depurador exibindo a área superior usada para selecionar o visor ativo.

Movimentos do mouse

Aumentar e diminuir o zoom usando Ctrl + Roda de rolagem.

Panorâmica de um lado a o outro usando a Shift + Roda de rolagem.

Técnicas de depuração da linha do tempo

Para demonstrar técnicas de linha do tempo de depuração, o Passo a passo da depuração da viagem do tempo é reutilizado aqui. Esta demonstração presume que você tenha concluído as duas primeiras etapas para criar o código de exemplo e criado a gravação TTD com os dois primeiros passos descritos.

Seção 1: Criar o código de exemplo

Seção 2: Gravar um rastreamento da exemplo "DisplayGreeting"

Nesse caso, o primeiro passo é encontrar a exceção no rastreamento de viagem no tempo. Isso pode ser feito clicando duas vezes na única exceção na linha do tempo.

Olhando na janela de comando, vemos que o comando seguinte foi emitido quando clicamos na exceção.

(2dcc.6600): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: CC:0
@$curprocess.TTD.Events.Where(t => t.Type == "Exception")[0x0].Position.SeekTo()

Selecione Exibir>>Registros para ver os registros neste ponto da linha do tempo para iniciar nossa investigação.

Linha do tempo no depurador mostrando a exceção demolab e a janela de registros.

Na saída do comando, observe que a pilha (esp) e o ponteiro base (ebp) apontam para dois endereços bem diferentes. Isso pode indicar a corrupção da pilha; possivelmente uma função retornou e corrompeu a pilha. Para validar isso, precisamos viajar de volta para antes de o estado da CPU ser corrompido e ver se podemos determinar quando a corrupção da pilha ocorreu.

Com isso, examinaremos os valores das variáveis locais e a pilha.

Selecione Exibir>>Locais para ver os valores locais.

Selecione Exibir>>Pilha para exibir a pilha de execução de código.

No ponto de falha no rastreamento, é comum terminar alguns passos depois da verdadeira causa no código de tratamento de erros. Com a viagem no tempo, podemos voltar uma instrução de cada vez para localizar a verdadeira causa raiz.

Na faixa de opções Página inicial, use o comando Voltar para recuar três instruções. Ao fazer isso, continue examinando a pilha, os locais e as janelas de registro.

A janela de comando mostrará a posição de viagem no tempo e os registros à medida que você recua três instruções.

0:000> t-
Time Travel Position: CB:41
eax=00000000 ebx=00564000 ecx=c0d21d62 edx=7a1e4a6c esi=00061299 edi=00061299
eip=00540020 esp=003cf7d0 ebp=00520055 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
00540020 ??              ???
0:000> t-
Time Travel Position: CB:40
eax=00000000 ebx=00564000 ecx=c0d21d62 edx=7a1e4a6c esi=00061299 edi=00061299
eip=00061767 esp=003cf7cc ebp=00520055 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
DisplayGreeting!main+0x57:
00061767 c3              ret
0:000> t-
Time Travel Position: CB:3A
eax=0000004c ebx=00564000 ecx=c0d21d62 edx=7a1e4a6c esi=00061299 edi=00061299
eip=0006175f esp=003cf718 ebp=003cf7c8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
DisplayGreeting!main+0x4f:
0006175f 33c0            xor     eax,eax

Neste ponto do rastreamento, nossa pilha e ponteiro de base têm valores que fazem mais sentido, então parece que estamos nos chegando ao ponto no código onde a corrupção ocorreu.

esp=003cf718 ebp=003cf7c8

Também é interessante que a janela Locais contém valores do nosso aplicativo de destino e a janela Código-fonte está destacando a linha de código que está pronta para ser executada em nosso código-fonte neste ponto do rastreamento.

Para investigar melhor, podemos abrir uma janela de memória para ver o conteúdo perto do endereço de memória do ponteiro da pilha (esp). Neste exemplo, ele tem um valor de 003cf7c8. Selecione Memória>>Texto>>ASCII para exibir o texto ASCII armazenado nesse endereço.

Depurador mostrando registros, pilha e janelas de memória.

Linha do tempo de acesso à memória

Depois que um local de memória de interesse tiver sido identificado, adicione uma linha do tempo de acesso à memória com esse valor. Clique em + Adicionar linha do tempo e preencha o endereço inicial. Vamos ver 4 bytes, então adicionando isso ao endereço inicial de 003cf7c8, temos 003cf7cb. O padrão é examinar todas as gravações de memória, mas você também pode examinar somente gravações ou execução de código nesse endereço.

Adicionar uma caixa de diálogo de acesso à memória da linha do tempo com o botão de gravação marcado e um valor inicial de 003cf7c8.

Agora podemos percorrer a linha do tempo na direção reversa para examinar em que ponto deste rastreamento de viagem no tempo esse local de memória foi gravado para ver o que podemos encontrar. Ao clicar nessa posição na linha do tempo, vemos que os locais valorizam valores diferentes para a cadeia de caracteres que está sendo copiada. O valor de destino parece não estar completo, como se o comprimento da nossa cadeia de caracteres estivesse incorreto.

Linha do tempo de acesso à memória e janela de locais mostrando valores locais com valores de origem e destino diferentes.

Linha do tempo do ponto de interrupção

O uso de pontos de interrupção é comum para pausar a execução de código em algum evento de interesse. O TTD lhe permite definir um ponto de interrupção e viajar no tempo até que esse ponto de interrupção seja atingido depois da gravação do rastreamento. A capacidade de examinar o estado do processo depois de um problema, para determinar o melhor local para um ponto de interrupção, permite fluxos de trabalho de depuração adicionais exclusivos do TTD.

Para explorar uma técnica alternativa de depuração de linha do tempo, clique na exceção na linha do tempo e, novamente, recue três passos usando o comando Voltar na faixa de opções Início.

Neste exemplo bem pequeno, seria muito fácil só ver o código, mas se houver centenas de linhas de código e dezenas de sub-rotinas, as técnicas descritas aqui podem ser usadas para reduzir o tempo necessário para localizar o problema.

Como já mencionamos, o ponteiro base (esp), em vez de apontar para uma instrução, está apontando ao texto da nossa mensagem.

Use o comando ba para definir um ponto de interrupção no acesso à memória. Vamos definir um ponto de interrupção w - gravação para ver quando esta área da memória é gravada.

0:000> ba w4 003cf7c8

Embora iremos usar um ponto de interrupção de acesso à memória simples, os pontos de interrupção podem ser construídos para serem instruções condicionais mais complexas. ara obter mais informações, consulte bp, bu, bm (Definir pontos de interrupção).

No menu Início, selecione Voltar para viajar no tempo até chegar ao ponto de interrupção.

Neste ponto, podemos ver a pilha de programas para ver qual código está ativo.

Linha do tempo no depurador mostrando a linha do tempo de acesso à memória e as janelas de pilha.

Como é improvável que a função wscpy_s() fornecida pela Microsoft tenha um bug de código como este, procuramos mais adiante na pilha. A pilha mostra que Greeting!main chama Greeting!GetCppConGreeting. Em nosso exemplo de código bem pequeno, poderíamos simplesmente abrir o código neste ponto e provavelmente ver o erro com bastante facilidade. Porém, para ilustrar as técnicas que podem ser usadas com programas maiores e mais complexos, vamos adicionar uma linha do tempo de chamada de função.

Linha do tempo da chamada de função

Clique em + Adicionar linha do tempo e preencha a DisplayGreeting!GetCppConGreeting para a cade de caracteres de pesquisa da função.

As caixas de seleção Local inicial e Final indicam o início e o fim de uma chamada de função no rastreamento.

Podemos usar o comando dx para mostrar o objeto de chamada de função para ver os campos TimeStart e TimeEnd associados, que correspondem a Local Inicial e Local Final da chamada de função.

dx @$cursession.TTD.Calls("DisplayGreeting!GetCppConGreeting")[0x0]
    EventType        : 0x0
    ThreadId         : 0x6600
    UniqueThreadId   : 0x2
    TimeStart        : 6D:BD [Time Travel]
    SystemTimeStart  : Thursday, October 31, 2019 23:36:05
    TimeEnd          : 6D:742 [Time Travel]
    SystemTimeEnd    : Thursday, October 31, 2019 23:36:05
    Function         : DisplayGreeting!GetCppConGreeting
    FunctionAddress  : 0x615a0
    ReturnAddress    : 0x61746
    Parameters  

As caixas Início ou Fim, ou as caixas Local inicial e Final, devem ser marcadas.

Adicionar nova caixa de diálogo Linha do tempo mostrando a adição da linha do tempo de chamada de função com uma cade de caracteres de pesquisa de função de DisplayGreeting!GetCppConGreeting.

Como nosso código não é recursivo nem reentrante, é bem fácil localizar na linha do tempo quando o método GetCppConGreeting é chamado. A chamada a GetCppConGreeting também ocorre ao mesmo tempo que nosso ponto de interrupção, bem como o evento de acesso à memória que definimos. Portanto, parece que reduzimos uma área de código para examinar com cuidado a causa raiz de nossa falha de aplicativo.

Linha do tempo no depurador mostrando a linha do tempo de acesso à memória e janela locais com mensagem e buffer com os valores de cadeia de caracteres diferentes.

Explorar a execução de código exibindo várias linhas do tempo

Embora nosso exemplo de código seja pequeno, a técnica de usar várias linhas do tempo permite explorar visualmente um rastreamento de viagem no tempo. Você pode examinar o arquivo de rastreamento para fazer perguntas do tipo "quando uma área da memória é acessada antes que um ponto de interrupção seja atingido?".

Linha do tempo no depurador mostrando a linha do tempo de acesso à memória e as janelas locais.

A capacidade de ver correlações adicionais e encontrar coisas que você não esperava diferencia a ferramenta de linha do tempo da interação com o rastreamento da viagem no tempo com a linha de comando

Marcadores da linha do tempo

Marque as posições importantes de viagem no tempo no WinDbg em vez de copiar manualmente colando a posição no bloco de notas. Os marcadores facilitam visualizar rapidamente as diversas posições no rastreamento em relação a outros eventos e anotá-las.

Você pode dar um nome descritivo para marcadores.

Nova caixa de diálogo de indicador mostrando o nome de exemplo para a primeira chamada de API no aplicativo Exibir saudação.

Acesse Favoritos na da janela Linha do Tempo disponível em Exibir > Linha do Tempo. Quando você passa o mouse em um marcador, ele mostra o nome do marcador.

Linha do tempo com três marcadores com o cursor pairando sobre um, revelando o nome do marcador.

Você pode clicar com o botão direito do mouse no marcador para viajar a essa posição, renomear ou excluir o marcador.

Marque o menu pop-up do botão direito do mouse mostrando opções para posicionar, editar e remover.

Observação

Na versão do depurador 1.2402.24001.0, o recurso de indicador não está disponível.

Confira também

Recursos do WinDbg

Passo a passo da depuração da viagem no tempo