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