Depuração de viagem no tempo - Passo a passo do aplicativo de exemplo
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.
- Capturar um rastreamento de viagem no tempo do programa com falha.
- Use o comando dx (expressão para exibir modelo de objeto de depurador) para localizar o evento de exceção armazenado na gravação.
- Use o comando !tt (viagem no tempo) para viajar até a posição do evento de exceção no rastreamento.
- A partir desse ponto no rastreamento, volte um passo até que o código com falha em questão entre no escopo.
- 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.
- Determine o endereço da memória da variável com o valor incorreto.
- 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).
- Use g- para voltar ao último ponto de acesso à memória da variável suspeita.
- 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.
- 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.
- 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
- Seção 2: Gravar um rastreamento da exemplo "DisplayGreeting"
- Seção 3: Analisar a gravação do arquivo de rastreamento para identificar o problema de código
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
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.
Desmarque as verificações do SDL (Microsoft Security Development Lifecycle).
Clique em Concluir.
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; }
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.
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.
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.
Executar o aplicativo de exemplo com a falha de código
Clique duas vezes no arquivo exe e execute o aplicativo de exemplo.
Se esta caixa de diálogo for exibida, selecione Fechar programa
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
Execute o WinDbg como Administrador para poder gravar rastreamentos de viagem no tempo.
No WinDbg, selecione Arquivo>Iniciar depuração>Lançar executável (avançado).
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.
Marque a caixa Gravar com depuração de viagem no tempo para gravar um rastreamento quando o executável é iniciado.
Clique em Configurar e gravar para iniciar a gravação.
Quando a caixa de diálogo "Configurar gravação" aparecer, clique em Gravar para iniciar o executável e a gravação.
A caixa de diálogo Gravar aparece indicando que o rastreamento está sendo gravado. Pouco depois, o aplicativo falha.
Clique em Fechar programa para descartar a caixa de diálogo "DisplayGreeting parou de funcionar".
Quando o programa falhar, o arquivo de rastreamento será encerrado e gravado no disco.
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.
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
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
Adicione a localização do seu código local ao caminho de origem digitando o comando seguinte.
.srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
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.
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
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
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.
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
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.
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.
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.
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.
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.
Para exibir os caracteres ASCII associados, na faixa de opções Memória, selecione Texto e ASCII.
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
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
Selecione Exibir e, em seguida, Pontos de interrupção para confirmar que eles estejam definidos conforme o esperado.
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
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.
Neste ponto, podemos ver a pilha de programas para ver qual código está ativo. Na faixa de opções Exibir, selecione 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
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.
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)]
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
Confirme se um ponto de interrupção de Leitura de hardware está ativo na janela de pontos de interrupção.
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'.
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
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
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)
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.
À 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!
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));
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
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.
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
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.
Em Exibir e Locais. Na janela Locais, greeting está disponível no contexto atual, de forma que podemos determinar seu local de memória.
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.
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.
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
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
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.
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.
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] ...
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
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
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
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