Cuadro de pila dañado GDB-¿Cómo depurar?
recursion (5)
Esas direcciones falsas (0x00000002 y similares) son en realidad valores de PC, no valores de SP. Ahora, cuando obtienes este tipo de SEGV, con una dirección de PC falsa (muy pequeña), el 99% del tiempo se debe a una llamada a través de un puntero de función falso. Tenga en cuenta que las llamadas virtuales en C ++ se implementan a través de punteros de función, por lo que cualquier problema con una llamada virtual puede manifestarse de la misma manera.
Una instrucción de llamada indirecta simplemente empuja la PC después de la llamada a la pila y luego establece la PC al valor objetivo (falso en este caso), así que si esto es lo que sucedió, puede deshacerlo fácilmente sacando manualmente la PC de la pila . En el código x86 de 32 bits, simplemente lo hace:
(gdb) set $pc = *(void **)$esp
(gdb) set $esp = $esp + 4
Con el código x86 de 64 bits que necesita
(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8
Entonces, deberías poder hacer un bt
y averiguar dónde está realmente el código.
El otro 1% del tiempo, el error se debe a sobrescribir la pila, generalmente al desbordar una matriz almacenada en la pila. En este caso, es posible que pueda obtener más claridad sobre la situación mediante el uso de una herramienta como valgrind
Tengo el siguiente rastro de pila. ¿Es posible distinguir algo útil de esto para la depuración?
Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0 0x00000002 in ?? ()
#1 0x00000001 in ?? ()
#2 0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)
¿Dónde empezar a mirar el código cuando obtenemos un Segmentation fault
y el seguimiento de la pila no es tan útil?
NOTA: Si publico el código, los expertos en SO me darán la respuesta. Quiero tomar la guía de SO y encontrar la respuesta yo mismo, así que no estoy publicando el código aquí. Disculpas
Mire algunos de sus otros registros para ver si uno de ellos tiene el puntero de la pila en caché. A partir de ahí, es posible que pueda recuperar una pila. Además, si esto está incrustado, muy a menudo la pila se define en una dirección muy particular. Usando eso, a veces también puedes obtener una pila decente. Todo esto supone que cuando saltó al hiperespacio, su programa no vomitó en la memoria en el camino ...
Si la situación es bastante simple, la respuesta de Chris Dodd es la mejor. Parece que saltó a través de un puntero NULL.
Sin embargo, es posible que el programa se haya disparado en el pie, la rodilla, el cuello y el ojo antes de colgarse: sobrescribió la pila, dañó el puntero del marco y otros males. Si es así, desenredar el hash no es probable que te muestre patatas y carne.
La solución más eficiente será ejecutar el programa bajo el depurador y pasar por encima de las funciones hasta que el programa falle. Una vez que se identifica una función de bloqueo, vuelva a comenzar y pise esa función y determine a qué función llama provoca el bloqueo. Repita hasta encontrar la única línea de código ofensiva. El 75% del tiempo, la solución será obvia.
En el otro 25% de las situaciones, la llamada línea de código ofensiva es una pista falsa. Estará reaccionando a las condiciones (no válidas) configuradas en muchas líneas antes, quizás miles de líneas antes. Si ese es el caso, el mejor curso elegido depende de muchos factores: principalmente su comprensión del código y la experiencia con él:
- Quizás establecer un punto de observación de depurador o insertar una
printf
diagnóstico en variables críticas conducirá a la necesaria A ha! - Tal vez cambiar las condiciones de prueba con diferentes entradas proporcionará más información que la depuración.
- Tal vez un segundo par de ojos te obligue a verificar tus suposiciones o reunir evidencia pasada por alto.
- A veces, todo lo que se necesita es ir a cenar y pensar en la evidencia reunida.
¡Buena suerte!
Si se trata de una sobrescritura de pila, los valores pueden corresponder a algo reconocible del programa.
Por ejemplo, acabo de encontrarme mirando la pila
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x000000000000342d in ?? ()
#2 0x0000000000000000 in ?? ()
y 0x342d
es 13357, que resultó ser una identificación de nodo cuando grepped los registros de la aplicación para ello. Eso ayudó de inmediato a reducir los sitios candidatos donde podría haberse producido la sobrescritura de la pila.
Suponiendo que el puntero de pila es válido ...
Puede ser imposible saber exactamente dónde se produce el SEGV desde el backtrace: creo que los dos primeros cuadros de pila se sobrescriben por completo. 0xbffff284 parece una dirección válida, pero las siguientes dos no lo son. Para ver más de cerca la pila, puedes probar lo siguiente:
gdb $ x / 32ga $ rsp
o una variante (reemplaza el 32 con otro número). Esto imprimirá una cantidad de palabras (32) comenzando desde el puntero de pila de tamaño gigante (g), formateado como direcciones (a). Escriba ''help x'' para obtener más información sobre el formato.
Instrumentar su código con algunos ''printf'' de centinela puede no ser una mala idea, en este caso.