2012-06-06 13 views
8

A menudo tengo que depurar el bloqueo de programas C++ en Windows donde puedo reproducir el bloqueo, pero es difícil determinar qué secuencia de instrucciones causó el bloqueo (por ejemplo, otro subproceso que sobrescribe la memoria del subproceso que falla). Incluso una pila de llamadas no ayuda en ese caso. Por lo general, recurro a reducir la causa del error comentando secciones del código fuente, pero esto es muy tedioso.Cómo iniciar sesión o reproducir líneas o instrucciones ejecutadas inmediatamente antes de un bloqueo

¿Alguien conoce una herramienta para Windows que pueda informar o reproducir las últimas pocas líneas de código fuente o instrucciones de código de máquina ejecutadas en todos los hilos inmediatamente antes de un bloqueo? Es decir. algo así como la capacidad de depuración inversa de gdb o algo así como BugTrapper de Mutek (que ya no está disponible). Estoy buscando una herramienta liberada y estable (conozco el 'Validador de errores' de SoftwareVerify y el Reproductor de seguimiento IDA Pro 6.3 de Hexray, los cuales aún están en programas beta cerrados).

Lo que ya probé fueron los comandos de rastreo WinDbg wt y ta @$ra, pero ambos comandos tienen la desventaja de que se detienen automáticamente después de unos segundos. Necesito comandos de rastreo que se ejecutan hasta que ocurre el bloqueo y que rastrean todos los hilos del programa en ejecución.

NOTA: estoy no buscando una herramienta de depuración diseñado para solucionar un problema particular, como gflags, PageHeap, Memoria del validador, purifica, etc. Estoy buscando la herramienta liberada y estable para rastrear o volver a reproducir en el nivel de instrucción.

+2

Simplemente ejecute su código en un depurador y debería indicarle la ubicación en la fuente de origen que desencadena el bloqueo. –

+0

@Als: Encontrar la ubicación del choque generalmente no es un problema; el problema es cómo determinar * por qué * ha tenido lugar el bloqueo. Por ejemplo, puede haber un bloqueo porque otro subproceso ha sobrescrito las regiones de memoria del subproceso bloqueado (por ejemplo, las escrituras no válidas en el montón o en el área de la pila). Entonces, sería realmente útil ver las últimas líneas o instrucciones ejecutadas por el programa para determinar qué hilo ha escrito algo en la región de memoria dañada. He adaptado mi pregunta anterior para aclarar esto. –

+0

posible duplicado de [Depuración revés] (http://stackoverflow.com/questions/221806/debugging-backwards) – Ferruccio

Respuesta

4

Encontré una solución: "reprocesar la repetición" usando VMware Workstation y Visual Studio 2010.Configurarlo lleva mucho tiempo, pero se le recompensa con un depurador de Visual Studio C++ que puede depurar al revés en el tiempo. Aquí hay un video que muestra cómo funciona la depuración de reproducción: http://blogs.vmware.com/workstation/2010/01/replay-debugging-try-it-today.html.

Una desventaja de la solución es que VMware aparentemente ha suspendido la depuración de la repetición en las últimas versiones de VMware. Además, solo ciertos tipos de procesadores parecen ser compatibles con la repetición. No he encontrado ninguna lista completa de procesadores compatibles; Probé las funciones de reproducción en tres de mis PC: la reproducción no funcionó en Core i7 200; la reproducción funcionó en un Core2 6700 y en un Core2 Q9650.

Realmente espero que VMware reconsidere e introduzca de nuevo la depuración de la repetición en futuras versiones de VMware Workstation, porque esto realmente agrega una nueva dimensión a la depuración.

Para aquellos de ustedes que están interesados, aquí es una descripción de cómo se puede configurar un entorno para la reproducción de depuración:

En la descripción siguiente, "depuración local" significa que Visual Studio y VMware están instalados en el misma PC. La "depuración remota" significa que Visual Studio y VMware están instalados en diferentes PC.

  • Instalar Visual Studio 2010 con SP1 en el sistema host.

  • Asegúrese de que Visual Studio se haya configurado para usar los servidores de símbolos de Microsoft. (En "Herramientas | Opciones | Depuración | Símbolos").

  • En el sistema host, instale "Herramientas de depuración para Windows".

  • Instalar VMware Workstation 7.1. (La versión 8.0 ya no contiene la función de depuración de repetición). Esto también instalará un complemento en Visual Studio.

  • Instale una máquina virtual (VM) en VMware con Windows XP SP3.

  • Si la aplicación bajo prueba es una versión de depuración, instale las DLL de depuración de Visual Studio en la VM. (Consulte http://msdn.microsoft.com/en-us/library/dd293568.aspx para obtener instrucciones sobre cómo hacerlo, pero use una configuración de "Depuración" en lugar de "Liberar").

  • Copie "gflags.exe" del directorio "Herramientas de depuración para Windows" del host en la máquina virtual, ejecute gflags.exe en la máquina virtual, seleccione "Desactivar la búsqueda de pilas del núcleo" en la pestaña "Registro del sistema" y presione DE ACUERDO. Reinicia la VM.

  • Copie todos los archivos EXE y DLL de la aplicación bajo prueba a la máquina virtual y asegúrese de que puede iniciar la aplicación y reproducir el problema.

  • Apague la máquina virtual y cree una instantánea (mediante el elemento de menú contextual "Tomar instantánea" en VMware Workstation).

  • (Sólo para la depuración remota :) Iniciar el siguiente comando en el Estudio Visual PC e introduzca un código de acceso arbitraria:

    C: \ Archivos de programa \ VMware \ VMware Workstation \ Visual Studio integrado depurador \ dclProxy.exe nombre de host

    Reemplazar nombre de host con el nombre de la PC.

  • (Solo para la depuración remota :) Crea una grabación manualmente para la máquina virtual. Es decir. inicie sesión en el sistema operativo de la VM, inicie la grabación (a través del menú contextual "Grabar"), ejecute la aplicación bajo prueba y realice las acciones necesarias para reproducir el problema. Luego detén y guarda la grabación.

  • de inicio de Visual Studio e ir a los siguientes valores, y establecer "VMware | Opciones generales | | Replay Depuración de VM":

    • "local o remoto" se debe establecer en "local" para los locales depuración o "Remoto" para la depuración remota.
    • "Máquina virtual" debe establecerse en la ruta al archivo .vmx de la VM.
    • "Contraseña remota de Machione" se debe configurar para que sea la contraseña que utilizó anteriormente (solo para la eliminación remota de errores).
    • "Grabar para reproducir" debe establecerse en un nombre de grabación que haya creado previamente con VMware.
    • "Ruta de búsqueda de ejecutable de host" debe establecerse en un directorio en el que guarde las DLL que la aplicación bajo prueba necesita y que Visual Studio necesita para mostrar los seguimientos de pila correctos.

    Presione "Aplicar".

  • Ir a "VMware | Opciones | Replay depuración en VM | Pre-registro de eventos", y establezca los siguientes valores:

    • "Base de instantáneas para la grabación": nombre de la instantánea creada previamente.

    Presione "Aceptar".

  • (Para la depuración local :) En Visual Studio, seleccione "VMware | Create Recording for Replay"; esto reinicia la VM. Inicie sesión en la máquina virtual, ejecute la aplicación bajo prueba y realice las acciones necesarias para reproducir el problema. Luego detén y guarda la grabación.

  • Seleccione "VMware | Start Replay Debugging". VMware ahora reinicia automáticamente la VM y la aplicación bajo prueba y reproduce las acciones registradas. Espere hasta que la aplicación se cuelgue; el depurador de Visual Studio se activa automáticamente.

  • En el depurador de Visual Studio, establezca un punto de interrupción en una ubicación donde cree que la aplicación ha estado antes del bloqueo. Luego, seleccione "VMware | Reverse Continue". El depurador ahora ejecuta hacia atrás hasta el punto de interrupción. Esta operación puede llevar algo de tiempo porque la VM se reiniciará y reproducirá hasta que se alcance su punto de interrupción. (Puede acelerar esta operación agregando una instantánea unos segundos antes de que ocurra el bloqueo al grabar el escenario. Puede agregar instantáneas adicionales durante la depuración de la repetición).

  • Una vez que VMware ha reproducido la VM en su punto de interrupción, puede utilizar "Paso a paso" y "Paso a paso" para avanzar desde su punto de interrupción, es decir, puede reproducir el historial de eventos registrados, hasta que llegue a un punto donde pueda identificar la razón por la cual se bloqueó su aplicación.

Más información: http://www.replaydebugging.com/

+0

Aunque la respuesta de @Stefan (quien obtuvo el bounty) también es correcta, estoy aceptando mi propia respuesta, porque la solución de VMware es mucho más barato que BMC AppSight, y probablemente todavía funcione durante unos años (hasta que los requisitos del procesador de VMWare 7.1 ya no puedan cumplirse). –

9

En caso de que se enfrente con another thread overwriting memory of the crashing thread, es útil usar gflags (GFlags and PageHeap). En lugar de decirle algunas líneas que se han ejecutado antes de un bloqueo, le dirá exactamente el lugar donde su algoritmo ha sobreescrito un bloque de memoria asignado correctamente.

En primer lugar, activar este tipo de cheque:

gflags /p /enable your_app.exe /full o
gflags /p /enable your_app.exe /full /backwards

Compruebe que ha activado correctamente
gflags /p

plazo que la aplicación y recoger los archivos de volcado

y luego deshabilitar la comprobación con gflags:

gflags /p /disable your_app.exe


Actualización 1

It does not immediately detect problems like *p = 0; where p is an invalid pointer
Al menos se detectan algunos problemas.
Por ejemplo:

#include <stdio.h> 
int main(int argc, char *argv[]) 
{ 
    int *p = new int; 
    printf("1) p=%p\n",p); 
    *p = 1; 
    delete p; 
    printf("2) p=%p\n",p); 
    *p = 2; 
    printf("Done\n"); 
    return 0;  
} 

Cuando corro con gflags permitió consigo un archivo de volcado y el problema se identifica correctamente:

STACK_TEXT: 
0018ff44 00401215 00000001 03e5dfb8 03dfdf48 mem_alloc_3!main+0x5b [c:\src\tests\test.cpp\mem_alloc\mem_alloc\mem_alloc.3.cpp @ 11] 
0018ff88 75f8339a 7efde000 0018ffd4 77bb9ef2 mem_alloc_3!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586] 
0018ff94 77bb9ef2 7efde000 2558d82c 00000000 kernel32!BaseThreadInitThunk+0xe 
0018ffd4 77bb9ec5 004013bc 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70 
0018ffec 00000000 004013bc 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b 


STACK_COMMAND: ~0s; .ecxr ; kb 

FAULTING_SOURCE_CODE: 
    7: printf("1) p=%p\n",p); 
    8: *p = 1; 
    9: delete p; 
    10: printf("2) p=%p\n",p); 
> 11: *p = 2; 
    12: printf("Done\n"); 
    13: return 0; 
    14: 
    15: } 


Actualización 2

Otro ejemplo de @fmunkert:

#include <stdio.h> 

int main() 
{ 

     int *p = new int; 
     printf("1) p=%p\n",p); 
     *p = 1; 
     p++; 
     printf("2) p=%p\n",p); 
     *p = 2; // <==== Illegal memory access 
     printf("Done\n"); 
     return 0; 

} 

gflags /p /enable mem_alloc.3.exe /full /unaligned

STACK_TEXT: 
0018ff44 00401205 00000001 0505ffbe 04ffdf44 mem_alloc_3!main+0x52 [c:\src\tests\test.cpp\mem_alloc\mem_alloc\mem_alloc.3.cpp @ 12] 
0018ff88 75f8339a 7efde000 0018ffd4 77bb9ef2 mem_alloc_3!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586] 
0018ff94 77bb9ef2 7efde000 2577c47c 00000000 kernel32!BaseThreadInitThunk+0xe 
0018ffd4 77bb9ec5 004013ac 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70 
0018ffec 00000000 004013ac 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b 


STACK_COMMAND: ~0s; .ecxr ; kb 

FAULTING_SOURCE_CODE: 
    8:   printf("1) p=%p\n",p); 
    9:   *p = 1; 
    10:   p++; 
    11:   printf("2) p=%p\n",p); 
> 12:   *p = 2; // <==== Illegal memory access 
    13:   printf("Done\n"); 
    14:   return 0; 
    15: 
    16: } 

Desafortunadamente El/ opción no alineados podrían resultar en el hecho de que un programa no funcionará correctamente (How to use Pageheap.exe):

Algunos programas hacen suposiciones acerca de la alineación de 8 bytes y dejan de funcionar correctamente con el parámetro/desalineado. Microsoft Internet Explorer es uno de esos programas.

+0

El mecanismo gflags comprueba el montón solo de vez en cuando. No detecta inmediatamente problemas como '* p = 0;' donde p es un puntero no válido. Y un inconveniente mucho más importante de gflags es que también informa muchos falsos positivos y no hay forma de decirle a gflags que dirijan ciertos módulos o call stacks. –

+0

'no hay forma de decirle a gflags que diga ciertos módulos' - ver ayuda:' gflags/p /? '->'/dlls DLL ...Asignaciones de montón de página para dlls.destino –

+0

Fuera de curioisy. Por favor, muéstrame un código C++ donde gflags informa falso positivo. –

2

Me adjuntar WinDbg cuando se ejecuta el programa y hacer un minivolcado cuando debugbreaks en un accidente o una excepción:

.dump /ma c:\mem.dmp // c:\mem.dmp could be any other location you desire 

que permitiría gflags para su aplicación, ya sea desde la línea de comandos del plazo de WinDbg:

!gflag +ust 

¡Recuerde quitar esta marca después!

entonces se podría ejecutar un análisis exepction automatizado:

!analyze -v 

esto puede decirle lo que piensa que la causa del accidente, se puede volcar las pilas de llamadas de todas las discusiones:

~* kb 

y si vea algo sospechoso, puede cambiar el hilo e inspeccionar más:

~x s 

Puede inspeccionar t el registro contexto excepción:

.ecxr 

hay un buen enlace sobre cómo recuperar la pila de llamadas de un bloque catch: http: //blogs.msdn.com/b/slavao/archive/2005/01/ 30/363428.aspx y también esto: http://blogs.msdn.com/b/jmstall/archive/2005/01/18/355697.aspx

Lo principal aquí es que con windbg adjunto deberías poder inspeccionar el estado de todos los hilos y las pilas de llamadas, también puedes abrir el minivolcado en el visual studio: http://msdn.microsoft.com/en-us/library/windows/desktop/ee416349%28v=vs.85%29.aspx#Analysis_of_a_minidump si prefiere Visual Studio para navegar, puede abrir el mismo volcado en windbg para usar sus herramientas de análisis y Visual Studio para navegar el código. Espero que esto ayude.

+0

Gracias por la respuesta elaborada, pero analizar el estado actual del programa no es el problema; También sé la pila de llamadas del hilo que se cuelga. Lo que necesito es conocer el historial de ejecución (sin tener que agregar declaraciones de seguimiento en todas partes en el programa). –

+0

lo que estás pidiendo es alguna forma de registrar la actividad? Puede establecer un punto de interrupción en su módulo u otros módulos y ejecutar el comando 'wt':' bp myDLL! MyClass: someFunc() "wt; g;" 'puede reemplazar myDLL con una dirección de inicio y lo mismo para' wt ', y escriba el resultado en un archivo de registro:' .logopen c: \ myLog.txt' pero esto necesitaría algún tipo de precognición para saber dónde ocurrirían los errores, podría inyectar algún registro usando Logexts.dll pero esto lo limita a MS Llamadas API – EdChum

+0

'wt' solo registra el hilo actual; Estoy buscando una forma de registrar todos los hilos. Además, he notado que 'wt' algunas veces detiene el registro sin ninguna razón aparente. –

1

no GDB ofrecer esta funcionalidad fuera de la caja?

ha sido un tiempo desde que lo usé pero recuerdo que podría ejecutar un programa hasta que se estrelló y luego repetir los pasos para que en el depurador.

Además, sería fácil de configurar su propia aplicación de registro, que podría generar cualquier cantidad de datos que eligió y podría ser activado por una línea de parámetro de comando para el exe?

Usted puede configurarlo para abordar un accidente que está teniendo o simplemente para cubrir los aspectos básicos y luego se extenderá a medida que solucionan los problemas o ha agregado una nueva funcionalidad. La ventaja sería que sería capaz de capturar exactamente los datos que encuentre útiles e incluso podría especificar niveles de registro para evitar verse abrumado por el ruido.

+0

Estoy buscando una solución para Windows (vea las etiquetas en mi pregunta original). Gdb ofrece esta funcionalidad solo para destinos Linux. Agregar el registro a la aplicación afectada no es una solución porque también debo conocer las instrucciones ejecutadas en bibliotecas de terceros. –

+0

Ah, no me di cuenta de que gdb no era compatible con Windows. La adición de terceros también hace un agujero al escribir su propio registrador. Hmmm, lo siento, no tengo nada para ti en este momento, buena suerte. – Stefan

1

¿Qué le parece usar la aplicación AppSight de BMC?

Lo usamos en una compañía anterior (lo siento, me tomó un tiempo recordar el nombre), se usó para investigar bloqueos, etc. ISTR lo ejecutó y luego ejecutó su software y grabó todo lo que estaba sucediendo en un archivo de registro que puede ver más adelante.

Definitivamente funciona en Windows, ya que es en lo que lo usé.

bien podría ser lo que estás buscando?

+0

Parece que AppSight es idéntica a la desactivada Mutek BugTrapper (más tarde adquirida por Identify); por lo tanto, su respuesta es la primera que realmente responde mi pregunta. Lamentablemente, me temo que el precio mínimo del proyecto de $ 150,000 solicitado por BMC va más allá de mi presupuesto. –

-1

No sabe con certeza si esto es lo que quieres, pero 'U' se desmonte las últimas instrucciones del registro de IP actual en el flujo actual. Esto le mostrará las últimas instrucciones que se ejecutaron, y usted normalmente puede averiguar qué valores fueron para diferentes registros retrocediendo a través del código que desensambla. Es un proceso lento y difícil la mayor parte del tiempo, pero te proporciona casi el 100% de precisión (salvo problemas de hardware extraños o problemas de códigos realmente extraños) que acaba de suceder. He utilizado este método en el pasado para descubrir por qué ciertas cosas fueron anuladas cuando no tenía el código fuente.

Si consulta el archivo de ayuda windbg encontrará más información al respecto.

+0

-1. Esto funcionará bien solo en el código que no tiene ramas condicionales y no calcula las direcciones a las que saltar. En la práctica, hay muchas formas en que el procesador puede llegar a cualquier instrucción dada y descubrir que el camino que lo conduce es imprácticamente difícil o tedioso. Conocer el camino en lugar de tratar de reconstruirlo en función de los diversos efectos secundarios ayuda inmensamente. Y esto es lo que se preguntó. –

Cuestiones relacionadas