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