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