1 /** -*- c-basic-offset: 4; default-tab-width: 4; indent-tabs-mode: nil; -*- */
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)
20 # include <imagehlp.h>
22 # if defined(__MINGW32__)
24 # define PACKAGE_VERSION 1
25 # include <bfd.h> // link against libbfd and libiberty
26 # include <psapi.h> // link against psapi
30 #elif defined(__GNUC__)
37 const char * const unknown_function = "[unknown function]";
38 const char * const unknown_module = "[unknown module]";
41 std::string demangle(const char *name)
44 return unknown_function;
48 std::string ret = name;
51 if ((d = abi::__cxa_demangle(name, 0, 0, &status)))
54 catch (const std::bad_alloc &) { }
63 // Derive from this to disallow copying of your class.
64 // c.f. boost::noncopyable
71 uncopyable(const uncopyable &);
72 uncopyable &operator= (const uncopyable &);
75 #if defined(__MINGW32__)
77 // Provides a means to translate a program counter offset in to the name of the corresponding function.
78 class bfd_context : uncopyable
85 asymbol **symbol_table;
95 char procname[MAX_PATH];
96 GetModuleFileNameA(NULL, procname, sizeof procname);
99 abfd_ = bfd_openr(procname, 0);
101 throw std::runtime_error("Failed to parse object data for the executable");
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;
108 if (!(b1 && b2 && b3))
112 throw std::runtime_error("Failed to parse object data for the executable");
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)
123 throw std::runtime_error("Failed to parse object data for the executable");
133 std::pair<std::string, unsigned int> get_function_name_and_line(DWORD offset)
136 data.symbol_table = symbol_table_;
137 data.counter = offset;
139 bfd_map_over_sections(abfd_, &find_function_name_in_section, &data);
141 return std::make_pair(data.func, data.line);
145 static void find_function_name_in_section(bfd *abfd, asection *sec, void *opaque_data)
149 find_data &data = *static_cast<find_data *>(opaque_data);
151 if (!data.func.empty()) return; // already found it
153 if (!(bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) return;
155 bfd_vma vma = bfd_get_section_vma(abfd, sec);
156 if (data.counter < vma || vma + bfd_get_section_size(sec) <= data.counter) return;
158 const char *func = 0;
159 const char *file = 0;
162 if (bfd_find_nearest_line(abfd, sec, data.symbol_table, data.counter - vma, &file, &func, &line) && func) {
163 data.func = demangle(func);
171 asymbol **symbol_table_;
174 #endif // __MINGW32__
176 // g++ spouts warnings if you use {0} to initialize PODs. So we use this instead:
179 template<typename POD>
180 operator POD () const { POD p; std::memset(&p, 0, sizeof p); return p; }
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
188 auto_cast_function_ptr(FARPROC f) : fptr_(f) { }
190 template<typename FuncPtr>
191 operator FuncPtr() const { return reinterpret_cast<FuncPtr>(fptr_); }
196 // A wrapper around a DLL. Can dynamically get function pointers with the function() function!
197 class windows_dll : uncopyable
200 explicit windows_dll(const std::string &libname) :
202 lib_(LoadLibraryA(name_.c_str()))
204 if (!lib_) throw std::runtime_error("Failed to load dll " + name_);
207 ~windows_dll() { FreeLibrary(lib_); }
209 const std::string &name() const { return name_; }
211 auto_cast_function_ptr function(const char *func_name) const
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_);
224 // An object that makes sure debugging symbols are available
225 class symbol_context : uncopyable
230 if (!SymInitialize(GetCurrentProcess(), 0, TRUE))
231 throw std::runtime_error("Failed to initialize symbol context");
233 ~symbol_context() { SymCleanup(GetCurrentProcess()); }
236 // A simple Windows mutex class. Use a lock object to lock the mutex for the duration of a scope.
237 class mutex : uncopyable
240 mutex() { InitializeCriticalSection(&cs_); }
241 ~mutex() { DeleteCriticalSection(&cs_); }
245 void lock() { EnterCriticalSection(&cs_); }
246 void unlock() { LeaveCriticalSection(&cs_); }
248 CRITICAL_SECTION cs_;
252 // A lock for the mutex
253 class lock : uncopyable
256 lock(mutex &m) : m_(m) { m.lock(); }
257 ~lock() { m_.unlock(); }
263 void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
265 lock lk(g_fill_frames_mtx);
272 STACKFRAME frame = empty_pod;
273 CONTEXT context = empty_pod;
274 context.ContextFlags = CONTEXT_FULL;
276 windows_dll kernel32("kernel32.dll");
277 void (WINAPI *RtlCaptureContext_)(CONTEXT*) = kernel32.function("RtlCaptureContext");
279 RtlCaptureContext_(&context);
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;
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;
297 HANDLE process = GetCurrentProcess();
298 HANDLE thread = GetCurrentThread();
301 bool has_limit = limit != 0;
302 char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255];
303 char module_name_raw[MAX_PATH];
305 #if defined(_M_AMD64)
306 const DWORD machine = IMAGE_FILE_MACHINE_AMD64;
308 const DWORD machine = IMAGE_FILE_MACHINE_I386;
311 while(StackWalk(machine, process, thread, &frame, &context, 0, SymFunctionTableAccess, SymGetModuleBase, 0))
319 if (has_limit && limit-- == 0) break;
321 IMAGEHLP_SYMBOL *symbol = reinterpret_cast<IMAGEHLP_SYMBOL *>(symbol_buffer);
322 symbol->SizeOfStruct = (sizeof *symbol) + 255;
323 symbol->MaxNameLength = 254;
326 DWORD64 module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
328 DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
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;
334 #if defined(__MINGW32__)
335 std::pair<std::string, unsigned int> func_and_line = bfdc.get_function_name_and_line(frame.AddrPC.Offset);
337 if (func_and_line.first.empty())
344 BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol);
345 func_and_line.first = got_symbol ? symbol->Name : unknown_function;
349 BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol);
350 std::string func = got_symbol ? symbol->Name : unknown_function;
353 dbg::stack_frame f(reinterpret_cast<const void *>(frame.AddrPC.Offset), func_and_line.first, func_and_line.second, module_name);
357 #elif defined(__GNUC__)
358 # if defined(__i386__) || defined(__amd64__)
360 void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
362 // Based on code found at:
363 // http://www.tlug.org.za/wiki/index.php/Obtaining_a_stack_trace_in_C_upon_SIGSEGV
366 void **frame = static_cast<void **>(__builtin_frame_address(0));
367 void **bp = static_cast<void **>(*frame);
370 bool has_limit = limit != 0;
373 while(bp && ip && dladdr(ip, &info))
379 if (has_limit && limit-- == 0) break;
380 frames.push_back(dbg::stack_frame(ip, demangle(info.dli_sname), info.dli_fname));
382 if(info.dli_sname && !std::strcmp(info.dli_sname, "main")) break;
386 bp = static_cast<void**>(bp[0]);
390 # elif defined(__ppc__)
392 void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
394 // Based on code found at:
395 // http://www.informit.com/articles/article.aspx?p=606582&seqNum=4&rl=1
397 void *ip = __builtin_return_address(0);
398 void **frame = static_cast<void **>(__builtin_frame_address(1));
399 bool has_limit = limit != 0;
404 if (has_limit && limit-- == 0) break;
406 if (dladdr(ip, &info))
407 frames.push_back(dbg::stack_frame(ip, demangle(info.dli_sname), info.dli_fname));
409 if (frame && (frame = static_cast<void**>(*frame))) ip = *(frame + 2);
415 // GNU, but not x86, x64 nor PPC
416 # error "Sorry but dbg::stack is not supported on this architecture"
419 // Unsupported compiler
420 # error "Sorry but dbg::stack is not supported on this compiler"
423 } // close anonymous namespace
429 stack_frame::stack_frame(const void *instruction, const std::string &function, unsigned int line, const std::string &module) :
430 instruction(instruction),
437 std::ostream &operator<< (std::ostream &out, const stack_frame &frame)
439 return out << frame.instruction << ": " << frame.function << ":" << frame.line << " in " << frame.module;
442 stack::stack(depth_type limit)
444 fill_frames(frames_, limit);
447 stack::const_iterator stack::begin() const
449 return frames_.begin();
452 stack::const_iterator stack::end() const
454 return frames_.end();
457 stack::depth_type stack::depth() const
459 return frames_.size();
462 } // close namespace dbg