Compartilhar via


Depuração de viagem no tempo - Passo a passo do aplicativo de exemplo

Logotipo de depuração da viagem no tempo com um relógio.

Este laboratório introduz TTD (Depuração de Viagem no Tempo), com um pequeno programa de exemplo com uma falha de código. O TTD é usado para depurar, identificar e descobrir causa raiz do problema. Embora o problema neste pequeno programa seja fácil de encontrar, o procedimento geral pode ser usado com código mais complexo. Este procedimento geral pode ser resumido da seguinte forma.

  1. Capturar um rastreamento de viagem no tempo do programa com falha.
  2. Use o comando dx (expressão para exibir modelo de objeto de depurador) para localizar o evento de exceção armazenado na gravação.
  3. Use o comando !tt (viagem no tempo) para viajar até a posição do evento de exceção no rastreamento.
  4. A partir desse ponto no rastreamento, volte um passo até que o código com falha em questão entre no escopo.
  5. Com o código com falha no escopo, examine os valores locais e desenvolva uma hipótese de variável que possa conter um valor incorreto.
  6. Determine o endereço da memória da variável com o valor incorreto.
  7. Defina um ponto de interrupção (ba) de acesso à memória no endereço da variável suspeita usando o comando ba (interrupção no acesso).
  8. Use g- para voltar ao último ponto de acesso à memória da variável suspeita.
  9. Confira se esse local, ou algumas instruções antes, é o ponto da falha do código. Se for, você terminou. Se o valor incorreto veio de alguma outra variável, defina outra interrupção no ponto de interrupção de acesso na segunda variável.
  10. Use g- para voltar ao último ponto de acesso à memória na segunda variável suspeita. Confira se esse local ou algumas instruções anteriores contém a falha de código. Se for, você terminou.
  11. Repita esse processo voltando até localizar o código que definiu o valor incorreto que causou o erro.

Embora as técnicas gerais descritas neste procedimento se apliquem a um amplo conjunto de problemas de código, existem problemas de código exclusivos que exigirão uma abordagem exclusiva. As técnicas ilustradas no passo a passo devem servir para expandir seu conjunto de ferramentas de depuração e ilustrar um pouco do que é possível com um rastreamento TTD.

Objetivos do laboratório

Depois de concluir este laboratório, você poderá usar o procedimento geral com um rastreamento de viagem no tempo para localizar problemas no código.

Configuração do laboratório

Você precisará do seguinte hardware para concluir o laboratório.

  • Um laptop ou desktop (host) executando o Windows 10 ou Windows 11

Você precisará do seguinte software para concluir o laboratório.

  • O WinDbg. Para informações sobre como instalar o WinDbg, consulte WinDbg - Instalação
  • Visual Studio para criar o código C++ de exemplo.

O laboratório tem três seções.

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

Na Seção 1, você irá criar o código de exemplo usando o Visual Studio.

Criar o aplicativo de exemplo no Visual Studio

  1. No Microsoft Visual Studio, clique em Arquivo>Novo>Projeto/Solução... e clique nos modelos do Visual C++.

    Selecione Aplicativo de Console Win32.

    Forneça um nome de projeto de DisplayGreeting e clique em OK.

  2. Desmarque as verificações do SDL (Microsoft Security Development Lifecycle).

    Configurações do Assistente de aplicativo Win32 no Visual Studio.

  3. Clique em Concluir.

  4. Cole o texto a seguir no painel DisplayGreeting.cpp no Visual Studio.

    // DisplayGreeting.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <array>
    #include <stdio.h>
    #include <string.h>
    
    void GetCppConGreeting(wchar_t* buffer, size_t size)
    {
        wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!";
    
        wcscpy_s(buffer, size, message);
    }
    
    int main()
    {
         std::array <wchar_t, 50> greeting{};
         GetCppConGreeting(greeting.data(), sizeof(greeting));
    
         wprintf(L"%ls\n", greeting.data());
    
         return 0;
    }
    
  5. No Visual Studio, clique em Projeto>Propriedades DisplayGreeting. Em seguida, clique em C/C++ e Geração de código.

    Defina as seguintes propriedades.

    Configuração Valor
    Verificação de Segurança Desativar verificação de segurança (/GS-)
    Verificações básicas de runtime Padrão

    Observação

    Embora essas configurações não sejam recomendadas, é possível imaginar uma situação em que alguém aconselharia usar essas configurações para agilizar a codificação ou facilitar alguns ambientes de teste.

  6. No Visual Studio, clique em Build>Solução de compilação.

    Se tudo correr bem, as janelas de compilação devem mostrar uma mensagem indicando que a compilação foi bem-sucedida.

  7. Localizar os arquivos de aplicativo de exemplo criados

    No Solution Explorer, clique com o botão direito no projeto DisplayGreeting e selecione Abrir Pasta no Explorador de Arquivos.

    Navegue até a pasta Depurar que contém o arquivo pdb de símbolo e exe compatível para o exemplo. Por exemplo, você navegaria até C:\Projetos\DisplayGreeting\Depurar, se essa for a pasta em que seus projetos estão armazenados.

  8. Executar o aplicativo de exemplo com a falha de código

    Clique duas vezes no arquivo exe e execute o aplicativo de exemplo.

    Captura de tela do console executando o arquivo DisplayGreeting.exe.

    Se esta caixa de diálogo for exibida, selecione Fechar programa

    Captura de tela da caixa de diálogo exibindo

    Na próxima seção do passo a passo, iremos gravar a execução do aplicativo de exemplo para ver se conseguimos determinar por que essa exceção está ocorrendo.

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

Na Seção 2, você registrará um rastro do aplicativo "DisplayGreeting" de amostra de comportamento inadequado

Para abrir o aplicativo de exemplo e gravar um rastreamento TTD, siga estes passos. Para obter informações gerais sobre como gravar rastreamentos TTD, consulte Depuração de viagem no tempo - Gravar um rastreamento

  1. Execute o WinDbg como Administrador para poder gravar rastreamentos de viagem no tempo.

  2. No WinDbg, selecione Arquivo>Iniciar depuração>Lançar executável (avançado).

  3. Insira o caminho ao executável do modo de usuário que você quer gravar ou selecione Procurar para ir até o executável. Para informações sobre como trabalhar com o menu iniciar executável no WinDbg, consulte WinDbg - Iniciar uma sessão de modo de usuário.

    Captura de tela do WinDbg com a caixa de seleção

  4. Marque a caixa Gravar com depuração de viagem no tempo para gravar um rastreamento quando o executável é iniciado.

  5. Clique em Configurar e gravar para iniciar a gravação.

  6. Quando a caixa de diálogo "Configurar gravação" aparecer, clique em Gravar para iniciar o executável e a gravação.

    Captura de tela do WinDbg exibindo a caixa de diálogo Configurar Gravação com o caminho definido como c: temp.

  7. A caixa de diálogo Gravar aparece indicando que o rastreamento está sendo gravado. Pouco depois, o aplicativo falha.

  8. Clique em Fechar programa para descartar a caixa de diálogo "DisplayGreeting parou de funcionar".

    Caixa de diálogo mostrando que o aplicativo DisplayGreeting parou de funcionar.

  9. Quando o programa falhar, o arquivo de rastreamento será encerrado e gravado no disco.

    Captura de tela da saída do WinDbg mostrando 1/1 quadros-chave indexados.

  10. O depurador abre automaticamente o arquivo de rastreamento e o indexa. A indexação é um processo que permite depurar eficientemente o arquivo de rastreamento. Esse processo de indexação leva mais tempo para arquivos de rastreamento maiores.

    (5120.2540): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: D:0 [Unindexed] Index
    !index
    Indexed 10/22 keyframes
    Indexed 20/22 keyframes
    Indexed 22/22 keyframes
    Successfully created the index in 755ms.
    

Observação

Um quadro-chave é um lugar em um rastreamento usado para indexação. Quadros-chave são gerados automaticamente. Rastreamentos maiores irão conter mais quadros-chave.

  1. Neste ponto, você está no início do arquivo de rastreamento e pronto para viajar para frente e para trás no tempo.

    Agora que já gravou um rastreamento TTD, você pode reproduzir o rastreamento ou trabalhar com o arquivo de rastreamento, por exemplo, compartilhando-o com um colega de trabalho. Para mais informações sobre como trabalhar com arquivos de rastreamento, consulte Depuração de viagem no tempo - Trabalhando com arquivos de rastreamento

Na próxima seção deste laboratório, analisaremos o arquivo de rastreamento para encontrar o problema com nosso código.

Seção 3: Analisar a gravação do arquivo de rastreamento para identificar o problema de código

Na Seção 3, você irá analisar a gravação do arquivo de rastreamento para identificar o problema de código.

Configurar o ambiente do WinDbg

  1. Adicione a localização do símbolo local ao caminho do símbolo e recarregue os símbolos, digitando os comandos a seguir.

    .sympath+ C:\MyProjects\DisplayGreeting\Debug
    .reload
    
  2. Adicione a localização do seu código local ao caminho de origem digitando o comando seguinte.

    .srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
    
  3. Para poder ver o estado da pilha e das variáveis locais, na faixa do WinDbg, selecione Exibir e Locais e Exibir e Pilha. Organize as janelas para permitir visualizar o código-fonte e as janelas de comando ao mesmo tempo.

  4. Na faixa de opções do WinDgb, selecione Fonte e Abra o arquivo de origem. Localize e abra o arquivo DisplayGreeting.cpp.

Examinar a exceção

  1. Quando o arquivo de rastreamento é carregado, ele exibe informações de que ocorreu uma exceção.

    2fa8.1fdc): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68ef8100 ebx=00000000 ecx=77a266ac edx=69614afc esi=6961137c edi=004da000
    eip=77a266ac esp=0023f9b4 ebp=0023fc04 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:0023fac0=00000000
    
  2. Use o comando dx para listar os eventos na gravação. O evento de exceção está listado nos eventos.

    0:000> dx -r1 @$curprocess.TTD.Events
    ...
    [0x2c]           : Module Loaded at position: 9967:0
    [0x2d]           : Exception at 9BDC:0
    [0x2e]           : Thread terminated at 9C43:0
    ...
    
    

    Observação

    Neste passo a passo, três períodos são usados para indicar que a saída incorreta foi removida.

  3. Clique no evento Exceção para exibir informações sobre esse evento TTD.

    0:000> dx -r1 @$curprocess.TTD.Events[17]
    @$curprocess.TTD.Events[17]                 : Exception at 68:0
        Type             : Exception
        Position         : 68:0 [Time Travel]
        Exception        : Exception of type Hardware at PC: 0X540020
    
  4. Clique no campo Exceção para detalhar melhor os dados da exceção.

    0:000> dx -r1 @$curprocess.TTD.Events[17].Exception
    @$curprocess.TTD.Events[17].Exception                 : Exception of type Hardware at PC: 0X540020
        Position         : 68:0 [Time Travel]
        Type             : Hardware
        ProgramCounter   : 0x540020
        Code             : 0xc0000005
        Flags            : 0x0
        RecordAddress    : 0x0
    

    Os dados da exceção indicam que esta é uma falha de hardware lançada pela CPU. Eles também fornecem o código de exceção de 0xc0000005 que indica que se trata de uma violação de acesso. Normalmente, isso indica que estávamos tentando gravar na memória à qual não temos acesso.

  5. Clique no link [Viagem no tempo] no evento da exceção para mover para essa posição no rastreamento.

    0:000> dx @$curprocess.TTD.Events[17].Exception.Position.SeekTo()
    Setting position: 68:0
    
    @$curprocess.TTD.Events[17].Exception.Position.SeekTo()
    (16c8.1f28): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 68:0
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00540020 esp=00effe4c 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 ??
    

    Observe nesta saída que a pilha e o ponteiro base estão apontando a dois endereços bem diferentes.

    esp=00effe4c ebp=00520055
    

    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.

Examinar as variáveis locais e definir o ponto de interrupçã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.

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

    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: 67:40
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00540020 esp=00effe4c 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: 67:3F
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=0019193d esp=00effe48 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+0x4d:
    0019193d c3
    
    0:000> t-
    Time Travel Position: 67:39
    eax=0000004c ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00191935 esp=00effd94 ebp=00effe44 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    DisplayGreeting!main+0x45:
    

    Observação

    Neste passo a passo, a saída do comando mostra os comandos que podem ser usados no lugar das opções do Menu da interface do usuário para permitir que usuários com preferência de uso de linha de comando usem comandos de linha de comando.

  2. 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=00effd94 ebp=00effe44
    

    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 neste ponto do rastreamento.

  3. Para investigar melhor, podemos abrir uma janela de memória para ver o conteúdo perto do endereço de memória do ponteiro base de 0x00effe44.

  4. Para exibir os caracteres ASCII associados, na faixa de opções Memória, selecione Texto e ASCII.

    Captura de tela do WinDbg Preview mostrando a saída ASCII da memória e a janela Código-fonte.

  5. Em vez de o ponteiro base apontar a uma instrução, ele está apontando ao texto da mensagem. Portanto, algo não está certo, isso pode estar perto do ponto no tempo em que corrompemos a pilha. Para investigar, vamos definir um ponto de interrupção.

Observação

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.

TTD e pontos 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.

Pontos de interrupção de acesso à memória

Você pode definir pontos de interrupção acionados quando um local de memória é acessado. Use o comando ba (interrupção no acesso), com a sintaxe a seguir.

ba <access> <size> <address> {options}
Opção Descrição
e execute (quando a CPU procura uma instrução do endereço)
r leitura/gravação (quando a CPU lê ou grava no endereço)
w gravação (quando a CPU grava no endereço)

Observe que você só pode definir quatro pontos de interrupção de dados a qualquer momento e cabe a você certificar-se de que está alinhando seus dados corretamente ou não acionará o ponto de interrupção (as palavras devem terminar em endereços divisíveis por 2, dwords devem ser divisíveis por 4 e quadwords por 0 ou 8).

Definir a interrupção no ponto de interrupção de acesso à memória para o ponteiro base

  1. Neste ponto do rastreamento, vamos definir um ponto de interrupção no acesso à memória de gravação para o ponteiro base, ebp, que, em nosso exemplo, é 00effe44. Para fazer isso, use o comando ba usando o endereço que queremos monitorar. Vamos monitorar gravações para quatro bytes, então especificamos w4.

    0:000> ba w4 00effe44
    
  2. Selecione Exibir e, em seguida, Pontos de interrupção para confirmar que eles estejam definidos conforme o esperado.

    Captura de tela da janela Pontos de interrupção do WinDbg mostrando um único ponto de interrupção.

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

    0:000> g-
    Breakpoint 0 hit
    Time Travel Position: 5B:92
    eax=0000000f ebx=003db000 ecx=00000000 edx=00cc1a6c esi=00d41046 edi=0053fde8
    eip=00d4174a esp=0053fcf8 ebp=0053fde8 iopl=0         nv up ei pl nz ac pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
    DisplayGreeting!DisplayGreeting+0x3a:
    00d4174a c745e000000000  mov     dword ptr [ebp-20h],0 ss:002b:0053fdc8=cccccccc
    
  4. Selecione Exibir e, em seguida, Locais. Na janela Locais, podemos ver que a variável destination tem só parte da mensagem, enquanto a source contém todo o texto. Essas informações apoiam a ideia de que a pilha foi corrompida.

    Captura de tela do WinDbg mostrando a janela Locais.

  5. Neste ponto, podemos ver a pilha de programas para ver qual código está ativo. Na faixa de opções Exibir, selecione Pilha.

    Captura de tela do WinDbg mostrando a janela 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. Mas para ilustrar as técnicas que podem ser usadas com programas maiores e mais complexos, vamos definir outro ponto de interrupção para investigar mais.

Definir a interrupção no ponto de interrupção de acesso para a função GetCppConGreeting

  1. Use a janela de pontos de interrupção para limpar o ponto de interrupção presente; basta clicar com o botão direito do mouse no ponto de interrupção existente e selecionar Remover.

  2. Determine o endereço da função DisplayGreeting!GetCppConGreeting usando o comando dx.

    0:000> dx &DisplayGreeting!GetCppConGreeting
    &DisplayGreeting!GetCppConGreeting                 : 0xb61720 [Type: void (__cdecl*)(wchar_t *,unsigned int)]
        [Type: void __cdecl(wchar_t *,unsigned int)]
    
  3. Use o comando ba para definir um ponto de interrupção no acesso à memória. Como a função será lida da memória somente para execução, precisamos definir um ponto de interrupção r - leitura.

    0:000> ba r4 b61720
    
  4. Confirme se um ponto de interrupção de Leitura de hardware está ativo na janela de pontos de interrupção.

    Captura de tela da janela Pontos de interrupção do WinDbg mostrando um único ponto de interrupção de leitura de hardware.

  5. Como estamos nos perguntando sobre o tamanho da cadeia de caracteres de saudação, vamos definir uma janela de observação para exibir o valor de sizeof(greeting). Na faixa de opções Exibir, selecione Assistir e forneça sizeof(greeting). Se o valor não estiver no escopo, a janela de inspeção será exibida - Não é possível vincular o nome 'saudação'.

    Captura de tela do WinDbg mostrando uma janela Observar locais.

  6. No menu Viagem no tempo, use Viagem no tempo a iniciar ou use o comando !tt 0 para mover para o início do rastreamento.

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  7. No menu Início, selecione Ir ou use o comando g para avançar no código até que o ponto de interrupção seja atingido.

    0:000> g
    Breakpoint 2 hit
    Time Travel Position: 4B:1AD
    eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046
    eip=00b61721 esp=00ddf7a4 ebp=00ddf864 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!GetCppConGreeting+0x1:
    00b61721 8bec            mov     ebp,esp
    
  8. No menu Início, selecione Sair e voltar ou use o comando g-u para voltar uma etapa.

    0:000> g-u
    Time Travel Position: 4B:1AA
    eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046
    eip=00b61917 esp=00ddf7ac ebp=00ddf864 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!main+0x27:
    00b61917 e8def7ffff      call    DisplayGreeting!ILT+245(?GetCppConGreetingYAXPA_WIZ) (00b610fa)
    
  9. Parece que já encontramos a causa raiz. A matriz de saudação que declaramos tem 50 caracteres de comprimento, enquanto o sizeof(greeting) que passamos para GetCppConGreeting é 0x64, 100.

    Captura de tela do WinDbg mostrando o código DisplayGreeting com uma janela Observar locais mostrando 0x64.

    À medida que examinamos mais detalhadamente a questão do tamanho, também notamos que a mensagem tem 75 caracteres de comprimento e 76 ao incluir o final do caractere de string.

    HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!
    
  10. Um jeito de corrigir o código seria expandir o tamanho da matriz de caracteres para 100.

    std::array <wchar_t, 100> greeting{};
    

    E também precisamos trocar sizeof(greeting) para size(greeting) nesta linha de código.

     GetCppConGreeting(greeting.data(), size(greeting));
    
  11. Para validar estas correções, poderíamos recompilar o código e confirmar que ele é executado sem erros.

Definindo um ponto de interrupção com a janela de origem

  1. Um jeito alternativo de fazer essa investigação seria definir um ponto de interrupção clicando em qualquer linha de código. Por exemplo, clicar no lado direito da linha de definição std:array na janela de origem define um ponto de interrupção.

    Captura de tela da janela Código-fonte em WinDbg com um ponto de interrupção definido em std::array.

  2. No menu Viagem no tempo, use o comando Viagem no tempo a iniciar para mover para o início do rastreamento.

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  3. Na faixa Início, clique em Ir para viajar de volta até que o ponto de interrupção seja atingido.

    Breakpoint 0 hit
    Time Travel Position: 5B:AF
    eax=0000000f ebx=00c20000 ecx=00000000 edx=00000000 esi=013a1046 edi=00effa60
    eip=013a17c1 esp=00eff970 ebp=00effa60 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!DisplayGreeting+0x41:
    013a17c1 8bf4            mov     esi,esp
    

Definir a quebra no ponto de interrupção de acesso para a variável greeting

Outro jeito de fazer essa investigação, seria definir um ponto de interrupção em variáveis suspeitas e examinar qual código está alterando-as. Por exemplo, para definir um ponto de interrupção na variável greeting no método GetCppConGreeting, use este procedimento.

Esta parte do passo a passo presume que você ainda esteja localizado no ponto de interrupção da seção anterior.

  1. Em Exibir e Locais. Na janela Locais, greeting está disponível no contexto atual, de forma que podemos determinar seu local de memória.

  2. Use o comando dx para examinar a matriz greeting.

    0:000> dx &greeting
    &greeting                 : ddf800 : { size=50 } [Type: std::array<wchar_t,50> *]
       [<Raw View>]     [Type: std::array<wchar_t,50>]
       [0]              : 3 [Type: wchar_t]
       [1]              : 0 [Type: wchar_t]
    

    Nesse rastreamento, greeting está na memória em ddf800.

  3. Use a janela de pontos de interrupção para limpar qualquer ponto de interrupção presente; basta clicar com o botão direito do mouse no ponto de interrupção existente e selecionar Remover.

  4. Defina o ponto de interrupção com o comando ba usando o endereço de memória que queremos monitorar para acesso de gravação.

    ba w4 ddf800
    
  5. No menu Viagem no tempo, use o comando Viagem no tempo a iniciar para mover para o início do rastreamento.

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  6. No menu Início, selecione Ir para avançar ao primeiro ponto de acesso à memória da matriz greeting.

    0:000> g-
    Breakpoint 0 hit
    Time Travel Position: 5B:9C
    eax=cccccccc ebx=002b1000 ecx=00000000 edx=68d51a6c esi=013a1046 edi=001bf7d8
    eip=013a1735 esp=001bf6b8 ebp=001bf7d8 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!GetCppConGreeting+0x25:
    013a1735 c745ec04000000  mov     dword ptr [ebp-14h],4 ss:002b:001bf7c4=cccccccc
    

    Como alternativa, podemos viajar ao final do rastreamento e trabalhar inversamente no código para encontrar o último ponto no rastreamento no qual o local de memória da matriz foi gravado.

Use o objetos TTD.Memory para exibir o acesso à memória

Outra forma de determinar em quais pontos da memória de rastreamento foi acessada é usar o objetos TTD.Memory e o comando dx.

  1. Use o comando dx para examinar a matriz greeting.

    0:000> dx &greeting
    &greeting                 : 0xddf800 [Type: std::array<wchar_t,50> *]
       [+0x000] _Elems           : "꽘棶檙瞝???" [Type: wchar_t [50]]
    

    Nesse rastreamento, greeting está na memória em ddf800.

  2. Use o comando dx para examinar os quatro bytes na memória começando nesse endereço com o acesso de leitura e gravação.

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")
    @$cursession.TTD.Memory(0x1bf7d0,0x1bf7d4, "rw")                
        [0x0]           
        [0x1]           
        [0x2]           
        [0x3]           
        [0x4]           
        [0x5]           
        [0x6]           
        [0x7]           
        [0x8]           
        [0x9]           
        [0xa]           
        ...         
    
  3. Clique em uma das ocorrências para mostrar mais informações sobre essa ocorrência de acesso à memória.

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5]
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5]                
        EventType        : MemoryAccess
        ThreadId         : 0x710
        UniqueThreadId   : 0x2
        TimeStart        : 27:3C1 [Time Travel]
        TimeEnd          : 27:3C1 [Time Travel]
        AccessType       : Write
        IP               : 0x6900432f
        Address          : 0xddf800
        Size             : 0x4
        Value            : 0xddf818
        OverwrittenValue : 0x0
        SystemTimeStart  : Monday, November 18, 2024 23:01:43.400
        SystemTimeEnd    : Monday, November 18, 2024 23:01:43.400
    
  4. Clique em [Viagem no Tempo] para TimeStart para posicionar o traço no momento.

    0:000> dx @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5].TimeStart.SeekTo()
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5].TimeStart.SeekTo()
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 27:3C1
    eax=00ddf81c ebx=00fa2000 ecx=00ddf818 edx=ffffffff esi=00000000 edi=00b61046
    eip=6900432f esp=00ddf804 ebp=00ddf810 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    ucrtbased!_register_onexit_function+0xf:
    6900432f 51              push    ecx
    
  5. Se tivermos interesse na última ocorrência de acesso à memória de leitura/gravação no rastreamento, podemos clicar no último item da lista ou anexar a função .Last() ao final do comando dx.

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw").Last()
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw").Last()                
        EventType        : MemoryAccess
        ThreadId         : 0x710
        UniqueThreadId   : 0x2
        TimeStart        : 53:100E [Time Travel]
        TimeEnd          : 53:100E [Time Travel]
        AccessType       : Read
        IP               : 0x690338e4
        Address          : 0xddf802
        Size             : 0x2
        Value            : 0x45
        SystemTimeStart  : Monday, November 18, 2024 23:01:43.859
        SystemTimeEnd    : Monday, November 18, 2024 23:01:43.859
    
  6. Depois, podemos clicar em [Viagem no Tempo] para mover para essa posição no rastreamento e examinar mais a execução do código nesse ponto, usando as técnicas já descritas neste laboratório.

Para mais informações sobre os objetos TTD.Memory, consulte TTD.Memory Object.

Resumo

Nesta amostra bem pequena, o problema poderia ter sido determinado observando as poucas linhas de código, mas em programas maiores as técnicas apresentadas aqui podem ser usadas para reduzir o tempo necessário para localizar um problema.

Depois que um rastreamento é registrado, as etapas de rastreamento e reprodução podem ser compartilhadas e o problema poderá ser reproduzido em qualquer PC.

Confira também

Depuração de viagem no tempo – Visão geral

Depuração de viagem no tempo - Gravação

Depuração de viagem no tempo - Repetir rastreamento

Depuração de viagem no tempo - Trabalhando com arquivos de rastreamento