Debug di viaggi temporali - Procedura dettagliata dell'app di esempio
Questo lab introduce il debug TTD (Time Travel Debugging), usando un piccolo programma di esempio con un difetto di codice. TTD viene usato per eseguire il debug, identificare e causare il problema. Anche se il problema in questo piccolo programma è facile da trovare, la procedura generale può essere usata su codice più complesso. Questa procedura generale può essere riepilogata come segue.
- Acquisire una traccia di spostamento temporale del programma non riuscito.
- Usare il comando dx (Display Debugger Object Model Expression) per trovare l'evento di eccezione archiviato nella registrazione.
- Usare il comando !tt (tempo di spostamento) per spostarsi nella posizione dell'evento di eccezione nella traccia.
- Da quel punto nel singolo passaggio di traccia all'indietro fino a quando il codice di errore in questione non rientra nell'ambito.
- Con il codice di errore nell'ambito, esaminare i valori locali e sviluppare un'ipotesi di una variabile che può contenere un valore non corretto.
- Determinare l'indirizzo di memoria della variabile con il valore non corretto.
- Impostare un punto di interruzione di accesso alla memoria (ba) sull'indirizzo della variabile sospetta usando il comando ba (Break on Access).
- Usare g- per eseguire il runback all'ultimo punto di accesso alla memoria della variabile sospetta.
- Verificare se tale posizione, o alcune istruzioni prima, è il punto del difetto del codice. In tal caso, hai finito. Se il valore non corretto proviene da un'altra variabile, impostare un'altra interruzione sul punto di interruzione di accesso nella seconda variabile.
- Usare g- per eseguire il runback all'ultimo punto di accesso alla memoria nella seconda variabile sospetta. Verificare se tale posizione o alcune istruzioni prima di contenere il difetto del codice. In tal caso, hai finito.
- Ripetere questo processo a ritroso fino a quando non viene individuato il codice che imposta il valore non corretto che ha causato l'errore.
Anche se le tecniche generali descritte in questa procedura si applicano a un ampio set di problemi di codice, esistono problemi di codice univoci che richiedono un approccio univoco. Le tecniche illustrate nella procedura dettagliata devono essere utili per espandere il set di strumenti di debug e illustrano alcune delle possibili operazioni con una traccia TTD.
Obiettivi del lab
Dopo aver completato questo lab, sarà possibile usare la procedura generale con una traccia di viaggio temporale per individuare i problemi nel codice.
Configurazione del laboratorio
Per completare il lab, sarà necessario disporre dell'hardware seguente.
- Un computer portatile o desktop (host) che esegue Windows 10 o Windows 11
Per completare il lab, sarà necessario il software seguente.
- The WinDbg. Per informazioni sull'installazione di WinDbg, vedere WinDbg - Installazione
- Visual Studio per compilare il codice C++ di esempio.
Il lab include le tre sezioni seguenti.
- Sezione 1: Compilare il codice di esempio
- Sezione 2: Registrare una traccia dell'esempio "DisplayGreeting"
- Sezione 3: Analizzare la registrazione dei file di traccia per identificare il problema del codice
Sezione 1: Compilare il codice di esempio
Nella sezione 1 si compilerà il codice di esempio usando Visual Studio.
Creare l'app di esempio in Visual Studio
In Microsoft Visual Studio fare clic su File>nuovo>progetto/soluzione e fare clic sui modelli di Visual C++.
Selezionare l'applicazione console Win32.
Specificare un nome di progetto DisplayGreeting e fare clic su OK.
Deselezionare i controlli Security Development Lifecycle (SDL).
Fare clic su Fine.
Incollare il testo seguente nel riquadro DisplayGreeting.cpp in 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; }
In Visual Studio fare clic su Project DisplayGreeting properties .In Visual Studio fare clic su Project DisplayGreeting properties.In Visual Studio, click Project>DisplayGreeting properties. Fare quindi clic su C/C++ e generazione di codice.
Imposta le proprietà seguenti.
Impostazione Valore Controllo di sicurezza Disabilitare il controllo di sicurezza (/GS-) Controlli di runtime di base Impostazione predefinita Nota
Anche se queste impostazioni non sono consigliate, è possibile immaginare uno scenario in cui un utente consiglia di usare queste impostazioni per accelerare la codifica o per facilitare determinati ambienti di test.
In Visual Studio fare clic su Compila soluzione di>compilazione.
Se tutto va bene, le finestre di compilazione dovrebbero visualizzare un messaggio che indica che la compilazione è riuscita.
Individuare i file di app di esempio compilati
Nel Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto DisplayGreeting e selezionare Apri cartella in Esplora file.
Passare alla cartella Debug che contiene il file exe e simbolo pdb conforme per l'esempio. Ad esempio, si passa a C:\Projects\DisplayGreeting\Debug, se si tratta della cartella in cui sono archiviati i progetti.
Eseguire l'app di esempio con il difetto del codice
Fare doppio clic sul file exe per eseguire l'app di esempio.
Se viene visualizzata questa finestra di dialogo, selezionare Chiudi programma
Nella sezione successiva della procedura dettagliata si registrerà l'esecuzione dell'app di esempio per verificare se è possibile determinare il motivo per cui si verifica questa eccezione.
Sezione 2: Registrare una traccia dell'esempio "DisplayGreeting"
Nella sezione 2 si registrerà una traccia dell'app "DisplayGreeting" di esempio di comportamento errato
Per avviare l'app di esempio e registrare una traccia TTD, seguire questa procedura. Per informazioni generali sulla registrazione di tracce TTD, vedere Debug di viaggi temporali - Registrare una traccia
Eseguire WinDbg come amministratore per poter registrare le tracce di spostamento temporali.
In WinDbg selezionare Avvia debug>file>eseguibile (avanzato).
Immettere il percorso del file eseguibile in modalità utente da registrare o selezionare Sfoglia per passare al file eseguibile. Per informazioni sull'uso del menu eseguibile di avvio in WinDbg, vedere WinDbg - Avviare una sessione in modalità utente.
Selezionare la casella Record with Time Travel Debugging (Record with Time Travel Debugging ) per registrare una traccia all'avvio dell'eseguibile.
Fare clic su Configura e registra per avviare la registrazione.
Quando viene visualizzata la finestra di dialogo "Configura registrazione", fare clic su Registra per avviare il file eseguibile e avviare la registrazione.
Viene visualizzata la finestra di dialogo di registrazione che indica che è in corso la registrazione della traccia. Poco dopo, l'applicazione si arresta in modo anomalo.
Fare clic su Chiudi programma per chiudere la finestra di dialogo "DisplayGreeting ha smesso di funzionare".
Quando il programma si arresta in modo anomalo, il file di traccia verrà chiuso e scritto su disco.
Il debugger aprirà automaticamente il file di traccia e lo indicizzarà. L'indicizzazione è un processo che consente un debug efficiente del file di traccia. Questo processo di indicizzazione richiederà più tempo per i file di traccia di dimensioni maggiori.
(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.
Nota
Un fotogramma chiave è una posizione in una traccia usata per l'indicizzazione. I fotogrammi chiave vengono generati automaticamente. Le tracce più grandi conterranno più fotogrammi chiave.
A questo punto si è all'inizio del file di traccia e si è pronti a spostarsi avanti e indietro nel tempo.
Ora che è stata registrata una traccia TTD, è possibile riprodurre la traccia o lavorare con il file di traccia, ad esempio condividendola con un collega. Per altre informazioni sull'uso dei file di traccia, vedere Debug di viaggi temporali - Uso dei file di traccia
Nella sezione successiva di questo lab verrà analizzato il file di traccia per individuare il problema con il codice.
Sezione 3: Analizzare la registrazione dei file di traccia per identificare il problema del codice
Nella sezione 3 si analizzerà la registrazione dei file di traccia per identificare il problema del codice.
Configurare l'ambiente WinDbg
Aggiungere la posizione del simbolo locale al percorso del simbolo e ricaricare i simboli digitando i comandi seguenti.
.sympath+ C:\MyProjects\DisplayGreeting\Debug .reload
Aggiungere il percorso del codice locale al percorso di origine digitando il comando seguente.
.srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
Per poter visualizzare lo stato dello stack e delle variabili locali, nella barra multifunzione WinDbg selezionare Visualizza e variabili locali e Visualizza e Stack. Organizzare le finestre per consentirne la visualizzazione, il codice sorgente e le finestre dei comandi contemporaneamente.
Sulla barra multifunzione WinDbg selezionare Origine e File open source. Individuare il file DisplayGreeting.cpp e aprirlo.
Esaminare l'eccezione
Quando il file di traccia è stato caricato, vengono visualizzate informazioni che si sono verificate eccezioni.
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
Usare il comando dx per elencare tutti gli eventi nella registrazione. L'evento di eccezione è elencato negli eventi.
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 ...
Nota
In questa procedura dettagliata vengono usati tre periodi per indicare che l'output estraneo è stato rimosso.
Fare clic sull'evento Exception per visualizzare informazioni sull'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
Fare clic sul campo Eccezione per eseguire il drill-down sui dati delle eccezioni.
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
I dati dell'eccezione indicano che si tratta di un errore hardware generato dalla CPU. Fornisce inoltre il codice di eccezione di 0xc0000005 che indica che si tratta di una violazione di accesso. Ciò indica in genere che si sta tentando di scrivere in memoria a cui non si ha accesso.
Fare clic sul collegamento [Time Travel] nell'evento di eccezione per passare a tale posizione nella traccia.
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 ??
Si noti che in questo output lo stack e il puntatore di base puntano a due indirizzi molto diversi.
esp=00effe4c ebp=00520055
Ciò potrebbe indicare che il danneggiamento dello stack, probabilmente una funzione restituita e quindi danneggiata lo stack. Per convalidare questo problema, è necessario tornare indietro prima che lo stato della CPU sia danneggiato e verificare se è possibile determinare quando si è verificato il danneggiamento dello stack.
Esaminare le variabili locali e impostare un punto di interruzione del codice
Al momento dell'errore nella traccia è comune terminare alcuni passaggi dopo la vera causa nel codice di gestione degli errori. Con il viaggio del tempo è possibile tornare indietro un'istruzione alla volta, per individuare la vera causa radice.
Nella barra multifunzione Home usare il comando Esegui istruzione indietro per eseguire il passaggio indietro di tre istruzioni. A questo scopo, continuare a esaminare le finestre dello stack e della memoria.
La finestra di comando visualizzerà la posizione di spostamento del tempo e i registri mentre si esegue il passaggio indietro di tre istruzioni.
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:
Nota
In questa procedura dettagliata, l'output del comando mostra i comandi che possono essere usati invece delle opzioni del menu dell'interfaccia utente per consentire agli utenti con una preferenza di utilizzo della riga di comando di usare i comandi della riga di comando.
A questo punto della traccia lo stack e il puntatore di base hanno valori che hanno più senso, quindi sembra che ci si avvicini al punto nel codice in cui si è verificato il danneggiamento.
esp=00effd94 ebp=00effe44
Inoltre, la finestra variabili locali contiene valori dell'app di destinazione e la finestra del codice sorgente evidenzia la riga di codice che è pronta per essere eseguita a questo punto nella traccia.
Per ulteriori indagini, è possibile aprire una finestra di memoria per visualizzare il contenuto vicino all'indirizzo di memoria del puntatore di base di 0x00effe44.
Per visualizzare i caratteri ASCII associati, nella barra multifunzione Memoria selezionare Testo e quindi ASCII.
Anziché il puntatore di base che punta a un'istruzione che punta al testo del messaggio. Quindi qualcosa non è giusto qui, questo potrebbe essere vicino al momento in cui abbiamo danneggiato lo stack. Per ulteriori indagini, verrà impostato un punto di interruzione.
Nota
In questo esempio molto piccolo sarebbe piuttosto facile guardare nel codice, ma se ci sono centinaia di righe di codice e decine di subroutine le tecniche descritte qui possono essere usate per ridurre il tempo necessario per individuare il problema.
TTD e punti di interruzione
L'uso dei punti di interruzione è un approccio comune per sospendere l'esecuzione del codice a un certo evento di interesse. TTD consente di impostare un punto di interruzione e tornare indietro nel tempo fino a quando tale punto di interruzione non viene raggiunto dopo la registrazione della traccia. La possibilità di esaminare lo stato del processo dopo che si è verificato un problema, per determinare la posizione migliore per un punto di interruzione, consente flussi di lavoro di debug aggiuntivi univoci per TTD.
Punti di interruzione dell'accesso alla memoria
È possibile impostare punti di interruzione attivati quando si accede a una posizione di memoria. Usare il comando ba (break on access) con la sintassi seguente.
ba <access> <size> <address> {options}
Opzione | Descrizione |
---|---|
e | execute (quando la CPU recupera un'istruzione dall'indirizzo) |
r | lettura/scrittura (quando la CPU legge o scrive nell'indirizzo) |
w | write (quando la CPU scrive nell'indirizzo) |
Si noti che è possibile impostare solo quattro punti di interruzione dei dati in un determinato momento ed è necessario assicurarsi di allineare correttamente i dati o di non attivare il punto di interruzione (le parole devono terminare in indirizzi divisibile per 2, dwords deve essere divisibile per 4 e quadwords per 0 o 8).
Impostare l'interruzione sul punto di interruzione dell'accesso alla memoria per il puntatore di base
A questo punto della traccia si vuole impostare un punto di interruzione sull'accesso in memoria di scrittura al puntatore di base, ovvero ebp, che nell'esempio è 00effe44. A tale scopo, usare il comando ba usando l'indirizzo da monitorare. Si vuole monitorare le scritture per quattro byte, quindi si specifica w4.
0:000> ba w4 00effe44
Selezionare Visualizza e quindi Punti di interruzione per verificare che siano impostati come previsto.
Dal menu Home selezionare Torna indietro per tornare indietro nel tempo fino a quando non viene raggiunto il punto di interruzione.
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
Selezionare Visualizza e quindi Variabili locali. Nella finestra variabili locali è possibile notare che la variabile di destinazione ha solo parte del messaggio, mentre l'origine contiene tutto il testo. Queste informazioni supportano l'idea che lo stack sia danneggiato.
A questo punto è possibile esaminare lo stack di programmi per vedere quale codice è attivo. Nella barra multifunzione Visualizza selezionare Stack.
Poiché è molto improbabile che la funzione microsoft fornita wscpy_s() abbia un bug di codice simile al seguente, verrà esaminato ulteriormente nello stack. Lo stack mostra che Greeting!main chiama Greeting! GetCppConGreeting. Nell'esempio di codice molto piccolo è possibile aprire il codice a questo punto e probabilmente trovare l'errore abbastanza facilmente. Tuttavia, per illustrare le tecniche che possono essere usate con un programma più grande e più complesso, verrà impostato un nuovo punto di interruzione per approfondire l'analisi.
Impostare l'interruzione sul punto di interruzione di accesso per la funzione GetCppConGreeting
Usare la finestra punti di interruzione per cancellare il punto di interruzione esistente facendo clic con il pulsante destro del mouse sul punto di interruzione esistente e selezionando Rimuovi.
Determinare l'indirizzo di DisplayGreeting! Funzione GetCppConGreeting usando il comando dx .
0:000> dx &DisplayGreeting!GetCppConGreeting &DisplayGreeting!GetCppConGreeting : 0xb61720 [Type: void (__cdecl*)(wchar_t *,unsigned int)] [Type: void __cdecl(wchar_t *,unsigned int)]
Usare il comando ba per impostare un punto di interruzione sull'accesso alla memoria. Poiché la funzione verrà semplicemente letta dalla memoria per l'esecuzione, è necessario impostare un punto di interruzione r - lettura.
0:000> ba r4 b61720
Verificare che un punto di interruzione Lettura hardware sia attivo nella finestra dei punti di interruzione.
Come ci stiamo chiedendo le dimensioni della stringa di saluto, verrà impostata una finestra di controllo per visualizzare il valore di sizeof(greeting). Nella barra multifunzione Visualizza selezionare Espressione di controllo e specificare sizeof(greeting). Se il valore non è incluso nell'ambito verrà visualizzato nella finestra espressioni di controllo: impossibile associare il nome 'greeting'.
Nel menu Tempo di viaggio usare Il viaggio temporale per iniziare o usare il
!tt 0
comando per passare all'inizio della traccia.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
Nel menu Home selezionare Vai o usare il
g
comando per andare avanti nel codice fino a quando non viene raggiunto il punto di interruzione.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
Nel menu Home selezionare Esci indietro o usare il
g-u
comando per tornare a un passaggio.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)
Sembra che sia stata trovata la causa radice. La matrice di saluto dichiarata è di 50 caratteri, mentre sizeof(greeting) passato in GetCppConGreeting è 0x64, 100.
Come si esamina ulteriormente il problema relativo alle dimensioni, si noterà anche che il messaggio ha una lunghezza di 75 caratteri e è 76 quando si include la fine del carattere stringa.
HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!
Un modo per correggere il codice consiste nell'espandere le dimensioni della matrice di caratteri a 100.
std::array <wchar_t, 100> greeting{};
È anche necessario modificare sizeof(greeting) in size(greeting) in questa riga di codice.
GetCppConGreeting(greeting.data(), size(greeting));
Per convalidare queste correzioni, è possibile ricompilare il codice e verificare che venga eseguito senza errori.
Impostazione di un punto di interruzione tramite la finestra di origine
Un modo alternativo per eseguire questa indagine consiste nell'impostare un punto di interruzione facendo clic su qualsiasi riga di codice. Ad esempio, facendo clic sul lato destro della riga di definizione std:array nella finestra di origine verrà impostato un punto di interruzione.
Nel menu Tempo di viaggio usare il comando Time travel per iniziare per passare all'inizio della traccia.
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
Nella barra multifunzione Home fare clic su Vai per tornare indietro fino a quando non viene raggiunto il punto di interruzione.
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
Impostare l'interruzione sul punto di interruzione di accesso per la variabile greeting
Un altro modo alternativo per eseguire questa indagine consiste nell'impostare un punto di interruzione sulle variabili sospette ed esaminare il codice che li sta modificando. Ad esempio, per impostare un punto di interruzione nella variabile greeting nel metodo GetCppConGreeting, utilizzare questa procedura.
Questa parte della procedura dettagliata presuppone che si trovi ancora nel punto di interruzione della sezione precedente.
Da Visualizza e quindi Variabili locali. Nella finestra variabili locali, il messaggio di saluto è disponibile nel contesto corrente, quindi sarà possibile determinarne la posizione di memoria.
Usare il comando dx per esaminare la matrice di saluto .
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]
In questa traccia il messaggio di saluto si trova in memoria in ddf800.
Usare la finestra punti di interruzione per cancellare qualsiasi punto di interruzione esistente facendo clic con il pulsante destro del mouse sul punto di interruzione esistente e selezionando Rimuovi.
Impostare il punto di interruzione con il comando ba usando l'indirizzo di memoria che si vuole monitorare per l'accesso in scrittura.
ba w4 ddf800
Nel menu Tempo di viaggio usare il comando Time travel per iniziare per passare all'inizio della traccia.
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
Nel menu Home selezionare Vai per andare avanti fino al primo punto di accesso alla memoria della matrice di saluto.
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
In alternativa, è possibile che si sia recato alla fine della traccia e si sia lavorato in modo inverso nel codice per trovare l'ultimo punto nella traccia in cui è stata scritta la posizione di memoria della matrice.
Usare il TTD. Oggetti memoria per visualizzare l'accesso alla memoria
Un altro modo per determinare in quali punti della memoria di traccia è stato eseguito l'accesso, consiste nell'usare il TTD. Oggetti memoria e comando dx.
Usare il comando dx per esaminare la matrice di saluto .
0:000> dx &greeting &greeting : 0xddf800 [Type: std::array<wchar_t,50> *] [+0x000] _Elems : "꽘棶檙瞝???" [Type: wchar_t [50]]
In questa traccia il messaggio di saluto si trova in memoria in ddf800.
Usare il comando dx per esaminare i quattro byte in memoria a partire da tale indirizzo con l'accesso in lettura in scrittura.
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] ...
Fare clic su una delle occorrenze per visualizzare altre informazioni sull'occorrenza dell'accesso alla memoria.
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
Fare clic su [Time Travel] per TimeStart per posizionare la traccia nel momento in cui si trova.
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 si è interessati all'ultima occorrenza dell'accesso alla memoria di lettura/scrittura nella traccia, è possibile fare clic sull'ultimo elemento nell'elenco o accodarvi . Funzione Last() alla fine del 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
È quindi possibile fare clic su [Time Travel] per passare a tale posizione nella traccia ed esaminare ulteriormente l'esecuzione del codice a quel punto, usando le tecniche descritte in precedenza in questo lab.
Per altre informazioni sul TTD. Oggetti di memoria, vedere TTD. Oggetto Memory.
Riepilogo
In questo esempio molto piccolo il problema potrebbe essere stato determinato esaminando le poche righe di codice, ma nei programmi più grandi le tecniche presentate qui possono essere usate per ridurre il tempo necessario per individuare un problema.
Una volta registrata una traccia, i passaggi di traccia e riproduzione possono essere condivisi e il problema sarà riproducibile in qualsiasi PC.
Vedi anche
Debug di viaggi temporali - Panoramica
Debug del tempo di spostamento - Registrazione