Skip to content

Commit 389f788

Browse files
committed
Add get_object_info method to stacktrace frames, adding onto the previous work for jeremy-rifkin#97
1 parent 123e7df commit 389f788

7 files changed

Lines changed: 85 additions & 75 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ namespace cpptrace {
169169
bool is_inline;
170170
bool operator==(const stacktrace_frame& other) const;
171171
bool operator!=(const stacktrace_frame& other) const;
172+
object_frame get_object_info() const; // object_address is stored but if the object_path is needed this can be used
172173
std::string to_string() const;
173174
/* operator<<(ostream, ..) and std::format support exist for this object */
174175
};

docs/c-api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth);
5252
ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color);
5353
void ctrace_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color);
5454
void ctrace_free_stacktrace(ctrace_stacktrace* trace);
55+
56+
// object_address is stored but if the object_path is needed this can be used
57+
ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame);
5558
```
5659
5760
### Object Traces

include/cpptrace/cpptrace.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ namespace cpptrace {
168168
return !operator==(other);
169169
}
170170

171+
object_frame get_object_info() const;
172+
171173
std::string to_string() const;
172174
friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
173175
};

include/ctrace/ctrace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ CTRACE_BEGIN_DEFINITIONS
143143
CPPTRACE_EXPORT int ctrace_stdout_fileno(void);
144144
CPPTRACE_EXPORT ctrace_bool ctrace_isatty(int fd);
145145

146+
CPPTRACE_EXPORT ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame);
147+
146148
/* ctrace::config: */
147149
typedef enum {
148150
/* Only minimal lookup tables */

src/binary/object.hpp

Lines changed: 58 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -65,57 +65,47 @@ namespace detail {
6565
}
6666
#endif
6767
#ifdef CPPTRACE_HAS_DL_FIND_OBJECT
68-
inline std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addrs) {
68+
inline object_frame get_frame_object_info(frame_ptr address) {
6969
// Use _dl_find_object when we can, it's orders of magnitude faster
70-
std::vector<object_frame> frames;
71-
frames.reserve(addrs.size());
72-
for(const frame_ptr addr : addrs) {
73-
object_frame frame;
74-
frame.raw_address = addr;
75-
frame.object_address = 0;
76-
dl_find_object result;
77-
if(_dl_find_object(reinterpret_cast<void*>(addr), &result) == 0) { // thread safe
78-
if(result.dlfo_link_map->l_name != nullptr && result.dlfo_link_map->l_name[0] != 0) {
79-
frame.object_path = result.dlfo_link_map->l_name;
70+
object_frame frame;
71+
frame.raw_address = address;
72+
frame.object_address = 0;
73+
dl_find_object result;
74+
if(_dl_find_object(reinterpret_cast<void*>(address), &result) == 0) { // thread safe
75+
if(result.dlfo_link_map->l_name != nullptr && result.dlfo_link_map->l_name[0] != 0) {
76+
frame.object_path = result.dlfo_link_map->l_name;
77+
} else {
78+
// empty l_name, this means it's the currently running executable
79+
// TODO: Caching and proper handling
80+
char buffer[CPPTRACE_PATH_MAX + 1]{};
81+
auto res = readlink("/proc/self/exe", buffer, CPPTRACE_PATH_MAX);
82+
if(res == -1) {
83+
// error handling?
8084
} else {
81-
// empty l_name, this means it's the currently running executable
82-
// TODO: Caching and proper handling
83-
char buffer[CPPTRACE_PATH_MAX + 1]{};
84-
auto res = readlink("/proc/self/exe", buffer, CPPTRACE_PATH_MAX);
85-
if(res == -1) {
86-
// error handling?
87-
} else {
88-
frame.object_path = buffer;
89-
}
85+
frame.object_path = buffer;
9086
}
91-
frame.object_address = addr
92-
- to_frame_ptr(result.dlfo_link_map->l_addr)
93-
+ get_module_image_base(frame.object_path);
9487
}
95-
frames.push_back(frame);
88+
frame.object_address = address
89+
- to_frame_ptr(result.dlfo_link_map->l_addr)
90+
+ get_module_image_base(frame.object_path);
9691
}
97-
return frames;
92+
return frame;
9893
}
9994
#else
100-
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
101-
inline std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addrs) {
95+
// dladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
96+
inline object_frame get_frame_object_info(frame_ptr address) {
10297
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
103-
std::vector<object_frame> frames;
104-
frames.reserve(addrs.size());
105-
for(const frame_ptr addr : addrs) {
106-
Dl_info info;
107-
object_frame frame;
108-
frame.raw_address = addr;
109-
frame.object_address = 0;
110-
if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread safe
111-
frame.object_path = info.dli_fname;
112-
frame.object_address = addr
113-
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
114-
+ get_module_image_base(info.dli_fname);
115-
}
116-
frames.push_back(frame);
98+
Dl_info info;
99+
object_frame frame;
100+
frame.raw_address = address;
101+
frame.object_address = 0;
102+
if(dladdr(reinterpret_cast<void*>(address), &info)) { // thread safe
103+
frame.object_path = info.dli_fname;
104+
frame.object_address = address
105+
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
106+
+ get_module_image_base(info.dli_fname);
117107
}
118-
return frames;
108+
return frame;
119109
}
120110
#endif
121111
#else
@@ -156,34 +146,36 @@ namespace detail {
156146
}
157147
}
158148

159-
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
160-
inline std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addrs) {
161-
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
149+
inline object_frame get_frame_object_info(frame_ptr address) {
150+
object_frame frame;
151+
frame.raw_address = address;
152+
frame.object_address = 0;
153+
HMODULE handle;
154+
// Multithread safe as long as another thread doesn't come along and free the module
155+
if(GetModuleHandleExA(
156+
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
157+
reinterpret_cast<const char*>(address),
158+
&handle
159+
)) {
160+
frame.object_path = get_module_name(handle);
161+
frame.object_address = address
162+
- reinterpret_cast<std::uintptr_t>(handle)
163+
+ get_module_image_base(frame.object_path);
164+
} else {
165+
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
166+
}
167+
return frame;
168+
}
169+
#endif
170+
171+
inline std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addresses) {
162172
std::vector<object_frame> frames;
163-
frames.reserve(addrs.size());
164-
for(const frame_ptr addr : addrs) {
165-
object_frame frame;
166-
frame.raw_address = addr;
167-
frame.object_address = 0;
168-
HMODULE handle;
169-
// Multithread safe as long as another thread doesn't come along and free the module
170-
if(GetModuleHandleExA(
171-
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
172-
reinterpret_cast<const char*>(addr),
173-
&handle
174-
)) {
175-
frame.object_path = get_module_name(handle);
176-
frame.object_address = addr
177-
- reinterpret_cast<std::uintptr_t>(handle)
178-
+ get_module_image_base(frame.object_path);
179-
} else {
180-
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
181-
}
182-
frames.push_back(frame);
173+
frames.reserve(addresses.size());
174+
for(const frame_ptr address : addresses) {
175+
frames.push_back(get_frame_object_info(address));
183176
}
184177
return frames;
185178
}
186-
#endif
187179

188180
inline object_frame resolve_safe_object_frame(const safe_object_frame& frame) {
189181
return {

src/cpptrace.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ namespace cpptrace {
100100
return frames.empty();
101101
}
102102

103+
object_frame stacktrace_frame::get_object_info() const {
104+
return detail::get_frame_object_info(raw_address);
105+
}
106+
103107
std::string stacktrace_frame::to_string() const {
104108
std::ostringstream oss;
105109
oss << *this;

src/ctrace.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,15 @@ CTRACE_FORMAT_EPILOGUE
8888
free_owning_string(owned_string.data);
8989
}
9090

91+
static ctrace_object_frame convert_object_frame(const cpptrace::object_frame& frame) {
92+
const char* new_path = generate_owning_string(frame.object_path).data;
93+
return { frame.raw_address, frame.object_address, new_path };
94+
}
95+
9196
static ctrace_object_trace c_convert(const std::vector<cpptrace::object_frame>& trace) {
9297
std::size_t count = trace.size();
9398
auto* frames = new ctrace_object_frame[count];
94-
std::transform(
95-
trace.begin(),
96-
trace.end(),
97-
frames,
98-
[] (const cpptrace::object_frame& frame) -> ctrace_object_frame {
99-
const char* new_path = generate_owning_string(frame.object_path).data;
100-
return { frame.raw_address, frame.object_address, new_path };
101-
}
102-
);
99+
std::transform(trace.begin(), trace.end(), frames, convert_object_frame);
103100
return { frames, count };
104101
}
105102

@@ -425,4 +422,13 @@ extern "C" {
425422
void ctrace_enable_inlined_call_resolution(ctrace_bool enable) {
426423
cpptrace::enable_inlined_call_resolution(enable);
427424
}
425+
426+
ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame) {
427+
try {
428+
cpptrace::object_frame new_frame = cpptrace::detail::get_frame_object_info(frame->raw_address);
429+
return ctrace::convert_object_frame(new_frame);
430+
} catch(...) {
431+
return {0, 0, nullptr};
432+
}
433+
}
428434
}

0 commit comments

Comments
 (0)