jaulib v1.4.1-14-g15926ba
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
debug.hpp
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#ifndef JAU_DEBUG_HPP_
27#define JAU_DEBUG_HPP_
28
29#include <cstdlib>
30
31#include <cstdint>
32#include <cstring>
33#include <string>
34#include <cstdio>
35#include <cstdarg>
36#include <string_view>
37
38#include <jau/cpp_lang_util.hpp>
39#include <jau/environment.hpp>
40
41#include <jau/string_util.hpp>
42#include <jau/string_cfmt.hpp>
43
44// #define PERF_PRINT_ON 1
45
46namespace jau {
47
48 namespace impl {
49 template <typename... Args>
50 void dbgPrint0(FILE *out, bool addErrno, bool addBacktrace, std::string_view format, const Args &...args) noexcept {
51 std::exception_ptr eptr;
52 try {
53 ::fputs(jau::format_string(format, args...).c_str(), out);
54 if (addErrno) {
55 ::fprintf(stderr, "; last errno %d %s", errno, strerror(errno));
56 }
57 ::fputs("\n", out);
58 if (addBacktrace) {
59 ::fprintf(stderr, "%s", jau::get_backtrace(true /* skip_anon_frames */, 4 /* max_frames */, 2 /* skip_frames: this() + get_b*() */).c_str());
60 }
61 if (addErrno || addBacktrace) {
62 ::fflush(stderr);
63 }
64 } catch (...) {
65 eptr = std::current_exception();
66 }
67 handle_exception(eptr);
68 }
69
70 template <typename... Args>
71 void dbgPrint1(FILE *out, bool printPrefix, const char *msg, std::string_view format, const Args &...args) noexcept {
72 if (printPrefix) {
73 std::exception_ptr eptr;
74 try {
75 ::fputc('[', out);
76 ::fputs(jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), out);
77 ::fputs("] ", out);
78 if (msg) {
79 ::fputs(msg, out);
80 ::fputs(": ", out);
81 }
82 } catch (...) {
83 eptr = std::current_exception();
84 }
85 handle_exception(eptr);
86 }
87 jau::impl::dbgPrint0(out, false, false, format, args...);
88 }
89
90 template <typename... Args>
91 void dbgPrint2(FILE *out, const char *msg, bool addErrno, bool addBacktrace, const char *func, const char *file, const int line,
92 std::string_view format, const Args &...args) noexcept {
93 std::exception_ptr eptr;
94 try {
95 ::fputc('[', out);
96 ::fputs(jau::to_decstring(environment::getElapsedMillisecond(), ',', 9).c_str(), out);
97 ::fputs("] ", out);
98 if (msg) {
99 ::fputs(msg, out);
100 ::fputs(" ", out);
101 }
102 ::fprintf(stderr, "@ %s:%d %s: ", file, line, func);
103 } catch (...) {
104 eptr = std::current_exception();
105 }
106 handle_exception(eptr);
107 jau::impl::dbgPrint0(out, addErrno, addBacktrace, format, args...);
108 }
109 }
110
111 #define jau_dbgPrint1(out, printPrefix, msg, fmt, ...) \
112 jau::impl::dbgPrint1((out), (printPrefix), (msg), (fmt) __VA_OPT__(,) __VA_ARGS__); \
113 static_assert(0 <= jau::cfmt::check2< JAU_FOR_EACH1_LIST(JAU_DECLTYPE_VALUE, __VA_ARGS__) >(fmt)); // compile time validation!
114
115 #define jau_dbgPrint1Line(out, printPrefix, msg, fmt, ...) \
116 jau::impl::dbgPrint1((out), (printPrefix), (msg), (fmt) __VA_OPT__(,) __VA_ARGS__); \
117 static_assert(0 == jau::cfmt::check2Line< JAU_FOR_EACH1_LIST(JAU_DECLTYPE_VALUE, __VA_ARGS__) >(fmt)); // compile time validation!
118
119 #define jau_dbgPrint2(out, msg, addErrno, addBacktrace, func, file, line, fmt, ...) \
120 jau::impl::dbgPrint2((out), (msg), (addErrno), (addBacktrace), (func), (file), (line), (fmt) __VA_OPT__(,) __VA_ARGS__); \
121 static_assert(0 <= jau::cfmt::check2< JAU_FOR_EACH1_LIST(JAU_DECLTYPE_VALUE, __VA_ARGS__) >(fmt)); // compile time validation!
122
123 #define jau_dbgPrint2Line(out, msg, addErrno, addBacktrace, func, file, line, fmt, ...) \
124 jau::impl::dbgPrint2((out), (msg), (addErrno), (addBacktrace), (func), (file), (line), (fmt) __VA_OPT__(,) __VA_ARGS__); \
125 static_assert(0 == jau::cfmt::check2Line< JAU_FOR_EACH1_LIST(JAU_DECLTYPE_VALUE, __VA_ARGS__) >(fmt)); // compile time validation!
126
127 /** Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] Debug: '. */
128 #define DBG_PRINT(fmt, ...) { if( jau::environment::get().debug ) { jau_dbgPrint1(stderr, true, "Debug", fmt __VA_OPT__(,) __VA_ARGS__); } }
129 #define DBG_PRINT_LINE(fmt, ...) { if( jau::environment::get().debug ) { jau_dbgPrint1Line(stderr, true, "Debug", fmt __VA_OPT__(,) __VA_ARGS__); } }
130
131 /** Use for environment-variable environment::DEBUG_JNI conditional debug messages, prefix '[elapsed_time] Debug: '. */
132 #define DBG_JNI_PRINT(...) { if( jau::environment::get().debug_jni ) { jau_dbgPrint1(stderr, true, "Debug", __VA_ARGS__); } }
133
134 /** Use for environment-variable environment::DEBUG conditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: ' */
135 #define DBG_WARN_PRINT(...) { if( jau::environment::get().debug ) { jau_dbgPrint2(stderr, "Warning", false /* errno */, false /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); } }
136
137 /** Use for environment-variable environment::DEBUG conditional error messages, prefix '[elapsed_time] Debug @ FILE:LINE FUNC: '. Function also appends last errno, strerror(errno) and full backtrace*/
138 #define DBG_ERR_PRINT(...) { if( jau::environment::get().debug ) { jau_dbgPrint2(stderr, "Debug", true /* errno */, true /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); } }
139
140 /**
141 * Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time] Wordy: '.
142 * <p>
143 * 'Wordy' is the shorter English form of the Latin word 'verbosus', from which the word 'verbosity' is sourced.
144 * </p>
145 */
146 #define WORDY_PRINT(...) { if( jau::environment::get().verbose ) { jau_dbgPrint1(stderr, true, "Wordy", __VA_ARGS__); } }
147
148 #define PERF_TS_T0_BASE() const uint64_t _t0 = jau::getCurrentMilliseconds()
149
150 #define PERF_TS_TD_BASE(m) { const uint64_t _td = jau::getCurrentMilliseconds() - _t0; \
151 fprintf(stderr, "[%s] PERF %s done in %d ms,\n", jau::to_decstring(jau::environment::getElapsedMillisecond(), ',', 9).c_str(), (m), (int)_td); }
152 #ifdef PERF_PRINT_ON
153 #define PERF_TS_T0() PERF_TS_T0_BASE()
154 #define PERF_TS_TD(m) PERF_TS_TD_BASE(m)
155 #else
156 #define PERF_TS_T0()
157 #define PERF_TS_TD(m)
158 #endif
159 #ifdef PERF2_PRINT_ON
160 #define PERF2_TS_T0() PERF_TS_T0_BASE()
161 #define PERF2_TS_TD(m) PERF_TS_TD_BASE(m)
162 #else
163 #define PERF2_TS_T0()
164 #define PERF2_TS_TD(m)
165 #endif
166 #ifdef PERF3_PRINT_ON
167 #define PERF3_TS_T0() PERF_TS_T0_BASE()
168 #define PERF3_TS_TD(m) PERF_TS_TD_BASE(m)
169 #else
170 #define PERF3_TS_T0()
171 #define PERF3_TS_TD(m)
172 #endif
173
174 /** Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line func: '. Function also appends last errno and strerror(errno). */
175 #define ABORT(...) { jau_dbgPrint2(stderr, "ABORT", true /* errno */, true /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); abort(); }
176
177 /** Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '. Function also appends last errno, strerror(errno) and full backtrace*/
178 #define ERR_PRINT(...) { jau_dbgPrint2(stderr, "Error", true /* errno */, true /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); }
179
180 /** Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '. Function also appends last errno and strerror(errno). No backtrace. */
181 #define ERR_PRINT2(...) { jau_dbgPrint2(stderr, "Error", true /* errno */, false /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); }
182
183 /** Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '. Function also appends last errno and strerror(errno). Full backtrace. */
184 #define ERR_PRINT3(...) { jau_dbgPrint2(stderr, "Error", true /* errno */, true /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); }
185
186 /** Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE FUNC: '. Function also appends last errno and strerror(errno). */
187 #define IRQ_PRINT(...) { jau_dbgPrint2(stderr, "Interrupted", true /* errno */, false /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); }
188
189 /** Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: ' */
190 #define WARN_PRINT(...) { jau_dbgPrint2(stderr, "Warning", false /* errno */, false /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); }
191
192 /** Use for unconditional informal messages, prefix '[elapsed_time] Info: '. */
193 #define INFO_PRINT(fmt, ...) { jau_dbgPrint1(stderr, true, "Info", fmt __VA_OPT__(,) __VA_ARGS__); }
194 #define INFO_PRINT_LINE(fmt, ...) { jau_dbgPrint1Line(stderr, true, "Info", fmt __VA_OPT__(,) __VA_ARGS__); }
195
196 /** Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true. */
197 #define PLAIN_PRINT(printPrefix, fmt, ...) { jau_dbgPrint1(stderr, (printPrefix), nullptr, fmt __VA_OPT__(,) __VA_ARGS__); }
198
199 /**
200 * Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
201 * @param elapsed_ms the given elapsed time in milliseconds
202 * @param stream the output stream
203 * @param format the format
204 * @param args the optional arguments
205 */
206 template <typename... Args>
207 int fprintf_td(const uint64_t elapsed_ms, FILE* stream, std::string_view format, const Args &...args) noexcept {
208 int res = 0;
209 std::exception_ptr eptr;
210 try {
211 res = ::fprintf(stream, "[%s] ", jau::to_decstring(elapsed_ms, ',', 9).c_str());
212 const int r = ::fputs(jau::format_string(format, args...).c_str(), stream);
213 if (r >= 0) {
214 res += r;
215 }
216 } catch (...) {
217 eptr = std::current_exception();
218 }
219 handle_exception(eptr);
220 return res;
221 }
222 /**
223 * Convenient fprintf() invocation, prepending the environment::getElapsedMillisecond() timestamp.
224 * @param stream the output stream
225 * @param format the format
226 * @param args the optional arguments
227 */
228 template <typename... Args>
229 inline int fprintf_td(FILE* stream, std::string_view format, const Args &...args) noexcept {
230 return fprintf_td(environment::getElapsedMillisecond(), stream, format, args...);
231 }
232
233 /** Use for conditional plain messages, prefix '[elapsed_time] '. */
234 #define COND_PRINT(C, ...) { if( C ) { jau::impl::dbgPrint0(stderr, false, false, __VA_ARGS__); } }
235
236 template<class List>
237 void printSharedPtrList(const std::string& prefix, List & list) noexcept {
238 ::fprintf(stderr, "%s: Start: %zu elements\n", prefix.c_str(), (size_t)list.size());
239 int idx = 0;
240 for (auto it = list.begin(); it != list.end(); idx++) {
241 typename List::value_type & e = *it;
242 if ( nullptr != e ) {
243 ::fprintf(stderr, "%s[%d]: useCount %zu, mem %p\n", prefix.c_str(), idx, (size_t)e.use_count(), e.get());
244 } else {
245 ::fprintf(stderr, "%s[%d]: NULL\n", prefix.c_str(), idx);
246 }
247 ++it;
248 }
249 }
250
251} // namespace jau
252
253#endif /* JAU_DEBUG_HPP_ */
static uint64_t getElapsedMillisecond() noexcept
Returns current elapsed monotonic time in milliseconds since module startup, see startupTimeMilliseco...
bool handle_exception(std::exception_ptr eptr)
Handle given optional exception (nullable std::exception_ptr) and send std::exception::what() message...
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 dbgPrint0(FILE *out, bool addErrno, bool addBacktrace, std::string_view format, const Args &...args) noexcept
Definition debug.hpp:50
void dbgPrint2(FILE *out, const char *msg, bool addErrno, bool addBacktrace, const char *func, const char *file, const int line, std::string_view format, const Args &...args) noexcept
Definition debug.hpp:91
void dbgPrint1(FILE *out, bool printPrefix, const char *msg, std::string_view format, const Args &...args) noexcept
Definition debug.hpp:71
__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
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, std::string_view format, const Args &...args) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
Definition debug.hpp:207
std::string format_string(std::string_view format, const Args &...args) noexcept
Safely returns a (non-truncated) string according to snprintf() formatting rules using a reserved str...
void printSharedPtrList(const std::string &prefix, List &list) noexcept
Definition debug.hpp:237