diff options
| author | Carl Hetherington <cth@carlh.net> | 2014-12-15 10:58:01 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2014-12-15 10:58:01 +0000 |
| commit | 815e4cf7441d83ffca6d4d797a1a313f7c6464ab (patch) | |
| tree | a469fb8153fc83cc8e0b35ced4e32328d9fca8e9 /src | |
| parent | 1e33ccb216e73d3ea94ed08aaa4766c856178757 (diff) | |
Hand-apply 155b4b9f615f42b5cc26e2953860aba34b17bbc0; allow build of Windows debug version using gdb.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/stack.cpp | 460 | ||||
| -rw-r--r-- | src/lib/stack.hpp | 58 | ||||
| -rw-r--r-- | src/lib/util.cc | 78 | ||||
| -rw-r--r-- | src/lib/util.h | 2 | ||||
| -rw-r--r-- | src/lib/wscript | 1 | ||||
| -rw-r--r-- | src/tools/dcpomatic_kdm.cc | 2 |
6 files changed, 71 insertions, 530 deletions
diff --git a/src/lib/stack.cpp b/src/lib/stack.cpp deleted file mode 100644 index 167524017..000000000 --- a/src/lib/stack.cpp +++ /dev/null @@ -1,460 +0,0 @@ -// 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 <cassert> -#include <cstring> -#include <cstdlib> -#include <iomanip> -#include <ostream> -#include <stdexcept> - -#include "stack.hpp" - -#if defined(_WIN32) -# include <windows.h> -# include <imagehlp.h> - -# if defined(__MINGW32__) -# define PACKAGE 1 -# define PACKAGE_VERSION 1 -# include <bfd.h> // link against libbfd and libiberty -# include <psapi.h> // link against psapi -# include <cxxabi.h> -# endif - -#elif defined(__GNUC__) -# include <dlfcn.h> -# include <cxxabi.h> -#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; - unsigned int line; - 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<void **>(&symbol_table_), &dummy) == 0 && - bfd_read_minisymbols(abfd_, TRUE, reinterpret_cast<void **>(&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::pair<std::string, unsigned int> get_function_name_and_line(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 std::make_pair(data.func, data.line); - } - - 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<find_data *>(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); - data.line = line; - } - } - - 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<typename POD> - 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<typename FuncPtr> - operator FuncPtr() const { return reinterpret_cast<FuncPtr>(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_(LoadLibraryA(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<dbg::stack_frame> &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<IMAGEHLP_SYMBOL *>(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<HINSTANCE>(module_base), module_name_raw, MAX_PATH)) - module_name = module_name_raw; - -#if defined(__MINGW32__) - std::pair<std::string, unsigned int> func_and_line = bfdc.get_function_name_and_line(frame.AddrPC.Offset); - - if (func_and_line.first.empty()) - { -#if defined(_WIN64) - DWORD64 dummy = 0; -#else - DWORD dummy = 0; -#endif - BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol); - func_and_line.first = 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<const void *>(frame.AddrPC.Offset), func_and_line.first, func_and_line.second, module_name); - frames.push_back(f); - } - } -#elif defined(__GNUC__) -# if defined(__i386__) || defined(__amd64__) - - void fill_frames(std::list<dbg::stack_frame> &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<void **>(__builtin_frame_address(0)); - void **bp = static_cast<void **>(*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<void**>(bp[0]); - } - } - -# elif defined(__ppc__) - - void fill_frames(std::list<dbg::stack_frame> &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<void **>(__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<void**>(*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, 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 << ":" << frame.line << " 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 - diff --git a/src/lib/stack.hpp b/src/lib/stack.hpp deleted file mode 100644 index 73a13bf85..000000000 --- a/src/lib/stack.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// 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) - -#ifndef STACK_HPP_0022_01092007 -#define STACK_HPP_0022_01092007 - -#include <string> -#include <list> -#include <iosfwd> - -namespace dbg -{ - //! stack_frame objects are collected by a stack object. They contain information about the instruction pointer, - //! 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, unsigned int line, const std::string &module); - - const void *instruction; - std::string function; - unsigned int line; - std::string module; - }; - - //! Allows you to write a stack_frame object to an std::ostream - std::ostream &operator<< (std::ostream &out, const stack_frame &frame); - - //! Instantiate a dbg::stack object to collect information about the current call stack. Once created, a stack object - //! may be freely copied about and will continue to contain the information about the scope in which collection occurred. - class stack - { - public: - typedef std::list<stack_frame>::size_type depth_type; - typedef std::list<stack_frame>::const_iterator const_iterator; - - //! Collect information about the current call stack. Information on the most recent frames will be collected - //! up to the specified limit. 0 means unlimited. - //! An std::runtime_error may be thrown on failure. - stack(depth_type limit = 0); - - //! Returns an iterator referring to the "top" stack frame - const_iterator begin() const; - - //! Returns an iterator referring to one past the "bottom" stack frame - const_iterator end() const; - - //! Returns the number of frames collected - depth_type depth() const; - - private: - std::list<stack_frame> frames_; - }; - -} // close namespace dbg - -#endif // STACK_HPP_0022_01092007 diff --git a/src/lib/util.cc b/src/lib/util.cc index e03b47568..502393094 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -37,9 +37,6 @@ #include "md5_digester.h" #include "audio_processor.h" #include "safe_stringstream.h" -#ifdef DCPOMATIC_WINDOWS -#include "stack.hpp" -#endif #include <dcp/version.h> #include <dcp/util.h> #include <dcp/signer.h> @@ -69,6 +66,7 @@ extern "C" { #include <boost/filesystem.hpp> #ifdef DCPOMATIC_WINDOWS #include <boost/locale.hpp> +#include <dbghelp.h> #endif #include <signal.h> #include <iomanip> @@ -103,6 +101,10 @@ using boost::optional; using dcp::Size; using dcp::raw_convert; +/** Path to our executable, required by the stacktrace stuff and filled + * in during App::onInit(). + */ +string program_name; static boost::thread::id ui_thread; static boost::filesystem::path backtrace_file; @@ -263,15 +265,69 @@ seconds (struct timeval t) } #ifdef DCPOMATIC_WINDOWS + +/** Resolve symbol name and source location given the path to the executable */ +int +addr2line (void const * const adr) +{ + char addr2line_cmd[512] = { 0 }; + sprintf (addr2line_cmd, "addr2line -f -p -e %.256s %p > %s", program_name.c_str(), addr, backtrace_file.string().c_str()); + return system(addr2line_cmd); +} + +/** This is called when C signals occur on Windows (e.g. SIGSEGV) + * (NOT C++ exceptions!). We write a backtrace to backtrace_file by dark means. + * Adapted from code here: http://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c/ + */ LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *) { - dbg::stack s; FILE* f = fopen_boost (backtrace_file, "w"); - fprintf (f, "Exception thrown:"); - for (dbg::stack::const_iterator i = s.begin(); i != s.end(); ++i) { - fprintf (f, "%p %s %d %s\n", i->instruction, i->function.c_str(), i->line, i->module.c_str()); + fprintf (f, "C-style exception %d\n", info->ExceptionRecord->ExceptionCode); + fclose(f); + + if (info->ExceptionRecord->ExceptionCode != EXCEPTION_STACK_OVERFLOW) { + CONTEXT* context = info->ContextRecord; + SymInitialize (GetCurrentProcess (), 0, true); + + STACKFRAME frame = { 0 }; + + /* setup initial stack frame */ +#if _WIN64 + frame.AddrPC.Offset = context->Rip; + frame.AddrStack.Offset = context->Rsp; + frame.AddrFrame.Offset = context->Rbp; +#else + frame.AddrPC.Offset = context->Eip; + frame.AddrStack.Offset = context->Esp; + frame.AddrFrame.Offset = context->Ebp; +#endif + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Mode = AddrModeFlat; + + while ( + StackWalk ( + IMAGE_FILE_MACHINE_I386, + GetCurrentProcess (), + GetCurrentThread (), + &frame, + context, + 0, + SymFunctionTableAccess, + SymGetModuleBase, + 0 + ) + ) { + addr2line((void *) frame.AddrPC.Offset); + } + } else { +#ifdef _WIN64 + addr2line ((void *) info->ContextRecord->Rip); +#else + addr2line ((void *) info->ContextRecord->Eip); +#endif } - fclose (f); + return EXCEPTION_CONTINUE_SEARCH; } #endif @@ -282,7 +338,11 @@ set_backtrace_file (boost::filesystem::path p) backtrace_file = p; } -/* From http://stackoverflow.com/questions/2443135/how-do-i-find-where-an-exception-was-thrown-in-c */ +/** This is called when there is an unhandled exception. Any + * backtrace in this function is useless on Windows as the stack has + * already been unwound from the throw; we have the gdb wrap hack to + * cope with that. + */ void terminate () { diff --git a/src/lib/util.h b/src/lib/util.h index 1d04d358b..f5d0b3aca 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -48,6 +48,8 @@ extern "C" { #define HISTORY_SIZE 10 #define REPORT_PROBLEM _("Please report this problem by using Help -> Report a problem or via email to carl@dcpomatic.com") +extern std::string program_name; + class Job; struct AVSubtitle; diff --git a/src/lib/wscript b/src/lib/wscript index 0a6b79207..7edd5dcdf 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -118,7 +118,6 @@ def build(bld): if bld.env.TARGET_WINDOWS: obj.uselib += ' WINSOCK2 BFD DBGHELP IBERTY SHLWAPI MSWSOCK BOOST_LOCALE' - obj.source += ' stack.cpp' if bld.env.BUILD_STATIC: obj.uselib += ' XMLPP' diff --git a/src/tools/dcpomatic_kdm.cc b/src/tools/dcpomatic_kdm.cc index 6257d60af..2398dc74d 100644 --- a/src/tools/dcpomatic_kdm.cc +++ b/src/tools/dcpomatic_kdm.cc @@ -33,8 +33,6 @@ using std::list; using std::vector; using boost::shared_ptr; -static string program_name; - static void help () { |
