Add get_object_info method to stacktrace frames, adding onto the previous work for #97

This commit is contained in:
Jeremy 2024-03-03 10:54:03 -06:00
parent 123e7df4f4
commit 389f788b57
No known key found for this signature in database
GPG Key ID: BE03111EB7ED6E2E
7 changed files with 85 additions and 75 deletions

View File

@ -169,6 +169,7 @@ namespace cpptrace {
bool is_inline; bool is_inline;
bool operator==(const stacktrace_frame& other) const; bool operator==(const stacktrace_frame& other) const;
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; std::string to_string() const;
/* operator<<(ostream, ..) and std::format support exist for this object */ /* operator<<(ostream, ..) and std::format support exist for this object */
}; };

View File

@ -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); 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_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color);
void ctrace_free_stacktrace(ctrace_stacktrace* trace); 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 ### Object Traces

View File

@ -168,6 +168,8 @@ namespace cpptrace {
return !operator==(other); return !operator==(other);
} }
object_frame get_object_info() const;
std::string to_string() const; std::string to_string() const;
friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame); friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
}; };

View File

@ -143,6 +143,8 @@ CTRACE_BEGIN_DEFINITIONS
CPPTRACE_EXPORT int ctrace_stdout_fileno(void); CPPTRACE_EXPORT int ctrace_stdout_fileno(void);
CPPTRACE_EXPORT ctrace_bool ctrace_isatty(int fd); CPPTRACE_EXPORT ctrace_bool ctrace_isatty(int fd);
CPPTRACE_EXPORT ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame);
/* ctrace::config: */ /* ctrace::config: */
typedef enum { typedef enum {
/* Only minimal lookup tables */ /* Only minimal lookup tables */

View File

@ -65,16 +65,13 @@ namespace detail {
} }
#endif #endif
#ifdef CPPTRACE_HAS_DL_FIND_OBJECT #ifdef CPPTRACE_HAS_DL_FIND_OBJECT
inline std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addrs) { inline object_frame get_frame_object_info(frame_ptr address) {
// Use _dl_find_object when we can, it's orders of magnitude faster // Use _dl_find_object when we can, it's orders of magnitude faster
std::vector<object_frame> frames;
frames.reserve(addrs.size());
for(const frame_ptr addr : addrs) {
object_frame frame; object_frame frame;
frame.raw_address = addr; frame.raw_address = address;
frame.object_address = 0; frame.object_address = 0;
dl_find_object result; dl_find_object result;
if(_dl_find_object(reinterpret_cast<void*>(addr), &result) == 0) { // thread safe if(_dl_find_object(reinterpret_cast<void*>(address), &result) == 0) { // thread safe
if(result.dlfo_link_map->l_name != nullptr && result.dlfo_link_map->l_name[0] != 0) { 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; frame.object_path = result.dlfo_link_map->l_name;
} else { } else {
@ -88,34 +85,27 @@ namespace detail {
frame.object_path = buffer; frame.object_path = buffer;
} }
} }
frame.object_address = addr frame.object_address = address
- to_frame_ptr(result.dlfo_link_map->l_addr) - to_frame_ptr(result.dlfo_link_map->l_addr)
+ get_module_image_base(frame.object_path); + get_module_image_base(frame.object_path);
} }
frames.push_back(frame); return frame;
}
return frames;
} }
#else #else
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on // dladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
inline std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addrs) { inline object_frame get_frame_object_info(frame_ptr address) {
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
std::vector<object_frame> frames;
frames.reserve(addrs.size());
for(const frame_ptr addr : addrs) {
Dl_info info; Dl_info info;
object_frame frame; object_frame frame;
frame.raw_address = addr; frame.raw_address = address;
frame.object_address = 0; frame.object_address = 0;
if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread safe if(dladdr(reinterpret_cast<void*>(address), &info)) { // thread safe
frame.object_path = info.dli_fname; frame.object_path = info.dli_fname;
frame.object_address = addr frame.object_address = address
- reinterpret_cast<std::uintptr_t>(info.dli_fbase) - reinterpret_cast<std::uintptr_t>(info.dli_fbase)
+ get_module_image_base(info.dli_fname); + get_module_image_base(info.dli_fname);
} }
frames.push_back(frame); return frame;
}
return frames;
} }
#endif #endif
#else #else
@ -156,34 +146,36 @@ namespace detail {
} }
} }
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on inline object_frame get_frame_object_info(frame_ptr address) {
inline std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addrs) {
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
std::vector<object_frame> frames;
frames.reserve(addrs.size());
for(const frame_ptr addr : addrs) {
object_frame frame; object_frame frame;
frame.raw_address = addr; frame.raw_address = address;
frame.object_address = 0; frame.object_address = 0;
HMODULE handle; HMODULE handle;
// Multithread safe as long as another thread doesn't come along and free the module // Multithread safe as long as another thread doesn't come along and free the module
if(GetModuleHandleExA( if(GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
reinterpret_cast<const char*>(addr), reinterpret_cast<const char*>(address),
&handle &handle
)) { )) {
frame.object_path = get_module_name(handle); frame.object_path = get_module_name(handle);
frame.object_address = addr frame.object_address = address
- reinterpret_cast<std::uintptr_t>(handle) - reinterpret_cast<std::uintptr_t>(handle)
+ get_module_image_base(frame.object_path); + get_module_image_base(frame.object_path);
} else { } else {
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what()); std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
} }
frames.push_back(frame); return frame;
}
#endif
inline std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addresses) {
std::vector<object_frame> frames;
frames.reserve(addresses.size());
for(const frame_ptr address : addresses) {
frames.push_back(get_frame_object_info(address));
} }
return frames; return frames;
} }
#endif
inline object_frame resolve_safe_object_frame(const safe_object_frame& frame) { inline object_frame resolve_safe_object_frame(const safe_object_frame& frame) {
return { return {

View File

@ -100,6 +100,10 @@ namespace cpptrace {
return frames.empty(); 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::string stacktrace_frame::to_string() const {
std::ostringstream oss; std::ostringstream oss;
oss << *this; oss << *this;

View File

@ -88,18 +88,15 @@ CTRACE_FORMAT_EPILOGUE
free_owning_string(owned_string.data); free_owning_string(owned_string.data);
} }
static ctrace_object_trace c_convert(const std::vector<cpptrace::object_frame>& trace) { static ctrace_object_frame convert_object_frame(const cpptrace::object_frame& frame) {
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; const char* new_path = generate_owning_string(frame.object_path).data;
return { frame.raw_address, frame.object_address, new_path }; return { frame.raw_address, frame.object_address, new_path };
} }
);
static ctrace_object_trace c_convert(const std::vector<cpptrace::object_frame>& trace) {
std::size_t count = trace.size();
auto* frames = new ctrace_object_frame[count];
std::transform(trace.begin(), trace.end(), frames, convert_object_frame);
return { frames, count }; return { frames, count };
} }
@ -425,4 +422,13 @@ extern "C" {
void ctrace_enable_inlined_call_resolution(ctrace_bool enable) { void ctrace_enable_inlined_call_resolution(ctrace_bool enable) {
cpptrace::enable_inlined_call_resolution(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};
}
}
} }