diff --git a/src/demangle/demangle.hpp b/src/demangle/demangle.hpp index 6445466..9aba59d 100644 --- a/src/demangle/demangle.hpp +++ b/src/demangle/demangle.hpp @@ -4,9 +4,9 @@ #include namespace cpptrace { - namespace detail { - std::string demangle(const std::string&); - } +namespace detail { + std::string demangle(const std::string&); +} } #endif diff --git a/src/demangle/demangle_with_cxxabi.cpp b/src/demangle/demangle_with_cxxabi.cpp index 8306ed4..1aadaae 100644 --- a/src/demangle/demangle_with_cxxabi.cpp +++ b/src/demangle/demangle_with_cxxabi.cpp @@ -8,25 +8,25 @@ #include namespace cpptrace { - namespace detail { - std::string demangle(const std::string& name) { - int status; - // presumably thread-safe - // it appears safe to pass nullptr for status however the docs don't explicitly say it's safe so I don't - // want to rely on it - char* const demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status); - // demangled will always be nullptr on non-zero status, and if __cxa_demangle ever fails for any reason - // we'll just quietly return the mangled name - if(demangled) { - std::string str = demangled; - // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) - std::free(demangled); - return str; - } else { - return name; - } +namespace detail { + std::string demangle(const std::string& name) { + int status; + // presumably thread-safe + // it appears safe to pass nullptr for status however the docs don't explicitly say it's safe so I don't + // want to rely on it + char* const demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status); + // demangled will always be nullptr on non-zero status, and if __cxa_demangle ever fails for any reason + // we'll just quietly return the mangled name + if(demangled) { + std::string str = demangled; + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + std::free(demangled); + return str; + } else { + return name; } } } +} #endif diff --git a/src/demangle/demangle_with_nothing.cpp b/src/demangle/demangle_with_nothing.cpp index a524584..8ccd23d 100644 --- a/src/demangle/demangle_with_nothing.cpp +++ b/src/demangle/demangle_with_nothing.cpp @@ -5,11 +5,11 @@ #include namespace cpptrace { - namespace detail { - std::string demangle(const std::string& name) { - return name; - } +namespace detail { + std::string demangle(const std::string& name) { + return name; } } +} #endif diff --git a/src/full/full_trace.hpp b/src/full/full_trace.hpp index f3da9da..bc027d4 100644 --- a/src/full/full_trace.hpp +++ b/src/full/full_trace.hpp @@ -9,10 +9,10 @@ #include namespace cpptrace { - namespace detail { - CPPTRACE_FORCE_NO_INLINE - std::vector generate_trace(size_t skip); - } +namespace detail { + CPPTRACE_FORCE_NO_INLINE + std::vector generate_trace(size_t skip); +} } #endif diff --git a/src/full/full_trace_with_libbacktrace.cpp b/src/full/full_trace_with_libbacktrace.cpp index 4067571..babe8e2 100644 --- a/src/full/full_trace_with_libbacktrace.cpp +++ b/src/full/full_trace_with_libbacktrace.cpp @@ -17,77 +17,77 @@ #endif namespace cpptrace { - namespace detail { - struct trace_data { - // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) - std::vector& frames; - // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) - size_t& skip; - }; +namespace detail { + struct trace_data { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) + std::vector& frames; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) + size_t& skip; + }; - int full_callback(void* data_pointer, uintptr_t address, const char* file, int line, const char* symbol) { - trace_data& data = *reinterpret_cast(data_pointer); - if(data.skip > 0) { - data.skip--; - } else if(address == uintptr_t(-1)) { - // sentinel for libbacktrace, stop tracing - return 1; - } else { - data.frames.push_back({ - address, - static_cast(line), - 0, - file ? file : "", - symbol ? symbol : "" - }); - } - return 0; - } - - void syminfo_callback(void* data, uintptr_t, const char* symbol, uintptr_t, uintptr_t) { - stacktrace_frame& frame = *static_cast(data); - frame.symbol = symbol ? symbol : ""; - } - - void error_callback(void*, const char* msg, int errnum) { - nonfatal_error(stringf("libbacktrace error: %s, code %d", msg, errnum)); - } - - backtrace_state* get_backtrace_state() { - static std::mutex mutex; - const std::lock_guard lock(mutex); - // backtrace_create_state must be called only one time per program - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - static backtrace_state* state = nullptr; - static bool called = false; - if(!called) { - state = backtrace_create_state(nullptr, true, error_callback, nullptr); - called = true; - } - return state; - } - - CPPTRACE_FORCE_NO_INLINE - std::vector generate_trace(size_t skip) { - std::vector frames; - skip++; // add one for this call - trace_data data { frames, skip }; - backtrace_full(get_backtrace_state(), 0, full_callback, error_callback, &data); - for(auto& frame : frames) { - if(frame.symbol.empty()) { - // fallback, try to at least recover the symbol name with backtrace_syminfo - backtrace_syminfo( - get_backtrace_state(), - frame.address, - syminfo_callback, - error_callback, - &frame - ); - } - } - return frames; + int full_callback(void* data_pointer, uintptr_t address, const char* file, int line, const char* symbol) { + trace_data& data = *reinterpret_cast(data_pointer); + if(data.skip > 0) { + data.skip--; + } else if(address == uintptr_t(-1)) { + // sentinel for libbacktrace, stop tracing + return 1; + } else { + data.frames.push_back({ + address, + static_cast(line), + 0, + file ? file : "", + symbol ? symbol : "" + }); } + return 0; } + + void syminfo_callback(void* data, uintptr_t, const char* symbol, uintptr_t, uintptr_t) { + stacktrace_frame& frame = *static_cast(data); + frame.symbol = symbol ? symbol : ""; + } + + void error_callback(void*, const char* msg, int errnum) { + nonfatal_error(stringf("libbacktrace error: %s, code %d", msg, errnum)); + } + + backtrace_state* get_backtrace_state() { + static std::mutex mutex; + const std::lock_guard lock(mutex); + // backtrace_create_state must be called only one time per program + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + static backtrace_state* state = nullptr; + static bool called = false; + if(!called) { + state = backtrace_create_state(nullptr, true, error_callback, nullptr); + called = true; + } + return state; + } + + CPPTRACE_FORCE_NO_INLINE + std::vector generate_trace(size_t skip) { + std::vector frames; + skip++; // add one for this call + trace_data data { frames, skip }; + backtrace_full(get_backtrace_state(), 0, full_callback, error_callback, &data); + for(auto& frame : frames) { + if(frame.symbol.empty()) { + // fallback, try to at least recover the symbol name with backtrace_syminfo + backtrace_syminfo( + get_backtrace_state(), + frame.address, + syminfo_callback, + error_callback, + &frame + ); + } + } + return frames; + } +} } #endif diff --git a/src/full/full_trace_with_stacktrace.cpp b/src/full/full_trace_with_stacktrace.cpp index 14fec0b..b9f6a22 100644 --- a/src/full/full_trace_with_stacktrace.cpp +++ b/src/full/full_trace_with_stacktrace.cpp @@ -3,28 +3,29 @@ #include #include "full_trace.hpp" #include "../platform/common.hpp" +#include "../platform/utils.hpp" #include #include namespace cpptrace { - namespace detail { - CPPTRACE_FORCE_NO_INLINE - std::vector generate_trace(size_t skip) { - std::vector frames; - std::stacktrace trace = std::stacktrace::current(skip + 1); - for(const auto entry : trace) { - frames.push_back({ - entry.native_handle(), - entry.source_line(), - 0, - entry.source_file(), - entry.description() - }); - } - return frames; +namespace detail { + CPPTRACE_FORCE_NO_INLINE + std::vector generate_trace(size_t skip) { + std::vector frames; + std::stacktrace trace = std::stacktrace::current(skip + 1); + for(const auto entry : trace) { + frames.push_back({ + entry.native_handle(), + entry.source_line(), + 0, + entry.source_file(), + entry.description() + }); } + return frames; } } +} #endif diff --git a/src/symbols/symbols.hpp b/src/symbols/symbols.hpp index f1fa920..9078744 100644 --- a/src/symbols/symbols.hpp +++ b/src/symbols/symbols.hpp @@ -7,39 +7,39 @@ #include namespace cpptrace { - namespace detail { - #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE - namespace libbacktrace { - std::vector resolve_frames(const std::vector& frames); - } - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF - namespace libdwarf { - std::vector resolve_frames(const std::vector& frames); - } - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL - namespace libdl { - std::vector resolve_frames(const std::vector& frames); - } - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE - namespace addr2line { - std::vector resolve_frames(const std::vector& frames); - } - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP - namespace dbghelp { - std::vector resolve_frames(const std::vector& frames); - } - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING - namespace nothing { - std::vector resolve_frames(const std::vector& frames); - } - #endif +namespace detail { + #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE + namespace libbacktrace { std::vector resolve_frames(const std::vector& frames); } + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF + namespace libdwarf { + std::vector resolve_frames(const std::vector& frames); + } + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL + namespace libdl { + std::vector resolve_frames(const std::vector& frames); + } + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE + namespace addr2line { + std::vector resolve_frames(const std::vector& frames); + } + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP + namespace dbghelp { + std::vector resolve_frames(const std::vector& frames); + } + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING + namespace nothing { + std::vector resolve_frames(const std::vector& frames); + } + #endif + std::vector resolve_frames(const std::vector& frames); +} } #endif diff --git a/src/symbols/symbols_core.cpp b/src/symbols/symbols_core.cpp index c6e8684..5737855 100644 --- a/src/symbols/symbols_core.cpp +++ b/src/symbols/symbols_core.cpp @@ -1,55 +1,56 @@ #include "symbols.hpp" #include +#include #include "../platform/object.hpp" namespace cpptrace { - namespace detail { - void apply_trace( - std::vector& result, - std::vector&& trace - ) { - for(std::size_t i = 0; i < result.size(); i++) { - if(result[i].address == 0) { - result[i].address = trace[i].address; - } - if(result[i].line == 0) { - result[i].line = trace[i].line; - } - if(result[i].col == 0) { - result[i].col = trace[i].col; - } - if(result[i].filename.empty()) { - result[i].filename = std::move(trace[i].filename); - } - if(result[i].symbol.empty()) { - result[i].symbol = std::move(trace[i].symbol); - } +namespace detail { + void apply_trace( + std::vector& result, + std::vector&& trace + ) { + for(std::size_t i = 0; i < result.size(); i++) { + if(result[i].address == 0) { + result[i].address = trace[i].address; + } + if(result[i].line == 0) { + result[i].line = trace[i].line; + } + if(result[i].col == 0) { + result[i].col = trace[i].col; + } + if(result[i].filename.empty()) { + result[i].filename = std::move(trace[i].filename); + } + if(result[i].symbol.empty()) { + result[i].symbol = std::move(trace[i].symbol); } } + } - std::vector resolve_frames(const std::vector& frames) { - std::vector trace(frames.size()); - #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL - apply_trace(trace, libdl::resolve_frames(frames)); - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF - apply_trace(trace, libdwarf::resolve_frames(frames)); - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP - apply_trace(trace, dbghelp::resolve_frames(frames)); - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE - apply_trace(trace, addr2line::resolve_frames(frames)); - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE - apply_trace(trace, libbacktrace::resolve_frames(frames)); - #endif - #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING - apply_trace(trace, nothing::resolve_frames(frames)); - #endif - return trace; - } + std::vector resolve_frames(const std::vector& frames) { + std::vector trace(frames.size()); + #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL + apply_trace(trace, libdl::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF + apply_trace(trace, libdwarf::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP + apply_trace(trace, dbghelp::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE + apply_trace(trace, addr2line::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE + apply_trace(trace, libbacktrace::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING + apply_trace(trace, nothing::resolve_frames(frames)); + #endif + return trace; } } +} diff --git a/src/symbols/symbols_with_addr2line.cpp b/src/symbols/symbols_with_addr2line.cpp index 2b0a5d4..d345cbb 100644 --- a/src/symbols/symbols_with_addr2line.cpp +++ b/src/symbols/symbols_with_addr2line.cpp @@ -24,329 +24,329 @@ #include "../platform/object.hpp" namespace cpptrace { - namespace detail { - namespace addr2line { - #if IS_LINUX || IS_APPLE - bool has_addr2line() { - static std::mutex mutex; - static bool has_addr2line = false; - static bool checked = false; - std::lock_guard lock(mutex); - if(!checked) { - checked = true; - // Detects if addr2line exists by trying to invoke addr2line --help - constexpr int magic = 42; - // NOLINTNEXTLINE(misc-include-cleaner) - const pid_t pid = fork(); - if(pid == -1) { return false; } - if(pid == 0) { // child - close(STDOUT_FILENO); - close(STDERR_FILENO); // atos --help writes to stderr - #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH - #if !IS_APPLE - execlp("addr2line", "addr2line", "--help", nullptr); - #else - execlp("atos", "atos", "--help", nullptr); - #endif - #else - #ifndef CPPTRACE_ADDR2LINE_PATH - #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" - #endif - execl(CPPTRACE_ADDR2LINE_PATH, CPPTRACE_ADDR2LINE_PATH, "--help", nullptr); - #endif - _exit(magic); - } - int status; - waitpid(pid, &status, 0); - // NOLINTNEXTLINE(misc-include-cleaner) - has_addr2line = WEXITSTATUS(status) == 0; - } - return has_addr2line; - } - - struct pipe_t { - union { - struct { - int read_end; - int write_end; - }; - int data[2]; - }; - }; - static_assert(sizeof(pipe_t) == 2 * sizeof(int), "Unexpected struct packing"); - - std::string resolve_addresses(const std::string& addresses, const std::string& executable) { - pipe_t output_pipe; - pipe_t input_pipe; - CPPTRACE_VERIFY(pipe(output_pipe.data) == 0); - CPPTRACE_VERIFY(pipe(input_pipe.data) == 0); - // NOLINTNEXTLINE(misc-include-cleaner) - const pid_t pid = fork(); - if(pid == -1) { return ""; } // error? TODO: Diagnostic - if(pid == 0) { // child - dup2(output_pipe.write_end, STDOUT_FILENO); - dup2(input_pipe.read_end, STDIN_FILENO); - close(output_pipe.read_end); - close(output_pipe.write_end); - close(input_pipe.read_end); - close(input_pipe.write_end); - close(STDERR_FILENO); // TODO: Might be worth conditionally enabling or piping - #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH - #if !IS_APPLE - execlp("addr2line", "addr2line", "-e", executable.c_str(), "-f", "-C", "-p", nullptr); - #else - execlp("atos", "atos", "-o", executable.c_str(), "-fullPath", nullptr); - #endif - #else - #ifndef CPPTRACE_ADDR2LINE_PATH - #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" - #endif - #if !IS_APPLE - execl( - CPPTRACE_ADDR2LINE_PATH, - CPPTRACE_ADDR2LINE_PATH, - "-e", - executable.c_str(), - "-f", - "-C", - "-p", - nullptr - ); - #else - execl( - CPPTRACE_ADDR2LINE_PATH, - CPPTRACE_ADDR2LINE_PATH, - "-o", executable.c_str(), - "-fullPath", - nullptr - ); - #endif - #endif - _exit(1); // TODO: Diagnostic? - } - CPPTRACE_VERIFY(write(input_pipe.write_end, addresses.data(), addresses.size()) != -1); - close(input_pipe.read_end); - close(input_pipe.write_end); - close(output_pipe.write_end); - std::string output; - constexpr int buffer_size = 4096; - char buffer[buffer_size]; - size_t count = 0; - while((count = read(output_pipe.read_end, buffer, buffer_size)) > 0) { - output.insert(output.end(), buffer, buffer + count); - } - // TODO: check status from addr2line? - waitpid(pid, nullptr, 0); - return output; - } - #elif IS_WINDOWS - bool has_addr2line() { - static std::mutex mutex; - static bool has_addr2line = false; - static bool checked = false; - std::lock_guard lock(mutex); - if(!checked) { - // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. - checked = true; - #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH - FILE* p = popen("addr2line --version", "r"); - #else - #ifndef CPPTRACE_ADDR2LINE_PATH - #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" - #endif - FILE* p = popen(CPPTRACE_ADDR2LINE_PATH " --version", "r"); - #endif - if(p) { - has_addr2line = pclose(p) == 0; - } - } - return has_addr2line; - } - - std::string resolve_addresses(const std::string& addresses, const std::string& executable) { - // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. - ///fprintf(stderr, ("addr2line -e " + executable + " -fCp " + addresses + "\n").c_str()); +namespace detail { +namespace addr2line { + #if IS_LINUX || IS_APPLE + bool has_addr2line() { + static std::mutex mutex; + static bool has_addr2line = false; + static bool checked = false; + std::lock_guard lock(mutex); + if(!checked) { + checked = true; + // Detects if addr2line exists by trying to invoke addr2line --help + constexpr int magic = 42; + // NOLINTNEXTLINE(misc-include-cleaner) + const pid_t pid = fork(); + if(pid == -1) { return false; } + if(pid == 0) { // child + close(STDOUT_FILENO); + close(STDERR_FILENO); // atos --help writes to stderr #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH - FILE* p = popen(("addr2line -e \"" + executable + "\" -fCp " + addresses).c_str(), "r"); + #if !IS_APPLE + execlp("addr2line", "addr2line", "--help", nullptr); + #else + execlp("atos", "atos", "--help", nullptr); + #endif #else #ifndef CPPTRACE_ADDR2LINE_PATH #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" #endif - FILE* p = popen( - (CPPTRACE_ADDR2LINE_PATH " -e \"" + executable + "\" -fCp " + addresses).c_str(), - "r" - ); + execl(CPPTRACE_ADDR2LINE_PATH, CPPTRACE_ADDR2LINE_PATH, "--help", nullptr); #endif - std::string output; - constexpr int buffer_size = 4096; - char buffer[buffer_size]; - size_t count = 0; - while((count = fread(buffer, 1, buffer_size, p)) > 0) { - output.insert(output.end(), buffer, buffer + count); - } - pclose(p); - ///fprintf(stderr, "%s\n", output.c_str()); - return output; + _exit(magic); } + int status; + waitpid(pid, &status, 0); + // NOLINTNEXTLINE(misc-include-cleaner) + has_addr2line = WEXITSTATUS(status) == 0; + } + return has_addr2line; + } + + struct pipe_t { + union { + struct { + int read_end; + int write_end; + }; + int data[2]; + }; + }; + static_assert(sizeof(pipe_t) == 2 * sizeof(int), "Unexpected struct packing"); + + std::string resolve_addresses(const std::string& addresses, const std::string& executable) { + pipe_t output_pipe; + pipe_t input_pipe; + CPPTRACE_VERIFY(pipe(output_pipe.data) == 0); + CPPTRACE_VERIFY(pipe(input_pipe.data) == 0); + // NOLINTNEXTLINE(misc-include-cleaner) + const pid_t pid = fork(); + if(pid == -1) { return ""; } // error? TODO: Diagnostic + if(pid == 0) { // child + dup2(output_pipe.write_end, STDOUT_FILENO); + dup2(input_pipe.read_end, STDIN_FILENO); + close(output_pipe.read_end); + close(output_pipe.write_end); + close(input_pipe.read_end); + close(input_pipe.write_end); + close(STDERR_FILENO); // TODO: Might be worth conditionally enabling or piping + #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH + #if !IS_APPLE + execlp("addr2line", "addr2line", "-e", executable.c_str(), "-f", "-C", "-p", nullptr); + #else + execlp("atos", "atos", "-o", executable.c_str(), "-fullPath", nullptr); #endif - - using target_vec = std::vector>>; - - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - std::unordered_map get_addr2line_targets( - const std::vector& dlframes, - std::vector& trace - ) { - std::unordered_map entries; - for(std::size_t i = 0; i < dlframes.size(); i++) { - const auto& entry = dlframes[i]; - // If libdl fails to find the shared object for a frame, the path will be empty. I've observed this - // on macos when looking up the shared object containing `start`. - if(!entry.obj_path.empty()) { - ///fprintf( - /// stderr, - /// "%s %s\n", - /// to_hex(entry.raw_address).c_str(), - /// to_hex(entry.raw_address - entry.obj_base + base).c_str() - ///); - try { - entries[entry.obj_path].emplace_back( - to_hex(entry.obj_address), - trace[i] - ); - } catch(file_error&) { - // - } catch(...) { - throw; - } - // Set what is known for now, and resolutions from addr2line should overwrite - trace[i].filename = entry.obj_path; - trace[i].symbol = entry.symbol; - } - } - return entries; - } - - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - void update_trace(const std::string& line, size_t entry_index, const target_vec& entries_vec) { - #if !IS_APPLE - // Result will be of the form " at path:line" - // The path may be ?? if addr2line cannot resolve, line may be ? - // Edge cases: - // ?? ??:0 - // symbol :? - const std::size_t at_location = line.find(" at "); - std::size_t symbol_end; - std::size_t filename_start; - if(at_location != std::string::npos) { - symbol_end = at_location; - filename_start = at_location + 4; - } else { - CPPTRACE_VERIFY(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output"); - symbol_end = 2; - filename_start = 3; - } - auto symbol = line.substr(0, symbol_end); - auto colon = line.rfind(':'); - CPPTRACE_VERIFY(colon != std::string::npos); - CPPTRACE_VERIFY(colon >= filename_start); // :? to deal with "symbol :?" edge case - auto filename = line.substr(filename_start, colon - filename_start); - auto line_number = line.substr(colon + 1); - if(line_number != "?") { - entries_vec[entry_index].second.get().line = std::stoi(line_number); - } - if(!filename.empty() && filename != "??") { - entries_vec[entry_index].second.get().filename = filename; - } - if(!symbol.empty()) { - entries_vec[entry_index].second.get().symbol = symbol; - } - #else - // Result will be of the form " (in ) (file:line)" - // The symbol may just be the given address if atos can't resolve it - // Examples: - // trace() (in demo) (demo.cpp:8) - // 0x100003b70 (in demo) - // 0xffffffffffffffff - // foo (in bar) + 14 - // I'm making some assumptions here. Support may need to be improved later. This is tricky output to - // parse. - const std::size_t in_location = line.find(" (in "); - if(in_location == std::string::npos) { - // presumably the 0xffffffffffffffff case - return; - } - const std::size_t symbol_end = in_location; - entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end); - const std::size_t obj_end = line.find(")", in_location); - CPPTRACE_VERIFY( - obj_end != std::string::npos, - "Unexpected edge case while processing addr2line/atos output" - ); - const std::size_t filename_start = line.find(") (", obj_end); - if(filename_start == std::string::npos) { - // presumably something like 0x100003b70 (in demo) or foo (in bar) + 14 - return; - } - const std::size_t filename_end = line.find(":", filename_start); - CPPTRACE_VERIFY( - filename_end != std::string::npos, - "Unexpected edge case while processing addr2line/atos output" - ); - entries_vec[entry_index].second.get().filename = line.substr( - filename_start + 3, - filename_end - filename_start - 3 - ); - const std::size_t line_start = filename_end + 1; - const std::size_t line_end = line.find(")", filename_end); - CPPTRACE_VERIFY( - line_end == line.size() - 1, - "Unexpected edge case while processing addr2line/atos output" - ); - entries_vec[entry_index].second.get().line = std::stoi(line.substr(line_start, line_end - line_start)); - #endif - } - - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - std::vector resolve_frames(const std::vector& frames) { - // TODO: Refactor better - std::vector trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" }); - for(size_t i = 0; i < frames.size(); i++) { - trace[i].address = reinterpret_cast(frames[i]); - } - if(has_addr2line()) { - const std::vector dlframes = get_frames_object_info(frames); - const auto entries = get_addr2line_targets(dlframes, trace); - for(const auto& entry : entries) { - const auto& object_name = entry.first; - const auto& entries_vec = entry.second; - // You may ask why it'd ever happen that there could be an empty entries_vec array, if there're - // no addresses why would get_addr2line_targets do anything? The reason is because if things in - // get_addr2line_targets fail it will silently skip. This is partly an optimization but also an - // assertion below will fail if addr2line is given an empty input. - if(entries_vec.empty()) { - continue; - } - std::string address_input; - for(const auto& pair : entries_vec) { - address_input += pair.first; - #if !IS_WINDOWS - address_input += '\n'; - #else - address_input += ' '; - #endif - } - auto output = split(trim(resolve_addresses(address_input, object_name)), "\n"); - CPPTRACE_VERIFY(output.size() == entries_vec.size()); - for(size_t i = 0; i < output.size(); i++) { - update_trace(output[i], i, entries_vec); - } - } - } - return trace; + #else + #ifndef CPPTRACE_ADDR2LINE_PATH + #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" + #endif + #if !IS_APPLE + execl( + CPPTRACE_ADDR2LINE_PATH, + CPPTRACE_ADDR2LINE_PATH, + "-e", + executable.c_str(), + "-f", + "-C", + "-p", + nullptr + ); + #else + execl( + CPPTRACE_ADDR2LINE_PATH, + CPPTRACE_ADDR2LINE_PATH, + "-o", executable.c_str(), + "-fullPath", + nullptr + ); + #endif + #endif + _exit(1); // TODO: Diagnostic? + } + CPPTRACE_VERIFY(write(input_pipe.write_end, addresses.data(), addresses.size()) != -1); + close(input_pipe.read_end); + close(input_pipe.write_end); + close(output_pipe.write_end); + std::string output; + constexpr int buffer_size = 4096; + char buffer[buffer_size]; + size_t count = 0; + while((count = read(output_pipe.read_end, buffer, buffer_size)) > 0) { + output.insert(output.end(), buffer, buffer + count); + } + // TODO: check status from addr2line? + waitpid(pid, nullptr, 0); + return output; + } + #elif IS_WINDOWS + bool has_addr2line() { + static std::mutex mutex; + static bool has_addr2line = false; + static bool checked = false; + std::lock_guard lock(mutex); + if(!checked) { + // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. + checked = true; + #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH + FILE* p = popen("addr2line --version", "r"); + #else + #ifndef CPPTRACE_ADDR2LINE_PATH + #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" + #endif + FILE* p = popen(CPPTRACE_ADDR2LINE_PATH " --version", "r"); + #endif + if(p) { + has_addr2line = pclose(p) == 0; } } + return has_addr2line; } + + std::string resolve_addresses(const std::string& addresses, const std::string& executable) { + // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. + ///fprintf(stderr, ("addr2line -e " + executable + " -fCp " + addresses + "\n").c_str()); + #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH + FILE* p = popen(("addr2line -e \"" + executable + "\" -fCp " + addresses).c_str(), "r"); + #else + #ifndef CPPTRACE_ADDR2LINE_PATH + #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" + #endif + FILE* p = popen( + (CPPTRACE_ADDR2LINE_PATH " -e \"" + executable + "\" -fCp " + addresses).c_str(), + "r" + ); + #endif + std::string output; + constexpr int buffer_size = 4096; + char buffer[buffer_size]; + size_t count = 0; + while((count = fread(buffer, 1, buffer_size, p)) > 0) { + output.insert(output.end(), buffer, buffer + count); + } + pclose(p); + ///fprintf(stderr, "%s\n", output.c_str()); + return output; + } + #endif + + using target_vec = std::vector>>; + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + std::unordered_map get_addr2line_targets( + const std::vector& dlframes, + std::vector& trace + ) { + std::unordered_map entries; + for(std::size_t i = 0; i < dlframes.size(); i++) { + const auto& entry = dlframes[i]; + // If libdl fails to find the shared object for a frame, the path will be empty. I've observed this + // on macos when looking up the shared object containing `start`. + if(!entry.obj_path.empty()) { + ///fprintf( + /// stderr, + /// "%s %s\n", + /// to_hex(entry.raw_address).c_str(), + /// to_hex(entry.raw_address - entry.obj_base + base).c_str() + ///); + try { + entries[entry.obj_path].emplace_back( + to_hex(entry.obj_address), + trace[i] + ); + } catch(file_error&) { + // + } catch(...) { + throw; + } + // Set what is known for now, and resolutions from addr2line should overwrite + trace[i].filename = entry.obj_path; + trace[i].symbol = entry.symbol; + } + } + return entries; + } + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + void update_trace(const std::string& line, size_t entry_index, const target_vec& entries_vec) { + #if !IS_APPLE + // Result will be of the form " at path:line" + // The path may be ?? if addr2line cannot resolve, line may be ? + // Edge cases: + // ?? ??:0 + // symbol :? + const std::size_t at_location = line.find(" at "); + std::size_t symbol_end; + std::size_t filename_start; + if(at_location != std::string::npos) { + symbol_end = at_location; + filename_start = at_location + 4; + } else { + CPPTRACE_VERIFY(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output"); + symbol_end = 2; + filename_start = 3; + } + auto symbol = line.substr(0, symbol_end); + auto colon = line.rfind(':'); + CPPTRACE_VERIFY(colon != std::string::npos); + CPPTRACE_VERIFY(colon >= filename_start); // :? to deal with "symbol :?" edge case + auto filename = line.substr(filename_start, colon - filename_start); + auto line_number = line.substr(colon + 1); + if(line_number != "?") { + entries_vec[entry_index].second.get().line = std::stoi(line_number); + } + if(!filename.empty() && filename != "??") { + entries_vec[entry_index].second.get().filename = filename; + } + if(!symbol.empty()) { + entries_vec[entry_index].second.get().symbol = symbol; + } + #else + // Result will be of the form " (in ) (file:line)" + // The symbol may just be the given address if atos can't resolve it + // Examples: + // trace() (in demo) (demo.cpp:8) + // 0x100003b70 (in demo) + // 0xffffffffffffffff + // foo (in bar) + 14 + // I'm making some assumptions here. Support may need to be improved later. This is tricky output to + // parse. + const std::size_t in_location = line.find(" (in "); + if(in_location == std::string::npos) { + // presumably the 0xffffffffffffffff case + return; + } + const std::size_t symbol_end = in_location; + entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end); + const std::size_t obj_end = line.find(")", in_location); + CPPTRACE_VERIFY( + obj_end != std::string::npos, + "Unexpected edge case while processing addr2line/atos output" + ); + const std::size_t filename_start = line.find(") (", obj_end); + if(filename_start == std::string::npos) { + // presumably something like 0x100003b70 (in demo) or foo (in bar) + 14 + return; + } + const std::size_t filename_end = line.find(":", filename_start); + CPPTRACE_VERIFY( + filename_end != std::string::npos, + "Unexpected edge case while processing addr2line/atos output" + ); + entries_vec[entry_index].second.get().filename = line.substr( + filename_start + 3, + filename_end - filename_start - 3 + ); + const std::size_t line_start = filename_end + 1; + const std::size_t line_end = line.find(")", filename_end); + CPPTRACE_VERIFY( + line_end == line.size() - 1, + "Unexpected edge case while processing addr2line/atos output" + ); + entries_vec[entry_index].second.get().line = std::stoi(line.substr(line_start, line_end - line_start)); + #endif + } + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + std::vector resolve_frames(const std::vector& frames) { + // TODO: Refactor better + std::vector trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" }); + for(size_t i = 0; i < frames.size(); i++) { + trace[i].address = reinterpret_cast(frames[i]); + } + if(has_addr2line()) { + const std::vector dlframes = get_frames_object_info(frames); + const auto entries = get_addr2line_targets(dlframes, trace); + for(const auto& entry : entries) { + const auto& object_name = entry.first; + const auto& entries_vec = entry.second; + // You may ask why it'd ever happen that there could be an empty entries_vec array, if there're + // no addresses why would get_addr2line_targets do anything? The reason is because if things in + // get_addr2line_targets fail it will silently skip. This is partly an optimization but also an + // assertion below will fail if addr2line is given an empty input. + if(entries_vec.empty()) { + continue; + } + std::string address_input; + for(const auto& pair : entries_vec) { + address_input += pair.first; + #if !IS_WINDOWS + address_input += '\n'; + #else + address_input += ' '; + #endif + } + auto output = split(trim(resolve_addresses(address_input, object_name)), "\n"); + CPPTRACE_VERIFY(output.size() == entries_vec.size()); + for(size_t i = 0; i < output.size(); i++) { + update_trace(output[i], i, entries_vec); + } + } + } + return trace; + } +} +} } #endif diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index 3b1b102..08cf304 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -14,416 +14,416 @@ #include namespace cpptrace { - namespace detail { - namespace dbghelp { +namespace detail { +namespace dbghelp { - // SymFromAddr only returns the function's name. In order to get information about parameters, - // important for C++ stack traces where functions may be overloaded, we have to manually use - // Windows DIA to walk debug info structures. Resources: - // https://web.archive.org/web/20201027025750/http://www.debuginfo.com/articles/dbghelptypeinfo.html - // https://web.archive.org/web/20201203160805/http://www.debuginfo.com/articles/dbghelptypeinfofigures.html - // https://github.com/DynamoRIO/dynamorio/blob/master/ext/drsyms/drsyms_windows.c#L1370-L1439 - // TODO: Currently unable to detect rvalue references - // TODO: Currently unable to detect const - enum class SymTagEnum { - SymTagNull, SymTagExe, SymTagCompiland, SymTagCompilandDetails, SymTagCompilandEnv, - SymTagFunction, SymTagBlock, SymTagData, SymTagAnnotation, SymTagLabel, SymTagPublicSymbol, - SymTagUDT, SymTagEnum, SymTagFunctionType, SymTagPointerType, SymTagArrayType, - SymTagBaseType, SymTagTypedef, SymTagBaseClass, SymTagFriend, SymTagFunctionArgType, - SymTagFuncDebugStart, SymTagFuncDebugEnd, SymTagUsingNamespace, SymTagVTableShape, - SymTagVTable, SymTagCustom, SymTagThunk, SymTagCustomType, SymTagManagedType, - SymTagDimension, SymTagCallSite, SymTagInlineSite, SymTagBaseInterface, SymTagVectorType, - SymTagMatrixType, SymTagHLSLType, SymTagCaller, SymTagCallee, SymTagExport, - SymTagHeapAllocationSite, SymTagCoffGroup, SymTagMax - }; + // SymFromAddr only returns the function's name. In order to get information about parameters, + // important for C++ stack traces where functions may be overloaded, we have to manually use + // Windows DIA to walk debug info structures. Resources: + // https://web.archive.org/web/20201027025750/http://www.debuginfo.com/articles/dbghelptypeinfo.html + // https://web.archive.org/web/20201203160805/http://www.debuginfo.com/articles/dbghelptypeinfofigures.html + // https://github.com/DynamoRIO/dynamorio/blob/master/ext/drsyms/drsyms_windows.c#L1370-L1439 + // TODO: Currently unable to detect rvalue references + // TODO: Currently unable to detect const + enum class SymTagEnum { + SymTagNull, SymTagExe, SymTagCompiland, SymTagCompilandDetails, SymTagCompilandEnv, + SymTagFunction, SymTagBlock, SymTagData, SymTagAnnotation, SymTagLabel, SymTagPublicSymbol, + SymTagUDT, SymTagEnum, SymTagFunctionType, SymTagPointerType, SymTagArrayType, + SymTagBaseType, SymTagTypedef, SymTagBaseClass, SymTagFriend, SymTagFunctionArgType, + SymTagFuncDebugStart, SymTagFuncDebugEnd, SymTagUsingNamespace, SymTagVTableShape, + SymTagVTable, SymTagCustom, SymTagThunk, SymTagCustomType, SymTagManagedType, + SymTagDimension, SymTagCallSite, SymTagInlineSite, SymTagBaseInterface, SymTagVectorType, + SymTagMatrixType, SymTagHLSLType, SymTagCaller, SymTagCallee, SymTagExport, + SymTagHeapAllocationSite, SymTagCoffGroup, SymTagMax + }; - enum class IMAGEHLP_SYMBOL_TYPE_INFO { - TI_GET_SYMTAG, TI_GET_SYMNAME, TI_GET_LENGTH, TI_GET_TYPE, TI_GET_TYPEID, TI_GET_BASETYPE, - TI_GET_ARRAYINDEXTYPEID, TI_FINDCHILDREN, TI_GET_DATAKIND, TI_GET_ADDRESSOFFSET, - TI_GET_OFFSET, TI_GET_VALUE, TI_GET_COUNT, TI_GET_CHILDRENCOUNT, TI_GET_BITPOSITION, - TI_GET_VIRTUALBASECLASS, TI_GET_VIRTUALTABLESHAPEID, TI_GET_VIRTUALBASEPOINTEROFFSET, - TI_GET_CLASSPARENTID, TI_GET_NESTED, TI_GET_SYMINDEX, TI_GET_LEXICALPARENT, TI_GET_ADDRESS, - TI_GET_THISADJUST, TI_GET_UDTKIND, TI_IS_EQUIV_TO, TI_GET_CALLING_CONVENTION, - TI_IS_CLOSE_EQUIV_TO, TI_GTIEX_REQS_VALID, TI_GET_VIRTUALBASEOFFSET, - TI_GET_VIRTUALBASEDISPINDEX, TI_GET_IS_REFERENCE, TI_GET_INDIRECTVIRTUALBASECLASS, - TI_GET_VIRTUALBASETABLETYPE, TI_GET_OBJECTPOINTERTYPE, IMAGEHLP_SYMBOL_TYPE_INFO_MAX - }; + enum class IMAGEHLP_SYMBOL_TYPE_INFO { + TI_GET_SYMTAG, TI_GET_SYMNAME, TI_GET_LENGTH, TI_GET_TYPE, TI_GET_TYPEID, TI_GET_BASETYPE, + TI_GET_ARRAYINDEXTYPEID, TI_FINDCHILDREN, TI_GET_DATAKIND, TI_GET_ADDRESSOFFSET, + TI_GET_OFFSET, TI_GET_VALUE, TI_GET_COUNT, TI_GET_CHILDRENCOUNT, TI_GET_BITPOSITION, + TI_GET_VIRTUALBASECLASS, TI_GET_VIRTUALTABLESHAPEID, TI_GET_VIRTUALBASEPOINTEROFFSET, + TI_GET_CLASSPARENTID, TI_GET_NESTED, TI_GET_SYMINDEX, TI_GET_LEXICALPARENT, TI_GET_ADDRESS, + TI_GET_THISADJUST, TI_GET_UDTKIND, TI_IS_EQUIV_TO, TI_GET_CALLING_CONVENTION, + TI_IS_CLOSE_EQUIV_TO, TI_GTIEX_REQS_VALID, TI_GET_VIRTUALBASEOFFSET, + TI_GET_VIRTUALBASEDISPINDEX, TI_GET_IS_REFERENCE, TI_GET_INDIRECTVIRTUALBASECLASS, + TI_GET_VIRTUALBASETABLETYPE, TI_GET_OBJECTPOINTERTYPE, IMAGEHLP_SYMBOL_TYPE_INFO_MAX + }; - enum class BasicType { - btNoType = 0, btVoid = 1, btChar = 2, btWChar = 3, btInt = 6, btUInt = 7, btFloat = 8, - btBCD = 9, btBool = 10, btLong = 13, btULong = 14, btCurrency = 25, btDate = 26, - btVariant = 27, btComplex = 28, btBit = 29, btBSTR = 30, btHresult = 31 - }; + enum class BasicType { + btNoType = 0, btVoid = 1, btChar = 2, btWChar = 3, btInt = 6, btUInt = 7, btFloat = 8, + btBCD = 9, btBool = 10, btLong = 13, btULong = 14, btCurrency = 25, btDate = 26, + btVariant = 27, btComplex = 28, btBit = 29, btBSTR = 30, btHresult = 31 + }; - // SymGetTypeInfo utility - template - T get_info(ULONG type_index, HANDLE proc, ULONG64 modbase) { - T info; - if( - !SymGetTypeInfo( - proc, - modbase, - type_index, - static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType), - &info - ) - ) { - if(FAILABLE) { - return (T)-1; - } else { - throw std::logic_error( - std::string("SymGetTypeInfo failed: ") - + std::system_error(GetLastError(), std::system_category()).what() - ); - } - } - return info; - } - - template - std::string get_info_wchar(ULONG type_index, HANDLE proc, ULONG64 modbase) { - WCHAR* info; - if( - !SymGetTypeInfo(proc, modbase, type_index, static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType), &info) - ) { - throw std::logic_error( - std::string("SymGetTypeInfo failed: ") - + std::system_error(GetLastError(), std::system_category()).what() - ); - } - // special case to properly free a buffer and convert string to narrow chars, only used for - // TI_GET_SYMNAME - static_assert( - SymType == IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_SYMNAME, - "get_info_wchar called with unexpected IMAGEHLP_SYMBOL_TYPE_INFO" + // SymGetTypeInfo utility + template + T get_info(ULONG type_index, HANDLE proc, ULONG64 modbase) { + T info; + if( + !SymGetTypeInfo( + proc, + modbase, + type_index, + static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType), + &info + ) + ) { + if(FAILABLE) { + return (T)-1; + } else { + throw std::logic_error( + std::string("SymGetTypeInfo failed: ") + + std::system_error(GetLastError(), std::system_category()).what() ); - std::wstring wstr(info); - std::string str; - str.reserve(wstr.size()); - for(const auto c : wstr) { - str.push_back(static_cast(c)); - } - LocalFree(info); - return str; } + } + return info; + } - // Translate basic types to string - static std::string get_basic_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { - auto basic_type = get_info( + template + std::string get_info_wchar(ULONG type_index, HANDLE proc, ULONG64 modbase) { + WCHAR* info; + if( + !SymGetTypeInfo(proc, modbase, type_index, static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType), &info) + ) { + throw std::logic_error( + std::string("SymGetTypeInfo failed: ") + + std::system_error(GetLastError(), std::system_category()).what() + ); + } + // special case to properly free a buffer and convert string to narrow chars, only used for + // TI_GET_SYMNAME + static_assert( + SymType == IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_SYMNAME, + "get_info_wchar called with unexpected IMAGEHLP_SYMBOL_TYPE_INFO" + ); + std::wstring wstr(info); + std::string str; + str.reserve(wstr.size()); + for(const auto c : wstr) { + str.push_back(static_cast(c)); + } + LocalFree(info); + return str; + } + + // Translate basic types to string + static std::string get_basic_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { + auto basic_type = get_info( + type_index, + proc, + modbase + ); + //auto length = get_info(type_index, proc, modbase); + switch(basic_type) { + case BasicType::btNoType: + return ""; + case BasicType::btVoid: + return "void"; + case BasicType::btChar: + return "char"; + case BasicType::btWChar: + return "wchar_t"; + case BasicType::btInt: + return "int"; + case BasicType::btUInt: + return "unsigned int"; + case BasicType::btFloat: + return "float"; + case BasicType::btBool: + return "bool"; + case BasicType::btLong: + return "long"; + case BasicType::btULong: + return "unsigned long"; + default: + return ""; + } + } + + static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase); + + struct class_name_result { + bool has_class_name; + std::string name; + }; + // Helper for member pointers + static class_name_result lookup_class_name(ULONG type_index, HANDLE proc, ULONG64 modbase) { + DWORD class_parent_id = get_info( + type_index, + proc, + modbase + ); + if(class_parent_id == (DWORD)-1) { + return {false, ""}; + } else { + return {true, resolve_type(class_parent_id, proc, modbase)}; + } + } + + struct type_result { + std::string base; + std::string extent; + }; + // Resolve more complex types + // returns [base, extent] + static type_result lookup_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { + auto tag = get_info(type_index, proc, modbase); + switch(tag) { + case SymTagEnum::SymTagBaseType: + return {get_basic_type(type_index, proc, modbase), ""}; + case SymTagEnum::SymTagPointerType: { + DWORD underlying_type_id = get_info( type_index, proc, modbase ); - //auto length = get_info(type_index, proc, modbase); - switch(basic_type) { - case BasicType::btNoType: - return ""; - case BasicType::btVoid: - return "void"; - case BasicType::btChar: - return "char"; - case BasicType::btWChar: - return "wchar_t"; - case BasicType::btInt: - return "int"; - case BasicType::btUInt: - return "unsigned int"; - case BasicType::btFloat: - return "float"; - case BasicType::btBool: - return "bool"; - case BasicType::btLong: - return "long"; - case BasicType::btULong: - return "unsigned long"; - default: - return ""; + bool is_ref = get_info( + type_index, + proc, + modbase + ); + std::string pp = is_ref ? "&" : "*"; // pointer punctuator + auto class_name_res = lookup_class_name(type_index, proc, modbase); + if(class_name_res.has_class_name) { + pp = class_name_res.name + "::" + pp; + } + const auto type = lookup_type(underlying_type_id, proc, modbase); + if(type.extent.empty()) { + return {type.base + (pp.size() > 1 ? " " : "") + pp, ""}; + } else { + return {type.base + "(" + pp, ")" + type.extent}; } } - - static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase); - - struct class_name_result { - bool has_class_name; - std::string name; - }; - // Helper for member pointers - static class_name_result lookup_class_name(ULONG type_index, HANDLE proc, ULONG64 modbase) { + case SymTagEnum::SymTagArrayType: { + DWORD underlying_type_id = get_info( + type_index, + proc, + modbase + ); + DWORD length = get_info( + type_index, + proc, + modbase + ); + const auto type = lookup_type(underlying_type_id, proc, modbase); + return {type.base, "[" + std::to_string(length) + "]" + type.extent}; + } + case SymTagEnum::SymTagFunctionType: { + DWORD return_type_id = get_info( + type_index, + proc, + modbase + ); + DWORD n_children = get_info( + type_index, + proc, + modbase + ); DWORD class_parent_id = get_info( type_index, proc, modbase ); - if(class_parent_id == (DWORD)-1) { - return {false, ""}; + int n_ignore = class_parent_id != (DWORD)-1; // ignore this param + // this must be ignored before TI_FINDCHILDREN_PARAMS::Count is set, else error + n_children -= n_ignore; + // return type + const auto return_type = lookup_type(return_type_id, proc, modbase); + if(n_children == 0) { + return {return_type.base, "()" + return_type.extent}; } else { - return {true, resolve_type(class_parent_id, proc, modbase)}; + // alignment should be fine + size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) + + (n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]); + TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz]; + children->Start = 0; + children->Count = n_children; + if( + !SymGetTypeInfo( + proc, modbase, type_index, + static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>( + IMAGEHLP_SYMBOL_TYPE_INFO::TI_FINDCHILDREN + ), + children + ) + ) { + throw std::logic_error( + std::string("SymGetTypeInfo failed: ") + + std::system_error(GetLastError(), std::system_category()).what() + ); + } + // get children type + std::string extent = "("; + if(children->Start != 0) { + throw std::logic_error("Error: children->Start == 0"); + } + for(std::size_t i = 0; i < n_children; i++) { + extent += (i == 0 ? "" : ", ") + resolve_type(children->ChildId[i], proc, modbase); + } + extent += ")"; + delete[] (char*) children; + return {return_type.base, extent + return_type.extent}; } } - - struct type_result { - std::string base; - std::string extent; - }; - // Resolve more complex types - // returns [base, extent] - static type_result lookup_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { - auto tag = get_info(type_index, proc, modbase); - switch(tag) { - case SymTagEnum::SymTagBaseType: - return {get_basic_type(type_index, proc, modbase), ""}; - case SymTagEnum::SymTagPointerType: { - DWORD underlying_type_id = get_info( - type_index, - proc, - modbase - ); - bool is_ref = get_info( - type_index, - proc, - modbase - ); - std::string pp = is_ref ? "&" : "*"; // pointer punctuator - auto class_name_res = lookup_class_name(type_index, proc, modbase); - if(class_name_res.has_class_name) { - pp = class_name_res.name + "::" + pp; - } - const auto type = lookup_type(underlying_type_id, proc, modbase); - if(type.extent.empty()) { - return {type.base + (pp.size() > 1 ? " " : "") + pp, ""}; - } else { - return {type.base + "(" + pp, ")" + type.extent}; - } - } - case SymTagEnum::SymTagArrayType: { - DWORD underlying_type_id = get_info( - type_index, - proc, - modbase - ); - DWORD length = get_info( - type_index, - proc, - modbase - ); - const auto type = lookup_type(underlying_type_id, proc, modbase); - return {type.base, "[" + std::to_string(length) + "]" + type.extent}; - } - case SymTagEnum::SymTagFunctionType: { - DWORD return_type_id = get_info( - type_index, - proc, - modbase - ); - DWORD n_children = get_info( - type_index, - proc, - modbase - ); - DWORD class_parent_id = get_info( - type_index, - proc, - modbase - ); - int n_ignore = class_parent_id != (DWORD)-1; // ignore this param - // this must be ignored before TI_FINDCHILDREN_PARAMS::Count is set, else error - n_children -= n_ignore; - // return type - const auto return_type = lookup_type(return_type_id, proc, modbase); - if(n_children == 0) { - return {return_type.base, "()" + return_type.extent}; - } else { - // alignment should be fine - size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) + - (n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]); - TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz]; - children->Start = 0; - children->Count = n_children; - if( - !SymGetTypeInfo( - proc, modbase, type_index, - static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>( - IMAGEHLP_SYMBOL_TYPE_INFO::TI_FINDCHILDREN - ), - children - ) - ) { - throw std::logic_error( - std::string("SymGetTypeInfo failed: ") - + std::system_error(GetLastError(), std::system_category()).what() - ); - } - // get children type - std::string extent = "("; - if(children->Start != 0) { - throw std::logic_error("Error: children->Start == 0"); - } - for(std::size_t i = 0; i < n_children; i++) { - extent += (i == 0 ? "" : ", ") + resolve_type(children->ChildId[i], proc, modbase); - } - extent += ")"; - delete[] (char*) children; - return {return_type.base, extent + return_type.extent}; - } - } - case SymTagEnum::SymTagFunctionArgType: { - DWORD underlying_type_id = - get_info(type_index, proc, modbase); - return {resolve_type(underlying_type_id, proc, modbase), ""}; - } - case SymTagEnum::SymTagTypedef: - case SymTagEnum::SymTagEnum: - case SymTagEnum::SymTagUDT: - case SymTagEnum::SymTagBaseClass: - return { - get_info_wchar(type_index, proc, modbase), "" - }; - default: - return { - "::type>(tag)) + - ">", - "" - }; + case SymTagEnum::SymTagFunctionArgType: { + DWORD underlying_type_id = + get_info(type_index, proc, modbase); + return {resolve_type(underlying_type_id, proc, modbase), ""}; + } + case SymTagEnum::SymTagTypedef: + case SymTagEnum::SymTagEnum: + case SymTagEnum::SymTagUDT: + case SymTagEnum::SymTagBaseClass: + return { + get_info_wchar(type_index, proc, modbase), "" }; - } + default: + return { + "::type>(tag)) + + ">", + "" + }; + }; + } - static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { - const auto type = lookup_type(type_index, proc, modbase); - return type.base + type.extent; - } + static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { + const auto type = lookup_type(type_index, proc, modbase); + return type.base + type.extent; + } - struct function_info { - HANDLE proc; - ULONG64 modbase; - int counter; - int n_children; - int n_ignore; - std::string str; - }; + struct function_info { + HANDLE proc; + ULONG64 modbase; + int counter; + int n_children; + int n_ignore; + std::string str; + }; - // Enumerates function parameters - static BOOL __stdcall enumerator_callback( - PSYMBOL_INFO symbol_info, - ULONG, - PVOID data - ) { - function_info* ctx = (function_info*)data; - if(ctx->counter++ >= ctx->n_children) { - return false; - } - if(ctx->n_ignore-- > 0) { - return true; // just skip - } - ctx->str += resolve_type(symbol_info->TypeIndex, ctx->proc, ctx->modbase); - if(ctx->counter < ctx->n_children) { - ctx->str += ", "; - } - return true; - } + // Enumerates function parameters + static BOOL __stdcall enumerator_callback( + PSYMBOL_INFO symbol_info, + ULONG, + PVOID data + ) { + function_info* ctx = (function_info*)data; + if(ctx->counter++ >= ctx->n_children) { + return false; + } + if(ctx->n_ignore-- > 0) { + return true; // just skip + } + ctx->str += resolve_type(symbol_info->TypeIndex, ctx->proc, ctx->modbase); + if(ctx->counter < ctx->n_children) { + ctx->str += ", "; + } + return true; + } - std::mutex dbghelp_lock; + std::mutex dbghelp_lock; - // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions - stacktrace_frame resolve_frame(HANDLE proc, void* addr) { - const std::lock_guard lock(dbghelp_lock); // all dbghelp functions are not thread safe - alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; - SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer; - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = MAX_SYM_NAME; - union { DWORD64 a; DWORD b; } displacement; - IMAGEHLP_LINE64 line; - bool got_line = SymGetLineFromAddr64(proc, (DWORD64)addr, &displacement.b, &line); - if(SymFromAddr(proc, (DWORD64)addr, &displacement.a, symbol)) { - if(got_line) { - IMAGEHLP_STACK_FRAME frame; - frame.InstructionOffset = symbol->Address; - // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetcontext - // "If you call SymSetContext to set the context to its current value, the - // function fails but GetLastError returns ERROR_SUCCESS." - // This is the stupidest fucking api I've ever worked with. - if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) { - fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n"); - return { - reinterpret_cast(addr), - static_cast(line.LineNumber), - 0, - line.FileName, - symbol->Name - }; - } - DWORD n_children = get_info( - symbol->TypeIndex, - proc, - symbol->ModBase - ); - DWORD class_parent_id = get_info( - symbol->TypeIndex, - proc, - symbol->ModBase - ); - function_info fi { - proc, - symbol->ModBase, - 0, - int(n_children), - class_parent_id != (DWORD)-1, - "" - }; - SymEnumSymbols(proc, 0, nullptr, enumerator_callback, &fi); - std::string signature = symbol->Name + std::string("(") + fi.str + ")"; - // There's a phenomina with DIA not inserting commas after template parameters. Fix them here. - static std::regex comma_re(R"(,(?=\S))"); - signature = std::regex_replace(signature, comma_re, ", "); - return { - reinterpret_cast(addr), - static_cast(line.LineNumber), - 0, - line.FileName, - signature - }; - } else { - return { - reinterpret_cast(addr), - 0, - 0, - "", - symbol->Name - }; - } - } else { + // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions + stacktrace_frame resolve_frame(HANDLE proc, void* addr) { + const std::lock_guard lock(dbghelp_lock); // all dbghelp functions are not thread safe + alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + union { DWORD64 a; DWORD b; } displacement; + IMAGEHLP_LINE64 line; + bool got_line = SymGetLineFromAddr64(proc, (DWORD64)addr, &displacement.b, &line); + if(SymFromAddr(proc, (DWORD64)addr, &displacement.a, symbol)) { + if(got_line) { + IMAGEHLP_STACK_FRAME frame; + frame.InstructionOffset = symbol->Address; + // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetcontext + // "If you call SymSetContext to set the context to its current value, the + // function fails but GetLastError returns ERROR_SUCCESS." + // This is the stupidest fucking api I've ever worked with. + if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) { + fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n"); return { reinterpret_cast(addr), + static_cast(line.LineNumber), 0, - 0, - "", - "" + line.FileName, + symbol->Name }; } + DWORD n_children = get_info( + symbol->TypeIndex, + proc, + symbol->ModBase + ); + DWORD class_parent_id = get_info( + symbol->TypeIndex, + proc, + symbol->ModBase + ); + function_info fi { + proc, + symbol->ModBase, + 0, + int(n_children), + class_parent_id != (DWORD)-1, + "" + }; + SymEnumSymbols(proc, 0, nullptr, enumerator_callback, &fi); + std::string signature = symbol->Name + std::string("(") + fi.str + ")"; + // There's a phenomina with DIA not inserting commas after template parameters. Fix them here. + static std::regex comma_re(R"(,(?=\S))"); + signature = std::regex_replace(signature, comma_re, ", "); + return { + reinterpret_cast(addr), + static_cast(line.LineNumber), + 0, + line.FileName, + signature + }; + } else { + return { + reinterpret_cast(addr), + 0, + 0, + "", + symbol->Name + }; } - - std::vector resolve_frames(const std::vector& frames) { - std::vector trace; - trace.reserve(frames.size()); - - // TODO: When does this need to be called? Can it be moved to the symbolizer? - SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS); - HANDLE proc = GetCurrentProcess(); - if(!SymInitialize(proc, NULL, TRUE)) { - //TODO? - throw std::logic_error("SymInitialize failed"); - } - for(const auto frame : frames) { - trace.push_back(resolve_frame(proc, frame)); - } - if(!SymCleanup(proc)) { - //throw std::logic_error("SymCleanup failed"); - } - - return trace; - } + } else { + return { + reinterpret_cast(addr), + 0, + 0, + "", + "" + }; } } + + std::vector resolve_frames(const std::vector& frames) { + std::vector trace; + trace.reserve(frames.size()); + + // TODO: When does this need to be called? Can it be moved to the symbolizer? + SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS); + HANDLE proc = GetCurrentProcess(); + if(!SymInitialize(proc, NULL, TRUE)) { + //TODO? + throw std::logic_error("SymInitialize failed"); + } + for(const auto frame : frames) { + trace.push_back(resolve_frame(proc, frame)); + } + if(!SymCleanup(proc)) { + //throw std::logic_error("SymCleanup failed"); + } + + return trace; + } +} +} } #endif diff --git a/src/symbols/symbols_with_dl.cpp b/src/symbols/symbols_with_dl.cpp index 1b4534f..352a19a 100644 --- a/src/symbols/symbols_with_dl.cpp +++ b/src/symbols/symbols_with_dl.cpp @@ -10,39 +10,39 @@ #include namespace cpptrace { - namespace detail { - namespace libdl { - stacktrace_frame resolve_frame(const void* addr) { - Dl_info info; - if(dladdr(addr, &info)) { // thread-safe - return { - reinterpret_cast(addr), - 0, - 0, - info.dli_fname ? info.dli_fname : "", - info.dli_sname ? info.dli_sname : "" - }; - } else { - return { - reinterpret_cast(addr), - 0, - 0, - "", - "" - }; - } - } - - std::vector resolve_frames(const std::vector& frames) { - std::vector trace; - trace.reserve(frames.size()); - for(const void* frame : frames) { - trace.push_back(resolve_frame(frame)); - } - return trace; - } +namespace detail { +namespace libdl { + stacktrace_frame resolve_frame(const void* addr) { + Dl_info info; + if(dladdr(addr, &info)) { // thread-safe + return { + reinterpret_cast(addr), + 0, + 0, + info.dli_fname ? info.dli_fname : "", + info.dli_sname ? info.dli_sname : "" + }; + } else { + return { + reinterpret_cast(addr), + 0, + 0, + "", + "" + }; } } + + std::vector resolve_frames(const std::vector& frames) { + std::vector trace; + trace.reserve(frames.size()); + for(const void* frame : frames) { + trace.push_back(resolve_frame(frame)); + } + return trace; + } +} +} } #endif diff --git a/src/symbols/symbols_with_libbacktrace.cpp b/src/symbols/symbols_with_libbacktrace.cpp index 976d8a8..1d6f092 100644 --- a/src/symbols/symbols_with_libbacktrace.cpp +++ b/src/symbols/symbols_with_libbacktrace.cpp @@ -17,80 +17,80 @@ #endif namespace cpptrace { - namespace detail { - namespace libbacktrace { - int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { - stacktrace_frame& frame = *static_cast(data); - if(line == 0) { - ///fprintf(stderr, "Getting bad data for some reason\n"); // TODO: Eliminate - } - frame.address = address; - frame.line = line; - frame.filename = file ? file : ""; - frame.symbol = symbol ? symbol : ""; - return 0; - } - - void syminfo_callback(void* data, uintptr_t address, const char* symbol, uintptr_t, uintptr_t) { - stacktrace_frame& frame = *static_cast(data); - frame.address = address; - frame.line = 0; - frame.filename = ""; - frame.symbol = symbol ? symbol : ""; - } - - void error_callback(void*, const char* msg, int errnum) { - fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum); - } - - backtrace_state* get_backtrace_state() { - static std::mutex mutex; - const std::lock_guard lock(mutex); - // backtrace_create_state must be called only one time per program - // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - static backtrace_state* state = nullptr; - static bool called = false; - if(!called) { - state = backtrace_create_state(program_name(), true, error_callback, nullptr); - called = true; - } - return state; - } - - // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions - stacktrace_frame resolve_frame(const void* addr) { - stacktrace_frame frame; - frame.col = 0; - backtrace_pcinfo( - get_backtrace_state(), - reinterpret_cast(addr), - full_callback, - error_callback, - &frame - ); - if(frame.symbol.empty()) { - // fallback, try to at least recover the symbol name with backtrace_syminfo - backtrace_syminfo( - get_backtrace_state(), - reinterpret_cast(addr), - syminfo_callback, - error_callback, - &frame - ); - } - return frame; - } - - std::vector resolve_frames(const std::vector& frames) { - std::vector trace; - trace.reserve(frames.size()); - for(const void* frame : frames) { - trace.push_back(resolve_frame(frame)); - } - return trace; - } +namespace detail { +namespace libbacktrace { + int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { + stacktrace_frame& frame = *static_cast(data); + if(line == 0) { + ///fprintf(stderr, "Getting bad data for some reason\n"); // TODO: Eliminate } + frame.address = address; + frame.line = line; + frame.filename = file ? file : ""; + frame.symbol = symbol ? symbol : ""; + return 0; } + + void syminfo_callback(void* data, uintptr_t address, const char* symbol, uintptr_t, uintptr_t) { + stacktrace_frame& frame = *static_cast(data); + frame.address = address; + frame.line = 0; + frame.filename = ""; + frame.symbol = symbol ? symbol : ""; + } + + void error_callback(void*, const char* msg, int errnum) { + fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum); + } + + backtrace_state* get_backtrace_state() { + static std::mutex mutex; + const std::lock_guard lock(mutex); + // backtrace_create_state must be called only one time per program + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + static backtrace_state* state = nullptr; + static bool called = false; + if(!called) { + state = backtrace_create_state(program_name(), true, error_callback, nullptr); + called = true; + } + return state; + } + + // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions + stacktrace_frame resolve_frame(const void* addr) { + stacktrace_frame frame; + frame.col = 0; + backtrace_pcinfo( + get_backtrace_state(), + reinterpret_cast(addr), + full_callback, + error_callback, + &frame + ); + if(frame.symbol.empty()) { + // fallback, try to at least recover the symbol name with backtrace_syminfo + backtrace_syminfo( + get_backtrace_state(), + reinterpret_cast(addr), + syminfo_callback, + error_callback, + &frame + ); + } + return frame; + } + + std::vector resolve_frames(const std::vector& frames) { + std::vector trace; + trace.reserve(frames.size()); + for(const void* frame : frames) { + trace.push_back(resolve_frame(frame)); + } + return trace; + } +} +} } #endif diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index e067a04..d433619 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -200,891 +200,891 @@ static int dwarf4_ranges( } namespace cpptrace { - namespace detail { - namespace libdwarf { - // printbugging as we go - constexpr bool dump_dwarf = false; - constexpr bool trace_dwarf = false; +namespace detail { +namespace libdwarf { + // printbugging as we go + constexpr bool dump_dwarf = false; + constexpr bool trace_dwarf = false; - static void err_handler(Dwarf_Error err, Dwarf_Ptr errarg) { - printf("libdwarf error reading %s: %lu %s\n", "xx", (unsigned long)dwarf_errno(err), dwarf_errmsg(err)); - if(errarg) { - printf("Error: errarg is nonnull but it should be null\n"); + static void err_handler(Dwarf_Error err, Dwarf_Ptr errarg) { + printf("libdwarf error reading %s: %lu %s\n", "xx", (unsigned long)dwarf_errno(err), dwarf_errmsg(err)); + if(errarg) { + printf("Error: errarg is nonnull but it should be null\n"); + } + printf("Giving up"); + exit(1); + } + + static void print_line(Dwarf_Debug dbg, Dwarf_Line line, Dwarf_Addr pc, stacktrace_frame& frame) { + char what[] = "??"; + char * linesrc = what; + Dwarf_Unsigned lineno = 0; + + (void)pc; + + if(line) { + /* These never return DW_DLV_NO_ENTRY */ + dwarf_linesrc(line, &linesrc, nullptr); + dwarf_lineno(line, &lineno, nullptr); + } + if(dump_dwarf) { + printf("%s:%" DW_PR_DUu "\n", linesrc, lineno); + } + frame.line = static_cast(lineno); + frame.filename = linesrc; + if(line) { + dwarf_dealloc(dbg, linesrc, DW_DLA_STRING); + } + } + + static Dwarf_Bool pc_in_die(Dwarf_Debug dbg, Dwarf_Die die, int version, Dwarf_Addr pc) { + int ret; + Dwarf_Addr cu_lowpc = 0xffffffffffffffff; + Dwarf_Addr cu_highpc = 0; + enum Dwarf_Form_Class highpc_cls; + Dwarf_Addr lowest = 0xffffffffffffffff; + Dwarf_Addr highest = 0; + + ret = dwarf_lowpc(die, &cu_lowpc, nullptr); + if(ret == DW_DLV_OK) { + if(pc == cu_lowpc) { + return true; + } + ret = dwarf_highpc_b(die, &cu_highpc, + nullptr, &highpc_cls, nullptr); + if(ret == DW_DLV_OK) { + if(highpc_cls == DW_FORM_CLASS_CONSTANT) { + cu_highpc += cu_lowpc; } - printf("Giving up"); + if(pc >= cu_lowpc && pc < cu_highpc) { + return true; + } + } + } + if(version >= 5) { + ret = dwarf5_ranges(die, &lowest, &highest); + } else { + ret = dwarf4_ranges(dbg, die, cu_lowpc, &lowest, &highest); + } + if(pc >= lowest && pc < highest) { + return true; + } + return false; + } + + static_assert(std::is_pointer::value, "Dwarf_Die not a pointer"); + static_assert(std::is_pointer::value, "Dwarf_Debug not a pointer"); + + struct die_object { + Dwarf_Debug dbg = nullptr; + Dwarf_Die die = nullptr; + + die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) {} + + ~die_object() { + if(die) { + dwarf_dealloc(dbg, die, DW_DLA_DIE); + } + } + + die_object(const die_object&) = delete; + + die_object& operator=(const die_object&) = delete; + + die_object(die_object&& other) : dbg(other.dbg), die(other.die) { + other.die = nullptr; + } + + die_object& operator=(die_object&& other) { + dbg = other.dbg; + die = other.die; + other.die = nullptr; + return *this; + } + + die_object get_child() const { + Dwarf_Die child = nullptr; + int ret = dwarf_child( + die, + &child, + nullptr + ); + if(ret == DW_DLV_OK) { + return die_object(dbg, child); + } else if(ret == DW_DLV_NO_ENTRY) { + return die_object(dbg, 0); + } else { + fprintf(stderr, "Error\n"); exit(1); } + } - static void print_line(Dwarf_Debug dbg, Dwarf_Line line, Dwarf_Addr pc, stacktrace_frame& frame) { - char what[] = "??"; - char * linesrc = what; - Dwarf_Unsigned lineno = 0; - - (void)pc; - - if(line) { - /* These never return DW_DLV_NO_ENTRY */ - dwarf_linesrc(line, &linesrc, nullptr); - dwarf_lineno(line, &lineno, nullptr); - } - if(dump_dwarf) { - printf("%s:%" DW_PR_DUu "\n", linesrc, lineno); - } - frame.line = static_cast(lineno); - frame.filename = linesrc; - if(line) { - dwarf_dealloc(dbg, linesrc, DW_DLA_STRING); - } + die_object get_sibling() const { + Dwarf_Die sibling = 0; + int ret = dwarf_siblingof_b(dbg, die, true, &sibling, nullptr); + if(ret == DW_DLV_OK) { + return die_object(dbg, sibling); + } else if(ret == DW_DLV_NO_ENTRY) { + return die_object(dbg, 0); + } else { + fprintf(stderr, "Error\n"); + exit(1); } + } - static Dwarf_Bool pc_in_die(Dwarf_Debug dbg, Dwarf_Die die, int version, Dwarf_Addr pc) { - int ret; - Dwarf_Addr cu_lowpc = 0xffffffffffffffff; - Dwarf_Addr cu_highpc = 0; - enum Dwarf_Form_Class highpc_cls; - Dwarf_Addr lowest = 0xffffffffffffffff; - Dwarf_Addr highest = 0; + operator bool() const { + return die != nullptr; + } - ret = dwarf_lowpc(die, &cu_lowpc, nullptr); - if(ret == DW_DLV_OK) { - if(pc == cu_lowpc) { - return true; - } - ret = dwarf_highpc_b(die, &cu_highpc, - nullptr, &highpc_cls, nullptr); - if(ret == DW_DLV_OK) { - if(highpc_cls == DW_FORM_CLASS_CONSTANT) { - cu_highpc += cu_lowpc; - } - if(pc >= cu_lowpc && pc < cu_highpc) { - return true; - } - } - } - if(version >= 5) { - ret = dwarf5_ranges(die, &lowest, &highest); - } else { - ret = dwarf4_ranges(dbg, die, cu_lowpc, &lowest, &highest); - } - if(pc >= lowest && pc < highest) { - return true; - } + Dwarf_Die get() const { + return die; + } + + std::string get_name() const { + char empty[] = ""; + char* name = empty; + int ret = dwarf_diename(die, &name, nullptr); + std::string str; + if(ret != DW_DLV_NO_ENTRY) { + str = name; + dwarf_dealloc(dbg, name, DW_DLA_STRING); + } + return name; + } + + optional get_string_attribute(Dwarf_Half dw_attrnum) const { + Dwarf_Attribute attr; + int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr); + if(ret == DW_DLV_OK) { + char* raw_str; + std::string str; + ret = dwarf_formstring(attr, &raw_str, nullptr); + CPPTRACE_VERIFY(ret == DW_DLV_OK); + str = raw_str; + dwarf_dealloc(dbg, raw_str, DW_DLA_STRING); + dwarf_dealloc_attribute(attr); + return str; + } else { + return nullopt; + } + } + + bool has_attr(Dwarf_Half dw_attrnum) const { + Dwarf_Attribute attr; + int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr); + if(ret == DW_DLV_NO_ENTRY) { return false; + } else if(ret == DW_DLV_OK) { + // TODO: Better impl that doesn't require allocation....? + dwarf_dealloc_attribute(attr); + return true; + } else { + fprintf(stderr, "Error\n"); + exit(1); } + } - static_assert(std::is_pointer::value, "Dwarf_Die not a pointer"); - static_assert(std::is_pointer::value, "Dwarf_Debug not a pointer"); + Dwarf_Half get_tag() const { + Dwarf_Half tag = 0; + dwarf_tag(die, &tag, nullptr); + return tag; + } - struct die_object { - Dwarf_Debug dbg = nullptr; - Dwarf_Die die = nullptr; + const char* get_tag_name() const { + const char* tag_name; + dwarf_get_TAG_name(get_tag(), &tag_name); + return tag_name; + } - die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) {} + Dwarf_Off get_global_offset() const { + Dwarf_Off off; + int ret = dwarf_dieoffset(die, &off, nullptr); + CPPTRACE_VERIFY(ret == DW_DLV_OK); + return off; + } - ~die_object() { - if(die) { - dwarf_dealloc(dbg, die, DW_DLA_DIE); - } - } - - die_object(const die_object&) = delete; - - die_object& operator=(const die_object&) = delete; - - die_object(die_object&& other) : dbg(other.dbg), die(other.die) { - other.die = nullptr; - } - - die_object& operator=(die_object&& other) { - dbg = other.dbg; - die = other.die; - other.die = nullptr; - return *this; - } - - die_object get_child() const { - Dwarf_Die child = nullptr; - int ret = dwarf_child( - die, - &child, - nullptr - ); - if(ret == DW_DLV_OK) { - return die_object(dbg, child); - } else if(ret == DW_DLV_NO_ENTRY) { - return die_object(dbg, 0); - } else { - fprintf(stderr, "Error\n"); - exit(1); - } - } - - die_object get_sibling() const { - Dwarf_Die sibling = 0; - int ret = dwarf_siblingof_b(dbg, die, true, &sibling, nullptr); - if(ret == DW_DLV_OK) { - return die_object(dbg, sibling); - } else if(ret == DW_DLV_NO_ENTRY) { - return die_object(dbg, 0); - } else { - fprintf(stderr, "Error\n"); - exit(1); - } - } - - operator bool() const { - return die != nullptr; - } - - Dwarf_Die get() const { - return die; - } - - std::string get_name() const { - char empty[] = ""; - char* name = empty; - int ret = dwarf_diename(die, &name, nullptr); - std::string str; - if(ret != DW_DLV_NO_ENTRY) { - str = name; - dwarf_dealloc(dbg, name, DW_DLA_STRING); - } - return name; - } - - optional get_string_attribute(Dwarf_Half dw_attrnum) const { - Dwarf_Attribute attr; - int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr); - if(ret == DW_DLV_OK) { - char* raw_str; - std::string str; - ret = dwarf_formstring(attr, &raw_str, nullptr); + die_object resolve_reference_attribute(Dwarf_Half dw_attrnum) const { + Dwarf_Attribute attr; + int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr); + Dwarf_Half form = 0; + ret = dwarf_whatform(attr, &form, nullptr); + switch(form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + { + Dwarf_Off off = 0; + Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); + ret = dwarf_formref(attr, &off, &is_info, nullptr); + CPPTRACE_VERIFY(ret == DW_DLV_OK); + Dwarf_Off goff = 0; + ret = dwarf_convert_to_global_offset(attr, off, &goff, nullptr); + CPPTRACE_VERIFY(ret == DW_DLV_OK); + Dwarf_Die targ_die_a = 0; + ret = dwarf_offdie_b(dbg, goff, is_info, &targ_die_a, nullptr); CPPTRACE_VERIFY(ret == DW_DLV_OK); - str = raw_str; - dwarf_dealloc(dbg, raw_str, DW_DLA_STRING); dwarf_dealloc_attribute(attr); - return str; - } else { - return nullopt; + return die_object(dbg, targ_die_a); } - } + case DW_FORM_ref_addr: + { + Dwarf_Off off; + ret = dwarf_global_formref(attr, &off, nullptr); + int is_info_a = dwarf_get_die_infotypes_flag(die); + Dwarf_Die targ_die_a = 0; + ret = dwarf_offdie_b(dbg, off, is_info_a, &targ_die_a, nullptr); + CPPTRACE_VERIFY(ret == DW_DLV_OK); + dwarf_dealloc_attribute(attr); + return die_object(dbg, targ_die_a); + } + case DW_FORM_ref_sig8: + { + Dwarf_Sig8 signature; + ret = dwarf_formsig8(attr, &signature, nullptr); + CPPTRACE_VERIFY(ret == DW_DLV_OK); + Dwarf_Die targdie = 0; + Dwarf_Bool targ_is_info = false; + ret = dwarf_find_die_given_sig8(dbg, &signature, &targdie, &targ_is_info, nullptr); + CPPTRACE_VERIFY(ret == DW_DLV_OK); + dwarf_dealloc_attribute(attr); + return die_object(dbg, targdie); + } + default: + fprintf(stderr, "unknown form for attribute %d %d\n", dw_attrnum, form); + exit(1); + } + } + }; - bool has_attr(Dwarf_Half dw_attrnum) const { - Dwarf_Attribute attr; - int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr); - if(ret == DW_DLV_NO_ENTRY) { + // walk die list, callback is called on each die and should return true to + // continue traversal + void walk_die_list( + Dwarf_Debug dbg, + const die_object& die, + std::function fn + ) { + // TODO: Refactor so there is only one fn call + if(fn(dbg, die)) { + die_object current = die.get_sibling(); + while(current) { + if(fn(dbg, current)) { + current = current.get_sibling(); + } else { + break; + } + } + } + if(dump_dwarf) { + fprintf(stderr, "End walk_die_list\n"); + } + } + + // walk die list, recursing into children, callback is called on each die + // and should return true to continue traversal + // returns true if traversal should continue + bool walk_die_list_recursive( + Dwarf_Debug dbg, + const die_object& die, + std::function fn + ) { + bool continue_traversal = true; + walk_die_list( + dbg, + die, + [&fn, &continue_traversal](Dwarf_Debug dbg, const die_object& die) { + auto child = die.get_child(); + if(child) { + if(!walk_die_list_recursive(dbg, child, fn)) { + continue_traversal = false; return false; - } else if(ret == DW_DLV_OK) { - // TODO: Better impl that doesn't require allocation....? - dwarf_dealloc_attribute(attr); - return true; - } else { - fprintf(stderr, "Error\n"); - exit(1); } } - - Dwarf_Half get_tag() const { - Dwarf_Half tag = 0; - dwarf_tag(die, &tag, nullptr); - return tag; - } - - const char* get_tag_name() const { - const char* tag_name; - dwarf_get_TAG_name(get_tag(), &tag_name); - return tag_name; - } - - Dwarf_Off get_global_offset() const { - Dwarf_Off off; - int ret = dwarf_dieoffset(die, &off, nullptr); - CPPTRACE_VERIFY(ret == DW_DLV_OK); - return off; - } - - die_object resolve_reference_attribute(Dwarf_Half dw_attrnum) const { - Dwarf_Attribute attr; - int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr); - Dwarf_Half form = 0; - ret = dwarf_whatform(attr, &form, nullptr); - switch(form) { - case DW_FORM_ref1: - case DW_FORM_ref2: - case DW_FORM_ref4: - case DW_FORM_ref8: - case DW_FORM_ref_udata: - { - Dwarf_Off off = 0; - Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); - ret = dwarf_formref(attr, &off, &is_info, nullptr); - CPPTRACE_VERIFY(ret == DW_DLV_OK); - Dwarf_Off goff = 0; - ret = dwarf_convert_to_global_offset(attr, off, &goff, nullptr); - CPPTRACE_VERIFY(ret == DW_DLV_OK); - Dwarf_Die targ_die_a = 0; - ret = dwarf_offdie_b(dbg, goff, is_info, &targ_die_a, nullptr); - CPPTRACE_VERIFY(ret == DW_DLV_OK); - dwarf_dealloc_attribute(attr); - return die_object(dbg, targ_die_a); - } - case DW_FORM_ref_addr: - { - Dwarf_Off off; - ret = dwarf_global_formref(attr, &off, nullptr); - int is_info_a = dwarf_get_die_infotypes_flag(die); - Dwarf_Die targ_die_a = 0; - ret = dwarf_offdie_b(dbg, off, is_info_a, &targ_die_a, nullptr); - CPPTRACE_VERIFY(ret == DW_DLV_OK); - dwarf_dealloc_attribute(attr); - return die_object(dbg, targ_die_a); - } - case DW_FORM_ref_sig8: - { - Dwarf_Sig8 signature; - ret = dwarf_formsig8(attr, &signature, nullptr); - CPPTRACE_VERIFY(ret == DW_DLV_OK); - Dwarf_Die targdie = 0; - Dwarf_Bool targ_is_info = false; - ret = dwarf_find_die_given_sig8(dbg, &signature, &targdie, &targ_is_info, nullptr); - CPPTRACE_VERIFY(ret == DW_DLV_OK); - dwarf_dealloc_attribute(attr); - return die_object(dbg, targdie); - } - default: - fprintf(stderr, "unknown form for attribute %d %d\n", dw_attrnum, form); - exit(1); - } - } - }; - - // walk die list, callback is called on each die and should return true to - // continue traversal - void walk_die_list( - Dwarf_Debug dbg, - const die_object& die, - std::function fn - ) { - // TODO: Refactor so there is only one fn call - if(fn(dbg, die)) { - die_object current = die.get_sibling(); - while(current) { - if(fn(dbg, current)) { - current = current.get_sibling(); - } else { - break; - } - } - } - if(dump_dwarf) { - fprintf(stderr, "End walk_die_list\n"); - } + return fn(dbg, die); } + ); + return continue_traversal; + } - // walk die list, recursing into children, callback is called on each die - // and should return true to continue traversal - // returns true if traversal should continue - bool walk_die_list_recursive( - Dwarf_Debug dbg, - const die_object& die, - std::function fn - ) { - bool continue_traversal = true; - walk_die_list( - dbg, - die, - [&fn, &continue_traversal](Dwarf_Debug dbg, const die_object& die) { - auto child = die.get_child(); - if(child) { - if(!walk_die_list_recursive(dbg, child, fn)) { - continue_traversal = false; - return false; - } - } - return fn(dbg, die); - } - ); - return continue_traversal; + /*die_object get_type_die(Dwarf_Debug dbg, const die_object& die) { + Dwarf_Off type_offset; + Dwarf_Bool is_info; + int ret = dwarf_dietype_offset(die.get(), &type_offset, &is_info, nullptr); + if(ret == DW_DLV_OK) { + Dwarf_Die type_die; + ret = dwarf_offdie_b( + dbg, + type_offset, + is_info, + &type_die, + nullptr + ); + if(ret == DW_DLV_OK) { + return die_object(dbg, type_die); + } else { + fprintf(stderr, "Error\n"); + exit(1); } + } else { + fprintf(stderr, "no type offset??\n"); + } + return die_object(dbg, nullptr); + } - /*die_object get_type_die(Dwarf_Debug dbg, const die_object& die) { - Dwarf_Off type_offset; - Dwarf_Bool is_info; - int ret = dwarf_dietype_offset(die.get(), &type_offset, &is_info, nullptr); - if(ret == DW_DLV_OK) { - Dwarf_Die type_die; - ret = dwarf_offdie_b( - dbg, - type_offset, - is_info, - &type_die, - nullptr - ); - if(ret == DW_DLV_OK) { - return die_object(dbg, type_die); - } else { - fprintf(stderr, "Error\n"); - exit(1); - } - } else { - fprintf(stderr, "no type offset??\n"); - } - return die_object(dbg, nullptr); - } + bool has_type(Dwarf_Debug dbg, const die_object& die) { + Dwarf_Attribute attr; + int ret = dwarf_attr(die.get(), DW_AT_type, &attr, nullptr); + if(ret == DW_DLV_NO_ENTRY) { + return false; + } else if(ret == DW_DLV_OK) { + dwarf_dealloc_attribute(attr); + return true; + } else { + fprintf(stderr, "Error\n"); + exit(1); + } + } - bool has_type(Dwarf_Debug dbg, const die_object& die) { - Dwarf_Attribute attr; - int ret = dwarf_attr(die.get(), DW_AT_type, &attr, nullptr); - if(ret == DW_DLV_NO_ENTRY) { - return false; - } else if(ret == DW_DLV_OK) { - dwarf_dealloc_attribute(attr); - return true; - } else { - fprintf(stderr, "Error\n"); + struct type_result { + std::string base; + std::string extent; + + std::string get_type() { + return base + extent; + } + }; + + // TODO: ::*, namespace lookup, arrays + // DW_TAG_namespace + const char* tag_to_keyword(Dwarf_Half tag) { + switch(tag) { + case DW_TAG_atomic_type: + return "_Atomic"; + case DW_TAG_const_type: + return "const"; + case DW_TAG_volatile_type: + return "volatile"; + case DW_TAG_restrict_type: + return "restrict"; + default: + { + const char* tag_name = nullptr; + dwarf_get_TAG_name(tag, &tag_name); + fprintf(stderr, "tag_to_keyword unknown tag %s\n", tag_name); exit(1); } - } - - struct type_result { - std::string base; - std::string extent; - - std::string get_type() { - return base + extent; + } + } + const char* tag_to_ptr_ref(Dwarf_Half tag) { + switch(tag) { + case DW_TAG_pointer_type: + return "*"; + case DW_TAG_ptr_to_member_type: + return "::*"; // TODO + case DW_TAG_reference_type: + return "&"; + case DW_TAG_rvalue_reference_type: + return "&&"; + default: + { + const char* tag_name = nullptr; + dwarf_get_TAG_name(tag, &tag_name); + fprintf(stderr, "tag_to_ptr_ref unknown tag %s\n", tag_name); + exit(1); } - }; + } + } - // TODO: ::*, namespace lookup, arrays - // DW_TAG_namespace - const char* tag_to_keyword(Dwarf_Half tag) { - switch(tag) { - case DW_TAG_atomic_type: - return "_Atomic"; - case DW_TAG_const_type: - return "const"; - case DW_TAG_volatile_type: - return "volatile"; - case DW_TAG_restrict_type: - return "restrict"; - default: - { - const char* tag_name = nullptr; - dwarf_get_TAG_name(tag, &tag_name); - fprintf(stderr, "tag_to_keyword unknown tag %s\n", tag_name); - exit(1); - } + std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = ""); + + std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) { + CPPTRACE_VERIFY(die.get_tag() == DW_TAG_array_type); + std::string extents = ""; + walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) { + if(subrange.get_tag() == DW_TAG_subrange_type) { + Dwarf_Attribute attr = 0; + int res = 0; + res = dwarf_attr(subrange.get(), DW_AT_upper_bound, &attr, nullptr); + if(res != DW_DLV_OK) { + fprintf(stderr, "Error\n"); + return; } - } - const char* tag_to_ptr_ref(Dwarf_Half tag) { - switch(tag) { - case DW_TAG_pointer_type: - return "*"; - case DW_TAG_ptr_to_member_type: - return "::*"; // TODO - case DW_TAG_reference_type: - return "&"; - case DW_TAG_rvalue_reference_type: - return "&&"; - default: - { - const char* tag_name = nullptr; - dwarf_get_TAG_name(tag, &tag_name); - fprintf(stderr, "tag_to_ptr_ref unknown tag %s\n", tag_name); - exit(1); - } + Dwarf_Half form; + res = dwarf_whatform(attr, &form, nullptr); + if(res != DW_DLV_OK) { + fprintf(stderr, "Error\n"); + return; } + //fprintf(stderr, "form: %d\n", form); + Dwarf_Unsigned val; + res = dwarf_formudata(attr, &val, nullptr); + if(res != DW_DLV_OK) { + fprintf(stderr, "Error\n"); + return; + } + extents += "[" + std::to_string(val + 1) + "]"; + dwarf_dealloc_attribute(attr); + } else { + fprintf(stderr, "unknown tag %s\n", subrange.get_tag_name()); } + }); + return extents; + } - std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = ""); + std::string get_parameters(Dwarf_Debug dbg, const die_object& die) { + CPPTRACE_VERIFY(die.get_tag() == DW_TAG_subroutine_type); + std::vector params; + walk_die_list(dbg, die.get_child(), [¶ms](Dwarf_Debug dbg, const die_object& die) { + if(die.get_tag() == DW_TAG_formal_parameter) { + // TODO: Ignore DW_AT_artificial + params.push_back(resolve_type(dbg, get_type_die(dbg, die))); + } + }); + return "(" + join(params, ", ") + ")"; + } - std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) { - CPPTRACE_VERIFY(die.get_tag() == DW_TAG_array_type); - std::string extents = ""; - walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) { - if(subrange.get_tag() == DW_TAG_subrange_type) { - Dwarf_Attribute attr = 0; - int res = 0; - res = dwarf_attr(subrange.get(), DW_AT_upper_bound, &attr, nullptr); - if(res != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - return; - } - Dwarf_Half form; - res = dwarf_whatform(attr, &form, nullptr); - if(res != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - return; - } - //fprintf(stderr, "form: %d\n", form); - Dwarf_Unsigned val; - res = dwarf_formudata(attr, &val, nullptr); - if(res != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - return; - } - extents += "[" + std::to_string(val + 1) + "]"; - dwarf_dealloc_attribute(attr); - } else { - fprintf(stderr, "unknown tag %s\n", subrange.get_tag_name()); + std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build) { + switch(auto tag = die.get_tag()) { + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_enumeration_type: + return die.get_name() + build; + case DW_TAG_typedef: + return resolve_type(dbg, get_type_die(dbg, die)); + //case DW_TAG_subroutine_type: + // { + // // If there's no DW_AT_type then it's a void + // std::vector params; + // // TODO: Code duplication with retrieve_symbol_for_subprogram? + // walk_die_list(dbg, die.get_child(), [¶ms] (Dwarf_Debug dbg, const die_object& die) { + // if(die.get_tag() == DW_TAG_formal_parameter) { + // // TODO: Ignore DW_AT_artificial + // params.push_back(resolve_type(dbg, get_type_die(dbg, die))); + // } + // }); + // if(!has_type(dbg, die)) { + // return "void" + (build.empty() ? "" : "(" + build + ")") + "(" + join(params, ", ") + ")"; + // } else { + // // resolving return type, building on build + // return resolve_type( + // dbg, get_type_die(dbg, die), + // (build.empty() ? "" : "(" + build + ")") + // + "(" + // + join(params, ", ") + // + ")" + // ); + // } + // } + //case DW_TAG_array_type: + // return resolve_type(dbg, get_type_die(dbg, die), (build.empty() ? "" : "(" + build + ")") + "[" + "x" + "]"); + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + case DW_TAG_ptr_to_member_type: + { + const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child + const auto child_tag = child.get_tag(); + switch(child_tag) { + case DW_TAG_subroutine_type: + if(!has_type(dbg, child)) { + return "void(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child); + } else { + return resolve_type( + dbg, + get_type_die(dbg, child), + "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child) + ); + } + case DW_TAG_array_type: + return resolve_type( + dbg, + get_type_die(dbg, child), + "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_array_extents(dbg, child) + ); + default: + if(build.empty()) { + return resolve_type(dbg, get_type_die(dbg, die), tag_to_ptr_ref(tag)); + } else { + return resolve_type( + dbg, + get_type_die(dbg, die), + std::string(tag_to_ptr_ref(tag)) + " " + build + ); + } } - }); - return extents; - } + } + case DW_TAG_const_type: + case DW_TAG_atomic_type: + case DW_TAG_volatile_type: + case DW_TAG_restrict_type: + { + const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child + const auto child_tag = child.get_tag(); + switch(child_tag) { + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_typedef: + return std::string(tag_to_keyword(tag)) + + " " + + resolve_type(dbg, get_type_die(dbg, die), build); + default: + return resolve_type( + dbg, + get_type_die(dbg, die), + std::string(tag_to_keyword(tag)) + " " + build + ); + } + } + default: + { + fprintf(stderr, "unknown tag %s\n", die.get_tag_name()); + exit(1); + } + } + return {"", ""}; + }*/ - std::string get_parameters(Dwarf_Debug dbg, const die_object& die) { - CPPTRACE_VERIFY(die.get_tag() == DW_TAG_subroutine_type); - std::vector params; - walk_die_list(dbg, die.get_child(), [¶ms](Dwarf_Debug dbg, const die_object& die) { + bool is_mangled_name(const std::string& name) { + return name.find("_Z") || name.find("?h@@"); + } + + void retrieve_symbol_for_subprogram( + Dwarf_Debug dbg, + const die_object& die, + Dwarf_Addr pc, + Dwarf_Half dwversion, + stacktrace_frame& frame + ) { + CPPTRACE_VERIFY(die.get_tag() == DW_TAG_subprogram); + optional name; + if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) { + name = std::move(linkage_name); + } else if(auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name)) { + name = std::move(linkage_name); + } else if(auto linkage_name = die.get_string_attribute(DW_AT_name)) { + name = std::move(linkage_name); + } + if(name) { + frame.symbol = std::move(name).unwrap(); + } else { + if(die.has_attr(DW_AT_specification)) { + die_object spec = die.resolve_reference_attribute(DW_AT_specification); + // TODO: Passing pc here is misleading + return retrieve_symbol_for_subprogram(dbg, spec, pc, dwversion, frame); + } + } + // TODO: Disabled for now + // TODO: Handle namespaces + /*std::string name = die.get_name(); + std::vector params; + auto child = die.get_child(); + if(child) { + walk_die_list_recursive( + dbg, + child, + [pc, dwversion, &frame, ¶ms] (Dwarf_Debug dbg, const die_object& die) { if(die.get_tag() == DW_TAG_formal_parameter) { // TODO: Ignore DW_AT_artificial params.push_back(resolve_type(dbg, get_type_die(dbg, die))); } - }); - return "(" + join(params, ", ") + ")"; - } - - std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build) { - switch(auto tag = die.get_tag()) { - case DW_TAG_base_type: - case DW_TAG_class_type: - case DW_TAG_structure_type: - case DW_TAG_union_type: - case DW_TAG_enumeration_type: - return die.get_name() + build; - case DW_TAG_typedef: - return resolve_type(dbg, get_type_die(dbg, die)); - //case DW_TAG_subroutine_type: - // { - // // If there's no DW_AT_type then it's a void - // std::vector params; - // // TODO: Code duplication with retrieve_symbol_for_subprogram? - // walk_die_list(dbg, die.get_child(), [¶ms] (Dwarf_Debug dbg, const die_object& die) { - // if(die.get_tag() == DW_TAG_formal_parameter) { - // // TODO: Ignore DW_AT_artificial - // params.push_back(resolve_type(dbg, get_type_die(dbg, die))); - // } - // }); - // if(!has_type(dbg, die)) { - // return "void" + (build.empty() ? "" : "(" + build + ")") + "(" + join(params, ", ") + ")"; - // } else { - // // resolving return type, building on build - // return resolve_type( - // dbg, get_type_die(dbg, die), - // (build.empty() ? "" : "(" + build + ")") - // + "(" - // + join(params, ", ") - // + ")" - // ); - // } - // } - //case DW_TAG_array_type: - // return resolve_type(dbg, get_type_die(dbg, die), (build.empty() ? "" : "(" + build + ")") + "[" + "x" + "]"); - case DW_TAG_pointer_type: - case DW_TAG_reference_type: - case DW_TAG_rvalue_reference_type: - case DW_TAG_ptr_to_member_type: - { - const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child - const auto child_tag = child.get_tag(); - switch(child_tag) { - case DW_TAG_subroutine_type: - if(!has_type(dbg, child)) { - return "void(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child); - } else { - return resolve_type( - dbg, - get_type_die(dbg, child), - "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child) - ); - } - case DW_TAG_array_type: - return resolve_type( - dbg, - get_type_die(dbg, child), - "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_array_extents(dbg, child) - ); - default: - if(build.empty()) { - return resolve_type(dbg, get_type_die(dbg, die), tag_to_ptr_ref(tag)); - } else { - return resolve_type( - dbg, - get_type_die(dbg, die), - std::string(tag_to_ptr_ref(tag)) + " " + build - ); - } - } - } - case DW_TAG_const_type: - case DW_TAG_atomic_type: - case DW_TAG_volatile_type: - case DW_TAG_restrict_type: - { - const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child - const auto child_tag = child.get_tag(); - switch(child_tag) { - case DW_TAG_base_type: - case DW_TAG_class_type: - case DW_TAG_typedef: - return std::string(tag_to_keyword(tag)) - + " " - + resolve_type(dbg, get_type_die(dbg, die), build); - default: - return resolve_type( - dbg, - get_type_die(dbg, die), - std::string(tag_to_keyword(tag)) + " " + build - ); - } - } - default: - { - fprintf(stderr, "unknown tag %s\n", die.get_tag_name()); - exit(1); - } } - return {"", ""}; - }*/ + ); + } else { + fprintf(stderr, "no child %s\n", name.c_str()); + } + frame.symbol = name + "(" + join(params, ", ") + ")";*/ + } - bool is_mangled_name(const std::string& name) { - return name.find("_Z") || name.find("?h@@"); - } - - void retrieve_symbol_for_subprogram( - Dwarf_Debug dbg, - const die_object& die, - Dwarf_Addr pc, - Dwarf_Half dwversion, - stacktrace_frame& frame - ) { - CPPTRACE_VERIFY(die.get_tag() == DW_TAG_subprogram); - optional name; - if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) { - name = std::move(linkage_name); - } else if(auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name)) { - name = std::move(linkage_name); - } else if(auto linkage_name = die.get_string_attribute(DW_AT_name)) { - name = std::move(linkage_name); - } - if(name) { - frame.symbol = std::move(name).unwrap(); - } else { - if(die.has_attr(DW_AT_specification)) { - die_object spec = die.resolve_reference_attribute(DW_AT_specification); - // TODO: Passing pc here is misleading - return retrieve_symbol_for_subprogram(dbg, spec, pc, dwversion, frame); - } - } - // TODO: Disabled for now - // TODO: Handle namespaces - /*std::string name = die.get_name(); - std::vector params; - auto child = die.get_child(); - if(child) { - walk_die_list_recursive( - dbg, - child, - [pc, dwversion, &frame, ¶ms] (Dwarf_Debug dbg, const die_object& die) { - if(die.get_tag() == DW_TAG_formal_parameter) { - // TODO: Ignore DW_AT_artificial - params.push_back(resolve_type(dbg, get_type_die(dbg, die))); - } - } - ); - } else { - fprintf(stderr, "no child %s\n", name.c_str()); - } - frame.symbol = name + "(" + join(params, ", ") + ")";*/ - } - - // returns true if this call found the symbol - bool retrieve_symbol( - Dwarf_Debug dbg, - const die_object& die, - Dwarf_Addr pc, - Dwarf_Half dwversion, - stacktrace_frame& frame - ) { - bool found = false; - walk_die_list( - dbg, - die, - [pc, dwversion, &frame, &found] (Dwarf_Debug dbg, const die_object& die) { - if(dump_dwarf) { - fprintf( - stderr, - "-------------> %08llx %s %s\n", - (unsigned long long) die.get_global_offset(), - die.get_tag_name(), - die.get_name().c_str() - ); - } - - if(!(die.get_tag() == DW_TAG_namespace || pc_in_die(dbg, die.get(), dwversion, pc))) { - if(dump_dwarf) { - fprintf(stderr, "pc not in die\n"); - } - } else { - if(trace_dwarf) { - fprintf( - stderr, - "%s %08llx %s\n", - die.get_tag() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die", - (unsigned long long) die.get_global_offset(), - die.get_tag_name() - ); - } - if(die.get_tag() == DW_TAG_subprogram) { - retrieve_symbol_for_subprogram(dbg, die, pc, dwversion, frame); - found = true; - return false; - } - auto child = die.get_child(); - if(child) { - if(retrieve_symbol(dbg, child, pc, dwversion, frame)) { - found = true; - return false; - } - } else { - if(dump_dwarf) { - fprintf(stderr, "(no child)\n"); - } - } - } - return true; - } - ); - return found; - } - - void retrieve_line_info( - Dwarf_Debug dbg, - const die_object& die, - Dwarf_Addr pc, - Dwarf_Half dwversion, - stacktrace_frame& frame - ) { - Dwarf_Unsigned version; - Dwarf_Small table_count; - Dwarf_Line_Context ctxt; - Dwarf_Bool is_found = false; - (void)dwversion; - int ret = dwarf_srclines_b( - die.get(), - &version, - &table_count, - &ctxt, - nullptr - ); - if(ret == DW_DLV_NO_ENTRY) { - fprintf(stderr, "dwarf_srclines_b error\n"); - return; - } - if(table_count == 1) { - Dwarf_Line *linebuf = 0; - Dwarf_Signed linecount = 0; - Dwarf_Addr prev_lineaddr = 0; - - dwarf_srclines_from_linecontext(ctxt, &linebuf, - &linecount, nullptr); - Dwarf_Line prev_line = 0; - for(int i = 0; i < linecount; i++) { - Dwarf_Line line = linebuf[i]; - Dwarf_Addr lineaddr = 0; - - dwarf_lineaddr(line, &lineaddr, nullptr); - if(pc == lineaddr) { - /* Print the last line entry containing current pc. */ - Dwarf_Line last_pc_line = line; - - for(int j = i + 1; j < linecount; j++) { - Dwarf_Line j_line = linebuf[j]; - dwarf_lineaddr(j_line, &lineaddr, nullptr); - - if(pc == lineaddr) { - last_pc_line = j_line; - } - } - is_found = true; - print_line(dbg, last_pc_line, pc, frame); - break; - } else if(prev_line && pc > prev_lineaddr && - pc < lineaddr) { - is_found = true; - print_line(dbg, prev_line, pc, frame); - break; - } - Dwarf_Bool is_lne; - dwarf_lineendsequence(line, &is_lne, nullptr); - if(is_lne) { - prev_line = 0; - } else { - prev_lineaddr = lineaddr; - prev_line = line; - } - } - } - dwarf_srclines_dealloc_b(ctxt); - } - - void walk_compilation_units(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) { - // 0 passed as the die to the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d - // to fetch the cu die - die_object cu_die(dbg, nullptr); - cu_die = cu_die.get_sibling(); - if(!cu_die) { - if(dump_dwarf) { - fprintf(stderr, "End walk_compilation_units\n"); - } - return; - } - walk_die_list( - dbg, - cu_die, - [&frame, pc] (Dwarf_Debug dbg, const die_object& cu_die) { - Dwarf_Half offset_size = 0; - Dwarf_Half dwversion = 0; - dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); - if(trace_dwarf) { - fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str()); - } - Dwarf_Unsigned offset = 0; - // TODO: I'm unsure if I'm supposed to take DW_AT_rnglists_base into account here - // However it looks like it is correct when not taking an offset into account and incorrect - // otherwise - //if(dwversion >= 5) { - // Dwarf_Attribute attr; - // int ret = dwarf_attr(cu_die.get(), DW_AT_rnglists_base, &attr, nullptr); - // CPPTRACE_VERIFY(ret == DW_DLV_OK); - // Dwarf_Unsigned uval = 0; - // ret = dwarf_global_formref(attr, &uval, nullptr); - // offset = uval; - // dwarf_dealloc_attribute(attr); - //} - //fprintf(stderr, "------------> pc: %llx offset: %llx final: %llx\n", pc, offset, pc - offset); - if(pc_in_die(dbg, cu_die.get(), dwversion, pc - offset)) { - if(trace_dwarf) { - fprintf( - stderr, - "pc in die %08llx %s (now searching for %08llx)\n", - (unsigned long long) cu_die.get_global_offset(), - cu_die.get_tag_name(), - pc - offset - ); - } - retrieve_line_info(dbg, cu_die, pc, dwversion, frame); // no offset for line info - retrieve_symbol(dbg, cu_die, pc - offset, dwversion, frame); - return false; - } - return true; - } - ); - } - - void walk_dbg(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) { - // libdwarf keeps track of where it is in the file, dwarf_next_cu_header_d is statefull - Dwarf_Unsigned next_cu_header; - Dwarf_Half header_cu_type; - while(true) { - int ret = dwarf_next_cu_header_d( - dbg, - true, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - &next_cu_header, - &header_cu_type, - nullptr - ); - if(ret == DW_DLV_NO_ENTRY) { - if(dump_dwarf) { - fprintf(stderr, "End walk_dbg\n"); - } - return; - } - if(ret != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - return; - } - walk_compilation_units(dbg, pc, frame); - } - } - - void lookup_pc( - const char* object, - Dwarf_Addr pc, - stacktrace_frame& frame - ) { + // returns true if this call found the symbol + bool retrieve_symbol( + Dwarf_Debug dbg, + const die_object& die, + Dwarf_Addr pc, + Dwarf_Half dwversion, + stacktrace_frame& frame + ) { + bool found = false; + walk_die_list( + dbg, + die, + [pc, dwversion, &frame, &found] (Dwarf_Debug dbg, const die_object& die) { if(dump_dwarf) { - fprintf(stderr, "%s\n", object); - fprintf(stderr, "%llx\n", pc); - } - Dwarf_Debug dbg; - Dwarf_Ptr errarg = 0; - auto ret = dwarf_init_path( - object, - nullptr, - 0, - DW_GROUPNUMBER_ANY, - err_handler, - errarg, - &dbg, - nullptr - ); - if(ret == DW_DLV_NO_ENTRY) { - // fail, no debug info - } else if(ret != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - } else { - walk_dbg(dbg, pc, frame); - } - dwarf_finish(dbg); - } - - stacktrace_frame resolve_frame(const dlframe& frame_info) { - stacktrace_frame frame{}; - frame.filename = frame_info.obj_path; - frame.symbol = frame_info.symbol; - frame.address = frame_info.raw_address; - std::string obj_path = frame_info.obj_path; - #if IS_APPLE - if(directory_exists(obj_path + ".dSYM")) { - obj_path += ".dSYM/Contents/Resources/DWARF/" + basename(frame_info.obj_path); - } - #endif - if(trace_dwarf) { fprintf( stderr, - "Starting resolution for %s %08llx %s\n", - obj_path.c_str(), - (unsigned long long)frame_info.obj_address, - frame_info.symbol.c_str() + "-------------> %08llx %s %s\n", + (unsigned long long) die.get_global_offset(), + die.get_tag_name(), + die.get_name().c_str() ); } - lookup_pc( - obj_path.c_str(), - frame_info.obj_address, - frame - ); - return frame; - } - std::vector resolve_frames(const std::vector& frames) { - std::vector trace; - trace.reserve(frames.size()); - for(const auto& frame : get_frames_object_info(frames)) { - trace.push_back(resolve_frame(frame)); + if(!(die.get_tag() == DW_TAG_namespace || pc_in_die(dbg, die.get(), dwversion, pc))) { + if(dump_dwarf) { + fprintf(stderr, "pc not in die\n"); + } + } else { + if(trace_dwarf) { + fprintf( + stderr, + "%s %08llx %s\n", + die.get_tag() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die", + (unsigned long long) die.get_global_offset(), + die.get_tag_name() + ); + } + if(die.get_tag() == DW_TAG_subprogram) { + retrieve_symbol_for_subprogram(dbg, die, pc, dwversion, frame); + found = true; + return false; + } + auto child = die.get_child(); + if(child) { + if(retrieve_symbol(dbg, child, pc, dwversion, frame)) { + found = true; + return false; + } + } else { + if(dump_dwarf) { + fprintf(stderr, "(no child)\n"); + } + } + } + return true; + } + ); + return found; + } + + void retrieve_line_info( + Dwarf_Debug dbg, + const die_object& die, + Dwarf_Addr pc, + Dwarf_Half dwversion, + stacktrace_frame& frame + ) { + Dwarf_Unsigned version; + Dwarf_Small table_count; + Dwarf_Line_Context ctxt; + Dwarf_Bool is_found = false; + (void)dwversion; + int ret = dwarf_srclines_b( + die.get(), + &version, + &table_count, + &ctxt, + nullptr + ); + if(ret == DW_DLV_NO_ENTRY) { + fprintf(stderr, "dwarf_srclines_b error\n"); + return; + } + if(table_count == 1) { + Dwarf_Line *linebuf = 0; + Dwarf_Signed linecount = 0; + Dwarf_Addr prev_lineaddr = 0; + + dwarf_srclines_from_linecontext(ctxt, &linebuf, + &linecount, nullptr); + Dwarf_Line prev_line = 0; + for(int i = 0; i < linecount; i++) { + Dwarf_Line line = linebuf[i]; + Dwarf_Addr lineaddr = 0; + + dwarf_lineaddr(line, &lineaddr, nullptr); + if(pc == lineaddr) { + /* Print the last line entry containing current pc. */ + Dwarf_Line last_pc_line = line; + + for(int j = i + 1; j < linecount; j++) { + Dwarf_Line j_line = linebuf[j]; + dwarf_lineaddr(j_line, &lineaddr, nullptr); + + if(pc == lineaddr) { + last_pc_line = j_line; + } + } + is_found = true; + print_line(dbg, last_pc_line, pc, frame); + break; + } else if(prev_line && pc > prev_lineaddr && + pc < lineaddr) { + is_found = true; + print_line(dbg, prev_line, pc, frame); + break; + } + Dwarf_Bool is_lne; + dwarf_lineendsequence(line, &is_lne, nullptr); + if(is_lne) { + prev_line = 0; + } else { + prev_lineaddr = lineaddr; + prev_line = line; } - return trace; } } + dwarf_srclines_dealloc_b(ctxt); } + + void walk_compilation_units(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) { + // 0 passed as the die to the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d + // to fetch the cu die + die_object cu_die(dbg, nullptr); + cu_die = cu_die.get_sibling(); + if(!cu_die) { + if(dump_dwarf) { + fprintf(stderr, "End walk_compilation_units\n"); + } + return; + } + walk_die_list( + dbg, + cu_die, + [&frame, pc] (Dwarf_Debug dbg, const die_object& cu_die) { + Dwarf_Half offset_size = 0; + Dwarf_Half dwversion = 0; + dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); + if(trace_dwarf) { + fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str()); + } + Dwarf_Unsigned offset = 0; + // TODO: I'm unsure if I'm supposed to take DW_AT_rnglists_base into account here + // However it looks like it is correct when not taking an offset into account and incorrect + // otherwise + //if(dwversion >= 5) { + // Dwarf_Attribute attr; + // int ret = dwarf_attr(cu_die.get(), DW_AT_rnglists_base, &attr, nullptr); + // CPPTRACE_VERIFY(ret == DW_DLV_OK); + // Dwarf_Unsigned uval = 0; + // ret = dwarf_global_formref(attr, &uval, nullptr); + // offset = uval; + // dwarf_dealloc_attribute(attr); + //} + //fprintf(stderr, "------------> pc: %llx offset: %llx final: %llx\n", pc, offset, pc - offset); + if(pc_in_die(dbg, cu_die.get(), dwversion, pc - offset)) { + if(trace_dwarf) { + fprintf( + stderr, + "pc in die %08llx %s (now searching for %08llx)\n", + (unsigned long long) cu_die.get_global_offset(), + cu_die.get_tag_name(), + pc - offset + ); + } + retrieve_line_info(dbg, cu_die, pc, dwversion, frame); // no offset for line info + retrieve_symbol(dbg, cu_die, pc - offset, dwversion, frame); + return false; + } + return true; + } + ); + } + + void walk_dbg(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) { + // libdwarf keeps track of where it is in the file, dwarf_next_cu_header_d is statefull + Dwarf_Unsigned next_cu_header; + Dwarf_Half header_cu_type; + while(true) { + int ret = dwarf_next_cu_header_d( + dbg, + true, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + &next_cu_header, + &header_cu_type, + nullptr + ); + if(ret == DW_DLV_NO_ENTRY) { + if(dump_dwarf) { + fprintf(stderr, "End walk_dbg\n"); + } + return; + } + if(ret != DW_DLV_OK) { + fprintf(stderr, "Error\n"); + return; + } + walk_compilation_units(dbg, pc, frame); + } + } + + void lookup_pc( + const char* object, + Dwarf_Addr pc, + stacktrace_frame& frame + ) { + if(dump_dwarf) { + fprintf(stderr, "%s\n", object); + fprintf(stderr, "%llx\n", pc); + } + Dwarf_Debug dbg; + Dwarf_Ptr errarg = 0; + auto ret = dwarf_init_path( + object, + nullptr, + 0, + DW_GROUPNUMBER_ANY, + err_handler, + errarg, + &dbg, + nullptr + ); + if(ret == DW_DLV_NO_ENTRY) { + // fail, no debug info + } else if(ret != DW_DLV_OK) { + fprintf(stderr, "Error\n"); + } else { + walk_dbg(dbg, pc, frame); + } + dwarf_finish(dbg); + } + + stacktrace_frame resolve_frame(const dlframe& frame_info) { + stacktrace_frame frame{}; + frame.filename = frame_info.obj_path; + frame.symbol = frame_info.symbol; + frame.address = frame_info.raw_address; + std::string obj_path = frame_info.obj_path; + #if IS_APPLE + if(directory_exists(obj_path + ".dSYM")) { + obj_path += ".dSYM/Contents/Resources/DWARF/" + basename(frame_info.obj_path); + } + #endif + if(trace_dwarf) { + fprintf( + stderr, + "Starting resolution for %s %08llx %s\n", + obj_path.c_str(), + (unsigned long long)frame_info.obj_address, + frame_info.symbol.c_str() + ); + } + lookup_pc( + obj_path.c_str(), + frame_info.obj_address, + frame + ); + return frame; + } + + std::vector resolve_frames(const std::vector& frames) { + std::vector trace; + trace.reserve(frames.size()); + for(const auto& frame : get_frames_object_info(frames)) { + trace.push_back(resolve_frame(frame)); + } + return trace; + } +} +} } #endif diff --git a/src/symbols/symbols_with_nothing.cpp b/src/symbols/symbols_with_nothing.cpp index e1e2cf0..107c4b4 100644 --- a/src/symbols/symbols_with_nothing.cpp +++ b/src/symbols/symbols_with_nothing.cpp @@ -6,19 +6,19 @@ #include namespace cpptrace { - namespace detail { - namespace nothing { - std::vector resolve_frames(const std::vector& frames) { - return std::vector(frames.size(), { - 0, - 0, - 0, - "", - "" - }); - } - } +namespace detail { +namespace nothing { + std::vector resolve_frames(const std::vector& frames) { + return std::vector(frames.size(), { + 0, + 0, + 0, + "", + "" + }); } } +} +} #endif diff --git a/src/unwind/unwind.hpp b/src/unwind/unwind.hpp index 5f7d60a..d50a71a 100644 --- a/src/unwind/unwind.hpp +++ b/src/unwind/unwind.hpp @@ -8,15 +8,15 @@ #include namespace cpptrace { - namespace detail { - #ifdef CPPTRACE_HARD_MAX_FRAMES - constexpr size_t hard_max_frames = CPPTRACE_HARD_MAX_FRAMES; - #else - constexpr size_t hard_max_frames = 100; - #endif - CPPTRACE_FORCE_NO_INLINE - std::vector capture_frames(size_t skip); - } +namespace detail { + #ifdef CPPTRACE_HARD_MAX_FRAMES + constexpr size_t hard_max_frames = CPPTRACE_HARD_MAX_FRAMES; + #else + constexpr size_t hard_max_frames = 100; + #endif + CPPTRACE_FORCE_NO_INLINE + std::vector capture_frames(size_t skip); +} } #endif diff --git a/src/unwind/unwind_with_execinfo.cpp b/src/unwind/unwind_with_execinfo.cpp index 4260471..ff9cdd3 100644 --- a/src/unwind/unwind_with_execinfo.cpp +++ b/src/unwind/unwind_with_execinfo.cpp @@ -11,17 +11,17 @@ #include namespace cpptrace { - namespace detail { - CPPTRACE_FORCE_NO_INLINE - std::vector capture_frames(size_t skip) { - std::vector frames(hard_max_frames + skip, nullptr); - const int n_frames = backtrace(frames.data(), int(hard_max_frames + skip)); // thread safe - frames.resize(n_frames); - frames.erase(frames.begin(), frames.begin() + ptrdiff_t(std::min(skip + 1, frames.size()))); - frames.shrink_to_fit(); - return frames; - } +namespace detail { + CPPTRACE_FORCE_NO_INLINE + std::vector capture_frames(size_t skip) { + std::vector frames(hard_max_frames + skip, nullptr); + const int n_frames = backtrace(frames.data(), int(hard_max_frames + skip)); // thread safe + frames.resize(n_frames); + frames.erase(frames.begin(), frames.begin() + ptrdiff_t(std::min(skip + 1, frames.size()))); + frames.shrink_to_fit(); + return frames; } } +} #endif diff --git a/src/unwind/unwind_with_nothing.cpp b/src/unwind/unwind_with_nothing.cpp index 78a016f..e877098 100644 --- a/src/unwind/unwind_with_nothing.cpp +++ b/src/unwind/unwind_with_nothing.cpp @@ -6,11 +6,11 @@ #include namespace cpptrace { - namespace detail { - std::vector capture_frames(size_t) { - return {}; - } +namespace detail { + std::vector capture_frames(size_t) { + return {}; } } +} #endif diff --git a/src/unwind/unwind_with_unwind.cpp b/src/unwind/unwind_with_unwind.cpp index f3dbc41..02b36cd 100644 --- a/src/unwind/unwind_with_unwind.cpp +++ b/src/unwind/unwind_with_unwind.cpp @@ -13,52 +13,52 @@ #include namespace cpptrace { - namespace detail { - struct unwind_state { - std::size_t skip; - std::size_t count; - std::vector& vec; - }; +namespace detail { + struct unwind_state { + std::size_t skip; + std::size_t count; + std::vector& vec; + }; - _Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) { - unwind_state& state = *static_cast(arg); - if(state.skip) { - state.skip--; - if(_Unwind_GetIP(context) == uintptr_t(0)) { - return _URC_END_OF_STACK; - } else { - return _URC_NO_REASON; - } - } - - CPPTRACE_VERIFY( - state.count < state.vec.size(), - "Somehow cpptrace::detail::unwind_callback is overflowing a vector" - ); - int is_before_instruction = 0; - uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction); - if(!is_before_instruction && ip != uintptr_t(0)) { - ip--; - } - if (ip == uintptr_t(0) || state.count == state.vec.size()) { + _Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) { + unwind_state& state = *static_cast(arg); + if(state.skip) { + state.skip--; + if(_Unwind_GetIP(context) == uintptr_t(0)) { return _URC_END_OF_STACK; } else { - // TODO: push_back?... - state.vec[state.count++] = (void*)ip; return _URC_NO_REASON; } } - CPPTRACE_FORCE_NO_INLINE - std::vector capture_frames(size_t skip) { - std::vector frames(hard_max_frames, nullptr); - unwind_state state{skip + 1, 0, frames}; - _Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe - frames.resize(state.count); - frames.shrink_to_fit(); - return frames; + CPPTRACE_VERIFY( + state.count < state.vec.size(), + "Somehow cpptrace::detail::unwind_callback is overflowing a vector" + ); + int is_before_instruction = 0; + uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction); + if(!is_before_instruction && ip != uintptr_t(0)) { + ip--; + } + if (ip == uintptr_t(0) || state.count == state.vec.size()) { + return _URC_END_OF_STACK; + } else { + // TODO: push_back?... + state.vec[state.count++] = (void*)ip; + return _URC_NO_REASON; } } + + CPPTRACE_FORCE_NO_INLINE + std::vector capture_frames(size_t skip) { + std::vector frames(hard_max_frames, nullptr); + unwind_state state{skip + 1, 0, frames}; + _Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe + frames.resize(state.count); + frames.shrink_to_fit(); + return frames; + } +} } #endif diff --git a/src/unwind/unwind_with_winapi.cpp b/src/unwind/unwind_with_winapi.cpp index 242c502..932e571 100644 --- a/src/unwind/unwind_with_winapi.cpp +++ b/src/unwind/unwind_with_winapi.cpp @@ -10,16 +10,16 @@ #include namespace cpptrace { - namespace detail { - CPPTRACE_FORCE_NO_INLINE - std::vector capture_frames(size_t skip) { - std::vector addrs(hard_max_frames, nullptr); - int frames = CaptureStackBackTrace(static_cast(skip + 1), hard_max_frames, addrs.data(), NULL); - addrs.resize(frames); - addrs.shrink_to_fit(); - return addrs; - } +namespace detail { + CPPTRACE_FORCE_NO_INLINE + std::vector capture_frames(size_t skip) { + std::vector addrs(hard_max_frames, nullptr); + int frames = CaptureStackBackTrace(static_cast(skip + 1), hard_max_frames, addrs.data(), NULL); + addrs.resize(frames); + addrs.shrink_to_fit(); + return addrs; } } +} #endif