1 /*
2  * PROJECT:     Dr. Watson crash reporter
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Print a stacktrace
5  * COPYRIGHT:   Copyright 2017,2018 Mark Jansen <mark.jansen@reactos.org>
6  */
7 
8 #include "precomp.h"
9 #include <dbghelp.h>
10 
11 #define STACK_MAX_RECURSION_DEPTH   50
12 
13 
BeginStackBacktrace(DumpData & data)14 void BeginStackBacktrace(DumpData& data)
15 {
16     DWORD symOptions = SymGetOptions();
17     symOptions |= SYMOPT_UNDNAME | SYMOPT_AUTO_PUBLICS | SYMOPT_DEFERRED_LOADS;
18     SymSetOptions(symOptions);
19     SymInitialize(data.ProcessHandle, NULL, TRUE);
20 }
21 
EndStackBacktrace(DumpData & data)22 void EndStackBacktrace(DumpData& data)
23 {
24     SymCleanup(data.ProcessHandle);
25 }
26 
ToChar(UCHAR data)27 static char ToChar(UCHAR data)
28 {
29     if (data < 0xa)
30         return '0' + data;
31     else if (data <= 0xf)
32         return 'a' + data - 0xa;
33     return '?';
34 }
35 
PrintStackBacktrace(FILE * output,DumpData & data,ThreadData & thread)36 void PrintStackBacktrace(FILE* output, DumpData& data, ThreadData& thread)
37 {
38     DWORD MachineType;
39     STACKFRAME64 StackFrame = { { 0 } };
40 
41     StackFrame.AddrPC.Mode = AddrModeFlat;
42     StackFrame.AddrReturn.Mode = AddrModeFlat;
43     StackFrame.AddrFrame.Mode = AddrModeFlat;
44     StackFrame.AddrStack.Mode = AddrModeFlat;
45     StackFrame.AddrBStore.Mode = AddrModeFlat;
46 
47 
48 #if defined(_M_IX86)
49     MachineType = IMAGE_FILE_MACHINE_I386;
50     StackFrame.AddrPC.Offset = thread.Context.Eip;
51     StackFrame.AddrStack.Offset = thread.Context.Esp;
52     StackFrame.AddrFrame.Offset = thread.Context.Ebp;
53 #elif defined(_M_AMD64)
54     MachineType = IMAGE_FILE_MACHINE_AMD64;
55     StackFrame.AddrPC.Offset = thread.Context.Rip;
56     StackFrame.AddrStack.Offset = thread.Context.Rsp;
57     StackFrame.AddrFrame.Offset = thread.Context.Rbp;
58 #elif defined(_M_ARM)
59     MachineType = IMAGE_FILE_MACHINE_ARMNT;
60     StackFrame.AddrPC.Offset = thread.Context.Pc;
61     StackFrame.AddrStack.Offset = thread.Context.Sp;
62     StackFrame.AddrFrame.Offset = thread.Context.R11;
63 #elif defined(_M_ARM64)
64     MachineType = IMAGE_FILE_MACHINE_ARM64;
65     StackFrame.AddrPC.Offset = thread.Context.Pc;
66     StackFrame.AddrStack.Offset = thread.Context.Sp;
67     StackFrame.AddrFrame.Offset = thread.Context.u.s.Fp;
68 #else
69 #error "Unknown architecture"
70 #endif
71 
72 #define STACKWALK_MAX_NAMELEN   512
73     char buf[sizeof(SYMBOL_INFO) + STACKWALK_MAX_NAMELEN] = {0};
74     SYMBOL_INFO* sym = (SYMBOL_INFO *)buf;
75     IMAGEHLP_MODULE64 Module = { 0 };
76     LONG RecursionDepth = 0;
77     sym->SizeOfStruct = sizeof(sym);
78 
79     /* FIXME: dump x bytes at PC here + disasm it! */
80 
81     xfprintf(output, NEWLINE "*----> Stack Back Trace <----*" NEWLINE NEWLINE);
82     bool first = true;
83     while (StackWalk64(MachineType, data.ProcessHandle, thread.Handle, &StackFrame, &thread.Context,
84                        NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
85     {
86         if (StackFrame.AddrPC.Offset == StackFrame.AddrReturn.Offset)
87         {
88             if (RecursionDepth++ > STACK_MAX_RECURSION_DEPTH)
89             {
90                 xfprintf(output, "- Aborting stackwalk -" NEWLINE);
91                 break;
92             }
93         }
94         else
95         {
96             RecursionDepth = 0;
97         }
98 
99         if (first)
100         {
101             xfprintf(output, "FramePtr ReturnAd Param#1  Param#2  Param#3  Param#4  Function Name" NEWLINE);
102             first = false;
103         }
104 
105         Module.SizeOfStruct = sizeof(Module);
106         DWORD64 ModBase = SymGetModuleBase64(data.ProcessHandle, StackFrame.AddrPC.Offset);
107         if (!ModBase || !SymGetModuleInfo64(data.ProcessHandle, ModBase, &Module))
108             strcpy(Module.ModuleName, "<nomod>");
109 
110         memset(sym, '\0', sizeof(*sym) + STACKWALK_MAX_NAMELEN);
111         sym->SizeOfStruct = sizeof(*sym);
112         sym->MaxNameLen = STACKWALK_MAX_NAMELEN;
113         DWORD64 displacement = 0;
114 
115         if (!StackFrame.AddrPC.Offset || !SymFromAddr(data.ProcessHandle, StackFrame.AddrPC.Offset, &displacement, sym))
116             strcpy(sym->Name, "<nosymbols>");
117 
118         xfprintf(output, "%p %p %p %p %p %p %s!%s +0x%I64x" NEWLINE,
119                  (ULONG_PTR)StackFrame.AddrFrame.Offset, (ULONG_PTR)StackFrame.AddrPC.Offset,
120                  (ULONG_PTR)StackFrame.Params[0], (ULONG_PTR)StackFrame.Params[1],
121                  (ULONG_PTR)StackFrame.Params[2], (ULONG_PTR)StackFrame.Params[3],
122                  Module.ModuleName, sym->Name, displacement);
123     }
124 
125     UCHAR stackData[0x10 * 10];
126     SIZE_T sizeRead;
127 #if defined(_M_IX86)
128     ULONG_PTR stackPointer = thread.Context.Esp;
129 #elif defined(_M_AMD64)
130     ULONG_PTR stackPointer = thread.Context.Rsp;
131 #elif defined(_M_ARM) || defined(_M_ARM64)
132     ULONG_PTR stackPointer = thread.Context.Sp;
133 #else
134 #error Unknown architecture
135 #endif
136     if (!ReadProcessMemory(data.ProcessHandle, (PVOID)stackPointer, stackData, sizeof(stackData), &sizeRead))
137         return;
138 
139     xfprintf(output, NEWLINE "*----> Raw Stack Dump <----*" NEWLINE NEWLINE);
140     for (size_t n = 0; n < sizeof(stackData); n += 0x10)
141     {
142         char HexData1[] = "?? ?? ?? ?? ?? ?? ?? ??";
143         char HexData2[] = "?? ?? ?? ?? ?? ?? ?? ??";
144         char AsciiData1[] = "????????";
145         char AsciiData2[] = "????????";
146 
147         for (size_t j = 0; j < 8; ++j)
148         {
149             size_t idx = j + n;
150             if (idx < sizeRead)
151             {
152                 HexData1[j * 3] = ToChar(stackData[idx] >> 4);
153                 HexData1[j * 3 + 1] = ToChar(stackData[idx] & 0xf);
154                 AsciiData1[j] = isprint(stackData[idx]) ? stackData[idx] : '.';
155             }
156             idx += 8;
157             if (idx < sizeRead)
158             {
159                 HexData2[j * 3] = ToChar(stackData[idx] >> 4);
160                 HexData2[j * 3 + 1] = ToChar(stackData[idx] & 0xf);
161                 AsciiData2[j] = isprint(stackData[idx]) ? stackData[idx] : '.';
162             }
163         }
164 
165         xfprintf(output, "%p %s - %s  %s%s" NEWLINE, stackPointer+n, HexData1, HexData2, AsciiData1, AsciiData2);
166     }
167 }
168