From 5dc819186e4d709c8911a3049190efc2ccc60de0 Mon Sep 17 00:00:00 2001 From: Jeremy <51220084+jeremy-rifkin@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:57:01 -0400 Subject: [PATCH] Baseline for middle-end system --- CMakeLists.txt | 1 + src/symbols/symbols.hpp | 30 + src/symbols/symbols_core.cpp | 84 ++ src/symbols/symbols_with_addr2line.cpp | 572 ++++----- src/symbols/symbols_with_dbghelp.cpp | 717 +++++------ src/symbols/symbols_with_dl.cpp | 52 +- src/symbols/symbols_with_libbacktrace.cpp | 116 +- src/symbols/symbols_with_libdwarf.cpp | 1324 +++++++++++---------- src/symbols/symbols_with_nothing.cpp | 18 +- 9 files changed, 1548 insertions(+), 1366 deletions(-) create mode 100644 src/symbols/symbols_core.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e0c407..3cca1e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,6 +224,7 @@ set( src/symbols/symbols_with_libbacktrace.cpp src/symbols/symbols_with_libdwarf.cpp src/symbols/symbols_with_nothing.cpp + src/symbols/symbols_core.cpp src/unwind/unwind_with_execinfo.cpp src/unwind/unwind_with_nothing.cpp src/unwind/unwind_with_unwind.cpp diff --git a/src/symbols/symbols.hpp b/src/symbols/symbols.hpp index 1c23597..f1fa920 100644 --- a/src/symbols/symbols.hpp +++ b/src/symbols/symbols.hpp @@ -8,6 +8,36 @@ 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 std::vector resolve_frames(const std::vector& frames); } } diff --git a/src/symbols/symbols_core.cpp b/src/symbols/symbols_core.cpp new file mode 100644 index 0000000..f6889b6 --- /dev/null +++ b/src/symbols/symbols_core.cpp @@ -0,0 +1,84 @@ +#include "symbols.hpp" + +#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 + + void apply_trace( + std::vector& result, + const 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_LIBBACKTRACE + apply_trace(trace, libbacktrace::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF + apply_trace(trace, libdwarf::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL + apply_trace(trace, libdl::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE + apply_trace(trace, addr2line::resolve_frames(frames)); + #endif + #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP + apply_trace(trace, dbghelp::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 54c0878..9ec918e 100644 --- a/src/symbols/symbols_with_addr2line.cpp +++ b/src/symbols/symbols_with_addr2line.cpp @@ -24,310 +24,320 @@ namespace cpptrace { namespace detail { - #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; + 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; + internal_verify(pipe(output_pipe.data) == 0); + internal_verify(pipe(input_pipe.data) == 0); // NOLINTNEXTLINE(misc-include-cleaner) const pid_t pid = fork(); - if(pid == -1) { return false; } + if(pid == -1) { return ""; } // error? TODO: Diagnostic if(pid == 0) { // child - close(STDOUT_FILENO); - close(STDERR_FILENO); // atos --help writes to stderr + 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", "--help", nullptr); - #else - execlp("atos", "atos", "--help", nullptr); - #endif + #if !IS_APPLE + execlp("addr2line", "addr2line", "-e", executable.c_str(), "-f", "-C", "-p", nullptr); #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); + execlp("atos", "atos", "-o", executable.c_str(), nullptr); #endif - _exit(magic); + #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(), nullptr); + #endif + #endif + _exit(1); // TODO: Diagnostic? } - 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; - internal_verify(pipe(output_pipe.data) == 0); - internal_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); + internal_verify(write(input_pipe.write_end, addresses.data(), addresses.size()) != -1); 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(), 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(), nullptr); - #endif - #endif - _exit(1); // TODO: Diagnostic? + 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; } - internal_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); + #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; } - // 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) { + + std::string resolve_addresses(const std::string& addresses, const std::string& executable) { // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. - checked = true; + ///fprintf(stderr, ("addr2line -e " + executable + " -fCp " + addresses + "\n").c_str()); #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH - FILE* p = popen("addr2line --version", "r"); + 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 " --version", "r"); + #ifndef CPPTRACE_ADDR2LINE_PATH + #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" #endif - if(p) { - has_addr2line = pclose(p) == 0; + 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; } - 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>>; + 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 { - internal_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(':'); - internal_verify(colon != std::string::npos); - internal_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); - internal_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); - internal_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); - internal_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"); - internal_verify(output.size() == entries_vec.size()); - for(size_t i = 0; i < output.size(); i++) { - update_trace(output[i], i, entries_vec); + // 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 { + internal_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(':'); + internal_verify(colon != std::string::npos); + internal_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); + internal_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); + internal_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); + internal_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"); + internal_verify(output.size() == entries_vec.size()); + for(size_t i = 0; i < output.size(); i++) { + update_trace(output[i], i, entries_vec); + } + } + } + return trace; } - return trace; } } } diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index e8bd2bb..3b1b102 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -15,382 +15,413 @@ namespace cpptrace { namespace detail { - // 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 - }; + namespace dbghelp { - 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 - }; + // 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 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 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 + }; - // 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 { + 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() ); } - } - 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" ); - } - // 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 - ); - 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}; - } + std::wstring wstr(info); + std::string str; + str.reserve(wstr.size()); + for(const auto c : wstr) { + str.push_back(static_cast(c)); } - 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}; + 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 ""; } - 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 - n_children -= n_ignore; // this must be ignored before TI_FINDCHILDREN_PARAMS::Count is set, else error - // 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)) + ">", - "" - }; + } + + static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase); + + struct class_name_result { + bool has_class_name; + std::string name; }; - } - - 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; - }; - - // 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; + // 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)}; + } } - 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; + 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)) + + ">", + "" + }; + }; + } - // 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"); + 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; + }; + + // 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; + + // 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 }; } - 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 { - 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; + 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; + } } } } diff --git a/src/symbols/symbols_with_dl.cpp b/src/symbols/symbols_with_dl.cpp index 1692828..1b4534f 100644 --- a/src/symbols/symbols_with_dl.cpp +++ b/src/symbols/symbols_with_dl.cpp @@ -11,34 +11,36 @@ namespace cpptrace { namespace detail { - 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, - "", - "" - }; + 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)); + 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; } - return trace; } } } diff --git a/src/symbols/symbols_with_libbacktrace.cpp b/src/symbols/symbols_with_libbacktrace.cpp index f3c552d..976d8a8 100644 --- a/src/symbols/symbols_with_libbacktrace.cpp +++ b/src/symbols/symbols_with_libbacktrace.cpp @@ -18,75 +18,77 @@ namespace cpptrace { namespace detail { - 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 + 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; } - 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; + 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 : ""; } - 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( + 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), - syminfo_callback, + 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; } - 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)); + 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; } - return trace; } } } diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index a24d4a6..d2d567b 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -182,462 +182,452 @@ static int dwarf4_ranges( namespace cpptrace { namespace detail { - // printbugging as we go - constexpr bool dump_dwarf = false; + namespace libdwarf { + // printbugging as we go + constexpr bool dump_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"); - } - 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; + 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"); } - 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; - } - //fprintf(stderr, "low: %llx high: %llx pc: %llx\n", cu_lowpc, cu_highpc, pc); - 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); - } - //fprintf(stderr, "low: %llu high: %llu\n", 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); - } - } - 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* name; - 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; - } - Dwarf_Half get_tag() const { - Dwarf_Half tag = 0; - dwarf_tag(die, &tag, nullptr); - return tag; - } - }; - - void walk_die_list( - Dwarf_Debug dbg, - const die_object& die, - std::function fn - ) { - fn(dbg, die); - die_object current = die.get_sibling(); - while(true) { - if(!current) { - if(dump_dwarf) { - fprintf(stderr, "End walk_die_list\n"); - } - return; - } - fn(dbg, current); - current = current.get_sibling(); - } - } - - void walk_die_list_recursive( - Dwarf_Debug dbg, - const die_object& die, - std::function fn - ) { - walk_die_list( - dbg, - die, - [&fn](Dwarf_Debug dbg, const die_object& die) { - auto child = die.get_child(); - if(child) { - walk_die_list_recursive(dbg, child, fn); - } - fn(dbg, die); - } - ); - } - - 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(dbg, attr, DW_DLA_ATTR); - return true; - } else { - fprintf(stderr, "Error\n"); + printf("Giving up"); exit(1); } - } - struct type_result { - std::string base; - std::string extent; + 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; - std::string get_type() { - return base + extent; + (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); + } } - }; - // 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); - } - } - } - 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); - } - } - } + 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; - /*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) { - assert(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; + ret = dwarf_lowpc(die, &cu_lowpc, nullptr); + if(ret == DW_DLV_OK) { + if(pc == cu_lowpc) { + return true; } - Dwarf_Half form; - res = dwarf_whatform(attr, &form, nullptr); - if(res != DW_DLV_OK) { - fprintf(stderr, "Error\n"); - return; + 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; + } + //fprintf(stderr, "low: %llx high: %llx pc: %llx\n", cu_lowpc, cu_highpc, pc); + if(pc >= cu_lowpc && pc < cu_highpc) { + return true; + } } - //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); + } + if(version >= 5) { + ret = dwarf5_ranges(die, + &lowest,&highest); } else { - const char* tag_name = nullptr; - dwarf_get_TAG_name(subrange.get_tag(), &tag_name); - fprintf(stderr, "unknown tag %s\n", tag_name); + ret = dwarf4_ranges(dbg,die,cu_lowpc, + &lowest,&highest); } - }); - return extents; - } - - std::string get_parameters(Dwarf_Debug dbg, const die_object& die) { - assert(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))); + //fprintf(stderr, "low: %llu high: %llu\n", lowest, highest); + if(pc >= lowest && pc < highest) { + return true; } - }); - return "(" + join(params, ", ") + ")"; - } + return false; + } - 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 { + 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); + } + } + 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* name; + 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; + } + Dwarf_Half get_tag() const { + Dwarf_Half tag = 0; + dwarf_tag(die, &tag, nullptr); + return tag; + } + }; + + void walk_die_list( + Dwarf_Debug dbg, + const die_object& die, + std::function fn + ) { + fn(dbg, die); + die_object current = die.get_sibling(); + while(true) { + if(!current) { + if(dump_dwarf) { + fprintf(stderr, "End walk_die_list\n"); + } + return; + } + fn(dbg, current); + current = current.get_sibling(); + } + } + + void walk_die_list_recursive( + Dwarf_Debug dbg, + const die_object& die, + std::function fn + ) { + walk_die_list( + dbg, + die, + [&fn](Dwarf_Debug dbg, const die_object& die) { + auto child = die.get_child(); + if(child) { + walk_die_list_recursive(dbg, child, fn); + } + fn(dbg, die); + } + ); + } + + 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(dbg, attr, DW_DLA_ATTR); + return true; + } else { + fprintf(stderr, "Error\n"); + exit(1); + } + } + + 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); + } + } + } + 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); + } + } + } + + /*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) { + assert(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 { + const char* tag_name = nullptr; + dwarf_get_TAG_name(subrange.get_tag(), &tag_name); + fprintf(stderr, "unknown tag %s\n", tag_name); + } + }); + return extents; + } + + std::string get_parameters(Dwarf_Debug dbg, const die_object& die) { + assert(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 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_parameters(dbg, child) + "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_array_extents(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 { + 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_ptr_ref(tag)) + " " + build + std::string(tag_to_keyword(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: + { + const char* tag_name = nullptr; + dwarf_get_TAG_name(die.get_tag(), &tag_name); + fprintf(stderr, "unknown tag %s\n", tag_name); + exit(1); } - } - default: - { - const char* tag_name = nullptr; - dwarf_get_TAG_name(die.get_tag(), &tag_name); - fprintf(stderr, "unknown tag %s\n", tag_name); - exit(1); - } - } - return {"", ""}; - }*/ - - 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) { - assert(die.get_tag() == DW_TAG_subprogram); - Dwarf_Attribute attr; - int ret = dwarf_attr(die.get(), DW_AT_linkage_name, &attr, nullptr); - if(ret != DW_DLV_OK) { - ret = dwarf_attr(die.get(), DW_AT_MIPS_linkage_name, &attr, nullptr); - } - if(ret == DW_DLV_OK) { - char* raw_linkage_name; - std::string linkage_name; - if(dwarf_formstring(attr, &raw_linkage_name, nullptr) == DW_DLV_OK) { - linkage_name = raw_linkage_name; - if(dump_dwarf) { - fprintf(stderr, "name: %s\n", raw_linkage_name); - } - dwarf_dealloc(dbg, raw_linkage_name, DW_DLA_STRING); } - dwarf_dealloc(dbg, attr, DW_DLA_ATTR); - if(!linkage_name.empty()) { - frame.symbol = linkage_name; + return {"", ""}; + }*/ + + 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 + ) { + assert(die.get_tag() == DW_TAG_subprogram); + Dwarf_Attribute attr; + int ret = dwarf_attr(die.get(), DW_AT_linkage_name, &attr, nullptr); + if(ret != DW_DLV_OK) { + ret = dwarf_attr(die.get(), DW_AT_MIPS_linkage_name, &attr, nullptr); } - } else { - // TODO: temporary - ret = dwarf_attr(die.get(), DW_AT_name, &attr, nullptr); if(ret == DW_DLV_OK) { char* raw_linkage_name; std::string linkage_name; @@ -652,250 +642,280 @@ namespace cpptrace { if(!linkage_name.empty()) { frame.symbol = linkage_name; } + } else { + // TODO: temporary + ret = dwarf_attr(die.get(), DW_AT_name, &attr, nullptr); + if(ret == DW_DLV_OK) { + char* raw_linkage_name; + std::string linkage_name; + if(dwarf_formstring(attr, &raw_linkage_name, nullptr) == DW_DLV_OK) { + linkage_name = raw_linkage_name; + if(dump_dwarf) { + fprintf(stderr, "name: %s\n", raw_linkage_name); + } + dwarf_dealloc(dbg, raw_linkage_name, DW_DLA_STRING); + } + dwarf_dealloc(dbg, attr, DW_DLA_ATTR); + if(!linkage_name.empty()) { + frame.symbol = linkage_name; + } + } } + // TODO: Handle namespaces + // TODO: Disabled for now + /*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, ", ") + ")";*/ } - // TODO: Handle namespaces - // TODO: Disabled for now - /*std::string name = die.get_name(); - std::vector params; - auto child = die.get_child(); - if(child) { - walk_die_list_recursive( + + void retrieve_symbol( + Dwarf_Debug dbg, + const die_object& die, + Dwarf_Addr pc, + Dwarf_Half dwversion, + stacktrace_frame& frame + ) { + walk_die_list( 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))); + die, + [pc, dwversion, &frame] (Dwarf_Debug dbg, const die_object& die) { + int ret; + if(dump_dwarf) { + const char* tag_name; + dwarf_get_TAG_name(die.get_tag(), &tag_name); + fprintf( + stderr, + "-------------> %d %s %s\n", + dwversion, + tag_name, + die.get_name().c_str() + ); + } + + if(!pc_in_die(dbg, die.get(), dwversion, pc)) { + if(dump_dwarf) { + fprintf(stderr, "pc not in die\n"); + } + } else { + if(dump_dwarf) { + fprintf(stderr, "pc in die <-----------------------------------\n"); + } + if(die.get_tag() == DW_TAG_subprogram) { + retrieve_symbol_for_subprogram(dbg, die, pc, dwversion, frame); + } + auto child = die.get_child(); + if(child) { + retrieve_symbol(dbg, child, pc, dwversion, frame); + } else { + if(dump_dwarf) { + fprintf(stderr, "(no child)\n"); + } + } } } ); - } else { - fprintf(stderr, "no child %s\n", name.c_str()); } - frame.symbol = name + "(" + join(params, ", ") + ")";*/ - } - void retrieve_symbol(Dwarf_Debug dbg, const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, stacktrace_frame& frame) { - walk_die_list( - dbg, - die, - [pc, dwversion, &frame] (Dwarf_Debug dbg, const die_object& die) { - int ret; - if(dump_dwarf) { - const char* tag_name; - dwarf_get_TAG_name(die.get_tag(), &tag_name); - fprintf( - stderr, - "-------------> %d %s %s\n", - dwversion, - tag_name, - die.get_name().c_str() - ); - } - - if(!pc_in_die(dbg, die.get(), dwversion, pc)) { - if(dump_dwarf) { - fprintf(stderr, "pc not in die\n"); - } - } else { - if(dump_dwarf) { - fprintf(stderr, "pc in die <-----------------------------------\n"); - } - if(die.get_tag() == DW_TAG_subprogram) { - retrieve_symbol_for_subprogram(dbg, die, pc, dwversion, frame); - } - auto child = die.get_child(); - if(child) { - retrieve_symbol(dbg, child, pc, dwversion, frame); - } else { - if(dump_dwarf) { - fprintf(stderr, "(no child)\n"); - } - } - } - } - ); - } - - 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 dieto 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); - /*auto child = cu_die.get_child(); - if(child) { - walk_die_list_recursive( - dbg, - child, - [&frame, pc, dwversion] (Dwarf_Debug dbg, const die_object& cu_die) { - - } - ); - }*/ - //walk_die(dbg, cu_die, pc, dwversion, false, frame); - if(pc_in_die(dbg, cu_die.get(), dwversion, pc)) { - retrieve_line_info(dbg, cu_die, pc, dwversion, frame); - retrieve_symbol(dbg, cu_die, pc, dwversion, frame); - } - } - ); - } - - 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, + 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 dieto 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_dbg\n"); + fprintf(stderr, "End walk_compilation_units\n"); } return; } - if(ret != DW_DLV_OK) { - fprintf(stderr, "Error\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); + /*auto child = cu_die.get_child(); + if(child) { + walk_die_list_recursive( + dbg, + child, + [&frame, pc, dwversion] (Dwarf_Debug dbg, const die_object& cu_die) { + + } + ); + }*/ + //walk_die(dbg, cu_die, pc, dwversion, false, frame); + if(pc_in_die(dbg, cu_die.get(), dwversion, pc)) { + retrieve_line_info(dbg, cu_die, pc, dwversion, frame); + retrieve_symbol(dbg, cu_die, pc, dwversion, frame); + } + } + ); + } + + 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); } - walk_compilation_units(dbg, pc, frame); } - } - void lookup_pc2( - const char* object, - Dwarf_Addr pc, - stacktrace_frame& frame - ) { - if(dump_dwarf) { - fprintf(stderr, "%s\n", object); - fprintf(stderr, "%llx\n", pc); + void lookup_pc2( + 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); } - 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; - lookup_pc2( - frame_info.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)); + 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; + lookup_pc2( + frame_info.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; } - return trace; } } } diff --git a/src/symbols/symbols_with_nothing.cpp b/src/symbols/symbols_with_nothing.cpp index 3a68b35..e1e2cf0 100644 --- a/src/symbols/symbols_with_nothing.cpp +++ b/src/symbols/symbols_with_nothing.cpp @@ -7,14 +7,16 @@ namespace cpptrace { namespace detail { - std::vector resolve_frames(const std::vector& frames) { - return std::vector(frames.size(), { - 0, - 0, - 0, - "", - "" - }); + namespace nothing { + std::vector resolve_frames(const std::vector& frames) { + return std::vector(frames.size(), { + 0, + 0, + 0, + "", + "" + }); + } } } }