jaulib v1.4.1-17-gd77ace3-dirty
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#include <jau/exceptions.hpp>
28#include <jau/backtrace.hpp>
29
30#include <cstdarg>
31
32#ifdef USE_LIBUNWIND
33 #define UNW_LOCAL_ONLY
34 #include <libunwind.h>
35#endif /* USE_LIBUNWIND */
36
37#include <cxxabi.h>
38
39using namespace jau;
40
41/**
42 * On aarch64 using g++ (Debian 10.2.1-6) 10.2.1 20210110
43 * optimization of `jau::get_backtrace(..)` leads to a SIGSEGV
44 * on libunwind version [1.3 - 1.6.2] function `unw_step(..)`
45 */
46#if defined(__GNUC__) && ! defined(__clang__)
47 #pragma GCC push_options
48 #pragma GCC optimize("-O0")
49#endif
50
51// gcc-1 CTTI mangled name: 'constexpr const char* jau::ctti_name() [with T = int(int)]'
52static const std::string ctti_name_prefix_gcc1 = "constexpr const char* jau::ctti_name() [with T = ";
53
54// clang-1 CTTI mangled name: 'const char *jau::ctti_name() [T = int (int)]`
55static const std::string ctti_name_prefix_clang1 = "const char *jau::ctti_name() [T = ";
56
58
59std::string jau::demangle_name(const char* mangled_name) noexcept {
60 const size_t len = ::strlen(mangled_name);
61 if( nullptr == mangled_name || 0 == len ) {
62 return std::string();
63 }
64 for(const std::string* ctti_name_prefix : ctti_name_prefixes) {
65 if( len > ctti_name_prefix->length()+2 &&
66 mangled_name == ::strstr(mangled_name, ctti_name_prefix->c_str()) )
67 {
68 std::string r( mangled_name + ctti_name_prefix->length() );
69 r.resize(r.length() - 1);
70 return r;
71 }
72 }
73
74 int status;
75 /**
76 * 0: The demangling operation succeeded.
77 * -1: A memory allocation failure occurred.
78 * -2: @a mangled_name is not a valid name under the C++ ABI mangling rules.
79 * -3: One of the arguments is invalid.
80 */
81 char* real_name = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status);
82 if ( nullptr == real_name ) {
83 return std::string(mangled_name); // didn't work, use mangled_name
84 } else {
85 std::string res;
86 if( 0 != status || 0 == ::strlen(real_name) ) {
87 res = std::string(mangled_name); // didn't work, use mangled_name
88 } else {
89 res = std::string(real_name);
90 }
91 free( real_name );
92 return res;
93 }
94}
95
96std::string jau::get_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames, const jau::snsize_t skip_frames) noexcept {
97 std::string out;
98#ifdef USE_LIBUNWIND
99 // symbol:
100 // 1: _ZN9direct_bt10DBTAdapter14startDiscoveryEbNS_19HCILEOwnAddressTypeEtt + 0x58d @ ip 0x7faa959d6daf, sp 0x7ffe38f301e0
101 // de-mangled:
102 // 1: direct_bt::DBTAdapter::startDiscovery(bool, direct_bt::HCILEOwnAddressType, unsigned short, unsigned short) + 0x58d @ ip 0x7f687b459071, sp 0x7fff2bf795d0
103 jau::snsize_t frame=0;
104 int res;
105 char cstr[256];
106 unw_context_t uc;
107 unw_word_t ip, sp;
108 unw_cursor_t cursor;
109 unw_word_t offset;
110
111 if( 0 != ( res = unw_getcontext(&uc) ) ) {
112 INFO_PRINT("unw_getcontext ERR: %d\n", res);
113 return out;
114 }
115 if( 0 != ( res = unw_init_local(&cursor, &uc) ) ) {
116 INFO_PRINT("unw_init_local ERR %d\n", res);
117 return out;
118 }
119 bool last_frame_anon = false;
120 while( unw_step(&cursor) > 0 && ( 0 > max_frames || ( max_frames + skip_frames ) > ( frame + 1 ) ) ) {
121 frame++;
122 if( skip_frames > frame ) {
123 continue;
124 }
125 bool append_line;
126 snprintf(cstr, sizeof(cstr), "%3zd: ", (ssize_t)frame);
127 std::string line(cstr);
128
129 if( 0 != ( res = unw_get_reg(&cursor, UNW_REG_IP, &ip) ) ) { // instruction pointer (pc)
130 INFO_PRINT("unw_get_reg(IP): frame %zd, ERR %d\n", frame, res);
131 ip = 0;
132 }
133 if( 0 != ( res = unw_get_reg(&cursor, UNW_REG_SP, &sp) ) ) { // stack pointer
134 INFO_PRINT("unw_get_reg(SP): frame %zd, ERR %d\n", frame, res);
135 sp = 0;
136 }
137 if( 0 == unw_get_proc_name(&cursor, cstr, sizeof(cstr), &offset) ) {
138 int status;
139 char *real_name;
140 cstr[sizeof(cstr) -1] = 0; // fail safe
141 if ( (real_name = abi::__cxa_demangle(cstr, nullptr, nullptr, &status)) == nullptr ) {
142 line.append(cstr); // didn't work, use cstr
143 } else {
144 line.append(real_name);
145 free( real_name );
146 }
147 snprintf(cstr, sizeof(cstr), " + 0x%lx @ ip %p, sp %p", (unsigned long)offset, (void*)ip, (void*)sp); // NOLINT(performance-no-int-to-ptr)
148 append_line = true;
149 last_frame_anon = false;
150 } else {
151 // anon frame w/o proc-name
152 snprintf(cstr, sizeof(cstr), "__no_proc_name__ @ ip %p, sp %p", (void*)ip, (void*)sp); // NOLINT(performance-no-int-to-ptr)
153 append_line = !skip_anon_frames || !last_frame_anon;
154 last_frame_anon = true;
155 }
156 line.append(cstr);
157 line.append("\n");
158 if( append_line ) {
159 out.append(line);
160 }
161 }
162#else /* USE_LIBUNWIND */
163 (void)skip_anon_frames;
164 (void)max_frames;
165 (void)skip_frames;
166 out.append("0: backtrace disabled\n");
167#endif /* USE_LIBUNWIND */
168 return out;
169}
170
171#if defined(__GNUC__) && ! defined(__clang__)
172 #pragma GCC pop_options
173#endif
174
175void jau::print_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames, const jau::snsize_t skip_frames) noexcept {
176 ::fprintf(stderr, "%s", get_backtrace(skip_anon_frames, max_frames, skip_frames).c_str());
177 ::fflush(stderr);
178}
179
180extern "C" {
181 int jaulib_id_entryfunc() { return 1; }
182}
static const std::string ctti_name_prefix_clang1
Definition debug.cpp:55
int jaulib_id_entryfunc()
Definition debug.cpp:181
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:52
static const std::string * ctti_name_prefixes[]
Definition debug.cpp:57
#define INFO_PRINT(fmt,...)
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
Definition debug.hpp:193
std::string demangle_name(const char *mangled_name) noexcept
Returns the demangled given mangled_name if successful, otherwise the mangled_name.
Definition debug.cpp:59
sint_bytes_t< sizeof(long int)> snsize_t
Natural 'ssize_t' alternative using int<XX>_t with xx = sizeof(long int)*8 as its natural sized type,...
Definition int_types.hpp:97
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
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:96
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:175