GCC Code Coverage Report


Directory: src/
File: src/support/debug.c
Date: 2026-02-23 15:13:19
Exec Total Coverage
Lines: 147 204 72.1%
Functions: 18 24 75.0%
Branches: 60 104 57.7%

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