2016-02-13 5 views
0

J'essaie de générer une trace de pile par programmation. Quand mes utilisateurs ont un crash, en particulier un accident, il est difficile de leur parler à travers le processus d'obtenir un vidage afin que je puisse résoudre le problème. Dans le passé, une fois qu'ils m'enverraient la trace, je croisais les adresses dans le fichier Intermediate/foo.map pour déterminer quelle fonction était le problème (est-ce le meilleur moyen?)Comment obtenir une pile-trace significative en utilisant MiniDumpWriteDump

J'ai construit une bibliothèque de divers exemples j'ai trouvé autour du filet, pour produire un minidump pour faciliter mon travail. J'ai mis en scène un crash, mais la trace de pile que je reçois du fichier minidump est très différente d'une trace de pile en direct que j'obtiens en attachant windbg. Des exemples de deux ci-dessous:

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 est pas à moi, et je n'ai pas source/symboles. Le Main.dll est injecté dans game.exe et fournit des fonctionnalités frontales pour charger des DLL supplémentaires depuis le jeu. Le code de débogage et le plantage par étapes se trouvent dans Minidump.dll. Après que Main.dll charge Minidump, il appelle AfterLoad(), qui définit le filtre d'exception, puis déclenche le blocage. Le code minidump pertinent est ci-dessous:

Lorsque j'ai ouvert le MiniDump.dmp je l'ai pointé vers tous mes fichiers de symboles (à l'exception de game.exe, que je n'ai pas) et cette partie semble fonctionner . Je le pointe vers le binaire game.exe depuis que j'ai ça. La trace de pile que j'en retire n'est vraiment pas utile. Mon but ultime est que l'utilisateur puisse simplement charger la DLL, provoquer le plantage et m'envoyer le fichier de vidage par courrier électronique. Ensuite, je vais attacher les fichiers de symboles et les binaires et être en mesure de diagnostiquer le problème pour eux. Est-ce que je fais quelque chose de mal, ou est-ce que ce n'est pas possible d'obtenir ce que je veux?

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(); 
} 

J'ai essayé de couper une partie de la graisse de tous les détails pour simplifier le problème, mais je peux être plus explicite si nécessaire. J'ai trouvé le bon article sur CodeProject, et j'ai essayé de trouver plus d'informations de base à lire pour m'aider à comprendre le problème, mais ce que j'ai pu trouver ne m'a pas aidé à comprendre qu'ils étaient juste étape par étape pour l'obtenir en cours d'exécution (ce qui est déjà fait). Quelqu'un at-il une idée de ce que je fais de mal, ou peut-être me diriger vers une lecture pertinente?


Après la suggestion de Sergei j'ai fait .ecxr en windbg et a obtenu un meilleur rendement, mais il ne correspond toujours pas la trace que je reçois quand je droit accroche windbg jusqu'à processus et déclencher l'accident. Voici la trace minidump;

*** 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 

et la trace d'attacher le débogueur au processus

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 

Je ne suis pas la source de game.exe (je l'ai pour les DLL qui est l'endroit où l'erreur est), mais j'ai décompilé game.exe et voici ce qui est au jeu + 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 

Mon erreur que je suis le déclenchement est en Minidump.dll, mais ce code en haut de la pile est en game.exe. Il pourrait y avoir beaucoup de choses à l'intérieur du game.exe que je ne connais pas, pourrait-il être détournant l'erreur que je déclenche en quelque sorte? I.E., je déclenche l'erreur dans la DLL, mais quelque chose d'installation dans le jeu.exe capture le flux du programme avant que le filtre d'exception qui écrit le minidump est appelé? Si c'est le cas, lorsque j'attache le débogueur au processus, déclenche l'erreur et obtiens la bonne sortie qui indique que l'erreur est dans ma DLL, cela signifie que game.exe ne capture pas le flux du programme avant le débogueur peut faire la trace. Comment pourrais-je faire en sorte que mon code minidump se comporte de la même façon ... C'est entrer dans un territoire avec lequel je ne suis pas très familier. Des idées?


Je pourchassé plus en arrière, et la fonction d'appel que l'on, a cette ligne en elle:

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

Donc, je pense que game.exe est Détournement l'exception de le faire de son propre décharge avant mon le code essaie d'obtenir le vidage. Et puis ma trace de décharges est juste une trace du processus de vidage de game.exe ...


Réponse

Je l'ai compris. Je ne suis pas sûr de savoir comment répondre à mon propre message, alors voici l'affaire.

.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 

Ceci est issu de game.exe. Il se trouve que game.exe fait son propre minidump. Mon minidump se déclenchait après le leur, alors ce que je voyais dans ma trace de pile était une trace de leur processus de vidage. J'ai trouvé un fichier dmp dans le répertoire d'installation du jeu et une fois que j'ai chargé mes symboles, il montrait la sortie correcte que je recherchais.

Répondre

0

Je l'ai compris. Fondamentalement game.exe avait son propre code MiniDumpWriteDump qui se déclenchait avant mon code. Donc la trace de la pile que je recevais n'était pas une trace de l'erreur, c'était une trace de game.exe qui faisait sa propre MiniDump. J'ai mis plus de détails dans le message original.

Merci!

0

Vous vous débrouillez bien. Lorsque vous ouvrez le minidump que vous avez généré, une fois que vous chargez les symboles, ne

.ecxr 

premier à mettre en contexte ce que vous avez enregistré dans le paramètre ExceptionInformation à MiniDumpWriteDump(). Ensuite, vous aurez une trace de pile légitime. Nous utilisons un mécanisme de génération de vidage similaire à l'endroit où je travaille.

il y a quelques futurs gotchas cependant. Vous voulez vérifier si votre mécanisme de capture est déclenché sur des éléments tels qu'un appel abort(). Pour cela, consultez _set_invalid_parameter_handler() et signal(SIGABRT, ...).

+0

Donc, c'est beaucoup plus proche; il fait en sorte que l'interprétation des windbgs ressemble plus au débogueur VS, mais elle ne correspond pas au débogage en direct que j'obtiens en accroissant le windbg directement au processus. Ajout des nouveaux détails au post ci-dessus. – Kmus

+0

Hehe, j'ai presque oublié: Merci d'avoir aidé. – Kmus