¿Cómo hacer efectivamente la depuración manual? [cerrado]

8

Digamos que no tiene un depurador disponible, ¿cuál sería un enfoque eficaz para el código de depuración que no funciona (como se esperaba)?

Anto
fuente
20
"La herramienta de depuración más efectiva sigue siendo un pensamiento cuidadoso, junto con declaraciones impresas juiciosas". - Brian Kernighan
3
Totalmente de acuerdo. La herramienta de depuración más poderosa todavía es imprimir declaraciones. Otro consejo es depurar el código real y no los comentarios.
romeroqj
44
@jromero: no diría que las declaraciones impresas son las "más poderosas". Más extendido y fácil de usar, seguro.
cuál es
1
Esa cita de Kernighan me hace desear poder desestimar los comentarios. La depuración de sentencias impresas es una herramienta de último recurso.
Mason Wheeler
2
@Mason: La pregunta supone que los depuradores no están disponibles (por lo que la forma "real" de rastrear la ejecución ha desaparecido), entonces, ¿qué más usaría para rastrear la ejecución?

Respuestas:

7

Hay una serie de técnicas:

  1. Inicio sesión. Si no tiene acceso a los archivos, inicie sesión en una consola serie o en cualquier dispositivo de salida disponible. Es una buena idea escribir siempre su código teniendo en cuenta el inicio de sesión, lo que permite una compilación condicional para diferentes niveles de registro, desde 'ninguno' hasta 'sobrebloqueado'.

  2. Cortándolo Excluya las partes de su código alrededor de un punto sospechoso de error, uno por uno, y analice lo que ha hecho cuando el error desaparece.

  3. Afirmaciones o contratos. Es una buena idea rellenar su código con afirmaciones desde el principio. No solo ayudan con la depuración, sino que también sirven como documentación adicional para su código.

  4. Similar a 2. - varíe su entrada y reorganice el código a menos que el error desaparezca o cambie su comportamiento. A menudo es una buena idea jugar con varios niveles de optimización (si está codificando en C o C ++), ya que los errores relacionados con el puntero tienden a ser extremadamente volátiles en su comportamiento.

  5. Una versión automatizada de 3. - use el código instrumentado, por ejemplo, ejecute el binario bajo valgrind.

Y, por supuesto, hay muchas más herramientas y trucos, dependiendo de la naturaleza de su entorno de ejecución y su código.

SK-logic
fuente
1
-1: No puedo estar de acuerdo 4). ¿Parece proponerlo como una solución al error? Si es así, eso es malo y tienes un problema. Todo lo que hace es hacer que los síntomas desaparezcan; todavía está allí, lo acaba de ocultar ... por ahora ...
mattnz
@mattnz, ¿te importaría explicarlo? Propongo este enfoque como una alternativa a un depurador interactivo. Lo que, a su vez, solo resalta los síntomas, no las causas reales. 4) es una forma de identificar un problema, no una solución. De hecho, mi enfoque es mucho mejor que la depuración en la mayoría de los casos, ya que ofrece una mejor cobertura. Entonces, es probable que no haya entendido lo que le propongo. Intenta leerlo de nuevo.
SK-logic
He visto a los desarrolladores usar las acciones planteadas en el Paso 4 para "arreglar" un defecto usando "Podría hacer que suceda, ahora no puedo, lo he arreglado, enviarlo". Su publicación sugiere que este método ofrece correcciones de errores válidas.
mattnz
@mattnz, no, no sugiere nada como esto. Estoy describiendo una forma de investigar un error, no de solucionarlo. Los depuradores no corrigen errores, y la pregunta era sobre una alternativa a un depurador.
SK-logic
18

Busque un colega y explique el problema en detalle mientras recorre el código problemático paso a paso.

Con frecuencia, el acto de explicar deja en claro tanto a su colega como a usted mismo.


fuente
12
+1 Y si no encuentras un colega, usa un oso de peluche .
Péter Török
44
@ Péter Török: No sé ... los osos de peluche solo tienden a mirar hacia atrás con sus ojos fríos y muertos ... ignorando todo lo que dices, haciéndote sentir inútil, pequeño, insignificante ... Hace la depuración con un oso de peluche ... Difícil.
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner, vea el enlace que agregué.
Péter Török
2
@ Peter, me temo que tener un estante lleno de ositos de peluche listos para que los colegas lo agarren para la depuración, podría causar una impresión equivocada en los clientes.
1
@FrustratedWithFormsDesigner: ¿Y en qué se diferencia eso de muchos colegas
Mattnz
3

¿Existe un sistema de registro para administrar la salida del programa? ¿Hay al menos una consola para imprimir o archivos en los que pueda escribir? Usar consolas o archivos de registro es una forma de depurar sin un depurador. Proporcione al programa una entrada para que sepa cuál debería ser la salida, luego verifique que la salida funcione y asegúrese de que su registro le brinde muchos detalles del proceso. Luego intente con una entrada que dé la salida incorrecta. Con suerte, los registros le darán un rastro detallado de lo que salió mal.

FrustratedWithFormsDesigner
fuente
Puede usar cualquier cosa PERO un depurador del tipo de gdb o lo que encontraría en su IDE
Hasta
@Anto: Eso suena como una línea de una tarea, pero en ese caso iniciar sesión en un archivo o la consola no está utilizando un depurador como "gdb o lo que encuentra en su IDE". gdb y otros depuradores le permiten recorrer su código línea por línea e inspeccionar los valores de las variables a medida que se ejecuta el programa . Depuración por registro significa que debe usar un seguimiento (en archivo o consola) de la ejecución del programa después de que finalice, y averiguar qué sucedió.
FrustratedWithFormsDesigner
Sé, por lo tanto, lo que recomendó está permitido. No, esta no es ninguna tarea; Estoy en la secundaria y no tenemos programación / cs en absoluto.
Anto
2
@Anto: Ok. El único inconveniente de este método es si está intentando depurar un programa que tiene problemas de sincronización. Por ejemplo, si se trata de un problema de IPC, sus declaraciones de impresión / registro pueden afectar la rapidez con la que los procesos se comunican entre sí y tener el registro activado o desactivado puede afectar si se reproduce el problema (en este caso, realmente tiene que ir con @ Consejo de Thorbjørn Ravn Andersen). En algunos casos, el registro puede degradar severamente el rendimiento, pero generalmente solo cuando se registra mucho en un sistema grande cuando se procesan grandes cantidades de datos ... pero hay que tener en cuenta.
FrustratedWithFormsDesigner
3

Depende ¿Funcionó antes? Si el código que solía funcionar se rompió de repente, entonces debe examinar con mucho cuidado los cambios más recientes.

Dima
fuente
2
Este enfoque no debe ser subestimado: el historial de revisiones es una excelente manera de identificar errores en el código que funcionaban anteriormente, incluso al agregar nuevas funciones.
edA-qa mort-ora-y
2
He oído que 'git bisect' automatiza un poco esta tarea. Sin embargo, todavía tengo que probarlo.
Clayton Stanley
2

1) Haz lo que sea necesario para que el error sea 100% reproducible o lo más cercano posible al 100%

2) Rastree el problema, utilizando printf () u otro recurso de registro. Sin embargo, este es un arte y depende de la naturaleza del error.

Si no tiene absolutamente ninguna idea sobre la ubicación del error, pero por ejemplo sabe que una condición se vuelve falsa en algún momento (el estado del programa se rompió; llamémoslo IsBroken ()), puede hacer una búsqueda de profundización / partición para averiguar la ubicación del problema. Por ejemplo, registre el valor de isBroken () al principio al final de los métodos principales:

void doSomething (void)
{
    printf("START doSomething() : %d\n", isBroken());
    doFoo();
    doBar();
    printf("END doSomething() : %d\n", isBroken());
}

Si en el registro ves

START doSomething() : 0
END doSomething() : 1

Sabes que algo salió mal allí. Por lo tanto, elimine todos los demás códigos de registro y pruebe esta nueva versión:

void doSomething (void)
{
    printf("START doSomething() : %d\n", isBroken());
    doFoo();
            printf("AFTER doFoo() : %d\n", isBroken());
    doBar();
    printf("END doSomething() : %d\n", isBroken());
}

Ahora en el registro puedes ver esto

START doSomething() : 0
AFTER doFoo() : 0
END doSomething() : 1

Entonces, ahora sabe que doBar () activa el error y puede repetir el procedimiento anterior en doBar (). Idealmente, reducirá el error a una sola línea.

Por supuesto, esto puede ayudarlo a revelar los síntomas del error y no la causa raíz ; por ejemplo, encuentra un puntero NULL que no debería ser NULL, pero ¿por qué? Luego puede volver a iniciar sesión, pero buscando una condición diferente "rota".

Si tiene un bloqueo en lugar de un estado roto, es más o menos lo mismo: la última línea del registro le da una pista de dónde se rompen las cosas.

ggambett
fuente
2

Después de que las otras respuestas han fallado , siempre hay depuración de búsqueda binaria :

  1. Eliminar una cierta porción (preferiblemente la mitad) de las posibles causas (líneas de código, revisiones , entradas, etc.)
  2. Intenta reproducir el problema nuevamente.
  3. Si el problema persiste: regrese al paso 1.
  4. Si solo le queda una causa (línea de código, revisión, entrada, etc.): ¡hurra! Procedimiento de salida.
  5. De lo contrario: revierta el paso 1 y ahora elimine la otra mitad.
  6. Regrese al paso 2.

Nota: obviamente, esta técnica solo funciona si puede reproducir el problema de manera confiable.

Jeroen
fuente
1. Eliminar la mitad de las posibles causas. El problema desaparece. 2. Restaure esa mitad y elimine la otra. El problema desaparece. 3. Elimine solo algunas causas posibles. El problema desaparece si elimina cualquier 20% arbitrario de ellos. 4. Comience a examinar el rendimiento, el motor subyacente y correr en círculos. 5. Pánico.
SF.
Con letras grandes y amigables.
Jeroen
0

"La herramienta de depuración más eficaz sigue siendo una reflexión cuidadosa, junto con declaraciones impresas juiciosas". - Brian Kernighan 'En su día puede haber sido cierto! El método más efectivo es mirar las pruebas unitarias, pero supongo que no tienes ninguna.


fuente
No tengo pruebas unitarias ya que no tengo ningún proyecto o código en particular; Sólo estoy pidiendo métodos de depuración
Anto
¿Por qué votarías esta respuesta? Deja de orinar en la oscuridad y prueba unitaria entonces.
No era yo el que downvoted así que ve llorar a otra persona
Anto
2
Las pruebas unitarias no reemplazan la depuración, simplemente ayudan a compartimentar y restringir los errores. Lo que simplifica la depuración, cuando aparece un error dentro de una prueba unitaria codificada. IME, la mayoría de los errores difíciles están en interacciones de componentes (prueba difícil de la unidad), y se ven con mucha más frecuencia en los conjuntos de pruebas basher de estilo de regresión.
Clayton Stanley
-1) ¿Cómo se repara el código identificado por una prueba unitaria rota? Se depura ... Las pruebas unitarias detectan errores, depuradores y depuración se utilizan para corregir el defecto.
mattnz
0

Depende del error.

Si el error es el tipo de 'por qué el código está haciendo A', entonces puede ser útil probar su propia comprensión del código que rodea la ubicación del 'código haciendo A'. Introduzca el nuevo código que espera generar nuevos errores (este código debería hacer que haga B, esto debería hacer que haga C). Por lo general, encuentro rápidamente algún código nuevo que genera un comportamiento que no espero. Luego espero pacientemente mientras mi mente construye un modelo mental actualizado del comportamiento del código para que el último cambio tenga sentido, y luego ese cambio del modelo mental generalmente explica por qué el código está haciendo A.

El segundo caso ha sido discutido en detalle aquí. Donde haya heredado el código, no tenga un modelo mental sólido de cómo funciona el código, no tenga una buena idea sobre la ubicación específica del error, etc. En este caso, desglose / división y- los métodos de conquista con declaraciones impresas pueden funcionar. Y si está bajo control de origen, asegúrese de verificar el cambio de código más reciente.

Clayton Stanley
fuente
0

Ampliando la "La herramienta de depuración más efectiva sigue siendo un pensamiento cuidadoso, junto con declaraciones impresas juiciosamente colocadas".

Primero, intente reducir el momento en que ocurre el error. Haga que los síntomas observables por el usuario sean observables por el sistema. (por ejemplo, algunas cadenas cambian a galimatías, agregue un bucle que sondea el contenido del script y activa su depuración a medida que cambia). Por supuesto, si el error es un bloqueo, agregue el manejo por defecto.

Luego, intente reducir el hilo si el problema es con un entorno de subprocesos múltiples. Dé a cada hilo un identificador y bótelo cuando ocurra el error.

Una vez que tenga el hilo, espolvoree el código del hilo dado con printfs copiosamente para clavar el punto donde emerge.

Luego, regrese a donde ocurre la acción real que lo crea (la acción destructiva a menudo será bastante antes de que los datos dañados desencadenen el problema). Examine qué estructuras / variables ocurren cerca de la memoria, observe los bucles que los afectan, verifique los puntos donde se escribe el valor dañado.

Una vez que tenga el punto que estaba causando el problema, antes de solucionarlo, piense dos veces cuál debería ser el comportamiento correcto.

SF.
fuente