Add verbatim stack.{cpp,hpp} from mr-edd.co.uk hg repo.
authorCarl Hetherington <cth@carlh.net>
Fri, 17 May 2013 11:54:59 +0000 (12:54 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 17 May 2013 11:54:59 +0000 (12:54 +0100)
src/lib/stack.cpp [new file with mode: 0644]
src/lib/stack.hpp [new file with mode: 0644]
src/lib/wscript

diff --git a/src/lib/stack.cpp b/src/lib/stack.cpp
new file mode 100644 (file)
index 0000000..b3479b1
--- /dev/null
@@ -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 <cassert>
+#include <cstring>
+#include <cstdlib>
+#include <iomanip>
+#include <ostream>
+#include <stdexcept>
+#include <sstream>
+
+#include "dbg/stack.hpp"
+
+#if defined(_WIN32)
+#   include <windows.h>
+#   include <imagehlp.h>
+
+#   if defined(__MINGW32__)
+#       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;
+                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::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<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);
+            }
+
+        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_(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<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::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<const void *>(frame.AddrPC.Offset), func, 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, 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
+
diff --git a/src/lib/stack.hpp b/src/lib/stack.hpp
new file mode 100644 (file)
index 0000000..0430439
--- /dev/null
@@ -0,0 +1,57 @@
+// 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, const std::string &module);
+
+        const void *instruction;
+        std::string function;
+        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
index 51b103afdc3e639fb357a8e09551551e6f3864b9..129b8d9fbc24e9122bca94a1161f1fd763b644fd 100644 (file)
@@ -50,6 +50,7 @@ sources = """
           util.cc
           video_decoder.cc
           video_source.cc
+          version.cc
           writer.cc
           """
 
@@ -68,7 +69,10 @@ def build(bld):
                  """
     if bld.env.TARGET_WINDOWS:
         obj.uselib += ' WINSOCK2'
-    obj.source = sources + " version.cc"
+        obj.source = sources + ' stack.cpp'
+    else:
+        obj.source = sources
+
     obj.target = 'dvdomatic'
 
     i18n.po_to_mo(os.path.join('src', 'lib'), 'libdvdomatic', bld)