forked from jeremy-rifkin/cpptrace
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpptrace.hpp
More file actions
309 lines (278 loc) · 12 KB
/
cpptrace.hpp
File metadata and controls
309 lines (278 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#ifndef CPPTRACE_HPP
#define CPPTRACE_HPP
#include <cstdint>
#include <cstdio>
#include <exception>
#include <ostream>
#include <string>
#include <vector>
#if __cplusplus >= 202002L
#ifdef __has_include
#if __has_include(<format>)
#define CPPTRACE_STD_FORMAT
#include <format>
#endif
#endif
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
#define CPPTRACE_API __declspec(dllexport)
#else
#define CPPTRACE_API
#endif
namespace cpptrace {
struct object_trace;
struct stacktrace;
struct raw_trace {
std::vector<uintptr_t> frames;
CPPTRACE_API static raw_trace current(std::uint_least32_t skip = 0);
CPPTRACE_API static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API object_trace resolve_object_trace() const;
CPPTRACE_API stacktrace resolve() const;
CPPTRACE_API void clear();
CPPTRACE_API bool empty() const noexcept;
using iterator = std::vector<uintptr_t>::iterator;
using const_iterator = std::vector<uintptr_t>::const_iterator;
inline iterator begin() noexcept { return frames.begin(); }
inline iterator end() noexcept { return frames.end(); }
inline const_iterator begin() const noexcept { return frames.begin(); }
inline const_iterator end() const noexcept { return frames.end(); }
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
inline const_iterator cend() const noexcept { return frames.cend(); }
};
struct object_frame {
std::string obj_path;
std::string symbol;
uintptr_t raw_address = 0;
uintptr_t obj_address = 0;
};
struct object_trace {
std::vector<object_frame> frames;
CPPTRACE_API static object_trace current(std::uint_least32_t skip = 0);
CPPTRACE_API static object_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API stacktrace resolve() const;
CPPTRACE_API void clear();
CPPTRACE_API bool empty() const noexcept;
using iterator = std::vector<object_frame>::iterator;
using const_iterator = std::vector<object_frame>::const_iterator;
inline iterator begin() noexcept { return frames.begin(); }
inline iterator end() noexcept { return frames.end(); }
inline const_iterator begin() const noexcept { return frames.begin(); }
inline const_iterator end() const noexcept { return frames.end(); }
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
inline const_iterator cend() const noexcept { return frames.cend(); }
};
struct stacktrace_frame {
uintptr_t address;
std::uint_least32_t line; // TODO: This should use UINT_LEAST32_MAX as a sentinel
std::uint_least32_t column; // UINT_LEAST32_MAX if not present
std::string filename;
std::string symbol;
bool operator==(const stacktrace_frame& other) const {
return address == other.address
&& line == other.line
&& column == other.column
&& filename == other.filename
&& symbol == other.symbol;
}
bool operator!=(const stacktrace_frame& other) const {
return !operator==(other);
}
CPPTRACE_API std::string to_string() const;
CPPTRACE_API friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
};
struct stacktrace {
std::vector<stacktrace_frame> frames;
CPPTRACE_API static stacktrace current(std::uint_least32_t skip = 0);
CPPTRACE_API static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API void print() const;
CPPTRACE_API void print(std::ostream& stream) const;
CPPTRACE_API void print(std::ostream& stream, bool color) const;
CPPTRACE_API void clear();
CPPTRACE_API bool empty() const noexcept;
CPPTRACE_API std::string to_string() const;
CPPTRACE_API friend std::ostream& operator<<(std::ostream& stream, const stacktrace& trace);
using iterator = std::vector<stacktrace_frame>::iterator;
using const_iterator = std::vector<stacktrace_frame>::const_iterator;
inline iterator begin() noexcept { return frames.begin(); }
inline iterator end() noexcept { return frames.end(); }
inline const_iterator begin() const noexcept { return frames.begin(); }
inline const_iterator end() const noexcept { return frames.end(); }
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
inline const_iterator cend() const noexcept { return frames.cend(); }
private:
CPPTRACE_API void print(std::ostream& stream, bool color, bool newline_at_end) const;
};
CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip = 0);
CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip = 0);
CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API stacktrace generate_trace(std::uint_least32_t skip = 0);
CPPTRACE_API stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
// utilities:
CPPTRACE_API std::string demangle(const std::string& name);
CPPTRACE_API void absorb_trace_exceptions(bool absorb);
enum class cache_mode {
// Only minimal lookup tables
prioritize_memory,
// Build lookup tables but don't keep them around between trace calls
hybrid,
// Build lookup tables as needed
prioritize_speed
};
namespace experimental {
CPPTRACE_API void set_cache_mode(cache_mode mode);
}
namespace detail {
CPPTRACE_API bool should_absorb_trace_exceptions();
CPPTRACE_API enum cache_mode get_cache_mode();
}
class exception : public std::exception {
protected:
mutable raw_trace trace;
mutable stacktrace resolved_trace;
mutable std::string resolved_what;
explicit exception(std::uint_least32_t skip, std::uint_least32_t max_depth) noexcept
: trace([skip, max_depth] () noexcept {
try {
return generate_raw_trace(skip + 2, max_depth);
} catch(const std::exception& e) {
if(!detail::should_absorb_trace_exceptions()) {
// TODO: Append to message somehow
fprintf(
stderr,
"Exception ocurred while resolving trace in cpptrace::exception object:\n%s\n",
e.what()
);
}
return raw_trace{};
}
} ()) {}
explicit exception(std::uint_least32_t skip) noexcept : exception(skip + 1, UINT_LEAST32_MAX) {}
const stacktrace& get_resolved_trace() const noexcept {
// I think a non-empty raw trace can never resolve as empty, so this will accurately prevent resolving more
// than once. Either way the raw trace is cleared.
try {
if(resolved_trace.empty() && !trace.empty()) {
resolved_trace = trace.resolve();
trace.clear();
}
} catch(const std::exception& e) {
if(!detail::should_absorb_trace_exceptions()) {
// TODO: Append to message somehow
fprintf(
stderr,
"Exception ocurred while resolving trace in cpptrace::exception object:\n%s\n",
e.what()
);
}
}
return resolved_trace;
}
virtual const std::string& get_resolved_what() const noexcept {
if(resolved_what.empty()) {
resolved_what = "cpptrace::exception:\n" + get_resolved_trace().to_string();
}
return resolved_what;
}
public:
explicit exception() noexcept : exception(1) {}
const char* what() const noexcept override {
return get_resolved_what().c_str();
}
// what(), but not a C-string
const std::string& get_what() const noexcept {
return resolved_what;
}
const raw_trace& get_raw_trace() const noexcept {
return trace;
}
const stacktrace& get_trace() const noexcept {
return resolved_trace;
}
};
class exception_with_message : public exception {
protected:
mutable std::string message;
explicit exception_with_message(
std::string&& message_arg,
uint32_t skip
) noexcept : exception(skip + 1), message(std::move(message_arg)) {}
explicit exception_with_message(
std::string&& message_arg,
uint_least32_t skip,
uint_least32_t max_depth
) noexcept : exception(skip + 1, max_depth), message(std::move(message_arg)) {}
const std::string& get_resolved_what() const noexcept override {
if(resolved_what.empty()) {
resolved_what = message + "\n" + get_resolved_trace().to_string();
}
return resolved_what;
}
public:
explicit exception_with_message(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
const std::string& get_message() const noexcept {
return message;
}
};
class logic_error : public exception_with_message {
public:
explicit logic_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
class domain_error : public exception_with_message {
public:
explicit domain_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
class invalid_argument : public exception_with_message {
public:
explicit invalid_argument(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
class length_error : public exception_with_message {
public:
explicit length_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
class out_of_range : public exception_with_message {
public:
explicit out_of_range(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
class runtime_error : public exception_with_message {
public:
explicit runtime_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
class range_error : public exception_with_message {
public:
explicit range_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
class overflow_error : public exception_with_message {
public:
explicit overflow_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
class underflow_error : public exception_with_message {
public:
explicit underflow_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
};
}
#if defined(CPPTRACE_STD_FORMAT) && defined(__cpp_lib_format)
template <>
struct std::formatter<cpptrace::stacktrace_frame> : std::formatter<std::string> {
auto format(cpptrace::stacktrace_frame frame, format_context& ctx) const {
return formatter<string>::format(frame.to_string(), ctx);
}
};
template <>
struct std::formatter<cpptrace::stacktrace> : std::formatter<std::string> {
auto format(cpptrace::stacktrace trace, format_context& ctx) const {
return formatter<string>::format(trace.to_string(), ctx);
}
};
#endif
#endif