tan  0.0.1
tan_backtrace.cpp
1 #include "backtrace/tan_backtrace.h"
2 #include <cstdlib>
3 
4 #ifdef _MSC_VER
5 #include <windows.h>
6 #include <intrin.h>
7 #include <dbghelp.h>
8 #include <string>
9 #include <vector>
10 #include <iostream>
11 #pragma comment(lib, "dbghelp.lib")
12 
13 struct StackFrame {
14  std::string name;
15  unsigned int line;
16  std::string file;
17 };
18 
19 bool init_back_trace(const char *) { return true; }
20 
21 inline std::vector<StackFrame> stack_trace() {
22  using std::cerr;
23 #if _WIN64
24  DWORD machine = IMAGE_FILE_MACHINE_AMD64;
25 #else
26  DWORD machine = IMAGE_FILE_MACHINE_I386;
27 #endif
28  HANDLE process = GetCurrentProcess();
29  HANDLE thread = GetCurrentThread();
30 
31  if (SymInitialize(process, nullptr, TRUE) == FALSE) {
32  cerr << "Failed to call SymInitialize\n";
33  return std::vector<StackFrame>();
34  }
35 
36  SymSetOptions(SYMOPT_LOAD_LINES);
37 
38  CONTEXT context = {};
39  context.ContextFlags = CONTEXT_FULL;
40  RtlCaptureContext(&context);
41 
42 #if _WIN64
43  STACKFRAME frame = {};
44  frame.AddrPC.Offset = context.Rip;
45  frame.AddrPC.Mode = AddrModeFlat;
46  frame.AddrFrame.Offset = context.Rbp;
47  frame.AddrFrame.Mode = AddrModeFlat;
48  frame.AddrStack.Offset = context.Rsp;
49  frame.AddrStack.Mode = AddrModeFlat;
50 #else
51  STACKFRAME frame = {};
52  frame.AddrPC.Offset = context.Eip;
53  frame.AddrPC.Mode = AddrModeFlat;
54  frame.AddrFrame.Offset = context.Ebp;
55  frame.AddrFrame.Mode = AddrModeFlat;
56  frame.AddrStack.Offset = context.Esp;
57  frame.AddrStack.Mode = AddrModeFlat;
58 #endif
59 
60  bool first = true;
61 
62  std::vector<StackFrame> frames;
63  while (StackWalk(
64  machine, process, thread, &frame, &context, nullptr, SymFunctionTableAccess, SymGetModuleBase, nullptr)) {
65  StackFrame f{};
66 
67 #if _WIN64
68  DWORD64 moduleBase = 0;
69 #else
70  DWORD moduleBase = 0;
71 #endif
72 
73  moduleBase = SymGetModuleBase(process, frame.AddrPC.Offset);
74 
75 #if _WIN64
76  DWORD64 offset = 0;
77 #else
78  DWORD offset = 0;
79 #endif
80  char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255]{0};
81  auto symbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
82  symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL) + 255;
83  symbol->MaxNameLength = 254;
84 
85  if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &offset, symbol)) {
86  f.name = symbol->Name;
87  } else {
88  DWORD error = GetLastError();
89  printf("Failed to resolve address 0x%llX: %lu\n", frame.AddrPC.Offset, error);
90  f.name = "Unknown Function";
91  }
92 
93  IMAGEHLP_LINE line;
94  line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
95 
96  DWORD offset_ln = 0;
97  if (SymGetLineFromAddr(process, frame.AddrPC.Offset, &offset_ln, &line)) {
98  f.file = line.FileName;
99  f.line = line.LineNumber;
100  } else {
101  DWORD error = GetLastError();
102  printf("Failed to resolve line for 0x%llX%lu\n", frame.AddrPC.Offset, error);
103  f.line = 0;
104  }
105 
106  if (!first) {
107  frames.push_back(f);
108  }
109  first = false;
110  }
111 
112  SymCleanup(process);
113  return frames;
114 }
115 
116 void print_back_trace() {
117  std::vector<StackFrame> stack = stack_trace();
118  for (auto &i : stack) {
119  printf("%s:%u in function %s\n", i.file.c_str(), i.line, i.name.c_str());
120  }
121 }
122 #else
123 #include <backtrace.h>
124 #include <cxxabi.h>
125 
126 void *__bt_state = nullptr;
127 
128 int bt_callback(void *, uintptr_t, const char *filename, int lineno, const char *function) {
129  /// demangle function name
130  const char *func_name = function;
131  int status;
132  char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status);
133  if (status == 0) {
134  func_name = demangled;
135  }
136 
137  if (filename && func_name) {
138  printf("%s:%d in function %s\n", filename, lineno, func_name);
139  }
140  return 0;
141 }
142 
143 void bt_error_callback(void *, const char *msg, int errnum) {
144  printf("Error %d occurred when getting the stacktrace: %s", errnum, msg);
145 }
146 
147 void bt_error_callback_create(void *data, const char *msg, int errnum) {
148  printf("Error %d occurred when initializing the stacktrace: %s", errnum, msg);
149  bool *status = (bool *)data;
150  *status = false;
151 }
152 
153 bool init_back_trace(const char *filename) {
154  bool status = true;
155  __bt_state = backtrace_create_state(filename, 0, bt_error_callback_create, (void *)status);
156  return status;
157 }
158 
159 void print_back_trace() {
160  if (!__bt_state) { /// make sure init_back_trace() is called
161  printf("Make sure init_back_trace() is called before calling print_stack_trace()\n");
162  abort();
163  }
164  backtrace_full((backtrace_state *)__bt_state, 0, bt_callback, bt_error_callback, nullptr);
165 }
166 
167 #endif