Some symbol backend refactoring, get rid of pimpl nonsense
This commit is contained in:
parent
871344f7ae
commit
0e27da8c11
@ -18,8 +18,7 @@ namespace cpptrace {
|
||||
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
|
||||
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
|
||||
std::vector<void*> frames = detail::capture_frames(skip + 1);
|
||||
detail::symbolizer symbolizer;
|
||||
std::vector<stacktrace_frame> trace = symbolizer.resolve_frames(frames);
|
||||
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
|
||||
for(auto& frame : trace) {
|
||||
frame.symbol = detail::demangle(frame.symbol);
|
||||
}
|
||||
|
||||
@ -8,15 +8,7 @@
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
|
||||
class symbolizer {
|
||||
struct impl;
|
||||
std::unique_ptr<impl> pimpl;
|
||||
public:
|
||||
symbolizer();
|
||||
~symbolizer();
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
|
||||
};
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -182,166 +182,152 @@ namespace cpptrace {
|
||||
}
|
||||
#endif
|
||||
|
||||
struct symbolizer::impl {
|
||||
using target_vec = std::vector<std::pair<std::string, std::reference_wrapper<stacktrace_frame>>>;
|
||||
using target_vec = std::vector<std::pair<std::string, std::reference_wrapper<stacktrace_frame>>>;
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
std::unordered_map<std::string, target_vec> get_addr2line_targets(
|
||||
const std::vector<dlframe>& dlframes,
|
||||
std::vector<stacktrace_frame>& trace
|
||||
) {
|
||||
std::unordered_map<std::string, target_vec> entries;
|
||||
for(std::size_t i = 0; i < dlframes.size(); i++) {
|
||||
const auto& entry = dlframes[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()) {
|
||||
///fprintf(stderr, "%s %s\n", to_hex(entry.raw_address).c_str(), to_hex(entry.raw_address - entry.obj_base + base).c_str());
|
||||
try {
|
||||
entries[entry.obj_path].emplace_back(
|
||||
to_hex(entry.obj_address),
|
||||
trace[i]
|
||||
);
|
||||
} catch(file_error&) {
|
||||
//
|
||||
} catch(...) {
|
||||
throw;
|
||||
}
|
||||
// Set what is known for now, and resolutions from addr2line should overwrite
|
||||
trace[i].filename = entry.obj_path;
|
||||
trace[i].symbol = entry.symbol;
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
std::unordered_map<std::string, target_vec> get_addr2line_targets(
|
||||
const std::vector<dlframe>& dlframes,
|
||||
std::vector<stacktrace_frame>& trace
|
||||
) {
|
||||
std::unordered_map<std::string, target_vec> entries;
|
||||
for(std::size_t i = 0; i < dlframes.size(); i++) {
|
||||
const auto& entry = dlframes[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()) {
|
||||
///fprintf(stderr, "%s %s\n", to_hex(entry.raw_address).c_str(), to_hex(entry.raw_address - entry.obj_base + base).c_str());
|
||||
try {
|
||||
entries[entry.obj_path].emplace_back(
|
||||
to_hex(entry.obj_address),
|
||||
trace[i]
|
||||
);
|
||||
} catch(file_error&) {
|
||||
//
|
||||
} catch(...) {
|
||||
throw;
|
||||
}
|
||||
// Set what is known for now, and resolutions from addr2line should overwrite
|
||||
trace[i].filename = entry.obj_path;
|
||||
trace[i].symbol = entry.symbol;
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
void update_trace(const std::string& line, size_t entry_index, const target_vec& entries_vec) {
|
||||
#if !IS_APPLE
|
||||
// Result will be of the form "<symbol> at path:line"
|
||||
// The path may be ?? if addr2line cannot resolve, line may be ?
|
||||
// Edge cases:
|
||||
// ?? ??:0
|
||||
// symbol :?
|
||||
const std::size_t at_location = line.find(" at ");
|
||||
std::size_t symbol_end;
|
||||
std::size_t filename_start;
|
||||
if(at_location != std::string::npos) {
|
||||
symbol_end = at_location;
|
||||
filename_start = at_location + 4;
|
||||
} else {
|
||||
internal_verify(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output");
|
||||
symbol_end = 2;
|
||||
filename_start = 3;
|
||||
}
|
||||
auto symbol = line.substr(0, symbol_end);
|
||||
auto colon = line.rfind(':');
|
||||
internal_verify(colon != std::string::npos);
|
||||
internal_verify(colon >= filename_start); // :? to deal with "symbol :?" edge case
|
||||
auto filename = line.substr(filename_start, colon - filename_start);
|
||||
auto line_number = line.substr(colon + 1);
|
||||
if(line_number != "?") {
|
||||
entries_vec[entry_index].second.get().line = std::stoi(line_number);
|
||||
}
|
||||
if(!filename.empty() && filename != "??") {
|
||||
entries_vec[entry_index].second.get().filename = filename;
|
||||
}
|
||||
if(!symbol.empty()) {
|
||||
entries_vec[entry_index].second.get().symbol = symbol;
|
||||
}
|
||||
#else
|
||||
// Result will be of the form "<symbol> (in <object name>) (file:line)"
|
||||
// The symbol may just be the given address if atos can't resolve it
|
||||
// Examples:
|
||||
// trace() (in demo) (demo.cpp:8)
|
||||
// 0x100003b70 (in demo)
|
||||
// 0xffffffffffffffff
|
||||
// foo (in bar) + 14
|
||||
// I'm making some assumptions here. Support may need to be improved later. This is tricky output to
|
||||
// parse.
|
||||
const std::size_t in_location = line.find(" (in ");
|
||||
if(in_location == std::string::npos) {
|
||||
// presumably the 0xffffffffffffffff case
|
||||
return;
|
||||
}
|
||||
const std::size_t symbol_end = in_location;
|
||||
entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end);
|
||||
const std::size_t obj_end = line.find(")", in_location);
|
||||
internal_verify(
|
||||
obj_end != std::string::npos,
|
||||
"Unexpected edge case while processing addr2line/atos output"
|
||||
);
|
||||
const std::size_t filename_start = line.find(") (", obj_end);
|
||||
if(filename_start == std::string::npos) {
|
||||
// presumably something like 0x100003b70 (in demo) or foo (in bar) + 14
|
||||
return;
|
||||
}
|
||||
const std::size_t filename_end = line.find(":", filename_start);
|
||||
internal_verify(
|
||||
filename_end != std::string::npos,
|
||||
"Unexpected edge case while processing addr2line/atos output"
|
||||
);
|
||||
entries_vec[entry_index].second.get().filename = line.substr(filename_start + 3, filename_end - filename_start - 3);
|
||||
const std::size_t line_start = filename_end + 1;
|
||||
const std::size_t line_end = line.find(")", filename_end);
|
||||
internal_verify(
|
||||
line_end == line.size() - 1,
|
||||
"Unexpected edge case while processing addr2line/atos output"
|
||||
);
|
||||
entries_vec[entry_index].second.get().line = std::stoi(line.substr(line_start, line_end - line_start));
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||
// TODO: Refactor better
|
||||
std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" });
|
||||
for(size_t i = 0; i < frames.size(); i++) {
|
||||
trace[i].address = reinterpret_cast<uintptr_t>(frames[i]);
|
||||
}
|
||||
if(has_addr2line()) {
|
||||
const std::vector<dlframe> dlframes = get_frames_object_info(frames);
|
||||
const auto entries = get_addr2line_targets(dlframes, trace);
|
||||
for(const auto& entry : entries) {
|
||||
const auto& object_name = entry.first;
|
||||
const auto& entries_vec = entry.second;
|
||||
// You may ask why it'd ever happen that there could be an empty entries_vec array, if there're
|
||||
// no addresses why would get_addr2line_targets do anything? The reason is because if things in
|
||||
// get_addr2line_targets fail it will silently skip. This is partly an optimization but also an
|
||||
// assertion below will fail if addr2line is given an empty input.
|
||||
if(entries_vec.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::string address_input;
|
||||
for(const auto& pair : entries_vec) {
|
||||
address_input += pair.first;
|
||||
#if !IS_WINDOWS
|
||||
address_input += '\n';
|
||||
#else
|
||||
address_input += ' ';
|
||||
#endif
|
||||
}
|
||||
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
|
||||
internal_verify(output.size() == entries_vec.size());
|
||||
for(size_t i = 0; i < output.size(); i++) {
|
||||
update_trace(output[i], i, entries_vec);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
void update_trace(const std::string& line, size_t entry_index, const target_vec& entries_vec) {
|
||||
#if !IS_APPLE
|
||||
// Result will be of the form "<symbol> at path:line"
|
||||
// The path may be ?? if addr2line cannot resolve, line may be ?
|
||||
// Edge cases:
|
||||
// ?? ??:0
|
||||
// symbol :?
|
||||
const std::size_t at_location = line.find(" at ");
|
||||
std::size_t symbol_end;
|
||||
std::size_t filename_start;
|
||||
if(at_location != std::string::npos) {
|
||||
symbol_end = at_location;
|
||||
filename_start = at_location + 4;
|
||||
} else {
|
||||
internal_verify(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output");
|
||||
symbol_end = 2;
|
||||
filename_start = 3;
|
||||
}
|
||||
auto symbol = line.substr(0, symbol_end);
|
||||
auto colon = line.rfind(':');
|
||||
internal_verify(colon != std::string::npos);
|
||||
internal_verify(colon >= filename_start); // :? to deal with "symbol :?" edge case
|
||||
auto filename = line.substr(filename_start, colon - filename_start);
|
||||
auto line_number = line.substr(colon + 1);
|
||||
if(line_number != "?") {
|
||||
entries_vec[entry_index].second.get().line = std::stoi(line_number);
|
||||
}
|
||||
if(!filename.empty() && filename != "??") {
|
||||
entries_vec[entry_index].second.get().filename = filename;
|
||||
}
|
||||
if(!symbol.empty()) {
|
||||
entries_vec[entry_index].second.get().symbol = symbol;
|
||||
}
|
||||
#else
|
||||
// Result will be of the form "<symbol> (in <object name>) (file:line)"
|
||||
// The symbol may just be the given address if atos can't resolve it
|
||||
// Examples:
|
||||
// trace() (in demo) (demo.cpp:8)
|
||||
// 0x100003b70 (in demo)
|
||||
// 0xffffffffffffffff
|
||||
// foo (in bar) + 14
|
||||
// I'm making some assumptions here. Support may need to be improved later. This is tricky output to
|
||||
// parse.
|
||||
const std::size_t in_location = line.find(" (in ");
|
||||
if(in_location == std::string::npos) {
|
||||
// presumably the 0xffffffffffffffff case
|
||||
return;
|
||||
}
|
||||
const std::size_t symbol_end = in_location;
|
||||
entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end);
|
||||
const std::size_t obj_end = line.find(")", in_location);
|
||||
internal_verify(
|
||||
obj_end != std::string::npos,
|
||||
"Unexpected edge case while processing addr2line/atos output"
|
||||
);
|
||||
const std::size_t filename_start = line.find(") (", obj_end);
|
||||
if(filename_start == std::string::npos) {
|
||||
// presumably something like 0x100003b70 (in demo) or foo (in bar) + 14
|
||||
return;
|
||||
}
|
||||
const std::size_t filename_end = line.find(":", filename_start);
|
||||
internal_verify(
|
||||
filename_end != std::string::npos,
|
||||
"Unexpected edge case while processing addr2line/atos output"
|
||||
);
|
||||
entries_vec[entry_index].second.get().filename = line.substr(filename_start + 3, filename_end - filename_start - 3);
|
||||
const std::size_t line_start = filename_end + 1;
|
||||
const std::size_t line_end = line.find(")", filename_end);
|
||||
internal_verify(
|
||||
line_end == line.size() - 1,
|
||||
"Unexpected edge case while processing addr2line/atos output"
|
||||
);
|
||||
entries_vec[entry_index].second.get().line = std::stoi(line.substr(line_start, line_end - line_start));
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||
// TODO: Refactor better
|
||||
std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" });
|
||||
for(size_t i = 0; i < frames.size(); i++) {
|
||||
trace[i].address = reinterpret_cast<uintptr_t>(frames[i]);
|
||||
}
|
||||
if(has_addr2line()) {
|
||||
const std::vector<dlframe> dlframes = get_frames_object_info(frames);
|
||||
const auto entries = get_addr2line_targets(dlframes, trace);
|
||||
for(const auto& entry : entries) {
|
||||
const auto& object_name = entry.first;
|
||||
const auto& entries_vec = entry.second;
|
||||
// You may ask why it'd ever happen that there could be an empty entries_vec array, if there're
|
||||
// no addresses why would get_addr2line_targets do anything? The reason is because if things in
|
||||
// get_addr2line_targets fail it will silently skip. This is partly an optimization but also an
|
||||
// assertion below will fail if addr2line is given an empty input.
|
||||
if(entries_vec.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::string address_input;
|
||||
for(const auto& pair : entries_vec) {
|
||||
address_input += pair.first;
|
||||
#if !IS_WINDOWS
|
||||
address_input += '\n';
|
||||
#else
|
||||
address_input += ' ';
|
||||
#endif
|
||||
}
|
||||
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
|
||||
internal_verify(output.size() == entries_vec.size());
|
||||
for(size_t i = 0; i < output.size(); i++) {
|
||||
update_trace(output[i], i, entries_vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
|
||||
symbolizer::symbolizer() : pimpl{new impl} {}
|
||||
symbolizer::~symbolizer() = default;
|
||||
|
||||
//stacktrace_frame symbolizer::resolve_frame(void* addr) {
|
||||
// return pimpl->resolve_frame(addr);
|
||||
//}
|
||||
|
||||
std::vector<stacktrace_frame> symbolizer::resolve_frames(const std::vector<void*>& frames) {
|
||||
return pimpl->resolve_frames(frames);
|
||||
return trace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,109 +302,94 @@ namespace cpptrace {
|
||||
std::mutex dbghelp_lock;
|
||||
|
||||
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
|
||||
struct symbolizer::impl {
|
||||
bool good = true;
|
||||
HANDLE proc;
|
||||
|
||||
impl() {
|
||||
// TODO: When does this need to be called? Can it be moved to the symbolizer?
|
||||
SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
|
||||
proc = GetCurrentProcess();
|
||||
if(!SymInitialize(proc, NULL, TRUE)) {
|
||||
good = false;
|
||||
}
|
||||
}
|
||||
|
||||
~impl() {
|
||||
if(!SymCleanup(proc)) {
|
||||
//throw std::logic_error("SymCleanup failed");
|
||||
}
|
||||
}
|
||||
|
||||
stacktrace_frame resolve_frame(void* addr) {
|
||||
const std::lock_guard<std::mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe
|
||||
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||
SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer;
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
union { DWORD64 a; DWORD b; } displacement;
|
||||
IMAGEHLP_LINE64 line;
|
||||
bool got_line = SymGetLineFromAddr64(proc, (DWORD64)addr, &displacement.b, &line);
|
||||
if(SymFromAddr(proc, (DWORD64)addr, &displacement.a, symbol)) {
|
||||
if(got_line) {
|
||||
IMAGEHLP_STACK_FRAME frame;
|
||||
frame.InstructionOffset = symbol->Address;
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetcontext
|
||||
// "If you call SymSetContext to set the context to its current value, the
|
||||
// function fails but GetLastError returns ERROR_SUCCESS."
|
||||
// This is the stupidest fucking api I've ever worked with.
|
||||
if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) {
|
||||
fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n");
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
static_cast<std::uint_least32_t>(line.LineNumber),
|
||||
0,
|
||||
line.FileName,
|
||||
symbol->Name
|
||||
};
|
||||
}
|
||||
DWORD n_children = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_COUNT, true>(
|
||||
symbol->TypeIndex,
|
||||
proc,
|
||||
symbol->ModBase
|
||||
);
|
||||
DWORD class_parent_id = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_CLASSPARENTID, true>(
|
||||
symbol->TypeIndex,
|
||||
proc,
|
||||
symbol->ModBase
|
||||
);
|
||||
function_info fi { proc, symbol->ModBase, 0, int(n_children), class_parent_id != (DWORD)-1, "" };
|
||||
SymEnumSymbols(proc, 0, nullptr, enumerator_callback, &fi);
|
||||
std::string signature = symbol->Name + std::string("(") + fi.str + ")";
|
||||
// There's a phenomina with DIA not inserting commas after template parameters. Fix them here.
|
||||
static std::regex comma_re(R"(,(?=\S))");
|
||||
signature = std::regex_replace(signature, comma_re, ", ");
|
||||
stacktrace_frame resolve_frame(HANDLE proc, void* addr) {
|
||||
const std::lock_guard<std::mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe
|
||||
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||
SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer;
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
union { DWORD64 a; DWORD b; } displacement;
|
||||
IMAGEHLP_LINE64 line;
|
||||
bool got_line = SymGetLineFromAddr64(proc, (DWORD64)addr, &displacement.b, &line);
|
||||
if(SymFromAddr(proc, (DWORD64)addr, &displacement.a, symbol)) {
|
||||
if(got_line) {
|
||||
IMAGEHLP_STACK_FRAME frame;
|
||||
frame.InstructionOffset = symbol->Address;
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetcontext
|
||||
// "If you call SymSetContext to set the context to its current value, the
|
||||
// function fails but GetLastError returns ERROR_SUCCESS."
|
||||
// This is the stupidest fucking api I've ever worked with.
|
||||
if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) {
|
||||
fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n");
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
static_cast<std::uint_least32_t>(line.LineNumber),
|
||||
0,
|
||||
line.FileName,
|
||||
signature
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
0,
|
||||
0,
|
||||
"",
|
||||
symbol->Name
|
||||
};
|
||||
}
|
||||
DWORD n_children = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_COUNT, true>(
|
||||
symbol->TypeIndex,
|
||||
proc,
|
||||
symbol->ModBase
|
||||
);
|
||||
DWORD class_parent_id = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_CLASSPARENTID, true>(
|
||||
symbol->TypeIndex,
|
||||
proc,
|
||||
symbol->ModBase
|
||||
);
|
||||
function_info fi { proc, symbol->ModBase, 0, int(n_children), class_parent_id != (DWORD)-1, "" };
|
||||
SymEnumSymbols(proc, 0, nullptr, enumerator_callback, &fi);
|
||||
std::string signature = symbol->Name + std::string("(") + fi.str + ")";
|
||||
// There's a phenomina with DIA not inserting commas after template parameters. Fix them here.
|
||||
static std::regex comma_re(R"(,(?=\S))");
|
||||
signature = std::regex_replace(signature, comma_re, ", ");
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
static_cast<std::uint_least32_t>(line.LineNumber),
|
||||
0,
|
||||
line.FileName,
|
||||
signature
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
0,
|
||||
0,
|
||||
"",
|
||||
""
|
||||
symbol->Name
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
0,
|
||||
0,
|
||||
"",
|
||||
""
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
symbolizer::symbolizer() : pimpl{new impl} {}
|
||||
symbolizer::~symbolizer() = default;
|
||||
|
||||
//stacktrace_frame symbolizer::resolve_frame(void* addr) {
|
||||
// return pimpl->resolve_frame(addr);
|
||||
//}
|
||||
|
||||
std::vector<stacktrace_frame> symbolizer::resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> trace;
|
||||
trace.reserve(frames.size());
|
||||
for(const auto frame : frames) {
|
||||
trace.push_back(pimpl->resolve_frame(frame));
|
||||
|
||||
// TODO: When does this need to be called? Can it be moved to the symbolizer?
|
||||
SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
|
||||
HANDLE proc = GetCurrentProcess();
|
||||
if(!SymInitialize(proc, NULL, TRUE)) {
|
||||
//TODO?
|
||||
throw std::logic_error("SymInitialize failed");
|
||||
}
|
||||
for(const auto frame : frames) {
|
||||
trace.push_back(resolve_frame(proc, frame));
|
||||
}
|
||||
if(!SymCleanup(proc)) {
|
||||
//throw std::logic_error("SymCleanup failed");
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,43 +11,32 @@
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
struct symbolizer::impl {
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
stacktrace_frame resolve_frame(const void* addr) {
|
||||
Dl_info info;
|
||||
if(dladdr(addr, &info)) { // thread-safe
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
0,
|
||||
0,
|
||||
info.dli_fname ? info.dli_fname : "",
|
||||
info.dli_sname ? info.dli_sname : ""
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
0,
|
||||
0,
|
||||
"",
|
||||
""
|
||||
};
|
||||
}
|
||||
stacktrace_frame resolve_frame(const void* addr) {
|
||||
Dl_info info;
|
||||
if(dladdr(addr, &info)) { // thread-safe
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
0,
|
||||
0,
|
||||
info.dli_fname ? info.dli_fname : "",
|
||||
info.dli_sname ? info.dli_sname : ""
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
0,
|
||||
0,
|
||||
"",
|
||||
""
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
|
||||
symbolizer::symbolizer() : pimpl{new impl} {}
|
||||
symbolizer::~symbolizer() = default;
|
||||
|
||||
//stacktrace_frame symbolizer::resolve_frame(void* addr) {
|
||||
// return pimpl->resolve_frame(addr);
|
||||
//}
|
||||
|
||||
std::vector<stacktrace_frame> symbolizer::resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> trace;
|
||||
trace.reserve(frames.size());
|
||||
for(const void* frame : frames) {
|
||||
trace.push_back(pimpl->resolve_frame(frame));
|
||||
trace.push_back(resolve_frame(frame));
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
|
||||
@ -57,45 +57,34 @@ namespace cpptrace {
|
||||
}
|
||||
|
||||
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
|
||||
struct symbolizer::impl {
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
stacktrace_frame resolve_frame(const void* addr) {
|
||||
stacktrace_frame frame;
|
||||
frame.col = 0;
|
||||
backtrace_pcinfo(
|
||||
stacktrace_frame resolve_frame(const void* addr) {
|
||||
stacktrace_frame frame;
|
||||
frame.col = 0;
|
||||
backtrace_pcinfo(
|
||||
get_backtrace_state(),
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
full_callback,
|
||||
error_callback,
|
||||
&frame
|
||||
);
|
||||
if(frame.symbol.empty()) {
|
||||
// fallback, try to at least recover the symbol name with backtrace_syminfo
|
||||
backtrace_syminfo(
|
||||
get_backtrace_state(),
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
full_callback,
|
||||
syminfo_callback,
|
||||
error_callback,
|
||||
&frame
|
||||
);
|
||||
if(frame.symbol.empty()) {
|
||||
// fallback, try to at least recover the symbol name with backtrace_syminfo
|
||||
backtrace_syminfo(
|
||||
get_backtrace_state(),
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
syminfo_callback,
|
||||
error_callback,
|
||||
&frame
|
||||
);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
return frame;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
|
||||
symbolizer::symbolizer() : pimpl{new impl} {}
|
||||
symbolizer::~symbolizer() = default;
|
||||
|
||||
//stacktrace_frame symbolizer::resolve_frame(void* addr) {
|
||||
// return pimpl->resolve_frame(addr);
|
||||
//}
|
||||
|
||||
std::vector<stacktrace_frame> symbolizer::resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> trace;
|
||||
trace.reserve(frames.size());
|
||||
for(const void* frame : frames) {
|
||||
trace.push_back(pimpl->resolve_frame(frame));
|
||||
trace.push_back(resolve_frame(frame));
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
|
||||
@ -856,35 +856,24 @@ namespace cpptrace {
|
||||
dwarf_finish(dbg);
|
||||
}
|
||||
|
||||
struct symbolizer::impl {
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
stacktrace_frame resolve_frame(const dlframe& frame_info) {
|
||||
stacktrace_frame frame{};
|
||||
frame.filename = frame_info.obj_path;
|
||||
frame.symbol = frame_info.symbol;
|
||||
frame.address = frame_info.raw_address;
|
||||
lookup_pc2(
|
||||
frame_info.obj_path.c_str(),
|
||||
frame_info.obj_address,
|
||||
frame
|
||||
);
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
stacktrace_frame resolve_frame(const dlframe& frame_info) {
|
||||
stacktrace_frame frame{};
|
||||
frame.filename = frame_info.obj_path;
|
||||
frame.symbol = frame_info.symbol;
|
||||
frame.address = frame_info.raw_address;
|
||||
lookup_pc2(
|
||||
frame_info.obj_path.c_str(),
|
||||
frame_info.obj_address,
|
||||
frame
|
||||
);
|
||||
return frame;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
|
||||
symbolizer::symbolizer() : pimpl{new impl} {}
|
||||
symbolizer::~symbolizer() = default;
|
||||
|
||||
//stacktrace_frame symbolizer::resolve_frame(void* addr) {
|
||||
// return pimpl->resolve_frame(addr);
|
||||
//}
|
||||
|
||||
std::vector<stacktrace_frame> symbolizer::resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> trace;
|
||||
trace.reserve(frames.size());
|
||||
for(const auto& frame : get_frames_object_info(frames)) {
|
||||
trace.push_back(pimpl->resolve_frame(frame));
|
||||
trace.push_back(resolve_frame(frame));
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
|
||||
@ -7,21 +7,7 @@
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
symbolizer::symbolizer() = default;
|
||||
symbolizer::~symbolizer() = default;
|
||||
|
||||
// stacktrace_frame symbolizer::resolve_frame(void*) {
|
||||
// return {
|
||||
// 0,
|
||||
// 0,
|
||||
// 0,
|
||||
// "",
|
||||
// "",
|
||||
// };
|
||||
// }
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
std::vector<stacktrace_frame> symbolizer::resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||
return std::vector<stacktrace_frame>(frames.size(), {
|
||||
0,
|
||||
0,
|
||||
@ -30,8 +16,6 @@ namespace cpptrace {
|
||||
""
|
||||
});
|
||||
}
|
||||
|
||||
struct symbolizer::impl {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user