From f7675eac91204eab306faccce51469ebcfc70f80 Mon Sep 17 00:00:00 2001 From: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com> Date: Tue, 18 Feb 2025 20:54:43 -0600 Subject: [PATCH] Refactor some dwarf utility abstractions into their own header --- src/symbols/dwarf/dwarf.hpp | 48 ------- src/symbols/dwarf/dwarf_resolver.cpp | 134 +----------------- src/symbols/dwarf/dwarf_utils.hpp | 198 +++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 181 deletions(-) create mode 100644 src/symbols/dwarf/dwarf_utils.hpp diff --git a/src/symbols/dwarf/dwarf.hpp b/src/symbols/dwarf/dwarf.hpp index aa2c689..835a7f9 100644 --- a/src/symbols/dwarf/dwarf.hpp +++ b/src/symbols/dwarf/dwarf.hpp @@ -531,54 +531,6 @@ namespace libdwarf { } } }; - - class srcfiles { - Dwarf_Debug dbg = nullptr; - char** dw_srcfiles = nullptr; - Dwarf_Unsigned dw_filecount = 0; - public: - srcfiles(Dwarf_Debug dbg, char** dw_srcfiles, Dwarf_Signed filecount) - : dbg(dbg), dw_srcfiles(dw_srcfiles), dw_filecount(static_cast(filecount)) - { - if(filecount < 0) { - throw internal_error(microfmt::format("Unexpected dw_filecount {}", filecount)); - } - } - ~srcfiles() { - if(dw_srcfiles) { - for(unsigned i = 0; i < dw_filecount; i++) { - dwarf_dealloc(dbg, dw_srcfiles[i], DW_DLA_STRING); - dw_srcfiles[i] = nullptr; - } - dwarf_dealloc(dbg, dw_srcfiles, DW_DLA_LIST); - } - } - srcfiles(const srcfiles&) = delete; - srcfiles(srcfiles&& other) { - *this = std::move(other); - } - srcfiles& operator=(const srcfiles&) = delete; - srcfiles& operator=(srcfiles&& other) { - std::swap(dbg, other.dbg); - std::swap(dw_srcfiles, other.dw_srcfiles); - std::swap(dw_filecount, other.dw_filecount); - return *this; - } - // note: dwarf uses 1-indexing - const char* get(Dwarf_Unsigned file_i) const { - if(file_i >= dw_filecount) { - throw internal_error(microfmt::format( - "Error while accessing the srcfiles list, requested index {} is out of bounds (count = {})", - file_i, - dw_filecount - )); - } - return dw_srcfiles[file_i]; - } - Dwarf_Unsigned count() const { - return dw_filecount; - } - }; } } } diff --git a/src/symbols/dwarf/dwarf_resolver.cpp b/src/symbols/dwarf/dwarf_resolver.cpp index a1b6d44..00dfad8 100644 --- a/src/symbols/dwarf/dwarf_resolver.cpp +++ b/src/symbols/dwarf/dwarf_resolver.cpp @@ -4,6 +4,7 @@ #include #include "symbols/dwarf/dwarf.hpp" // has dwarf #includes +#include "symbols/dwarf/dwarf_utils.hpp" #include "symbols/dwarf/dwarf_options.hpp" #include "symbols/symbols.hpp" #include "utils/common.hpp" @@ -40,139 +41,6 @@ namespace libdwarf { constexpr bool dump_dwarf = false; constexpr bool trace_dwarf = false; - // sorted range entries for dies - template< - typename T, - typename std::enable_if::value && sizeof(T) <= 16, int>::type = 0 - > - class die_cache { - public: - struct die_handle { - std::uint32_t die_index; - }; - private: - struct PACKED basic_range_entry { - die_handle die; - Dwarf_Addr low; - Dwarf_Addr high; - }; - struct PACKED annotated_range_entry { - die_handle die; - Dwarf_Addr low; - Dwarf_Addr high; - T data; - }; - using range_entry = typename std::conditional< - std::is_same::value, - basic_range_entry, - annotated_range_entry - >::type; - std::vector dies; - std::vector range_entries; - public: - die_handle add_die(die_object&& die) { - dies.push_back(std::move(die)); - VERIFY(dies.size() < std::numeric_limits::max()); - return die_handle{static_cast(dies.size() - 1)}; - } - template - auto insert(die_handle die, Dwarf_Addr low, Dwarf_Addr high) - -> typename std::enable_if::value, Void>::type - { - range_entries.push_back({die, low, high}); - } - template - auto insert(die_handle die, Dwarf_Addr low, Dwarf_Addr high, const T& t) - -> typename std::enable_if::value, Void>::type - { - range_entries.push_back({die, low, high, t}); - } - void finalize() { - std::sort(range_entries.begin(), range_entries.end(), [] (const range_entry& a, const range_entry& b) { - return a.low < b.low; - }); - } - std::size_t ranges_count() const { - return range_entries.size(); - } - - struct die_and_data { - const die_object& die; - T data; - }; - template - auto make_lookup_result(typename std::vector::const_iterator vec_it) const - -> typename std::enable_if::value, Ret>::type - { - return dies.at(vec_it->die.die_index); - } - template - auto make_lookup_result(typename std::vector::const_iterator vec_it) const - -> typename std::enable_if::value, Ret>::type - { - return die_and_data{dies.at(vec_it->die.die_index), vec_it->data}; - } - using lookup_result = typename std::conditional< - std::is_same::value, - const die_object&, - die_and_data - >::type; - optional lookup(Dwarf_Addr pc) const { - auto vec_it = first_less_than_or_equal( - range_entries.begin(), - range_entries.end(), - pc, - [] (Dwarf_Addr pc, const range_entry& entry) { - return pc < entry.low; - } - ); - if(vec_it == range_entries.end()) { - return nullopt; - } - // This would be an if constexpr if only C++17... - return make_lookup_result(vec_it); - } - }; - - struct line_entry { - Dwarf_Addr low; - // Dwarf_Addr high; - // int i; - Dwarf_Line line; - optional path; - optional line_number; - optional column_number; - line_entry(Dwarf_Addr low, Dwarf_Line line) : low(low), line(line) {} - }; - - struct line_table_info { - Dwarf_Unsigned version = 0; - Dwarf_Line_Context line_context = nullptr; - // sorted by low_addr - // TODO: Make this optional at some point, it may not be generated if cache mode switches during program exec... - std::vector line_entries; - - line_table_info( - Dwarf_Unsigned version, - Dwarf_Line_Context line_context, - std::vector&& line_entries - ) : version(version), line_context(line_context), line_entries(std::move(line_entries)) {} - ~line_table_info() { - dwarf_srclines_dealloc_b(line_context); - } - line_table_info(const line_table_info&) = delete; - line_table_info(line_table_info&& other) { - *this = std::move(other); - } - line_table_info& operator=(const line_table_info&) = delete; - line_table_info& operator=(line_table_info&& other) { - std::swap(version, other.version); - std::swap(line_context, other.line_context); - std::swap(line_entries, other.line_entries); - return *this; - } - }; - class dwarf_resolver; // used to describe data from an upstream binary to a resolver for the .dwo diff --git a/src/symbols/dwarf/dwarf_utils.hpp b/src/symbols/dwarf/dwarf_utils.hpp new file mode 100644 index 0000000..2f8101c --- /dev/null +++ b/src/symbols/dwarf/dwarf_utils.hpp @@ -0,0 +1,198 @@ +#ifndef DWARF_UTILS_HPP +#define DWARF_UTILS_HPP + +#include +#include "symbols/dwarf/dwarf.hpp" // has dwarf #includes +#include "utils/error.hpp" +#include "utils/microfmt.hpp" +#include "utils/utils.hpp" + +namespace cpptrace { +namespace detail { +namespace libdwarf { + class srcfiles { + Dwarf_Debug dbg = nullptr; + char** dw_srcfiles = nullptr; + Dwarf_Unsigned dw_filecount = 0; + + public: + srcfiles(Dwarf_Debug dbg, char** dw_srcfiles, Dwarf_Signed filecount) + : dbg(dbg), dw_srcfiles(dw_srcfiles), dw_filecount(static_cast(filecount)) + { + if(filecount < 0) { + throw internal_error(microfmt::format("Unexpected dw_filecount {}", filecount)); + } + } + ~srcfiles() { + if(dw_srcfiles) { + for(unsigned i = 0; i < dw_filecount; i++) { + dwarf_dealloc(dbg, dw_srcfiles[i], DW_DLA_STRING); + dw_srcfiles[i] = nullptr; + } + dwarf_dealloc(dbg, dw_srcfiles, DW_DLA_LIST); + } + } + srcfiles(const srcfiles&) = delete; + srcfiles(srcfiles&& other) { + *this = std::move(other); + } + srcfiles& operator=(const srcfiles&) = delete; + srcfiles& operator=(srcfiles&& other) { + std::swap(dbg, other.dbg); + std::swap(dw_srcfiles, other.dw_srcfiles); + std::swap(dw_filecount, other.dw_filecount); + return *this; + } + // note: dwarf uses 1-indexing + const char* get(Dwarf_Unsigned file_i) const { + if(file_i >= dw_filecount) { + throw internal_error(microfmt::format( + "Error while accessing the srcfiles list, requested index {} is out of bounds (count = {})", + file_i, + dw_filecount + )); + } + return dw_srcfiles[file_i]; + } + Dwarf_Unsigned count() const { + return dw_filecount; + } + }; + + // sorted range entries for dies + template< + typename T, + typename std::enable_if::value && sizeof(T) <= 16, int>::type = 0 + > + class die_cache { + public: + struct die_handle { + std::uint32_t die_index; + }; + private: + struct PACKED basic_range_entry { + die_handle die; + Dwarf_Addr low; + Dwarf_Addr high; + }; + struct PACKED annotated_range_entry { + die_handle die; + Dwarf_Addr low; + Dwarf_Addr high; + T data; + }; + using range_entry = typename std::conditional< + std::is_same::value, + basic_range_entry, + annotated_range_entry + >::type; + std::vector dies; + std::vector range_entries; + public: + die_handle add_die(die_object&& die) { + dies.push_back(std::move(die)); + VERIFY(dies.size() < std::numeric_limits::max()); + return die_handle{static_cast(dies.size() - 1)}; + } + template + auto insert(die_handle die, Dwarf_Addr low, Dwarf_Addr high) + -> typename std::enable_if::value, Void>::type + { + range_entries.push_back({die, low, high}); + } + template + auto insert(die_handle die, Dwarf_Addr low, Dwarf_Addr high, const T& t) + -> typename std::enable_if::value, Void>::type + { + range_entries.push_back({die, low, high, t}); + } + void finalize() { + std::sort(range_entries.begin(), range_entries.end(), [] (const range_entry& a, const range_entry& b) { + return a.low < b.low; + }); + } + std::size_t ranges_count() const { + return range_entries.size(); + } + + struct die_and_data { + const die_object& die; + T data; + }; + template + auto make_lookup_result(typename std::vector::const_iterator vec_it) const + -> typename std::enable_if::value, Ret>::type + { + return dies.at(vec_it->die.die_index); + } + template + auto make_lookup_result(typename std::vector::const_iterator vec_it) const + -> typename std::enable_if::value, Ret>::type + { + return die_and_data{dies.at(vec_it->die.die_index), vec_it->data}; + } + using lookup_result = typename std::conditional< + std::is_same::value, + const die_object&, + die_and_data + >::type; + optional lookup(Dwarf_Addr pc) const { + auto vec_it = first_less_than_or_equal( + range_entries.begin(), + range_entries.end(), + pc, + [] (Dwarf_Addr pc, const range_entry& entry) { + return pc < entry.low; + } + ); + if(vec_it == range_entries.end()) { + return nullopt; + } + // This would be an if constexpr if only C++17... + return make_lookup_result(vec_it); + } + }; + + struct line_entry { + Dwarf_Addr low; + // Dwarf_Addr high; + // int i; + Dwarf_Line line; + optional path; + optional line_number; + optional column_number; + line_entry(Dwarf_Addr low, Dwarf_Line line) : low(low), line(line) {} + }; + + struct line_table_info { + Dwarf_Unsigned version = 0; + Dwarf_Line_Context line_context = nullptr; + // sorted by low_addr + // TODO: Make this optional at some point, it may not be generated if cache mode switches during program exec... + std::vector line_entries; + + line_table_info( + Dwarf_Unsigned version, + Dwarf_Line_Context line_context, + std::vector&& line_entries + ) : version(version), line_context(line_context), line_entries(std::move(line_entries)) {} + ~line_table_info() { + dwarf_srclines_dealloc_b(line_context); + } + line_table_info(const line_table_info&) = delete; + line_table_info(line_table_info&& other) { + *this = std::move(other); + } + line_table_info& operator=(const line_table_info&) = delete; + line_table_info& operator=(line_table_info&& other) { + std::swap(version, other.version); + std::swap(line_context, other.line_context); + std::swap(line_entries, other.line_entries); + return *this; + } + }; +} +} +} + +#endif