Red de conocimiento informático - Material del sitio web - Cómo localizar la ubicación del código que provocó el bloqueo

Cómo localizar la ubicación del código que provocó el bloqueo

1. Localización de errores de bloqueo en el entorno de desarrollo

1.1 Bloqueo común

Echemos un vistazo primero al bloqueo más común

Consulte la Figura 1 (c01.png)

Cuando ejecute el programa anterior en modo de depuración, aparecerá el cuadro anterior. vc le ayudará a localizar la ubicación incorrecta. Es una operación sobre el puntero cero. Bastante simple, ¿no?

1.2 Fallos difíciles de localizar

Los fallos difíciles de localizar suelen deberse a errores de memoria (consulte 5.1 Por qué la pila de llamadas se estropea cuando el programa falla). Por ejemplo, el siguiente código:

Código

char *p = new char[16];

p[10] = 0xfd;

eliminar[] p;

printf(p);

Hay dos errores en el código anterior. Uno es que la escritura en memoria en la línea 2 está fuera de los límites. y la otra es que la línea 4 usa el puntero eliminado.

Pero el código anterior no informará un error durante el lanzamiento o la depuración de vc. Esto hace que tales errores sean difíciles de localizar.

Para detectar este tipo de problema, puede utilizar el modo FinalCheck (BoundsChecker) de la herramienta BoundsChecker.

Después de utilizar BoundsChecker para detectarlo, obtendrá dos errores: Desbordamiento de escritura ( escribir fuera de límites) y puntero colgante (use el puntero eliminado) y están ubicados exactamente en la ubicación del error. Es una buena herramienta.

Ver Figura 2 (c02.png)

Ver Figura 3 (c03.png)

1.3 Preste atención al registro de salida de vc

Debido a algunas razones actualmente desconocidas (posiblemente un error grave del programa o un error en el propio BoundsChecker), BoundsChekcer a veces no funciona correctamente.

El registro de salida de vc aquí a veces puede proporcionar información útil.

En los accidentes difíciles de encontrar, una gran proporción de ellos hacen referencia a indicaciones ilegales.

A veces puede ver información similar a esta en el registro de salida de vc

"La excepción más probable en 0x004277b7 en emule.exe: 0xC0000005: ocurrió al leer la ubicación 0xfeeeff62 Infracción de acceso ."

Esta es una información importante en ausencia de soporte de BoundsChecker. Significa que el "puntero con valor 0xfeeeff62" se opera en la "dirección del programa 0x004277b7".

(Cómo encontrar la línea de código correspondiente a través de la "Dirección del programa 0x004277b7" puede consultar 3.1)

La importancia de esta información es que esta operación solo activará una advertencia. not Provocará un bloqueo. Cuando el bloqueo realmente ocurra, es probable que no esté cerca de 0x004277b7.

Incluso la pila de llamadas se ha escrito de forma desordenada, lo que le impide comenzar. (Consulte 5.1 Por qué la pila de llamadas se estropea cuando el programa falla)

2. Localice errores de fallas en las versiones publicadas

Cuando el software lanzado externamente falla, a menudo es difícil depurarlo. En la actualidad, muchos programas tienen la función de "enviar informe de errores".

Lograr esta función generalmente implica los siguientes pasos:

a. Usar la función SetUnhandledExceptionFilter

Usar SetUnhandledExceptionFilter para configurar la función de manejo de excepciones de nivel más alto. ocurre el programa. Cualquier excepción no controlada activará la función que establezca. Para un uso específico, consulte el código fuente de msdn y emule.

b. Utilice la función MiniDumpWriteDump

En su función de manejo de excepciones, utilice MiniDumpWriteDump para guardar la información del error en un archivo en un formato específico.

Para un uso específico, consulte el código fuente de msdn y emule.

c.Enviar informe de errores

Elija un formulario para enviar el archivo de informe de errores (.dmp) generado en el segundo paso al lugar que especifique.

d.Ver el informe de errores

Aquí presentamos el método de usar vc para ver el informe de errores. También puede usar la herramienta de herramientas de depuración de Windows para verlo. , consulte 5.2 Usar herramientas de depuración de Windows para ver el archivo .dmp (Informe de errores)

Para ver el informe de errores, necesita tres cosas: el código correspondiente a la versión de lanzamiento y los archivos .exe y . Archivos pdb generados al compilar la versión de lanzamiento en ese momento. (Ambos archivos están en el directorio de salida compilado). Por lo tanto, cuando se lance el programa, estos dos archivos deben conservarse.

Coloque el código .dmp (archivo de informe de errores), .pdb y .exe en el mismo directorio y use vc para abrir el archivo .dmp.

Presione F5 para ejecutarlo y el programa alcanzará el estado en el que falló y podrá analizarlo en consecuencia.

Una pequeña adición: cuando no existe la función "Enviar informe de errores", o esta función falla, aparece el cuadro de diálogo "Enviar informe de errores" de Windows. En realidad, hay un informe de error en este momento, generalmente un archivo .dmp (generalmente solo un .dmp) en C:Documentos y configuración nombre de usuario Configuración localTemp

Consejos

3.1 Buscar. la ubicación del código según la dirección del programa

Siga los siguientes pasos:

a. (Por ejemplo, cuando el programa se esté ejecutando, presione Ctrl+Alt+Break en vc o establezca un punto de interrupción para detener el programa)

b. (Ctrl+F11)

c. Ingrese la dirección del programa en la barra de direcciones y presione Enter.

d. Presione Ctrl+F11 para volver al modo de código.

3.2 Ver el mensaje de Windows correspondiente según el valor del mensaje

Ingrese "valor del mensaje, wm" en la ventana de monitoreo de vc para ver el mensaje correspondiente.

3.3 Ver el valor de retorno de GetLastError

Ingrese "@err,hr" en la ventana de monitoreo de vc, podrá ver el LastError y su explicación.

3.4 Pausar el programa en el código

En la versión de depuración, puede agregar "AfxDebugBreak();" al código para pausar el programa. La versión de lanzamiento puede usar "_asm int 3;"

4. Advertencias de programación

4.1 Utilice la serie de funciones IsBadPtr con precaución

Cuando utilice IsBadReadPtr, IsBadWritePtr , IsBadCodePtr Tenga cuidado al utilizar una serie de funciones. Es posible que este tipo de función no logre lo que desea.

Por ejemplo, en el siguiente código, ambas devoluciones son falsas.

Código

char *p = new char[10];

bool b;

b = IsBadReadPtr(p+10, 1);

eliminar[] p;

char *q = nuevo char[10];

b = IsBadReadPtr(p, 1); p>

Así que asegúrese de no utilizar la función IsBadPtr en su programa para determinar si se puede operar con este puntero. Estas funciones solo se pueden utilizar durante la depuración o cuando se sabe exactamente qué significan los valores de retorno de estas funciones.

4.2 Utilice catch(...) con precaución

Para evitar que el programa falle o resolver un fallo poco claro, es fácil para todos pensar en intentarlo{ }catch(... ){} para resolver el problema.

Es cierto que la mayoría de las veces esto no causará problemas, pero es probable que este try-catch oculte el error en el intento, y cuando este error cause otros errores, será difícil rastrearlo. este problema.

Por lo tanto, se recomienda usar catch(...) lo menos posible. Si sabe que un determinado fragmento de código generará una excepción, debe usar el método de escritura exacto. Por ejemplo, catch(CFileException *e), catch(int e), etc.

5. Apéndice

5.1 ¿Por qué la pila de llamadas está desordenada cuando el programa falla?

Cuando la memoria se escribe de manera desordenada, es probable que el programa tenga un error. fallas que son difíciles de localizar, como que la pila de llamadas está desordenada o se encuentra con un código inexistente. Aquí hay un ejemplo

Código

clase CA

{

público:

CA(){}

~CA(){}

virtual f(){}

};

void show()

{

printf("mostrado");

}

int main()

{

// El objeto se crea y elimina

CA *p = new CA;

eliminar p;

// Algunas operaciones normales

int *q = new int;

int codeAddress = (int)show;

*q = (int)&codeAddress;

// Llamado eliminado objetos, el programa se puede ejecutar en cualquier lugar.

p->f();

}

El resultado del código anterior se mostrará en show() (esto está relacionado con el entorno de compilación). , Los resultados de las pruebas bajo vc2003 son los siguientes). Si comprende el mecanismo vtable de C++, comprenderá lo que está sucediendo.

Está bien si no lo entiendes. Te daré una idea aproximada a continuación.

Primero, se crea y elimina el objeto CA. Si llama a p->f() en este momento, probablemente fallará.

El segundo bloque de código puede considerarse como algunas operaciones normales. Crea una nueva q y luego le asigna algunos valores.

Si no comprende el mecanismo de vtable, entonces solo necesita saber que "p->f();" en la última línea ejecutará la dirección marcada por la variable señalada por variable q. (No hay ningún error tipográfico aquí, son dos "variables puntiagudas")

Aquí "amablemente" asigné una función legal para mostrar la dirección a este valor. Sin embargo, en el programa real, la variable señalada por q puede tener cualquier valor, y la variable a la que apunta puede incluso tener cualquier valor.

Entonces el programa no sabrá adónde fue. Si este valor es demasiado escandaloso, entonces tienes suerte, el programa fallará inmediatamente y sabrás dónde está el error. Pero lo molesto es que puede ser una dirección legal y el programa continuará funcionando,

pero tarde o temprano fallará y la pila de llamadas quedará irreconocible (la razón tiene que ver con el problema de "derivación de la pila de llamadas" No hay mucho que decir aquí),

En ese momento, no habrá forma de saber que fue causado originalmente por llamar al objeto p eliminado.

5.2 Utilice herramientas de depuración para Windows para ver archivos .dmp (informes de error)

a. Prepare el código, el archivo exe y el archivo pdb correspondientes al programa (en la salida de la compilación). directorio durante la compilación) )

b. Instalar WinDbg

c. En winDbg, configure el directorio de símbolos en el directorio donde se encuentra .pdb, el directorio de imágenes en el directorio donde se encuentra .exe se encuentra, y el directorio de códigos al directorio de códigos.

d.Abra el archivo .dmp

e. (Este comando devuelve el entorno al estado en el que se bloqueó)

f. Abra la pila de llamadas (ALT + F6) para ver la ubicación del bloqueo

g. /p>

Introducción

(Consulte el Apéndice 1 para ver la lista de errores que FinalCheck puede detectar)

BoundsChecker es una herramienta de depuración muy poderosa. Aquí solo presentamos brevemente cómo usar su modo FinalCheck para localizar errores que son difíciles de localizar.

El modo FinalCheck simplemente significa que BoundsChecker agrega algún código de diagnóstico a su código para verificar si hay memoria fuera de límites, uso incorrecto del puntero, etc. que generalmente son difíciles de detectar.

Sin embargo, el precio que se paga es que el programa se ejecutará más lento, por lo que es mejor desactivar el modo FinalCheck cuando no esté en uso. Especialmente antes del lanzamiento.