Логируем необрабатываемые исключения

HTTP/1.1 200 OK Server: httpd Date: Sun, 23 Dec 2012 22:14:53 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Accept-Encoding Vary: Cookie X-Pingback: http://kaimi.ru/xmlrpc.php Link: ; rel=shortlink Логируем необрабатываемые исключения

 

Print This Post Логируем необрабатываемые исключения

Пятница, 21. Декабрь 2012
Раздел: C/C++, Windows, автор: Kaimi

Надоело созерцать зелёную морду на титульной странице блога, поэтому настало время эту морду сместить.

Иногда при разработке мелких сетевых утилит приходится сталкиваться с ситуацией, когда софт был загружен на некий удаленный сервер, поработал немного и через какое-то время прекратил работать по неизвестной причине. Конечно, можно было бы вручную заходить на каждый сервер и пытаться разобраться, что произошло, но это несколько утомительное занятие. Для автоматизации процесса я решил набросать небольшую статическую библиотеку, которая будет заниматься логированием подобных ошибок и отправкой их на сервер.

Основу библиотеки фактически будет составлять одна функция - MiniDumpWriteDump, которая делает дамп памяти процесса. Этот дамп впоследствии можно открыть, например, в WinDBG и посмотреть причину неожиданного падения процесса. Также хочу отметить, что мы "покладем" на замечание из MSDN о необходимости вызова данной функции из отдельного процесса.

Итак, начнем с основной функции, которая будет обрабатывать исключение и создавать мини-дамп.

/* Функция получает на вход информацию об исключении, путь для сохранения мини-дампа, включая имя файла, *//* тип мини-дампа, адрес хоста, куда будет отправлен мини-дамп, и путь к скрипту, который его примет */
BOOL process_exception(EXCEPTION_POINTERS * exception, PTCHAR dump_path, MINIDUMP_TYPE type, PTCHAR host, PTCHAR uri){
    MINIDUMP_EXCEPTION_INFORMATION ex_info;
    HANDLE file;
    TCHAR path[MAX_PATH];/* Заполним структуру, необходимую для создания дампа, информацией о нашем исключении */
    ex_info.ThreadId= GetCurrentThreadId();
    ex_info.ExceptionPointers= exception;
    ex_info.ClientPointers= FALSE;
 
    /* Разворачиваем переменные окружения, если они присутствуют */
    ExpandEnvironmentStrings(dump_path, path, _countof(path));
 
    /* Открываем хендл и создаем мини-дамп */
    file = CreateFile(path, GENERIC_WRITE,0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if(file == INVALID_HANDLE_VALUE)return FALSE;
 
    if( MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, type,&ex_info,0,0)== FALSE ){
        CloseHandle(file);return FALSE;}
 
    CloseHandle(file);
 
    /* Отправляем дамп на сервер */if(host != NULL && uri != NULL)
        send_report(host, uri, path);
 
    return TRUE;}

Основной код в общем-то написан, осталось реализовать функцию отправки дампа на сервер и проверить работоспособность методов. Сначала отправка:

BOOL send_report(PTCHAR host, PTCHAR uri, PTCHAR file_path){
    BOOL status = TRUE;
    HINTERNET sess = NULL, conn = NULL, req = NULL;
    HANDLE fh;/* Заголовки для формирования multipart запроса на загрузку файла */conststatic TCHAR headers[]= _T("Content-Type: multipart/form-data; boundary=0123456789");conststaticchar data_head[]="--0123456789\r\n"                                                               \
                                    "Content-Disposition: form-data; name=\"report\"; filename=\"crash.dmp\"\r\n"    \
                                    "Content-Type: application/octet-stream\r\n\r\n";conststaticchar data_tail[]="\r\n--0123456789--";void* post_data = NULL;
    DWORD file_size, post_data_size, aux;
 
    /* Номинальный while, чтобы не использовать goto и поменьше дублировать код */while(TRUE){/* Открываем файл мини-дампа и определяем его размер */
        fh = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if(fh == INVALID_HANDLE_VALUE){
            status = FALSE;break;}
 
        file_size = GetFileSize(fh, NULL);if(file_size == INVALID_FILE_SIZE){
            status = FALSE;break;}
 
        post_data_size = sizeof_wo_null(data_head)+ file_size + sizeof_wo_null(data_tail);/* Выделяем память под содержимое файла + заголовки */
        post_data =malloc(post_data_size);if(post_data == NULL){
            status = FALSE;break;}
 
        ZeroMemory(post_data, post_data_size);/* Формируем тело multipart POST-запроса */
        CopyMemory(post_data, data_head, sizeof_wo_null(data_head));
 
        if( ReadFile(fh,(char*)post_data + sizeof_wo_null(data_head), file_size,&aux, NULL)== FALSE){
            status = FALSE;break;}
 
        CopyMemory((char*)post_data + sizeof_wo_null(data_head)+ file_size, data_tail, sizeof_wo_null(data_tail));
 
        /* Используем функции WinInet для отправки, чтобы не возиться с сокетами */
        sess = InternetOpen(_T("Crash Reporter"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL,0);if(sess == NULL){
            status = FALSE;break;}
 
        conn = InternetConnect(sess, host, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP,0,1);if(conn == NULL){
            status = FALSE;break;}
 
        req = HttpOpenRequest(conn, _T("POST"), uri, NULL, NULL, NULL, INTERNET_FLAG_NO_COOKIES,1);if(req == NULL){
            status = FALSE;break;}
 
        status = HttpSendRequest(req, headers,-1L, post_data, post_data_size);
 
        break;}
 
    /* Закрываем хендлы, освобождаем память */if(fh != INVALID_HANDLE_VALUE)
        CloseHandle(fh);if(post_data != NULL)free(post_data);if(req != NULL)
        InternetCloseHandle(req);if(conn != NULL)
        InternetCloseHandle(conn);if(sess != NULL)
        InternetCloseHandle(sess);
 
 
    return status;}

Всё, у нас есть всё, что необходимо для нашего небольшого логгера исключений. Ах да, забыли про инклюды и один дефайн:

#include <Windows.h>#include <DbgHelp.h>#include <WinInet.h>#include <tchar.h>
 
#pragma comment(lib, "dbghelp.lib")#pragma comment(lib, "wininet.lib")
 
/* Размер массива без учета нулл-байта (исключительно для char) */#define sizeof_wo_null(_Array) (sizeof(_Array) - 1)

Теперь точно всё, проверим работоспособность методов. Напишем пару строк кода, которые будут вызывать падение программы, и добавим SEH, в котором будем ловить наше исключение.
Получим такой вот простой код:

#include <Windows.h>#include <DbgHelp.h>
 
#pragma comment(lib, "report_lib.lib")
 
#ifdef __cplusplusextern"C"{#endif
BOOL process_exception(EXCEPTION_POINTERS * exception, PTCHAR dump_path, MINIDUMP_TYPE type, PTCHAR host, PTCHAR uri);#ifdef __cplusplus}#endif
 
LONG WINAPI SEH(EXCEPTION_POINTERS * lpTopLevelExceptionFilter){
    process_exception(lpTopLevelExceptionFilter, L"%TEMP%\\crash.dmp", MiniDumpNormal, L"kaimi.ru", L"test.php");
 
    return0L;}
 
int main(){
    SetUnhandledExceptionFilter(SEH);
 
    *(DWORD *)0=1;
 
    return0;}

Также для тестирования пригодится примитивный PHP-скрипт, который будет обрабатывать переданный на сервер файл, например, такой:

<?php$target_path='reports/';$target_path=$target_path.mt_rand().'_'.$_SERVER['REMOTE_ADDR'].'_'.time().'.dmp';if(move_uploaded_file($_FILES['report']['tmp_name'],$target_path))echo'ok';elseecho'err';?>

Переходим к проверке. Запускаем программу, наблюдаем сообщение Windows об ошибке, лезем на сервер и забираем дамп. Теперь открываем дамп в WinDbg, пишем !analyze -v и видим причину падения.

Кстати, результат анализа дампа может быть менее понятным в зависимости от отсутствия/наличия PDB файлов и Debug-информации в самом файле.

Исходный код и проект для VS2010: скачать

Также рекомендую почитать

crash Обсудить на форуме



Оставьте ваш комментарий

 

 

HTTP/1.1 200 OK Server: httpd Date: Sun, 23 Dec 2012 22:14:53 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Accept-Encoding Vary: Cookie X-Pingback: http://kaimi.ru/xmlrpc.php Link: ; rel=shortlink Логируем необрабатываемые исключения

 

Логируем необрабатываемые исключения Логируем необрабатываемые исключения

Пятница, 21. Декабрь 2012
Раздел: C/C++, Windows, автор: Kaimi

Надоело созерцать зелёную морду на титульной странице блога, поэтому настало время эту морду сместить.

Иногда при разработке мелких сетевых утилит приходится сталкиваться с ситуацией, когда софт был загружен на некий удаленный сервер, поработал немного и через какое-то время прекратил работать по неизвестной причине. Конечно, можно было бы вручную заходить на каждый сервер и пытаться разобраться, что произошло, но это несколько утомительное занятие. Для автоматизации процесса я решил набросать небольшую статическую библиотеку, которая будет заниматься логированием подобных ошибок и отправкой их на сервер.

Основу библиотеки фактически будет составлять одна функция - MiniDumpWriteDump, которая делает дамп памяти процесса. Этот дамп впоследствии можно открыть, например, в WinDBG и посмотреть причину неожиданного падения процесса. Также хочу отметить, что мы "покладем" на замечание из MSDN о необходимости вызова данной функции из отдельного процесса.

Итак, начнем с основной функции, которая будет обрабатывать исключение и создавать мини-дамп.

/* Функция получает на вход информацию об исключении, путь для сохранения мини-дампа, включая имя файла, *//* тип мини-дампа, адрес хоста, куда будет отправлен мини-дамп, и путь к скрипту, который его примет */
BOOL process_exception(EXCEPTION_POINTERS * exception, PTCHAR dump_path, MINIDUMP_TYPE type, PTCHAR host, PTCHAR uri){
    MINIDUMP_EXCEPTION_INFORMATION ex_info;
    HANDLE file;
    TCHAR path[MAX_PATH];/* Заполним структуру, необходимую для создания дампа, информацией о нашем исключении */
    ex_info.ThreadId= GetCurrentThreadId();
    ex_info.ExceptionPointers= exception;
    ex_info.ClientPointers= FALSE;
 
    /* Разворачиваем переменные окружения, если они присутствуют */
    ExpandEnvironmentStrings(dump_path, path, _countof(path));
 
    /* Открываем хендл и создаем мини-дамп */
    file = CreateFile(path, GENERIC_WRITE,0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if(file == INVALID_HANDLE_VALUE)return FALSE;
 
    if( MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, type,&ex_info,0,0)== FALSE ){
        CloseHandle(file);return FALSE;}
 
    CloseHandle(file);
 
    /* Отправляем дамп на сервер */if(host != NULL && uri != NULL)
        send_report(host, uri, path);
 
    return TRUE;}

Основной код в общем-то написан, осталось реализовать функцию отправки дампа на сервер и проверить работоспособность методов. Сначала отправка:

BOOL send_report(PTCHAR host, PTCHAR uri, PTCHAR file_path){
    BOOL status = TRUE;
    HINTERNET sess = NULL, conn = NULL, req = NULL;
    HANDLE fh;/* Заголовки для формирования multipart запроса на загрузку файла */conststatic TCHAR headers[]= _T("Content-Type: multipart/form-data; boundary=0123456789");conststaticchar data_head[]="--0123456789\r\n"                                                               \
                                    "Content-Disposition: form-data; name=\"report\"; filename=\"crash.dmp\"\r\n"    \
                                    "Content-Type: application/octet-stream\r\n\r\n";conststaticchar data_tail[]="\r\n--0123456789--";void* post_data = NULL;
    DWORD file_size, post_data_size, aux;
 
    /* Номинальный while, чтобы не использовать goto и поменьше дублировать код */while(TRUE){/* Открываем файл мини-дампа и определяем его размер */
        fh = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if(fh == INVALID_HANDLE_VALUE){
            status = FALSE;break;}
 
        file_size = GetFileSize(fh, NULL);if(file_size == INVALID_FILE_SIZE){
            status = FALSE;break;}
 
        post_data_size = sizeof_wo_null(data_head)+ file_size + sizeof_wo_null(data_tail);/* Выделяем память под содержимое файла + заголовки */
        post_data =malloc(post_data_size);if(post_data == NULL){
            status = FALSE;break;}
 
        ZeroMemory(post_data, post_data_size);/* Формируем тело multipart POST-запроса */
        CopyMemory(post_data, data_head, sizeof_wo_null(data_head));
 
        if( ReadFile(fh,(char*)post_data + sizeof_wo_null(data_head), file_size,&aux, NULL)== FALSE){
            status = FALSE;break;}
 
        CopyMemory((char*)post_data + sizeof_wo_null(data_head)+ file_size, data_tail, sizeof_wo_null(data_tail));
 
        /* Используем функции WinInet для отправки, чтобы не возиться с сокетами */
        sess = InternetOpen(_T("Crash Reporter"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL,0);if(sess == NULL){
            status = FALSE;break;}
 
        conn = InternetConnect(sess, host, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP,0,1);if(conn == NULL){
            status = FALSE;break;}
 
        req = HttpOpenRequest(conn, _T("POST"), uri, NULL, NULL, NULL, INTERNET_FLAG_NO_COOKIES,1);if(req == NULL){
            status = FALSE;break;}
 
        status = HttpSendRequest(req, headers,-1L, post_data, post_data_size);
 
        break;}
 
    /* Закрываем хендлы, освобождаем память */if(fh != INVALID_HANDLE_VALUE)
        CloseHandle(fh);if(post_data != NULL)free(post_data);if(req != NULL)
        InternetCloseHandle(req);if(conn != NULL)
        InternetCloseHandle(conn);if(sess != NULL)
        InternetCloseHandle(sess);
 
 
    return status;}

Всё, у нас есть всё, что необходимо для нашего небольшого логгера исключений. Ах да, забыли про инклюды и один дефайн:

#include <Windows.h>#include <DbgHelp.h>#include <WinInet.h>#include <tchar.h>
 
#pragma comment(lib, "dbghelp.lib")#pragma comment(lib, "wininet.lib")
 
/* Размер массива без учета нулл-байта (исключительно для char) */#define sizeof_wo_null(_Array) (sizeof(_Array) - 1)

Теперь точно всё, проверим работоспособность методов. Напишем пару строк кода, которые будут вызывать падение программы, и добавим SEH, в котором будем ловить наше исключение.
Получим такой вот простой код:

#include <Windows.h>#include <DbgHelp.h>
 
#pragma comment(lib, "report_lib.lib")
 
#ifdef __cplusplusextern"C"{#endif
BOOL process_exception(EXCEPTION_POINTERS * exception, PTCHAR dump_path, MINIDUMP_TYPE type, PTCHAR host, PTCHAR uri);#ifdef __cplusplus}#endif
 
LONG WINAPI SEH(EXCEPTION_POINTERS * lpTopLevelExceptionFilter){
    process_exception(lpTopLevelExceptionFilter, L"%TEMP%\\crash.dmp", MiniDumpNormal, L"kaimi.ru", L"test.php");
 
    return0L;}
 
int main(){
    SetUnhandledExceptionFilter(SEH);
 
    *(DWORD *)0=1;
 
    return0;}

Также для тестирования пригодится примитивный PHP-скрипт, который будет обрабатывать переданный на сервер файл, например, такой:

<?php$target_path='reports/';$target_path=$target_path.mt_rand().'_'.$_SERVER['REMOTE_ADDR'].'_'.time().'.dmp';if(move_uploaded_file($_FILES['report']['tmp_name'],$target_path))echo'ok';elseecho'err';?>

Переходим к проверке. Запускаем программу, наблюдаем сообщение Windows об ошибке, лезем на сервер и забираем дамп. Теперь открываем дамп в WinDbg, пишем !analyze -v и видим причину падения.

Кстати, результат анализа дампа может быть менее понятным в зависимости от отсутствия/наличия PDB файлов и Debug-информации в самом файле.

Исходный код и проект для VS2010: скачать

Также рекомендую почитать

Print This Post Обсудить на форуме



Оставьте ваш комментарий

 

 

Источник: http://feedproxy.google.com/~r/kaimi/dev/~3/ZXkYvPEllKw/

Читать комменты и комментировать

Добавить комментарий / отзыв



Защитный код
Обновить

Логируем необрабатываемые исключения | | 2012-12-21 19:19:00 | | Блоги и всяко-разно | | HTTP/1.1 200 OK Server: httpd Date: Sun, 23 Dec 2012 22:14:53 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Accept-Encoding Vary: Cookie | РэдЛайн, создание сайта, заказать сайт, разработка сайтов, реклама в Интернете, продвижение, маркетинговые исследования, дизайн студия, веб дизайн, раскрутка сайта, создать сайт компании, сделать сайт, создание сайтов, изготовление сайта, обслуживание сайтов, изготовление сайтов, заказать интернет сайт, создать сайт, изготовить сайт, разработка сайта, web студия, создание веб сайта, поддержка сайта, сайт на заказ, сопровождение сайта, дизайн сайта, сайт под ключ, заказ сайта, реклама сайта, хостинг, регистрация доменов, хабаровск, краснодар, москва, комсомольск |
 
Поделиться с друзьями: