GCC Code Coverage Report


Directory: src/
File: src/support/debug.c
Date: 2026-04-21 15:16:03
Exec Total Coverage
Lines: 145 218 66.5%
Functions: 18 25 72.0%
Branches: 60 112 53.6%

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