UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
debug.cpp
Go to the documentation of this file.
1/*
2 * FILE: debug.cpp
3 * PROGRAM: RAT
4 * AUTHORS: Isidor Kouvelas
5 * Colin Perkins
6 * Mark Handley
7 * Orion Hodson
8 * Jerry Isdale
9 *
10 * Copyright (c) 1995-2000 University College London
11 * Copyright (c) 2005-2023 CESNET, z. s. p. o.
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, is permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the Computer Science
25 * Department at University College London
26 * 4. Neither the name of the University nor of the Department may be used
27 * to endorse or promote products derived from this software without
28 * specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "AS IS" AND
30 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41
42#ifdef HAVE_CONFIG_H
43#include "config.h"
44#endif // defined HAVE_CONFIG_H
45#include "config_unix.h"
46#include "config_win32.h"
47
48#include <array>
49#include <atomic>
50#include <cstdint>
51#include <string>
52#include <string_view>
53#include <unordered_map>
54
55#include "debug.h"
56#include "host.h"
57#include "utils/color_out.h"
59#include "utils/misc.h" // ug_strerror
60
61using std::atomic;
62using std::string;
63using std::unordered_map;
64
66
67static void _dprintf(const char *format, ...)
68{
70 return;
71 }
72
73#ifdef _WIN32
74 char msg[65535];
75 va_list ap;
76
77 va_start(ap, format);
78 _vsnprintf(msg, 65535, format, ap);
79 va_end(ap);
80 OutputDebugString(msg);
81#else
82 va_list ap;
83
84 va_start(ap, format);
85 vfprintf(stderr, format, ap);
86 va_end(ap);
87#endif /* _WIN32 */
88}
89
90static void
91log_vprintf(int level, const char *format, va_list ap)
92{
93 va_list aq;
94
95 if (log_level < level) {
96 return;
97 }
98
99 // get number of required bytes
100 va_copy(aq, ap);
101 int size = vsnprintf(NULL, 0, format, aq);
102 va_end(aq);
103
104 // format the string
105 auto buf = get_log_output().get_buffer();
106 const auto& style = get_log_output().get_level_style(level);
107
108 buf.append(style);
109 buf.append(size + 1, '\0');
110
111 va_copy(aq, ap);
112 vsnprintf(buf.data() + style.length(), size + 1, format, ap);
113 va_end(aq);
114 auto & str = buf.get();
115 str.resize(str.size() - 1); // drop '\0' written by vsnprintf
116
117 if (!get_log_output().is_interactive()) {
119 } else if (style.size() > 0) {
120 if (str.at(str.size() - 1) == '\n') { // put TERM_RESET before '\n'
121 str.erase(str.size() - 1);
122 buf.append(TERM_RESET "\n");
123 } else {
124 buf.append(TERM_RESET);
125 }
126 }
127 buf.submit();
128}
129
130void log_msg(int level, const char *format, ...) {
131 va_list ap;
132 va_start(ap, format);
133 log_vprintf(level, format, ap);
134 va_end(ap);
135}
136
137void log_msg_once(int level, uint32_t id, const char *msg, ...) {
138 if (log_level < level) {
139 return;
140 }
141 static volatile uint32_t oneshot_messages[10];
142 static atomic<size_t> oneshot_messages_cnt;
143 for (size_t i = 0; i < oneshot_messages_cnt; ++i) {
144 if (oneshot_messages[i] == id) {
145 return;
146 }
147 }
148
149 va_list aq;
150 va_start(aq, msg);
151 log_vprintf(level, msg, aq);
152 va_end(aq);
153
154 size_t last_cnt = oneshot_messages_cnt.fetch_add(1);
155 if (last_cnt < sizeof oneshot_messages / sizeof oneshot_messages[0]) {
156 oneshot_messages[last_cnt] = id;
157 } else {
158 log_msg(LOG_LEVEL_WARNING, "oneshot_messages full!\n");
159 }
160}
161
165void log_perror(int level, const char *msg)
166{
167 log_msg(level, "%s: %s\n", msg, ug_strerror(errno));
168}
169
179void debug_dump(const void *lp, int len)
180{
182 return;
183 }
184 const char *p;
185 int i, j, start;
186 char Buff[81];
187 char tmpBuf[10];
188
189 _dprintf("Dump of %d=%xh bytes\n", len, len);
190 start = 0L;
191 while (start < len) {
192 /* start line with pointer position key */
193 p = (const char *)lp + start;
194 snprintf(Buff, sizeof Buff, "%p: ", p);
195
196 /* display each character as hex value */
197 for (i = start, j = 0; j < 16; p++, i++, j++) {
198 if (i < len) {
199 snprintf(tmpBuf, sizeof tmpBuf, "%02X", ((int)(*p) & 0xFF));
200 strcat(Buff, tmpBuf);
201 } else
202 strcat(Buff, " ");
203 if (j % 2 == 1) /* space between groups of 2 */
204 strcat(Buff, " ");
205 if (j == 7) /* space between groups of 8 */
206 strcat(Buff, " ");
207 }
208
209 strcat(Buff, " ");
210
211 /* display each character as character value */
212 for (i = start, j = 0, p = (const char *)lp + start;
213 (i < len && j < 16); p++, i++, j++) {
214 if (((*p) >= ' ') && ((*p) <= '~')) /* test displayable */
215 snprintf(tmpBuf, sizeof tmpBuf, "%c", *p);
216 else
217 snprintf(tmpBuf, sizeof tmpBuf, "%c", '.');
218 strcat(Buff, tmpBuf);
219 if (j == 7) /* space between groups of 8 */
220 strcat(Buff, " ");
221 }
222 _dprintf("%s\n", Buff);
223 start = i; /* next line starting byte */
224 }
225}
226
227bool parse_log_cfg(const char *conf_str,
228 int *log_lvl,
229 bool *logger_skip_repeats,
230 log_timestamp_mode *show_timestamps)
231{
232 assert(conf_str != nullptr);
233 assert(log_lvl != nullptr);
234 assert(logger_skip_repeats != nullptr);
235 assert(show_timestamps != nullptr);
236
237 using std::clog;
238
239 static const struct { const char *name; int level; } mapping[] = {
240 { "quiet", LOG_LEVEL_QUIET },
241 { "fatal", LOG_LEVEL_FATAL },
242 { "error", LOG_LEVEL_ERROR },
243 { "warning", LOG_LEVEL_WARNING},
244 { "notice", LOG_LEVEL_NOTICE},
245 { "info", LOG_LEVEL_INFO },
246 { "verbose", LOG_LEVEL_VERBOSE},
247 { "debug", LOG_LEVEL_DEBUG },
248 { "debug2", LOG_LEVEL_DEBUG2 },
249 };
250
251 std::string_view cfg = conf_str;
252
253 if (cfg == "help") {
254 col() << "log level: [0-" << LOG_LEVEL_MAX;
255 for (auto m : mapping) {
256 col() << "|" << m.name;
257 }
258 col() << "][+repeat][+/-timestamps]\n";
259 col() << TBOLD("\trepeat") << " - print repeating log messages\n";
260 col() << TBOLD("\ttimestamps") << " - enable/disable timestamps\n";
261 col() << "environment variable " << SBOLD("ULTRAGRID_VERBOSE") << " - use verbose output (same as `-V` on command-line)\n";
262 return false;
263 }
264
265 if (sv_contains(cfg, "+repeat")) {
266 *logger_skip_repeats = false;
267 }
268
269 if(auto ts_pos = cfg.find("timestamps"); ts_pos != cfg.npos && ts_pos > 0){
270 if(cfg[ts_pos - 1] == '+')
271 *show_timestamps = LOG_TIMESTAMP_ENABLED;
272 else
273 *show_timestamps = LOG_TIMESTAMP_DISABLED;
274 }
275
276 if (cfg.empty() || cfg[0] == '+' || cfg[0] == '-') { // only flags, no log level
277 return true;
278 }
279
280 if (isdigit(cfg[0])) {
281 long val = -1;
282 parse_num(cfg, val);
284 clog << "Log: wrong value: " << cfg << " (allowed range [0.." << LOG_LEVEL_MAX << "])\n";
285 return false;
286 }
287 *log_lvl = val;
288 return true;
289 }
290
291 auto log_lvl_sv = cfg;
292 auto sign_pos = log_lvl_sv.find_first_of("+-");
293 if(sign_pos != log_lvl_sv.npos)
294 log_lvl_sv.remove_suffix(log_lvl_sv.size() - sign_pos);
295
296 for (auto m : mapping) {
297 if (log_lvl_sv == m.name) {
298 *log_lvl = m.level;
299 return true;
300 }
301 }
302
303 LOG(LOG_LEVEL_ERROR) << "Wrong log level specification: " << cfg << "\n";
304 return false;
305}
306
307#ifdef DEBUG
308#define DUMP_FILE_USAGE \
309 "* debug-dump=<module>[=<n>][,<module2>[=<n>]\n" \
310 " Dumps specified buffer to a file for debugging, n-th buffer may " \
311 "be selected, name is <module>.dump.\n" \
312 " Avaiable modules: lavc-avframe, lavd-avframe\n"
313ADD_TO_PARAM("debug-dump", DUMP_FILE_USAGE);
314void debug_file_dump(const char *key, void (*serialize)(const void *data, FILE *), void *data) {
315 const char *dump_file_val = get_commandline_param("debug-dump");
316 static thread_local unordered_map<string, int> skip_map;
317 if (dump_file_val == nullptr) {
318 return;
319 }
320
321 if (strcmp(dump_file_val, "help") == 0) {
322 color_printf("\n" TBOLD("debug-dump") " usage:\n");
323 printf("%s\n", DUMP_FILE_USAGE);
324 exit_uv(0);
325 return;
326 }
327
328 // check if key is contained
329 string not_first = string(",") + key;
330 int skip_n = 0;
331 if (strstr(dump_file_val, key) == dump_file_val) {
332 const char *val = dump_file_val + strlen(key);
333 if (val[0] == '=') {
334 skip_n = atoi(val + 1);
335 }
336 } else if (strstr(dump_file_val, not_first.c_str()) != NULL) {
337 const char *val = strstr(dump_file_val, not_first.c_str()) + strlen(key);
338 if (val[0] == '=') {
339 skip_n = atoi(val + 1);
340 }
341 } else {
342 return;
343 }
344
345 if (skip_map.find(key) == skip_map.end()) {
346 skip_map[key] = skip_n;
347 }
348
349 if (skip_map[key] == -1) { // already exported
350 return;
351 }
352
353 if (skip_map[key] > 0) {
354 skip_map[key]--;
355 return;
356 }
357
358 // export
359 string name = string(key) + ".dump";
360 FILE *out = fopen(name.c_str(), "wb");
361 if (out == nullptr) {
362 perror("debug_file_dump fopen");
363 return;
364 }
365 serialize(data, out);
366 fclose(out);
367
368 skip_map[key] = -1;
369}
370#endif
371
378void
379bug_msg(int level, const char *format, ...)
380{
381 char fmt_adj[STR_LEN];
382 snprintf_ch(fmt_adj,
383 "%sPlease report a bug"
384#ifdef PACKAGE_BUGREPORT
385 " to " PACKAGE_BUGREPORT
386#endif // defined PACKAGE_BUGREPORT
387 " if you reach here.\n",
388 format);
389
390 va_list ap;
391 va_start(ap, format);
392 log_vprintf(level, fmt_adj, ap);
393 va_end(ap);
394}
395
int void * val
int void size_t * len
static const struct codec_param mapping[AC_COUNT]
Definition libavcodec.c:87
const std::string & get_level_style(int lvl)
Definition debug.h:194
static constexpr int initial_buf_size
Definition debug.h:173
std::string last_msg
Definition debug.h:188
bool interactive
Definition debug.h:182
Buffer get_buffer()
Definition debug.h:153
void prune_ansi_sequences_inplace(std::string &str)
bool color_output_init()
sets internal variable color_stdout and retunrs its contents
int color_printf(const char *format,...)
#define TERM_RESET
Definition color_out.h:47
#define SBOLD(x)
Definition color_out.h:80
#define TBOLD(x)
Definition color_out.h:70
void debug_dump(const void *lp, int len)
Definition debug.cpp:179
static void log_vprintf(int level, const char *format, va_list ap)
Definition debug.cpp:91
static void _dprintf(const char *format,...)
Definition debug.cpp:67
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
void log_msg_once(int level, uint32_t id, const char *msg,...)
Definition debug.cpp:137
void log_perror(int level, const char *msg)
Definition debug.cpp:165
volatile int log_level
Definition debug.cpp:65
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
Definition debug.cpp:379
bool parse_log_cfg(const char *conf_str, int *log_lvl, bool *logger_skip_repeats, log_timestamp_mode *show_timestamps)
Definition debug.cpp:227
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_DEBUG2
Definition debug.h:67
#define LOG_LEVEL_QUIET
suppress all logging
Definition debug.h:57
#define debug_file_dump(key, serialize, data)
Definition debug.h:86
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define LOG_LEVEL_MAX
Definition debug.h:69
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_FATAL
errors that prevent UG run
Definition debug.h:58
Log_output & get_log_output()
Definition debug.h:266
log_timestamp_mode
Definition debug.h:72
@ LOG_TIMESTAMP_ENABLED
Definition debug.h:74
@ LOG_TIMESTAMP_DISABLED
Definition debug.h:73
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
void exit_uv(int status)
Definition host.cpp:625
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define ADD_TO_PARAM(param, doc)
Definition host.h:203
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48
#define snprintf_ch(str,...)
shortcut for snprintf(var, sizeof var...), var must be a char array
Definition macros.h:101
@ STR_LEN
"standard" string length placeholder
Definition macros.h:134
const char * ug_strerror(int errnum)
Definition misc.cpp:203
static void start()
Definition sdp.c:202
const char * name
Definition settings.cpp:193
bool sv_contains(std::string_view haystack, std::string_view needle)
bool parse_num(std::string_view sv, T &res, int base=10)
const char * key
Definition cineform.cpp:125
int vsnprintf(char *s, size_t buf_size, const char *format, va_list ap)
Definition vsnprintf.c:46