2010-03-01 7 views
6

Если я использую класс/метод, например one described here, как я могу получить описание/адрес вызова в верхней части стека?Как получить строковое описание сбоя Win32 во время фильтрации верхнего уровня (я ищу адрес инструкции в верхней части стека)

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

(Это, как правило, что-то в виде MyDLL.dll 1234ABDC (!))

EDIT:

Некоторые справочная информация:

Я создаю минидампа по электронной почте для отслеживания дефектов системы (fogbugz). Чтобы уменьшить дубликаты, я пытаюсь придумать разумную «подпись» для сбоя. Я знаю, что для FB есть xml PI, но для этого требуется вход в систему, и мы пока не уверены, что можем позволить людям обнюхивать наш трафик и получать информацию о пользователях. Электронная почта также проще реализовать. Позже мы будем использовать XML API для отправки мини-дисков.

ответ

1

Структура EXCEPTION_POINTERS, которая отправляется на адрес TopLevelFilter(), содержит структуру EXCEPTION_RECORD, которая содержит ExceptionAddress. Какой из этих адресов вы можете выяснить, в какой DLL-код оскорбительного кода перечислены модули с CreateToolhelp32Snapshot. Вы также можете использовать функции в dbghelp.dll, чтобы найти символ, который соответствует адресу (функция, в которой он находится)

1

GetExceptionInformation вернет структуру EXCEPTION_POINTERS, содержащую информацию об исключении. Элемент ExceptionRecord содержит элемент ExceptionAddress, который является адресом исключения.

Вам нужно будет сопоставить этот адрес с относительным местоположением модуля в вашем коде, чтобы быть полезным. Вы можете использовать GetModuleHandleEx с GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, чтобы получить HMODULE (который также является базовым адресом модуля). GetModuleInformation может затем использоваться для получения фактического имени модуля, в котором произошло исключение.

Это может быть и не так полезно, если ошибка на самом деле находится внутри системной DLL. Более сложной схемой было бы создание трассировки стека (используя Stackwalk64 в dbghelp) и игнорирование самых верхних кадров, которые не входят в ваш код.

+0

+1 да, возможно, лучше просто пройти стек, пока я не получу первый вызов, который является нашим кодом. – Tim

4

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

try 
{ 
    // whatever 
} 
except (MyExceptionFilter(GetExceptionInformation())) 
{ 
} 

Ваш фильтр будет выглядеть примерно так

LONG WINAPI MyExceptionFilter (
    EXCEPTION_POINTERS * pExcept, 
    BOOL     fPassOn) 
{ 
    EXCEPTION_RECORD * pER = pExcept->ExceptionRecord; 
    DWORD dwExceptionCode = pER->ExceptionCode; 

    TCHAR szOut[MAX_PATH*4]; // exception output goes here. 
    szOut[0] = 0; 

    MEMORY_BASIC_INFORMATION mbi; 
    DWORD cb = VirtualQuery (pER->ExceptionAddress, &mbi, sizeof(mbi)); 
    if (cb == sizeof(mbi)) 
     { 
     TCHAR szModule[MAX_PATH]; 
     if (GetModuleFileName ((HMODULE)mbi.AllocationBase, szModule, MAX_PATH)) 
     { 
     wsprintf(szOut, "Exception at '%s' + 0x%X", szModule, 
        (ULONG_PTR)pER->ExceptionAddress - (ULONG_PTR)mbi.AllocationBase); 
     } 
     } 

    return EXCEPTION_EXECUTE_HANDLER; 
} 

Конечно, вам нужно будет настроить выход немного для 64-битных архитектур, так как ExceptionAddress и AllocationBase будет 64-битные величины в том, что дело.

0

Вы можете избежать боли при печати строки для исключения (что произойдет, если вы можете сохранить мини-накопитель, но не можете форматировать строку без сбоев?), Сохраняя мини-накопитель вместо этого и используя cdb.exe или windbg.exe для извлечения информации об исключениях.

+0

Я не уверен, что вы предлагаете здесь.Я понимаю, что я не могу отформатировать строку, но после того, как мой процесс замирает, я не вижу, как я могу это сделать без какого-либо контекста. Моя цель - отправить электронное письмо или другое сообщение http. – Tim

+0

@frungash, пытаясь диагностировать процесс, который уже разбился в процессе, довольно хрупкий. Вы можете запустить отдельный процесс сторожевого таймера при запуске, чтобы поймать любые сбои или запустить его в фильтре исключений. Я настоятельно рекомендую получить minidump, а не пытаться создать трассировку стека или сообщение вручную. Это делает посмертную отладку бесконечно проще. – MSN

+0

Я экономлю minidump - я просто хочу придумать «уникальное» имя, которое описывает крах (и последние несколько элементов в стеке, кажется, способ сделать это эффективно). В любом случае у нас есть minidump. Меня не интересует новый процесс - я могу жить с некоторым процентом неудачных создания строк для более простого кода. – Tim