Add verbatim stack.{cpp,hpp} from mr-edd.co.uk hg repo.
[dcpomatic.git] / src / lib / stack.cpp
1 // Copyright 2007 Edd Dawson.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5
6 #include <cassert>
7 #include <cstring>
8 #include <cstdlib>
9 #include <iomanip>
10 #include <ostream>
11 #include <stdexcept>
12 #include <sstream>
13
14 #include "dbg/stack.hpp"
15
16 #if defined(_WIN32)
17 #   include <windows.h>
18 #   include <imagehlp.h>
19
20 #   if defined(__MINGW32__)
21 #       include <bfd.h> // link against libbfd and libiberty
22 #       include <psapi.h> // link against psapi
23 #       include <cxxabi.h>
24 #   endif
25
26 #elif defined(__GNUC__)
27 #   include <dlfcn.h>
28 #   include <cxxabi.h>
29 #endif
30
31 namespace
32 {
33     const char * const unknown_function = "[unknown function]";
34     const char * const unknown_module = "[unknown module]";
35
36 #if defined(__GNUC__)
37     std::string demangle(const char *name)
38     {
39         if (!name)
40             return unknown_function;
41
42         int status = 0;
43         char *d = 0;
44         std::string ret = name;
45         try
46         {
47             if ((d = abi::__cxa_demangle(name, 0, 0, &status)))
48                 ret = d;
49         }
50         catch (const std::bad_alloc &) {  }
51
52         std::free(d);
53         return ret;
54     }
55 #endif
56
57 #if defined(_WIN32)
58
59     // Derive from this to disallow copying of your class.
60     // c.f. boost::noncopyable
61     class uncopyable
62     {
63         protected:
64             uncopyable() { }
65
66         private:
67             uncopyable(const uncopyable &);
68             uncopyable &operator= (const uncopyable &);
69     };
70
71 #if defined(__MINGW32__)
72
73     // Provides a means to translate a program counter offset in to the name of the corresponding function.
74     class bfd_context : uncopyable
75     {
76         private:
77             struct find_data
78             {
79                 std::string func;
80                 asymbol **symbol_table;
81                 bfd_vma counter;
82             };
83
84         public:
85             bfd_context() :
86                 abfd_(0),
87                 sec_(0),
88                 symbol_table_(0)
89             {
90                 char procname[MAX_PATH];
91                 GetModuleFileNameA(NULL, procname, sizeof procname);
92
93                 bfd_init();
94                 abfd_ = bfd_openr(procname, 0);
95                 if (!abfd_)
96                     throw std::runtime_error("Failed to parse object data for the executable");
97
98                 char **formats = 0;
99                 bool b1 = bfd_check_format(abfd_, bfd_object);
100                 bool b2 = bfd_check_format_matches(abfd_, bfd_object, &formats);
101                 bool b3 = bfd_get_file_flags(abfd_) & HAS_SYMS;
102
103                 if (!(b1 && b2 && b3))
104                 {
105                     bfd_close(abfd_);
106                     free(formats);
107                     throw std::runtime_error("Failed to parse object data for the executable");
108                 }
109                 free(formats);
110
111                 // Load symbol table
112                 unsigned dummy = 0;
113                 if (bfd_read_minisymbols(abfd_, FALSE, reinterpret_cast<void **>(&symbol_table_), &dummy) == 0 &&
114                     bfd_read_minisymbols(abfd_, TRUE, reinterpret_cast<void **>(&symbol_table_), &dummy) < 0)
115                 {
116                     free(symbol_table_);
117                     bfd_close(abfd_);
118                     throw std::runtime_error("Failed to parse object data for the executable");
119                 }
120             }
121
122             ~bfd_context()
123             {
124                 free(symbol_table_);
125                 bfd_close(abfd_);
126             }
127
128             std::string get_function_name(DWORD offset)
129             {
130                 find_data data;
131                 data.symbol_table = symbol_table_;
132                 data.counter = offset;
133
134                 bfd_map_over_sections(abfd_, &find_function_name_in_section, &data);
135
136                 return data.func;
137             }
138
139         private:
140             static void find_function_name_in_section(bfd *abfd, asection *sec, void *opaque_data)
141             {
142                 assert(sec);
143                 assert(opaque_data);
144                 find_data &data = *static_cast<find_data *>(opaque_data);
145
146                 if (!data.func.empty()) return; // already found it
147
148                 if (!(bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) return;
149
150                 bfd_vma vma = bfd_get_section_vma(abfd, sec);
151                 if (data.counter < vma || vma + bfd_get_section_size(sec) <= data.counter) return;
152
153                 const char *func = 0;
154                 const char *file = 0;
155                 unsigned line = 0;
156
157                 if (bfd_find_nearest_line(abfd, sec, data.symbol_table, data.counter - vma, &file, &func, &line) && func)
158                     data.func = demangle(func);
159             }
160
161         private:
162             bfd *abfd_;
163             asection *sec_;
164             asymbol **symbol_table_;
165     };
166
167 #endif // __MINGW32__
168
169     // g++ spouts warnings if you use {0} to initialize PODs. So we use this instead:
170     const struct
171     {
172         template<typename POD>
173         operator POD () const { POD p; std::memset(&p, 0, sizeof p); return p; }
174     }
175     empty_pod = { };
176
177     // Wraps a FARPROC. Implicitly convertible to any kind of pointer-to-function.
178     // Avoids having reinterpret casts all over the place.
179     struct auto_cast_function_ptr
180     {
181         auto_cast_function_ptr(FARPROC f) : fptr_(f) { }
182
183         template<typename FuncPtr>
184         operator FuncPtr() const { return reinterpret_cast<FuncPtr>(fptr_); }
185
186         FARPROC fptr_;
187     };
188
189     // A wrapper around a DLL. Can dynamically get function pointers with the function() function!
190     class windows_dll : uncopyable
191     {
192         public:
193             explicit windows_dll(const std::string &libname) :
194                 name_(libname),
195                 lib_(LoadLibrary(name_.c_str()))
196             {
197                 if (!lib_) throw std::runtime_error("Failed to load dll " + name_);
198             }
199
200             ~windows_dll() { FreeLibrary(lib_); }
201
202             const std::string &name() const { return name_; }
203
204             auto_cast_function_ptr function(const char *func_name) const
205             {
206                 FARPROC proc = GetProcAddress(lib_, func_name);
207                 if (!proc) throw std::runtime_error(std::string("failed to load function ") + func_name + " from library " + name_);
208
209                 return proc;
210             }
211
212         private:
213             std::string name_;
214             HMODULE lib_;
215     };
216
217     // An object that makes sure debugging symbols are available
218     class symbol_context : uncopyable
219     {
220         public:
221             symbol_context()
222             {
223                 if (!SymInitialize(GetCurrentProcess(), 0, TRUE))
224                     throw std::runtime_error("Failed to initialize symbol context");
225             }
226             ~symbol_context() { SymCleanup(GetCurrentProcess()); }
227     };
228
229     // A simple Windows mutex class. Use a lock object to lock the mutex for the duration of a scope.
230     class mutex : uncopyable
231     {
232         public:
233             mutex() { InitializeCriticalSection(&cs_); }
234             ~mutex() { DeleteCriticalSection(&cs_); }
235
236         private:
237             friend class lock;
238             void lock() { EnterCriticalSection(&cs_); }
239             void unlock() { LeaveCriticalSection(&cs_); }
240
241             CRITICAL_SECTION cs_;
242     }
243     g_fill_frames_mtx;
244
245     // A lock for the mutex
246     class lock : uncopyable
247     {
248         public:
249             lock(mutex &m) : m_(m) { m.lock(); }
250             ~lock() { m_.unlock(); }
251         private:
252             mutex &m_;
253     };
254
255
256     void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
257     {
258         lock lk(g_fill_frames_mtx);
259
260         symbol_context sc;
261 #ifdef __MINGW32__
262         bfd_context bfdc;
263 #endif
264
265         STACKFRAME frame = empty_pod;
266         CONTEXT context = empty_pod;
267         context.ContextFlags = CONTEXT_FULL;
268
269         windows_dll kernel32("kernel32.dll");
270         void (WINAPI *RtlCaptureContext_)(CONTEXT*) = kernel32.function("RtlCaptureContext");
271
272         RtlCaptureContext_(&context);
273
274 #if defined(_M_AMD64)
275         frame.AddrPC.Offset = context.Rip;
276         frame.AddrPC.Mode = AddrModeFlat;
277         frame.AddrStack.Offset = context.Rsp;
278         frame.AddrStack.Mode = AddrModeFlat;
279         frame.AddrFrame.Offset = context.Rbp;
280         frame.AddrFrame.Mode = AddrModeFlat;
281 #else
282         frame.AddrPC.Offset = context.Eip;
283         frame.AddrPC.Mode = AddrModeFlat;
284         frame.AddrStack.Offset = context.Esp;
285         frame.AddrStack.Mode = AddrModeFlat;
286         frame.AddrFrame.Offset = context.Ebp;
287         frame.AddrFrame.Mode = AddrModeFlat;
288 #endif
289
290         HANDLE process = GetCurrentProcess();
291         HANDLE thread = GetCurrentThread();
292
293         bool skip = true;
294         bool has_limit = limit != 0;
295         char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255];
296         char module_name_raw[MAX_PATH];
297
298 #if defined(_M_AMD64)
299         const DWORD machine = IMAGE_FILE_MACHINE_AMD64;
300 #else
301         const DWORD machine = IMAGE_FILE_MACHINE_I386;
302 #endif
303
304         while(StackWalk(machine, process, thread, &frame, &context, 0, SymFunctionTableAccess, SymGetModuleBase, 0))
305         {
306             if (skip)
307             {
308                 skip = false;
309                 continue;
310             }
311
312             if (has_limit && limit-- == 0) break;
313
314             IMAGEHLP_SYMBOL *symbol = reinterpret_cast<IMAGEHLP_SYMBOL *>(symbol_buffer);
315             symbol->SizeOfStruct = (sizeof *symbol) + 255;
316             symbol->MaxNameLength = 254;
317
318 #if defined(_WIN64)
319             DWORD64 module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
320 #else
321             DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
322 #endif
323             std::string module_name = unknown_module;
324             if (module_base && GetModuleFileNameA(reinterpret_cast<HINSTANCE>(module_base), module_name_raw, MAX_PATH))
325                 module_name = module_name_raw;
326
327 #if defined(__MINGW32__)
328                 std::string func = bfdc.get_function_name(frame.AddrPC.Offset);
329
330                 if (func.empty())
331                 {
332                     DWORD dummy = 0;
333                     BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol);
334                     func = got_symbol ? symbol->Name : unknown_function;
335                 }
336 #else
337                 DWORD dummy = 0;
338                 BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol);
339                 std::string func = got_symbol ? symbol->Name : unknown_function;
340 #endif
341
342             dbg::stack_frame f(reinterpret_cast<const void *>(frame.AddrPC.Offset), func, module_name);
343             frames.push_back(f);
344         }
345     }
346 #elif defined(__GNUC__)
347 #   if defined(__i386__) || defined(__amd64__)
348
349     void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
350     {
351         // Based on code found at:
352         // http://www.tlug.org.za/wiki/index.php/Obtaining_a_stack_trace_in_C_upon_SIGSEGV
353
354         Dl_info info;
355         void **frame = static_cast<void **>(__builtin_frame_address(0));
356         void **bp = static_cast<void **>(*frame);
357         void *ip = frame[1];
358
359         bool has_limit = limit != 0;
360         bool skip = true;
361
362         while(bp && ip && dladdr(ip, &info))
363         {
364             if (skip)
365                 skip = false;
366             else
367             {
368                 if (has_limit && limit-- == 0) break;
369                 frames.push_back(dbg::stack_frame(ip, demangle(info.dli_sname), info.dli_fname));
370
371                 if(info.dli_sname && !std::strcmp(info.dli_sname, "main")) break;
372             }
373
374             ip = bp[1];
375             bp = static_cast<void**>(bp[0]);
376         }
377     }
378
379 #   elif defined(__ppc__)
380
381     void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
382     {
383         // Based on code found at:
384         // http://www.informit.com/articles/article.aspx?p=606582&seqNum=4&rl=1
385
386         void *ip = __builtin_return_address(0);
387         void **frame = static_cast<void **>(__builtin_frame_address(1));
388         bool has_limit = limit != 0;
389         Dl_info info;
390
391         do
392         {
393             if (has_limit && limit-- == 0) break;
394
395             if (dladdr(ip, &info))
396                 frames.push_back(dbg::stack_frame(ip, demangle(info.dli_sname), info.dli_fname));
397
398             if (frame && (frame = static_cast<void**>(*frame))) ip = *(frame + 2);
399         }
400         while (frame && ip);
401     }
402
403 #   else
404         // GNU, but not x86, x64 nor PPC
405 #       error "Sorry but dbg::stack is not supported on this architecture"
406 #   endif
407 #else
408     // Unsupported compiler
409 #   error "Sorry but dbg::stack is not supported on this compiler"
410 #endif
411
412 } // close anonymous namespace
413
414
415
416 namespace dbg
417 {
418     stack_frame::stack_frame(const void *instruction, const std::string &function, const std::string &module) :
419         instruction(instruction),
420         function(function),
421         module(module)
422     {
423     }
424
425     std::ostream &operator<< (std::ostream &out, const stack_frame &frame)
426     {
427         return out << frame.instruction << ": " << frame.function << " in " << frame.module;
428     }
429
430     stack::stack(depth_type limit)
431     {
432         fill_frames(frames_, limit);
433     }
434
435     stack::const_iterator stack::begin() const
436     {
437         return frames_.begin();
438     }
439
440     stack::const_iterator stack::end() const
441     {
442         return frames_.end();
443     }
444
445     stack::depth_type stack::depth() const
446     {
447         return frames_.size();
448     }
449
450 } // close namespace dbg
451