diff --git a/README.md b/README.md index 599443c..7840eca 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ namespace cpptrace { bool is_inline; bool operator==(const stacktrace_frame& other) const; bool operator!=(const stacktrace_frame& other) const; + object_frame get_object_info() const; // object_address is stored but if the object_path is needed this can be used std::string to_string() const; /* operator<<(ostream, ..) and std::format support exist for this object */ }; diff --git a/docs/c-api.md b/docs/c-api.md index 851bf6d..6b79908 100644 --- a/docs/c-api.md +++ b/docs/c-api.md @@ -52,6 +52,9 @@ ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth); ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color); void ctrace_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color); void ctrace_free_stacktrace(ctrace_stacktrace* trace); + +// object_address is stored but if the object_path is needed this can be used +ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame); ``` ### Object Traces diff --git a/include/cpptrace/cpptrace.hpp b/include/cpptrace/cpptrace.hpp index 19d4c38..5282671 100644 --- a/include/cpptrace/cpptrace.hpp +++ b/include/cpptrace/cpptrace.hpp @@ -168,6 +168,8 @@ namespace cpptrace { return !operator==(other); } + object_frame get_object_info() const; + std::string to_string() const; friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame); }; diff --git a/include/ctrace/ctrace.h b/include/ctrace/ctrace.h index 08d748c..237a5f4 100644 --- a/include/ctrace/ctrace.h +++ b/include/ctrace/ctrace.h @@ -143,6 +143,8 @@ CTRACE_BEGIN_DEFINITIONS CPPTRACE_EXPORT int ctrace_stdout_fileno(void); CPPTRACE_EXPORT ctrace_bool ctrace_isatty(int fd); + CPPTRACE_EXPORT ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame); + /* ctrace::config: */ typedef enum { /* Only minimal lookup tables */ diff --git a/src/binary/object.hpp b/src/binary/object.hpp index efb3ae0..eeb9333 100644 --- a/src/binary/object.hpp +++ b/src/binary/object.hpp @@ -65,57 +65,47 @@ namespace detail { } #endif #ifdef CPPTRACE_HAS_DL_FIND_OBJECT - inline std::vector get_frames_object_info(const std::vector& addrs) { + inline object_frame get_frame_object_info(frame_ptr address) { // Use _dl_find_object when we can, it's orders of magnitude faster - std::vector frames; - frames.reserve(addrs.size()); - for(const frame_ptr addr : addrs) { - object_frame frame; - frame.raw_address = addr; - frame.object_address = 0; - dl_find_object result; - if(_dl_find_object(reinterpret_cast(addr), &result) == 0) { // thread safe - if(result.dlfo_link_map->l_name != nullptr && result.dlfo_link_map->l_name[0] != 0) { - frame.object_path = result.dlfo_link_map->l_name; + object_frame frame; + frame.raw_address = address; + frame.object_address = 0; + dl_find_object result; + if(_dl_find_object(reinterpret_cast(address), &result) == 0) { // thread safe + if(result.dlfo_link_map->l_name != nullptr && result.dlfo_link_map->l_name[0] != 0) { + frame.object_path = result.dlfo_link_map->l_name; + } else { + // empty l_name, this means it's the currently running executable + // TODO: Caching and proper handling + char buffer[CPPTRACE_PATH_MAX + 1]{}; + auto res = readlink("/proc/self/exe", buffer, CPPTRACE_PATH_MAX); + if(res == -1) { + // error handling? } else { - // empty l_name, this means it's the currently running executable - // TODO: Caching and proper handling - char buffer[CPPTRACE_PATH_MAX + 1]{}; - auto res = readlink("/proc/self/exe", buffer, CPPTRACE_PATH_MAX); - if(res == -1) { - // error handling? - } else { - frame.object_path = buffer; - } + frame.object_path = buffer; } - frame.object_address = addr - - to_frame_ptr(result.dlfo_link_map->l_addr) - + get_module_image_base(frame.object_path); } - frames.push_back(frame); + frame.object_address = address + - to_frame_ptr(result.dlfo_link_map->l_addr) + + get_module_image_base(frame.object_path); } - return frames; + return frame; } #else - // aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on - inline std::vector get_frames_object_info(const std::vector& addrs) { + // dladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on + inline object_frame get_frame_object_info(frame_ptr address) { // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c - std::vector frames; - frames.reserve(addrs.size()); - for(const frame_ptr addr : addrs) { - Dl_info info; - object_frame frame; - frame.raw_address = addr; - frame.object_address = 0; - if(dladdr(reinterpret_cast(addr), &info)) { // thread safe - frame.object_path = info.dli_fname; - frame.object_address = addr - - reinterpret_cast(info.dli_fbase) - + get_module_image_base(info.dli_fname); - } - frames.push_back(frame); + Dl_info info; + object_frame frame; + frame.raw_address = address; + frame.object_address = 0; + if(dladdr(reinterpret_cast(address), &info)) { // thread safe + frame.object_path = info.dli_fname; + frame.object_address = address + - reinterpret_cast(info.dli_fbase) + + get_module_image_base(info.dli_fname); } - return frames; + return frame; } #endif #else @@ -156,34 +146,36 @@ namespace detail { } } - // aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on - inline std::vector get_frames_object_info(const std::vector& addrs) { - // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c + inline object_frame get_frame_object_info(frame_ptr address) { + object_frame frame; + frame.raw_address = address; + frame.object_address = 0; + HMODULE handle; + // Multithread safe as long as another thread doesn't come along and free the module + if(GetModuleHandleExA( + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast(address), + &handle + )) { + frame.object_path = get_module_name(handle); + frame.object_address = address + - reinterpret_cast(handle) + + get_module_image_base(frame.object_path); + } else { + std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what()); + } + return frame; + } + #endif + + inline std::vector get_frames_object_info(const std::vector& addresses) { std::vector frames; - frames.reserve(addrs.size()); - for(const frame_ptr addr : addrs) { - object_frame frame; - frame.raw_address = addr; - frame.object_address = 0; - HMODULE handle; - // Multithread safe as long as another thread doesn't come along and free the module - if(GetModuleHandleExA( - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast(addr), - &handle - )) { - frame.object_path = get_module_name(handle); - frame.object_address = addr - - reinterpret_cast(handle) - + get_module_image_base(frame.object_path); - } else { - std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what()); - } - frames.push_back(frame); + frames.reserve(addresses.size()); + for(const frame_ptr address : addresses) { + frames.push_back(get_frame_object_info(address)); } return frames; } - #endif inline object_frame resolve_safe_object_frame(const safe_object_frame& frame) { return { diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 72f59dd..1b913f4 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -100,6 +100,10 @@ namespace cpptrace { return frames.empty(); } + object_frame stacktrace_frame::get_object_info() const { + return detail::get_frame_object_info(raw_address); + } + std::string stacktrace_frame::to_string() const { std::ostringstream oss; oss << *this; diff --git a/src/ctrace.cpp b/src/ctrace.cpp index 5307f59..d60a97e 100644 --- a/src/ctrace.cpp +++ b/src/ctrace.cpp @@ -88,18 +88,15 @@ CTRACE_FORMAT_EPILOGUE free_owning_string(owned_string.data); } + static ctrace_object_frame convert_object_frame(const cpptrace::object_frame& frame) { + const char* new_path = generate_owning_string(frame.object_path).data; + return { frame.raw_address, frame.object_address, new_path }; + } + static ctrace_object_trace c_convert(const std::vector& trace) { std::size_t count = trace.size(); auto* frames = new ctrace_object_frame[count]; - std::transform( - trace.begin(), - trace.end(), - frames, - [] (const cpptrace::object_frame& frame) -> ctrace_object_frame { - const char* new_path = generate_owning_string(frame.object_path).data; - return { frame.raw_address, frame.object_address, new_path }; - } - ); + std::transform(trace.begin(), trace.end(), frames, convert_object_frame); return { frames, count }; } @@ -425,4 +422,13 @@ extern "C" { void ctrace_enable_inlined_call_resolution(ctrace_bool enable) { cpptrace::enable_inlined_call_resolution(enable); } + + ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame) { + try { + cpptrace::object_frame new_frame = cpptrace::detail::get_frame_object_info(frame->raw_address); + return ctrace::convert_object_frame(new_frame); + } catch(...) { + return {0, 0, nullptr}; + } + } }