diff --git a/include/cpptrace/cpptrace.hpp b/include/cpptrace/cpptrace.hpp index f509d11..681d105 100644 --- a/include/cpptrace/cpptrace.hpp +++ b/include/cpptrace/cpptrace.hpp @@ -72,6 +72,7 @@ namespace cpptrace { std::uint_least32_t column; // UINT_LEAST32_MAX if not present std::string filename; std::string symbol; + bool is_inline; bool operator==(const stacktrace_frame& other) const { return address == other.address diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 3e423e9..bdf113e 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -175,16 +175,24 @@ namespace cpptrace { << std::left << counter << std::right - << " " - << std::hex - << blue - << "0x" - << std::setw(2 * sizeof(std::uintptr_t)) - << std::setfill('0') - << frame.address - << std::dec - << std::setfill(' ') - << reset + << " "; + if(frame.is_inline) { + stream + << std::setw(2 * sizeof(std::uintptr_t) + 2) + << "(inlined)"; + } else { + stream + << std::hex + << blue + << "0x" + << std::setw(2 * sizeof(std::uintptr_t)) + << std::setfill('0') + << frame.address + << std::dec + << std::setfill(' ') + << reset; + } + stream << " in " << yellow << frame.symbol diff --git a/src/platform/common.hpp b/src/platform/common.hpp index cae8934..2f3bd63 100644 --- a/src/platform/common.hpp +++ b/src/platform/common.hpp @@ -61,7 +61,7 @@ namespace detail { return str; } - static const stacktrace_frame null_frame {0, 0, UINT_LEAST32_MAX, "", ""}; + static const stacktrace_frame null_frame {0, 0, UINT_LEAST32_MAX, "", "", false}; } } diff --git a/src/platform/dwarf.hpp b/src/platform/dwarf.hpp index d9c4e87..b1058d1 100644 --- a/src/platform/dwarf.hpp +++ b/src/platform/dwarf.hpp @@ -26,7 +26,7 @@ namespace libdwarf { [[noreturn]] void handle_dwarf_error(Dwarf_Debug dbg, Dwarf_Error error) { Dwarf_Unsigned ev = dwarf_errno(error); char* msg = dwarf_errmsg(error); - dwarf_dealloc_error(dbg, error); + // dwarf_dealloc_error(dbg, error); throw std::runtime_error(stringf("Cpptrace dwarf error %u %s\n", ev, msg)); } @@ -153,6 +153,20 @@ namespace libdwarf { } } + optional get_unsigned_attribute(Dwarf_Half attr_num) const { + Dwarf_Attribute attr; + if(wrap(dwarf_attr, die, attr_num, &attr) == 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; + } + } + bool has_attr(Dwarf_Half attr_num) const { Dwarf_Bool present = false; VERIFY(wrap(dwarf_hasattr, die, attr_num, &present) == DW_DLV_OK); diff --git a/src/symbols/symbols.hpp b/src/symbols/symbols.hpp index 0354ec6..3a4060d 100644 --- a/src/symbols/symbols.hpp +++ b/src/symbols/symbols.hpp @@ -13,11 +13,22 @@ namespace detail { using collated_vec = std::vector< std::pair, std::reference_wrapper> >; + struct frame_with_inlines { + stacktrace_frame frame; + std::vector inlines; + }; + using collated_vec_with_inlines = std::vector< + std::pair, std::reference_wrapper> + >; std::unordered_map collate_frames( const std::vector& frames, std::vector& trace ); + std::unordered_map collate_frames( + const std::vector& frames, + std::vector& trace + ); #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE namespace libbacktrace { diff --git a/src/symbols/symbols_core.cpp b/src/symbols/symbols_core.cpp index 833e466..14fd19c 100644 --- a/src/symbols/symbols_core.cpp +++ b/src/symbols/symbols_core.cpp @@ -26,6 +26,34 @@ namespace detail { } return entries; } + // TODO: Refactor to eliminate the code duplication + std::unordered_map collate_frames( + const std::vector& frames, + std::vector& trace + ) { + std::unordered_map entries; + for(std::size_t i = 0; i < frames.size(); i++) { + const auto& entry = frames[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()) { + entries[entry.obj_path].emplace_back( + entry, + trace[i] + ); + } + } + return entries; + } + + /* + * + * + * All the code here is awful and I'm not proud of it. + * + * + * + */ void apply_trace( std::vector& result, diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index dcec7e0..8b053e4 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -273,12 +273,12 @@ namespace libdwarf { } } - void retrieve_symbol_for_subprogram( + std::string subprogram_symbol( const die_object& die, - Dwarf_Half dwversion, - stacktrace_frame& frame + Dwarf_Addr pc, + Dwarf_Half dwversion ) { - ASSERT(die.get_tag() == DW_TAG_subprogram); + ASSERT(die.get_tag() == DW_TAG_subprogram || die.get_tag() == DW_TAG_inlined_subroutine); optional name; if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) { name = std::move(linkage_name); @@ -288,30 +288,79 @@ namespace libdwarf { name = std::move(linkage_name); } if(name.has_value()) { - frame.symbol = std::move(name).unwrap(); + return std::move(name).unwrap(); } else { if(die.has_attr(DW_AT_specification)) { die_object spec = die.resolve_reference_attribute(DW_AT_specification); - return retrieve_symbol_for_subprogram(spec, dwversion, frame); + return subprogram_symbol(spec, 0, dwversion); } else if(die.has_attr(DW_AT_abstract_origin)) { die_object spec = die.resolve_reference_attribute(DW_AT_abstract_origin); - return retrieve_symbol_for_subprogram(spec, dwversion, frame); + return subprogram_symbol(spec, 0, dwversion); } } } + void get_inlines_info( + const die_object& die, + Dwarf_Addr pc, + Dwarf_Half dwversion, + std::vector& inlines + ) { + ASSERT(die.get_tag() == DW_TAG_subprogram || die.get_tag() == DW_TAG_inlined_subroutine); + auto child = die.get_child(); + if(child) { + if(pc) walk_die_list( + child, + [this, pc, dwversion, &inlines] (const die_object& die) { + if(die.get_tag() == DW_TAG_inlined_subroutine && die.pc_in_die(dwversion, pc)) { + const auto name = subprogram_symbol(die, pc, dwversion); + const auto file = ""; // die.get_string_attribute(DW_AT_call_file); + const auto line = die.get_unsigned_attribute(DW_AT_call_line); + const auto col = die.get_unsigned_attribute(DW_AT_call_column); + printf(" %s %s:%u:%u\n", name.c_str(), file, line.value_or(0), col.value_or(0)); + inlines.push_back(stacktrace_frame{ + 0, + (unsigned int)line.value_or(0), + (unsigned int)col.value_or(0), + file, + name, + true + }); + get_inlines_info(die, pc, dwversion, inlines); + } + return true; + } + ); + } + } + + std::string retrieve_symbol_for_subprogram( + const die_object& die, + Dwarf_Addr pc, + Dwarf_Half dwversion, + std::vector& inlines + ) { + ASSERT(die.get_tag() == DW_TAG_subprogram); + if(pc) printf("============================\n"); + const auto name = subprogram_symbol(die, pc, dwversion); + if(pc) printf("%s\n", name.c_str()); + get_inlines_info(die, pc, dwversion, inlines); + return name; + } + // returns true if this call found the symbol CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING bool retrieve_symbol_walk( const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, - stacktrace_frame& frame + stacktrace_frame& frame, + std::vector& inlines ) { bool found = false; walk_die_list( die, - [this, pc, dwversion, &frame, &found] (const die_object& die) { + [this, pc, dwversion, &frame, &inlines, &found] (const die_object& die) { if(dump_dwarf) { std::fprintf( stderr, @@ -336,13 +385,13 @@ namespace libdwarf { ); } if(die.get_tag() == DW_TAG_subprogram) { - retrieve_symbol_for_subprogram(die, dwversion, frame); + frame.symbol = retrieve_symbol_for_subprogram(die, pc, dwversion, inlines); found = true; return false; } auto child = die.get_child(); if(child) { - if(retrieve_symbol_walk(child, pc, dwversion, frame)) { + if(retrieve_symbol_walk(child, pc, dwversion, frame, inlines)) { found = true; return false; } @@ -409,10 +458,11 @@ namespace libdwarf { const die_object& cu_die, Dwarf_Addr pc, Dwarf_Half dwversion, - stacktrace_frame& frame + stacktrace_frame& frame, + std::vector& inlines ) { if(get_cache_mode() == cache_mode::prioritize_memory) { - retrieve_symbol_walk(cu_die, pc, dwversion, frame); + retrieve_symbol_walk(cu_die, pc, dwversion, frame, inlines); } else { auto off = cu_die.get_global_offset(); auto it = subprograms_cache.find(off); @@ -444,7 +494,7 @@ namespace libdwarf { if(vec_it != vec.end()) { //vec_it->die.print(); if(vec_it->die.pc_in_die(dwversion, pc)) { - retrieve_symbol_for_subprogram(vec_it->die, dwversion, frame); + frame.symbol = retrieve_symbol_for_subprogram(vec_it->die, pc, dwversion, inlines); } } else { ASSERT(vec.size() == 0, "Vec should be empty?"); @@ -656,7 +706,8 @@ namespace libdwarf { CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING void lookup_pc( Dwarf_Addr pc, - stacktrace_frame& frame + stacktrace_frame& frame, + std::vector& inlines ) { if(dump_dwarf) { std::fprintf(stderr, "%s\n", obj_path.c_str()); @@ -682,12 +733,12 @@ namespace libdwarf { cu_die.print(); } retrieve_line_info(cu_die, pc, frame); // no offset for line info - retrieve_symbol(cu_die, pc, dwversion, frame); + retrieve_symbol(cu_die, pc, dwversion, frame, inlines); } } else { if(get_cache_mode() == cache_mode::prioritize_memory) { // walk for the cu and go from there - walk_compilation_units([this, pc, &frame] (const die_object& cu_die) { + 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); @@ -708,7 +759,7 @@ namespace libdwarf { ); } retrieve_line_info(cu_die, pc, frame); // no offset for line info - retrieve_symbol(cu_die, pc, dwversion, frame); + retrieve_symbol(cu_die, pc, dwversion, frame, inlines); return false; } return true; @@ -733,7 +784,7 @@ namespace libdwarf { //vec_it->die.print(); if(vec_it->die.pc_in_die(vec_it->dwversion, pc)) { retrieve_line_info(vec_it->die, pc, frame); // no offset for line info - retrieve_symbol(vec_it->die, pc, vec_it->dwversion, frame); + retrieve_symbol(vec_it->die, pc, vec_it->dwversion, frame, inlines); } } else { ASSERT(cu_cache.size() == 0, "Vec should be empty?"); @@ -743,7 +794,7 @@ namespace libdwarf { } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING - stacktrace_frame resolve_frame(const object_frame& frame_info) { + frame_with_inlines resolve_frame(const object_frame& frame_info) { stacktrace_frame frame = null_frame; frame.filename = frame_info.obj_path; frame.symbol = frame_info.symbol; @@ -757,17 +808,20 @@ namespace libdwarf { frame_info.symbol.c_str() ); } + std::vector inlines; lookup_pc( frame_info.obj_address, - frame + frame, + inlines ); - return frame; + std::reverse(inlines.begin(), inlines.end()); + return {std::move(frame), std::move(inlines)}; } }; CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING std::vector resolve_frames(const std::vector& frames) { - std::vector trace(frames.size(), null_frame); + std::vector trace(frames.size(), {null_frame, {}}); static std::mutex mutex; // cache resolvers since objects are likely to be traced more than once static std::unordered_map resolver_map; @@ -809,7 +863,14 @@ namespace libdwarf { } } } - return trace; + // flatten trace with inlines + std::vector final_trace; + for(const auto& entry : trace) { + // most recent call first + final_trace.insert(final_trace.end(), entry.inlines.begin(), entry.inlines.end()); + final_trace.push_back(entry.frame); + } + return final_trace; } } }