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