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