-
Notifications
You must be signed in to change notification settings - Fork 0
/
DebugPrinter.hpp
341 lines (270 loc) · 10.2 KB
/
DebugPrinter.hpp
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*******************************************************************************
*
* DebugPrinter.hpp - build 20130426
* (C) 2011-2014 Donjan Rodic <[email protected]>
* Released under the WTFPL (www.wtfpl.net) with no warranty whatsoever.
*
*
* Creates a global static object named dout and defines the macros:
* dout_HERE dout_FUNC
*
* Link with -rdynamic in order to properly get stack() frame names and useful
* dout_FUNC output.
*
* Pass DEBUGPRINTER_OFF to turn off all functionality provided here. You can
* leave the debug statements in your code, since all methods and macros become
* inline and trivial, and thus should be optimised away by your compiler.
*
* Pass DEBUGPRINTER_NO_EXECINFO flag on Windows (disables stack and dout_FUNC).
*
* Pass DEBUGPRINTER_NO_CXXABI if you don't have a cxxabi demangle call in your
* libc distribution. The stack and type methods will then print raw stack
* frame names and a c++filt-ready output.
*
* Note: keep in mind that compiler optimisations may inline (-> shorter stack).
*
*
* Usage:
* dout << "foo" << std::endl;
* dout, var , 5, " bar ", 6 << " foobar " << 7, 8;
*
* dout.type(obj); // print the RTTI of any object
*
* dout.stack(); // print a full stack trace
* dout.stack(count); // print at most (int)count frames
* dout.stack(count, true); // print in compact format
* dout_FUNC // shortcut for dout.stack(1, true);
*
* dout(anything); // highlighted format, anything needs a
* // std::ostream::operator<< overload
* dout(label, anything); // highlighted format, label also needs a
* // std::ostream::operator<< overload
* dout_HERE // shortcut for dout(__func__, __LINE__);
*
*
* Precision of float output (default == 5) can be set with
* dout.precision(12);
*
* The output std::ostream (default == std::cout) can be changed
* dout = std::cerr;
*
* The highlighted bash output color (default == "36" == cyan) can be set with
* dout.hcolor("31");
*
******************************************************************************/
#pragma once
#include <iostream>
#ifndef DEBUGPRINTER_OFF
#include <iomanip>
#include <typeinfo>
#include <cstdlib>
#ifndef DEBUGPRINTER_NO_EXECINFO
#include <execinfo.h>
#endif // DEBUGPRINTER_NO_EXECINFO
#ifndef DEBUGPRINTER_NO_CXXABI
#include <cxxabi.h>
#endif // DEBUGPRINTER_NO_CXXABI
#endif // DEBUGPRINTER_OFF
#ifndef DEBUGPRINTER_OFF
class DebugPrinter {
public:
/*******************************************************************************
* Ctor and friends
*/
DebugPrinter() : outstream(&std::cout), prec(5), hcol("36") {}
template <typename T>
friend DebugPrinter & operator<<(DebugPrinter & d, const T& output);
friend inline DebugPrinter & operator<<(DebugPrinter & d,
std::ostream& (*pf)(std::ostream&));
/*******************************************************************************
* Setters
*/
void operator=(std::ostream & os) { outstream = &os; }
void precision(int prec_) { prec = prec_; }
void hcolor(std::string str) { hcol = str; } // no chaining -> void
/*******************************************************************************
* Parentheses operators
*/
template <typename T, typename U>
inline void operator()(const T& label, const U& line,
const std::string sc = ": ") const {
*outstream << "\033[0;" << hcol << "m" << ">>> " << label << sc << line
<< "\033[0m" << std::endl;
}
template <typename T>
inline void operator()(const T& line) const { operator()("", line, ""); }
/*******************************************************************************
* RTTI (type and stack methods)
*/
template <typename T>
inline void type(T obj) const {
int dummy;
*outstream << demangle( typeid(obj).name() , dummy) << std::endl;
}
#ifndef DEBUGPRINTER_NO_EXECINFO
void stack(
const int backtrace_size = max_backtrace,
const bool compact = false,
const int begin = 1 /* should stay 1 except for special needs */
) const {
std::ostream & out = *outstream;
int _end = -1; // ignore last
void * stack[max_backtrace];
int r = backtrace(stack, backtrace_size+begin-_end);
char ** symbols = backtrace_symbols(stack, r);
if(!symbols) return;
int end = r + _end;
if(compact == false)
out << "DebugPrinter obtained " << end-begin << " stack frames:"
<< std::endl;
#ifndef DEBUGPRINTER_NO_CXXABI
for(int i = begin; i < end; ++i) {
std::string prog = " " + prog_part(std::string(symbols[i])) + ": ";
std::string mangled = mangled_part(std::string(symbols[i]));
std::string offset = " +" + offset_part(std::string(symbols[i]));
if(compact == true) { prog = ""; offset = ""; }
int status;
std::string demangled = demangle(mangled, status);
switch (status) {
case -1:
out << " Error: Could not allocate memory!" << std::endl;
break;
case -2: // invalid name under the C++ ABI mangling rules
out << prog << mangled << offset << std::endl;
break;
case -3:
out << " Error: Invalid argument to demangle()" << std::endl;
break;
default:
out << prog << demangled << offset << std::endl;
}
}
if(compact == false) out << std::endl;
#else // DEBUGPRINTER_NO_CXXABI
for(int i = begin; i < end; ++i) {
if(compact == false)
out << " " << symbols[i] << std::endl;
else
out << mangled_part(std::string(symbols[i])) << std::endl;
}
if(compact == false) out << std::endl;
out << "echo '' && c++filt";
for(int i = begin; i < end; ++i)
out << " " << mangled_part(std::string(symbols[i]));
out << " && echo ''" << std::endl;
if(compact == false) out << std::endl;
#endif // DEBUGPRINTER_NO_CXXABI
free(symbols);
}
// The dout_FUNC macro pollutes the namespace but is more convenient
//~ #ifndef DEBUGPRINTER_NO_CXXABI
//~ void func() { stack(1, true, 2); }
//~ #endif // DEBUGPRINTER_NO_CXXABI
#else // DEBUGPRINTER_NO_EXECINFO
void stack(
const int backtrace_size = max_backtrace,
const bool compact = false,
const int begin = 1 /* should stay 1 except for special needs */
) const {
*outstream << "DebugPrinter::stack() not available" << std::endl;
}
#endif // DEBUGPRINTER_NO_EXECINFO
/*******************************************************************************
* Private parts
*/
private:
std::ostream * outstream;
int prec;
std::string hcol;
static const unsigned int max_backtrace = 50;
static const unsigned int max_demangled = 4096;
#ifndef DEBUGPRINTER_NO_CXXABI
inline std::string demangle(const std::string & str, int & status) const {
char * demangled = abi::__cxa_demangle(str.c_str(), 0, 0, &status);
std::string out = (status==0) ? std::string(demangled) : str;
free(demangled);
return out;
}
#else // DEBUGPRINTER_NO_CXXABI
inline std::string demangle(const std::string & str, int & status) const {
return str;
}
#endif // DEBUGPRINTER_NO_CXXABI
inline std::string prog_part(const std::string str) const {
return str.substr(0, str.find("("));
}
inline std::string mangled_part(const std::string str) const {
std::string::size_type pos = str.find("(") + 1;
return str.substr(pos, str.find("+", pos) - pos);
}
inline std::string offset_part(const std::string str) const {
std::string::size_type pos = str.find("+") + 1;
return str.substr(pos, str.find(")", pos) - pos);
}
};
/*******************************************************************************
* std::ostream overloads
*/
template <typename T>
DebugPrinter & operator<<(DebugPrinter & d, const T& output) {
std::ostream & out = *d.outstream;
size_t savep = (size_t)out.precision();
std::ios_base::fmtflags savef =
out.setf(std::ios_base::fixed, std::ios::floatfield);
out << std::setprecision(d.prec) << std::fixed << output
<< std::setprecision(savep);
out.setf(savef, std::ios::floatfield);
out.flush();
return d;
}
inline DebugPrinter & operator<<(DebugPrinter & d, // manipulators overload
std::ostream& (*pf)(std::ostream&)) {
std::ostream & out = *d.outstream;
out << pf;
return d;
}
template <typename T>
inline DebugPrinter & operator,(DebugPrinter & d, const T& output) {
return operator<<(d, output);
}
inline DebugPrinter & operator,(DebugPrinter & d, // manipulators overload
std::ostream& (*pf)(std::ostream&)) {
return operator<<(d, pf);
}
/*******************************************************************************
* Globals
*/
static DebugPrinter dout;
#define dout_HERE dout(__func__, __LINE__);
#define dout_FUNC dout.stack(1, true);
/**
* End
******************************************************************************/
#else // DEBUGPRINTER_OFF
class DebugPrinter {
public:
void operator=(std::ostream & os) {}
void precision(int prec_) {}
void hcolor(std::string str) {}
template <typename T, typename U>
inline void operator()(const T& label, const U& line,
const std::string sc = "") const {}
template <typename T>
inline void operator()(const T& line) const {}
template <typename T>
inline void type(T obj) const {}
inline void stack(const int backtrace_size = 0, const bool compact = false,
const int begin = 0) const {}
};
template <typename T>
inline DebugPrinter & operator<<(DebugPrinter & d, const T& output) {return d;}
inline DebugPrinter & operator<<(DebugPrinter & d,
std::ostream& (*pf)(std::ostream&)) {return d;}
template <typename T>
inline DebugPrinter & operator,(DebugPrinter & d, const T& output) {return d;}
inline DebugPrinter & operator,(DebugPrinter & d,
std::ostream& (*out)(std::ostream&)) {return d;}
static DebugPrinter dout;
#define dout_HERE ;
#define dout_FUNC ;
#endif // DEBUGPRINTER_OFF