CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
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
46namespace CppCommon {
47
48std::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
188cleanup:
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
262std::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
const std::vector< Frame > & frames() const noexcept
Get stack trace frames.
Definition stack_trace.h:66
StackTrace(int skip=0)
Capture the current stack trace snapshot.
Static array countof definition.
Critical section synchronization primitive definition.
C++ Common project definitions.
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.
std::string filename
Frame file name.
Definition stack_trace.h:41
std::string std::string function
< Frame module
Definition stack_trace.h:40
int line
Frame line number.
Definition stack_trace.h:42
void * address
Frame address.
Definition stack_trace.h:38