2016-02-13 8 views
0

Я пытаюсь программно генерировать трассировку стека. Когда у моих пользователей произошел сбой, в частности случайный, трудно обсуждать их через процесс получения дампа, чтобы я мог решить проблему. В прошлом, как только они отправили бы мне трассировку, я бы перекрестно ссылался на адреса в нем в файл Intermediate/foo.map, чтобы выяснить, какая функция была проблемой (это лучший способ?)Как получить значимую трассировку стека с помощью MiniDumpWriteDump

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

minidump.dmp:

KERNELBASE.dll!76a6c42d() 
[Frames below may be incorrect and/or missing, no symbols loaded for KERNELBASE.dll] 
KERNELBASE.dll!76a6c42d() 
kernel32.dll!75bd14bd() 
game.exe!00759035() 
game.exe!00575ba3() 

WINDBG.EXE:

0:000:x86> kv 
ChildEBP RetAddr Args to Child    
00186f44 00bc8ea9 19460268 0018a9b7 03f70a28 Minidump!crashme+0x2 (FPO: [0,0,0]) (CONV: cdecl) [c:\project\debug\minidump.cpp @ 68] 
0018795c 00b9ef31 0018796c 03f56c00 6532716d Main!LoadPlugin+0x339 (FPO: [1,642,4]) (CONV: cdecl) [c:\project\main\pluginloader.cpp @ 129] 
00188968 00b9667d 19460268 0018a9ac 00000000 Main!Command+0x1f1 (FPO: [2,1024,4]) (CONV: cdecl) [c:\project\main\commands.cpp @ 2617] 
*** WARNING: Unable to verify checksum for C:\Game\game.exe 
*** ERROR: Module load completed but symbols could not be loaded for C:\Game\game.exe 
0018b1a8 005b5095 19460268 0018beac 00000000 Main!Hook::Detour+0x52d (FPO: [2,2570,0]) (CONV: thiscall) [c:\project\main\hook.cpp @ 275] 
WARNING: Stack unwind information not available. Following frames may be wrong. 
0018b1b4 00000000 19495200 19495200 00000006 game+0x1b5095 

game.exe не мой, и я не имею источник/символов. Main.dll вводится в файл game.exe и предоставляет функции переднего конца для загрузки дополнительных DLL из игры. Код отладки и поэтапная авария находятся в файле Minidump.dll. После того, как Main.dll загружает Minidump, он вызывает AfterLoad(), который устанавливает фильтр исключений, а затем вызывает сбой. Соответствующий код minidump приведен ниже:

Когда я открыл MiniDump.dmp, я указал его на все мои файлы символов (за исключением файла game.exe, которого у меня нет), и эта часть, похоже, работает , Я указываю его на двоичный файл game.exe, поскольку у меня это есть. Трассировка стека, которую я получаю, просто действительно не помогает. Моя конечная цель заключается в том, что пользователь может просто загрузить DLL, вызвать сбой и отправить мне файл дампа. Затем я приложу файлы символов и двоичные файлы и смогу диагностировать проблему для них. Я делаю что-то неправильно, или просто невозможно получить то, что я хочу.

typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(
    HANDLE hProcess, 
    DWORD ProcessId, 
    HANDLE hFile, 
    MINIDUMP_TYPE DumpType, 
    CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 
    CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 
    CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam 
); 

LONG WINAPI WriteDumpFilter(struct _EXCEPTION_POINTERS *pExceptionPointers) 
{ 
    HANDLE hFile = NULL; 
    HMODULE hDll = NULL; 
    MINIDUMPWRITEDUMP pMiniDumpWriteDump = NULL; 
    _MINIDUMP_EXCEPTION_INFORMATION ExceptionInformation = {0}; 

    //load MiniDumpWriteDump 
    hDll = LoadLibrary(TEXT("DbgHelp.dll")); 
    pMiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(hDll, "MiniDumpWriteDump"); 

    //create output file 
    hFile = CreateFile(_T("C:\\temp\\MiniDump.dmp"), 
          GENERIC_READ|GENERIC_WRITE, 0, NULL, 
          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 

    //bail if we don't have a file 
    if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) 
    { 
     //get exception information 
     ExceptionInformation.ThreadId   = GetCurrentThreadId(); 
     ExceptionInformation.ExceptionPointers = pExceptionPointers; 
     ExceptionInformation.ClientPointers  = TRUE; 

     //write the debug dump 
     pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), 
          hFile, MiniDumpWithFullMemory, &ExceptionInformation, 
          NULL, NULL); 


     //close the debug output file 
     CloseHandle(hFile); 
    } 

    return EXCEPTION_EXECUTE_HANDLER; 
} 

VOID crashme() {int* foo = 0; *foo = 0;} 

VOID AfterLoad(VOID) 
{ 
    SetUnhandledExceptionFilter(WriteDumpFilter); 
    crashme(); 
} 

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


После предложения Сергея я .ecxr в WinDbg и получил лучший выход, но он по-прежнему не соответствует трассировку я получаю, когда я подключить WinDbg прямо к процессу и вызвать аварию. Вот мини-трасса;

*** Stack trace for last set context - .thread/.cxr resets it 
ChildEBP RetAddr Args to Child    
WARNING: Stack unwind information not available. Following frames may be wrong. 
0018e774 00759035 e06d7363 00000001 00000003 KERNELBASE!RaiseException+0x58 
0018e7b4 00575ba3 00000000 00000000 00000001 game+0x359035 
0018fc50 0057788a 009855ef 0018fdcb 00000001 game+0x175ba3 
0018fc78 77b7e013 012d9230 002d91d0 002d9200 game+0x17788a 
0018fc90 77ba9567 00290000 00000000 002d91d0 ntdll!RtlFreeHeap+0x7e 
0018fd6c 0076ece2 0018ff78 007e1b7e ffffffff ntdll!LdrRemoveLoadAsDataTable+0x4e0 
002bbc38 5c306174 61666544 00746c75 5d4c3055 game+0x36ece2 
002bbc3c 61666544 00746c75 5d4c3055 8c000000 0x5c306174 
002bbc40 00746c75 5d4c3055 8c000000 00000101 0x61666544 
002bbc44 5d4c3055 8c000000 00000101 01000000 game+0x346c75 
002bbc48 8c000000 00000101 01000000 00000000 0x5d4c3055 
002bbc4c 00000000 01000000 00000000 0000006e 0x8c000000 

и след от присоединения отладчика к процессу

0:000:x86> kv 
ChildEBP RetAddr Args to Child    
00186f44 00bc8ea9 19460268 0018a9b7 03f70a28 Minidump!crashme+0x2 (FPO: [0,0,0]) (CONV: cdecl) [c:\project\debug\minidump.cpp @ 68] 
0018795c 00b9ef31 0018796c 03f56c00 6532716d Main!LoadPlugin+0x339 (FPO: [1,642,4]) (CONV: cdecl) [c:\project\main\pluginloader.cpp @ 129] 
00188968 00b9667d 19460268 0018a9ac 00000000 Main!Command+0x1f1 (FPO: [2,1024,4]) (CONV: cdecl) [c:\project\main\commands.cpp @ 2617] 
*** WARNING: Unable to verify checksum for C:\Game\game.exe 
*** ERROR: Module load completed but symbols could not be loaded for C:\Game\game.exe 
0018b1a8 005b5095 19460268 0018beac 00000000 Main!Hook::Detour+0x52d (FPO: [2,2570,0]) (CONV: thiscall) [c:\project\main\hook.cpp @ 275] 
WARNING: Stack unwind information not available. Following frames may be wrong. 
0018b1b4 00000000 19495200 19495200 00000006 game+0x1b5095 

У меня нет источника для game.exe (я его для библиотек DLL, которые где ошибка), но я декомпилировал game.exe и вот что есть в игре + 0x359035.

.text:00759001 ; =============== S U B R O U T I N E ======================================= 
.text:00759001 
.text:00759001 ; Attributes: library function bp-based frame 
.text:00759001 
.text:00759001 ; __stdcall _CxxThrowException(x, x) 
.text:00759001 [email protected] proc near   ; CODE XREF: .text:0040100Fp 
.text:00759001           ; sub_401640+98p ... 
.text:00759001 
.text:00759001 dwExceptionCode = dword ptr -20h 
.text:00759001 dwExceptionFlags= dword ptr -1Ch 
.text:00759001 nNumberOfArguments= dword ptr -10h 
.text:00759001 Arguments  = dword ptr -0Ch 
.text:00759001 var_8   = dword ptr -8 
.text:00759001 var_4   = dword ptr -4 
.text:00759001 arg_0   = dword ptr 8 
.text:00759001 arg_4   = dword ptr 0Ch 
.text:00759001 
.text:00759001     push ebp 
.text:00759002     mov  ebp, esp 
.text:00759004     sub  esp, 20h 
.text:00759007     mov  eax, [ebp+arg_0] 
.text:0075900A     push esi 
.text:0075900B     push edi 
.text:0075900C     push 8 
.text:0075900E     pop  ecx 
.text:0075900F     mov  esi, offset unk_853A3C 
.text:00759014     lea  edi, [ebp+dwExceptionCode] 
.text:00759017     rep movsd 
.text:00759019     mov  [ebp+var_8], eax 
.text:0075901C     mov  eax, [ebp+arg_4] 
.text:0075901F     mov  [ebp+var_4], eax 
.text:00759022     lea  eax, [ebp+Arguments] 
.text:00759025     push eax    ; lpArguments 
.text:00759026     push [ebp+nNumberOfArguments] ; nNumberOfArguments 
.text:00759029     push [ebp+dwExceptionFlags] ; dwExceptionFlags 
.text:0075902C     push [ebp+dwExceptionCode] ; dwExceptionCode 
.text:0075902F     call ds:RaiseException 
.text:00759035     pop  edi 
.text:00759036     pop  esi 
.text:00759037     leave 
.text:00759038     retn 8 
.text:00759038 [email protected] endp 

Моя ошибка, что я запуска в Minidump.dll, но этот код на вершине стека находится в game.exe. Внутри game.exe может быть много чего-то, о чем я не знаю, может быть, это может быть ошибка, которую я запускаю как-то? I.E., я запускаю ошибку в DLL, но что-то настраивает в игре.exe захватывает поток программы до того, как вызывается фильтр исключения, который пишет мини-накопитель?

Если это так, когда я присоединяю отладчик к процессу, запускаю ошибку и получаю правильный вывод, указывающий на ошибку в моей DLL, то это означает, что game.exe не захватывает поток программы до отладчик может выполнять трассировку. Как я могу сделать код minidump таким же образом ... Это попадает на территорию, с которой я не очень хорошо знаком. Есть идеи?


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

.text:00575A8D     mov  esi, offset aCrashDumpTooLa ; "Crash dump too large to send.\n" 

Так что, я думаю, game.exe это угон исключение, чтобы сделать это самостоятельно свалка до моего код пытается получить дамп. И тогда мой отвалы след просто след процесса сброса GAME.EXE в ...


Ответ

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

.text:0057494A     push offset aDbghelp_dll ; "DbgHelp.dll" 
.text:0057494F     call ds:LoadLibraryA 
.text:00574955     test eax, eax 
.text:00574957     jz  short loc_5749C8 
.text:00574959     push offset aMinidumpwrited ; "MiniDumpWriteDump" 
.text:0057495E     push eax    ; hModule 
.text:0057495F     call ds:GetProcAddress 
.text:00574965     mov  edi, eax 
.text:00574967     test edi, edi 
.text:00574969     jz  short loc_5749C8 
.text:0057496B     mov  edx, lpFileName 
.text:00574971     push 0    ; hTemplateFile 
.text:00574973     push 80h    ; dwFlagsAndAttributes 
.text:00574978     push 2    ; dwCreationDisposition 
.text:0057497A     push 0    ; lpSecurityAttributes 
.text:0057497C     push 0    ; dwShareMode 
.text:0057497E     push 40000000h  ; dwDesiredAccess 
.text:00574983     push edx    ; lpFileName 
.text:00574984     call ds:CreateFileA 
.text:0057498A     mov  esi, eax 
.text:0057498C     cmp  esi, 0FFFFFFFFh 
.text:0057498F     jz  short loc_5749C8 
.text:00574991     call ds:GetCurrentThreadId 
.text:00574997     push 0 
.text:00574999     push 0 
.text:0057499B     mov  [ebp+var_1C], eax 
.text:0057499E     lea  eax, [ebp+var_1C] 
.text:005749A1     push eax 
.text:005749A2     push 0 
.text:005749A4     push esi 
.text:005749A5     mov  [ebp+var_18], ebx 
.text:005749A8     mov  [ebp+var_14], 1 
.text:005749AF     call ds:__imp_GetCurrentProcessId 
.text:005749B5     push eax 
.text:005749B6     call ds:GetCurrentProcess 
.text:005749BC     push eax 
.text:005749BD     call edi 
.text:005749BF     push esi    ; hObject 
.text:005749C0     call ds:CloseHandle 
.text:005749C6     jmp  short loc_574A02 

Thats from game.exe. Оказывается, game.exe делает собственный мини-накопитель. Мой мини-пуск запускался после их, так что то, что я видел в трассировке стека, было следствием их свалки. Я нашел файл dmp в каталоге установки игры, и как только я загрузил в него свои символы, он показал правильный результат, который я получил после.

ответ

0

Я понял. В основном у game.exe был свой собственный код MiniDumpWriteDump, который запускался до моего кода. Таким образом, трассировка стека, которую я получал, не была следствием ошибки, это был след game.exe, делающий свой собственный MiniDump. Я поставил больше деталей в исходное сообщение.

Спасибо!

0

Вы все прекрасно. Когда вы открываете минидампу созданную вами, после того, как вы загрузите символы, сделайте

.ecxr 

первым, чтобы установить контекст для того, что вы сохранили в ExceptionInformation параметре MiniDumpWriteDump(). Тогда у вас будет законная трассировка стека.

Мы используем аналогичный механизм генерации дампа в том месте, где я работаю.

Есть некоторые будущие ошибки. Вы хотите проверить, срабатывает ли ваш механизм захвата дам на таких вещах, как вызов abort().

Для этого ознакомьтесь с _set_invalid_parameter_handler() и signal(SIGABRT, ...).

+0

Итак, это намного ближе; он делает интерпретацию windbgs ближе к отладчику VS, но он не соответствует живому отладку, который я получаю от привязки windbg до процесса. Добавление новых данных в сообщение выше. – Kmus

+0

Хе-хе, я почти забыл: Спасибо за помощь. – Kmus