Partilhar via


WinDbg: Cronogramas

logotipo WinDbg com uma lupa inspecionando bits.

Time Travel Debugging (TTD) permite aos usuários gravar rastreamentos, que são gravações da execução de um programa. As linhas do tempo são uma representação visual de eventos que acontecem 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.

Captura de ecrã de cronologias no depurador exibindo exceções, acesso à memória, pontos de interrupção e chamadas de função.

Use a janela Linhas do tempo para visualizar eventos importantes, entender sua posição relativa e saltar facilmente para sua localização no arquivo de rastreamento TTD. Use várias linhas do tempo para explorar visualmente eventos no rastreamento de viagem no tempo e descobrir a correlação de eventos.

A janela Linhas do tempo aparece quando você abre um arquivo de rastreamento TTD. Ele mostra eventos-chave sem que você precise criar manualmente consultas de modelo de dados. Ao mesmo tempo, todos os objetos de viagem no tempo estão disponíveis para permitir consultas de dados mais complexas.

Para obter 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 cronogramas

A janela Linhas do tempo mostra eventos nas seguintes linhas do tempo:

  • Exceções: você pode filtrar um código de exceção específico.
  • Pontos de interrupção: você pode ver quando os pontos de interrupção atingem uma linha do tempo.
  • Acessos à memória: Você pode ler, gravar e executar entre dois endereços de memória.
  • Chamadas de função: Você pode pesquisar na forma de module!function.

Passe o cursor sobre cada evento para obter mais informações por meio do tooltip. A seleção de um evento executa a consulta para o evento e exibe mais informações. Clicar duas vezes em um evento salta para esse local no arquivo de rastreamento TTD.

Exceções

Quando você carrega um arquivo de rastreamento e a linha do tempo está ativa, ela 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.

Captura de tela de uma linha do tempo no depurador exibindo 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 usando o campo Código de exceção opcional.

Captura de tela de uma caixa de diálogo de exceção do depurador de linha do tempo com Tipo de linha do tempo definido como Exceções e Código de exceção definido como 0xC0000004.

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

Pontos de interrupção

Depois de adicionar um ponto de interrupção, as posições na linha do tempo mostram quando esse ponto de interrupção é atingido. Por exemplo, você pode usar o comando bp set Breakpoint. Quando você passa o mouse sobre um ponto de interrupção, o endereço e o ponteiro de instrução associados ao ponto de interrupção aparecem.

Captura de ecrã de uma linha de tempo no depurador exibindo aproximadamente 30 indicadores de ponto de interrupção.

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

Chamadas de função

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

Captura de ecrã a mostrar a adição de uma linha cronológica no depurador com um nome de chamada de função introduzido.

Quando você passa o mouse sobre uma chamada de função, o nome da função, os parâmetros de entrada, seus valores e o valor de retorno aparecem. Este exemplo mostra o buffer e o tamanho porque são os parâmetros do DisplayGreeting!GetCppConGreeting.

Captura de tela de uma linha do tempo no depurador exibindo chamadas de função e a janela Registros.

Acesso à memória

Use a cronologia de Acessos à Memória para ver quando um intervalo específico de memória foi lido ou gravado, ou onde ocorreu a execução do código. Os endereços Start e Stop são usados para definir um intervalo entre dois endereços de memória.

Captura de ecrã a mostrar a adição de uma caixa de diálogo Acessos à memória com a opção Escrever selecionada.

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

Captura de tela de uma linha do tempo no depurador exibindo eventos de acesso à memória.

Trabalhar com cronogramas

Uma linha cinza vertical segue o cursor quando você passa o mouse sobre a linha do tempo. A linha azul vertical indica a posição atual no traço.

Selecione os ícones da lupa para aumentar e diminuir o zoom na linha do tempo.

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

Captura de tela de uma linha do tempo no depurador mostrando a área superior usada para selecionar a exibição ativa.

Movimentos do rato

Para aumentar e diminuir o zoom, selecione Ctrl e use a roda de rolagem.

Para mover de um lado para o outro, selecione Shift e use a roda de rolagem.

Técnicas de depuração temporal

Para demonstrar as técnicas de depuração da linha do tempo, o passo a passo da depuração de viagem no tempo é reutilizado aqui. Esta demonstração pressupõe que você concluiu as duas primeiras etapas para criar o código de exemplo e criou a gravação TTD usando as duas primeiras etapas descritas lá.

Nesse cenário, a primeira etapa é encontrar a exceção no rastreamento de viagem no tempo. Clique duas vezes na única exceção que você vê na linha do tempo.

Na janela Comando , você pode ver que o seguinte comando foi emitido quando você selecionou a 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 exibir os registros neste ponto da linha do tempo para iniciar sua investigação.

Captura de tela de uma linha do tempo no depurador exibindo a exceção de laboratório de demonstração e a janela Registros.

Na saída do comando, o stack (esp) e o ponteiro base (ebp) estão apontando para endereços diferentes. Essa discrepância pode indicar corrupção na stack. Possivelmente, uma função retornou e, em seguida, corrompeu a pilha. Para validar este problema, volte para antes do estado da CPU ser corrompido e verifique se é possível determinar quando a corrupção da pilha ocorreu.

Ao fazer esse processo, examine os valores das variáveis locais e da pilha:

  • Selecione Exibir>locais para exibir os valores locais.
  • Selecione View>Stack para exibir a pilha de execução de código.

No momento em que a falha é detectada em rastreamento, é comum que acabemos alguns passos além da verdadeira causa, dentro do código de tratamento de erros. Com a viagem no tempo, é possível regressar uma instrução de cada vez para localizar a verdadeira causa.

Na faixa de opções Página Inicial , use o comando Step Into Back para retroceder três instruções. Ao fazer esse processo, continue a examinar as janelas Pilha, Locais e Registros.

A janela Comando exibe a posição da 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, a pilha e o ponteiro base têm valores que fazem mais sentido. Parece que você está se aproximando do ponto no código onde a corrupção ocorreu.

esp=003cf718 ebp=003cf7c8

A janela Locais contém valores do seu aplicativo de destino. A janela Código-fonte destaca a linha de código que está pronta para ser executada no código-fonte neste ponto do rastreamento.

Para investigar mais, abra uma janela de memória para visualizar o conteúdo perto do endereço de memória do ponteiro de pilha (esp). Neste exemplo, ele tem um valor de 003cf7c8. Selecione >>ASCII para exibir o texto ASCII armazenado nesse endereço.

Captura de tela do depurador exibindo as janelas Registros, Pilha e Memória.

Linha do tempo de acesso à memória

Depois que um local de interesse de memória for identificado, use esse valor para adicionar uma linha do tempo de acesso à memória. Selecione + Adicionar linha do tempo e preencha o endereço inicial. Observe 4 bytes para que, ao adicioná-los ao valor Endereço Inicial de 003cf7c8, você tenha um valor de Endereço Final de 003cf7cb. O padrão é examinar todas as gravações de memória, mas você também pode examinar apenas gravações ou execução de código nesse endereço.

Captura de tela da adição de uma caixa de diálogo de acesso à memória da linha do tempo com a opção Gravar selecionada e um valor inicial de 003cf7c8.

Agora você pode percorrer a linha do tempo em sentido inverso para examinar em que ponto neste traçado de viagem no tempo esse local de memória foi consignado para ver o que você pode encontrar. Ao selecionar essa posição na linha do tempo, você pode ver que os locais valorizam valores diferentes para a cadeia de caracteres que está sendo copiada. O valor de destino parece estar incompleto, como se o comprimento da cadeia de caracteres não estivesse correto.

Captura de tela da linha do tempo de acesso à memória e da janela Locais exibindo valores locais com valores de origem e destino diferentes.

Linha temporal do ponto de interrupção

O uso de pontos de interrupção é uma abordagem comum para pausar a execução de código em algum evento de interesse. Com o TTD, você pode definir um ponto de interrupção e viajar de volta no tempo até que esse ponto de interrupção seja atingido depois que o rastreamento for registrado. A capacidade de examinar o estado do processo após a ocorrência de um problema, para determinar o melhor local para um ponto de interrupção, permite mais fluxos de trabalho de depuração exclusivos para TTD.

Para explorar uma técnica alternativa de depuração de linha do tempo, selecione a exceção na linha do tempo e volte novamente três passos usando o comando Step Into Back no separador Página Inicial.

Neste pequeno exemplo, é fácil procurar no código. Se você tiver centenas de linhas de código e dezenas de sub-rotinas, use as técnicas descritas aqui para diminuir o tempo necessário para localizar o problema.

Como mencionado anteriormente, o ponteiro base (esp) aponta para o texto da mensagem em vez de apontar para uma instrução.

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

0:000> ba w4 003cf7c8

Embora você use um ponto de interrupção de acesso à memória simples, você pode construir pontos de interrupção para serem instruções condicionais mais complexas. Para obter mais informações, consulte bp, bu, bm (Definir ponto de interrupção).

No menu Página Inicial , selecione Voltar para viajar no tempo até que o ponto de interrupção seja atingido.

Neste ponto, você pode examinar a pilha de programas para ver qual código está ativo.

Captura de tela de uma linha do tempo no depurador exibindo linhas do tempo de acesso à memória e janelas de pilha.

Como é improvável que a função fornecida wscpy_s() pela Microsoft possa ter um bug de código como este, procure mais na pilha. A pilha mostra que Greeting!main chama Greeting!GetCppConGreeting. Em seu pequeno exemplo de código, você pode abrir o código neste ponto e provavelmente encontrar o erro facilmente. Mas para ilustrar as técnicas que você pode usar com um programa maior e mais complexo, você adiciona uma linha do tempo de chamada de função.

Cronologia da chamada de função

Selecione + Adicionar cronograma e insira a cadeia de pesquisa de função em DisplayGreeting!GetCppConGreeting.

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

Você pode usar o dx comando para exibir o objeto de chamada de função para ver os campos associados TimeStart e TimeEnd que correspondem ao local inicial e ao 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  

Uma das caixas de seleção Local Inicial ou Local de Término ou ambas as caixas de seleção Local Inicial e Local de Término devem ser marcadas.

Captura de ecrã da caixa de diálogo Adicionar nova cronologia exibindo a adição de uma cronologia de Chamadas de função com uma string de pesquisa de função de DisplayGreeting!GetCppConGreeting.

Seu código não é recursivo ou reentrante, por isso é fácil de localizar na linha do tempo quando o GetCppConGreeting método é chamado. A chamada para GetCppConGreeting também ocorre ao mesmo tempo que o ponto de interrupção e o evento de acesso à memória que definiste. Portanto, parece que você restringiu uma área de código para examinar cuidadosamente a causa raiz da falha do aplicativo.

Captura de tela de uma linha do tempo no depurador exibindo a linha do tempo de acesso à memória e a janela Locais com uma mensagem e buffer contendo diferentes valores de cadeia de caracteres.

Explore a execução de código visualizando várias linhas do tempo

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

Captura de ecrã de um cronograma no depurador a mostrar um cronograma de Acessos à Memória e a janela de Locais.

A capacidade de ver mais correlações e encontrar coisas inesperadas diferencia a ferramenta de linha do tempo da interação com o rastreamento de viagem no tempo usando comandos de linha de comando.

Marcadores da linha do tempo

Marque posições importantes de viagem no tempo no WinDbg em vez de copiar e colar manualmente a posição no Bloco de Notas. Os marcadores facilitam a visualização rápida de diferentes posições no rastreio em relação a outros eventos e a sua anotação.

Você pode fornecer um nome descritivo para os favoritos.

Captura de ecrã da caixa de diálogo Novo marcador mostrando um nome de exemplo para a primeira chamada de API na aplicação Exibir mensagem de saudação.

Selecione Ver>Linhas do Tempo para abrir a janela Linhas do Tempo para poder aceder à Linha do Tempo dos Favoritos. Quando você passa o mouse sobre um marcador, o nome do marcador aparece.

Captura de ecrã da linha cronológica a apresentar três marcadores com o cursor sobre um para revelar o nome do marcador.

Clique com o botão direito do rato no marcador para viajar para a posição do marcador, mudar o nome do marcador ou eliminá-lo.

Captura de ecrã do menu pop-up dos Marcadores ao clicar com o botão direito do rato, apresentando opções para ir para a posição, editar e remover.

Observação

O recurso de marcador não está disponível na versão 1.2402.24001.0 do WinDbg.