Compartir a través de


Depuración de viajes en el tiempo - Tutorial de la aplicación de ejemplo

Logotipo de depuración de viajes en el tiempo con un reloj.

En este laboratorio se presenta la depuración de viajes en el tiempo (TTD), utilizando un pequeño programa de ejemplo con un fallo de código. TTD se usa para depurar, identificar y causar el problema. Aunque el problema en este pequeño programa es fácil de encontrar, el procedimiento general puede utilizarse en códigos más complejos. Este procedimiento general se puede resumir de la siguiente manera.

  1. Capture un rastreo de viaje en el tiempo del programa con errores.
  2. Use el comando dx (Mostrar la expresión del modelo de objeto del depurador) para buscar el evento de excepción almacenado en el registro.
  3. Use el comando !tt (viaje en el tiempo) para desplazarse a la posición del evento de excepción en el rastreo.
  4. A partir de ese punto, el rastreo retrocede un solo paso hasta que el código de error en cuestión entra en el ámbito de aplicación.
  5. Con el código defectuoso en el ámbito de aplicación, mire los valores locales y desarrolle una hipótesis de una variable que puede contener un valor incorrecto.
  6. Determine la dirección de memoria de la variable con el valor incorrecto.
  7. Establezca un punto de interrupción de acceso a memoria (ba) en la dirección de la variable sospechosa utilizando el comando ba (Interrumpir el acceso).
  8. Utilice g- para volver al último punto de acceso a memoria de la variable sospechosa.
  9. Vea si esa ubicación, o algunas instrucciones antes, es el punto del error de código. Si es así, ya ha terminado. Si el valor incorrecto proviene de alguna otra variable, establezca otra interrupción en el punto de interrupción de acceso en la segunda variable.
  10. Utilice g- para retroceder hasta el último punto de acceso a memoria en la segunda variable sospechosa. Utilice g- para retroceder hasta el último punto de acceso a memoria en la segunda variable sospechosa. Si es así, ya ha terminado.
  11. Repita este proceso yendo hacia atrás hasta localizar el código que fijó el valor incorrecto que causó el error.

Aunque las técnicas generales descritas en este procedimiento se aplican a un amplio conjunto de problemas de código, hay problemas de código únicos que requerirán un enfoque único. Las técnicas ilustradas en el tutorial deberían servir para ampliar su conjunto de herramientas de depuración e ilustrarán algunas de las posibilidades que ofrece un rastreo de TTD.

Objetivos del laboratorio

Después de completar este laboratorio, será capaz de utilizar el procedimiento general con un rastreo de viaje en el tiempo para localizar problemas en el código.

Configuración del laboratorio

Necesitará el siguiente hardware para poder completar el laboratorio.

  • Un ordenador portátil o de sobremesa (host) con Windows 10 o Windows 11

Necesitará el siguiente software para poder completar el laboratorio.

  • El WinDbg. Para obtener información sobre cómo instalar WinDbg, consulte WinDbg - Instalación
  • Visual Studio para crear el código C++ de ejemplo.

El laboratorio tiene las siguientes tres secciones.

Sección 1: Compilar el código de ejemplo

En la sección 1, compilará el código de ejemplo mediante Visual Studio.

Creación de la aplicación de ejemplo en Visual Studio

  1. En Microsoft Visual Studio, haga clic en Archivo>Nuevo>Proyecto/Solución... y haga clic en las plantillas de Visual C++.

    Seleccione la aplicación de consola Win32.

    Proporcione un nombre de proyecto de DisplayGreeting y haga clic en Aceptar.

  2. Desmarque las comprobaciones del ciclo de vida de desarrollo de seguridad (SDL).

    Configuración del asistente para aplicaciones Win32 en Visual Studio.

  3. Haga clic en Finalizar.

  4. Pegue el texto siguiente en el panel DisplayGreeting.cpp de 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. En Visual Studio, haga clic en Proyecto>Propiedades de DisplayGreeting. A continuación, haga clic en C/C++ y Generación de código.

    Configure las siguientes propiedades.

    Configuración Valor
    Comprobación de seguridad Desactivar comprobación de seguridad (/GS-)
    Comprobaciones en tiempo de ejecución básicas Valor predeterminado

    Nota:

    Aunque estos ajustes no se recomiendan, es posible imaginar un escenario en el que alguien aconseje utilizarlos para agilizar la codificación o facilitar determinados entornos de prueba.

  6. En Visual Studio, haga clic en Compilar>Solución de compilación.

    Si todo va bien, las ventanas de compilación deberían mostrar un mensaje indicando que la compilación se ha realizado correctamente.

  7. Busque los archivos de la aplicación de ejemplo creada

    En el Explorador de soluciones, haga clic con el botón derecho en el proyecto DisplayGreeting y seleccione Abrir carpeta en el Explorador de archivos.

    Navegue hasta la carpeta Depurar que contiene el archivo exe y el archivo pdb de símbolos compilados para el ejemplo. Por ejemplo, navegaría a C:\Projects\DisplayGreeting\Debug, si esa es la carpeta en la que se almacenan los proyectos.

  8. Ejecución de la aplicación de ejemplo con el error de código

    Haga doble clic en el archivo exe para ejecutar la aplicación de ejemplo.

    Captura de pantalla de la consola que ejecuta el archivo DisplayGreeting.exe.

    Si aparece este cuadro de diálogo, seleccione Cerrar programa

    Captura de pantalla del cuadro de diálogo que muestra

    En la sección siguiente del tutorial, registraremos la ejecución de la aplicación de ejemplo para ver si podemos determinar por qué se está produciendo esta excepción.

Sección 2: Registrar un rastreo del ejemplo "DisplayGreeting"

En la sección 2, registrará un rastreo del comportamiento erróneo de la aplicación de ejemplo "DisplayGreeting".

Para iniciar la aplicación de ejemplo y registrar un rastreo de TTD, siga estos pasos. Para obtener información general sobre cómo registrar rastreos de TTD, consulte Depuración de viajes en el tiempo - Registrar un rastreo).

  1. Ejecute WinDbg como administrador, para poder registrar rastreos de viajes en el tiempo.

  2. En WinDbg, seleccione Archivo>Iniciar depuración>Iniciar ejecutable (avanzado).

  3. Introduzca la ruta del ejecutable en el modo de usuario que desea registrar o seleccione Examinar para navegar hasta el ejecutable. Para obtener información sobre cómo trabajar con el menú Iniciar ejecutable en WinDbg, consulte WinDbg - Iniciar una sesión en modo usuario.

    Captura de pantalla de WinDbg con la casilla

  4. Marque la casilla Registrar con depuración de viajes en el tiempo para registrar un rastreo cuando se inicie el ejecutable.

  5. Haga clic en Configurar y registrar para iniciar el registro.

  6. Cuando aparezca el cuadro de diálogo "Configurar registro", haga clic en Registrar para iniciar el ejecutable e iniciar el registro.

    Captura de pantalla de WinDbg que muestra el cuadro de diálogo Configurar registro con la ruta de acceso establecida en temp.

  7. Aparece el cuadro de diálogo de registro indicando que se está registrando el rastreo. Poco después, la aplicación se bloquea.

  8. Haga clic en Cerrar programa para descartar el cuadro de diálogo "DisplayGreeting ha dejado de funcionar".

    Cuadro de diálogo que muestra que la aplicación DisplayGreeting ha dejado de funcionar.

  9. Cuando el programa se bloquea, el archivo de rastreo se cerrará y se escribirá en el disco.

    Captura de pantalla de la salida de WinDbg que muestra fotogramas clave 1/1 indexados.

  10. El depurador abrirá automáticamente el archivo de rastreo y lo indexará. La indexación es un proceso que permite la depuración eficaz del archivo de rastreo. Este proceso de indexación tardará más en el caso de archivos de rastreo de mayor tamaño.

    (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 fotograma clave es una ubicación en un rastreo que se utiliza para indexar. Los fotogramas clave se generan automáticamente. Los rastreos más grandes contendrán más fotogramas clave.

  1. En este punto se encuentra al principio del archivo de rastreo y estás listo para viajar hacia adelante y hacia atrás en el tiempo.

    Ahora que ha registrado un rastreo de TTD, puede volver a reproducir el rastreo o trabajar con el archivo de rastreo, por ejemplo, compartiéndolo con un compañero de trabajo. Para obtener más información sobre cómo trabajar con archivos de rastreo, consulte Depuración de viajes en el tiempo - Trabajo con archivos de rastreo).

En la siguiente sección de este laboratorio analizaremos el archivo de rastreo para localizar el problema con nuestro código.

Sección 3: Analizar el registro del archivo de rastreo para identificar el problema del código

En la Sección 3, analizará el registro del archivo de rastreo para identificar el problema del código.

Configuración del entorno de WinDbg

  1. Añada su ubicación local de símbolos a la ruta de símbolos y vuelva a cargar los símbolos, escribiendo los siguientes comandos.

    .sympath+ C:\MyProjects\DisplayGreeting\Debug
    .reload
    
  2. Añada la ubicación de su código local a la ruta de origen escribiendo el siguiente comando.

    .srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
    
  3. Para poder ver el estado de la pila y las variables locales, en la cinta de opciones de WinDbg, seleccione Ver y Variables locales y Ver y Pila. Organice las ventanas para poder verlas, el código fuente y las ventanas de comandos al mismo tiempo.

  4. En la cinta de opciones de WinDbg, seleccione Origen y Abrir archivo de origen. Busque el archivo DisplayGreeting.cpp y ábralo.

Examinar la excepción

  1. Cuando se carga el archivo de rastreo, muestra información de que se produjo una excepción.

    2fa8.1fdc): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68ef8100 ebx=00000000 ecx=77a266ac edx=69614afc esi=6961137c edi=004da000
    eip=77a266ac esp=0023f9b4 ebp=0023fc04 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:0023fac0=00000000
    
  2. Use el comando dx para enumerar todos los eventos del registro. El evento de excepción se muestra en los 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
    ...
    
    

    Nota:

    En este tutorial se usan tres períodos para indicar que se quitó la salida extraña.

  3. Haga clic en el evento Excepción para mostrar información sobre ese evento de 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. Haga clic en el campo Excepción para profundizar en los datos de la excepción.

    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
    

    Los datos de la excepción indican que se trata de un fallo de hardware producido por la CPU. También proporciona el código de excepción 0xc0000005 que indica que se trata de una violación de acceso. Esto suele indicar que estamos intentando escribir en memoria a la que no tenemos acceso.

  5. Haga clic en el enlace [Viaje en el tiempo] del evento de excepción para desplazarse a esa posición en el rastreo.

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

    Cabe destacar que la pila y el puntero base apuntan a dos direcciones muy diferentes.

    esp=00effe4c ebp=00520055
    

    Esto podría indicar que una corrupción de la pila, posiblemente una función regresó y luego corrompió la pila. Para validarlo, tenemos que retroceder hasta antes de que se corrompiera el estado de la CPU y ver si podemos determinar cuándo se produjo la corrupción de la pila.

Examinar las variables locales y establecer un punto de interrupción de código

En el punto de fallo en el rastreo es común terminar unos pasos después de la verdadera causa en el código de manejo de errores. Con el viaje en el tiempo podemos retroceder una instrucción a la vez, para localizar la verdadera causa raíz.

  1. Desde la cinta Inicio utilice el comando Volver atrás para retroceder tres instrucciones. Mientras lo hace, siga examinando la pila y las ventanas de memoria.

    La ventana de comandos mostrará la posición del viaje en el tiempo y los registros a medida que retrocede tres instrucciones.

    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:

    En este tutorial, la salida de comandos muestra los comandos que se pueden utilizar en lugar de las opciones del menú de la interfaz de usuario para que los usuarios que prefieran utilizar la línea de comandos puedan hacerlo.

  2. En este punto del rastreo nuestra pila y puntero base tienen valores que tienen más sentido, por lo que parece que nos hemos acercado al punto del código donde se produjo la corrupción.

    esp=00effd94 ebp=00effe44
    

    También es interesante que la ventana local contiene valores de nuestra aplicación de destino y la ventana de código fuente está resaltando la línea de código que está lista para ser ejecutada en este punto del rastreo.

    Captura de pantalla de WinDbg que muestra la ventana Variables locales con la salida ASCII de memoria y la ventana Código fuente.

  3. Para investigar aún más, podemos abrir una ventana de memoria para ver el contenido cerca de la dirección de memoria del puntero base de 0x00effe44.

  4. Para mostrar los caracteres ASCII asociados, en la cinta Memoria, seleccione Texto y ASCII.

    Captura de pantalla de la vista previa de WinDbg que muestra la salida ASCII de memoria y la ventana Código fuente.

  5. En lugar de que el puntero base apunte a una instrucción, apunta al texto de nuestro mensaje. Así que algo no está bien aquí, esto puede estar cerca del momento en que hemos dañado la pila. Para investigar aún más, estableceremos un punto de interrupción.

Nota:

En esta muestra tan pequeña sería bastante fácil simplemente mirar en el código, pero si hay cientos de líneas de código y docenas de subrutinas las técnicas descritas aquí pueden utilizarse para disminuir el tiempo necesario para localizar el problema.

TTD y puntos de interrupción

El uso de puntos de interrupción es un enfoque común para detener la ejecución de código en algún evento de interés. TTD le permite establecer un punto de interrupción y viajar hacia atrás en el tiempo hasta que ese punto de interrupción se alcanza después de registrar el rastreo. La capacidad de examinar el estado del proceso después de que se haya producido un problema, para determinar la mejor ubicación para un punto de interrupción, permite flujos de trabajo de depuración adicionales exclusivos de TTD.

Puntos de interrupción de acceso a memoria

Puede establecer puntos de interrupción que se activan cuando se accede a una ubicación de memoria. Use el comando ba (interrumpir el acceso), con la siguiente sintaxis.

ba <access> <size> <address> {options}
Opción Descripción

e

ejecutar (cuando la CPU captura una instrucción de la dirección)

r

leer/escribir (cuando la CPU lee o escribe en la dirección)

t

escribir (cuando la CPU escribe en la dirección)

Tenga en cuenta que solo puede establecer cuatro puntos de interrupción de datos en un momento dado y depende de usted asegurarse de que está alineando sus datos correctamente o no activará el punto de interrupción (las palabras deben terminar en direcciones divisibles por 2, las dwords deben ser divisibles por 4, y las quadwords por 0 u 8).

Establecer el punto de interrupción de acceso a memoria para el puntero base

  1. En este punto del rastreo nos gustaría establecer un punto de interrupción en el acceso de memoria de escritura al puntero base - ebp que en nuestro ejemplo es 00effe44. Para ello, use el comando ba mediante la dirección que queremos supervisar. Queremos supervisar las escrituras de cuatro bytes, por lo que se especifica w4.

    0:000> ba w4 00effe44
    
  2. Seleccione Ver y, a continuación, Puntos de interrupción para confirmar que están establecidos según lo previsto.

    Captura de pantalla de la ventana Puntos de interrupción de WinDbg que muestra un único punto de interrupción.

  3. En el menú Inicio, seleccione Volver para retroceder en el tiempo hasta alcanzar el punto de interrupción.

    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. Seleccione Ver y, a continuación, Variables locales. En la ventana de locales podemos ver que la variable destino tiene sólo parte del mensaje, mientras que el origen contiene todo el texto. Esta información admite la idea de que la pila estaba dañada.

    Captura de pantalla de WinDbg que muestra la ventana Variables locales.

  5. En este punto podemos examinar la pila del programa para ver qué código está activo. En la cinta Ver, seleccione Pila.

    Captura de pantalla de WinDbg que muestra la ventana Pila.

Como es muy poco probable que la función wscpy_s() proporcionada por Microsoft tenga un error de código como este, miramos más allá en la pila. La pila muestra que Greeting!main llama a Greeting!GetCppConGreeting. En nuestro pequeño ejemplo de código, podríamos abrir el código en este punto y encontrar el error con bastante facilidad. Pero para ilustrar las técnicas que pueden utilizarse con programas más grandes y complejos, estableceremos un nuevo punto de interrupción para investigar más a fondo.

Establecimiento del punto de interrupción de acceso para la función GetCppConGreeting

  1. Utilice la ventana de puntos de interrupción para borrar el punto de interrupción existente haciendo clic con el botón derecho del ratón sobre el punto de interrupción existente y seleccionando Eliminar.

  2. Determine la dirección de la función DisplayGreeting!GetCppConGreeting utilizando el 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. Utilice el comando ba para establecer un punto de interrupción en el acceso a la memoria. Debido a que la función solo se leerá de la memoria para su ejecución, necesitamos establecer un punto de interrupción r - read.

    0:000> ba r4 b61720
    
  4. Confirme que un punto de interrupción de lectura de hardware está activo en la ventana de puntos de interrupción.

    Captura de pantalla de la ventana de puntos de interrupción de WinDbg que muestra un único punto de interrupción de lectura de hardware.

  5. Como nos estamos preguntando por el tamaño de la cadena de saludo, estableceremos una ventana de vigilancia para que muestre el valor de sizeof(greeting). En la cinta Ver, seleccione Inspección y proporcione sizeof(greeting).

    Captura de pantalla de WinDbg que muestra la ventana Inspección de variables locales.

  6. En el menú Viaje en el tiempo, use Viaje de tiempo para iniciar o use el !tt 0comando para pasar al inicio del rastreo.

    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. En el menú Inicio, seleccione Ir o use el comando g para avanzar en el código hasta que se alcance el punto de interrupción.

    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. En el menú Inicio, seleccione Paso atrás o utilice el comando g-u para retroceder un paso.

    0:000> g-u
    Time Travel Position: 4B:1AA
    eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046
    eip=00b61917 esp=00ddf7ac ebp=00ddf864 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!main+0x27:
    00b61917 e8def7ffff      call    DisplayGreeting!ILT+245(?GetCppConGreetingYAXPA_WIZ) (00b610fa)
    
  9. Parece que hemos encontrado la causa principal. La matriz g que declaramos tiene 50 caracteres de longitud, mientras que el sizeof(g) que pasamos a GetCppConGreeting es 0x64, 100).

    Captura de pantalla de WinDbg mostrando el código DisplayGreeting con una ventana Inspección de variables locales mostrando 0x64.

    Si nos fijamos más en la cuestión del tamaño, también observamos que el mensaje tiene 75 caracteres de longitud, 76 incluyendo el carácter de fin de cadena.

    HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!
    
  10. Una forma de arreglar el código sería ampliar el tamaño de la matriz de caracteres a 100.

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

    Y también tenemos que cambiar sizeof(greeting) por size(greeting) en esta línea de código.

     GetCppConGreeting(greeting.data(), size(greeting));
    
  11. Para validar estas correcciones, podríamos recompilar el código y confirmar que se ejecuta sin errores.

Establecimiento de un punto de interrupción mediante la ventana de origen

  1. Una forma alternativa de realizar esta investigación sería establecer un punto de interrupción haciendo clic en cualquier línea de código. Por ejemplo, si hace clic en la parte derecha de la línea de definición de std:array en la ventana de código fuente, se establecerá un punto de interrupción allí.

    Captura de pantalla de la ventana Origen en WinDbg con un punto de interrupción establecido en std::array.

  2. En el menú Viaje en el tiempo, use el comando Viaje en el tiempo para comenzar para pasar al inicio del rastreo.

    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. En la cinta de Inicio, haga clic en Ir para retroceder hasta que se alcance el punto de interrupción.

    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
    

Establecer el punto de interrupción en el punto de interrupción de acceso para la variable greeting

Otra forma alternativa de realizar esta investigación, sería establecer un punto de interrupción en las variables sospechosas y examinar qué código las está modificando. Por ejemplo, para establecer un punto de interrupción en la variable greeting del método GetCppConGreeting, use este procedimiento.

En esta parte del tutorial se supone que todavía se encuentra en el punto de interrupción de la sección anterior.

  1. Desde Ver y luego Variables locales. En la ventana de variables locales, greeting está disponible en el contexto actual, por lo que podremos determinar su ubicación de memoria.

  2. Utilice el comando dx para examinar la matriz greeting.

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

    En este rastreo, greeting se encuentra en la memoria en ddf800.

  3. Utilice la ventana de puntos de interrupción para borrar cualquier punto de interrupción existente haciendo clic con el botón derecho del ratón sobre el punto de interrupción existente y seleccionando Eliminar.

  4. Establezca el punto de interrupción con el comando ba utilizando la dirección de memoria que queremos monitorizar para el acceso de escritura.

    ba w4 ddf800
    
  5. En el menú Viaje en el tiempo, use el comando Viaje en el tiempo para comenzar para pasar al inicio del rastreo.

    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. En el menú Inicio, seleccione Ir para avanzar hasta el primer punto de acceso a la memoria de la 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
    

    Alternativamente, podríamos haber ido hasta el final del rastreo y trabajar en sentido inverso a través del código para encontrar ese último punto del rastreo en el que se escribió la ubicación de memoria de la matriz.

Utilice los objetos TTD.Memory para ver el acceso a la memoria

Otra forma de determinar en qué puntos de la memoria de rastreo se ha accedido, es utilizar los objetos TTD.Memory y el comando dx.

  1. Utilice el comando dx para examinar la matriz greeting.

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

    En este rastreo, greeting se encuentra en la memoria en ddf800.

  2. Utilice el comando dx para ver los cuatro bytes en memoria que comienzan en esa dirección con el acceso de lectura y escritura.

    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. Haga clic en cualquiera de las apariciones para mostrar más información sobre esa aparición del acceso a la 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. Haga clic en [Viaje en el tiempo] para situar el rastreo en el punto en el tiempo.

    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. Si estamos interesados en la última aparición del acceso a memoria de lectura/escritura en el rastreo, podemos hacer clic en el último elemento de la lista o añadir la función .Last() al final 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. A continuación, podemos hacer clic en [Viaje en el tiempo] para desplazarnos a esa posición en el rastreo y examinar más a fondo la ejecución del código en ese punto, utilizando las técnicas descritas anteriormente en este laboratorio.

Para más información sobre los objetos TTD.Memory, consulte Objeto TTD.Memory.

Resumen

En esta pequeña muestra, el problema podría haberse detectado observando las pocas líneas de código, pero en programas más grandes, las técnicas aquí presentadas pueden utilizarse para reducir el tiempo necesario para localizar un problema.

Una vez registrado el rastreo, este y los pasos de la reproducción pueden compartirse, y el problema podrá reproducirse a petición en cualquier PC.

Consulte también

Depuración de viajes en el tiempo - Información general

Depuración de viajes en el tiempo - Registro

Depuración de viajes en el tiempo - Reproducción de un rastreo

Depuración de viajes en el tiempo - Trabajo con archivos de rastreo