Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004

.pdf
Скачиваний:
353
Добавлен:
13.08.2013
Размер:
3.3 Mб
Скачать

ГЛАВА 13 Обработчики ошибок

483

 

 

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 CrashHandlerExceptionFilter (EXCEPTION_POINTERS* pExPtrs)

{

LONG lRet = EXCEPTION_CONTINUE_SEARCH ;

//Если исключение имеет тип EXCEPTION_STACK_OVERFLOW, с ним почти

//ничего нельзя сделать из за плачевного состояния стека. Любые

//действия скорее всего приведут к еще одной ошибке сразу же около

//вашего фильтра исключений. Я не рекомендую этого делать, однако

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

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

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

//Я использую безопасный подход и выполняю несколько вызовов функции

//OutputDebugString. Они также могут привести к повторной ошибке,

//но попробовать это стоит, так как OutputDebugString требует совсем

//небольшого пространства в стеке (8 16 байт). Чтобы ваши пользователи

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

//с сайта www.sysinternals.com программу DebugView, написанную

//Марком Руссиновичем (Mark Russinovich). Единственная проблема в том,

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

//К счастью, исключение EXCEPTION_STACK_OVERFLOW случается довольно

//редко. Вас, возможно, интересует, почему я не вызываю здесь новую

//функцию _resetstkoflw. Она вызывается только при критических

//исключениях, поэтому, если приложение "умирает", попытка восстановления

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

//только если ее вызвать до этого момента.

__try

{

//Обратите внимание: я все же вызываю ваш обработчик ошибок.

//Если же переполнение стека нарушит работу вашего

//обработчика ошибок, я вывожу информацию о типе исключения.

см. след. стр.

484 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

if ( EXCEPTION_STACK_OVERFLOW == pExPtrs >ExceptionRecord >ExceptionCode )

{

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

}

if ( NULL != g_pfnCallBack )

{

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

//чтобы подготовить ее к работе и иметь возможность

//получения базового адреса модуля по адресу ошибки. InitSymEng ( ) ;

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

BOOL bCallIt = FALSE ;

 

if ( 0 == g_uiModCount )

 

{

 

bCallIt = TRUE ;

 

}

 

else

 

{

 

HINSTANCE hBaseAddr = (HINSTANCE)

 

SymGetModuleBase64( GetCurrentProcess ( )

,

(DWORD64)pExPtrs >

 

ExceptionRecord >

 

ExceptionAddress);

if ( NULL != hBaseAddr )

{

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

{

if ( hBaseAddr == g_ahMod[ i ] )

{

bCallIt = TRUE ; break ;

}

}

}

}

if ( TRUE == bCallIt )

{

//Прежде чем вызвать обработчик ошибок, я проверяю его

//наличие в памяти. Пользователь может забыть отменить

//регистрацию, и обработчик ошибок может быть

//некорректным, если он был выгружен. Однако, если

//по тому же адресу будет загружена какая нибудь другая

//функция, я ничего не смогу сделать.

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

ГЛАВА 13 Обработчики ошибок

485

 

 

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

{

lRet = g_pfnCallBack ( pExPtrs ) ;

}

}

else

{

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

//проверки. Я становлюсь немного подозрительным.

ASSERT ( FALSE == IsBadCodePtr((FARPROC)g_pfnOrigFilt)); if ( FALSE == IsBadCodePtr ( (FARPROC)g_pfnOrigFilt ) )

{

lRet = g_pfnOrigFilt ( pExPtrs ) ;

}

}

CleanupSymEng ( ) ;

}

}

__except ( EXCEPTION_EXECUTE_HANDLER )

{

lRet = EXCEPTION_CONTINUE_SEARCH ;

}

return ( lRet ) ;

}

/*////////////////////////////////////////////////////////////////////// // Реализация функций преобразования структуры EXCEPTION_POINTERS.

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

LPCTSTR __stdcall GetFaultReason ( EXCEPTION_POINTERS * pExPtrs )

{

ASSERT ( FALSE == IsBadReadPtr ( pExPtrs ,

sizeof ( EXCEPTION_POINTERS ) ) ) ; if ( TRUE == IsBadReadPtr ( pExPtrs ,

sizeof ( EXCEPTION_POINTERS ) ) )

{

TRACE0 ( "Bad parameter to GetFaultReason\n" ) ; return ( NULL ) ;

}

// Переменная для хранения возвращаемого значения LPCTSTR szRet ;

__try

{

//Инициализация символьной машины, если она не инициализирована. InitSymEng ( ) ;

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

см. след. стр.

486 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

int iCurr = 0 ;

//Переменная для хранения временного значения. Это

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

iCurr += BSUGetModuleBaseName ( GetCurrentProcess ( ) ,

NULL

,

g_szBuff

,

BUFF_SIZE

) ;

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

dwTemp = (DWORD_PTR) 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

 

);

}

 

 

ASSERT ( iCurr < ( BUFF_SIZE MAX_PATH ) ) ;

 

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

dwTemp =

SymGetModuleBase64( GetCurrentProcess ( ) ,

(DWORD64)pExPtrs >ExceptionRecord >

ExceptionAddress ) ;

ASSERT ( NULL != dwTemp ) ;

if ( NULL == dwTemp )

{

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

}

else

ГЛАВА 13 Обработчики ошибок

487

 

 

 

 

 

 

 

 

 

 

{

 

 

 

 

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_SYMBOL64 pSym = (PIMAGEHLP_SYMBOL64)&g_stSymbol ;

 

ZeroMemory ( pSym , SYM_BUFF_SIZE ) ;

 

 

 

 

pSym >SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL64 ) ;

 

 

 

pSym >MaxNameLength = SYM_BUFF_SIZE

 

 

 

 

sizeof ( IMAGEHLP_SYMBOL64 ) ;

 

 

DWORD64 dwDisp ;

 

 

 

 

if ( TRUE ==

 

 

 

 

SymGetSymFromAddr64 ( GetCurrentProcess ( )

 

,

 

(DWORD64)pExPtrs >ExceptionRecord >

 

 

ExceptionAddress ,

 

&dwDisp

 

 

,

 

pSym

 

 

))

 

{

 

 

 

 

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

 

 

// Копируемая в буфер информация о символах

 

 

 

// не должна превышать объем свободного места.

 

 

 

// Помните: имена символов имеют формат ANSI!

 

 

 

int iLen = lstrlenA ( pSym >Name ) ;

 

 

 

 

// Проверка того, что у нас хватает пространства

 

 

 

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

 

 

 

if ( iLen > ( ( BUFF_SIZE iCurr)

 

 

 

 

( MAX_SYM_SIZE + 50 )

) )

 

 

 

{

 

 

 

 

#ifdef UNICODE

 

 

 

 

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

 

TCHAR * pWideName = (TCHAR*)_alloca ( iLen + 1 ) ;

 

 

 

 

 

 

 

см. след. стр.

488

ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

 

 

 

 

 

 

 

BSUAnsi2Wide ( pSym >Name , pWideName , iLen + 1 ) ;

 

lstrcpyn ( g_szBuff + iCurr

,

 

 

pWideName

,

 

 

BUFF_SIZE iCurr 1

) ;

 

#else

 

 

 

 

lstrcpyn ( g_szBuff + iCurr

,

 

 

pSym >Name

,

 

 

BUFF_SIZE iCurr 1

) ;

 

#endif

// UNICODE

 

 

 

// Выход

 

 

 

szRet = g_szBuff ;

 

 

 

__leave ;

 

 

 

}

 

 

 

else

 

 

 

{

 

 

 

if ( dwDisp > 0 )

 

 

 

{

 

 

 

iCurr += wsprintf ( g_szBuff + iCurr

,

 

k_NAMEDISPFMT

,

 

pSym >Name

,

 

dwDisp

 

) ;

 

}

 

 

 

else

 

 

 

{

 

 

 

iCurr += wsprintf ( g_szBuff + iCurr ,

 

k_NAMEFMT

 

,

 

pSym >Name

) ;

 

}

 

 

 

}

 

 

 

}

 

 

 

else

 

 

 

{

 

 

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

//и номере строки также не будет получена, поэтому выходим. szRet = g_szBuff ;

__leave ;

}

ASSERT ( iCurr < ( BUFF_SIZE 200 ) ) ;

 

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

 

ZeroMemory ( &g_stLine , sizeof ( IMAGEHLP_LINE64 ) ) ;

 

g_stLine.SizeOfStruct = sizeof ( IMAGEHLP_LINE64 ) ;

 

DWORD dwLineDisp ;

 

if ( TRUE ==

 

SymGetLineFromAddr64 ( GetCurrentProcess ( )

,

(DWORD64)pExPtrs >

 

ExceptionRecord >

 

ГЛАВА 13

Обработчики ошибок

489

 

 

 

 

 

 

ExceptionAddress ,

 

&dwLineDisp

,

 

&g_stLine

) )

 

{

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

//Копируемая в буфер информация об исходном файле и номере

//строки не должна превышать объем свободного места.

int iLen = lstrlenA ( g_stLine.FileName ) ; if ( iLen > ( BUFF_SIZE iCurr

MAX_PATH 50 ) )

{

#ifdef UNICODE

// Получение места в стеке для преобразования строки. TCHAR * pWideName = (TCHAR*)_alloca ( iLen + 1 ) ;

BSUAnsi2Wide(g_stLine.FileName , pWideName , iLen + 1);

lstrcpyn ( g_szBuff + iCurr

,

 

pWideName

,

 

BUFF_SIZE iCurr 1

) ;

 

#else

 

 

lstrcpyn ( g_szBuff + iCurr

,

 

g_stLine.FileName

,

 

BUFF_SIZE iCurr 1

) ;

 

#endif // UNICODE

 

 

// Выход

 

 

szRet = g_szBuff ;

 

 

__leave ;

 

 

}

 

 

else

 

 

{

 

 

if ( dwLineDisp > 0 )

 

 

{

 

 

iCurr += wsprintf ( g_szBuff + iCurr

,

k_FILELINEDISPFMT

,

g_stLine.FileName

,

g_stLine.LineNumber

,

dwLineDisp

);

}

 

else

 

{

 

iCurr += wsprintf ( g_szBuff + iCurr

,

k_FILELINEFMT

,

g_stLine.FileName

,

g_stLine.LineNumber

) ;

}

 

}

 

}

 

см. след. стр.

490 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

szRet = g_szBuff ;

}

__except ( EXCEPTION_EXECUTE_HANDLER )

{

ASSERT ( !"Crashed in GetFaultReason" ) ; szRet = NULL ;

}

return ( szRet ) ;

}

//Вспомогательная функция, позволяющая изолировать заполнение

//структуры кадра стека, которое зависит от процессора.

void FillInStackFrame ( PCONTEXT pCtx )

{

// Инициализация структуры STACKFRAME.

ZeroMemory ( &g_stFrame , sizeof ( STACKFRAME64 ) ) ;

#ifdef _X86_

 

 

 

g_stFrame.AddrPC.Offset

= pCtx >Eip

;

 

g_stFrame.AddrPC.Mode

= AddrModeFlat

;

 

g_stFrame.AddrStack.Offset

= pCtx >Esp

;

 

g_stFrame.AddrStack.Mode

= AddrModeFlat

;

 

g_stFrame.AddrFrame.Offset

= pCtx >Ebp

;

 

g_stFrame.AddrFrame.Mode

= AddrModeFlat ;

 

#elif

_AMD64_

 

 

 

g_stFrame.AddrPC.Offset

= pCtx >Rip

;

 

g_stFrame.AddrPC.Mode

= AddrModeFlat

;

 

g_stFrame.AddrStack.Offset

= pCtx >Rsp

;

 

g_stFrame.AddrStack.Mode

= AddrModeFlat

;

 

g_stFrame.AddrFrame.Offset

= pCtx >Rbp

;

 

g_stFrame.AddrFrame.Mode

= AddrModeFlat ;

 

#elif

_IA64_

 

 

 

#pragma message ( "IA64 NOT DEFINED!!" )

 

 

#pragma FORCE COMPILATION ABORT!

 

 

#else

 

 

 

 

#pragma message ( "CPU NOT DEFINED!!" )

 

 

#pragma FORCE COMPILATION ABORT!

 

 

#endif

 

 

 

 

}

 

 

 

 

LPCTSTR BUGSUTIL_DLLINTERFACE __stdcall

 

 

 

GetFirstStackTraceString ( DWORD

dwOpts

,

 

 

EXCEPTION_POINTERS * pExPtrs

)

{

 

 

 

 

ASSERT ( FALSE == IsBadReadPtr ( pExPtrs

,

 

 

 

sizeof ( EXCEPTION_POINTERS * ))) ;

if ( TRUE == IsBadReadPtr ( pExPtrs

,

 

 

 

sizeof ( EXCEPTION_POINTERS * ) ) )

 

{

 

 

 

 

TRACE0 ( "GetFirstStackTraceString invalid pExPtrs!\n" ) ;

ГЛАВА 13 Обработчики ошибок

491

 

 

return ( NULL ) ;

}

//Заполнение структуры кадра стека. FillInStackFrame ( pExPtrs >ContextRecord ) ;

//Чтобы не повредить поля структуры EXCEPTION_POINTERS,

//я выполняю их копирование.

g_stContext = *(pExPtrs >ContextRecord) ;

return ( InternalGetStackTraceString ( dwOpts ) ) ;

}

LPCTSTR BUGSUTIL_DLLINTERFACE __stdcall

 

GetNextStackTraceString ( DWORD

dwOpts ,

EXCEPTION_POINTERS * /*pExPtrs*/)

{

//Вся проверка ошибок выполняется в InternalGetStackTraceString.

//Предполагается, что GetFirstStackTraceString уже инициализировала

//информацию о кадре стека.

return ( InternalGetStackTraceString ( dwOpts ) ) ;

}

BOOL __stdcall CH_ReadProcessMemory ( HANDLE

 

 

,

DWORD64

qwBaseAddress

,

PVOID

lpBuffer

,

DWORD

nSize

 

,

LPDWORD

lpNumberOfBytesRead

)

{

 

 

 

return ( ReadProcessMemory ( GetCurrentProcess ( )

,

 

(LPCVOID)qwBaseAddress ,

 

lpBuffer

 

,

 

nSize

 

,

 

lpNumberOfBytesRead

) ) ;

 

}

 

 

 

// Внутренняя функция, отвечающая за весь анализ стека

LPCTSTR __stdcall InternalGetStackTraceString ( DWORD dwOpts )

{

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

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

//функции StackWalk, чтобы гарантировать корректность модуля. DWORD64 dwModBase ;

__try

{

// Инициализация символьной машины, если она не инициализирована.

InitSymEng ( ) ;

см. след. стр.

492

ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

 

 

 

 

 

 

 

 

 

 

 

 

 

// Примечание:

При использовании функций получения информации

 

 

//

об исходном

файле и номере строки StackWalk

 

 

//

может вызвать нарушение доступа.

 

 

BOOL bSWRet = StackWalk64 (

CH_MACHINE

,

 

 

 

GetCurrentProcess ( )

,

 

 

 

GetCurrentThread ( )

,

 

 

 

&g_stFrame

,

 

 

 

&g_stContext

,

 

 

 

CH_ReadProcessMemory

,

 

 

 

SymFunctionTableAccess64

,

 

 

 

SymGetModuleBase64

,

 

 

 

NULL

);

 

if ( ( FALSE == bSWRet ) ||

( 0 == g_stFrame.AddrFrame.Offset ))

 

{

 

 

 

szRet = NULL ; __leave ;

}

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

//в том, что адрес, возвращенный из StackWalk, действительно

//существует. Мне известны случаи, когда StackWalk возвращала

//TRUE, но адрес не относился к модулю данного процесса.

dwModBase = SymGetModuleBase64 ( GetCurrentProcess ( )

,

g_stFrame.AddrPC.Offset

) ;

if ( 0 == dwModBase )

 

 

 

{

 

 

 

szRet = NULL ;

 

 

 

__leave ;

 

 

 

}

 

 

 

int iCurr = 0 ;

 

 

 

// Как минимум помещаем в буфер адрес.

 

 

 

#ifdef _WIN64

 

 

 

iCurr += wsprintf ( g_szBuff + iCurr

,

 

 

_T ( "0x%016X" )

,

 

 

g_stFrame.AddrPC.Offset

) ;

 

 

#else

 

 

 

iCurr += wsprintf ( g_szBuff + iCurr

,

 

 

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

,

 

 

g_stContext.SegCs

,

 

 

g_stFrame.AddrPC.Offset

) ;

 

 

#endif

 

 

 

// Выводить параметры?

 

 

 

if ( GSTSO_PARAMS == ( dwOpts & GSTSO_PARAMS ) )

 

 

{

 

 

 

iCurr += wsprintf ( g_szBuff + iCurr

 

,

 

k_PARAMFMTSTRING

 

,

 

g_stFrame.Params[ 0 ]

,

 

Соседние файлы в предмете Программирование на C++