Я пытаюсь программно генерировать трассировку стека. Когда у моих пользователей произошел сбой, в частности случайный, трудно обсуждать их через процесс получения дампа, чтобы я мог решить проблему. В прошлом, как только они отправили бы мне трассировку, я бы перекрестно ссылался на адреса в нем в файл 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 в каталоге установки игры, и как только я загрузил в него свои символы, он показал правильный результат, который я получил после.
Итак, это намного ближе; он делает интерпретацию windbgs ближе к отладчику VS, но он не соответствует живому отладку, который я получаю от привязки windbg до процесса. Добавление новых данных в сообщение выше. – Kmus
Хе-хе, я почти забыл: Спасибо за помощь. – Kmus