jaulib v1.4.0-2-g788cf73
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
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::impl::dbgPrint2(const char * s) noexcept {
179 fprintf(stderr, "[%s] Debug: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
180 fprintf(stderr, "%s\n", s);
181 fflush(stderr);
182}
183
184void jau::impl::dbgPrint1(const char * format, ...) noexcept {
185 fprintf(stderr, "[%s] Debug: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
186 va_list args;
187 va_start (args, format);
188 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
189 va_end (args);
190 fprintf(stderr, "\n");
191 fflush(stderr);
192 }
193
194void jau::impl::wordyPrint(const char * format, ...) noexcept {
195 fprintf(stderr, "[%s] Wordy: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
196 va_list args;
197 va_start (args, format);
198 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
199 va_end (args);
200 fprintf(stderr, "\n");
201 fflush(stderr);
202}
203
204void jau::impl::abortImpl(const char *func, const char *file, const int line, const char * format, ...) noexcept {
205 fprintf(stderr, "[%s] ABORT @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), file, line, func);
206 va_list args;
207 va_start (args, format);
208 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
209 va_end (args);
210 fprintf(stderr, "; last errno %d %s\n", errno, strerror(errno));
211 fflush(stderr);
212 jau::print_backtrace(true /* skip_anon_frames */, -1 /* max_frames -> all */, 3 /* skip_frames: this() + print_b*() + get_b*() */);
213 abort();
214}
215
216void jau::ERR_PRINTv(const char *func, const char *file, const int line, const char * format, va_list args) noexcept {
217 fprintf(stderr, "[%s] Error @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), file, line, func);
218 vfprintf(stderr, format, args);
219 fprintf(stderr, "; last errno %d %s\n", errno, strerror(errno));
220 fflush(stderr);
221 jau::print_backtrace(true /* skip_anon_frames */, 4 /* max_frames */, 3 /* skip_frames: this() + print_b*() + get_b*() */);
222}
223
224void jau::impl::errPrint(const char *prefix, const bool backtrace, const char *func, const char *file, const int line, const char * format, ...) noexcept {
225 fprintf(stderr, "[%s] %s @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), prefix, file, line, func);
226 va_list args;
227 va_start (args, format);
228 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
229 va_end (args);
230 fprintf(stderr, "; last errno %d %s\n", errno, strerror(errno));
231 fflush(stderr);
232 if( backtrace ) {
233 jau::print_backtrace(true /* skip_anon_frames */, 4 /* max_frames */, 3 /* skip_frames: this() + print_b*() + get_b*() */);
234 }
235}
236
237void jau::WARN_PRINTv(const char *func, const char *file, const int line, const char * format, va_list args) noexcept {
238 fprintf(stderr, "[%s] Warning @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), file, line, func);
239 vfprintf(stderr, format, args);
240 fprintf(stderr, "\n");
241 fflush(stderr);
242}
243
244void jau::impl::warnPrint(const char *func, const char *file, const int line, const char * format, ...) noexcept {
245 fprintf(stderr, "[%s] Warning @ %s:%d %s: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), file, line, func);
246 va_list args;
247 va_start (args, format);
248 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
249 va_end (args);
250 fprintf(stderr, "\n");
251 fflush(stderr);
252}
253
254void jau::INFO_PRINT(const char * format, ...) noexcept {
255 fprintf(stderr, "[%s] Info: ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
256 va_list args;
257 va_start (args, format);
258 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
259 va_end (args);
260 fprintf(stderr, "\n");
261 fflush(stderr);
262}
263
264void jau::PLAIN_PRINT(const bool printPrefix, const char * format, ...) noexcept {
265 if( printPrefix ) {
266 fprintf(stderr, "[%s] ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
267 }
268 va_list args;
269 va_start (args, format);
270 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
271 va_end (args);
272 fprintf(stderr, "\n");
273 fflush(stderr);
274}
275
276int jau::fprintf_td(const uint64_t elapsed_ms, FILE* stream, const char * format, ...) noexcept {
277 int res = ::fprintf(stream, "[%s] ", jau::to_decstring(elapsed_ms, ',', 9).c_str());
278 va_list args;
279 va_start (args, format);
280 res += ::vfprintf(stream, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
281 va_end (args);
282 return res;
283}
284int jau::fprintf_td(FILE* stream, const char * format, ...) noexcept {
285 int res = ::fprintf(stream, "[%s] ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
286 va_list args;
287 va_start (args, format);
288 res += ::vfprintf(stream, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
289 va_end (args);
290 return res;
291}
292
293void jau::COND_PRINT_impl(const char * format, ...) noexcept {
294 fprintf(stderr, "[%s] ", jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str());
295 va_list args;
296 va_start (args, format);
297 vfprintf(stderr, format, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
298 va_end (args);
299 fprintf(stderr, "\n");
300 fflush(stderr);
301}
302
303extern "C" {
304 int jaulib_id_entryfunc() { return 1; }
305}
static uint64_t getElapsedMillisecond() noexcept
Returns current elapsed monotonic time in milliseconds since module startup, see startupTimeMilliseco...
static const std::string ctti_name_prefix_clang1
Definition debug.cpp:53
int jaulib_id_entryfunc()
Definition debug.cpp:304
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:67
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.
void abortImpl(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:204
void wordyPrint(const char *format,...) noexcept
Definition debug.cpp:194
void dbgPrint1(const char *format,...) noexcept
Definition debug.cpp:184
void dbgPrint2(const char *s) noexcept
Definition debug.cpp:178
void errPrint(const char *prefix, const bool backtrace, const char *func, const char *file, const int line, const char *format,...) noexcept
Definition debug.cpp:224
void warnPrint(const char *func, const char *file, const int line, const char *format,...) noexcept
Definition debug.cpp:244
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.cpp:264
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:216
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:254
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:276
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:237
void COND_PRINT_impl(const char *format,...) noexcept
Definition debug.cpp:293
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