2011-01-17 10 views
14

Me encuentro con un choque extraño en nuestro software y tengo muchos problemas para depurarlo, y entonces estoy buscando el consejo de SO sobre cómo abordarlo.¿Cómo puedo depurar una falla difícil de reproducir sin una pila de llamadas útil?

El accidente es una violación de acceso de leer un puntero NULL:

Excepción de primera oportunidad en $ 00CF0041. Clase de excepción $ C0000005 con mensaje 'infracción de acceso en 0x00cf0041: lea de la dirección 0x00000000'.

Sucede solo 'a veces' - No he logrado encontrar ninguna rima o razón, sin embargo, para cuándo - y solo en el hilo principal. Cuando esto ocurre, la pila de llamadas contiene una entrada incorrecta:

Call stack with one line, Classes::TList::Get, address 0x00cf0041

Para el hilo principal, que se trata, debe mostrar una gran pila de artículos de todo tipo.

En este punto, todos los otros subprocesos están inactivos (la mayoría se encuentra en WaitForSingleObject o una función similar). Solo he visto este bloqueo en el hilo principal. Siempre tiene la misma pila de llamadas de una entrada, en el mismo método en la misma dirección. Este método puede o no estar relacionado: usamos el VCL en nuestra aplicación. Mi apuesta, sin embargo, es que algo (posiblemente hace bastante tiempo) está corrompiendo la pila, y la dirección donde se bloquea es efectivamente aleatoria. Sin embargo, tenga en cuenta que ha sido la misma dirección en varias compilaciones, probablemente no sea realmente aleatoria.

Esto es lo que he intentado:

  • tratando de reproducir de forma fiable en un momento determinado. No he encontrado nada que lo reproduzca todo el tiempo, y un par de cosas que ocasionalmente hacen, o no, sin motivo aparente. Estas no son acciones lo suficientemente 'estrechas' como para limitarlo a una sección particular de código. Puede estar relacionado con el tiempo, pero en el momento en que el IDE se rompe, otros hilos generalmente no hacen nada. No puedo descartar un problema de subprocesamiento, pero creo que es poco probable.
  • Construyendo con declaraciones de depuración adicionales (información de depuración adicional, afirmaciones adicionales, etc.) Después de hacerlo, el bloqueo nunca se produce.
  • Edificio con Codeguard activado. Después de hacerlo, el bloqueo nunca ocurre y Codeguard no muestra ningún error.

Mis preguntas:

1. ¿Cómo puedo encontrar qué código causó el accidente? ¿Cómo hago el equivalente a caminar de nuevo por la pila?

2. ¿Qué consejos generales tiene para encontrar la causa de este bloqueo?

estoy usando Embarcadero RAD Studio 2010 (el proyecto contiene sobre todo el código C++ Builder y pequeñas cantidades de Delphi.)

Editar: pensé que debería agregar lo que realmente causó esto. Hubo un hilo que llamó al ReadDirectoryChangesW y luego, usando GetOverlappedResult, esperó en un evento para continuar y hacer algo con los cambios.El evento también fue señalado para terminar el hilo después de configurar un indicador de estado. El problema era que cuando el hilo salía nunca llamaba al CancelIO. Como resultado, Windows todavía realizaba un seguimiento de los cambios y probablemente seguía escribiendo en el búfer cuando el directorio cambiaba, aunque el búfer, la estructura solapada y el evento ya no existían (ni el contexto de subproceso en el que se crearon). Cuando se llamó CancelIO , no hubo más accidentes.

+0

No estoy familiarizado con CodeGaurd - ¿también presenta la pila de canarios y la validación? Lo pregunto porque estás mezclando C++ y Delphi, lo que significa que puedes estar mezclando convenciones de llamadas sin darte cuenta. Eso puede estropear muy rápidamente tu pila de forma que se manifiesta como un bloqueo aparentemente aleatorio en tu hilo principal con una pila de llamadas dañada. –

+0

Codeguard rellena la porción no inicializada de la pila con un patrón de bytes. También (intenta) verificar cosas como acceder a la memoria liberada, sobrepasar la memoria asignada, etc. Conseguir una convención de llamadas incorrecta definitivamente causaría algo como esto, sí (¡y gracias por la sugerencia!) Pero si es así, no tengo idea de dónde : C++ Builder está diseñado para interoperar con el código Delphi y tendríamos que haber cometido un error en una declaración en alguna parte, y la mayoría son IDE o compiladores. Supongo que la pregunta clave es, ¿cómo podría encontrar un método incorrectamente declarado? –

+0

No voy a poner esto como una respuesta porque es vago, pero es posible que desee probar un depurador diferente. Puedes dar, por ejemplo, Consejos WinDbg (o todo) para reconstruir la pila de llamadas real si se ha dañado o confundido. –

Respuesta

14

Incluso cuando la traza de pila proporcionada por IDE no es muy completa, eso no significa que no haya todavía información útil en la pila. Abre la vista de la CPU y mira el panel de la pila; por cada código de operación CALL, se inserta una dirección de retorno en la pila. Dado que la pila crece hacia abajo, encontrará estas direcciones de retorno por encima de la ubicación actual de la pila, es decir, desplazándose hacia arriba en el panel de la pila.

La pila para el hilo principal será alrededor de $ 00120000 o $ 00180000 (la aleatorización del espacio de direcciones en Vista y hacia arriba la ha hecho más aleatoria). El código para el ejecutable principal será alrededor de $ 00400000. Puede investigar de forma especulativa elementos de la pila que no parecen datos enteros (valores bajos) o direcciones de pila (rango $ 00120000 +) haciendo clic derecho en la entrada de la pila y seleccionando Seguir -> Cerca del código, que causará el ventana de desmontaje para saltar a esa dirección de código. Si parece un código no válido, probablemente no sea una entrada válida en el seguimiento de la pila. Si es un código válido, puede ser un código del sistema operativo (con frecuencia alrededor de $ 77000000 o más) en cuyo caso no tendrá símbolos significativos, pero de vez en cuando aparecerá en una entrada correcta de la pila.

Esta técnica, aunque algo laboriosa, puede proporcionarle información útil sobre la pila cuando el depurador no puede rastrear las cosas.Sin embargo, no ayuda si ESP (el puntero de la pila) ha sido atornillado. Afortunadamente, eso es bastante raro.

+0

Gracias Barry! Esto es muy útil, y una información muy útil para saber en general de todos modos. –

+0

Esto acaba de resolver lo que pudo haber sido este error (u otro - en cualquier caso, ha sido muy útil. He estado descubriendo un código aleatorio recientemente) Gracias por tomarse el tiempo para responder - Acabo de marcarlo como el Respuesta a la pregunta. –

2

Enlazar puede ser el motivo aquí. El sospechoso habitual son los hilos que utilizan estructuras OVERLAPPED en la pila y los hilos que envían punteros a los objetos que están en la pila a otros hilos.

Es posible recuperar la información de la pila parcial si usa Deubgging Tools For Windows y usa el comando "dps".

+0

Gracias John, y veré esto. He escrito la mayor parte de nuestro código de enhebrado, y donde pasa los objetos definitivamente se asignan dinámicamente. ¡Aun así haré una doble verificación! –

+0

¿Funcionarán las herramientas de depuración para Windows con código no compilado con un compilador de Microsoft y sin utilizar su formato de información de depuración? Las herramientas de Embarcadero no producen archivos PDB, por ejemplo. –

+1

Las herramientas de Windows requieren un formato de símbolos compatible (preferiblemente PDB, pero para este fin, incluso el archivo DBG funcionaría). – John

2

No estoy 100% seguro, pero a partir de la imagen que proporcionaste, creo que en algún punto de la ejecución intentas acceder a un objeto en un TList que es NULO. es decir:

AList[Index].SomeProperty/SomeMethod/etc. <-- error if (AList[Index] == NULL) 

En cuanto a la depuración y encontrar el lugar real donde se produce la excepción no es una tarea fácil, especialmente cuando no hay mucha información o que es difícil de reproducir, en este caso, por lo general:

    paso
  • ir a paso de la ejecución del formulario principal (si no es una excepción hasta allí)

  • , mientras que ir paso a paso, si encuentro cualquier código no seguro lo pongo entre try ... except y las condiciones para los índices (si Tengo matrices, listas, valores esperados para ser aprobada, etc.)

  • si lo anterior falla para encontrar el problema, compruebe si algunas bibliotecas están fallando

  • uso de registro de Eureka, que a veces no así (muy pocas veces) pero por lo general los puntos que en la dirección correcta

que ha tenido numerosos problemas similares a los suyos y yo puedo decir que el tema era casi un extremadamente fácil de solucionar, sin embargo, cuando los estallidos de error, no se hizo un "punto cercano " el error.

+0

Sé que parece que el código está accediendo a un TList, pero puede no serlo. La pila está rota, así que quién sabe si incluso esa parte es válida. Eureka Log es una sugerencia interesante: ¡he oído hablar de él, pero nunca lo he usado antes! –

+0

@David M, así debería, ahorra mucho tiempo, cuando escuché por primera vez, era escéptico, pero después de algunas pruebas me impresionó mucho el tiempo que ahorra, una vez más, hay situaciones en que Eureka falla, pero estos son muy pocos. – ComputerSaysNo

4

Esa es la razón por la que es hecho el proceso Pila espectador :-) http://code.google.com/p/asmprofiler/wiki/ProcessStackViewer

Puede mostrar la pila con prima seguimiento de pila, por lo que se muestran la pantalla completa cuando el rastreo de pila normal no es posible.
Pero cuidado: ¡el seguimiento de pila sin formato mostrará "falsos positivos"! Se mostrará una dirección en la pila para la cual se puede encontrar un nombre de función.

Me ayudó un número de veces cuando me encontré en el mismo problema que el suyo (sin pila normal de caminar por Delphi posible debido al estado de pila no válido)

Editar: nueva versión subido, en la página web era una versión antigua (Utilizo mucho la nueva versión) http://asmprofiler.googlecode.com/files/AsmProfiler_Sampling%20v1.0.7.13.zip

+0

rrrr demasiado ocupado últimamente, para tenerlo todo, gracias por recordarlo !! – ComputerSaysNo

+0

+1 Parece que ha automatizado el procedimiento que describí. –

+0

¡Suena muy útil! Voy a tratar de salir. –

Cuestiones relacionadas