Condividi tramite


Debug di viaggi temporali - Procedura dettagliata dell'app di esempio

Logo di debug del tempo di viaggio con un orologio.

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.

  1. Acquisire una traccia di spostamento temporale del programma non riuscito.
  2. Usare il comando dx (Display Debugger Object Model Expression) per trovare l'evento di eccezione archiviato nella registrazione.
  3. Usare il comando !tt (tempo di spostamento) per spostarsi nella posizione dell'evento di eccezione nella traccia.
  4. Da quel punto nel singolo passaggio di traccia all'indietro fino a quando il codice di errore in questione non rientra nell'ambito.
  5. 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.
  6. Determinare l'indirizzo di memoria della variabile con il valore non corretto.
  7. Impostare un punto di interruzione di accesso alla memoria (ba) sull'indirizzo della variabile sospetta usando il comando ba (Break on Access).
  8. Usare g- per eseguire il runback all'ultimo punto di accesso alla memoria della variabile sospetta.
  9. 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.
  10. 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.
  11. 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

Nella sezione 1 si compilerà il codice di esempio usando Visual Studio.

Creare l'app di esempio in Visual Studio

  1. 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.

  2. Deselezionare i controlli Security Development Lifecycle (SDL).

    Impostazioni della Creazione guidata applicazione Win32 in Visual Studio.

  3. Fare clic su Fine.

  4. 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;
    }
    
  5. 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.

  6. 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.

  7. 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.

  8. Eseguire l'app di esempio con il difetto del codice

    Fare doppio clic sul file exe per eseguire l'app di esempio.

    Screenshot della console che esegue il file DisplayGreeting.exe.

    Se viene visualizzata questa finestra di dialogo, selezionare Chiudi programma

    Screenshot della finestra di dialogo che mostra 'DisplayGreeting.exe ha smesso di funzionare'.

    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

  1. Eseguire WinDbg come Amministrazione istrator, in modo da poter registrare le tracce di viaggio temporali.

  2. In WinDbg selezionare Avvia debug>file>eseguibile (avanzato).

  3. 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.

    Screenshot di WinDbg con la casella di controllo

  4. Selezionare la casella Record with Time Travel Debugging (Record with Time Travel Debugging ) per registrare una traccia all'avvio dell'eseguibile.

  5. Fare clic su Configura e registra per avviare la registrazione.

  6. Quando viene visualizzata la finestra di dialogo "Configura registrazione", fare clic su Registra per avviare il file eseguibile e avviare la registrazione.

    Screenshot di WinDbg che mostra la finestra di dialogo Configura registrazione con il percorso impostato su temp.

  7. 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.

  8. Fare clic su Chiudi programma per chiudere la finestra di dialogo "DisplayGreeting ha smesso di funzionare".

    Finestra di dialogo che mostra che l'app DisplayGreeting non è più funzionante.

  9. Quando il programma si arresta in modo anomalo, il file di traccia verrà chiuso e scritto su disco.

    Screenshot dell'output winDbg che mostra 1/1 fotogrammi chiave indicizzati.

  10. 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.

  1. 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

  1. Aggiungere la posizione del simbolo locale al percorso del simbolo e ricaricare i simboli digitando i comandi seguenti.

    .sympath+ C:\MyProjects\DisplayGreeting\Debug
    .reload
    
  2. Aggiungere il percorso del codice locale al percorso di origine digitando il comando seguente.

    .srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
    
  3. 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.

  4. Sulla barra multifunzione WinDbg selezionare Origine e File open source. Individuare il file DisplayGreeting.cpp e aprirlo.

Esaminare l'eccezione

  1. 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
    
  2. 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.

  3. 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
    
  4. 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.

  5. 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.

  1. 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.

  2. 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.

    Screenshot di WinDbg che mostra la finestra Variabili locali con l'output ASCII della memoria e la finestra Codice sorgente.

  3. Per ulteriori indagini, è possibile aprire una finestra di memoria per visualizzare il contenuto vicino all'indirizzo di memoria del puntatore di base di 0x00effe44.

  4. Per visualizzare i caratteri ASCII associati, nella barra multifunzione Memoria selezionare Testo e quindi ASCII.

    Screenshot dell'anteprima di WinDbg che mostra l'output ASCII della memoria e la finestra Codice sorgente.

  5. 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

  1. 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
    
  2. Selezionare Visualizza e quindi Punti di interruzione per verificare che siano impostati come previsto.

    Screenshot della finestra Punti di interruzione WinDbg che mostra un singolo punto di interruzione.

  3. 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
    
  4. 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.

    Screenshot di WinDbg che mostra la finestra Variabili locali.

  5. A questo punto è possibile esaminare lo stack di programmi per vedere quale codice è attivo. Nella barra multifunzione Visualizza selezionare Stack.

    Screenshot di WinDbg che mostra la finestra 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

  1. 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.

  2. 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)]
    
  3. 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
    
  4. Verificare che un punto di interruzione Lettura hardware sia attivo nella finestra dei punti di interruzione.

    Screenshot della finestra Punti di interruzione winDbg che mostra un singolo punto di interruzione di lettura hardware.

  5. 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).

    Screenshot di WinDbg che mostra una finestra Espressioni di controllo variabili locali.

  6. Nel menu Tempo di viaggio usare Il viaggio temporale per iniziare o usare il !tt 0comando 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
    
  7. 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
    
  8. 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)
    
  9. Sembra che sia stata trovata la causa radice. La matrice di saluto dichiarata è di 50 caratteri, mentre sizeof(greeting) passato in GetCppConGreeting è 0x64, 100).

    Screenshot di WinDbg che mostra il codice DisplayGreeting con una finestra Espressioni di controllo locali che mostra 0x64.

    Come si esamina ulteriormente il problema relativo alle dimensioni, si noterà anche che il messaggio ha una lunghezza di 75 caratteri, 76 inclusi la fine del carattere stringa.

    HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!
    
  10. 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));
    
  11. 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

  1. 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.

    Screenshot della finestra Origine in WinDbg con un punto di interruzione impostato su std::array.

  2. 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
    
  3. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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
    
  5. 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
    
  6. 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.

  1. 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.

  2. 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]           
        ...         
    
  3. 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
    
  4. Fare clic su [Tempo di viaggio] per posizionare la traccia nel 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 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
    
  6. È 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, è possibile condividere i passaggi di traccia e riproduzione e il problema sarà riproducibile su richiesta su qualsiasi PC.

Vedi anche

Debug di viaggi temporali - Panoramica

Debug del tempo di spostamento - Registrazione

Debug del tempo di spostamento - Riprodurre una traccia

Debug di viaggi temporali - Uso dei file di traccia