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
|
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
|
||||||
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
|
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
|
||||||
std::vector<void*> frames = detail::capture_frames(skip + 1);
|
std::vector<void*> frames = detail::capture_frames(skip + 1);
|
||||||
detail::symbolizer symbolizer;
|
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
|
||||||
std::vector<stacktrace_frame> trace = symbolizer.resolve_frames(frames);
|
|
||||||
for(auto& frame : trace) {
|
for(auto& frame : trace) {
|
||||||
frame.symbol = detail::demangle(frame.symbol);
|
frame.symbol = detail::demangle(frame.symbol);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,15 +8,7 @@
|
|||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
|
||||||
class symbolizer {
|
|
||||||
struct impl;
|
|
||||||
std::unique_ptr<impl> pimpl;
|
|
||||||
public:
|
|
||||||
symbolizer();
|
|
||||||
~symbolizer();
|
|
||||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -182,166 +182,152 @@ namespace cpptrace {
|
|||||||
}
|
}
|
||||||
#endif
|
#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)
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||||
std::unordered_map<std::string, target_vec> get_addr2line_targets(
|
std::unordered_map<std::string, target_vec> get_addr2line_targets(
|
||||||
const std::vector<dlframe>& dlframes,
|
const std::vector<dlframe>& dlframes,
|
||||||
std::vector<stacktrace_frame>& trace
|
std::vector<stacktrace_frame>& trace
|
||||||
) {
|
) {
|
||||||
std::unordered_map<std::string, target_vec> entries;
|
std::unordered_map<std::string, target_vec> entries;
|
||||||
for(std::size_t i = 0; i < dlframes.size(); i++) {
|
for(std::size_t i = 0; i < dlframes.size(); i++) {
|
||||||
const auto& entry = dlframes[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
|
// 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`.
|
// on macos when looking up the shared object containing `start`.
|
||||||
if(!entry.obj_path.empty()) {
|
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());
|
///fprintf(stderr, "%s %s\n", to_hex(entry.raw_address).c_str(), to_hex(entry.raw_address - entry.obj_base + base).c_str());
|
||||||
try {
|
try {
|
||||||
entries[entry.obj_path].emplace_back(
|
entries[entry.obj_path].emplace_back(
|
||||||
to_hex(entry.obj_address),
|
to_hex(entry.obj_address),
|
||||||
trace[i]
|
trace[i]
|
||||||
);
|
);
|
||||||
} catch(file_error&) {
|
} catch(file_error&) {
|
||||||
//
|
//
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
// Set what is known for now, and resolutions from addr2line should overwrite
|
// Set what is known for now, and resolutions from addr2line should overwrite
|
||||||
trace[i].filename = entry.obj_path;
|
trace[i].filename = entry.obj_path;
|
||||||
trace[i].symbol = entry.symbol;
|
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;
|
|
||||||
}
|
}
|
||||||
|
return trace;
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -302,109 +302,94 @@ namespace cpptrace {
|
|||||||
std::mutex dbghelp_lock;
|
std::mutex dbghelp_lock;
|
||||||
|
|
||||||
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
|
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
|
||||||
struct symbolizer::impl {
|
stacktrace_frame resolve_frame(HANDLE proc, void* addr) {
|
||||||
bool good = true;
|
const std::lock_guard<std::mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe
|
||||||
HANDLE proc;
|
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||||
|
SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer;
|
||||||
impl() {
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||||
// TODO: When does this need to be called? Can it be moved to the symbolizer?
|
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||||
SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
|
union { DWORD64 a; DWORD b; } displacement;
|
||||||
proc = GetCurrentProcess();
|
IMAGEHLP_LINE64 line;
|
||||||
if(!SymInitialize(proc, NULL, TRUE)) {
|
bool got_line = SymGetLineFromAddr64(proc, (DWORD64)addr, &displacement.b, &line);
|
||||||
good = false;
|
if(SymFromAddr(proc, (DWORD64)addr, &displacement.a, symbol)) {
|
||||||
}
|
if(got_line) {
|
||||||
}
|
IMAGEHLP_STACK_FRAME frame;
|
||||||
|
frame.InstructionOffset = symbol->Address;
|
||||||
~impl() {
|
// https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetcontext
|
||||||
if(!SymCleanup(proc)) {
|
// "If you call SymSetContext to set the context to its current value, the
|
||||||
//throw std::logic_error("SymCleanup failed");
|
// 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");
|
||||||
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, ", ");
|
|
||||||
return {
|
return {
|
||||||
reinterpret_cast<uintptr_t>(addr),
|
reinterpret_cast<uintptr_t>(addr),
|
||||||
static_cast<std::uint_least32_t>(line.LineNumber),
|
static_cast<std::uint_least32_t>(line.LineNumber),
|
||||||
0,
|
0,
|
||||||
line.FileName,
|
line.FileName,
|
||||||
signature
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
reinterpret_cast<uintptr_t>(addr),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
"",
|
|
||||||
symbol->Name
|
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 {
|
} else {
|
||||||
return {
|
return {
|
||||||
reinterpret_cast<uintptr_t>(addr),
|
reinterpret_cast<uintptr_t>(addr),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
"",
|
"",
|
||||||
""
|
symbol->Name
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
reinterpret_cast<uintptr_t>(addr),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
symbolizer::symbolizer() : pimpl{new impl} {}
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||||
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> trace;
|
std::vector<stacktrace_frame> trace;
|
||||||
trace.reserve(frames.size());
|
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;
|
return trace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,43 +11,32 @@
|
|||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
struct symbolizer::impl {
|
stacktrace_frame resolve_frame(const void* addr) {
|
||||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
Dl_info info;
|
||||||
stacktrace_frame resolve_frame(const void* addr) {
|
if(dladdr(addr, &info)) { // thread-safe
|
||||||
Dl_info info;
|
return {
|
||||||
if(dladdr(addr, &info)) { // thread-safe
|
reinterpret_cast<uintptr_t>(addr),
|
||||||
return {
|
0,
|
||||||
reinterpret_cast<uintptr_t>(addr),
|
0,
|
||||||
0,
|
info.dli_fname ? info.dli_fname : "",
|
||||||
0,
|
info.dli_sname ? info.dli_sname : ""
|
||||||
info.dli_fname ? info.dli_fname : "",
|
};
|
||||||
info.dli_sname ? info.dli_sname : ""
|
} else {
|
||||||
};
|
return {
|
||||||
} else {
|
reinterpret_cast<uintptr_t>(addr),
|
||||||
return {
|
0,
|
||||||
reinterpret_cast<uintptr_t>(addr),
|
0,
|
||||||
0,
|
"",
|
||||||
0,
|
""
|
||||||
"",
|
};
|
||||||
""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||||
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> trace;
|
std::vector<stacktrace_frame> trace;
|
||||||
trace.reserve(frames.size());
|
trace.reserve(frames.size());
|
||||||
for(const void* frame : frames) {
|
for(const void* frame : frames) {
|
||||||
trace.push_back(pimpl->resolve_frame(frame));
|
trace.push_back(resolve_frame(frame));
|
||||||
}
|
}
|
||||||
return trace;
|
return trace;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,45 +57,34 @@ namespace cpptrace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
|
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
|
||||||
struct symbolizer::impl {
|
stacktrace_frame resolve_frame(const void* addr) {
|
||||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
stacktrace_frame frame;
|
||||||
stacktrace_frame resolve_frame(const void* addr) {
|
frame.col = 0;
|
||||||
stacktrace_frame frame;
|
backtrace_pcinfo(
|
||||||
frame.col = 0;
|
get_backtrace_state(),
|
||||||
backtrace_pcinfo(
|
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(),
|
get_backtrace_state(),
|
||||||
reinterpret_cast<uintptr_t>(addr),
|
reinterpret_cast<uintptr_t>(addr),
|
||||||
full_callback,
|
syminfo_callback,
|
||||||
error_callback,
|
error_callback,
|
||||||
&frame
|
&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)
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||||
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> trace;
|
std::vector<stacktrace_frame> trace;
|
||||||
trace.reserve(frames.size());
|
trace.reserve(frames.size());
|
||||||
for(const void* frame : frames) {
|
for(const void* frame : frames) {
|
||||||
trace.push_back(pimpl->resolve_frame(frame));
|
trace.push_back(resolve_frame(frame));
|
||||||
}
|
}
|
||||||
return trace;
|
return trace;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -856,35 +856,24 @@ namespace cpptrace {
|
|||||||
dwarf_finish(dbg);
|
dwarf_finish(dbg);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct symbolizer::impl {
|
stacktrace_frame resolve_frame(const dlframe& frame_info) {
|
||||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
stacktrace_frame frame{};
|
||||||
stacktrace_frame resolve_frame(const dlframe& frame_info) {
|
frame.filename = frame_info.obj_path;
|
||||||
stacktrace_frame frame{};
|
frame.symbol = frame_info.symbol;
|
||||||
frame.filename = frame_info.obj_path;
|
frame.address = frame_info.raw_address;
|
||||||
frame.symbol = frame_info.symbol;
|
lookup_pc2(
|
||||||
frame.address = frame_info.raw_address;
|
frame_info.obj_path.c_str(),
|
||||||
lookup_pc2(
|
frame_info.obj_address,
|
||||||
frame_info.obj_path.c_str(),
|
frame
|
||||||
frame_info.obj_address,
|
);
|
||||||
frame
|
return frame;
|
||||||
);
|
}
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||||
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> trace;
|
std::vector<stacktrace_frame> trace;
|
||||||
trace.reserve(frames.size());
|
trace.reserve(frames.size());
|
||||||
for(const auto& frame : get_frames_object_info(frames)) {
|
for(const auto& frame : get_frames_object_info(frames)) {
|
||||||
trace.push_back(pimpl->resolve_frame(frame));
|
trace.push_back(resolve_frame(frame));
|
||||||
}
|
}
|
||||||
return trace;
|
return trace;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,21 +7,7 @@
|
|||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
symbolizer::symbolizer() = default;
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||||
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) {
|
|
||||||
return std::vector<stacktrace_frame>(frames.size(), {
|
return std::vector<stacktrace_frame>(frames.size(), {
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@ -30,8 +16,6 @@ namespace cpptrace {
|
|||||||
""
|
""
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct symbolizer::impl {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user