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 
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 
22 void EndStackBacktrace(DumpData& data)
23 {
24     SymCleanup(data.ProcessHandle);
25 }
26 
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 
36 void PrintStackBacktrace(FILE* output, DumpData& data, ThreadData& thread)
37 {
38     DWORD MachineType;
39     STACKFRAME64 StackFrame = { { 0 } };
40 
41 #ifdef _M_X64
42     MachineType = IMAGE_FILE_MACHINE_AMD64;
43     StackFrame.AddrPC.Offset = thread.Context.Rip;
44     StackFrame.AddrPC.Mode = AddrModeFlat;
45     StackFrame.AddrStack.Offset = thread.Context.Rsp;
46     StackFrame.AddrStack.Mode = AddrModeFlat;
47     StackFrame.AddrFrame.Offset = thread.Context.Rbp;
48     StackFrame.AddrFrame.Mode = AddrModeFlat;
49 #else
50     MachineType = IMAGE_FILE_MACHINE_I386;
51     StackFrame.AddrPC.Offset =  thread.Context.Eip;
52     StackFrame.AddrPC.Mode = AddrModeFlat;
53     StackFrame.AddrStack.Offset = thread.Context.Esp;
54     StackFrame.AddrStack.Mode = AddrModeFlat;
55     StackFrame.AddrFrame.Offset = thread.Context.Ebp;
56     StackFrame.AddrFrame.Mode = AddrModeFlat;
57 #endif
58 
59 
60 #define STACKWALK_MAX_NAMELEN   512
61     char buf[sizeof(SYMBOL_INFO) + STACKWALK_MAX_NAMELEN] = {0};
62     SYMBOL_INFO* sym = (SYMBOL_INFO *)buf;
63     IMAGEHLP_MODULE64 Module = { 0 };
64     LONG RecursionDepth = 0;
65     sym->SizeOfStruct = sizeof(sym);
66 
67     /* FIXME: dump x bytes at EIP here + disasm it! */
68 
69     xfprintf(output, NEWLINE "*----> Stack Back Trace <----*" NEWLINE NEWLINE);
70     bool first = true;
71     while (StackWalk64(MachineType, data.ProcessHandle, thread.Handle, &StackFrame, &thread.Context,
72                        NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
73     {
74         if (StackFrame.AddrPC.Offset == StackFrame.AddrReturn.Offset)
75         {
76             if (RecursionDepth++ > STACK_MAX_RECURSION_DEPTH)
77             {
78                 xfprintf(output, "- Aborting stackwalk -" NEWLINE);
79                 break;
80             }
81         }
82         else
83         {
84             RecursionDepth = 0;
85         }
86 
87         if (first)
88         {
89             xfprintf(output, "FramePtr ReturnAd Param#1  Param#2  Param#3  Param#4  Function Name" NEWLINE);
90             first = false;
91         }
92 
93         Module.SizeOfStruct = sizeof(Module);
94         DWORD64 ModBase = SymGetModuleBase64(data.ProcessHandle, StackFrame.AddrPC.Offset);
95         if (!ModBase || !SymGetModuleInfo64(data.ProcessHandle, ModBase, &Module))
96             strcpy(Module.ModuleName, "<nomod>");
97 
98         memset(sym, '\0', sizeof(*sym) + STACKWALK_MAX_NAMELEN);
99         sym->SizeOfStruct = sizeof(*sym);
100         sym->MaxNameLen = STACKWALK_MAX_NAMELEN;
101         DWORD64 displacement = 0;
102 
103         if (!StackFrame.AddrPC.Offset || !SymFromAddr(data.ProcessHandle, StackFrame.AddrPC.Offset, &displacement, sym))
104             strcpy(sym->Name, "<nosymbols>");
105 
106         xfprintf(output, "%p %p %p %p %p %p %s!%s +0x%I64x" NEWLINE,
107                  (ULONG_PTR)StackFrame.AddrFrame.Offset, (ULONG_PTR)StackFrame.AddrPC.Offset,
108                  (ULONG_PTR)StackFrame.Params[0], (ULONG_PTR)StackFrame.Params[1],
109                  (ULONG_PTR)StackFrame.Params[2], (ULONG_PTR)StackFrame.Params[3],
110                  Module.ModuleName, sym->Name, displacement);
111     }
112 
113     UCHAR stackData[0x10 * 10];
114     SIZE_T sizeRead;
115 #if defined(_M_IX86)
116     ULONG_PTR stackPointer = thread.Context.Esp;
117 #elif defined(_M_AMD64)
118     ULONG_PTR stackPointer = thread.Context.Rsp;
119 #else
120 #error Unknown architecture
121 #endif
122     if (!ReadProcessMemory(data.ProcessHandle, (PVOID)stackPointer, stackData, sizeof(stackData), &sizeRead))
123         return;
124 
125     xfprintf(output, NEWLINE "*----> Raw Stack Dump <----*" NEWLINE NEWLINE);
126     for (size_t n = 0; n < sizeof(stackData); n += 0x10)
127     {
128         char HexData1[] = "?? ?? ?? ?? ?? ?? ?? ??";
129         char HexData2[] = "?? ?? ?? ?? ?? ?? ?? ??";
130         char AsciiData1[] = "????????";
131         char AsciiData2[] = "????????";
132 
133         for (size_t j = 0; j < 8; ++j)
134         {
135             size_t idx = j + n;
136             if (idx < sizeRead)
137             {
138                 HexData1[j * 3] = ToChar(stackData[idx] >> 4);
139                 HexData1[j * 3 + 1] = ToChar(stackData[idx] & 0xf);
140                 AsciiData1[j] = isprint(stackData[idx]) ? stackData[idx] : '.';
141             }
142             idx += 8;
143             if (idx < sizeRead)
144             {
145                 HexData2[j * 3] = ToChar(stackData[idx] >> 4);
146                 HexData2[j * 3 + 1] = ToChar(stackData[idx] & 0xf);
147                 AsciiData2[j] = isprint(stackData[idx]) ? stackData[idx] : '.';
148             }
149         }
150 
151         xfprintf(output, "%p %s - %s  %s%s" NEWLINE, stackPointer+n, HexData1, HexData2, AsciiData1, AsciiData2);
152     }
153 }
154