From 48501fb37dd46c2e9f6f016dd987b13e61dd476a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 17 May 2013 12:54:59 +0100 Subject: Add verbatim stack.{cpp,hpp} from mr-edd.co.uk hg repo. --- src/lib/stack.cpp | 451 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 src/lib/stack.cpp (limited to 'src/lib/stack.cpp') diff --git a/src/lib/stack.cpp b/src/lib/stack.cpp new file mode 100644 index 000000000..b3479b1bb --- /dev/null +++ b/src/lib/stack.cpp @@ -0,0 +1,451 @@ +// Copyright 2007 Edd Dawson. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include +#include + +#include "dbg/stack.hpp" + +#if defined(_WIN32) +# include +# include + +# if defined(__MINGW32__) +# include // link against libbfd and libiberty +# include // link against psapi +# include +# endif + +#elif defined(__GNUC__) +# include +# include +#endif + +namespace +{ + const char * const unknown_function = "[unknown function]"; + const char * const unknown_module = "[unknown module]"; + +#if defined(__GNUC__) + std::string demangle(const char *name) + { + if (!name) + return unknown_function; + + int status = 0; + char *d = 0; + std::string ret = name; + try + { + if ((d = abi::__cxa_demangle(name, 0, 0, &status))) + ret = d; + } + catch (const std::bad_alloc &) { } + + std::free(d); + return ret; + } +#endif + +#if defined(_WIN32) + + // Derive from this to disallow copying of your class. + // c.f. boost::noncopyable + class uncopyable + { + protected: + uncopyable() { } + + private: + uncopyable(const uncopyable &); + uncopyable &operator= (const uncopyable &); + }; + +#if defined(__MINGW32__) + + // Provides a means to translate a program counter offset in to the name of the corresponding function. + class bfd_context : uncopyable + { + private: + struct find_data + { + std::string func; + asymbol **symbol_table; + bfd_vma counter; + }; + + public: + bfd_context() : + abfd_(0), + sec_(0), + symbol_table_(0) + { + char procname[MAX_PATH]; + GetModuleFileNameA(NULL, procname, sizeof procname); + + bfd_init(); + abfd_ = bfd_openr(procname, 0); + if (!abfd_) + throw std::runtime_error("Failed to parse object data for the executable"); + + char **formats = 0; + bool b1 = bfd_check_format(abfd_, bfd_object); + bool b2 = bfd_check_format_matches(abfd_, bfd_object, &formats); + bool b3 = bfd_get_file_flags(abfd_) & HAS_SYMS; + + if (!(b1 && b2 && b3)) + { + bfd_close(abfd_); + free(formats); + throw std::runtime_error("Failed to parse object data for the executable"); + } + free(formats); + + // Load symbol table + unsigned dummy = 0; + if (bfd_read_minisymbols(abfd_, FALSE, reinterpret_cast(&symbol_table_), &dummy) == 0 && + bfd_read_minisymbols(abfd_, TRUE, reinterpret_cast(&symbol_table_), &dummy) < 0) + { + free(symbol_table_); + bfd_close(abfd_); + throw std::runtime_error("Failed to parse object data for the executable"); + } + } + + ~bfd_context() + { + free(symbol_table_); + bfd_close(abfd_); + } + + std::string get_function_name(DWORD offset) + { + find_data data; + data.symbol_table = symbol_table_; + data.counter = offset; + + bfd_map_over_sections(abfd_, &find_function_name_in_section, &data); + + return data.func; + } + + private: + static void find_function_name_in_section(bfd *abfd, asection *sec, void *opaque_data) + { + assert(sec); + assert(opaque_data); + find_data &data = *static_cast(opaque_data); + + if (!data.func.empty()) return; // already found it + + if (!(bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) return; + + bfd_vma vma = bfd_get_section_vma(abfd, sec); + if (data.counter < vma || vma + bfd_get_section_size(sec) <= data.counter) return; + + const char *func = 0; + const char *file = 0; + unsigned line = 0; + + if (bfd_find_nearest_line(abfd, sec, data.symbol_table, data.counter - vma, &file, &func, &line) && func) + data.func = demangle(func); + } + + private: + bfd *abfd_; + asection *sec_; + asymbol **symbol_table_; + }; + +#endif // __MINGW32__ + + // g++ spouts warnings if you use {0} to initialize PODs. So we use this instead: + const struct + { + template + operator POD () const { POD p; std::memset(&p, 0, sizeof p); return p; } + } + empty_pod = { }; + + // Wraps a FARPROC. Implicitly convertible to any kind of pointer-to-function. + // Avoids having reinterpret casts all over the place. + struct auto_cast_function_ptr + { + auto_cast_function_ptr(FARPROC f) : fptr_(f) { } + + template + operator FuncPtr() const { return reinterpret_cast(fptr_); } + + FARPROC fptr_; + }; + + // A wrapper around a DLL. Can dynamically get function pointers with the function() function! + class windows_dll : uncopyable + { + public: + explicit windows_dll(const std::string &libname) : + name_(libname), + lib_(LoadLibrary(name_.c_str())) + { + if (!lib_) throw std::runtime_error("Failed to load dll " + name_); + } + + ~windows_dll() { FreeLibrary(lib_); } + + const std::string &name() const { return name_; } + + auto_cast_function_ptr function(const char *func_name) const + { + FARPROC proc = GetProcAddress(lib_, func_name); + if (!proc) throw std::runtime_error(std::string("failed to load function ") + func_name + " from library " + name_); + + return proc; + } + + private: + std::string name_; + HMODULE lib_; + }; + + // An object that makes sure debugging symbols are available + class symbol_context : uncopyable + { + public: + symbol_context() + { + if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) + throw std::runtime_error("Failed to initialize symbol context"); + } + ~symbol_context() { SymCleanup(GetCurrentProcess()); } + }; + + // A simple Windows mutex class. Use a lock object to lock the mutex for the duration of a scope. + class mutex : uncopyable + { + public: + mutex() { InitializeCriticalSection(&cs_); } + ~mutex() { DeleteCriticalSection(&cs_); } + + private: + friend class lock; + void lock() { EnterCriticalSection(&cs_); } + void unlock() { LeaveCriticalSection(&cs_); } + + CRITICAL_SECTION cs_; + } + g_fill_frames_mtx; + + // A lock for the mutex + class lock : uncopyable + { + public: + lock(mutex &m) : m_(m) { m.lock(); } + ~lock() { m_.unlock(); } + private: + mutex &m_; + }; + + + void fill_frames(std::list &frames, dbg::stack::depth_type limit) + { + lock lk(g_fill_frames_mtx); + + symbol_context sc; +#ifdef __MINGW32__ + bfd_context bfdc; +#endif + + STACKFRAME frame = empty_pod; + CONTEXT context = empty_pod; + context.ContextFlags = CONTEXT_FULL; + + windows_dll kernel32("kernel32.dll"); + void (WINAPI *RtlCaptureContext_)(CONTEXT*) = kernel32.function("RtlCaptureContext"); + + RtlCaptureContext_(&context); + +#if defined(_M_AMD64) + frame.AddrPC.Offset = context.Rip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Rsp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Rbp; + frame.AddrFrame.Mode = AddrModeFlat; +#else + frame.AddrPC.Offset = context.Eip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Offset = context.Esp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context.Ebp; + frame.AddrFrame.Mode = AddrModeFlat; +#endif + + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + + bool skip = true; + bool has_limit = limit != 0; + char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255]; + char module_name_raw[MAX_PATH]; + +#if defined(_M_AMD64) + const DWORD machine = IMAGE_FILE_MACHINE_AMD64; +#else + const DWORD machine = IMAGE_FILE_MACHINE_I386; +#endif + + while(StackWalk(machine, process, thread, &frame, &context, 0, SymFunctionTableAccess, SymGetModuleBase, 0)) + { + if (skip) + { + skip = false; + continue; + } + + if (has_limit && limit-- == 0) break; + + IMAGEHLP_SYMBOL *symbol = reinterpret_cast(symbol_buffer); + symbol->SizeOfStruct = (sizeof *symbol) + 255; + symbol->MaxNameLength = 254; + +#if defined(_WIN64) + DWORD64 module_base = SymGetModuleBase(process, frame.AddrPC.Offset); +#else + DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset); +#endif + std::string module_name = unknown_module; + if (module_base && GetModuleFileNameA(reinterpret_cast(module_base), module_name_raw, MAX_PATH)) + module_name = module_name_raw; + +#if defined(__MINGW32__) + std::string func = bfdc.get_function_name(frame.AddrPC.Offset); + + if (func.empty()) + { + DWORD dummy = 0; + BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol); + func = got_symbol ? symbol->Name : unknown_function; + } +#else + DWORD dummy = 0; + BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol); + std::string func = got_symbol ? symbol->Name : unknown_function; +#endif + + dbg::stack_frame f(reinterpret_cast(frame.AddrPC.Offset), func, module_name); + frames.push_back(f); + } + } +#elif defined(__GNUC__) +# if defined(__i386__) || defined(__amd64__) + + void fill_frames(std::list &frames, dbg::stack::depth_type limit) + { + // Based on code found at: + // http://www.tlug.org.za/wiki/index.php/Obtaining_a_stack_trace_in_C_upon_SIGSEGV + + Dl_info info; + void **frame = static_cast(__builtin_frame_address(0)); + void **bp = static_cast(*frame); + void *ip = frame[1]; + + bool has_limit = limit != 0; + bool skip = true; + + while(bp && ip && dladdr(ip, &info)) + { + if (skip) + skip = false; + else + { + if (has_limit && limit-- == 0) break; + frames.push_back(dbg::stack_frame(ip, demangle(info.dli_sname), info.dli_fname)); + + if(info.dli_sname && !std::strcmp(info.dli_sname, "main")) break; + } + + ip = bp[1]; + bp = static_cast(bp[0]); + } + } + +# elif defined(__ppc__) + + void fill_frames(std::list &frames, dbg::stack::depth_type limit) + { + // Based on code found at: + // http://www.informit.com/articles/article.aspx?p=606582&seqNum=4&rl=1 + + void *ip = __builtin_return_address(0); + void **frame = static_cast(__builtin_frame_address(1)); + bool has_limit = limit != 0; + Dl_info info; + + do + { + if (has_limit && limit-- == 0) break; + + if (dladdr(ip, &info)) + frames.push_back(dbg::stack_frame(ip, demangle(info.dli_sname), info.dli_fname)); + + if (frame && (frame = static_cast(*frame))) ip = *(frame + 2); + } + while (frame && ip); + } + +# else + // GNU, but not x86, x64 nor PPC +# error "Sorry but dbg::stack is not supported on this architecture" +# endif +#else + // Unsupported compiler +# error "Sorry but dbg::stack is not supported on this compiler" +#endif + +} // close anonymous namespace + + + +namespace dbg +{ + stack_frame::stack_frame(const void *instruction, const std::string &function, const std::string &module) : + instruction(instruction), + function(function), + module(module) + { + } + + std::ostream &operator<< (std::ostream &out, const stack_frame &frame) + { + return out << frame.instruction << ": " << frame.function << " in " << frame.module; + } + + stack::stack(depth_type limit) + { + fill_frames(frames_, limit); + } + + stack::const_iterator stack::begin() const + { + return frames_.begin(); + } + + stack::const_iterator stack::end() const + { + return frames_.end(); + } + + stack::depth_type stack::depth() const + { + return frames_.size(); + } + +} // close namespace dbg + -- cgit v1.2.3 From c22c118458b3eafd0b71cf61f9edb33e9770802e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 17 May 2013 13:12:15 +0100 Subject: Fix build. --- src/lib/stack.cpp | 10 ++++++++-- src/lib/wscript | 2 +- wscript | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src/lib/stack.cpp') diff --git a/src/lib/stack.cpp b/src/lib/stack.cpp index b3479b1bb..24668dfe7 100644 --- a/src/lib/stack.cpp +++ b/src/lib/stack.cpp @@ -11,13 +11,15 @@ #include #include -#include "dbg/stack.hpp" +#include "stack.hpp" #if defined(_WIN32) # include # include # if defined(__MINGW32__) +# define PACKAGE 1 +# define PACKAGE_VERSION 1 # include // link against libbfd and libiberty # include // link against psapi # include @@ -192,7 +194,7 @@ namespace public: explicit windows_dll(const std::string &libname) : name_(libname), - lib_(LoadLibrary(name_.c_str())) + lib_(LoadLibraryA(name_.c_str())) { if (!lib_) throw std::runtime_error("Failed to load dll " + name_); } @@ -329,7 +331,11 @@ namespace if (func.empty()) { +#if defined(_WIN64) + DWORD64 dummy = 0; +#else DWORD dummy = 0; +#endif BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol); func = got_symbol ? symbol->Name : unknown_function; } diff --git a/src/lib/wscript b/src/lib/wscript index 129b8d9fb..dcf44a7e8 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -68,7 +68,7 @@ def build(bld): SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP GLIB LZMA """ if bld.env.TARGET_WINDOWS: - obj.uselib += ' WINSOCK2' + obj.uselib += ' WINSOCK2 BFD DBGHELP IBERTY' obj.source = sources + ' stack.cpp' else: obj.source = sources diff --git a/wscript b/wscript index 51a2ad04d..35a55213e 100644 --- a/wscript +++ b/wscript @@ -32,6 +32,9 @@ def configure(conf): conf.env.append_value('CXXFLAGS', ['-mconsole']) conf.env.append_value('LINKFLAGS', ['-mconsole']) conf.check(lib = 'ws2_32', uselib_store = 'WINSOCK2', msg = "Checking for library winsock2") + conf.check(lib = 'bfd', uselib_store = 'BFD', msg = "Checking for library bfd") + conf.check(lib = 'dbghelp', uselib_store = 'DBGHELP', msg = "Checking for library dbghelp") + conf.check(lib = 'iberty', uselib_store = 'IBERTY', msg = "Checking for library iberty") boost_lib_suffix = '-mt' boost_thread = 'boost_thread_win32-mt' else: -- cgit v1.2.3 From beb7c9406f6c9d3979f527db3248eab66347c846 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 17 May 2013 13:37:18 +0100 Subject: Try to integrate; add line numbers to traces. --- src/lib/stack.cpp | 24 +++++++++++++++--------- src/lib/stack.hpp | 5 ++++- src/lib/util.cc | 22 ++++++++++++++++++++++ 3 files changed, 41 insertions(+), 10 deletions(-) (limited to 'src/lib/stack.cpp') diff --git a/src/lib/stack.cpp b/src/lib/stack.cpp index 24668dfe7..20a5c5be7 100644 --- a/src/lib/stack.cpp +++ b/src/lib/stack.cpp @@ -1,3 +1,5 @@ +/** -*- c-basic-offset: 4; default-tab-width: 4; indent-tabs-mode: nil; -*- */ + // Copyright 2007 Edd Dawson. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -79,6 +81,7 @@ namespace struct find_data { std::string func; + unsigned int line; asymbol **symbol_table; bfd_vma counter; }; @@ -127,7 +130,7 @@ namespace bfd_close(abfd_); } - std::string get_function_name(DWORD offset) + std::pair get_function_name_and_line(DWORD offset) { find_data data; data.symbol_table = symbol_table_; @@ -135,7 +138,7 @@ namespace bfd_map_over_sections(abfd_, &find_function_name_in_section, &data); - return data.func; + return std::make_pair(data.func, data.line); } private: @@ -156,8 +159,10 @@ namespace const char *file = 0; unsigned line = 0; - if (bfd_find_nearest_line(abfd, sec, data.symbol_table, data.counter - vma, &file, &func, &line) && func) + if (bfd_find_nearest_line(abfd, sec, data.symbol_table, data.counter - vma, &file, &func, &line) && func) { data.func = demangle(func); + data.line = line; + } } private: @@ -327,9 +332,9 @@ namespace module_name = module_name_raw; #if defined(__MINGW32__) - std::string func = bfdc.get_function_name(frame.AddrPC.Offset); + std::pair func_and_line = bfdc.get_function_name_and_line(frame.AddrPC.Offset); - if (func.empty()) + if (func_and_line.first.empty()) { #if defined(_WIN64) DWORD64 dummy = 0; @@ -337,7 +342,7 @@ namespace DWORD dummy = 0; #endif BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol); - func = got_symbol ? symbol->Name : unknown_function; + func_and_line.first = got_symbol ? symbol->Name : unknown_function; } #else DWORD dummy = 0; @@ -345,7 +350,7 @@ namespace std::string func = got_symbol ? symbol->Name : unknown_function; #endif - dbg::stack_frame f(reinterpret_cast(frame.AddrPC.Offset), func, module_name); + dbg::stack_frame f(reinterpret_cast(frame.AddrPC.Offset), func_and_line.first, func_and_line.second, module_name); frames.push_back(f); } } @@ -421,16 +426,17 @@ namespace namespace dbg { - stack_frame::stack_frame(const void *instruction, const std::string &function, const std::string &module) : + stack_frame::stack_frame(const void *instruction, const std::string &function, unsigned int line, const std::string &module) : instruction(instruction), function(function), + line(line), module(module) { } std::ostream &operator<< (std::ostream &out, const stack_frame &frame) { - return out << frame.instruction << ": " << frame.function << " in " << frame.module; + return out << frame.instruction << ": " << frame.function << ":" << frame.line << " in " << frame.module; } stack::stack(depth_type limit) diff --git a/src/lib/stack.hpp b/src/lib/stack.hpp index 0430439e0..2b622d020 100644 --- a/src/lib/stack.hpp +++ b/src/lib/stack.hpp @@ -1,3 +1,5 @@ +/** -*- c-basic-offset: 4; default-tab-width: 4; indent-tabs-mode: nil; -*- */ + // Copyright 2007 Edd Dawson. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -16,10 +18,11 @@ namespace dbg //! the name of the corresponding function and the "module" (executable or library) in which the function resides. struct stack_frame { - stack_frame(const void *instruction, const std::string &function, const std::string &module); + stack_frame(const void *instruction, const std::string &function, unsigned int line, const std::string &module); const void *instruction; std::string function; + unsigned int line; std::string module; }; diff --git a/src/lib/util.cc b/src/lib/util.cc index be078a95f..f5bcdf12c 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,9 @@ extern "C" { #include "filter.h" #include "sound_processor.h" #include "config.h" +#ifdef DVDOMATIC_WINDOWS +#include "stack.hpp" +#endif #include "i18n.h" @@ -75,12 +79,14 @@ using std::min; using std::max; using std::multimap; using std::pair; +using std::ofstream; using boost::shared_ptr; using boost::lexical_cast; using boost::optional; using libdcp::Size; boost::thread::id ui_thread; +boost::filesystem::path backtrace_file; /** Convert some number of seconds to a string representation * in hours, minutes and seconds. @@ -242,12 +248,28 @@ seconds (struct timeval t) return t.tv_sec + (double (t.tv_usec) / 1e6); } +#ifdef DVDOMATIC_WINDOWS +LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *) +{ + dbg::stack s; + ofstream f (backtrace_file.string().c_str()); + std::copy(s.begin(), s.end(), std::ostream_iterator(f, "\n")); + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + /** Call the required functions to set up DVD-o-matic's static arrays, etc. * Must be called from the UI thread, if there is one. */ void dvdomatic_setup () { +#ifdef DVDOMATIC_WINDOWS + backtrace_file /= g_get_user_config_dir (); + backtrace_file /= "backtrace.txt"; + SetUnhandledExceptionFilter(exception_handler); +#endif + avfilter_register_all (); Format::setup_formats (); -- cgit v1.2.3