From 35e555ae39e4336b4e43fa430e90746490ce965e Mon Sep 17 00:00:00 2001 From: Jeremy <51220084+jeremy-rifkin@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:32:51 -0500 Subject: [PATCH] Error handling in dwarf stuff --- src/symbols/symbols_with_libdwarf.cpp | 449 ++++++++++++++++++-------- src/utils/dwarf.hpp | 372 +++++++++++++-------- src/utils/error.hpp | 51 +-- src/utils/utils.hpp | 27 +- 4 files changed, 607 insertions(+), 292 deletions(-) diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index 64849fb..929eebf 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -120,13 +120,13 @@ namespace libdwarf { int >::type = 0 > - int wrap(int (*f)(Args...), Args2&&... args) const { + Result wrap(int (*f)(Args...), Args2&&... args) const { Dwarf_Error error = nullptr; int ret = f(std::forward(args)..., &error); if(ret == DW_DLV_ERROR) { - handle_dwarf_error(dbg, error); + return handle_dwarf_error(dbg, error); } - return ret; + return Ok(ret); } public: @@ -178,14 +178,17 @@ namespace libdwarf { nullptr, &dbg ); - if(ret == DW_DLV_OK) { + if(!ret) { + ret.drop_error(); + ok = false; + } else if(ret.unwrap_value() == DW_DLV_OK) { ok = true; - } else if(ret == DW_DLV_NO_ENTRY) { + } else if(ret.unwrap_value() == DW_DLV_NO_ENTRY) { // fail, no debug info ok = false; } else { ok = false; - PANIC("Unknown return code from dwarf_init_path"); + ASSERT(false, "Unknown return code from dwarf_init_path"); } if(ok) { @@ -256,7 +259,7 @@ namespace libdwarf { Dwarf_Unsigned next_cu_header; Dwarf_Half header_cu_type; while(true) { - int ret = wrap( + auto ret = wrap( dwarf_next_cu_header_d, dbg, true, @@ -271,20 +274,29 @@ namespace libdwarf { &next_cu_header, &header_cu_type ); - if(ret == DW_DLV_NO_ENTRY) { + if(!ret) { + ret.drop_error(); + return; + } + if(ret.unwrap_value() == DW_DLV_NO_ENTRY) { if(dump_dwarf) { std::fprintf(stderr, "End walk_dbg\n"); } return; } - if(ret != DW_DLV_OK) { - PANIC("Unexpected return code from dwarf_next_cu_header_d"); + if(ret.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "Unexpected return code from dwarf_next_cu_header_d"); return; } // 0 passed as the die to the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d // to fetch the cu die die_object cu_die(dbg, nullptr); - cu_die = cu_die.get_sibling(); + auto sibling = cu_die.get_sibling(); + if(sibling.is_error()) { + sibling.drop_error(); + break; + } + cu_die = std::move(sibling).unwrap_value(); if(!cu_die) { break; } @@ -304,8 +316,17 @@ namespace libdwarf { Dwarf_Half dwversion = 0; dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); auto ranges_vec = cu_die.get_rangelist_entries(dwversion); - for(auto range : ranges_vec) { - cu_cache.push_back({ cu_die.clone(), dwversion, range.first, range.second }); + if(!ranges_vec) { + ranges_vec.drop_error(); + return true; + } + for(auto range : ranges_vec.unwrap_value()) { + auto cu = cu_die.clone(); + if(!cu) { + ranges_vec.drop_error(); + return true; + } + cu_cache.push_back({std::move(cu).unwrap_value(), dwversion, range.low, range.high}); } return true; }); @@ -316,52 +337,75 @@ namespace libdwarf { } } - std::string subprogram_symbol( + Result subprogram_symbol( const die_object& die, Dwarf_Half dwversion ) { - ASSERT(die.get_tag() == DW_TAG_subprogram || die.get_tag() == DW_TAG_inlined_subroutine); + auto tag = die.get_tag(); + ASSERT(tag && (tag.unwrap_value() == DW_TAG_subprogram || tag.unwrap_value() == DW_TAG_inlined_subroutine)); optional name; - if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) { - name = std::move(linkage_name); - } else if(auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name)) { - name = std::move(linkage_name); - } else if(auto linkage_name = die.get_string_attribute(DW_AT_name)) { - name = std::move(linkage_name); + auto linkage_name = die.get_string_attribute(DW_AT_linkage_name); + if(!linkage_name) { + linkage_name.drop_error(); + } + if(linkage_name.has_value() && linkage_name.unwrap_value()) { + name = std::move(linkage_name).unwrap_value(); + } { + auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name); + if(!linkage_name) { + linkage_name.drop_error(); + } + if(linkage_name.has_value() && linkage_name.unwrap_value()) { + name = std::move(linkage_name).unwrap_value(); + } else { + auto linkage_name = die.get_string_attribute(DW_AT_name); + if(!linkage_name) { + linkage_name.drop_error(); + } + if(linkage_name.has_value() && linkage_name.unwrap_value()) { + name = std::move(linkage_name).unwrap_value(); + } + } } if(name.has_value()) { return std::move(name).unwrap(); } else { if(die.has_attr(DW_AT_specification)) { - die_object spec = die.resolve_reference_attribute(DW_AT_specification); - return subprogram_symbol(spec, dwversion); + auto spec = die.resolve_reference_attribute(DW_AT_specification); + if(!spec) { + return std::move(spec).unwrap_error(); + } + return subprogram_symbol(std::move(spec).unwrap_value(), dwversion); } else if(die.has_attr(DW_AT_abstract_origin)) { - die_object spec = die.resolve_reference_attribute(DW_AT_abstract_origin); - return subprogram_symbol(spec, dwversion); + auto spec = die.resolve_reference_attribute(DW_AT_abstract_origin); + if(!spec) { + return std::move(spec).unwrap_error(); + } + return subprogram_symbol(std::move(spec).unwrap_value(), dwversion); } } - return ""; + return Ok(""); } // despite (some) dwarf using 1-indexing, file_i should be the 0-based index - std::string resolve_filename(const die_object& cu_die, Dwarf_Unsigned file_i) { + Result resolve_filename(const die_object& cu_die, Dwarf_Unsigned file_i) { std::string filename; if(get_cache_mode() == cache_mode::prioritize_memory) { char** dw_srcfiles; Dwarf_Signed dw_filecount; - VERIFY(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount)); if(Dwarf_Signed(file_i) < dw_filecount) { // dwarf is using 1-indexing filename = dw_srcfiles[file_i]; } dwarf_dealloc(cu_die.dbg, dw_srcfiles, DW_DLA_LIST); } else { - auto off = cu_die.get_global_offset(); + PROP_ASSIGN(off, cu_die.get_global_offset()); auto it = srcfiles_cache.find(off); if(it == srcfiles_cache.end()) { char** dw_srcfiles; Dwarf_Signed dw_filecount; - VERIFY(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount)); it = srcfiles_cache.insert(it, {off, {dw_srcfiles, dw_filecount}}); } char** dw_srcfiles = it->second.first; @@ -381,50 +425,69 @@ namespace libdwarf { Dwarf_Half dwversion, std::vector& inlines ) { - ASSERT(die.get_tag() == DW_TAG_subprogram || die.get_tag() == DW_TAG_inlined_subroutine); + auto tag = die.get_tag(); + ASSERT(tag && (tag.unwrap_value() == DW_TAG_subprogram || tag.unwrap_value() == DW_TAG_inlined_subroutine)); // get_inlines_info is recursive and recurses into dies with pc ranges matching the pc we're looking for, // however, because I wouldn't want anything stack overflowing I'm breaking the recursion out into a loop optional> current_die = die; while(current_die.has_value()) { auto child = current_die.unwrap().get().get_child(); - if(!child) { + if(!child.has_value() || !child.unwrap_value()) { break; } optional> target_die; walk_die_list( - child, + child.unwrap_value(), [this, &cu_die, pc, dwversion, &inlines, &target_die] (const die_object& die) { - if(die.get_tag() == DW_TAG_inlined_subroutine && die.pc_in_die(dwversion, pc)) { + auto tag = die.get_tag(); + if(!tag) { + tag.drop_error(); + return true; + } + if(tag.unwrap_value() == DW_TAG_inlined_subroutine && die.pc_in_die(dwversion, pc)) { const auto name = subprogram_symbol(die, dwversion); - auto file_i = die.get_unsigned_attribute(DW_AT_call_file); + auto file_i_r = die.get_unsigned_attribute(DW_AT_call_file); + if(!file_i_r) { + file_i_r.drop_error(); + return true; + } + auto file_i = std::move(file_i_r).unwrap_value(); if(file_i) { // for dwarf 2, 3, 4, and experimental line table version 0xfe06 1-indexing is used // for dwarf 5 0-indexing is used auto line_table_opt = get_line_table(cu_die); - if(line_table_opt) { - auto& line_table = line_table_opt.unwrap().get(); - if(line_table.version != 5) { - if(file_i.unwrap() == 0) { - file_i.reset(); // 0 means no name to be found - } else { - // decrement to 0-based index - file_i.unwrap()--; - } + if(!line_table_opt) { + line_table_opt.drop_error(); + return true; + } + auto& line_table = line_table_opt.unwrap_value().get(); + if(line_table.version != 5) { + if(file_i.unwrap() == 0) { + file_i.reset(); // 0 means no name to be found + } else { + // decrement to 0-based index + file_i.unwrap()--; } - } else { - // silently continue } } - std::string file = file_i ? resolve_filename(cu_die, file_i.unwrap()) : ""; + std::string file = file_i ? resolve_filename(cu_die, file_i.unwrap()).value_or("") : ""; const auto line = die.get_unsigned_attribute(DW_AT_call_line); + if(!line) { + line.drop_error(); + return true; + } const auto col = die.get_unsigned_attribute(DW_AT_call_column); + if(!col) { + col.drop_error(); + return true; + } inlines.push_back(stacktrace_frame{ 0, 0, // TODO: Could put an object address here... - {static_cast(line.value_or(0))}, - {static_cast(col.value_or(0))}, + {static_cast(line.unwrap_value().value_or(0))}, + {static_cast(col.unwrap_value().value_or(0))}, file, - name, + name.value_or(""), true }); target_die = die; @@ -439,15 +502,16 @@ namespace libdwarf { } } - std::string retrieve_symbol_for_subprogram( + Result retrieve_symbol_for_subprogram( const die_object& cu_die, const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, std::vector& inlines ) { - ASSERT(die.get_tag() == DW_TAG_subprogram); - const auto name = subprogram_symbol(die, dwversion); + auto tag = die.get_tag(); + ASSERT(tag && tag.unwrap_value() == DW_TAG_subprogram); + auto name = subprogram_symbol(die, dwversion); if(detail::should_resolve_inlined_calls()) { get_inlines_info(cu_die, die, pc, dwversion, inlines); } @@ -469,42 +533,52 @@ namespace libdwarf { die, [this, &cu_die, pc, dwversion, &frame, &inlines, &found] (const die_object& die) { if(dump_dwarf) { - std::fprintf( - stderr, - "-------------> %08llx %s %s\n", - to_ull(die.get_global_offset()), + microfmt::print( + std::cerr, + "-------------> {8:0h} {} {}\n", + die.get_global_offset().value_or(0), die.get_tag_name(), - die.get_name().c_str() + die.get_name().value_or("") ); } - if(!(die.get_tag() == DW_TAG_namespace || die.pc_in_die(dwversion, pc))) { + auto tag = die.get_tag(); + if(!tag) { + tag.drop_error(); + return true; + } + if(!(tag.unwrap_value() == DW_TAG_namespace || die.pc_in_die(dwversion, pc))) { if(dump_dwarf) { - std::fprintf(stderr, "pc not in die\n"); + microfmt::print(std::cerr, "pc not in die\n"); } } else { if(trace_dwarf) { - std::fprintf( - stderr, - "%s %08llx %s\n", - die.get_tag() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die", - to_ull(die.get_global_offset()), + microfmt::print( + std::cerr, + "{} {8:0h} {}\n", + tag.unwrap_value() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die", + die.get_global_offset().value_or(0), die.get_tag_name() ); } - if(die.get_tag() == DW_TAG_subprogram) { - frame.symbol = retrieve_symbol_for_subprogram(cu_die, die, pc, dwversion, inlines); + if(tag.unwrap_value() == DW_TAG_subprogram) { + frame.symbol = retrieve_symbol_for_subprogram(cu_die, die, pc, dwversion, inlines) + .value_or(""); found = true; return false; } auto child = die.get_child(); - if(child) { - if(retrieve_symbol_walk(cu_die, child, pc, dwversion, frame, inlines)) { + if(!child) { + child.drop_error(); + return true; + } + if(child.unwrap_value()) { + if(retrieve_symbol_walk(cu_die, child.unwrap_value(), pc, dwversion, frame, inlines)) { found = true; return false; } } else { if(dump_dwarf) { - std::fprintf(stderr, "(no child)\n"); + microfmt::print(std::cerr, "(no child)\n"); } } } @@ -512,7 +586,7 @@ namespace libdwarf { } ); if(dump_dwarf) { - std::fprintf(stderr, "End walk_die_list\n"); + microfmt::print(std::cerr, "End walk_die_list\n"); } return found; } @@ -526,20 +600,38 @@ namespace libdwarf { walk_die_list( die, [this, dwversion, &vec] (const die_object& die) { - switch(die.get_tag()) { + auto tag = die.get_tag(); + if(!tag) { + tag.drop_error(); + return true; + } + switch(tag.unwrap_value()) { case DW_TAG_subprogram: { auto ranges_vec = die.get_rangelist_entries(dwversion); + if(!ranges_vec) { + ranges_vec.drop_error(); + return true; + } // TODO: Feels super inefficient and some day should maybe use an interval tree. - for(auto range : ranges_vec) { - vec.push_back({ die.clone(), range.first, range.second }); + for(auto range : ranges_vec.unwrap_value()) { + auto d = die.clone(); + if(!d) { + d.drop_error(); + return true; + } + vec.push_back({ std::move(d).unwrap_value(), range.low, range.high }); } // Walk children to get things like lambdas // TODO: Somehow find a way to get better names here? For gcc it's just "operator()" // On clang it's better auto child = die.get_child(); - if(child) { - preprocess_subprograms(child, dwversion, vec); + if(!child) { + child.drop_error(); + return true; + } + if(child.unwrap_value()) { + preprocess_subprograms(child.unwrap_value(), dwversion, vec); } } break; @@ -551,8 +643,12 @@ namespace libdwarf { case DW_TAG_compile_unit: { auto child = die.get_child(); - if(child) { - preprocess_subprograms(child, dwversion, vec); + if(!child) { + child.drop_error(); + return true; + } + if(child.unwrap_value()) { + preprocess_subprograms(child.unwrap_value(), dwversion, vec); } } break; @@ -579,7 +675,10 @@ namespace libdwarf { retrieve_symbol_walk(cu_die, cu_die, pc, dwversion, frame, inlines); } else { auto off = cu_die.get_global_offset(); - auto it = subprograms_cache.find(off); + if(!off) { + return; + } + auto it = subprograms_cache.find(off.unwrap_value()); if(it == subprograms_cache.end()) { // TODO: Refactor. Do the sort in the preprocess function and return the vec directly. std::vector vec; @@ -587,8 +686,8 @@ namespace libdwarf { std::sort(vec.begin(), vec.end(), [] (const subprogram_entry& a, const subprogram_entry& b) { return a.low < b.low; }); - subprograms_cache.emplace(off, std::move(vec)); - it = subprograms_cache.find(off); + subprograms_cache.emplace(off.unwrap_value(), std::move(vec)); + it = subprograms_cache.find(off.unwrap_value()); } auto& vec = it->second; auto vec_it = first_less_than_or_equal( @@ -603,7 +702,8 @@ namespace libdwarf { if(vec_it != vec.end()) { //vec_it->die.print(); if(vec_it->die.pc_in_die(dwversion, pc)) { - frame.symbol = retrieve_symbol_for_subprogram(cu_die, vec_it->die, pc, dwversion, inlines); + frame.symbol = retrieve_symbol_for_subprogram(cu_die, vec_it->die, pc, dwversion, inlines) + .value_or(""); } } else { ASSERT(vec.size() == 0, "Vec should be empty?"); @@ -613,29 +713,35 @@ namespace libdwarf { // returns a reference to a CU's line table, may be invalidated if the line_tables map is modified CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING - optional> get_line_table(const die_object& cu_die) { - auto off = cu_die.get_global_offset(); + Result, internal_error> get_line_table(const die_object& cu_die) { + PROP_ASSIGN(off, cu_die.get_global_offset()); auto it = line_tables.find(off); if(it != line_tables.end()) { - return it->second; + return std::reference_wrapper{it->second}; } else { Dwarf_Unsigned version; Dwarf_Small table_count; Dwarf_Line_Context line_context; - int ret = wrap( - dwarf_srclines_b, - cu_die.get(), - &version, - &table_count, - &line_context + PROP_ASSIGN( + ret, + wrap( + dwarf_srclines_b, + cu_die.get(), + &version, + &table_count, + &line_context + ) ); static_assert(std::is_unsigned::value, "Expected unsigned Dwarf_Small"); - VERIFY(/*table_count >= 0 &&*/ table_count <= 2, "Unknown dwarf line table count"); - if(ret == DW_DLV_NO_ENTRY) { - // TODO: Failing silently for now - return nullopt; + if(table_count > 2) { + return internal_error("Unknown dwarf line table count"); + } + if(ret == DW_DLV_NO_ENTRY) { + return internal_error("dwarf_srclines_b DW_DLV_NO_ENTRY"); + } + if(ret != DW_DLV_OK) { + return internal_error("dwarf_srclines_b not ok"); } - VERIFY(ret == DW_DLV_OK); std::vector line_entries; @@ -645,7 +751,7 @@ namespace libdwarf { Dwarf_Signed line_count = 0; Dwarf_Line* linebuf_actuals = nullptr; Dwarf_Signed linecount_actuals = 0; - VERIFY( + CHECK_OK( wrap( dwarf_srclines_two_level_from_linecontext, line_context, @@ -653,7 +759,7 @@ namespace libdwarf { &line_count, &linebuf_actuals, &linecount_actuals - ) == DW_DLV_OK + ) ); // TODO: Make any attempt to note PC ranges? Handle line end sequence? @@ -661,12 +767,12 @@ namespace libdwarf { for(int i = 0; i < line_count; i++) { Dwarf_Line line = line_buffer[i]; Dwarf_Addr low_addr = 0; - VERIFY(wrap(dwarf_lineaddr, line, &low_addr) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_lineaddr, line, &low_addr)); // scan ahead for the last line entry matching this pc int j; for(j = i + 1; j < line_count; j++) { Dwarf_Addr addr = 0; - VERIFY(wrap(dwarf_lineaddr, line_buffer[j], &addr) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_lineaddr, line_buffer[j], &addr)); if(addr != low_addr) { break; } @@ -703,7 +809,7 @@ namespace libdwarf { } it = line_tables.insert({off, {version, line_context, std::move(line_entries)}}).first; - return it->second; + return std::reference_wrapper{it->second}; } } @@ -715,9 +821,10 @@ namespace libdwarf { ) { auto table_info_opt = get_line_table(cu_die); if(!table_info_opt) { - return; // failing silently for now + table_info_opt.drop_error(); + return; } - auto& table_info = table_info_opt.unwrap().get(); + auto& table_info = table_info_opt.unwrap_value().get(); if(get_cache_mode() == cache_mode::prioritize_speed) { // Lookup in the table auto& line_entries = table_info.line_entries; @@ -735,21 +842,42 @@ namespace libdwarf { // line number if(!table_it->line_number) { Dwarf_Unsigned line_number = 0; - VERIFY(wrap(dwarf_lineno, line, &line_number) == DW_DLV_OK); + auto res = wrap(dwarf_lineno, line, &line_number); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } table_it->line_number = static_cast(line_number); } frame.line = table_it->line_number.unwrap(); // column number if(!table_it->column_number) { Dwarf_Unsigned column_number = 0; - VERIFY(wrap(dwarf_lineoff_b, line, &column_number) == DW_DLV_OK); + auto res = wrap(dwarf_lineoff_b, line, &column_number); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } table_it->column_number = static_cast(column_number); } frame.column = table_it->column_number.unwrap(); // filename if(!table_it->path) { char* filename = nullptr; - VERIFY(wrap(dwarf_linesrc, line, &filename) == DW_DLV_OK); + auto res = wrap(dwarf_linesrc, line, &filename); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } auto wrapper = raii_wrap( filename, [this] (char* str) { if(str) dwarf_dealloc(dbg, str, DW_DLA_STRING); } @@ -765,22 +893,34 @@ namespace libdwarf { Dwarf_Signed line_count = 0; Dwarf_Line* linebuf_actuals = nullptr; Dwarf_Signed linecount_actuals = 0; - VERIFY( - wrap( - dwarf_srclines_two_level_from_linecontext, - line_context, - &line_buffer, - &line_count, - &linebuf_actuals, - &linecount_actuals - ) == DW_DLV_OK + auto res = wrap( + dwarf_srclines_two_level_from_linecontext, + line_context, + &line_buffer, + &line_count, + &linebuf_actuals, + &linecount_actuals ); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } Dwarf_Addr last_lineaddr = 0; Dwarf_Line last_line = nullptr; for(int i = 0; i < line_count; i++) { Dwarf_Line line = line_buffer[i]; Dwarf_Addr lineaddr = 0; - VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK); + auto res = wrap(dwarf_lineaddr, line, &lineaddr); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } Dwarf_Line found_line = nullptr; if(pc == lineaddr) { // Multiple PCs may correspond to a line, find the last one @@ -788,7 +928,14 @@ namespace libdwarf { for(int j = i + 1; j < line_count; j++) { Dwarf_Line line = line_buffer[j]; Dwarf_Addr lineaddr = 0; - VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK); + auto res = wrap(dwarf_lineaddr, line, &lineaddr); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } if(pc == lineaddr) { found_line = line; } @@ -799,10 +946,24 @@ namespace libdwarf { } if(found_line) { Dwarf_Unsigned line_number = 0; - VERIFY(wrap(dwarf_lineno, found_line, &line_number) == DW_DLV_OK); + auto res = wrap(dwarf_lineno, found_line, &line_number); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } frame.line = static_cast(line_number); char* filename = nullptr; - VERIFY(wrap(dwarf_linesrc, found_line, &filename) == DW_DLV_OK); + auto res2 = wrap(dwarf_linesrc, found_line, &filename); + if(!res2) { + res2.drop_error(); + return; + } else if(res2.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } auto wrapper = raii_wrap( filename, [this] (char* str) { if(str) dwarf_dealloc(dbg, str, DW_DLA_STRING); } @@ -810,7 +971,14 @@ namespace libdwarf { frame.filename = filename; } else { Dwarf_Bool is_line_end; - VERIFY(wrap(dwarf_lineendsequence, line, &is_line_end) == DW_DLV_OK); + auto res = wrap(dwarf_lineendsequence, line, &is_line_end); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } if(is_line_end) { last_lineaddr = 0; last_line = nullptr; @@ -837,17 +1005,40 @@ namespace libdwarf { if(aranges) { // Try to find pc in aranges Dwarf_Arange arange; - if(wrap(dwarf_get_arange, aranges, arange_count, pc, &arange) == DW_DLV_OK) { + auto res = wrap(dwarf_get_arange, aranges, arange_count, pc, &arange); + if(!res) { + res.drop_error(); + return; + } + if(res.unwrap_value() == DW_DLV_OK) { // Address in table, load CU die Dwarf_Off cu_die_offset; - VERIFY(wrap(dwarf_get_cu_die_offset, arange, &cu_die_offset) == DW_DLV_OK); + auto res = wrap(dwarf_get_cu_die_offset, arange, &cu_die_offset); + if(!res) { + res.drop_error(); + return; + } else if(res.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } Dwarf_Die raw_die; // Setting is_info = true for now, assuming in .debug_info rather than .debug_types - VERIFY(wrap(dwarf_offdie_b, dbg, cu_die_offset, true, &raw_die) == DW_DLV_OK); + auto res2 = wrap(dwarf_offdie_b, dbg, cu_die_offset, true, &raw_die); + if(!res2) { + res2.drop_error(); + return; + } else if(res2.unwrap_value() != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } die_object cu_die(dbg, raw_die); Dwarf_Half offset_size = 0; Dwarf_Half dwversion = 0; - VERIFY(dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size) == DW_DLV_OK); + int res3 = dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); + if(res3 != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return; + } if(trace_dwarf) { std::fprintf(stderr, "Found CU in aranges\n"); cu_die.print(); @@ -866,19 +1057,23 @@ namespace libdwarf { walk_compilation_units([this, pc, &frame, &inlines] (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 res = dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); + if(res != DW_DLV_OK) { + ASSERT(false, "dwarf call not ok"); + return true; + } //auto p = cu_die.get_pc_range(dwversion); //cu_die.print(); //fprintf(stderr, " %llx, %llx\n", p.first, p.second); if(trace_dwarf) { - std::fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str()); + microfmt::print(std::cerr, "CU: {} {}\n", dwversion, cu_die.get_name().value_or("")); } if(cu_die.pc_in_die(dwversion, pc)) { if(trace_dwarf) { std::fprintf( stderr, "pc in die %08llx %s (now searching for %08llx)\n", - to_ull(cu_die.get_global_offset()), + to_ull(cu_die.get_global_offset().value_or(0)), cu_die.get_tag_name(), to_ull(pc) ); diff --git a/src/utils/dwarf.hpp b/src/utils/dwarf.hpp index 9f065c3..492c1c4 100644 --- a/src/utils/dwarf.hpp +++ b/src/utils/dwarf.hpp @@ -17,20 +17,57 @@ #include #endif +#define CONCAT_IMPL( x, y ) x##y +#define CONCAT( x, y ) CONCAT_IMPL( x, y ) + namespace cpptrace { namespace detail { namespace libdwarf { static_assert(std::is_pointer::value, "Dwarf_Die not a pointer"); static_assert(std::is_pointer::value, "Dwarf_Debug not a pointer"); - [[noreturn]] void handle_dwarf_error(Dwarf_Debug dbg, Dwarf_Error error) { + static NODISCARD ErrorResult handle_dwarf_error(Dwarf_Debug, Dwarf_Error error) { Dwarf_Unsigned ev = dwarf_errno(error); char* msg = dwarf_errmsg(error); - (void)dbg; - // dwarf_dealloc_error(dbg, error); - throw internal_error("Cpptrace dwarf error {} {}", ev, msg); + return {internal_error("Cpptrace dwarf error {} {}", ev, msg)}; } + #define CHECK_OK_IMPL(res_var, expr) \ + Result res_var = (expr); \ + if(!res_var) { \ + return std::move(res_var).unwrap_error(); \ + } else if(res_var.unwrap_value() != DW_DLV_OK) { \ + return Error( \ + internal_error( \ + "dwarf error: {} didn't evaluate to DW_DLV_OK, instead got {}", \ + #expr, \ + res_var.unwrap_value() \ + ) \ + ); \ + } + + // Check if the expression is an error or not DW_DLV_OK and error if either + #define CHECK_OK(expr) CHECK_OK_IMPL(CONCAT(res, __COUNTER__), (expr)) + + #define PROP_ASSIGN_IMPL(var, res_var, expr) \ + auto res_var = (expr); \ + if(!res_var) { \ + return std::move(res_var).unwrap_error(); \ + } \ + auto var = res_var.unwrap_value(); + + // If the expression is an error, return the error. Otherwise assign the value to the variable. + #define PROP_ASSIGN(var, expr) PROP_ASSIGN_IMPL(var, CONCAT(res, __COUNTER__), (expr)) + + #define PROP_IMPL(res_var, expr) \ + auto res_var = (expr); \ + if(!res_var) { \ + return std::move(res_var).unwrap_error(); \ + } + + // If the expression is an error, return the error. Otherwise assign the value to the variable. + #define PROP(expr) PROP_IMPL(CONCAT(res, __COUNTER__), (expr)) + struct die_object { Dwarf_Debug dbg = nullptr; Dwarf_Die die = nullptr; @@ -51,19 +88,29 @@ namespace libdwarf { int >::type = 0 > - int wrap(int (*f)(Args...), Args2&&... args) const { + Result wrap(int (*f)(Args...), Args2&&... args) const { Dwarf_Error error = nullptr; int ret = f(std::forward(args)..., &error); if(ret == DW_DLV_ERROR) { - handle_dwarf_error(dbg, error); + return handle_dwarf_error(dbg, error); } - return ret; + return Ok(ret); } + public: die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) { ASSERT(dbg != nullptr); } + // public: + // Result create(Dwarf_Debug dbg, Dwarf_Die die) { + // if(die == nullptr) { + // return internal_error("Error creating dwarf die object: Nullptr"); + // } else { + // return die_object(dbg, die); + // } + // } + ~die_object() { if(die) { dwarf_dealloc_die(die); @@ -87,39 +134,42 @@ namespace libdwarf { return *this; } - die_object clone() const { - Dwarf_Off global_offset = get_global_offset(); + Result clone() const { + auto global_offset = get_global_offset(); + if(!global_offset) { + return global_offset.unwrap_error(); + } Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); Dwarf_Die die_copy = nullptr; - VERIFY(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &die_copy) == DW_DLV_OK); - return {dbg, die_copy}; + CHECK_OK(wrap(dwarf_offdie_b, dbg, global_offset.unwrap_value(), is_info, &die_copy)); + return die_object(dbg, die_copy); } - die_object get_child() const { + Result get_child() const { Dwarf_Die child = nullptr; - int ret = wrap(dwarf_child, die, &child); + PROP_ASSIGN(ret, wrap(dwarf_child, die, &child)); if(ret == DW_DLV_OK) { return die_object(dbg, child); } else if(ret == DW_DLV_NO_ENTRY) { return die_object(dbg, nullptr); } else { - PANIC(); + return internal_error("dwarf error: Unexpected return from dwarf_child {}", ret); } } - die_object get_sibling() const { + Result get_sibling() const { Dwarf_Die sibling = nullptr; - int ret = wrap(dwarf_siblingof_b, dbg, die, true, &sibling); + PROP_ASSIGN(ret, wrap(dwarf_siblingof_b, dbg, die, true, &sibling)); if(ret == DW_DLV_OK) { return die_object(dbg, sibling); } else if(ret == DW_DLV_NO_ENTRY) { return die_object(dbg, nullptr); } else { - PANIC(); + return internal_error("dwarf error: Unexpected return from dwarf_siblingof_b {}", ret); } } - operator bool() const { + explicit operator bool() const { return die != nullptr; } @@ -127,79 +177,83 @@ namespace libdwarf { return die; } - std::string get_name() const { + Result get_name() const { char empty[] = ""; char* name = empty; - int ret = wrap(dwarf_diename, die, &name); + PROP_ASSIGN(ret, wrap(dwarf_diename, die, &name)); auto wrapper = raii_wrap(name, [this] (char* str) { dwarf_dealloc(dbg, str, DW_DLA_STRING); }); std::string str; if(ret != DW_DLV_NO_ENTRY) { str = name; } - return name; + return std::string(name); } - optional get_string_attribute(Dwarf_Half attr_num) const { + Result, internal_error> get_string_attribute(Dwarf_Half attr_num) const { Dwarf_Attribute attr; - if(wrap(dwarf_attr, die, attr_num, &attr) == DW_DLV_OK) { + auto ret = wrap(dwarf_attr, die, attr_num, &attr); + if(ret.is_error()) { + ret.drop_error(); + } else if(ret.unwrap_value() == DW_DLV_OK) { auto attwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); char* raw_str; - VERIFY(wrap(dwarf_formstring, attr, &raw_str) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_formstring, attr, &raw_str)); auto strwrapper = raii_wrap(raw_str, [this] (char* str) { dwarf_dealloc(dbg, str, DW_DLA_STRING); }); std::string str = raw_str; - return str; - } else { - return nullopt; + return Ok(str); } + return Ok(nullopt); } - optional get_unsigned_attribute(Dwarf_Half attr_num) const { + Result, internal_error> get_unsigned_attribute(Dwarf_Half attr_num) const { Dwarf_Attribute attr; - if(wrap(dwarf_attr, die, attr_num, &attr) == DW_DLV_OK) { + auto ret = wrap(dwarf_attr, die, attr_num, &attr); + if (ret.is_error()) { + ret.drop_error(); + } else if(ret.unwrap_value() == DW_DLV_OK) { auto attwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); - // Dwarf_Half form = 0; - // VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); Dwarf_Unsigned val; - VERIFY(wrap(dwarf_formudata, attr, &val) == DW_DLV_OK); - return val; - } else { - return nullopt; + CHECK_OK(wrap(dwarf_formudata, attr, &val)); + return Ok(val); } + return Ok(nullopt); } - bool has_attr(Dwarf_Half attr_num) const { + Result has_attr(Dwarf_Half attr_num) const { Dwarf_Bool present = false; - VERIFY(wrap(dwarf_hasattr, die, attr_num, &present) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_hasattr, die, attr_num, &present)); return present; } - Dwarf_Half get_tag() const { + Result get_tag() const { Dwarf_Half tag = 0; - VERIFY(wrap(dwarf_tag, die, &tag) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_tag, die, &tag)); return tag; } const char* get_tag_name() const { const char* tag_name; - if(dwarf_get_TAG_name(get_tag(), &tag_name) == DW_DLV_OK) { + auto tag = get_tag(); + if(!tag) { + tag.drop_error(); + } else if(dwarf_get_TAG_name(tag.unwrap_value(), &tag_name) == DW_DLV_OK) { return tag_name; - } else { - return ""; } + return ""; } - Dwarf_Off get_global_offset() const { + Result get_global_offset() const { Dwarf_Off off; - VERIFY(wrap(dwarf_dieoffset, die, &off) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_dieoffset, die, &off)); return off; } - die_object resolve_reference_attribute(Dwarf_Half attr_num) const { + Result resolve_reference_attribute(Dwarf_Half attr_num) const { Dwarf_Attribute attr; - VERIFY(dwarf_attr(die, attr_num, &attr, nullptr) == DW_DLV_OK); + CHECK_OK(dwarf_attr(die, attr_num, &attr, nullptr)); auto wrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); Dwarf_Half form = 0; - VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_whatform, attr, &form)); switch(form) { case DW_FORM_ref1: case DW_FORM_ref2: @@ -209,77 +263,86 @@ namespace libdwarf { { Dwarf_Off off = 0; Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); - VERIFY(wrap(dwarf_formref, attr, &off, &is_info) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_formref, attr, &off, &is_info)); Dwarf_Off global_offset = 0; - VERIFY(wrap(dwarf_convert_to_global_offset, attr, off, &global_offset) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_convert_to_global_offset, attr, off, &global_offset)); Dwarf_Die target = nullptr; - VERIFY(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &target) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &target)); return die_object(dbg, target); } case DW_FORM_ref_addr: { Dwarf_Off off; - VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_global_formref, attr, &off)); int is_info = dwarf_get_die_infotypes_flag(die); Dwarf_Die target = nullptr; - VERIFY(wrap(dwarf_offdie_b, dbg, off, is_info, &target) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_offdie_b, dbg, off, is_info, &target)); return die_object(dbg, target); } case DW_FORM_ref_sig8: { Dwarf_Sig8 signature; - VERIFY(wrap(dwarf_formsig8, attr, &signature) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_formsig8, attr, &signature)); Dwarf_Die target = nullptr; Dwarf_Bool targ_is_info = false; - VERIFY(wrap(dwarf_find_die_given_sig8, dbg, &signature, &target, &targ_is_info) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_find_die_given_sig8, dbg, &signature, &target, &targ_is_info)); return die_object(dbg, target); } default: - PANIC(microfmt::format("unknown form for attribute {} {}\n", attr_num, form)); + return internal_error(microfmt::format("unknown form for attribute {} {}", attr_num, form)); } } - Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) const { + Result get_ranges_offset(Dwarf_Attribute attr) const { Dwarf_Unsigned off = 0; Dwarf_Half form = 0; - VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_whatform, attr, &form)); if (form == DW_FORM_rnglistx) { - VERIFY(wrap(dwarf_formudata, attr, &off) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_formudata, attr, &off)); } else { - VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_global_formref, attr, &off)); } return off; } template // callback should return true to keep going - void dwarf5_ranges(F callback) const { + NODISCARD + Result dwarf5_ranges(F callback) const { Dwarf_Attribute attr = nullptr; - if(wrap(dwarf_attr, die, DW_AT_ranges, &attr) != DW_DLV_OK) { - return; + auto ranges_res = wrap(dwarf_attr, die, DW_AT_ranges, &attr); + if(ranges_res.is_error()) { + return ranges_res.unwrap_error(); + } else if(ranges_res.unwrap_value() != DW_DLV_OK) { + return internal_error("Unexpected value from dwarf_attr: " + std::to_string(ranges_res.unwrap_value())); } auto attrwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); - Dwarf_Unsigned offset = get_ranges_offset(attr); + PROP_ASSIGN(offset, get_ranges_offset(attr)); Dwarf_Half form = 0; - VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); + CHECK_OK(wrap(dwarf_whatform, attr, &form)); // get .debug_rnglists info Dwarf_Rnglists_Head head = nullptr; Dwarf_Unsigned rnglists_entries = 0; Dwarf_Unsigned dw_global_offset_of_rle_set = 0; - int res = wrap( - dwarf_rnglists_get_rle_head, - attr, - form, - offset, - &head, - &rnglists_entries, - &dw_global_offset_of_rle_set + PROP_ASSIGN( + res, + wrap( + dwarf_rnglists_get_rle_head, + attr, + form, + offset, + &head, + &rnglists_entries, + &dw_global_offset_of_rle_set + ) ); auto headwrapper = raii_wrap(head, [] (Dwarf_Rnglists_Head head) { dwarf_dealloc_rnglists_head(head); }); if(res == DW_DLV_NO_ENTRY) { - return; + return monostate{}; // I guess normal + } + if(res != DW_DLV_OK) { + return internal_error("dwarf_rnglists_get_rle_head not ok"); } - VERIFY(res == DW_DLV_OK); for(std::size_t i = 0 ; i < rnglists_entries; i++) { unsigned entrylen = 0; unsigned rle_value_out = 0; @@ -288,22 +351,27 @@ namespace libdwarf { Dwarf_Bool unavailable = 0; Dwarf_Unsigned cooked1 = 0; Dwarf_Unsigned cooked2 = 0; - res = wrap( - dwarf_get_rnglists_entry_fields_a, - head, - i, - &entrylen, - &rle_value_out, - &raw1, - &raw2, - &unavailable, - &cooked1, - &cooked2 + PROP_ASSIGN( + res2, + wrap( + dwarf_get_rnglists_entry_fields_a, + head, + i, + &entrylen, + &rle_value_out, + &raw1, + &raw2, + &unavailable, + &cooked1, + &cooked2 + ) ); - if(res == DW_DLV_NO_ENTRY) { + if(res2 == DW_DLV_NO_ENTRY) { continue; } - VERIFY(res == DW_DLV_OK); + if(res2 != DW_DLV_OK) { + return internal_error("dwarf_get_rnglists_entry_fields_a not ok"); + } if(unavailable) { continue; } @@ -320,35 +388,33 @@ namespace libdwarf { case DW_RLE_startx_length: case DW_RLE_start_length: if(!callback(cooked1, cooked2)) { - return; + return monostate{}; } break; default: - PANIC("Something is wrong"); + return internal_error("rle_value_out: something is wrong"); break; } } + return monostate{}; } template // callback should return true to keep going - void dwarf4_ranges(Dwarf_Addr lowpc, F callback) const { + NODISCARD + Result dwarf4_ranges(Dwarf_Addr lowpc, F callback) const { Dwarf_Attribute attr = nullptr; - if(wrap(dwarf_attr, die, DW_AT_ranges, &attr) != DW_DLV_OK) { - return; - } + CHECK_OK(wrap(dwarf_attr, die, DW_AT_ranges, &attr)); auto attrwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); Dwarf_Unsigned offset; - if(wrap(dwarf_global_formref, attr, &offset) != DW_DLV_OK) { - return; - } + CHECK_OK(wrap(dwarf_global_formref, attr, &offset)); Dwarf_Addr baseaddr = 0; if(lowpc != (std::numeric_limits::max)()) { baseaddr = lowpc; } Dwarf_Ranges* ranges = nullptr; Dwarf_Signed count = 0; - VERIFY( + CHECK_OK( wrap( dwarf_get_ranges_b, dbg, @@ -358,7 +424,7 @@ namespace libdwarf { &ranges, &count, nullptr - ) == DW_DLV_OK + ) ); auto rangeswrapper = raii_wrap( ranges, @@ -367,7 +433,7 @@ namespace libdwarf { for(int i = 0; i < count; i++) { if(ranges[i].dwr_type == DW_RANGES_ENTRY) { if(!callback(baseaddr + ranges[i].dwr_addr1, baseaddr + ranges[i].dwr_addr2)) { - return; + return monostate{}; } } else if(ranges[i].dwr_type == DW_RANGES_ADDRESS_SELECTION) { baseaddr = ranges[i].dwr_addr2; @@ -376,63 +442,76 @@ namespace libdwarf { baseaddr = lowpc; } } + return monostate{}; } template // callback should return true to keep going - void dwarf_ranges(int version, optional pc, F callback) const { + NODISCARD + Result dwarf_ranges(int version, optional pc, F callback) const { Dwarf_Addr lowpc = (std::numeric_limits::max)(); - if(wrap(dwarf_lowpc, die, &lowpc) == DW_DLV_OK) { + PROP_ASSIGN(res, wrap(dwarf_lowpc, die, &lowpc)); + if(res == DW_DLV_OK) { if(pc.has_value() && pc.unwrap() == lowpc) { callback(lowpc, lowpc + 1); - return; + return monostate{}; } Dwarf_Addr highpc = 0; enum Dwarf_Form_Class return_class; - if(wrap(dwarf_highpc_b, die, &highpc, nullptr, &return_class) == DW_DLV_OK) { + PROP_ASSIGN(res2, wrap(dwarf_highpc_b, die, &highpc, nullptr, &return_class)); + if(res2 == DW_DLV_OK) { if(return_class == DW_FORM_CLASS_CONSTANT) { highpc += lowpc; } if(!callback(lowpc, highpc)) { - return; + return monostate{}; } } } if(version >= 5) { - dwarf5_ranges(callback); + return dwarf5_ranges(callback); } else { - dwarf4_ranges(lowpc, callback); + return dwarf4_ranges(lowpc, callback); } } - std::vector> get_rangelist_entries(int version) const { - std::vector> vec; - dwarf_ranges(version, nullopt, [&vec] (Dwarf_Addr low, Dwarf_Addr high) { - // Simple coalescing optimization: - // Sometimes the range list entries are really continuous: [100, 200), [200, 300) - // Other times there's just one byte of separation [300, 399), [400, 500) - // Those are the main two cases I've observed. - // This will not catch all cases, presumably, as the range lists aren't sorted. But compilers/linkers - // seem to like to emit the ranges in sorted order. - if(!vec.empty() && low - vec.back().second <= 1) { - vec.back().second = high; - } else { - vec.push_back({low, high}); - } - return true; - }); + struct address_range { + Dwarf_Addr low; + Dwarf_Addr high; + }; + + Result, internal_error> get_rangelist_entries(int version) const { + std::vector vec; + PROP( + dwarf_ranges(version, nullopt, [&vec] (Dwarf_Addr low, Dwarf_Addr high) { + // Simple coalescing optimization: + // Sometimes the range list entries are really continuous: [100, 200), [200, 300) + // Other times there's just one byte of separation [300, 399), [400, 500) + // Those are the main two cases I've observed. + // This will not catch all cases, presumably, as the range lists aren't sorted. + // But compilers/linkers seem to like to emit the ranges in sorted order. + if(!vec.empty() && low - vec.back().high <= 1) { + vec.back().high = high; + } else { + vec.push_back({low, high}); + } + return true; + }) + ); return vec; } - Dwarf_Bool pc_in_die(int version, Dwarf_Addr pc) const { + Result pc_in_die(int version, Dwarf_Addr pc) const { bool found = false; - dwarf_ranges(version, pc, [&found, pc] (Dwarf_Addr low, Dwarf_Addr high) { - if(pc >= low && pc < high) { - found = true; - return false; - } - return true; - }); + PROP( + dwarf_ranges(version, pc, [&found, pc] (Dwarf_Addr low, Dwarf_Addr high) { + if(pc >= low && pc < high) { + found = true; + return false; + } + return true; + }) + ); return found; } @@ -440,9 +519,9 @@ namespace libdwarf { std::fprintf( stderr, "%08llx %s %s\n", - to_ull(get_global_offset()), + to_ull(get_global_offset().value_or(0)), get_tag_name(), - get_name().c_str() + get_name().drop_error().value_or("").c_str() ); } }; @@ -457,13 +536,22 @@ namespace libdwarf { // TODO: Refactor so there is only one fn call bool continue_traversal = true; if(fn(die)) { - die_object current = die.get_sibling(); - while(current) { - if(fn(current)) { - current = current.get_sibling(); - } else { - continue_traversal = false; - break; + auto sibling = die.get_sibling(); + if(!sibling) { + sibling.drop_error(); + } else { + die_object current = std::move(sibling).unwrap_value(); + while(current) { + if(fn(current)) { + auto sibling = current.get_sibling(); + if(!sibling) { + sibling.drop_error(); + } + current = std::move(sibling).unwrap_value(); + } else { + continue_traversal = false; + break; + } } } } @@ -481,8 +569,10 @@ namespace libdwarf { die, [&fn](const die_object& die) { auto child = die.get_child(); - if(child) { - if(!walk_die_list_recursive(child, fn)) { + if(child.is_error()) { + child.drop_error(); + } else if(child.unwrap_value()) { + if(!walk_die_list_recursive(child.unwrap_value(), fn)) { return false; } } diff --git a/src/utils/error.hpp b/src/utils/error.hpp index 241daf7..0dc6e3f 100644 --- a/src/utils/error.hpp +++ b/src/utils/error.hpp @@ -20,9 +20,10 @@ namespace detail { class internal_error : public std::exception { std::string msg; public: - internal_error(std::string message) : msg(std::move(message)) {} + internal_error(std::string message) : msg("Cpptrace internal error: " + std::move(message)) {} template - internal_error(const char* format, Args&&... args) : msg(microfmt::format(format, args...)) {} + internal_error(const char* format, Args&&... args) + : msg("Cpptrace internal error: " + microfmt::format(format, args...)) {} const char* what() const noexcept override { return msg.c_str(); } @@ -75,23 +76,27 @@ namespace detail { } } - [[noreturn]] inline void panic( - const char* signature, - source_location location, - const std::string& message = "" - ) { - if(message == "") { - throw internal_error( - "Cpptrace panic {}:{}: {}\n", - location.file, location.line, signature - ); - } else { - throw internal_error( - "Cpptrace panic {}:{}: {}: {}\n", - location.file, location.line, signature, message.c_str() - ); - } - } + // [[noreturn]] inline void panic( + // const char* signature, + // source_location location, + // const std::string& message = "" + // ) { + // if(message == "") { + // throw internal_error( + // stringf( + // "Cpptrace panic %s:%d: %s\n", + // location.file, location.line, signature + // ) + // ); + // } else { + // throw internal_error( + // stringf( + // "Cpptrace panic %s:%d: %s: %s\n", + // location.file, location.line, signature, message.c_str() + // ) + // ); + // } + // } template void nullfn() { @@ -107,7 +112,7 @@ namespace detail { } // Check condition in both debug and release. std::runtime_error on failure. - #define VERIFY(c, ...) ( \ + /*#define VERIFY(c, ...) ( \ (::cpptrace::detail::as_bool(c)) \ ? static_cast(0) \ : (::cpptrace::detail::assert_fail)( \ @@ -116,7 +121,7 @@ namespace detail { CPPTRACE_PFUNC, \ {}, \ ##__VA_ARGS__) \ - ) + )*/ // Workaround a compiler warning template @@ -128,8 +133,8 @@ namespace detail { return ""; } - // Check condition in both debug and release. std::runtime_error on failure. - #define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, {}, ::cpptrace::detail::as_string(__VA_ARGS__))) + // // Check condition in both debug and release. std::runtime_error on failure. + // #define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, {}, ::cpptrace::detail::as_string(__VA_ARGS__))) #ifndef NDEBUG // Check condition in both debug. std::runtime_error on failure. diff --git a/src/utils/utils.hpp b/src/utils/utils.hpp index 226d0df..ce6a0b3 100644 --- a/src/utils/utils.hpp +++ b/src/utils/utils.hpp @@ -331,6 +331,26 @@ namespace detail { } }; + template + struct OkResult { + T value; + }; + + template + struct ErrorResult { + T value; + }; + + template + OkResult Ok(T value) { + return {value}; + } + + template + ErrorResult Error(T value) { + return {value}; + } + // TODO: Better dump error // TODO: Explicit constructors for value, then add Ok()/Error() helpers template::value, int>::type = 0> @@ -345,6 +365,10 @@ namespace detail { public: Result(T value) : value_(std::move(value)), active(member::value) {} Result(E error) : error_(std::move(error)), active(member::error) {} + template + Result(OkResult&& value) : value_(std::move(value.value)), active(member::value) {} + template + Result(ErrorResult&& error) : error_(std::move(error.value)), active(member::error) {} Result(Result&& other) : active(other.active) { if(other.active == member::value) { new (&value_) T(std::move(other.value_)); @@ -428,10 +452,11 @@ namespace detail { return has_value() ? std::move(value_) : static_cast(std::forward(default_value)); } - void drop_error() const { + const Result& drop_error() const { if(is_error()) { std::fprintf(stderr, "%s\n", unwrap_error().what()); } + return *this; } };