jaulib v1.3.0
Jau Support Library (C++, Java, ..)
debug.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2020 ZAFENA AB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#include <jau/debug.hpp>
27
28#include <cstdarg>
29
30#ifdef USE_LIBUNWIND
31 #define UNW_LOCAL_ONLY
32 #include <libunwind.h>
33#endif /* USE_LIBUNWIND */
34
35#include <cxxabi.h>
36
37using namespace jau;
38
39/**
40 * On aarch64 using g++ (Debian 10.2.1-6) 10.2.1 20210110
41 * optimization of `jau::get_backtrace(..)` leads to a SIGSEGV
42 * on libunwind version [1.3 - 1.6.2] function `unw_step(..)`
43 */
44#if defined(__GNUC__) && ! defined(__clang__)
45 #pragma GCC push_options
46 #pragma GCC optimize("-O0")
47#endif
48
49// gcc-1 CTTI mangled name: 'constexpr const char* jau::ctti_name() [with T = int(int)]'
50static const std::string ctti_name_prefix_gcc1 = "constexpr const char* jau::ctti_name() [with T = ";
51
52// clang-1 CTTI mangled name: 'const char *jau::ctti_name() [T = int (int)]`
53static const std::string ctti_name_prefix_clang1 = "const char *jau::ctti_name() [T = ";
54
56
57std::string jau::demangle_name(const char* mangled_name) noexcept {
58 const size_t len = ::strlen(mangled_name);
59 if( nullptr == mangled_name || 0 == len ) {
60 return std::string();
61 }
62 for(const std::string* ctti_name_prefix : ctti_name_prefixes) {
63 if( len > ctti_name_prefix->length()+2 &&
64 mangled_name == ::strstr(mangled_name, ctti_name_prefix->c_str()) )
65 {
66 std::string r( mangled_name + ctti_name_prefix->length() );
67 r.resize(r.length() - 1);
68 return r;
69 }
70 }
71
72 int status;
73 /**
74 * 0: The demangling operation succeeded.
75 * -1: A memory allocation failure occurred.
76 * -2: @a mangled_name is not a valid name under the C++ ABI mangling rules.
77 * -3: One of the arguments is invalid.
78 */
79 char* real_name = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status);
80 if ( nullptr == real_name ) {
81 return std::string(mangled_name); // didn't work, use mangled_name
82 } else {
83 std::string res;
84 if( 0 != status || 0 == ::strlen(real_name) ) {
85 res = std::string(mangled_name); // didn't work, use mangled_name
86 } else {
87 res = std::string(real_name);
88 }
89 free( real_name );
90 return res;
91 }
92}
93
94std::string jau::get_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames, const jau::snsize_t skip_frames) noexcept {
95 std::string out;
96#ifdef USE_LIBUNWIND
97 // symbol:
98 // 1: _ZN9direct_bt10DBTAdapter14startDiscoveryEbNS_19HCILEOwnAddressTypeEtt + 0x58d @ ip 0x7faa959d6daf, sp 0x7ffe38f301e0
99 // de-mangled:
100 // 1: direct_bt::DBTAdapter::startDiscovery(bool, direct_bt::HCILEOwnAddressType, unsigned short, unsigned short) + 0x58d @ ip 0x7f687b459071, sp 0x7fff2bf795d0
101 jau::snsize_t frame=0;
102 int res;
103 char cstr[256];
104 unw_context_t uc;
105 unw_word_t ip, sp;
106 unw_cursor_t cursor;
107 unw_word_t offset;
108
109 if( 0 != ( res = unw_getcontext(&uc) ) ) {
110 INFO_PRINT("unw_getcontext ERR: %d\n", res);
111 return out;
112 }
113 if( 0 != ( res = unw_init_local(&cursor, &uc) ) ) {
114 INFO_PRINT("unw_init_local ERR %d\n", res);
115 return out;
116 }
117 bool last_frame_anon = false;
118 while( unw_step(&cursor) > 0 && ( 0 > max_frames || ( max_frames + skip_frames ) > ( frame + 1 ) ) ) {
119 frame++;
120 if( skip_frames > frame ) {
121 continue;
122 }
123 bool append_line;
124 snprintf(cstr, sizeof(cstr), "%3zd: ", (ssize_t)frame);
125 std::string line(cstr);
126
127 if( 0 != ( res = unw_get_reg(&cursor, UNW_REG_IP, &ip) ) ) { // instruction pointer (pc)
128 INFO_PRINT("unw_get_reg(IP): frame %zd, ERR %d\n", frame, res);
129 ip = 0;
130 }
131 if( 0 != ( res = unw_get_reg(&cursor, UNW_REG_SP, &sp) ) ) { // stack pointer
132 INFO_PRINT("unw_get_reg(SP): frame %zd, ERR %d\n", frame, res);
133 sp = 0;
134 }
135 if( 0 == unw_get_proc_name(&cursor, cstr, sizeof(cstr), &offset) ) {
136 int status;
137 char *real_name;
138 cstr[sizeof(cstr) -1] = 0; // fail safe
139 if ( (real_name = abi::__cxa_demangle(cstr, nullptr, nullptr, &status)) == nullptr ) {
140 line.append(cstr); // didn't work, use cstr
141 } else {
142 line.append(real_name);
143 free( real_name );
144 }
145 snprintf(cstr, sizeof(cstr), " + 0x%lx @ ip %p, sp %p", (unsigned long)offset, (void*)ip, (void*)sp); // NOLINT(performance-no-int-to-ptr)
146 append_line = true;
147 last_frame_anon = false;
148 } else {
149 // anon frame w/o proc-name
150 snprintf(cstr, sizeof(cstr), "__no_proc_name__ @ ip %p, sp %p", (void*)ip, (void*)sp); // NOLINT(performance-no-int-to-ptr)
151 append_line = !skip_anon_frames || !last_frame_anon;
152 last_frame_anon = true;
153 }
154 line.append(cstr);
155 line.append("\n");
156 if( append_line ) {
157 out.append(line);
158 }
159 }
160#else /* USE_LIBUNWIND */
161 (void)skip_anon_frames;
162 (void)max_frames;
163 (void)skip_frames;
164 out.append("0: backtrace disabled\n");
165#endif /* USE_LIBUNWIND */
166 return out;
167}
168
169#if defined(__GNUC__) && ! defined(__clang__)
170 #pragma GCC pop_options
171#endif
172
173void jau::print_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames, const jau::snsize_t skip_frames) noexcept {
174 fprintf(stderr, "%s", get_backtrace(skip_anon_frames, max_frames, skip_frames).c_str());
175 fflush(stderr);
176}
177
178void jau::DBG_PRINT_impl(const char * format, ...) noexcept {
179 fprintf(stderr, "[%s] Debug: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
180 va_list args;
181 va_start (args, format);
182 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
183 va_end (args);
184 fprintf(stderr, "\n");
185 fflush(stderr);
186}
187
188void jau::WORDY_PRINT_impl(const char * format, ...) noexcept {
189 fprintf(stderr, "[%s] Wordy: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
190 va_list args;
191 va_start (args, format);
192 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
193 va_end (args);
194 fprintf(stderr, "\n");
195 fflush(stderr);
196}
197
198void jau::ABORT_impl(const char *func, const char *file, const int line, const char * format, ...) noexcept {
199 fprintf(stderr, "[%s] ABORT @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), file, line, func);
200 va_list args;
201 va_start (args, format);
202 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
203 va_end (args);
204 fprintf(stderr, "; last errno %d %s\n", errno, strerror(errno));
205 fflush(stderr);
206 jau::print_backtrace(true /* skip_anon_frames */, -1 /* max_frames -> all */, 3 /* skip_frames: this() + print_b*() + get_b*() */);
207 abort();
208}
209
210void jau::ERR_PRINTv(const char *func, const char *file, const int line, const char * format, va_list args) noexcept {
211 fprintf(stderr, "[%s] Error @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), file, line, func);
212 vfprintf(stderr, format, args);
213 fprintf(stderr, "; last errno %d %s\n", errno, strerror(errno));
214 fflush(stderr);
215 jau::print_backtrace(true /* skip_anon_frames */, 4 /* max_frames */, 3 /* skip_frames: this() + print_b*() + get_b*() */);
216}
217
218void jau::ERR_PRINT_impl(const char *prefix, const bool backtrace, const char *func, const char *file, const int line, const char * format, ...) noexcept {
219 fprintf(stderr, "[%s] %s @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), prefix, file, line, func);
220 va_list args;
221 va_start (args, format);
222 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
223 va_end (args);
224 fprintf(stderr, "; last errno %d %s\n", errno, strerror(errno));
225 fflush(stderr);
226 if( backtrace ) {
227 jau::print_backtrace(true /* skip_anon_frames */, 4 /* max_frames */, 3 /* skip_frames: this() + print_b*() + get_b*() */);
228 }
229}
230
231void jau::WARN_PRINTv(const char *func, const char *file, const int line, const char * format, va_list args) noexcept {
232 fprintf(stderr, "[%s] Warning @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), file, line, func);
233 vfprintf(stderr, format, args);
234 fprintf(stderr, "\n");
235 fflush(stderr);
236}
237
238void jau::WARN_PRINT_impl(const char *func, const char *file, const int line, const char * format, ...) noexcept {
239 fprintf(stderr, "[%s] Warning @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), file, line, func);
240 va_list args;
241 va_start (args, format);
242 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
243 va_end (args);
244 fprintf(stderr, "\n");
245 fflush(stderr);
246}
247
248void jau::INFO_PRINT(const char * format, ...) noexcept {
249 fprintf(stderr, "[%s] Info: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
250 va_list args;
251 va_start (args, format);
252 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
253 va_end (args);
254 fprintf(stderr, "\n");
255 fflush(stderr);
256}
257
258void jau::PLAIN_PRINT(const bool printPrefix, const char * format, ...) noexcept {
259 if( printPrefix ) {
260 fprintf(stderr, "[%s] ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
261 }
262 va_list args;
263 va_start (args, format);
264 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
265 va_end (args);
266 fprintf(stderr, "\n");
267 fflush(stderr);
268}
269
270int jau::fprintf_td(const uint64_t elapsed_ms, FILE* stream, const char * format, ...) noexcept {
271 int res = ::fprintf(stream, "[%s] ", jau::to_decstring(elapsed_ms, ',', 9).c_str());
272 va_list args;
273 va_start (args, format);
274 res += ::vfprintf(stream, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
275 va_end (args);
276 return res;
277}
278int jau::fprintf_td(FILE* stream, const char * format, ...) noexcept {
279 int res = ::fprintf(stream, "[%s] ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
280 va_list args;
281 va_start (args, format);
282 res += ::vfprintf(stream, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
283 va_end (args);
284 return res;
285}
286
287void jau::COND_PRINT_impl(const char * format, ...) noexcept {
288 fprintf(stderr, "[%s] ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
289 va_list args;
290 va_start (args, format);
291 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
292 va_end (args);
293 fprintf(stderr, "\n");
294 fflush(stderr);
295}
296
297extern "C" {
298 int jaulib_id_entryfunc() { return 1; }
299}
static const std::string ctti_name_prefix_clang1
Definition: debug.cpp:53
int jaulib_id_entryfunc()
Definition: debug.cpp:298
static const std::string ctti_name_prefix_gcc1
On aarch64 using g++ (Debian 10.2.1-6) 10.2.1 20210110 optimization of jau::get_backtrace(....
Definition: debug.cpp:50
static const std::string * ctti_name_prefixes[]
Definition: debug.cpp:55
std::string demangle_name(const char *mangled_name) noexcept
Returns the demangled given mangled_name if successful, otherwise the mangled_name.
Definition: debug.cpp:57
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:65
std::string to_decstring(const value_type &v, const char separator=',', const nsize_t width=0) noexcept
Produce a decimal string representation of an integral integer value.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
void ABORT_impl(const char *func, const char *file, const int line, const char *format,...) noexcept
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line fu...
Definition: debug.cpp:198
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition: debug.cpp:258
void ERR_PRINTv(const char *func, const char *file, const int line, const char *format, va_list args) noexcept
Use for unconditional error messages, prefix '[elapsed_time] Error @ file:line func: '.
Definition: debug.cpp:210
std::string get_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames=-1, const jau::snsize_t skip_frames=1) noexcept
Returns a de-mangled backtrace string separated by newline excluding this function.
Definition: debug.cpp:94
void INFO_PRINT(const char *format,...) noexcept
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
Definition: debug.cpp:248
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, const char *format,...) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
Definition: debug.cpp:270
void WARN_PRINTv(const char *func, const char *file, const int line, const char *format, va_list args) noexcept
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ file:line func: '.
Definition: debug.cpp:231
void DBG_PRINT_impl(const char *format,...) noexcept
Definition: debug.cpp:178
void ERR_PRINT_impl(const char *prefix, const bool backtrace, const char *func, const char *file, const int line, const char *format,...) noexcept
Definition: debug.cpp:218
void COND_PRINT_impl(const char *format,...) noexcept
Definition: debug.cpp:287
void WORDY_PRINT_impl(const char *format,...) noexcept
Definition: debug.cpp:188
void WARN_PRINT_impl(const char *func, const char *file, const int line, const char *format,...) noexcept
Definition: debug.cpp:238
void print_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames=-1, const jau::snsize_t skip_frames=2) noexcept
Prints the de-mangled backtrace string separated by newline excluding this function to stderr,...
Definition: debug.cpp:173