Inital work to walk inline tree and show inlined calls
This commit is contained in:
parent
f4cf8c198b
commit
7ffec7b3c0
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user