Thank you for the very useful library! Few improvements: - Better header hygiene - Isolate `windows.h` to `.cpp` whenever possible - Use `WIN32_LEAN_AND_MEAN` - Remove unused headers Tested on Windows with ``` cmake .. -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS="-ftime-trace -Wall -Wextra -Wpedantic -Wno-ignored-attributes" -DCMAKE_COLOR_DIAGNOSTICS=1 -DCPPTRACE_BUILD_TESTING=1 -DCPPTRACE_BUILD_BENCHMARKING=0 ``` There's a lot more that can be improved if you are interested. --------- Co-authored-by: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com>
154 lines
5.6 KiB
C++
154 lines
5.6 KiB
C++
#include <cpptrace/cpptrace.hpp>
|
|
|
|
#include "symbols/symbols.hpp"
|
|
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
|
|
#include "utils/error.hpp"
|
|
#include "binary/object.hpp"
|
|
|
|
namespace cpptrace {
|
|
namespace detail {
|
|
template<typename CollatedVec, typename Entry>
|
|
std::unordered_map<std::string, CollatedVec> collate_frames(
|
|
const std::vector<object_frame>& frames,
|
|
std::vector<Entry>& trace
|
|
) {
|
|
std::unordered_map<std::string, CollatedVec> entries;
|
|
for(std::size_t i = 0; i < frames.size(); i++) {
|
|
const auto& entry = frames[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.object_path.empty()) {
|
|
entries[entry.object_path].emplace_back(
|
|
entry,
|
|
trace[i]
|
|
);
|
|
}
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
std::unordered_map<std::string, collated_vec> collate_frames(
|
|
const std::vector<object_frame>& frames,
|
|
std::vector<stacktrace_frame>& trace
|
|
) {
|
|
return collate_frames<collated_vec>(frames, trace);
|
|
}
|
|
std::unordered_map<std::string, collated_vec_with_inlines> collate_frames(
|
|
const std::vector<object_frame>& frames,
|
|
std::vector<frame_with_inlines>& trace
|
|
) {
|
|
return collate_frames<collated_vec_with_inlines>(frames, trace);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
* All the code here is awful and I'm not proud of it.
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
// Resolver must not support walking inlines
|
|
void fill_blanks(
|
|
std::vector<stacktrace_frame>& vec,
|
|
std::vector<stacktrace_frame> (*resolver)(const std::vector<frame_ptr>&)
|
|
) {
|
|
std::vector<frame_ptr> addresses;
|
|
for(const auto& frame : vec) {
|
|
if(frame.symbol.empty() || frame.filename.empty()) {
|
|
addresses.push_back(frame.raw_address);
|
|
}
|
|
}
|
|
std::vector<stacktrace_frame> new_frames = resolver(addresses);
|
|
std::size_t i = 0;
|
|
for(auto& frame : vec) {
|
|
if(frame.symbol.empty() || frame.filename.empty()) {
|
|
// three cases to handle, either partially overwrite or fully overwrite
|
|
if(frame.symbol.empty() && frame.filename.empty()) {
|
|
frame = new_frames[i];
|
|
} else if(frame.symbol.empty() && !frame.filename.empty()) {
|
|
frame.symbol = new_frames[i].symbol;
|
|
} else {
|
|
ASSERT(!frame.symbol.empty() && frame.filename.empty());
|
|
frame.filename = new_frames[i].filename;
|
|
frame.line = new_frames[i].line;
|
|
frame.column = new_frames[i].column;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
|
|
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
|
std::vector<stacktrace_frame> trace = libdwarf::resolve_frames(frames);
|
|
fill_blanks(trace, dbghelp::resolve_frames);
|
|
return trace;
|
|
#else
|
|
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDL) \
|
|
|| defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \
|
|
|| defined(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
|
// actually need to go backwards to a void*
|
|
std::vector<frame_ptr> raw_frames(frames.size());
|
|
for(std::size_t i = 0; i < frames.size(); i++) {
|
|
raw_frames[i] = frames[i].raw_address;
|
|
}
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
|
|
return libdl::resolve_frames(raw_frames);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
|
|
return libdwarf::resolve_frames(frames);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
|
|
return dbghelp::resolve_frames(raw_frames);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
|
|
return addr2line::resolve_frames(frames);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
|
|
return libbacktrace::resolve_frames(raw_frames);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
|
return nothing::resolve_frames(frames);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
std::vector<stacktrace_frame> resolve_frames(const std::vector<frame_ptr>& frames) {
|
|
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) \
|
|
|| defined(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
|
|
auto dlframes = get_frames_object_info(frames);
|
|
#endif
|
|
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
|
std::vector<stacktrace_frame> trace = libdwarf::resolve_frames(dlframes);
|
|
fill_blanks(trace, dbghelp::resolve_frames);
|
|
return trace;
|
|
#else
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
|
|
return libdl::resolve_frames(frames);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
|
|
return libdwarf::resolve_frames(dlframes);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
|
|
return dbghelp::resolve_frames(frames);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
|
|
return addr2line::resolve_frames(dlframes);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
|
|
return libbacktrace::resolve_frames(frames);
|
|
#endif
|
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
|
return nothing::resolve_frames(frames);
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
}
|