CppCommon  1.0.4.1
C++ Common Library
stack_trace.cpp
Go to the documentation of this file.
1 
9 // Workaround for the "binutils/bfd.h wants config.h now?" issue
10 // https://stackoverflow.com/questions/11748035/binutils-bfd-h-wants-config-h-now
11 #define PACKAGE "CppCommon"
12 #define PACKAGE_VERSION "1.0.0.0"
13 
14 #include "system/stack_trace.h"
15 
17 #include "utility/countof.h"
18 
19 #include <cstring>
20 #include <iomanip>
21 #include <sstream>
22 
23 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
24 #include <execinfo.h>
25 #if defined(LIBBFD_SUPPORT)
26 #include <bfd.h>
27 #endif
28 #if defined(LIBDL_SUPPORT)
29 #include <cxxabi.h>
30 #include <dlfcn.h>
31 #endif
32 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
33 #include <windows.h>
34 #if defined(DBGHELP_SUPPORT)
35 #if defined(_MSC_VER)
36 #pragma warning(push)
37 #pragma warning(disable:4091) // C4091: 'keyword' : ignored on left of 'type' when no variable is declared
38 #endif
39 #include <dbghelp.h>
40 #if defined(_MSC_VER)
41 #pragma warning(pop)
42 #endif
43 #endif
44 #endif
45 
46 namespace CppCommon {
47 
48 std::ostream& operator<<(std::ostream& os, const StackTrace::Frame& frame)
49 {
50  // Format stack trace frame address
51  std::ios_base::fmtflags flags = os.flags();
52  os << "0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(2 * sizeof(uintptr_t)) << (uintptr_t)frame.address << ": ";
53  os.flags(flags);
54  // Format stack trace frame other fields
55  os << (frame.module.empty() ? "<unknown>" : frame.module) << '!';
56  os << (frame.function.empty() ? "??" : frame.function) << ' ';
57  os << frame.filename;
58  if (frame.line > 0)
59  os << '(' << frame.line << ')';
60  return os;
61 }
62 
64 {
65 #if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
66  const int capacity = 1024;
67  void* frames[capacity];
68 
69  // Capture the current stack trace
70  int captured = backtrace(frames, capacity);
71  int index = skip + 1;
72  int size = captured - index;
73 
74  // Check the current stack trace size
75  if (size <= 0)
76  return;
77 
78  // Resize stack trace frames vector
79  _frames.resize(size);
80 
81  // Capture stack trace snapshot under the critical section
82  static CriticalSection cs;
83  Locker<CriticalSection> locker(cs);
84 
85  // Fill all captured frames with symbol information
86  for (int i = 0; i < size; ++i)
87  {
88  auto& frame = _frames[i];
89 
90  // Get the frame address
91  frame.address = frames[index + i];
92 
93 #if defined(LIBDL_SUPPORT)
94  // Get the frame information
95  Dl_info info;
96  if (dladdr(frames[index + i], &info) == 0)
97  continue;
98 
99  // Get the frame module
100  if (info.dli_fname != nullptr)
101  {
102  const char* module = std::strrchr(info.dli_fname, '/');
103  if (module != nullptr)
104  frame.module = module + 1;
105  }
106 
107  // Get the frame function
108  if (info.dli_sname != nullptr)
109  {
110  // Demangle symbol name if need
111  int status;
112  char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status);
113  if ((status == 0) && (demangled != nullptr))
114  {
115  frame.function = demangled;
116  free(demangled);
117  }
118  else
119  frame.function = info.dli_sname;
120  }
121 #endif
122 #if defined(LIBBFD_SUPPORT)
123  bfd* abfd = nullptr;
124  char** matching = nullptr;
125 
126  void* symsptr = nullptr;
127  asymbol** syms = nullptr;
128  unsigned int symsize;
129  long symcount;
130 
131  const char* filename = nullptr;
132  const char* functionname = nullptr;
133  unsigned int line;
134 
135  bfd_boolean found = false;
136  bfd_vma pc;
137 
138  if ((frame.address == nullptr) || (info.dli_fname == nullptr))
139  continue;
140 
141  abfd = bfd_openr(info.dli_fname, nullptr);
142  if (abfd == nullptr)
143  continue;
144 
145  if (bfd_check_format(abfd, bfd_archive))
146  goto cleanup;
147 
148  if (!bfd_check_format_matches(abfd, bfd_object, &matching))
149  goto cleanup;
150 
151  if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
152  goto cleanup;
153 
154  symcount = bfd_read_minisymbols(abfd, FALSE, &symsptr, &symsize);
155  if (symcount == 0)
156  symcount = bfd_read_minisymbols(abfd, TRUE, &symsptr, &symsize);
157  if (symcount < 0)
158  goto cleanup;
159  syms = (asymbol**)symsptr;
160 
161  pc = (bfd_vma)frame.address;
162  for (asection* section = abfd->sections; section != nullptr; section = section->next)
163  {
164  if (found)
165  break;
166 
167  if ((bfd_section_flags(section) & SEC_ALLOC) == 0)
168  continue;
169 
170  bfd_vma vma = bfd_section_vma(section);
171  if (pc < vma)
172  continue;
173 
174  bfd_size_type secsize = bfd_section_size(section);
175  if (pc >= vma + secsize)
176  continue;
177 
178  found = bfd_find_nearest_line(abfd, section, syms, pc - vma, &filename, &functionname, &line);
179  }
180 
181  if (!found)
182  goto cleanup;
183 
184  if (filename != nullptr)
185  frame.filename = filename;
186  frame.line = line;
187 
188 cleanup:
189  if (symsptr != nullptr)
190  free(symsptr);
191 
192  if (abfd != nullptr)
193  bfd_close(abfd);
194 #endif
195  }
196 #elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
197  const int capacity = 1024;
198  void* frames[capacity];
199 
200  // Capture the current stack trace
201  USHORT captured = CaptureStackBackTrace(skip + 1, capacity, frames, nullptr);
202 
203  // Resize stack trace frames vector
204  _frames.resize(captured);
205 
206  // Capture stack trace snapshot under the critical section
207  static CriticalSection cs;
208  Locker<CriticalSection> locker(cs);
209 
210  // Fill all captured frames with symbol information
211  for (int i = 0; i < captured; ++i)
212  {
213  auto& frame = _frames[i];
214 
215  // Get the frame address
216  frame.address = frames[i];
217 
218 #if defined(DBGHELP_SUPPORT)
219  // Get the current process handle
220  HANDLE hProcess = GetCurrentProcess();
221 
222  // Get the frame module
223  IMAGEHLP_MODULE64 module;
224  ZeroMemory(&module, sizeof(module));
225  module.SizeOfStruct = sizeof(module);
226  if (SymGetModuleInfo64(hProcess, (DWORD64)frame.address, &module))
227  {
228  const char* image = std::strrchr(module.ImageName, '\\');
229  if (image != nullptr)
230  frame.module = image + 1;
231  }
232 
233  // Get the frame function
234  char symbol[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
235  ZeroMemory(&symbol, countof(symbol));
236  PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbol;
237  pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
238  pSymbol->MaxNameLen = MAX_SYM_NAME;
239  if (SymFromAddr(hProcess, (DWORD64)frame.address, nullptr, pSymbol))
240  {
241  char buffer[4096];
242  if (UnDecorateSymbolName(pSymbol->Name, buffer, (DWORD)countof(buffer), UNDNAME_NAME_ONLY) > 0)
243  frame.function = buffer;
244  }
245 
246  // Get the frame file name and line number
247  DWORD offset = 0;
248  IMAGEHLP_LINE64 line;
249  ZeroMemory(&line, sizeof(line));
250  line.SizeOfStruct = sizeof(line);
251  if (SymGetLineFromAddr64(hProcess, (DWORD64)frame.address, &offset, &line))
252  {
253  if (line.FileName != nullptr)
254  frame.filename = line.FileName;
255  frame.line = line.LineNumber;
256  }
257 #endif
258  }
259 #endif
260 }
261 
262 std::ostream& operator<<(std::ostream& os, const StackTrace& stack_trace)
263 {
264  for (const auto& frame : stack_trace.frames())
265  os << frame << std::endl;
266  return os;
267 }
268 
269 } // namespace CppCommon
Critical section synchronization primitive.
Locker synchronization primitive.
Definition: locker.h:23
Stack trace snapshot provider.
Definition: stack_trace.h:33
StackTrace(int skip=0)
Capture the current stack trace snapshot.
Definition: stack_trace.cpp:63
const std::vector< Frame > & frames() const noexcept
Get stack trace frames.
Definition: stack_trace.h:66
Static array countof definition.
Critical section synchronization primitive definition.
C++ Common project definitions.
Definition: token_bucket.h:15
std::ostream & operator<<(std::ostream &os, const uint128_t &value)
Definition: uint128.inl:155
constexpr size_t countof(const T(&)[N]) noexcept
Count of elements in static array.
Definition: countof.h:16
Stack trace snapshot provider definition.
Stack trace frame.
Definition: stack_trace.h:37
std::string filename
Frame file name.
Definition: stack_trace.h:41
std::string function
Frame function.
Definition: stack_trace.h:40
int line
Frame line number.
Definition: stack_trace.h:42
std::string module
Frame module.
Definition: stack_trace.h:39
void * address
Frame address.
Definition: stack_trace.h:38