diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 035551e..be13292 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -18,8 +18,7 @@ namespace cpptrace { CPPTRACE_FORCE_NO_INLINE CPPTRACE_API std::vector generate_trace(std::uint32_t skip) { std::vector frames = detail::capture_frames(skip + 1); - detail::symbolizer symbolizer; - std::vector trace = symbolizer.resolve_frames(frames); + std::vector trace = detail::resolve_frames(frames); for(auto& frame : trace) { frame.symbol = detail::demangle(frame.symbol); } diff --git a/src/symbols/symbols.hpp b/src/symbols/symbols.hpp index 0a4fdbe..1c23597 100644 --- a/src/symbols/symbols.hpp +++ b/src/symbols/symbols.hpp @@ -8,15 +8,7 @@ namespace cpptrace { namespace detail { - // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) - class symbolizer { - struct impl; - std::unique_ptr pimpl; - public: - symbolizer(); - ~symbolizer(); - std::vector resolve_frames(const std::vector& frames); - }; + std::vector resolve_frames(const std::vector& frames); } } diff --git a/src/symbols/symbols_with_addr2line.cpp b/src/symbols/symbols_with_addr2line.cpp index 9e3a4ed..54c0878 100644 --- a/src/symbols/symbols_with_addr2line.cpp +++ b/src/symbols/symbols_with_addr2line.cpp @@ -182,166 +182,152 @@ namespace cpptrace { } #endif - struct symbolizer::impl { - using target_vec = std::vector>>; + using target_vec = std::vector>>; - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - std::unordered_map get_addr2line_targets( - const std::vector& dlframes, - std::vector& trace - ) { - std::unordered_map 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 get_addr2line_targets( + const std::vector& dlframes, + std::vector& trace + ) { + std::unordered_map 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 " 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 " (in ) (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 resolve_frames(const std::vector& frames) { + // TODO: Refactor better + std::vector trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" }); + for(size_t i = 0; i < frames.size(); i++) { + trace[i].address = reinterpret_cast(frames[i]); + } + if(has_addr2line()) { + const std::vector 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 " 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 " (in ) (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 resolve_frames(const std::vector& frames) { - // TODO: Refactor better - std::vector trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" }); - for(size_t i = 0; i < frames.size(); i++) { - trace[i].address = reinterpret_cast(frames[i]); - } - if(has_addr2line()) { - const std::vector 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 symbolizer::resolve_frames(const std::vector& frames) { - return pimpl->resolve_frames(frames); + return trace; } } } diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index d7e708d..e8bd2bb 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -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 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(addr), - static_cast(line.LineNumber), - 0, - line.FileName, - symbol->Name - }; - } - DWORD n_children = get_info( - symbol->TypeIndex, - proc, - symbol->ModBase - ); - DWORD class_parent_id = get_info( - 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 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(addr), static_cast(line.LineNumber), 0, line.FileName, - signature - }; - } else { - return { - reinterpret_cast(addr), - 0, - 0, - "", symbol->Name }; } + DWORD n_children = get_info( + symbol->TypeIndex, + proc, + symbol->ModBase + ); + DWORD class_parent_id = get_info( + 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(addr), + static_cast(line.LineNumber), + 0, + line.FileName, + signature + }; } else { return { reinterpret_cast(addr), 0, 0, "", - "" + symbol->Name }; } + } else { + return { + reinterpret_cast(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 symbolizer::resolve_frames(const std::vector& frames) { + std::vector resolve_frames(const std::vector& frames) { std::vector 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; } } diff --git a/src/symbols/symbols_with_dl.cpp b/src/symbols/symbols_with_dl.cpp index 906e036..1692828 100644 --- a/src/symbols/symbols_with_dl.cpp +++ b/src/symbols/symbols_with_dl.cpp @@ -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(addr), - 0, - 0, - info.dli_fname ? info.dli_fname : "", - info.dli_sname ? info.dli_sname : "" - }; - } else { - return { - reinterpret_cast(addr), - 0, - 0, - "", - "" - }; - } + stacktrace_frame resolve_frame(const void* addr) { + Dl_info info; + if(dladdr(addr, &info)) { // thread-safe + return { + reinterpret_cast(addr), + 0, + 0, + info.dli_fname ? info.dli_fname : "", + info.dli_sname ? info.dli_sname : "" + }; + } else { + return { + reinterpret_cast(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 symbolizer::resolve_frames(const std::vector& frames) { + std::vector resolve_frames(const std::vector& frames) { std::vector 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; } diff --git a/src/symbols/symbols_with_libbacktrace.cpp b/src/symbols/symbols_with_libbacktrace.cpp index 06895e6..f3c552d 100644 --- a/src/symbols/symbols_with_libbacktrace.cpp +++ b/src/symbols/symbols_with_libbacktrace.cpp @@ -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(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(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(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 symbolizer::resolve_frames(const std::vector& frames) { + std::vector resolve_frames(const std::vector& frames) { std::vector 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; } diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index e6b2d40..4c27eea 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -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 symbolizer::resolve_frames(const std::vector& frames) { + std::vector resolve_frames(const std::vector& frames) { std::vector 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; } diff --git a/src/symbols/symbols_with_nothing.cpp b/src/symbols/symbols_with_nothing.cpp index b7ea6ce..3a68b35 100644 --- a/src/symbols/symbols_with_nothing.cpp +++ b/src/symbols/symbols_with_nothing.cpp @@ -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 symbolizer::resolve_frames(const std::vector& frames) { + std::vector resolve_frames(const std::vector& frames) { return std::vector(frames.size(), { 0, 0, @@ -30,8 +16,6 @@ namespace cpptrace { "" }); } - - struct symbolizer::impl {}; } }