Compare commits
10 Commits
main
...
jr/more-th
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3ebd42802 | ||
|
|
d8b9f45a24 | ||
|
|
35e555ae39 | ||
|
|
4c5d4a5ee4 | ||
|
|
530954e69f | ||
|
|
89a7afaded | ||
|
|
91a719e534 | ||
|
|
e80a11d730 | ||
|
|
f135232933 | ||
|
|
f0bedd4104 |
@ -120,13 +120,13 @@ namespace libdwarf {
|
||||
int
|
||||
>::type = 0
|
||||
>
|
||||
int wrap(int (*f)(Args...), Args2&&... args) const {
|
||||
Result<int, internal_error> wrap(int (*f)(Args...), Args2&&... args) const {
|
||||
Dwarf_Error error = nullptr;
|
||||
int ret = f(std::forward<Args2>(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<std::string, internal_error> 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<std::string> 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<std::string, internal_error> 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,7 +425,8 @@ namespace libdwarf {
|
||||
Dwarf_Half dwversion,
|
||||
std::vector<stacktrace_frame>& 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
|
||||
// while looping when we find the target die we need to be able to store a die somewhere that doesn't die
|
||||
@ -390,47 +435,70 @@ namespace libdwarf {
|
||||
optional<std::reference_wrapper<const die_object>> 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<std::reference_wrapper<const die_object>> target_die;
|
||||
walk_die_list(
|
||||
child,
|
||||
child.unwrap_value(),
|
||||
[this, &cu_die, pc, dwversion, &inlines, &target_die, ¤t_obj_holder] (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<std::uint32_t>(line.value_or(0))},
|
||||
{static_cast<std::uint32_t>(col.value_or(0))},
|
||||
{static_cast<std::uint32_t>(line.unwrap_value().value_or(0))},
|
||||
{static_cast<std::uint32_t>(col.unwrap_value().value_or(0))},
|
||||
file,
|
||||
name,
|
||||
name.value_or(""),
|
||||
true
|
||||
});
|
||||
current_obj_holder = die.clone();
|
||||
auto d = die.clone();
|
||||
if(!d) {
|
||||
d.drop_error();
|
||||
return false;
|
||||
}
|
||||
current_obj_holder = std::move(d).unwrap_value();
|
||||
target_die = current_obj_holder;
|
||||
return false;
|
||||
} else {
|
||||
@ -443,15 +511,16 @@ namespace libdwarf {
|
||||
}
|
||||
}
|
||||
|
||||
std::string retrieve_symbol_for_subprogram(
|
||||
Result<std::string, internal_error> retrieve_symbol_for_subprogram(
|
||||
const die_object& cu_die,
|
||||
const die_object& die,
|
||||
Dwarf_Addr pc,
|
||||
Dwarf_Half dwversion,
|
||||
std::vector<stacktrace_frame>& 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);
|
||||
}
|
||||
@ -473,42 +542,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("<error>")
|
||||
);
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -516,7 +595,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;
|
||||
}
|
||||
@ -530,20 +609,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;
|
||||
@ -555,8 +652,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;
|
||||
@ -583,7 +684,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<subprogram_entry> vec;
|
||||
@ -591,8 +695,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(
|
||||
@ -607,7 +711,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?");
|
||||
@ -617,29 +722,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<std::reference_wrapper<line_table_info>> get_line_table(const die_object& cu_die) {
|
||||
auto off = cu_die.get_global_offset();
|
||||
Result<std::reference_wrapper<line_table_info>, 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<line_table_info>{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<decltype(table_count)>::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_entry> line_entries;
|
||||
|
||||
@ -649,7 +760,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,
|
||||
@ -657,7 +768,7 @@ namespace libdwarf {
|
||||
&line_count,
|
||||
&linebuf_actuals,
|
||||
&linecount_actuals
|
||||
) == DW_DLV_OK
|
||||
)
|
||||
);
|
||||
|
||||
// TODO: Make any attempt to note PC ranges? Handle line end sequence?
|
||||
@ -665,12 +776,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;
|
||||
}
|
||||
@ -707,7 +818,7 @@ namespace libdwarf {
|
||||
}
|
||||
|
||||
it = line_tables.insert({off, {version, line_context, std::move(line_entries)}}).first;
|
||||
return it->second;
|
||||
return std::reference_wrapper<line_table_info>{it->second};
|
||||
}
|
||||
}
|
||||
|
||||
@ -719,9 +830,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;
|
||||
@ -739,21 +851,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<std::uint32_t>(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<std::uint32_t>(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); }
|
||||
@ -769,22 +902,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
|
||||
@ -792,7 +937,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;
|
||||
}
|
||||
@ -803,10 +955,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<std::uint32_t>(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); }
|
||||
@ -814,7 +980,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;
|
||||
@ -841,17 +1014,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();
|
||||
@ -870,19 +1066,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("<error>"));
|
||||
}
|
||||
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)
|
||||
);
|
||||
|
||||
@ -17,20 +17,57 @@
|
||||
#include <dwarf.h>
|
||||
#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<Dwarf_Die>::value, "Dwarf_Die not a pointer");
|
||||
static_assert(std::is_pointer<Dwarf_Debug>::value, "Dwarf_Debug not a pointer");
|
||||
|
||||
[[noreturn]] void handle_dwarf_error(Dwarf_Debug dbg, Dwarf_Error error) {
|
||||
static NODISCARD ErrorResult<internal_error> 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<int, internal_error> 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<int, internal_error> wrap(int (*f)(Args...), Args2&&... args) const {
|
||||
Dwarf_Error error = nullptr;
|
||||
int ret = f(std::forward<Args2>(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<die_object, internal_error> 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<die_object, internal_error> 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<die_object, internal_error> 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<die_object, internal_error> 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<std::string, internal_error> 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<std::string> get_string_attribute(Dwarf_Half attr_num) const {
|
||||
Result<optional<std::string>, 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<Dwarf_Unsigned> get_unsigned_attribute(Dwarf_Half attr_num) const {
|
||||
Result<optional<Dwarf_Unsigned>, 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<bool, internal_error> 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<Dwarf_Half, internal_error> 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 "<unknown tag name>";
|
||||
}
|
||||
return "<unknown tag name>";
|
||||
}
|
||||
|
||||
Dwarf_Off get_global_offset() const {
|
||||
Result<Dwarf_Off, internal_error> 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<die_object, internal_error> 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<Dwarf_Unsigned, internal_error> 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<typename F>
|
||||
// callback should return true to keep going
|
||||
void dwarf5_ranges(F callback) const {
|
||||
NODISCARD
|
||||
Result<monostate, internal_error> 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 monostate{}; // normal
|
||||
}
|
||||
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,27 +388,31 @@ 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<typename F>
|
||||
// callback should return true to keep going
|
||||
void dwarf4_ranges(Dwarf_Addr lowpc, F callback) const {
|
||||
NODISCARD
|
||||
Result<monostate, internal_error> 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;
|
||||
PROP_ASSIGN(res, wrap(dwarf_attr, die, DW_AT_ranges, &attr));
|
||||
if(res != DW_DLV_OK) {
|
||||
return monostate{}; // normal
|
||||
}
|
||||
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;
|
||||
PROP_ASSIGN(res2, wrap(dwarf_global_formref, attr, &offset));
|
||||
if(res2 != DW_DLV_OK) {
|
||||
return monostate{}; // normal
|
||||
}
|
||||
Dwarf_Addr baseaddr = 0;
|
||||
if(lowpc != (std::numeric_limits<Dwarf_Addr>::max)()) {
|
||||
@ -348,7 +420,7 @@ namespace libdwarf {
|
||||
}
|
||||
Dwarf_Ranges* ranges = nullptr;
|
||||
Dwarf_Signed count = 0;
|
||||
VERIFY(
|
||||
CHECK_OK(
|
||||
wrap(
|
||||
dwarf_get_ranges_b,
|
||||
dbg,
|
||||
@ -358,7 +430,7 @@ namespace libdwarf {
|
||||
&ranges,
|
||||
&count,
|
||||
nullptr
|
||||
) == DW_DLV_OK
|
||||
)
|
||||
);
|
||||
auto rangeswrapper = raii_wrap(
|
||||
ranges,
|
||||
@ -367,7 +439,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 +448,76 @@ namespace libdwarf {
|
||||
baseaddr = lowpc;
|
||||
}
|
||||
}
|
||||
return monostate{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
// callback should return true to keep going
|
||||
void dwarf_ranges(int version, optional<Dwarf_Addr> pc, F callback) const {
|
||||
NODISCARD
|
||||
Result<monostate, internal_error> dwarf_ranges(int version, optional<Dwarf_Addr> pc, F callback) const {
|
||||
Dwarf_Addr lowpc = (std::numeric_limits<Dwarf_Addr>::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<std::pair<Dwarf_Addr, Dwarf_Addr>> get_rangelist_entries(int version) const {
|
||||
std::vector<std::pair<Dwarf_Addr, Dwarf_Addr>> 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<std::vector<address_range>, internal_error> get_rangelist_entries(int version) const {
|
||||
std::vector<address_range> 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<Dwarf_Bool, internal_error> 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 +525,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("<name error>").c_str()
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -457,13 +542,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 +575,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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<typename... Args>
|
||||
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<typename T>
|
||||
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<void>(0) \
|
||||
: (::cpptrace::detail::assert_fail)( \
|
||||
@ -116,7 +121,7 @@ namespace detail {
|
||||
CPPTRACE_PFUNC, \
|
||||
{}, \
|
||||
##__VA_ARGS__) \
|
||||
)
|
||||
)*/
|
||||
|
||||
// Workaround a compiler warning
|
||||
template<typename T>
|
||||
@ -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.
|
||||
|
||||
@ -325,6 +325,26 @@ namespace detail {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct OkResult {
|
||||
T value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ErrorResult {
|
||||
T value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
OkResult<T> Ok(T value) {
|
||||
return {value};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorResult<T> Error(T value) {
|
||||
return {value};
|
||||
}
|
||||
|
||||
// TODO: Better dump error
|
||||
// TODO: Explicit constructors for value, then add Ok()/Error() helpers
|
||||
template<typename T, typename E, typename std::enable_if<!std::is_same<T, E>::value, int>::type = 0>
|
||||
@ -339,6 +359,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<typename U>
|
||||
Result(OkResult<U>&& value) : value_(std::move(value.value)), active(member::value) {}
|
||||
template<typename U>
|
||||
Result(ErrorResult<U>&& 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_));
|
||||
@ -422,10 +446,11 @@ namespace detail {
|
||||
return has_value() ? std::move(value_) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
void drop_error() const {
|
||||
const Result& drop_error() const {
|
||||
if(is_error()) {
|
||||
std::fprintf(stderr, "%s\n", unwrap_error().what());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user