Arreglando fallas de segmentación en C ++

95

Estoy escribiendo un programa C ++ multiplataforma para Windows y Unix. En el lado de la ventana, el código se compilará y ejecutará sin problemas. En el lado de Unix, se compilará; sin embargo, cuando intento ejecutarlo, aparece un error de segmentación. Mi corazonada inicial es que hay un problema con los punteros.

¿Cuáles son las buenas metodologías para encontrar y corregir errores de falla de segmentación?

Elpezmuerto
fuente

Respuestas:

135
  1. Compile su aplicación con -g, luego tendrá símbolos de depuración en el archivo binario.

  2. Úselo gdbpara abrir la consola gdb.

  3. Úselo filey páselo en el archivo binario de su aplicación en la consola.

  4. Use runy pase los argumentos que su aplicación necesite para comenzar.

  5. Haga algo para provocar una falla de segmentación .

  6. Escriba bten la gdbconsola para obtener un seguimiento de la pila de la falla de segmentación .

Svisstack
fuente
¿Qué significa tenerlo compilado gen el contexto de CMake?
Schütze
2
Habilite el tipo de compilación de depuración. Una forma es cmake -DCMAKE_BUILD_TYPE=Debug.
Antonin Décimo
36

A veces, el bloqueo en sí no es la causa real del problema; tal vez la memoria se rompió en un momento anterior, pero la corrupción tardó un tiempo en mostrarse. Eche un vistazo a valgrind , que tiene muchas comprobaciones para detectar problemas de punteros (incluida la comprobación de límites de matriz). Le dirá dónde comienza el problema , no solo la línea donde ocurre el accidente.

paleozogt
fuente
19

Antes de que surja el problema, intente evitarlo tanto como sea posible:

  • Compile y ejecute su código tan a menudo como pueda. Será más fácil localizar la pieza defectuosa.
  • Trate de encapsular rutinas de bajo nivel / propensas a errores para que rara vez tenga que trabajar directamente con la memoria (preste atención a la modelización de su programa)
  • Mantener una suite de pruebas. Tener una descripción general de lo que está funcionando actualmente, lo que ya no funciona, etc., lo ayudará a descubrir dónde está el problema (la prueba de impulso es una posible solución, no la uso yo mismo, pero la documentación puede ayudar a comprender de qué tipo de información debe mostrarse).

Utilice las herramientas adecuadas para la depuración. En Unix:

  • GDB puede decirle dónde se bloquea el programa y le permitirá ver en qué contexto.
  • Valgrind le ayudará a detectar muchos errores relacionados con la memoria.
  • Con GCC también puedes usar mudflap Con GCC, Clang y desde octubre de forma experimental MSVC puedes usar Address / Memory Sanitizer . Puede detectar algunos errores que Valgrind no detecta y la pérdida de rendimiento es menor. Se usa compilando con la -fsanitize=addressbandera.

Finalmente recomendaría las cosas habituales. Cuanto más legible, fácil de mantener, claro y ordenado sea su programa, más fácil será depurarlo.

log0
fuente
5

En Unix puede usar valgrindpara encontrar problemas. Es gratis y poderoso. Si prefiere hacerlo usted mismo, puede sobrecargar los operadores newy deletepara establecer una configuración en la que tenga 1 byte 0xDEADBEEFantes y después de cada nuevo objeto. Luego, realice un seguimiento de lo que sucede en cada iteración. Esto puede fallar en capturar todo (no se garantiza que siquiera toque esos bytes) pero me ha funcionado en el pasado en una plataforma Windows.

Wheaties
fuente
1
bueno, esto sería 4 bytes en lugar de 1 ... pero el principio está bien.
Jonas Wagner
1
¿Puedo vincularme a mi depurador de montón no intrusivo ? :-)
fredoverflow
Ve a por ello. Nuestro objetivo es ayudar a otros aquí, por lo que se debe agregar cualquier cosa que pueda ayudar.
Wheaties
Aunque la sobrecarga newy deletepuede ser muy útil, usarlo -fsanitize=addresses una mejor opción ya que el compilador compilará en tiempo de ejecución para detectar problemas y volcará la memoria automáticamente a la pantalla, lo que facilita la depuración.
Tarick Welling
3

Sí, hay un problema con los punteros. Es muy probable que esté utilizando uno que no se haya inicializado correctamente, pero también es posible que esté arruinando la gestión de su memoria con dobles liberaciones o algo así.

Para evitar punteros no inicializados como variables locales, intente declararlos lo más tarde posible, preferiblemente (y esto no siempre es posible) cuando puedan inicializarse con un valor significativo. Convénzase de que tendrán un valor antes de que se utilicen, examinando el código. Si tiene dificultades con eso, inicialícelos a una constante de puntero nulo (generalmente escrito como NULLo 0) y verifíquelos.

Para evitar punteros no inicializados como valores de miembros, asegúrese de que estén inicializados correctamente en el constructor y se manejen correctamente en los constructores de copia y los operadores de asignación. No confíe en una initfunción para la gestión de la memoria, aunque puede hacerlo para otras inicializaciones.

Si su clase no necesita constructores de copia u operadores de asignación, puede declararlos como funciones miembro privadas y nunca definirlos. Eso provocará un error del compilador si se utilizan explícita o implícitamente.

Utilice punteros inteligentes cuando corresponda. La gran ventaja aquí es que, si se adhiere a ellos y los usa de manera consistente, puede evitar completamente la escritura deletey nada se eliminará dos veces.

Utilice cadenas de C ++ y clases de contenedor siempre que sea posible, en lugar de cadenas y matrices de estilo C. Considere usar en .at(i)lugar de [i], porque eso forzará la verificación de límites. Vea si su compilador o biblioteca se puede configurar para verificar límites [i], al menos en modo de depuración. Las fallas de segmentación pueden ser causadas por saturaciones de búfer que escriben basura sobre punteros perfectamente buenos.

Hacer esas cosas reducirá considerablemente la probabilidad de fallas de segmentación y otros problemas de memoria. Sin duda fallarán en arreglarlo todo, y es por eso que debería usar valgrind de vez en cuando cuando no tenga problemas, y valgrind y gdb cuando los tenga.

David Thornley
fuente
1

No conozco ninguna metodología que se pueda utilizar para solucionar este tipo de problemas. No creo que sea posible encontrar uno, ya que el problema en cuestión es que el comportamiento de su programa no está definido (no conozco ningún caso en el que SEGFAULT no haya sido causado por algún tipo de UB) .

Existen todo tipo de "metodologías" para evitar el problema antes de que surja. Uno importante es RAII.

Además de eso, solo tienes que ponerle tus mejores energías psíquicas.

Edward extraño
fuente