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

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

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

ГЛАВА 4 Поддержка отладки ОС и как работают отладчики Win32

161

 

 

//Проверка наличия аргументов в командной строке. if ( 1 == argc )

{

ShowHelp ( ) ; return ;

}

//Необходим достаточно большой буфер для команды

//или параметров командной строки. TCHAR szCmdLine[ MAX_PATH + MAX_PATH ] ;

//Идентификатор процесса, если производится присоединение к нему. DWORD dwPID = 0 ;

szCmdLine[ 0 ] = _T ( '\0' ) ;

//Проверка, начинается ли командная строка со знака " ", так как это

//означает идентификатор процесса, к которому мы присоединяемся.

if ( _T ( ' ' ) == argv[1][0] )

{

//Попытка вычленить идентификатор процесса из командной строки.

//Передвинуться за символ ' ' в строке.

TCHAR * pPID = argv[1] + 1 ; dwPID = _tstol ( pPID ) ;

if ( 0 == dwPID )

{

_tprintf ( _T ( "Invalid PID value : %s\n" ) , pPID ) ; return ;

}

}

else

{

dwPID = 0 ;

// Я собираюсь запустить процесс. for ( int i = 1 ; i < argc ; i++ )

{

_tcscat ( szCmdLine , argv[ i ] ) ; if ( i < argc )

{

_tcscat ( szCmdLine , _T ( " " ) ) ;

}

}

}

//Место для возвращаемого значения. BOOL bRet = FALSE ;

//Установить обработчик CTRL+BREAK.

bRet = SetConsoleCtrlHandler ( CtrlBreakHandler , TRUE ) ;

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

162 ЧАСТЬ II Производительная отладка

if ( FALSE == bRet )

{

_tprintf ( _T ( "Unable to set CTRL+BREAK handler!\n" ) ) ; return ;

}

// Если идентификатор процесса равен 0, я запускаю процесс. if ( 0 == dwPID )

{

//Попытаемся запустить отлаживаемый процесс. Этот вызов функции

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

//необязательного флага DEBUG_ONLY_THIS_PROCESS.

STARTUPINFO

stStartInfo

;

 

PROCESS_INFORMATION

stProcessInfo

;

 

memset ( &stStartInfo

, NULL , sizeof ( STARTUPINFO

));

memset ( &stProcessInfo , NULL , sizeof ( PROCESS_INFORMATION));

stStartInfo.cb = sizeof ( STARTUPINFO ) ;

bRet = CreateProcess ( NULL

,

szCmdLine

,

NULL

,

NULL

,

FALSE

,

CREATE_NEW_CONSOLE |

 

DEBUG_ONLY_THIS_PROCESS

,

NULL

,

NULL

,

&stStartInfo

,

&stProcessInfo

) ;

//Не забудьте закрыть описатели процесса и потока,

//возвращаемые CreateProcess.

VERIFY ( CloseHandle ( stProcessInfo.hProcess ) ) ;

VERIFY ( CloseHandle ( stProcessInfo.hThread ) ) ;

//Посмотрим, запустился ли процесс отлаживаемой программы. if ( FALSE == bRet )

{

_tprintf ( _T ( "Unable to start %s\n" ) , szCmdLine ) ; return ;

}

//Сохранить идентификатор процесса на случай

//необходимости отсоединения.

dwPID = stProcessInfo.dwProcessId ;

}

else

{

ГЛАВА 4 Поддержка отладки ОС и как работают отладчики Win32

163

 

 

bRet = DebugActiveProcess ( dwPID ) ; if ( FALSE == bRet )

{

_tprintf ( _T ( "Unable to attach to %u\n" ) , dwPID ) ; return ;

}

}

// Отлаживаемая программ запущена, поэтому запускаем цикл отладчика.

DEBUG_EVENT stDE

 

 

;

BOOL

bSeenInitialBP

= FALSE

;

BOOL

bContinue

=

TRUE

;

HANDLE

hProcess

=

INVALID_HANDLE_VALUE ;

DWORD

dwContinueStatus

 

 

;

// Цикл до тех пор, пока не потребуется остановиться. while ( TRUE == bContinue )

{

// Пауза до возникновения события отладки.

BOOL bProcessDbgEvent = WaitForDebugEvent ( &stDE , 100 ) ;

if ( TRUE == bProcessDbgEvent )

{

//Обработка конкретных событий отладки.

//Так как MinDBG — это только минимальный отладчик,

//он обрабатывает только несколько событий.

switch (

stDE.dwDebugEventCode )

 

{

 

 

case

CREATE_PROCESS_DEBUG_EVENT

:

{

 

 

DisplayCreateProcessEvent(stDE.u.CreateProcessInfo);

//Сохраним описатель, который понадобится позже.

//Заметьте: вы не можете закрыть этот описатель.

//Если вы это сделаете, CloseHandle завершится с ошибкой. hProcess = stDE.u.CreateProcessInfo.hProcess ;

//Описатель файла можно закрыть безболезненно.

//Если вы закроете поток, CloseHandle провалится

//глубоко в ContinueDebugEvent, когда вы будете

//завершать приложение. VERIFY(CloseHandle(stDE.u.CreateProcessInfo.hFile));

dwContinueStatus = DBG_CONTINUE ;

}

 

break ;

 

case EXIT_PROCESS_DEBUG_EVENT

:

{

 

DisplayExitProcessEvent ( stDE.u.ExitProcess ) ; bContinue = FALSE ;

dwContinueStatus = DBG_CONTINUE ;

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

164 ЧАСТЬ II Производительная отладка

}

break ;

case LOAD_DLL_DEBUG_EVENT

:

{

 

DisplayDllLoadEvent ( hProcess , stDE.u.LoadDll ) ;

// Не забудьте закрыть описатель соответствующего файла. VERIFY ( CloseHandle( stDE.u.LoadDll.hFile ) ) ;

dwContinueStatus = DBG_CONTINUE ;

}

break ;

case UNLOAD_DLL_DEBUG_EVENT :

{

DisplayDllUnLoadEvent ( stDE.u.UnloadDll ) ; dwContinueStatus = DBG_CONTINUE ;

}

break ;

case CREATE_THREAD_DEBUG_EVENT :

 

{

 

DisplayCreateThreadEvent ( stDE.dwThreadId

,

stDE.u.CreateThread

) ;

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

//Если вы это сделаете, CloseHandle провалится глубоко

//в ContinueDebugEvent.

dwContinueStatus = DBG_CONTINUE ;

 

}

 

 

break ;

 

 

case EXIT_THREAD_DEBUG_EVENT

:

 

{

 

 

DisplayExitThreadEvent ( stDE.dwThreadId

,

stDE.u.ExitThread

) ;

dwContinueStatus = DBG_CONTINUE ;

 

}

 

 

break ;

 

 

case OUTPUT_DEBUG_STRING_EVENT

:

 

{

 

 

DisplayODSEvent ( hProcess , stDE.u.DebugString ) ; dwContinueStatus = DBG_CONTINUE ;

}

break ;

case EXCEPTION_DEBUG_EVENT

:

{

 

DisplayExceptionEvent ( stDE.u.Exception ) ;

ГЛАВА 4 Поддержка отладки ОС и как работают отладчики Win32

165

 

 

//Единственное исключение, требующее специальной

//обработки, — это точка прерывания загрузчика. switch(stDE.u.Exception.ExceptionRecord.ExceptionCode)

{

case EXCEPTION_BREAKPOINT :

{

//Если возникает исключение по точке прерывания

//и оно первое, я продолжаю свое веселье, иначе

//я передаю исключение отлаживаемой программе.

if ( FALSE == bSeenInitialBP )

{

bSeenInitialBP = TRUE ; dwContinueStatus = DBG_CONTINUE ;

}

else

{

// Хьюстон, у нас проблема! dwContinueStatus =

DBG_EXCEPTION_NOT_HANDLED ;

}

}

break ;

//Все остальные исключения передаем

//отлаживаемой программе.

default :

{

dwContinueStatus =

DBG_EXCEPTION_NOT_HANDLED ;

}

break ;

}

}

break ;

// Для всех остальных событий – просто продолжаем. default :

{

dwContinueStatus = DBG_CONTINUE ;

}

break ;

}

// Передаем управление ОС. #ifdef _DEBUG

BOOL bCntDbg =

#endif

ContinueDebugEvent ( stDE.dwProcessId , stDE.dwThreadId , dwContinueStatus ) ;

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

166 ЧАСТЬ II Производительная отладка

ASSERT ( TRUE == bCntDbg ) ;

}

// Необходимо ли отсоединение? if ( TRUE == g_bDoTheDetach )

{

//Отсоединение работает только в XP или более поздней версии,

//поэтому я должен выполнить GetProcAddress, чтобы найти

//DebugActiveProcessStop.

bContinue = FALSE ;

HINSTANCE hKernel32 =

GetModuleHandle ( _T ( "KERNEL32.DLL" ) ) ; if ( 0 != hKernel32 )

{

PFNDEBUGACTIVEPROCESSSTOP pfnDAPS = (PFNDEBUGACTIVEPROCESSSTOP)

GetProcAddress ( hKernel32 ,

"DebugActiveProcessStop" ) ;

if ( NULL != pfnDAPS )

{

#ifdef _DEBUG

BOOL bTemp =

#endif

pfnDAPS ( dwPID ) ;

ASSERT ( TRUE == bTemp ) ;

}

}

}

}

}

/*////////////////////////////////////////////////////////////////////// // Мониторы обработки Ctrl+Break

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

BOOL WINAPI CtrlBreakHandler ( DWORD dwCtrlType )

{

//Я буду обрабатывать только Ctrl+Break.

//Все другое убивает отлаживаемую программу. if ( CTRL_BREAK_EVENT == dwCtrlType )

{

g_bDoTheDetach = TRUE ; return ( TRUE ) ;

}

return ( FALSE ) ;

}

/*////////////////////////////////////////////////////////////////////// // Отображает справку к программе.

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

ГЛАВА 4

Поддержка отладки ОС и как работают отладчики Win32

167

 

 

 

 

 

 

 

 

 

void ShowHelp ( void )

 

 

{

 

 

 

_tprintf ( _T (

"Start a program to debug:\n" )

 

_T (

" MinDBG <program to debug> " )

 

_T (

"<program's command line options>\n" )

 

_T (

"Attach to an existing program:\n" )

 

_T (

"

MinDBG PID\n" )

 

_T (

"

PID is the decimal process ID\n" ) ) ;

 

}

 

 

 

/*////////////////////////////////////////////////////////////////////// // Отображение события создания процесса.

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

void DisplayCreateProcessEvent ( CREATE_PROCESS_DEBUG_INFO & stCPDI )

{

_tprintf ( _T ( "Create Process Event

:\n" ) ) ;

_tprintf ( _T ( "

hFile

: 0x%08X\n" ) ,

stCPDI.hFile

) ;

_tprintf ( _T ( "

hProcess

: 0x%08X\n" ) ,

stCPDI.hProcess

) ;

_tprintf ( _T ( "

hThread

: 0x%08X\n" ) ,

stCPDI.hThread

) ;

_tprintf ( _T ( "

lpBaseOfImage

: 0x%08X\n" ) ,

stCPDI.lpBaseOfImage

) ;

_tprintf ( _T ( "

dwDebugInfoFileOffset

: 0x%08X\n" ) ,

stCPDI.dwDebugInfoFileOffset

) ;

_tprintf ( _T ( "

nDebugInfoSize

: 0x%08X\n" ) ,

stCPDI.nDebugInfoSize

) ;

_tprintf ( _T ( "

lpThreadLocalBase

: 0x%08X\n" ) ,

stCPDI.lpThreadLocalBase

) ;

_tprintf ( _T ( "

lpStartAddress

: 0x%08X\n" ) ,

stCPDI.lpStartAddress

) ;

_tprintf ( _T ( "

lpImageName

: 0x%08X\n" ) ,

stCPDI.lpImageName

) ;

_tprintf ( _T ( "

fUnicode

: 0x%08X\n" ) ,

stCPDI.fUnicode

) ;

}

/*////////////////////////////////////////////////////////////////////// // Отображение событий создания потока.

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

void DisplayCreateThreadEvent ( DWORD dwTID , CREATE_THREAD_DEBUG_INFO & stCTDI )

{

_tprintf ( _T ( "Create Thread Event

:\n" ) ) ;

_tprintf

(

_T

(

"

TID

: 0x%08X\n" ) ,

 

 

dwTID

 

) ;

_tprintf

(

_T

(

"

hThread

: 0x%08X\n" ) ,

 

 

stCTDI.hThread

) ;

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

168 ЧАСТЬ II Производительная отладка

_tprintf

(

_T

(

"

lpThreadLocalBase

: 0x%08X\n"

)

,

 

stCTDI.lpThreadLocalBase

 

 

) ;

_tprintf

(

_T

(

"

lpStartAddress

: 0x%08X\n"

)

,

 

 

stCTDI.lpStartAddress

 

 

) ;

}

/*////////////////////////////////////////////////////////////////////// // Отображение событий завершения потока.

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

void DisplayExitThreadEvent ( DWORD dwTID , EXIT_THREAD_DEBUG_INFO & stETDI )

{

_tprintf ( _T ( "Exit Thread Event

:\n" ) ) ;

_tprintf

(

_T

(

"

TID

: 0x%08X\n" ) ,

 

 

dwTID

 

) ;

_tprintf

(

_T

(

"

dwExitCode

: 0x%08X\n" ) ,

 

 

stETDI.dwExitCode

) ;

}

/*////////////////////////////////////////////////////////////////////// // Отображение событий завершения процесса.

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

void DisplayExitProcessEvent ( EXIT_PROCESS_DEBUG_INFO & stEPDI )

{

_tprintf

(

_T

(

"Exit Process Event

:\n" ) ) ;

_tprintf

(

_T

(

"

dwExitCode

: 0x%08X\n" ) ,

 

 

stEPDI.dwExitCode

) ;

}

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

// Отображение событий загрузки DLL.

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

void DisplayDllLoadEvent ( HANDLE

hProcess ,

 

 

LOAD_DLL_DEBUG_INFO

& stLDDI

)

{

 

 

 

_tprintf ( _T ( "DLL Load Event

:\n" ) ) ;

 

_tprintf ( _T ( "

hFile

: 0x%08X\n" ) ,

stLDDI.hFile

 

) ;

_tprintf ( _T ( "

lpBaseOfDll

: 0x%08X\n" ) ,

stLDDI.lpBaseOfDll

 

) ;

_tprintf ( _T ( "

dwDebugInfoFileOffset

: 0x%08X\n" ) ,

stLDDI.dwDebugInfoFileOffset

 

) ;

_tprintf ( _T ( "

nDebugInfoSize

: 0x%08X\n" ) ,

stLDDI.nDebugInfoSize

 

) ;

_tprintf ( _T ( "

lpImageName

: 0x%08X\n" ) ,

stLDDI.lpImageName

 

) ;

_tprintf ( _T ( "

fUnicode

: 0x%08X\n" ) ,

stLDDI.fUnicode

 

) ;

static bool bSeenNTDLL = false ;

ГЛАВА 4 Поддержка отладки ОС и как работают отладчики Win32

169

 

 

TCHAR szDLLName[ MAX_PATH ] ;

//NTDLL.DLL – это специальный случай. В W2K lpImageName равен NULL,

//а в XP он указывает просто на 'ntdll.dll', поэтому я сфабрикую

//загрузочную информацию.

if ( false == bSeenNTDLL )

{

bSeenNTDLL = true ;

UINT uiLen = GetWindowsDirectory ( szDLLName , MAX_PATH ) ;

ASSERT ( uiLen > 0 ) ;

if ( uiLen > 0 )

{

_tcscpy ( szDLLName + uiLen , _T ( "\\NTDLL.DLL" ) ) ;

}

else

{

_tcscpy ( szDLLName , _T ( "GetWindowsDirectory FAILED!" ));

}

}

else

{

szDLLName[ 0 ] = _T ( '\0' ) ;

//Значение в lpImageName является указателем на полный путь

//загружаемой DLL. Этот адрес находится в адресном пространстве

//отлаживаемой программы.

LPCVOID lpPtr = 0 ;

DWORD dwBytesRead = 0 ;

BOOL bRet = FALSE ;

bRet = ReadProcessMemory ( hProcess

,

stLDDI.lpImageName

,

&lpPtr

,

sizeof ( LPCVOID ) ,

 

&dwBytesRead

) ;

if ( TRUE == bRet )

 

{

 

//Если имя в отлаживаемой программе задано в UNICODE,

//я могу копировать его прямо в szDLLName,

//так как здесь все в UNICODE.

if ( TRUE == stLDDI.fUnicode )

{

//Иногда невозможно сразу считать весь буфер,

//содержащий имя, поэтому необходимо делать это

//частями, пока оно не будет считано целиком. DWORD dwSize = MAX_PATH * sizeof ( TCHAR ) ; do

{

bRet = ReadProcessMemory ( hProcess

,

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

170 ЧАСТЬ II Производительная отладка

lpPtr

,

szDLLName

,

dwSize

,

&dwBytesRead

) ;

dwSize = dwSize 20 ;

}

while ( ( FALSE == bRet ) && ( dwSize > 20 ) ) ;

}

else

{

// Считывание строки ANSI и преобразование ее в UNICODE. char szAnsiName[ MAX_PATH ] ;

DWORD dwAnsiSize = MAX_PATH ;

do

 

{

 

bRet = ReadProcessMemory ( hProcess

,

lpPtr

,

szAnsiName

,

dwAnsiSize

,

&dwBytesRead

) ;

dwAnsiSize = dwAnsiSize 20 ;

 

} while ( ( FALSE == bRet

) && ( dwAnsiSize > 20 ) ) ;

if ( TRUE == bRet )

 

 

{

 

 

MultiByteToWideChar (

CP_THREAD_ACP

,

 

0

,

 

szAnsiName

,

 

1

,

 

szDLLName

,

 

MAX_PATH

) ;

}

 

 

}

}

}

if ( _T ( '\0' ) == szDLLName[ 0 ] )

{

//С этой DLL связано несколько проблем. Попробуйте считать ее

//с помощью GetModuleHandleEx. Хотя вы и можете думать, что это

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

//информация о модуле этим способом. Если невозможно получить имя DLL

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

//с перемещенной DLL.

DWORD dwRet = GetModuleFileNameEx ( hProcess

,

(HMODULE)stLDDI.

 

lpBaseOfDll

,

szDLLName

,

MAX_PATH

);

ASSERT ( dwRet > 0 ) ;

 

if ( 0 == dwRet )

 

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