NovaMonoFix
Errores PHP
X
Usuario
Password
0 FPS

Tutorial C++ 4 Creación de archivos DUMP

05 de Octubre del 2011 por Josep Antoni Bover3805 visitas, 0 comentarios, 2 votos con una media de 5 sobre 5.
Categorías : Windows, Programación, C y C++.
La web está en segundo plano, animación en pausa.
Cargando animación...
Tutorial C++ 4 Creación de archivos DUMP

Los archivos DUMP son una forma de depurar aplicaciones con windows. La idea consiste en que cuando una aplicación falla críticamente se guarda un archivo con el contenido de la memoria de esta.

Más tarde podemos abrir esos archivos con el Visual Studio y depurar la aplicación (siempre que tengamos su código fuente a mano).

Por ejemplo BubaTronik si tiene un error critico, crea un archivo dump y pide al usuario permiso para mandarme este archivo dump a mi cuenta de correo. Cosa que es muy útil para depurar errores que yo no he visto en mi maquina.

Para ver cómo funciona este proceso, vamos a hacer una aplicación muy básica que consistirá en una ventana vacía, la cual al ser clickeada con el botón izquierdo provocara un error crítico.

Función MAIN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int APIENTRY WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpCmdLine, int nCmdShow){
// Asignación del filtro de errores
LPTOP_LEVEL_EXCEPTION_FILTER Filtro;
Filtro = SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)FiltroError);
// Creación de la clase ventana
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hIns;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = TEXT("VentanaDump");
wcex.hIconSm = NULL;
RegisterClassEx(&wcex);
// Creación de la ventana
HWND hWndVentana = CreateWindowEx( NULL, TEXT("VentanaDump"), TEXT("Ventana Dump"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 200, 200, NULL, NULL,
GetModuleHandle(NULL), NULL);
// Bucle de mensajes principal
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}

Lo más destacable de esta función WinMain son las dos primeras líneas de código (3,4). En la primera declaramos una variable del tipo LPTOP_LEVEL_EXCEPTION_FILTER diseñada para guardar la dirección de una función que hace de filtro de errores.

Luego llamamos a la API SetUnhandledExceptionFilter para asignar nuestra función para filtrar los errores, y esta nos devuelve la dirección del filtro de errores por defecto que tenía asignado la aplicación.

Con esto acabamos de re-direccionar los errores críticos a nuestra propia función, en la cual podemos entre otras osas escribir el archivo DUMP.

Función FiltroError

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
LONG WINAPI FiltroError(struct _EXCEPTION_POINTERS* ExceptionInfo) {
TCHAR PathAppData[MAX_PATH];
TCHAR PathDump[MAX_PATH];
if (ExceptionInfo == NULL) return -1;
// Obtenemos la carpeta APP_DATA donde podemos escribir cosas sin necesidad de permisos de administrador
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, PathAppData);
swprintf_s(PathDump, MAX_PATH, TEXT("%s\\DUMP.dmp"), PathAppData);
// Creamos el archivo DUMP.dmp para empezar a escribir los datos
HANDLE hFile = CreateFile(PathDump, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Si no hay ningún error al crear el archivo..
if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) {
// Rellenamos la estructura con la información de la excepción
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = ExceptionInfo;
mdei.ClientPointers = FALSE;
// Rellenamos la estructura con la información del callback
MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = NULL;
mci.CallbackParam = 0;
// Creamos una variable con las opciones que se usaran para crear el dump
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory | MiniDumpNormal);
// Escribimos el dump en el archivo
BOOL rv;
rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, (ExceptionInfo != 0) ? &mdei : 0, 0, &mci);
// Cerramos el archivo dump
CloseHandle(hFile);
// Abrimos la carpeta donde se ha guardado el DUMP.dmp
ShellExecute(NULL, TEXT("open"), PathAppData, TEXT(""), PathAppData, SW_SHOW);
}
return TRUE;
}

Esta función en primer lugar obtiene la ruta del directorio APP_DATA mediante la API SHGetFolderPath.

Una vez tiene la ruta donde guardar el archivo crea el archivo DUMP.dmp con la API CreateFile.

Cuando se ha usado la API CreateFile, comprueba que el archivo se ha creado correctamente, y crea e inicializa las variables necesarias para el reporte de error.

Una tenemos las variables inicializadas se utiliza la API MiniDumpWriteDump para escribir el volcado de memoria dentro del archivo que creamos anteriormente.

Cuando tenemos los datos en el archivo, lo cerramos utilizando la API CloseHandle.

Y por ultimo se utiliza la API ShellExecute para mostrarnos el directorio donde ha guardado el archivo DUMP.dmp, para que de esta forma nos sea más fácil de ejecutarlo.

Ahora que ya hemos visto la función FiltroError, nos falta por ver como provocamos el error en el WndProc :

Función WndProc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Window procedure para la ventana que provoca el error.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
int *i = NULL;
switch (message) {
case WM_LBUTTONUP :
MessageBox( hWnd, TEXT("Se va a provocar un error, para que podáis abrir el archivo DUMP.dmp con el visual studio"),
TEXT("Provocando error"), MB_OK);
*i = 2; // intentamos asignar el valor 2 a un puntero que no se ha inicializado, lo que provoca un error critico
return 0;
case WM_CLOSE :
PostQuitMessage(0);
return 0;
default :
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Ahora que tenemos una idea bastante más clara de cómo funciona todo esto, vamos a ver cómo hay que utilizar estos archivos 'dmp'.

En primer lugar esto solo funciona si la aplicación está corriendo fuera del depurador, puede ser tanto debug como release, pero es necesario que no la estemos ejecutando desde VisualStudio, así que lo primero será compilar el ejecutable (para hacer la prueba mejor lo compilaremos en modo Release), una vez compilado nos vamos a la carpeta donde tenemos los tutoriales WINAPI, y entramos dentro de la carpeta 'Release'. Allí buscamos el archivo '4 Tutorial crear archivo DUMP.exe', y lo ejecutamos. Una vez ejecutado hacemos un click izquierdo dentro de la ventana que nos sale, para provocar el error, y nos aparecerá un MessageBox. Una vez aceptemos el MessageBox se abrirá una carpeta del explorador, y si buscamos en esta encontraremos un archivo llamado 'DUMP.dmp'. Al hacer dobleclick en el archivo se abriría el Visual Studio y nos mostrara una pantalla similar a esta :

Depuración con VisualStudio 2010 Depuración con VisualStudio 2010

A la derecha de esta pantalla debería salir un menú que nos da tres opciones : 'Depurar con Solo Nativo', 'Establecer rutas de acceso de símbolos' y 'Copiar todo en el Portapapeles'.

La razón de que veamos esta pantalla es porque el Visual Studio no ha sabido encontrar los símbolos para poder depurar la aplicación, por lo que vamos a tener que indicarle la ruta de donde puede encontrar estos símbolos.

Por lo que vamos a hacer click en 'Establecer rutas de acceso de símbolos', y nos mostrara una pantalla similar a esta, aunque probablemente vacía :

Depuración con VisualStudio 2010

En esta pantalla vamos a tener que añadir el directorio donde se encuentra el ejecutable '4 Tutorial crear archivo DUMP.exe', que también es el mismo donde se han creado los símbolos de depuración.

Finalmente ya podemos hacer click en "Depurar solo nativo" y voila, vemos la línea de código que ha petado.

En Visual Studio 2008 a mi no me sale la pantalla del resumen, y me sale todo en gris, pero me permite utilizar la flecha verde para ejecutar, al utilizar la flecha verde me sale el código con la línea de error directamente, sin necesidad de establecer ninguna ruta para los símbolos. Por ello no sé exactamente cómo funciona esto, pero con los pasos descritos anteriormente debería funcionar todo a la perfección.

Y esto es todo!, en un futuro si encuentro un servidor de correo publico que permita enviar emails mediante smtp veremos cómo podemos mandarnos archivos dump al correo en caso de que una de nuestras aplicaciones tenga un error critico.

De todas formas ya os aviso que si queréis hacer algo así, el tema de enviar el correo se tiene que hacer ejecutando una nueva instancia de la aplicación, y no desde el filtro de errores.