Отладка приложений

       

Использование CrashHandler API


В динамической библиотеке BUGSLAYERUTIL.DLL мной реализован CrashHandler API, с помощью которого можно ограничить свой обработчик аварий конкретным модулем или модулями. Способ, с помощью которого это делается, состоит в том, что все исключения проходят через установленный фильтр необрабатываемых исключений. При вызове этого фильтра проверяется модуль, из которого пришло исключение. Если исключение пришло от одного из предписанных модулей, вызывается пользовательский обработчик аварий, но если оно приходит от другого модуля (не от предписанного), вызывается фильтр необрабатываемых исключений, который я заменил. Вызов замененного фильтра исключений означает, что многие модули могут использовать CrashHandler API, не мешая друг другу. Все функции CrashHandler API показаны в листинге 9-5.

Листинг 9-5. CrashHandler.СPP

/*- - - - - - - - - - - - - - - - - - - -

"Debugging Applications" (Microsoft Press)

Copyright (с) 1997-2000 John Robbing — All rights reserved.

УСЛОВНАЯ КОМПИЛЯЦИЯ:

WORK_AROUND_SRCLINE_BUG — 

Определить данный символ для работы с ошибкой SymGetLineFromAddr; после первого поиска вызывает сбой в поисках PDB-файла. Эта ошибка исправлена в DBGHELP.DLL, но есть обходной путь для пользователей, которым может потребоваться старая версия IMAGEHLP.DLL.

 - - - - - - - - - - - - - - - - - - - - - - - - -*/

#include "pch.h"

#include "BugslayerUtil.h"

#include "CrashHandler.h"

// Внутренний файл заголовков проекта

#include "Internal.h"

/*////////////////////////////////////

Определения области видимости файла

////////////////////////////////////////////////* 

Максимальный размер символов, обрабатываемых в файле 

#define MAX_SYM_SIZE 256

#define BUFF_SIZE 1024

#define SYM_BUFF_SIZE 512

/*///////////////////////////////////////////////////

Глобальные переменные области видимости файла

////////////////////////////////////////////////////*

// Фильтр необработанных исключений заказчика (обработчик аварий)


 static PFNCHFILTFN g_pfnCallBack = NULL;

 // Оригинальный фильтр необработанных исключений

 static LPTOP_LEVEL_EXCEPTION_FILTER g_pfnOrigFilt = NULL;

 // Массив модулей для ограниченного обработчика аварий 

static HMODULE * g_ahMod = NULL; 

// Размер массива g_ahMod (число элементов)

 static UINT g_uiModCount = 0;

// Статический буфер, возвращаемый различными функциями. Этот буфер 

// позволяет передавать данные без использования стека, 

static TCHAR g_szBuff [ BUFF_SIZE ]; 

// Буфер поиска статических символов

 static BYTE g_stSymbol [ SYM_BUFF_SIZE ];

// Структура статического исходного файла и номера строки

static IMAGEHLP_LINE g_stLine;

// Кадр стека, используемый для его проходов

static STACKFRAME g_stFrame;

// Флажки, указывающие, что символьная машина была инициализирована

static BOOL g_bSymEngInit = FALSE;

/*////////////////////////////////////////////////////

Объявление функций области видимости файла

/////////////////////////////////////////////////////*/

// Обработчик исключений

LONG _stdcall CrashHandlerExceptionFilter ( EXCEPTION_POINTERS *

pExPtrs );

// Конвертирует простое исключение в строчное значение 

LPCTSTR ConvertSimpleException ( DWORD dwExcept);

 // Внутренняя функция, которая выполняет все проходы по стеку

 LPCTSTR _stdcall

InternalGetStackTraceString ( DWORD dwOpts ,

EXCEPTION_POINTERS * pExPtrs); 

// Внутренняя функция SymGetLineFromAddr

BOOL InternalSymGetLineFromAddr ( IN HANDLE hProcess ,

IN DWORD dwAddr , 

OUT PDWORD pdwDisplacement, 

OUT PIMAGEHLP_LINE Line , );

// Инициализирует символьную машину, если это необходимо void InitSymEng ( void);

// Очищает символьную машину, если это необходимо

 void CleanupSymEng ( void); 

/*/////////////////////////////////////////////////

Класс деструктора

//////////////////////////////////////////////////*/ 

// См. Примечание в MEMDUMPVALIDATOR.CPP об автоматических классах. 



// Выключить предупреждение: initializers put in library initialization 

// area (инициализаторы помещены в область инициализации)

 #pragma warning (disable : 4073) 

#pragma init_seg(lib) 

class CleanUpCrashHandler

 {

 public :

CleanUpCrashHandler ( void)

{

}

--CleanUpCrashHandler ( void)

{

// Есть ли запрос на распределение памяти?

 if ( NULL != g_ahMod)

{

VERIFY ( HeapFree ( GetProcessHeap (), 

0

g_ahMod )); 

g_ahMod = NULL; 

}

if ( NULL != g_pfnOrigFilt) 

{

У/ Восстановить оригинальный фильтр необрабатываемых 

// исключений.

SetUnhandledExceptionFilter ( g_pfnOrigFilt);

 }

}

};

// Статический класс

static CleanUpCrashHandler g_cBeforeAndAfter;

 /*/////////////////////////////////////////////////

Инициализация функции обработчика аварий

////////////////////////////////////////////////*/ 

BOOL _stdcall SetCrashHandlerFilter ( PFNCHFILTFN pFn) 

{

// NULL-параметр "отцепляет" обратный вызов.

if { NULL == pFn)

{

if ( NULL != g_pfnOrigFilt) 

{

// Восстановить оригинальный фильтр необрабатываемых 

// исключений.

SetUnhandledExceptionFilter ( g_pfnOrigFilt); 

g_pfnOrigFilt = NULL;

 if ( NULL ! = g_ahMod) 

{

free ( g_ahMod);

 g_ahMod = NULL; 

}

g_pfnCallBack = NULL;



}

else 

{

ASSERT ( FALSE == IsBadCodePtr ( (FARPROC)pFn));

if ( TRUE •== IsBadCodePtr { (FARPROC)pFn))

{

return ( FALSE);

 }

g_pfnCallBack = pFn;

// Если обработчик аварии заказчика уже используется,

// активизировать CrashHandlerExceptionFilter и сохранить

// оригинальный фильтр необрабатываемых исключений.

if ( NULL = = g_pfnOrigFilt)

{

g_pfnOrigFilt =

SetUnhandledExceptionFilter( CrashHandlerExceptionFilter); 



 }

return ( TRUE); 

}

BOOL _stdcall AddCrashHandlerLimitModule ( HMODULE hMod)

 {

// Проверить очевидные случаи

 ASSERT ( NULL != hMod);



 if ( NULL == hMod) 

{

return ( FALSE); 

}_

// Распределить память под временный массив. Этот массив должен

 // быть распределен из памяти, наличие которой гарантировано, даже // если процесс разрушается. Если процесс разрушается,

 // куча времени выполнения, вероятно, небезопасна, поэтому 

// временный массив распределяется в куче процесса. 

HMODULE * phTemp = (HMODULE*)

HeapAlloc ( GetProcessHeap () ,

 HEAP_ZERO_MEMORY |

HEAP_GENERATE_EXCEPTIONS , 

( sizeof ( HMODULE) * ( g_uiModCount+l)) );

 ASSERT ( NULL != phTemp);

 if ( NULL = phTemp) 

{

TRACE0 ( "Serious trouble in the house! _ "

 "HeapAlloc failed!!!\n" );

return ( FALSE); 

}

if ( NULL = g_ahMod) 

{

g_ahMod = phTemp;

  g_ahMod[ 0 ] = hMod;

 g_uiModCount++; 

}

else

 {

// Копировать старые значения. 

CopyMemory ( phTemp ,

 g_ahMod ,

sizeof ( HMODULE) * g_uiModCount) ; 

// Освободить старую память.

VERIFY ( HeapFree ( GetProcessHeap (), 0, g_ahMod));

 g_ahMod = phTemp; 

g_ahMod[ g_uiModCount ] = hMod; 

g_uiModCount++; 

}

return ( TRUE);

 }

UINT _stdcall GetLimitModuleCount ( void) 

{

return ( g_uiModCount); 

}

int _stdcall GetLimitModulesArray ( HMODULE * pahMod, UINT uiSize)

 {

int iRet;

_try

{

ASSERT ( FALSE == IsBadWritePtr ( pahMod,

uiSize * sizeof ( HMODULE)));

 if ( TRUE == IsBadWritePtr ( pahMod,

uiSize * sizeof ( HMODULE)))

 {

iRet = GLMA_BADPARAM;

 _leave;

 }.

if ( uiSize < g_uiModCount) 

{

iRet = GLMA_BUFFTOOSMALL; 

_leave; 

}

CopyMemory ( pahMod ,

 g_ahMod ,

sizeof ( HMODULE) * g_uiModCount);

 iRet = GLMA_SUCCESS;

 }

_except ( EXCEPTION_EXECUTE_HANDLER) 

{

iRet = GLMA_FAILURE; 

}

return ( iRet); 

}

LONG _stdcall GrashHandlerExceptionFilter ( EXCEPTION_POINTERS* pExPtrs) { 



 LONG IRet = EXCEPTION_CONTINUE_SEARCH;

// Если возбуждено исключение EXCEPTION_STACK_OVERFLOW (переполнение 

// стека исключений), то мало что можно сделать, потому что стек 

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

// Хотя я не рекомендую это делать, но можно попробовать 

// управлять регистром стека так, чтобы освободить 

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

 //Я избрал безопасный путь и выполняю здесь несколько обращений //к OutputDebugString. Существует риск двойной ошибки, но 

// благодаря тому, что OutputDebugString очень мало использует

 // стек (примерно 8-16 байт), это — стоящий шаг.

 // Ваши пользователи могут также загрузить программу

 // DebugView/Enterprise Edition

// Марка Руссиновича (Mark Russinovich) с узла www.sysinternals.com.

 // Единственная проблема

// состоит в том, что я не могу даже удостовериться, что

 //в стеке достаточно места для преобразования указателя команд. 

//К счастью, EXCEPTION_STACK_OVERFLOW не случается очень часто.

 // Заметьте, что я еще вызываю ваш обработчик аварий.

 // Кроме того, в случае если переполненный стек разрушает ваш

 // обработчик аварий, здесь выполняется протоколирование.

 if ( EXCEPTION_STACK_OVERFLOW ==

pExPtrs->ExceptionRecord->ExceptionCode)

 {

OutputDebugString ( "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); 

OutputDebugString ( "EXCEPTION_STACK_OVERFLOW occurred\n"); OutputDebugString ( "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); 

}

_try

 {

if ( NULL != g_pfnCallBack)

 {

// Здесь должна быть инициализирована символьная машина,

// так чтобы можно было отыскать информацию базового модуля

// об адресе аварии, а заодно и привести символьную машину



//в состояние готовности.

InitSymEng ();

// Проверить список g_ahMod.

BOOL bCalllt = FALSE;

if ( 0 == g_uiModCount)

{

bCalllt = TRUE;

}

else {

HINSTANCE hBaseAddr = (HINSTANCE)

SymGetModuleBase ((HANDLE)GetCurrentProcessId (), (DWORD)pExPtrs->

ExceptionRecord->

ExceptionAddress);

 if ( NULL != hBaseAddr) 

{

for ( UINT i = 0; i < g__uiModCount; i ++) 

{

if ( hBaseAddr == g_ahMod[ i ]) 

{

bCalllt = TRUE; break; 



}

 }

 }

if ( TRUE == bCalllt) 

{

// Проверить перед вызовом обработчика аварии, что он все еще

 // существует в памяти.

// Пользователь может забыть зарегистрироваться,

 //и обработчик аварии оказывается недействительным, потому что 

// он не загружен. Однако, если какая-нибудь другая функция 

// загружена в тот же самый адрес, мало что можно сделать.

ASSERT ( FALSE == IsBadCodePtr( (FARPROC)g_pfnCallBack));

 if ( FALSE == IsBadCodePtr ( (FARPROC)g_pfnCallBack)) 

{

IRet = g_pfnCallBack ( pExPtrs); 



}

else 

{

// Вызвать предыдущий фильтр, но только после того, 

// как он закончит работу. Я просто немного параноик!:) 

ASSERT ( FALSE == IsBadCodePtr ( (FARPROC) g__pfnOrigFilt) ) ;

 if ( FALSE == IsBadCodePtr ( (FARPROC)g_pfnOrigFiIt)) 

{

IRet = g_pfnOrigFilt ( pExPtrs); 

}

}

CleanupSymEng (); 



}

_except ( EXCEPTION_EXECUTE_HANDLER) 

{

IRet = EXCEPTION_CONTINUE_SEARCH; 

}

return ( IRet); 



/*/////////////////////////////////////////////

Реализация функций-трансляторов EXCEPTION_POINTER-структyp

//////////////////////////////////////////////*/

 LPCTSTR _stdcall GetFaultReason ( EXCEPTION_POINTERS * pExPtrs) {

ASSERT ( FALSE == IsBadReadPtr ( pExPtrs,

sizeof ( EXCEPTION_POINTERS)));

 if ( TRUE = IsBadReadPtr ( pExPtrs,

sizeof ( EXCEPTION_POINTERS))) 

{

TRACEO ( "Bad parameter to GetFaultReasonA\n"); 



return ( NULL); 

}

// Переменная, которая содержит возвращаемое значение

 LPCTSTR szRet;

 _try 

{

// Инициализировать символьную машину в случае, если она

 //не инициализирована. 

InitSymEng (); 

// Текущая позиция в буфере

int iCurr = 0;

// Временное хранилище значения. Оно позволяет свести 

// использование стека к минимуму.

 DWORD dwTemp;

iCurr += BSUGetModuleBaseName ( GetCurrentProcess (),

NULL 

, g_szBuff , 

BUFF_SIZE );

iCurr += wsprintf ( g_szBuff + iCurr, _T ( " caused an "));

 dwTemp = (DWORD)

ConvertSimpleException ( pExPtrs->ExceptionRecord->

ExceptionCode);

 if ( NULL != dwTemp)

 {

iCurr += wsprintf ( g_szBuff + iCurr, _T ( "%s") dwTemp );

 }

else 

{

iCurr += ( FormatMessage ( FORMAT_MESSAGE_IGNORE_INSERTS |

FORMAT_MESSAGE_FROM_HMODULE,

 GetModuleHandle (_T("NTDLL.DLL")), 

pExPtrs->ExceptionRecord->

ExceptionCode,

0,

g_szBuff + iCurr ,

 BUFF_SIZE,

0 )

 * sizeof ( TCHAR)); 

}

ASSERT ( iCurr < ( BUFF_SIZE - MAX_PATH));

iCurr += wsprintf ( g_szBuff + i.Curr, _T ( " in module ") ) ;

 dwTemp =

SymGetModuleBase ( (HANDLE)GetCurrentProcessId (),

(DWORD)pExPtrs->ExceptionRecord->

ExceptionAddress); 

ASSERT ( NULL != dwTemp);

 if ( NULL == dwTemp) 

{

iCurr += wsprintf ( g_szBuff + iCurr, _T ( "<UNKNOWN>")); 

}

else 

{

iCurr += BSUGetModuleBaseName ( GetCurrentProcess () ,

(HINSTANCE)dwTemp , 

g_szBuff + iCurr

 BUFF_SIZE - iCurr ); 



#ifdef _WIN64

iCurr += wsprintf ( g_szBuff + iCurr ,

 _T ( " at %016X") ,

pExPtrs->ExceptionRecord->ExceptionAddress); 

#else

iCurr += wsprintf ( g_szBuff + iCurr , 

_T ( " at %04X:%08X") ,

 pExPtrs->ContextRecord->SegCs ,

 pExPtrs->ExceptionRecord->ExceptionAddress);



 #endif

ASSERT ( iCurr < ( BUFF_SIZE _ 200)); 

// Начать поиск адреса исключения.

PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)&g_stSymbol;

 FillMemory ( pSym, NULL, SYM_BUFF_SIZE);

pSym->SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL);

pSym->MaxNameLength = SYM_BUFF_SIZE - sizeof ( IMAGEHLP_SYMBOL); 

DWORD dwDisp;

 if ( TRUE ==

SymGetSymFromAddr ( (HANDLE)GetCurrentProcessId () ,

 (DWORD)pExPtrs->ExceptionRecord->

ExceptionAddress ,

SdwDisp , 

pSym ) ) 



iCurr += wsprintf ( g_szBuff + iCurr, _T ( ", ")); 

// Копировать не больше символьной информации, чем

 // позволяет память.

 dwTemp = Istrlen ( pSym->Name);

// Удостовериться, что достаточно места для самого длинного

 // символа и смещения.

if ( (int)dwTemp > ( ( BUFF_SIZE _ iCurr) -

( MAX_SYM_SIZE +50) ))

 {

Istrcpyn ( g_szBuff + iCurr , 

pSym->Name , 

BUFF_SIZE - iCurr - 1 );

 // Теперь можно выйти

 szRet = g_szBuff; _leave; 

}

else

 {

if ( dwDisp > 0)

 {

iCurr += wsprintf ( g_szBuff + iCurr , 

_T ( "%s()+%04d byte(s)"), 

pSym->Name , 

dwDisp ); 

}

else 

{

iCurr += wsprintf ( g_szBuff + iCurr, 

_T ( "%s ") pSym->Name ) ; 





}

else 

{

// Если символ не найден, то источник и номер строки 

// тоже не найдены, поэтому выходим сейчас.

 szRet = g_szBuff;

_leave; 

}

ASSERT ( iCurr < ( BUFF_SIZE _ 200));

 // Поиск исходного файла и номера строки.

 ZeroMemory ( &g_stLine, sizeof ( IMAGEHLP_LINE));

 g_stLine.SizeOfStruct = sizeof ( IMAGEHLP_LINE);

if ( TRUE ==

InternalSymGetLineFromAddr ( (HANDLE)

GetCurrentProcessId () , 

(DWORD)pExPtrs->

ExceptionRecord->

ExceptionAddress, 

SdwDisp ,

 &g_stLine )) 

{

iCurr += wsprintf ( g_szBuff + iCurr, _T ( ", ")); 



// Копировать не больше информации об исходном файле 

//и номере строки, чем позволяет память. 

dwTemp = Istrlen ( g_stLine.FileName);

 if ( (int)dwTemp > ( BUFF_SIZE - iCurr

_ MAX_PATH _ 50 ))

{

Istrcpyn ( g_szBuff + iCurr ,

g_stLine.FileName , 

BUFF_SIZE - iCurr - 1); 

// Теперь можно выйти 

szRet = g_szBuff; 

_leave;

}

else 

{

if ( dwDisp > 0)

 {

iCurr += wsprintf ( g_szBuff + iCurr ,

 _T("%s, line %04d+%04d byte(s)"),

 g_stLine.FileName ,

 g_stLine. LineNumber , 

dwDisp ) ; 

}

 else

{

iCurr += wsprintf ( g_szBuff + iCurr , 

_T ( "%s, line %04d"),

g_stLine.FileName , 

g_stLine.LineNumber); 





}

szRet = g_szBuff; 

}

_except ( EXCEPTION_EXECUTE_HANDLER)

 {

ASSERT ( !"Crashed in GetFaultReason"); 

szRet = NULL;

 }

return ( szRet); 

}

BOOL _stdcall GetFaultReasonVB ( EXCEPTION_POINTERS * pExPtrs,

LPTSTR szBuff , UINT uiSize ) 

{

ASSERT ( FALSE == IsBadWritePtr ( szBuff, uiSize));

if ( TRUE == IsBadWritePtr ( szBuff, uiSize))

{

return ( FALSE); 

}

LPCTSTR szRet;  

__try 

{

szRet = GetFaultReason ( pExPtrs); 

ASSERT ( NULL != szRet);

 if ( NULL == szRet) 

{

_leave; 

}

Istrcpyn ( szBuff , 

szRet ,

min ( (UINT)Istrlen ( szRet) + 1, uiSize)); 

}

_except ( EXCEPTION_EXECUTE_HANDLER) 

{

szRet = NULL; 

}

return ( NULL != szRet); 

}

LPCTSTR BUGSUTIL_DLLINTERFACE _stdcall

GetFirstStackTraceString ( DWORD dwOpts ,

EXCEPTION_POINTERS * pExPtrs)

{

// Все проверки ошибок выполняются в функции 

// InternalGetStackTraceString

// Инициализировать структуру STACKFRAME . 

ZeroMemory ( &g_stFrame, sizeof ( STACKFRAME));

#ifdef _X86_

g_stFrame.AddrPC.Offset = pExPtrs->ContextRecord->Eip;

 g_stFrame.AddrPC.Mode  = AddrModeFlat ; 



g_stFrame.AddrStack.Offset = pExPtrs->ContextRecord->Esp;

 g_stFrame.AddrStack.Mode = AddrModeFlat ;

g_stFrame.AddrFrame.Offset = pExPtrs->ContextRecord->Ebp;

 g_stFrame.AddrFrame.Mode = AddrModeFlat ;

#else

g_stFrame.AddrPC.Offset = (DWORD)pExPtrs->ContextRecord->Fir;

 g_stFrame.AddrPC.Mode = AddrModeFlat;

 g_stFrame.AddrReturn.Offset =

(DWORD)pExPtrs->ContextRecord->IntRa; 

g_stFrame.AddrReturn.Mode = AddrModeFlat; 

g_stFrame.AddrStack.Offset =

(DWORD)pExPtrs->ContextRecord->IntSp; 

g_stFrame.AddrStack.Mode = AddrModeFlat; 

g_stFrame.AddrFrame.Offset -

(DWORD)pExPtrs->ContextRecord->IntFp; 

g_stFrame.AddrFrame.Mode = AddrModeFlat;

#endif

return ( InternalGetStackTraceString ( dwOpts, pExPtrs)); 

}

 LPCTSTR BUGSUTIL_DLLINTERFACE _stdcall

GetNextStackTraceString ( DWORD dwOpts ,

EXCEPTION_POINTERS * pExPtrs) 

{

// Все проверки ошибок — в InternalGetStackTraceString.

 // Предполагается, что GetFirstStackTraceString уже 

// инициализировала информацию в кадре стека,

 return ( InternalGetStackTraceString ( dwOpts, pExPtrs)); 

}

BOOL _stdcall CH__ReadProcessMemory ( HANDLE ,

LPCVOID IpBaseAddress ,

 LPVOID IpBuffer , 

DWORD nSize ,

 LPDWORD IpNumberOfBytesRead ) 

{

return ( ReadProcessMemory ( GetCurrentProcess (),

IpBaseAddress ,

 IpBuffer ,

nSize , 

IpNumberOfBytesRead ));

}

// Внутренняя функция, которая выполняет все проходы по стеку

LPCTSTR _stdcall

InternalGetStackTraceString ( DWORD dwOpts ,

EXCEPTION_POINTERS * pExPtrs ) 

{

ASSERT ( FALSE == IsBadReadPtr ( pExPtrs

sizeof ( EXCEPTION_POINTERS)));

 if ( TRUE == IsBadReadPtr ( pExPtrs ,

sizeof ( EXCEPTION_POINTERS))) 

{

TRACED ( "GetStackTraceString — invalid pExPtrs!\n");

 return ( NULL); 

}

// Возвращаемое значение LPCTSTR szRet;

// Временная переменная для общего пользования.


Эта переменная 

// сохраняет область стека. DWORD dwTemp;

// Базовый адрес модуля. Я отыскиваю его прямо после прохода

 // по стеку, чтобы убедиться, что стек правильный. 

DWORD dwModBase; 

_try 

{

// Инициализировать символьную машину в случае, если она

 //не инициализирована. 

InitSymEng ();

#ifdef _WIN64

#define CH_MACHINE IMAGE_FILE_MACHINE_IA64

 #else

#define CH_MACHINE IMAGE_FILE_MACHINE_I386

#endif

// Замечание: Если функции исходных файлов и номеров строк

// используются, StackWalk может вызвать останов по нарушению

// доступа.

BOOL bSWRet = StackWalk ( CH_MACHINE ,

(HANDLE)GetCurrentProcessId () , 

GetCurrentThread () , 

&g_stFrame , 

pExPtrs->ContextRecord ,

 (PREAD_PROCESS_MEMORY_ROUTINE)

CH_ReadProcessMemory , 

SymFunctionTableAccess ,

 SymGetModuleBase , 

NULL ) ;

if ( ( FALSE = bSWKet) 11(0= g_stFrame.AddrFrame.Offset)) 

{

szRet = NULL; 

_leave; 

}

// Прежде чем все подсчитывать,

//я должен перепроверить, что адрес, возвращенный

 // из StackWalk, действительно существует. Бывает,

 // что StackWalk, возвращает TRUE, но адрес не принадлежит 

// модулю процесса. 

dwModBase = SymGetModuleBase ( (HANDLE)GetCurrentProcessId (),

g_stFrame.AddrPC.Offset ); 

if ( 0 == dwModBase) 

{

szRet = NULL;

 _leave; 

}

int iCurr = 0;

// Как минимум получить адрес, 

#ifdef _WIN64

iCurr += wsprintf ( g_szBuff + iCurr , 

_T ( "Ox%016X") ,

g_stFrame.AddrPC.Offset );

 #else

iCurr += wsprintf ( g_szBuff + iCurr , 

_T ( "%04X:%08X") 

pExPtrs->ContextReeord->SegCs, g_stFrame.AddrPC.Offset ); 

#endif

// Вывести параметры?

if ( GSTSO_PARAMS == ( dwOpts & GSTSO_PARAMS))

{

iCurr += wsprintf ( g_szBuff + iCurr , 

_T ( " ( Ox%08X Ox%08X "\

"Ox%08X Ox%08X)"), 

g_stFrame.Params[ 0 ] ,



 g_stFrame.Params[ 1 ] ,

 g_stFrame.Params[ 2 ] ,

 g_stFrame.Params[ 3 ] ); 

}

// Вывести имя модуля.

if ( GSTSO_MODULE = ( dwOpts & GSTSO_MODULE)) 

{

iCurr += wsprintf ( g_szBuff + iCurr , _T ( " ")); 

ASSERT ( iCurr < ( BUFF_SIZE - MAX_PATH));

iCurr += BSUGetModuleBaseName ( GetCurrentProcess (),

(HINSTANCE)dwModBase,

 g_szBuff + iCurr , 

BUFF_SIZE - iCurr ); 

}

ASSERT ( iCurr < ( BUFF_SIZE - MAX_PATH)); 

DWORD dwDisp; 

// Вывести имя символа?

if ( GSTSO_SYMBOL == ( dwOpts & GSTSO_SYMBOL)) 

{

// Начать поиск адреса исключения.

PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)Sg_stSymbol;

ZeroMemory ( pSym, SYM_BUFF_SIZE);

pSym->SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL);

pSym->MaxNameLength = SYM_BUFF_SIZE -

sizeof ( IMAGEHLP_SYMBOL); 

if ( TRUE ==

SymGetSymFromAddr ( (HANDLE)GetCurrentProcessId () ,

g_stFrame.AddrPC.Offset , 

sdwDisp , 

pSym )) 

{

iCurr += wsprintf ( g_szBuff + iCurr, _T ( ", ")); 

// Копировать не больше символьной информации, чем 

// позволяет память. 

dwTeitip = Istrlen ( pSym->Name) ;

 if ( dwTeitip > (DWORD) ( BUFF_SIZE - iCurr _

 ( MAX_SYM_SIZE + 50)))

 {

Istrcpyn ( g_szBuff + iCurr ,

 pSym->Name , 

BUFF_SIZE - iCurr - 1 );

 // Теперь можно выйти 

szRet = g_szBuff;

_leave;

 }

else 

{

if ( dwDisp > 0)

 {

iCurr += wsprintf ( g_szBuff + iCurr , 

_T( "%s()+%04d byte(s)"), 

pSym->Name , 

dwDisp ) ;

 }

else 

{

iCurr += wsprintf ( g_szBuff + iCurr,

 _T ( "%s") 

pSym->Name ) ; 

}

 } 

}

else 

{

// Если символ не был найден, то исходный файл и номер 

// строки тоже не будут найдены, поэтому выйти сейчас.

 szRet = g_szBuff;

_leave; 



}

ASSERT ( iCurr < ( BUFF_SIZE - MAX_PATH)); 



// Вывести информацию исходного файла и номера строки?

 if ( GSTSO_SRCLINE == ( dwOpts & GSTSO_SRCLINE))

{

ZeroMemory ( &g_stLine, sizeof ( IMAGEHLP_LINE));

 g_stLine.SizeOfStruct = sizeof ( IMAGEHLP_LINE);

 if ( TRUE ==

InternalSymGetLineFromAddr ( (HANDLE)

GetCurrentProcessId (), 

g_stFrame.AddrPC.Offset , 

SdwDisp , 

&g_stLine ))

{

iCurr += wsprintf ( g_szBuff + iCurr, _T ( ", "));

 // Копировать не больше информации об исходном файле и 

// номере строки, чем позволяет память.

 dwTemp = Istrlen ( g_stLine.FileName); 

if ( dwTerap > (DWORD)( BUFF_SIZE - iCurr 

_ ( MAX_PATH +50 ))) {

Istrcpyn ( g_szBuff + iCurr ,

 g_stLine.FileName , 

BUFF_SIZE - iCurr - 1 );

 // Теперь можно выйти

 szRet = g_szBuff; 

_leave; 

}

else

 {

if { dwDisp > 0)

{

iCurr += wsprintf(g_szBuff + iCurr ,

_T("%s, line %04d+%04d byte(s)"),

g_stLine.FileName ,

g_s tLine.LineNumbe r ,

dwDisp ) ;

}

else

 {

iCurr += wsprintf ( g_szBuff + iCurr ,

 _T ( "%s, line %04d") ,

 g_stLine.FileName ,

 g_stLine.LineNumber ); 

}



}

 }

szRet = g_szBuff; 

}

_except ( EXCEPTION_EXECUTE_HANDLER)

 {

ASSERT ( !"Crashed in InternalGetStackTraceString"); 

szRet = NULL; 

}

return ( szRet); 



BOOL _stdcall

GetFirstStackTraceStringVB ( DWORD dwOpts ,

EXCEPTION_POINTERS * pExPtrs, 

LPTSTR szBuff , 

UINT uiSize ) 

{

ASSERT ( FALSE == IsBadWritePtr ( szBuff, uiSize));

if ( TRUE = IsBadWritePtr ( szBuff, uiSize))

{

return ( FALSE); 

}

LPCTSTR szRet; 

_try 

{

szRet = GetNextStackTraceString ( dwOpts, pExPtrs);

if ( NULL == szRet)

{

_leave;

 }

Istrcpyn ( szBuff , szRet ,

min ( (UINT)lstrlen ( szRet) + I, uiSize)); 

}

_except ( EXCEPTION_EXECUTE_HANDLER) 

{

szRet = NULL; 



}

return ( NULL != szRet); 

}

LPCTSTR _stdcall GetRegisterString ( EXCEPTION_POINTERS * pExPtrs) {

// Проверить параметр.

ASSERT ( FALSE = IsBadReadPtr ( pExPtrs ,

sizeof ( EXCEPTION_POINTERS)));

 if { TRUE = IsBadReadPtr ( pExPtrs ,

sizeof ( EXCEPTION_POINTERS))) 

{

TRACED ( "GetRegisterString - invalid pExPtrs!\n"); 

return ( NULL); 

}

#ifdef _WIN64

ASSERT ( !"IA64 is not supported (YET!) ");

#else

// Этот вызов помещает в стек 48 байт, что может стать проблемой,

 // если он переполнен,

 wsprintf ( g_szBuff,

_Т ( "ЕАХ=%08Х ЕВХ=%08Х ЕСХ=%08Х EDX=%08X ESI=%08X\n"\

"EDI=%08X EBP=%08X ESP=%08X EIP=%08X FLG=%08X\n"\

"CS=%04X DS=%04X SS=%04X ES=%04X "\

"FS=%04X GS=%04X"),

pExPtrs->ContextRecord->Eax ,

pExPtrs->ContextRecord->Ebx ,

pExPtrs->ContextRecord->Ecx ,

pExPtrs->ContextRecord->Edx ,

pExPtrs->ContextRecord->Esi ,

pExPtrs->ContextRecord->Edi ,

pExPtrs->ContextRecord->Ebp ,

pExPtrs->ContextRecord->Esp ,

pExPtrs->ContextRecord->Eip ,

pExPtrs->ContextRecord->EFlags ,

pExPtrs->ContextRecord->SegCs ,

pExPtrs->ContextRecord->SegDs ,

pExPtrs->ContextRecord->SegSs ,

pExPtrs->ContextRecord->SegEs ,

pExPtrs->ContextRecord->SegFs ,

 pExPtrs->ContextRecord->SegGs ); 

#endif

return ( g_szBuff); 

}

BOOL _stdcall GetRegisterStringVB ( EXCEPTION_POINTERS * pExPtrs,

LPTSTR szBuff , UINT uiSize )

 {

ASSERT ( FALSE == IsBadWritePtr ( szBuff, uiSize));

if ( TRUE == IsBadWritePtr ( szBuff, uiSize))

{

return ( FALSE); 

}

LPCTSTR szRet; 

_try 

{

szRet = GetRegisterString ( pExPtrs);

if ( NULL = szRet)

{

_leave; 

}

Istrcpyn ( szBuff , szRet ,

min ( (UINT)Istrlen ( szRet) + 1, uiSize)); 

}

_except ( EXCEPTION_EXECUTE_HANDLER) {

szRet = NULL; 

}

return ( NULL != szRet); 

}



LPCTSTR ConvertSimpleException ( DWORD dwExcept) 

{

switch ( dwExcept) 

{

case EXCEPTION_ACCESS_VIOLATION :

return ( _T ( "EXCEPTION_ACCESS_VIOLATION")); 

break;

 case EXCEPTION_DATATYPE_MISALIGNMENT :

return ( _T ( "EXCEPTION_DATATYPE_MISALIGNMENT"));

 break; 

case EXCEPTION_BREAKPOINT :

return ( _T ( "EXCEPTION_BREAKPOINT")); 

break;

case EXCEPTION_SINGLE_STEP :

return ( _T ( "EXCEPTION_SINGLE_STEP")); 

break; 

case EXCEPTION_ARRAY_BOUNDSJEXCEEDED

return ( _T ( "EXCEPTION_ARRAY_BOUNDS_EXCEEDED")); 

break; 

case EXCEPTION_FLT_DENORMAL_OPERAND :

return ( _T ( "EXCEPTION_FLT_DENORMAL_OPERAND")); 

break; 

case EXCEPTION_FLT_DIVIDE_BY_ZERO :

return ( _T ( "EXCEPTION_FLT_DIVIDE_BY_ZERO")); 

break;

 case EXCEPTION_FLT_INEXACT_RESULT :

return ( _T ( "EXCEPTION_FLT_INEXACT_RESULT")); 

break;

 case EXCEPTION_FLT_INVALID_OPERATION :

return ( _T ( "EXCEPTION_FLT_INVALID_OPERATION")); 

break; 

case EXCEPTION_FLT_OVERFLOW :

return ( _T ( "EXCEPTION_FLT_OVERFLOW"));

 break; 

case EXCEPTION_FLT_STACK_CHECK :

return ( _T ( "EXCEPTION_FLT_STACK_CHECK")); 

break; 

case EXCEPTION_FLT_UNDERFLOW :

return ( _T ( "EXCEPTION_FLT_UNDERFLOW")); break; case EXCEPTION_INT_DIVIDE_BY_ZERO :

return ( _T ( "EXCEPTION_INT_DIVIDE_BY_ZERO")); 

break;

 case EXCEPTION_INT_OVERFLOW :

return ( _T ( "EXCEPTION_INT_OVERFLOW")); 

break;

 case EXCEPTION_PRIV_INSTRUCTION :

return ( _T ( "EXCEPTION_PRIV_INSTRUCTION"));

 break; 

case EXCEPTION_IN_PAGE_ERROR :

return ( _T ( "EXCEPTION_IN_PAGE_ERROR"));

 break; 

case EXCEPTION_ILLEGAL_INSTRUCTION :

return ( _T ( "EXCEPTION_ILLEGAL_INSTRUCTION"));

 break; 

case EXCEPTION_NONCONTINUABLE_EXCEPTION :



return ( _T ( "EXCEPTION_NONCONTINUABLE_EXCEPTION")); break;

case EXCEPTION_STACK_OVERFLOW :

return ( _T ( "EXCEPTION_STACK_OVERFLOW")); 

break;

 case EXCEPTION_INVALID_DISPOSITION :

return ( _T ( "EXCEPTION_INVALID_DISPOSITION")); 

break; 

case EXCEPTION_GUARD_PAGE :

return ( _T ( "EXCEPTION_GUARD_PAGE")); 

break; 

case EXCEPTION_INVALID_HANDLE :

return ( _T ( "EXCEPTION_INVALID_HANDLE"));

 break;

 default :

return ( NULL); 

break; 



}

BOOL InternalSymGetLineFromAddr ( IN HANDLE hProcess ,

IN DWORD dwAddr ,

 OUT PDWORD pdwDisplacement, 

OUT PIMAGEHLP_LINE Line ) 



#ifdef WORK_AROUND_SRCLINE_BUG

// Проблема заключается в том, что символьная машина находит 

// только те адреса исходных строк (после первого поиска), которые 

// попадают точно на нулевое смещение. Сместимся назад на 100 байт, 

// чтобы найти строку, и вернем подходящее смещение.

 DWORD dwTempDis = 0;

while ( FALSE == SymGetLineFromAddr ( hProcess ,

dwAddr -

dwTempDis ,

 pdwDisplacement, 

Line ) ) 

{

dwTempDis += 1;

if ( 100 == dwTempDis)

{

return ( FALSE);

 } 

}

// Строка найдена и информация исходной строки корректна, 

// поэтому нужно изменить смещение, если требуется 

// выполнить обратный поиск, чтобы найти исходную строку,

 if ( 0 != dwTempDis) 

{

*pdwDisplacement = dwTempDis; 

}

return ( TRUE);

#else // WORK_AROUND_SRCLINE_BUG

return ( SymGetLineFromAddr ( hProcess ,

dwAddr ,

 pdwDisplacement , 

Line ));

#endif

}

// Инициализировать символьную машину, если необходимо

void InitSymEng ( void)

{

if ( FALSE == g_bSymEngInit)

{

// Установить символьную машину.

DWORD dwOpts = SymGetOptions ();

// Включить загрузку строки и отложенную загрузку.

 SymSetOptions ( dwOpts |

 SYMOPT_DEFERRED_LOADS |

 SYMOPT_LOAD_LINES );



// Форсировать флажок захватывающего процесса, независимо от

 // того, в какой операционной системе мы находимся. 

HANDLE hPID = (HANDLE)GetCurrentProcessId (); 

VERIFY ( BSUSymlnitialize ( (DWORD)hPID,

hPID , 

NULL , 

TRUE ));

 g_bSymEngInit = TRUE; 



}

// Очистить символьную информацию, если необходимо,

 void CleanupSymEng ( void) CrashHandler

{

if ( TRUE == g_bSymEngInit) 

{

VERIFY ( SymCleanup ( (HANDLE)GetCurrentProcessId ()));

 g_bSymEngInit = FALSE;

 } 

}

Чтобы установить свою функцию фильтра, просто вызовите функцию SetCrashHandierFiiter, которая сохраняет вашу функцию фильтра в статической переменной И вызывает функцию SetUnhandledExceptionFilter, чтобы установить реальный фильтр Исключений CrashHandlerExceptionFilter. Если вы не добавляете какие-то модули, которые ограничивают фильтрацию исключений, то CrashHandlerExceptionFilter будет всегда вызывать ваш фильтр, независимо от того, в каком модуле произошел тяжелый останов. Если никакие модули не были добавлены, то вызов вашего фильтра исключений соответствует проекту, поэтому, чтобы установить заключительную обработку исключений, надо использовать только один API-вызов. Лучше всего сразу же вызвать функцию SetCrashHandierFiiter и убедиться, что она вызывается с NULL-аргументом непосредственно перед разгрузкой вашего фильтра, что позволит удалить вашу функцию фильтра с помощью рассмотренного обработчика аварий.

Функция AddCrashHandierLimitModuie вызывается там, где вы добавляете модуль, чтобы ограничить обработку аварий. Все, что требуется передать данной функции — это дескриптор добавляемого модуля (через ее единственный параметр HMODULE hMod). Если нужно ограничить обработку аварий для нескольких модулей, просто вызовите AddCrashHandierLimitModuie для каждого модуля. Память для массива дескрипторов модулей распределяется из кучи главного процесса.

Если посмотреть на различные функции в листинге 9-5, то можно заметить, что в них нет вызовов каких-либо функций исполнительной (run-time) библиотеки C++.


Поскольку подпрограммы обработчика аварий вызываются только в экстраординарных ситуациях, нельзя быть уверенным, что функции этой библиотеки находятся в устойчивом состоянии. Чтобы очистить любую распределенную мною память, используется автоматический статический класс, деструктор которого вызывается, когда библиотека BUGSLAYERUTIL.DLL не загружена. Я также включил функции GetLimitModuleCount И GetLimitModulesArray, Позволяющие получить размер ограничивающего модуля и копию массива таких модулей. Написание функции RemoveCrashHandlerLimitModule Предоставляется читателю.

Интересным аспектом реализации CRASHHANDLER.CPP является обработка инициализации символьной машины DBGHELP.DLL. Поскольку код обработчика аварий может быть вызван в любой момент, нужно было, чтобы во время аварии загружались все модули процесса. Об этом автоматически позаботится функция Syminitiaiize, если ее третий параметр (finvadeProcess) установить в значение TRUE. К сожалению, захват процесса и загрузка всех модулей будет работать только в Windows 2000, но не в Windows 98. Чтобы обеспечить такое же поведение и в Windows 98, следует использовать функцию Bsusyminitialize из BUGSLAYERUTIL.DLL, которая разыскивает все необходимые модули и загружает их по одному.



Содержание раздела