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