Inital work to walk inline tree and show inlined calls

This commit is contained in:
Jeremy 2023-11-08 13:05:39 -05:00
parent f4cf8c198b
commit 7ffec7b3c0
No known key found for this signature in database
GPG Key ID: B4C8300FEC395042
7 changed files with 159 additions and 36 deletions

View File

@ -72,6 +72,7 @@ namespace cpptrace {
std::uint_least32_t column; // UINT_LEAST32_MAX if not present std::uint_least32_t column; // UINT_LEAST32_MAX if not present
std::string filename; std::string filename;
std::string symbol; std::string symbol;
bool is_inline;
bool operator==(const stacktrace_frame& other) const { bool operator==(const stacktrace_frame& other) const {
return address == other.address return address == other.address

View File

@ -175,16 +175,24 @@ namespace cpptrace {
<< std::left << std::left
<< counter << counter
<< std::right << std::right
<< " " << " ";
<< std::hex if(frame.is_inline) {
<< blue stream
<< "0x" << std::setw(2 * sizeof(std::uintptr_t) + 2)
<< std::setw(2 * sizeof(std::uintptr_t)) << "(inlined)";
<< std::setfill('0') } else {
<< frame.address stream
<< std::dec << std::hex
<< std::setfill(' ') << blue
<< reset << "0x"
<< std::setw(2 * sizeof(std::uintptr_t))
<< std::setfill('0')
<< frame.address
<< std::dec
<< std::setfill(' ')
<< reset;
}
stream
<< " in " << " in "
<< yellow << yellow
<< frame.symbol << frame.symbol

View File

@ -61,7 +61,7 @@ namespace detail {
return str; 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};
} }
} }

View File

@ -26,7 +26,7 @@ namespace libdwarf {
[[noreturn]] void handle_dwarf_error(Dwarf_Debug dbg, Dwarf_Error error) { [[noreturn]] void handle_dwarf_error(Dwarf_Debug dbg, Dwarf_Error error) {
Dwarf_Unsigned ev = dwarf_errno(error); Dwarf_Unsigned ev = dwarf_errno(error);
char* msg = dwarf_errmsg(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)); throw std::runtime_error(stringf("Cpptrace dwarf error %u %s\n", ev, msg));
} }
@ -153,6 +153,20 @@ namespace libdwarf {
} }
} }
optional<uint64_t> 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 { bool has_attr(Dwarf_Half attr_num) const {
Dwarf_Bool present = false; Dwarf_Bool present = false;
VERIFY(wrap(dwarf_hasattr, die, attr_num, &present) == DW_DLV_OK); VERIFY(wrap(dwarf_hasattr, die, attr_num, &present) == DW_DLV_OK);

View File

@ -13,11 +13,22 @@ namespace detail {
using collated_vec = std::vector< using collated_vec = std::vector<
std::pair<std::reference_wrapper<const object_frame>, std::reference_wrapper<stacktrace_frame>> std::pair<std::reference_wrapper<const object_frame>, std::reference_wrapper<stacktrace_frame>>
>; >;
struct frame_with_inlines {
stacktrace_frame frame;
std::vector<stacktrace_frame> inlines;
};
using collated_vec_with_inlines = std::vector<
std::pair<std::reference_wrapper<const object_frame>, std::reference_wrapper<frame_with_inlines>>
>;
std::unordered_map<std::string, collated_vec> collate_frames( std::unordered_map<std::string, collated_vec> collate_frames(
const std::vector<object_frame>& frames, const std::vector<object_frame>& frames,
std::vector<stacktrace_frame>& trace std::vector<stacktrace_frame>& trace
); );
std::unordered_map<std::string, collated_vec_with_inlines> collate_frames(
const std::vector<object_frame>& frames,
std::vector<frame_with_inlines>& trace
);
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
namespace libbacktrace { namespace libbacktrace {

View File

@ -26,6 +26,34 @@ namespace detail {
} }
return entries; return entries;
} }
// TODO: Refactor to eliminate the code duplication
std::unordered_map<std::string, collated_vec_with_inlines> collate_frames(
const std::vector<object_frame>& frames,
std::vector<frame_with_inlines>& trace
) {
std::unordered_map<std::string, collated_vec_with_inlines> 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( void apply_trace(
std::vector<stacktrace_frame>& result, std::vector<stacktrace_frame>& result,

View File

@ -273,12 +273,12 @@ namespace libdwarf {
} }
} }
void retrieve_symbol_for_subprogram( std::string subprogram_symbol(
const die_object& die, const die_object& die,
Dwarf_Half dwversion, Dwarf_Addr pc,
stacktrace_frame& frame 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<std::string> name; optional<std::string> name;
if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) { if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) {
name = std::move(linkage_name); name = std::move(linkage_name);
@ -288,30 +288,79 @@ namespace libdwarf {
name = std::move(linkage_name); name = std::move(linkage_name);
} }
if(name.has_value()) { if(name.has_value()) {
frame.symbol = std::move(name).unwrap(); return std::move(name).unwrap();
} else { } else {
if(die.has_attr(DW_AT_specification)) { if(die.has_attr(DW_AT_specification)) {
die_object spec = die.resolve_reference_attribute(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)) { } else if(die.has_attr(DW_AT_abstract_origin)) {
die_object spec = die.resolve_reference_attribute(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<stacktrace_frame>& 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<stacktrace_frame>& 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 // returns true if this call found the symbol
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
bool retrieve_symbol_walk( bool retrieve_symbol_walk(
const die_object& die, const die_object& die,
Dwarf_Addr pc, Dwarf_Addr pc,
Dwarf_Half dwversion, Dwarf_Half dwversion,
stacktrace_frame& frame stacktrace_frame& frame,
std::vector<stacktrace_frame>& inlines
) { ) {
bool found = false; bool found = false;
walk_die_list( walk_die_list(
die, die,
[this, pc, dwversion, &frame, &found] (const die_object& die) { [this, pc, dwversion, &frame, &inlines, &found] (const die_object& die) {
if(dump_dwarf) { if(dump_dwarf) {
std::fprintf( std::fprintf(
stderr, stderr,
@ -336,13 +385,13 @@ namespace libdwarf {
); );
} }
if(die.get_tag() == DW_TAG_subprogram) { 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; found = true;
return false; return false;
} }
auto child = die.get_child(); auto child = die.get_child();
if(child) { if(child) {
if(retrieve_symbol_walk(child, pc, dwversion, frame)) { if(retrieve_symbol_walk(child, pc, dwversion, frame, inlines)) {
found = true; found = true;
return false; return false;
} }
@ -409,10 +458,11 @@ namespace libdwarf {
const die_object& cu_die, const die_object& cu_die,
Dwarf_Addr pc, Dwarf_Addr pc,
Dwarf_Half dwversion, Dwarf_Half dwversion,
stacktrace_frame& frame stacktrace_frame& frame,
std::vector<stacktrace_frame>& inlines
) { ) {
if(get_cache_mode() == cache_mode::prioritize_memory) { 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 { } else {
auto off = cu_die.get_global_offset(); auto off = cu_die.get_global_offset();
auto it = subprograms_cache.find(off); auto it = subprograms_cache.find(off);
@ -444,7 +494,7 @@ namespace libdwarf {
if(vec_it != vec.end()) { if(vec_it != vec.end()) {
//vec_it->die.print(); //vec_it->die.print();
if(vec_it->die.pc_in_die(dwversion, pc)) { 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 { } else {
ASSERT(vec.size() == 0, "Vec should be empty?"); ASSERT(vec.size() == 0, "Vec should be empty?");
@ -656,7 +706,8 @@ namespace libdwarf {
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
void lookup_pc( void lookup_pc(
Dwarf_Addr pc, Dwarf_Addr pc,
stacktrace_frame& frame stacktrace_frame& frame,
std::vector<stacktrace_frame>& inlines
) { ) {
if(dump_dwarf) { if(dump_dwarf) {
std::fprintf(stderr, "%s\n", obj_path.c_str()); std::fprintf(stderr, "%s\n", obj_path.c_str());
@ -682,12 +733,12 @@ namespace libdwarf {
cu_die.print(); cu_die.print();
} }
retrieve_line_info(cu_die, pc, frame); // no offset for line info 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 { } else {
if(get_cache_mode() == cache_mode::prioritize_memory) { if(get_cache_mode() == cache_mode::prioritize_memory) {
// walk for the cu and go from there // 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 offset_size = 0;
Dwarf_Half dwversion = 0; Dwarf_Half dwversion = 0;
dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); 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_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 false;
} }
return true; return true;
@ -733,7 +784,7 @@ namespace libdwarf {
//vec_it->die.print(); //vec_it->die.print();
if(vec_it->die.pc_in_die(vec_it->dwversion, pc)) { 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_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 { } else {
ASSERT(cu_cache.size() == 0, "Vec should be empty?"); ASSERT(cu_cache.size() == 0, "Vec should be empty?");
@ -743,7 +794,7 @@ namespace libdwarf {
} }
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING 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; stacktrace_frame frame = null_frame;
frame.filename = frame_info.obj_path; frame.filename = frame_info.obj_path;
frame.symbol = frame_info.symbol; frame.symbol = frame_info.symbol;
@ -757,17 +808,20 @@ namespace libdwarf {
frame_info.symbol.c_str() frame_info.symbol.c_str()
); );
} }
std::vector<stacktrace_frame> inlines;
lookup_pc( lookup_pc(
frame_info.obj_address, 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 CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
std::vector<stacktrace_frame> trace(frames.size(), null_frame); std::vector<frame_with_inlines> trace(frames.size(), {null_frame, {}});
static std::mutex mutex; static std::mutex mutex;
// cache resolvers since objects are likely to be traced more than once // cache resolvers since objects are likely to be traced more than once
static std::unordered_map<std::string, dwarf_resolver> resolver_map; static std::unordered_map<std::string, dwarf_resolver> resolver_map;
@ -809,7 +863,14 @@ namespace libdwarf {
} }
} }
} }
return trace; // flatten trace with inlines
std::vector<stacktrace_frame> 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;
} }
} }
} }