Commits

Anonymous committed f933d37

initial commit

  • Participants

Comments (0)

Files changed (12)

+syntax: glob
+*.ilk
+*.pdb
+*.o
+*.obj
+*.lib
+*.a
+.slam_made_this
+*.exe
+slam.db
+.DS_Store
+*.pyc
+*.swp
+release/*
+debug/*

example/example.cpp

+// 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 <algorithm>
+#include <iostream>
+#include <iterator>
+
+#include <dbg/stack.hpp>
+
+template<unsigned Depth>
+struct recurser
+{
+	static void f()
+	{
+		recurser<Depth - 1>::f();
+	}
+};
+
+template<>
+struct recurser<0>
+{
+	static void f()
+	{
+		dbg::stack s;
+		std::copy(s.begin(), s.end(), std::ostream_iterator<dbg::stack_frame>(std::cout, "\n"));
+	}
+};
+
+
+void e()
+{
+	dbg::stack s;
+	std::copy(s.begin(), s.end(), std::ostream_iterator<dbg::stack_frame>(std::cout, "\n"));
+}
+
+void d() { e(); }
+void c() { d(); }
+void b() { c(); }
+void a() { b(); }
+
+int main()
+{
+	std::cout << "Testing template functions:\n";
+	recurser<10>::f();
+
+	std::cout << "\nTesting regular functions:\n";
+	a();
+
+	return 0;
+}
+sources : common : *.cpp
+
+defines : debug : DEBUG
+defines : release : NDEBUG
+
+flags : common : strict iso
+flags : debug : debug optimize-off
+flags : release : optimize
+
+include : common : ../include
+depends : common : ..
+
+target : common : example
+type : common : executable
+
+dir : common : obj/$(toolset)/$(variant)
+

include/dbg/stack.hpp

+// 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
+depends : common : src

src/platforms/applegcc/slamfile

Empty file added.

src/platforms/gcc/slamfile

Empty file added.

src/platforms/mingw/slamfile

+libs : common : bfd iberty psapi imagehlp

src/platforms/msvc8/slamfile

+libs : common : dbghelp

src/platforms/msvc9/slamfile

+libs : common : dbghelp
+sources : common : *.cpp
+
+defines : debug : DEBUG
+defines : release : NDEBUG
+
+flags : common : strict iso
+flags : debug : debug optimize-off
+flags : release : optimize
+
+include : common : ../include
+depends : common : platforms/$(toolset)
+
+target : common : dbg
+type : common : static-library
+
+dir : common : obj/$(toolset)/$(variant)
+// 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 <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
+{
+#if defined(__GNUC__)
+    std::string demangle(const char *name)
+    {
+        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, (void **)&symbol_table_, &dummy) == 0 &&
+                    bfd_read_minisymbols(abfd_, TRUE, (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:
+    struct
+    {
+        template<typename POD>
+        operator POD () const { POD p; std::memset(&p, 0, sizeof p); return p; }
+    }
+    zero;
+
+    // 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 = zero;
+        CONTEXT context = zero;
+        context.ContextFlags = CONTEXT_FULL;
+
+        windows_dll kernel32("kernel32.dll");
+        void (WINAPI *RtlCaptureContext_) (CONTEXT*) = kernel32.function("RtlCaptureContext");
+
+        RtlCaptureContext_(&context);
+
+        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;
+
+        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];
+
+        while(StackWalk(IMAGE_FILE_MACHINE_I386, 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;
+
+            DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
+            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 displacement = 0; // dummy variable
+                    BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &displacement, symbol);
+                    func = got_symbol ? symbol->Name : "[unknown function]";
+                }
+            #else
+                DWORD displacement = 0; // dummy variable
+                BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &displacement, 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__)
+
+    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 neither x86 or 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
+