¿Qué debo aprender más sobre depuración? [cerrado]

8

Para depurar mis programas, uso principalmente los siguientes métodos:

  1. Use printf (o equivalente en otros idiomas) para verificar el valor de una variable después de una declaración en particular o para verificar si el programa ingresa una declaración condicional o un bucle.

  2. Use relojes / puntos de interrupción cuando use IDEs.

Soy capaz de resolver los problemas usando los métodos anteriores. Pero he notado que hay dos tipos de compilaciones: depuración y lanzamiento. Por el nombre, puedo entender que la compilación de depuración sería útil para la depuración. También he leído que la compilación de depuración almacena algo llamado información de tabla de símbolos y otras cosas.

¿Cómo puedo usar la compilación de depuración para la depuración? ¿Hay alguna otra técnica de depuración que deba aprender?

Galleta
fuente
La diferencia depende de la plataforma. Para Java, la diferencia es pequeña. Para C puede ser muy diferente
2
¿En qué idiomas / plataformas estás codificando?
DXM

Respuestas:

7

Aprende tu depurador

Es realmente útil familiarizarse con el depurador, ya sea basado en texto, IDE completo o alguna combinación de los mismos. No das muchos detalles, así que describiré el caso general:

1) puntos de interrupción

Además de detenerse en una línea de código, muchos depuradores le permiten especificar que se rompa cuando surge una condición (por ejemplo, "x> 5"), después de varias pasadas a través del código o cuando alguna memoria cambia el valor. Esto es muy útil para comprender cómo su código entra en un mal estado, por ejemplo, observar cuándo un puntero se vuelve nulo en lugar de detectar el bloqueo cuando se desreferencia.

2) Pasando por el código

Puede ingresar a las funciones, línea por línea a lo largo del código, saltar sobre las líneas ('establecer la siguiente declaración') y luego 'subir' fuera de las funciones. Es una forma realmente poderosa de seguir la ejecución de su código para verificar que hace lo que cree que hace :-)

3) Evaluar expresiones

Por lo tanto, puede colocar variables en una lista / ventana de Observación y ver su cambio de valor cuando alcanza un punto de interrupción o atraviesa el código, pero también puede hacer evaluaciones de expresiones complejas, por ejemplo, se evaluará "x + y / 5". Algunos depuradores también le permiten poner llamadas a funciones en las listas de vigilancia. Puede hacer cosas como "time ()", "MyFunction (...)", "time ()" y obtener la temporización del tiempo que tardó su función.

4) Excepciones y manejo de señales

Entonces, si su idioma admite excepciones y / o señales, generalmente puede configurar el depurador para saber cómo reaccionar ante esto. Algunos depuradores le permiten entrar en el código en el punto donde la excepción está por suceder, en lugar de después de que no se haya detectado. Esto es útil para rastrear problemas extraños como errores de "Archivo no encontrado" porque el programa se ejecuta como una cuenta de usuario diferente.

5) Adjuntar a un proceso / núcleo

Entonces, a veces tiene que usar el depurador para saltar a un proceso existente que está yendo mal. Si tiene el código fuente cerca y los símbolos de depuración están intactos, puede sumergirse como si hubiera comenzado en el depurador en primer lugar. Esto también es similar para los volcados de núcleo, excepto que generalmente no puede continuar la depuración en esos (el proceso ya ha muerto).

Configuración de compilación

Hay varias variaciones de compilación que puede realizar activando o desactivando funciones como símbolos de depuración, optimizaciones y otros indicadores del compilador:

1) Depuración

Tradicionalmente, esta es una compilación simple sin características especiales, lo que hace que sea fácil de depurar y predecible. Varía un poco según la plataforma, pero puede haber algo de margen adicional, por ejemplo, asignaciones y tamaños de búfer para garantizar la fiabilidad. Por lo general, estará presente un símbolo de compilador como DEBUG o Conditional ("Debug") para que se introduzca el código específico de depuración. Esta es a menudo la compilación que se envía, con símbolos de nivel de función intactos, especialmente si la confiabilidad y / o la repetibilidad son preocupación.

2) Lanzamiento / construcción optimizada

La habilitación de las optimizaciones del compilador permite que algunas instalaciones de generación de código de bajo nivel en el compilador hagan un código más rápido o más pequeño basado en suposiciones sobre su código. Los aumentos de velocidad posibles son irrelevantes si la elección de su algoritmo es deficiente, pero para los cálculos intensivos esto puede marcar una gran diferencia a través de la eliminación de subexpresión común y el desenrollado de bucle, etc. A veces, las suposiciones hechas por el optimizador son incompatibles con su código, por lo que tiene que ser reducido una muesca. Los errores del compilador en el código optimizado también han sido un problema en el pasado.

3) Construcción instrumentada / perfilada

Su código está construido con un código de instrumentación específico para medir la cantidad de veces que se llama a una función y cuánto tiempo se pasa en esa función. Este código generado por el compilador se escribe al final del proceso para su análisis. A veces es más fácil usar una herramienta de software especializada para esto, ver más abajo. Este tipo de compilación nunca se envía.

4) Construcción segura / comprobada

Todas las 'válvulas de seguridad' se habilitan mediante símbolos de preprocesador o configuraciones del compilador. Por ejemplo, los parámetros de la función de verificación de macros ASSERT, los iteradores verifican las colecciones no modificadas, los canarios se colocan en la pila para detectar la corrupción, las asignaciones del montón se llenan con valores centinela (0xdeadbeef es memorable) para detectar la corrupción del montón. Para los clientes que tienen problemas persistentes que solo pueden reproducirse en su sitio, es algo útil.

5) Construcción de características

Si tiene diferentes clientes que tienen diferentes requisitos de su producto de software, es común hacer una compilación para cada cliente que ejercite las diferentes partes durante la prueba. Por ejemplo, un cliente quiere la funcionalidad sin conexión y otro quiere solo en línea. Es importante probar en ambos sentidos si el código se construye de manera diferente.

Registro y rastreo

Así que hay que escribir algunas declaraciones útiles para printf () y luego hay que escribir información de rastreo integral y estructurada en los archivos de datos a medida que avanza. Esta información se puede extraer para comprender el comportamiento / características de tiempo de ejecución de su software. Si su código no falla, o si toma algún tiempo reprobarlo, es útil tener una imagen de, por ejemplo, una lista de subprocesos, sus transiciones de estado, asignaciones de memoria, tamaños de grupo, memoria libre, número de identificadores de archivos, etc. realmente depende del tamaño, la complejidad y los requisitos de rendimiento de su aplicación, pero como ejemplo, los desarrolladores de juegos quieren asegurarse de que no haya "picos" en el uso de CPU o memoria mientras un juego está en progreso, ya que eso probablemente afectará la velocidad de fotogramas. El sistema mantiene parte de esta información, algunas las bibliotecas y el resto el código.

Otras herramientas

No siempre es necesario crear una compilación diferente para cubrir estos escenarios: se pueden elegir algunos aspectos en tiempo de ejecución a través de la configuración del proceso (trucos del Registro de Windows), haciendo que las bibliotecas alternativas estén disponibles con mayor prioridad que las bibliotecas estándar, por ejemplo, en su ruta del cargador o utilizando un software ICE o un depurador especializado para sondear su software en busca de características de tiempo de ejecución (por ejemplo, Intel v-Tune). Algunos de estos cuestan mucho dinero, algunos son herramientas de código libre, Xtrace.

JBRWilkinson
fuente
6

Una compilación de depuración se crea con símbolos de depuración en el ejecutable. Básicamente, lo que eso significa es que cada línea de código fuente está vinculada al código de máquina que se generó a partir de él, y se mantienen todos los nombres de variables y funciones. En GCC esto se hace con la -gbandera cuando compila archivos fuente. Otra cosa que a menudo se hace es desactivar la optimización, esto se debe a que el compilador hace algunos trucos geniales que hacen que su programa vaya más rápido, pero hace que la depuración sea imposible. En GCC esto se hace con -O0.

La herramienta más útil que he usado para depurar es gdb . Es una versión de texto de los puntos de interrupción IDE que mencionó. Sin embargo, puede hacer mucho más con gdb que con un IDE. De hecho, algunos IDE son solo una envoltura alrededor de gdb, pero algunas características se pierden. Puede ver ubicaciones de memoria, imprimir ensamblajes, imprimir cuadros de pila, cambiar el manejo de la señal y mucho más. Si se toma en serio la depuración, aprendería gdb o algún programa de depuración basado en texto equivalente.

Otra cosa que a menudo encuentro útil es valgrind . Encuentra pérdidas de memoria y realiza un seguimiento de si la memoria se inicializa o no. Lo usa con su compilación de depuración, porque luego obtiene números de línea donde suceden cosas interesantes.

Jarryd
fuente
1
De Verdad? Cada vez que he tenido que usar GDB, encuentro un retroceso significativo respecto a la potencia y facilidad de uso que tengo al alcance de la mano con un buen depurador basado en IDE. Particularmente porque GDB es una herramienta muy general, mientras que un depurador integrado puede aprovechar el conocimiento de las reglas del lenguaje para el que fue diseñado para brindarle comentarios más específicos y relevantes.
Mason Wheeler
1
GDB está muy diseñado para C y C ++. Utilicé depuradores IDE durante bastante tiempo y me frustraron sin fin. Me parece que tengo mucho más control sobre lo que estoy haciendo cuando uso GDB.
Jarryd
2
@MasonWheeler, gdbes programable, IDEs normalmente no lo son. No necesito ningún "poder" en la punta de mis dedos cuando una computadora puede hacer todo el trabajo por mí mientras estoy tomando mi té.
SK-logic
3
@MasonWheeler, no, estás haciendo algo mal al presionar "paso-inspección-paso-inspección" como loco. Es mucho más fácil escribir un pequeño script para verificar su hipótesis actual sobre la causa de un problema, ejecutarlo (casi de la misma manera que ejecuta las pruebas funcionales) y analizar los resultados, repetir si es necesario. Todas las formas de una depuración interactiva casi siempre son casi inútiles, incluso si es una descarga post-mortem de núcleo. Un script de depuración típico establecería varios puntos de interrupción, ejecutaría, inspeccionaría y formatearía muy bien los valores en los puntos de interrupción.
SK-logic
1
@Mason Wheeler, un simple seguimiento de la pila ( bten gdb) en la mayoría de los casos le proporcionaría información más que suficiente para una hipótesis 0 (si este no es el caso, su código está, muy por debajo del umbral de calidad inferior al estándar) . He estado usando depuradores desde VMS, probé todos los sabores posibles, incluidas algunas bestias exóticas extremas como los depuradores de tiempo, pero hasta ahora no pude encontrar ninguno realmente útil. Estás probando tus suposiciones de forma interactiva y luego estás perdiendo el tiempo. Estoy probando mis suposiciones en lote (podrían ser muchas en paralelo), rápidamente y con muy poco esfuerzo.
SK-logic
3

Hay una técnica de depuración extremadamente poderosa, que aún no se menciona en las otras respuestas. Está disponible de forma gratuita para algunos entornos de desarrollo, o se puede agregar con relativamente poco esfuerzo a casi cualquier otra cosa.

Esto es un REPL incorporado, que permite conectarse en cualquier momento a través de un socket, ejecutarse en un hilo dedicado y puede usar todas las formas posibles de reflexión para el código en ejecución, modificando o reemplazando completamente el código en ejecución, agregando cosas nuevas, en ejecución funciones, etc.

Lo tendrá listo para usar si codifica, por ejemplo, Common Lisp, Smalltalk, Python, Ruby, etc. Es posible integrar un intérprete liviano (por ejemplo, Lua, Guile, JavaScript o Python) en una aplicación nativa . Para entornos basados ​​en JVM o .NET, hay muchos compiladores e intérpretes incorporables disponibles, y existe una reflexión bastante poderosa de forma gratuita.

Este enfoque es mucho más eficiente que los depuradores interactivos / iterativos (como gdb, visual studio debugger, etc.), y para obtener los mejores resultados debe usarse junto con una instalación de registro adecuada y aserciones correctamente colocadas.

SK-logic
fuente
pero no se utiliza junto con un hacker que accede a su red. Asegúrese de deshabilitar esto en
Build
@gbjbaanb, para eso se usa SSL. Lea la Viawebhistoria: se han beneficiado mucho de este enfoque, al tener la capacidad de depurar el sistema de producción en ejecución.
SK-logic
2

La herramienta de depuración más importante que he usado es el volcado de memoria post-mortem (volcado de núcleo en Linux, dmp de usuario en Windows).

Es un tema realmente complejo, así que aquí hay un enlace :

Básicamente (en Windows, la plataforma con la que tengo más experiencia en la depuración post mortem), construye sus aplicaciones con símbolos que se guardan en un archivo separado (un archivo .pdb). Los mantiene a salvo (para que nadie pueda realizar ingeniería inversa de su código fácilmente) y espere un bloqueo. Cuando lo hace (y tiene DrWatson o similar ejecutándose para capturar el bloqueo y generar el archivo de volcado), carga el archivo .dmp en WinDbg (depurador de Windows) junto con los símbolos (y opcionalmente, una ruta al código fuente) y le mostrará mucha información como la pila de llamadas, registros, variables, valores de memoria, etc. Es hermoso cuando funciona.

Para una compilación de depuración, todo esto está configurado para suceder automáticamente. Debe habilitar los símbolos al generar la versión. Las compilaciones de depuración también agregan otras cosas como protectores de memoria (que activan excepciones si intentas escribir en ellas, esto muestra desbordamientos de búfer o corrupción de memoria muy fácilmente; o afirma cosas que no son del todo correctas). En general, aunque las compilaciones de depuración son para que los desarrolladores ejecuten y prueben su código antes de pasarlo.

Ahora esto es para la depuración de código nativo, el código .NET es un PiTA para cosas post-mortem como esta, pero a veces puede obtener cosas de excepción .NET cargando sos .

Todas las cosas complejas, pero no es tan malo. Sin embargo, no esperes una buena GUI puntiaguda, esta es la belleza de la línea de comando.

gbjbaanb
fuente
2

Use printf (o equivalente en otros idiomas) para verificar el valor de una variable después de una declaración en particular o para verificar si el programa ingresa una declaración condicional o un bucle.

También debe usar cualquier tipo de declaración de afirmación que se ofrezca.

También debe escribir pruebas unitarias.

Soy capaz de resolver los problemas usando los métodos anteriores.

Perfecto. Sabes todo lo que necesitas saber.

¿Hay alguna otra técnica de depuración que deba aprender?

No. No es que debas aprender. Puede obtener más información si cree que ayudará. Pero no necesitas nada más de lo que tienes.

Durante los últimos 30 años, he usado un depurador solo unas pocas veces (quizás tres o cuatro). Y luego, solo lo he usado para leer volcados de memoria post-mortem para encontrar la llamada a la función que falló.

El uso del depurador no es una habilidad esencial . La declaración impresa es suficiente.

S.Lott
fuente
0

Aquí hay una lista rápida de técnicas:

  • Gráficos de llamadas estáticas
  • Gráficos dinámicos de llamadas
  • Perfiles de puntos de acceso
  • Ver mensajes de subprocesos
  • Reproducir depuración / ejecución inversa
  • Miradores
  • Puntos de trazado

También puede implementar comportamientos personalizados con herramientas de estilo AOP, así como recorrer un largo camino con una buena herramienta de análisis estático.

Paul Nathan
fuente
0

Aprenda todo sobre su herramienta de depuración.

A menudo ocultan características realmente poderosas que pueden ayudarlo a comprender mejor lo que está sucediendo. (en particular, el depurador C ++ de Visual Studio)

Klaim
fuente