GCC Code Coverage Report


Directory: src/
File: src/support/debug.c
Date: 2026-09-15 07:37:49
Exec Total Coverage
Lines: 157 230 68.3%
Functions: 20 28 71.4%
Branches: 65 122 53.3%

Line Branch Exec Source
1 /*********************************************************************************/
2 /* Copyright 2009-2021 Barcelona Supercomputing Center */
3 /* */
4 /* This file is part of the DLB library. */
5 /* */
6 /* DLB is free software: you can redistribute it and/or modify */
7 /* it under the terms of the GNU Lesser General Public License as published by */
8 /* the Free Software Foundation, either version 3 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* DLB is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU Lesser General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU Lesser General Public License */
17 /* along with DLB. If not, see <https://www.gnu.org/licenses/>. */
18 /*********************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include "support/debug.h"
25
26 #include "apis/dlb_errors.h"
27 #include "support/options.h"
28 #include "support/types.h"
29 #include "support/mask_utils.h"
30 #include "LB_comm/shmem.h"
31 #include "LB_comm/shmem_async.h"
32 #include "LB_comm/shmem_barrier.h"
33 #include "LB_comm/shmem_cpuinfo.h"
34 #include "LB_comm/shmem_lewi_light.h"
35 #include "LB_comm/shmem_procinfo.h"
36 #include "LB_comm/shmem_talp.h"
37 #include "LB_core/spd.h"
38
39 #ifdef MPI_LIB
40 #include "mpi/mpi_core.h"
41 #endif
42
43 #include <sys/types.h>
44 #include <sys/syscall.h>
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <limits.h>
49 #include <unistd.h>
50 #include <stdarg.h>
51 #include <time.h>
52
53 #ifdef HAVE_EXECINFO_H
54 #include <execinfo.h>
55 #endif
56
57 verbose_opts_t vb_opts = VB_UNDEF;
58
59 enum { VBFORMAT_LEN = 128 };
60 static verbose_fmt_t vb_fmt;
61 static char fmt_str[VBFORMAT_LEN];
62 static bool quiet = false;
63 static bool silent = false;
64 static bool wall = false;
65 static bool werror = false;
66 static pthread_mutex_t dlb_clean_mutex = PTHREAD_MUTEX_INITIALIZER;
67
68 160 void debug_init(const options_t *options) {
69 160 quiet = options->quiet;
70 160 silent = options->silent;
71 160 wall = options->debug_opts & DBG_WALL;
72 160 werror = options->debug_opts & DBG_WERROR;
73 160 vb_opts = options->verbose;
74 160 vb_fmt = options->verbose_fmt;
75
76 160 int i = 0;
77
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 2 times.
160 if ( vb_fmt & VBF_NODE ) {
78 char hostname[VBFORMAT_LEN/2];
79 158 gethostname(hostname, sizeof(hostname));
80 158 hostname[sizeof(hostname)-1] = '\0';
81 158 i += sprintf( &fmt_str[i], "%s:", hostname);
82 }
83 #ifdef MPI_LIB
84 if ( vb_fmt & VBF_MPINODE ) { i += sprintf( &fmt_str[i], "%d:", _node_id); }
85 if ( vb_fmt & VBF_MPIRANK ) { i += sprintf( &fmt_str[i], "%d:", _mpi_rank); }
86 #endif
87
88 // Remove last separator ':' if fmt_str is not empty
89
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 2 times.
160 if ( i !=0 ) {
90 158 fmt_str[i-1] = '\0';
91 }
92 160 }
93
94 1902 static void vprint(FILE *fp, const char *prefix, const char *fmt, va_list list) {
95 // Write timestamp
96 enum { TIMESTAMP_MAX_SIZE = 32 };
97 char timestamp[TIMESTAMP_MAX_SIZE];
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1902 times.
1902 if (vb_fmt & VBF_TSTAMP) {
99 time_t t = time(NULL);
100 struct tm *tm = localtime(&t);
101 strftime(timestamp, TIMESTAMP_MAX_SIZE, "[%Y-%m-%dT%T] ", tm);
102 } else {
103 1902 timestamp[0] = '\0';
104 }
105
106 // Write spid
107 enum { SPID_MAX_SIZE = 16 };
108 char spid[SPID_MAX_SIZE];
109
4/4
✓ Branch 0 taken 808 times.
✓ Branch 1 taken 1094 times.
✓ Branch 2 taken 525 times.
✓ Branch 3 taken 283 times.
1902 if (vb_fmt & VBF_SPID && thread_spd) {
110 525 snprintf(spid, SPID_MAX_SIZE, ":%d", thread_spd->id);
111 } else {
112 1377 spid[0] = '\0';
113 }
114
115 // Write thread id
116 enum { THREADID_MAX_SIZE = 24 };
117 char threadid[THREADID_MAX_SIZE];
118
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1901 times.
1902 if (vb_fmt & VBF_THREAD) {
119 1 snprintf(threadid, THREADID_MAX_SIZE, ":%ld", syscall(SYS_gettid));
120 } else {
121 1901 threadid[0] = '\0';
122 }
123
124 // Allocate message in an intermediate buffer and print in one function
125 char *msg;
126 1902 vasprintf(&msg, fmt, list);
127 1902 fprintf(fp, "%s%s[%s%s%s]: %s\n", timestamp, prefix, fmt_str, spid, threadid, msg);
128 1902 free(msg);
129 1902 }
130
131 static void __attribute__((__noreturn__)) vfatal(const char *fmt, va_list list) {
132 /* Parse --silent option if fatal() was invoked before init */
133 if (unlikely(vb_opts == VB_UNDEF)) {
134 /* If fatal() was invoked before debug_init, we want to parse the
135 * --silent option but ensuring that parsing the options does not cause
136 * a recursive fatal error */
137 vb_opts = VB_CLEAR;
138 options_parse_entry("--silent", &silent);
139 }
140
141 if (!silent) {
142 vprint(stderr, "DLB PANIC", fmt, list);
143 }
144 dlb_clean();
145 abort();
146 }
147
148 void fatal(const char *fmt, ...) {
149 va_list list;
150 va_start(list, fmt);
151 vfatal(fmt, list);
152 va_end(list);
153 }
154
155 void fatal0(const char *fmt, ...) {
156 #ifdef MPI_LIB
157 if (_mpi_rank <= 0) {
158 #endif
159 va_list list;
160 va_start(list, fmt);
161 vfatal(fmt, list);
162 va_end(list);
163 #ifdef MPI_LIB
164 } else {
165 dlb_clean();
166 abort();
167 }
168 #endif
169 }
170
171 116 static void vwarning(const char *fmt, va_list list) {
172 /* Parse --silent option if warning() was invoked before init */
173
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 65 times.
116 if (unlikely(vb_opts == VB_UNDEF)) {
174 51 options_parse_entry("--silent", &silent);
175 }
176
177
1/2
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
116 if (!silent) {
178 116 vprint(stderr, "DLB WARNING", fmt, list);
179 }
180 116 }
181
182 115 void warning(const char *fmt, ...) {
183 va_list list;
184 115 va_start(list, fmt);
185
1/2
✓ Branch 0 taken 115 times.
✗ Branch 1 not taken.
115 if (!werror) vwarning(fmt, list);
186 else vfatal(fmt, list);
187 115 va_end(list);
188 115 }
189
190 1 void warning0(const char *fmt, ...) {
191 #ifdef MPI_LIB
192 if (_mpi_rank <= 0) {
193 #endif
194 va_list list;
195 1 va_start(list, fmt);
196
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!werror) vwarning(fmt, list);
197 else vfatal(fmt, list);
198 1 va_end(list);
199 #ifdef MPI_LIB
200 }
201 #endif
202 1 }
203
204 138 void debug_warning_impl(const char *file, int line, const char *func, const char *fmt, ...) {
205
206
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 if (!wall) return;
207
208 /* Construct new fmt based on args */
209 enum { MAX_WARNING_LEN = 8096 };
210 const char *warning_fmt = "%s:%d:%s(): %s";
211 int newfmt_len = snprintf(NULL, 0, warning_fmt, file, line, func, fmt);
212 if (newfmt_len < 0) return;
213 newfmt_len = max_int(newfmt_len, MAX_WARNING_LEN - 1) + 1;
214
215 char *newfmt = malloc((size_t)newfmt_len);
216 if (newfmt == NULL) return;
217 snprintf(newfmt, newfmt_len, warning_fmt, file, line, func, fmt);
218
219 va_list list;
220 va_start(list, fmt);
221 if (!werror) vwarning(newfmt, list);
222 else vfatal(newfmt, list);
223 va_end(list);
224
225 free(newfmt);
226 }
227
228 596 static void vinfo(const char *fmt, va_list list) {
229 /* Parse --quiet and --silent options if info() was invoked before init */
230
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 552 times.
596 if (unlikely(vb_opts == VB_UNDEF)) {
231 44 options_parse_entry("--quiet", &quiet);
232 44 options_parse_entry("--silent", &silent);
233 }
234
235
2/4
✓ Branch 0 taken 596 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 596 times.
✗ Branch 3 not taken.
596 if (!quiet && !silent) {
236 596 vprint(stderr, "DLB", fmt, list);
237 }
238 596 }
239
240 304 void info(const char *fmt, ...) {
241 va_list list;
242 304 va_start(list, fmt);
243 304 vinfo(fmt, list);
244 304 va_end(list);
245 304 }
246
247 292 void info0(const char *fmt, ...) {
248 #ifdef MPI_LIB
249 if (_mpi_rank <= 0) {
250 #endif
251 va_list list;
252 292 va_start(list, fmt);
253 292 vinfo(fmt, list);
254 292 va_end(list);
255 #ifdef MPI_LIB
256 }
257 #endif
258 292 }
259
260 /* Ignores --quiet and --silent, used for explicit prints like variable listing */
261 4 void info0_force_print(const char *fmt, ...) {
262 #ifdef MPI_LIB
263 if (_mpi_rank <= 0) {
264 #endif
265 va_list list;
266 4 va_start(list, fmt);
267 4 vprint(stderr, "DLB", fmt, list);
268 4 va_end(list);
269 #ifdef MPI_LIB
270 }
271 #endif
272 4 }
273
274 1918 static void vverbose(verbose_opts_t flag, const char *fmt, va_list list) {
275
276 /* Parse verbose options if verbose() was invoked before init */
277
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 1858 times.
1918 if (unlikely(vb_opts == VB_UNDEF)) {
278 60 options_parse_entry("--verbose", &vb_opts);
279 }
280
281
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1918 times.
1918 if (vb_opts & flag & VB_API) { vprint(stderr, "DLB API", fmt, list); }
282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1918 times.
1918 else if (vb_opts & flag & VB_MICROLB) { vprint(stderr, "DLB MICROLB", fmt, list); }
283
2/2
✓ Branch 0 taken 139 times.
✓ Branch 1 taken 1779 times.
1918 else if (vb_opts & flag & VB_SHMEM) { vprint(stderr, "DLB SHMEM", fmt, list); }
284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1779 times.
1779 else if (vb_opts & flag & VB_MPI_API) { vprint(stderr, "DLB MPI API", fmt, list); }
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1779 times.
1779 else if (vb_opts & flag & VB_MPI_INT) { vprint(stderr, "DLB MPI INT", fmt, list); }
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1779 times.
1779 else if (vb_opts & flag & VB_STATS) { vprint(stderr, "DLB STATS", fmt, list); }
287
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1762 times.
1779 else if (vb_opts & flag & VB_DROM) { vprint(stderr, "DLB DROM", fmt, list); }
288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1762 times.
1762 else if (vb_opts & flag & VB_ASYNC) { vprint(stderr, "DLB ASYNC", fmt, list); }
289
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1762 times.
1762 else if (vb_opts & flag & VB_OMPT) { vprint(stderr, "DLB OMPT", fmt, list); }
290
2/2
✓ Branch 0 taken 888 times.
✓ Branch 1 taken 874 times.
1762 else if (vb_opts & flag & VB_AFFINITY){ vprint(stderr, "DLB AFFINITY", fmt, list); }
291
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 858 times.
874 else if (vb_opts & flag & VB_BARRIER) { vprint(stderr, "DLB BARRIER", fmt, list); }
292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 858 times.
858 else if (vb_opts & flag & VB_TALP) { vprint(stderr, "DLB TALP", fmt, list); }
293
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 858 times.
858 else if (vb_opts & flag & VB_INSTR) { vprint(stderr, "DLB INSTRUMENT", fmt, list); }
294
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 732 times.
858 else if (vb_opts & flag & VB_MNGO) { vprint(stderr, "DLB MNGO", fmt, list); }
295 1918 }
296
297 #undef verbose
298 1846 void verbose(verbose_opts_t flag, const char *fmt, ...) {
299
300
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1846 times.
1846 if (quiet) return;
301
302 va_list list;
303 1846 va_start(list, fmt);
304 1846 vverbose(flag, fmt, list);
305 1846 va_end(list);
306 }
307
308 #undef verbose0
309 72 void verbose0(verbose_opts_t flag, const char *fmt, ...) {
310 #ifdef MPI_LIB
311 if (_mpi_rank <= 0) {
312 #endif
313 va_list list;
314 72 va_start(list, fmt);
315 72 vverbose(flag, fmt, list);
316 72 va_end(list);
317 #ifdef MPI_LIB
318 }
319 #endif
320 72 }
321
322 bool verbose_check(verbose_opts_t flag) {
323 /* Parse verbose options if verbose() was invoked before init */
324 if (unlikely(vb_opts == VB_UNDEF)) {
325 options_parse_entry("--verbose", &vb_opts);
326 }
327
328 return !quiet && !silent && (vb_opts & flag);
329 }
330
331 static void todo_func(const char *description, const char* file, int line, const char* func) {
332 fprintf(stderr, "!! DLB TODO [%s:%d (%s)] %s\n", file, line, func, description);
333 }
334
335 void todo0(const char *description, const char* file, int line, const char* func) {
336 #ifdef MPI_LIB
337 if (_mpi_rank <= 0) {
338 #endif
339 todo_func(description, file, line, func);
340 #ifdef MPI_LIB
341 }
342 #endif
343 }
344
345 1 void print_backtrace(void) {
346 #ifdef HAVE_EXECINFO_H
347 void* trace_ptrs[100];
348 1 int count = backtrace( trace_ptrs, 100 );
349 1 char** func_names = backtrace_symbols( trace_ptrs, count );
350 1 fprintf( stderr, "+--------------------------------------\n" );
351
352 // Print the stack trace
353 int i;
354
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for( i = 0; i < count; i++ ) {
355 5 fprintf( stderr, "| %s\n", func_names[i] );
356 }
357
358 // Free the string pointers
359 1 free( func_names );
360 1 fprintf( stderr, "+--------------------------------------\n" );
361 #else
362 fprintf( stderr, "+--------------------------------------\n" );
363 fprintf( stderr, " Backtrace not supported\n") ;
364 fprintf( stderr, "+--------------------------------------\n" );
365 #endif
366 1 }
367
368 static void clean_shmems(pid_t id, const char *shmem_key,
369 int shmem_size_multiplier, int lewi_color) {
370
371 if (shmem_cpuinfo__exists()) {
372 shmem_cpuinfo__finalize(id, shmem_key, lewi_color);
373 }
374 if (shmem_procinfo__exists()) {
375 shmem_procinfo__finalize(id, false, shmem_key, shmem_size_multiplier);
376 }
377 if (shmem_talp__exists()) {
378 shmem_talp__finalize(id);
379 }
380 shmem_async_finalize(id);
381 }
382
383 void dlb_clean(void) {
384 pthread_mutex_lock(&dlb_clean_mutex);
385 {
386 /* First, try to finalize shmems of registered subprocess */
387 const subprocess_descriptor_t** spds = spd_get_spds();
388 const subprocess_descriptor_t** spd = spds;
389 while (*spd) {
390 pid_t id = (*spd)->id;
391 const char *shmem_key = (*spd)->options.shm_key;
392 int shmem_size_multiplier = (*spd)->options.shm_size_multiplier;
393 int lewi_color = (*spd)->options.lewi_color;
394 clean_shmems(id, shmem_key, shmem_size_multiplier, lewi_color);
395 ++spd;
396 }
397 free(spds);
398
399 /* Then, try to finalize current pid */
400 pid_t pid = thread_spd ? thread_spd->id : getpid();
401 const char *shmem_key = thread_spd ? thread_spd->options.shm_key : NULL;
402 int shmem_size_multiplier = thread_spd ? thread_spd->options.shm_size_multiplier : 1;
403 int lewi_color = thread_spd ? thread_spd->options.lewi_color : 0;
404 clean_shmems(pid, shmem_key, shmem_size_multiplier, lewi_color);
405
406 /* Finalize shared memories that do not support subprocesses */
407 shmem_barrier__finalize(shmem_key, shmem_size_multiplier);
408 shmem_lewi_light__finalize();
409
410 /* Destroy shared memories if they still exist */
411 const char *shmem_names[] = {"cpuinfo", "procinfo", "talp", "async"};
412 enum { shmem_nelems = sizeof(shmem_names) / sizeof(shmem_names[0]) };
413 int i;
414 for (i=0; i<shmem_nelems; ++i) {
415 if (shmem_exists(shmem_names[i], shmem_key)) {
416 shmem_destroy(shmem_names[i], shmem_key);
417 }
418 }
419 }
420 pthread_mutex_unlock(&dlb_clean_mutex);
421 }
422
423 /* Trigger warning on some errors, tipically common or complex errors during init */
424 27 void warn_error(int error) {
425
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
27 switch(error) {
426 12 case DLB_ERR_NOMEM:
427 12 warning("DLB initialization failed due to insufficient space in shared memory."
428 " This error may be caused by corrupted DLB shared memory. If that's the case,"
429 " try running dlb_shm --delete and then attempt again. Alternatively, if you"
430 " need to register a large number of processes, you can use the"
431 " --shm-size-multiplier flag to increase the default shared memory size."
432 " See dlb -hh for more info.");
433 12 break;
434 11 case DLB_ERR_PERM:
435
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (thread_spd != NULL) {
436 1 warning("The process with CPU affinity mask %s failed to initialize DLB."
437 " Please, check that each process initializing DLB has a"
438 1 " non-overlapping set of CPUs.", mu_to_str(&thread_spd->process_mask));
439 } else {
440 10 warning("This process has failed to initialize DLB."
441 " Please, check that each process initializing DLB has a"
442 " non-overlapping set of CPUs.");
443 }
444
4/4
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
11 if (shmem_procinfo__exists() && thread_spd != NULL) {
445 1 warning("This is the list of current registered processes and their"
446 " affinity mask:");
447 1 shmem_procinfo__print_info(thread_spd->options.shm_key,
448 1 thread_spd->options.shm_size_multiplier);
449 }
450 11 break;
451 3 case DLB_ERR_NOCOMP:
452 3 warning("DLB could not initialize the shared memory due to incompatible"
453 " options among processes, likely ones sharing CPUs and others not."
454 " Please, if you believe this is a bug contact us at " PACKAGE_BUGREPORT);
455 3 break;
456 }
457 27 }
458
459
460 /* Print Buffers */
461
462 enum { INITIAL_BUFFER_SIZE = 1024 };
463
464 42 void printbuffer_init(print_buffer_t *buffer) {
465 42 buffer->addr = malloc(INITIAL_BUFFER_SIZE*sizeof(char));
466 42 buffer->size = INITIAL_BUFFER_SIZE;
467 42 buffer->len = 0;
468 42 buffer->addr[0] = '\0';
469 42 }
470
471 42 void printbuffer_destroy(print_buffer_t *buffer) {
472 42 free(buffer->addr);
473 42 buffer->addr = NULL;
474 42 buffer->size = 0;
475 42 buffer->len = 0;
476 42 }
477
478 1117 static void printbuffer_append_internal(print_buffer_t *buffer, const char *line, bool newline) {
479
480
2/2
✓ Branch 0 taken 365 times.
✓ Branch 1 taken 752 times.
1117 size_t line_len = strlen(line) + (newline ? 1 : 0) + 1; /* + '\0' */
481 1117 size_t buffer_len = buffer->len;
482
483 /* Realloc buffer if needed */
484
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1105 times.
1117 if (buffer_len + line_len > buffer->size) {
485 12 size_t new_size = buffer->size * 2;
486
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 while (buffer_len + line_len > new_size) {
487 2 new_size *= 2;
488 }
489
490 12 void *p = realloc(buffer->addr, new_size*sizeof(char));
491
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (!p) {
492 fatal("realloc failed");
493 }
494
495 12 buffer->addr = p;
496 12 buffer->size = new_size;
497 }
498
499 /* Append line to buffer */
500
2/2
✓ Branch 0 taken 365 times.
✓ Branch 1 taken 752 times.
1117 int written = snprintf(buffer->addr + buffer_len, buffer->size - buffer_len,
501 "%s%s", line, newline ? "\n" : "");
502
503
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1117 times.
1117 if (written < 0) {
504 fatal("snprintf failed");
505 }
506
507 1117 buffer->len = buffer_len + written;
508 1117 }
509
510 365 void printbuffer_append(print_buffer_t *buffer, const char *line) {
511 365 printbuffer_append_internal(buffer, line, true);
512 365 }
513
514 752 void printbuffer_append_no_newline(print_buffer_t *buffer, const char *text) {
515 752 printbuffer_append_internal(buffer, text, false);
516 752 }
517