Some symbol backend refactoring, get rid of pimpl nonsense

This commit is contained in:
Jeremy 2023-08-31 01:08:27 -04:00
parent 871344f7ae
commit 0e27da8c11
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
8 changed files with 265 additions and 352 deletions

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 {};
}
}