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