Depuración de viajes en el tiempo - Tutorial de la aplicación de ejemplo
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.
- Capture un rastreo de viaje en el tiempo del programa con errores.
- 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.
- Use el comando !tt (viaje en el tiempo) para desplazarse a la posición del evento de excepción en el rastreo.
- 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.
- 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.
- Determine la dirección de memoria de la variable con el valor incorrecto.
- 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).
- Utilice g- para volver al último punto de acceso a memoria de la variable sospechosa.
- 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.
- 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.
- 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
- Sección 2: Registrar un rastreo del ejemplo "DisplayGreeting"
- Sección 3: Analizar el registro del archivo de rastreo para identificar el problema del código
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
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.
Desmarque las comprobaciones del ciclo de vida de desarrollo de seguridad (SDL).
Haga clic en Finalizar.
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; }
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.
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.
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.
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.
Si aparece este cuadro de diálogo, seleccione Cerrar programa
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).
Ejecute WinDbg como administrador, para poder registrar rastreos de viajes en el tiempo.
En WinDbg, seleccione Archivo>Iniciar depuración>Iniciar ejecutable (avanzado).
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.
Marque la casilla Registrar con depuración de viajes en el tiempo para registrar un rastreo cuando se inicie el ejecutable.
Haga clic en Configurar y registrar para iniciar el registro.
Cuando aparezca el cuadro de diálogo "Configurar registro", haga clic en Registrar para iniciar el ejecutable e iniciar el registro.
Aparece el cuadro de diálogo de registro indicando que se está registrando el rastreo. Poco después, la aplicación se bloquea.
Haga clic en Cerrar programa para descartar el cuadro de diálogo "DisplayGreeting ha dejado de funcionar".
Cuando el programa se bloquea, el archivo de rastreo se cerrará y se escribirá en el disco.
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.
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
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
Añada la ubicación de su código local a la ruta de origen escribiendo el siguiente comando.
.srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
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.
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
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
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.
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
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.
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.
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.
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.
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.
Para mostrar los caracteres ASCII asociados, en la cinta Memoria, seleccione Texto y ASCII.
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
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
Seleccione Ver y, a continuación, Puntos de interrupción para confirmar que están establecidos según lo previsto.
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
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.
En este punto podemos examinar la pila del programa para ver qué código está activo. En la cinta Ver, seleccione 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
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.
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)]
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
Confirme que un punto de interrupción de lectura de hardware está activo en la ventana de puntos de interrupción.
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). Si el valor no está en el ámbito, se mostrará la ventana de inspección: no se puede enlazar el nombre "greeting".
En el menú Viaje en el tiempo, use Viaje de tiempo para iniciar o use el
!tt 0
comando 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
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
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)
Parece que hemos encontrado la causa principal. La matriz de saludos que declaramos es de 50 caracteres de longitud, mientras que el tamañoof(greeting) que pasamos a GetCppConGreeting es 0x64, 100.
A medida que examinamos aún más el problema de tamaño, también observamos que el mensaje tiene 75 caracteres de longitud y es 76 cuando se incluye el final del carácter de cadena.
HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!
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));
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
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í.
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
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.
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.
Utilice el comando dx para examinar la matriz greeting.
0:000> dx &greeting &greeting : ddf800 : { size=50 } [Type: std::array<wchar_t,50> *] [<Raw View>] [Type: std::array<wchar_t,50>] [0] : 3 [Type: wchar_t] [1] : 0 [Type: wchar_t]
En este rastreo, greeting se encuentra en la memoria en ddf800.
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.
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
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
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.
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.
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] ...
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 OverwrittenValue : 0x0 SystemTimeStart : Monday, November 18, 2024 23:01:43.400 SystemTimeEnd : Monday, November 18, 2024 23:01:43.400
Haga clic en [Time Travel] para TimeStart para colocar el seguimiento en el momento dado.
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
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 SystemTimeStart : Monday, November 18, 2024 23:01:43.859 SystemTimeEnd : Monday, November 18, 2024 23:01:43.859
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 que se registra un seguimiento, se pueden compartir los pasos de seguimiento y reproducción, y el problema se podrá reproducir en cualquier equipo.
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