GCC Code Coverage Report


Directory: src/
File: src/talp/talp.c
Date: 2026-09-15 07:37:49
Exec Total Coverage
Lines: 189 206 91.7%
Functions: 8 8 100.0%
Branches: 77 100 77.0%

Line Branch Exec Source
1 /*********************************************************************************/
2 /* Copyright 2009-2026 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 "talp/talp.h"
25
26 #include "LB_core/node_barrier.h"
27 #include "LB_core/spd.h"
28 #include "LB_core/thread_ctx.h"
29 #include "LB_comm/shmem_talp.h"
30 #include "apis/dlb_errors.h"
31 #include "apis/dlb_talp.h"
32 #include "support/atomic.h"
33 #include "support/debug.h"
34 #include "support/error.h"
35 #include "support/gslist.h"
36 #include "support/gtree.h"
37 #include "support/mytime.h"
38 #include "support/tracing.h"
39 #include "support/options.h"
40 #include "support/mask_utils.h"
41 #include "support/gpu_mask_utils.h"
42 #include "talp/backend.h"
43 #include "talp/perf_metrics.h"
44 #include "talp/sample.h"
45 #include "talp/regions.h"
46 #include "talp/talp_gpu.h"
47 #include "talp/talp_hwc.h"
48 #include "talp/talp_output.h"
49 #include "talp/talp_record.h"
50 #include "talp/talp_types.h"
51 #ifdef MPI_LIB
52 #include "mpi/mpi_core.h"
53 #endif
54
55 #include <stdlib.h>
56 #include <pthread.h>
57
58
59 #ifdef MPI_LIB
60 /* Returns the number of MPI processes that have HWC enabled */
61 static int get_hwc_init_across_world(const subprocess_descriptor_t *spd) {
62
63 talp_info_t *talp_info = spd->talp_info;
64
65 // status = 1 means HWC are enabled
66 int hwc_local_status = talp_info->flags.have_hwc ? 1 : 0;
67
68 int hwc_global_statuses = 0;
69
70 PMPI_Allreduce(&hwc_local_status, &hwc_global_statuses, 1,
71 MPI_INT, MPI_SUM, getWorldComm());
72
73 return hwc_global_statuses;
74 }
75 #endif
76
77
78 /*********************************************************************************/
79 /* Init / Finalize */
80 /*********************************************************************************/
81
82 38 void talp_init(subprocess_descriptor_t *spd) {
83
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 ensure(!spd->talp_info, "TALP already initialized");
85
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 ensure(!thread_is_observer(), "An observer thread cannot call talp_init");
86
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 27 times.
38 verbose(VB_TALP, "Initializing TALP module with worker mask: %s",
87 mu_to_str(&spd->process_mask));
88
89 38 int num_cpus = CPU_COUNT(&spd->process_mask);
90
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 37 times.
38 if (num_cpus == 0) {
91 /* Possibly due to testing or DLB_Init, query process mask: */
92 cpu_set_t process_mask;
93 1 sched_getaffinity(0, sizeof(process_mask), &process_mask);
94 1 num_cpus = CPU_COUNT(&process_mask);
95 }
96
97 /* Initialize talp info */
98 38 talp_info_t *talp_info = malloc(sizeof(talp_info_t));
99 38 *talp_info = (const talp_info_t) {
100 .flags = {
101 38 .external_profiler = spd->options.talp_external_profiler,
102 38 .have_shmem = spd->options.talp_external_profiler,
103 38 .have_minimal_shmem = !spd->options.talp_external_profiler
104
3/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
38 && spd->options.talp_summary & SUMMARY_NODE,
105 },
106 .num_cpus = num_cpus,
107 38 .regions = g_tree_new_full(
108 (GCompareDataFunc)region_compare_by_name,
109 NULL, NULL, region_dealloc),
110 .regions_mutex = PTHREAD_MUTEX_INITIALIZER,
111 };
112 38 spd->talp_info = talp_info;
113
114 /* Initialize shared memory */
115
3/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
38 if (talp_info->flags.have_shmem || talp_info->flags.have_minimal_shmem) {
116 /* If we only need a minimal shmem, its size will be the user-provided
117 * multiplier times 'system_size' (usually, 1 region per process)
118 * Otherwise, we multiply it by DEFAULT_REGIONS_PER_PROC.
119 */
120 enum { DEFAULT_REGIONS_PER_PROC = 100 };
121 46 int shmem_size_multiplier = spd->options.shm_size_multiplier
122
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
23 * (talp_info->flags.have_shmem ? DEFAULT_REGIONS_PER_PROC : 1);
123 23 shmem_talp__init(spd->options.shm_key, shmem_size_multiplier);
124 }
125
126 /* Initialize TALP components */
127
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 18 times.
38 if (spd->options.talp & (TALP_COMPONENT_DEFAULT | TALP_COMPONENT_GPU)) {
128
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
20 if (talp_gpu_init(spd) == DLB_SUCCESS) {
129 talp_info->flags.have_gpu = true;
130 verbose(VB_TALP, "GPU component enabled successfully");
131 } else {
132
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (spd->options.talp & TALP_COMPONENT_GPU) {
133 /* component was explicit and failed, warn user */
134 warning("TALP: Failed to load GPU component");
135 }
136 }
137 }
138
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 10 times.
38 if (spd->options.talp & (TALP_COMPONENT_DEFAULT | TALP_COMPONENT_HWC)) {
139
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
28 if (talp_hwc_init(spd) == DLB_SUCCESS) {
140 talp_info->flags.have_hwc = true;
141 verbose(VB_TALP, "HWC component enabled successfully");
142 } else {
143
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 20 times.
28 if (spd->options.talp & TALP_COMPONENT_HWC) {
144 /* component was explicit and failed, warn user */
145 8 warning("TALP: Failed to load HWC component");
146 }
147 }
148 }
149
150 #ifdef MPI_LIB
151 /* Check HWC status across all process. Every process needs to do the check
152 * because it's a collective operation and some process may have been started
153 * without the appropriate flag. */
154 if (is_mpi_ready()) {
155 int num_procs_with_hwc = get_hwc_init_across_world(spd);
156 if (num_procs_with_hwc > 0 && num_procs_with_hwc < _mpi_size) {
157 warning0("Hardware Counters initialization has failed, disabling option.");
158 talp_hwc_finalize();
159 talp_info->flags.have_hwc = false;
160 }
161 }
162 #endif
163
164 /* Initialize sample structure */
165 38 talp_sample_init(talp_info);
166
167 /* Create the main thread's sample */
168 38 (void)talp_sample_get(talp_info);
169 38 talp_sample_record_cpuid(talp_info);
170 38 talp_sample_set_state(talp_info, TALP_STATE_USEFUL);
171
172 /* Initialize global region monitor
173 * (at this point we don't know how many CPUs, it will be fixed in talp_openmp_init) */
174 38 talp_info->monitor = region_register(spd, region_get_global_name());
175
176 /* Start global region */
177 38 region_start(spd, talp_info->monitor);
178 38 }
179
180 38 void talp_finalize(subprocess_descriptor_t *spd) {
181
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 ensure(spd->talp_info, "TALP is not initialized");
182
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 ensure(thread_is_main(), "Only main thread can call talp_finalize");
183
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 30 times.
38 verbose(VB_TALP, "Finalizing TALP module");
184
185 38 talp_info_t *talp_info = spd->talp_info;
186
187 /* Stop open regions
188 * (Note that region_stop need to acquire the regions_mutex
189 * lock, so we we need to iterate without it) */
190
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 38 times.
65 while(talp_info->open_regions != NULL) {
191 27 dlb_monitor_t *monitor = talp_info->open_regions->data;
192 27 region_stop(spd, monitor);
193 }
194
195 /* Finalize TALP components */
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (talp_info->flags.have_gpu) {
197 talp_gpu_finalize();
198 }
199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (talp_info->flags.have_hwc) {
200 talp_hwc_finalize();
201 }
202
203 /* Per-process output (no MPI or requested by user) */
204
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 26 times.
38 if (!talp_info->flags.have_mpi
205
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 || spd->options.talp_partial_output) {
206
207 26 pthread_mutex_lock(&talp_info->regions_mutex);
208 {
209 /* Record all regions */
210 26 for (GTreeNode *node = g_tree_node_first(talp_info->regions);
211
2/2
✓ Branch 0 taken 1233 times.
✓ Branch 1 taken 26 times.
1259 node != NULL;
212 1233 node = g_tree_node_next(node)) {
213 1233 const dlb_monitor_t *monitor = g_tree_node_value(node);
214 1233 talp_record_monitor(spd, monitor);
215 }
216 }
217 26 pthread_mutex_unlock(&talp_info->regions_mutex);
218 }
219
220 /* Print/write all collected summaries */
221 38 talp_output_finalize(spd->options.talp_output_file, spd->options.talp_partial_output);
222
223 /* Deallocate samples structure */
224 38 talp_sample_finalize(talp_info);
225
226 /* Finalize shared memory */
227
3/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
38 if (talp_info->flags.have_shmem || talp_info->flags.have_minimal_shmem) {
228 23 shmem_talp__finalize(spd->id);
229 }
230
231 /* Deallocate monitoring regions and talp_info */
232 38 pthread_mutex_lock(&talp_info->regions_mutex);
233 {
234 /* Destroy GTree, each node is deallocated with the function region_dealloc */
235 38 g_tree_destroy(talp_info->regions);
236 38 talp_info->regions = NULL;
237 38 talp_info->monitor = NULL;
238
239 /* Destroy list of open regions */
240 38 g_slist_free(talp_info->open_regions);
241 38 talp_info->open_regions = NULL;
242 }
243 38 pthread_mutex_unlock(&talp_info->regions_mutex);
244 38 free(talp_info);
245 38 spd->talp_info = NULL;
246 38 }
247
248
249 /*********************************************************************************/
250 /* Functions for sample aggregation to regions */
251 /*********************************************************************************/
252
253 1 void talp_aggregate_sample_to_region(talp_info_t *talp_info,
254 dlb_monitor_t *monitor, const talp_sample_t *sample, int64_t elapsed) {
255
256 1 pthread_mutex_lock(&talp_info->regions_mutex);
257 {
258 1 monitor_data_t *monitor_data = monitor->_data;
259
260 /* CPU mask */
261
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
17 CPU_OR(&monitor_data->cpu_mask, &monitor_data->cpu_mask, &sample->cpu_mask);
262
263 /* Number of CPUs */
264 1 monitor->num_cpus = min_int(CPU_COUNT(&monitor_data->cpu_mask), talp_info->num_cpus);
265
266 /* Number of threads: we oversimplify here, and sometimes we may give
267 * a wrong number. The solution would be to keep track of thread ids
268 * that have participated. Not worth the effort. */
269 1 monitor->num_omp_threads = monitor->num_cpus;
270
271 /* Timers */
272 /* Note: not_useful_omp_{during_mpi,lb,sched} are purposely not reduced
273 * and the entire omp_in is attributed to omp_scheduling. */
274 1 monitor->useful_time += sample->timers.useful;
275 1 monitor->mpi_time += sample->timers.not_useful_mpi;
276 1 monitor->omp_scheduling_time += sample->timers.not_useful_omp_in;
277 1 monitor->gpu_runtime_time += sample->timers.not_useful_gpu;
278
279 /* Counters */
280 1 monitor->cycles += sample->counters.cycles;
281 1 monitor->instructions += sample->counters.instructions;
282
283 /* Stats */
284 1 monitor->num_mpi_calls += sample->stats.num_mpi_calls;
285 1 monitor->num_omp_parallels += sample->stats.num_omp_parallels;
286 1 monitor->num_omp_tasks += sample->stats.num_omp_tasks;
287 1 monitor->num_gpu_runtime_calls += sample->stats.num_gpu_runtime_calls;
288
289 1 monitor->elapsed_time += elapsed;
290 1 ++(monitor->num_measurements);
291 }
292 1 pthread_mutex_unlock(&talp_info->regions_mutex);
293 1 }
294
295 /* Update all open regions with the macrosample */
296 5411 static void update_regions_with_macrosample(talp_info_t *restrict talp_info,
297 const talp_macrosample_t *restrict macrosample) {
298
299 5411 bool have_openmp = talp_info->flags.have_openmp;
300 5411 bool external_profiler = talp_info->flags.external_profiler;
301
302 /* Update all open regions */
303 5411 pthread_mutex_lock(&talp_info->regions_mutex);
304 {
305 5411 for (GSList *node = talp_info->open_regions;
306
2/2
✓ Branch 0 taken 5982 times.
✓ Branch 1 taken 5411 times.
11393 node != NULL;
307 5982 node = node->next) {
308 5982 dlb_monitor_t *monitor = node->data;
309 5982 monitor_data_t *monitor_data = monitor->_data;
310
311 /* CPU mask */
312
2/2
✓ Branch 0 taken 95712 times.
✓ Branch 1 taken 5982 times.
101694 CPU_OR(&monitor_data->cpu_mask, &monitor_data->cpu_mask, &macrosample->cpu_mask);
313
314 /* Number of CPUs:
315 * - the CPU mask in macrosample may have more CPUs than the actual
316 * number of threads, e.g.: when OMP_PLACES=cores or when the initial
317 * thread is bound only after the first parallel region.
318 * - */
319 5982 int macrosample_num_cpus = min_int(
320 5982 CPU_COUNT(&macrosample->cpu_mask), macrosample->num_samples);
321
2/2
✓ Branch 0 taken 2274 times.
✓ Branch 1 taken 3708 times.
5982 if (monitor->num_cpus < macrosample_num_cpus) {
322 2274 monitor->num_cpus = macrosample_num_cpus;
323 }
324
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5982 times.
5982 ensure(monitor->num_cpus > 0, "Updating region with 0 CPUs. Please report.");
325
326 /* Number of OpenMP threads */
327
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5976 times.
5982 if (have_openmp) {
328
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (monitor->num_omp_threads < macrosample->num_samples) {
329 3 monitor->num_omp_threads = macrosample->num_samples;
330 }
331 }
332
333 /* GPU mask */
334 5982 monitor_data->gpu_mask |= macrosample->gpu_mask;
335
336 /* Number of GPUs */
337 5982 monitor->num_gpus = gm_count(monitor_data->gpu_mask);
338
339 /* Timers */
340 5982 monitor->useful_time += macrosample->timers.useful;
341 5982 monitor->mpi_time += macrosample->timers.not_useful_mpi;
342 5982 monitor->mpi_worker_idle_time += macrosample->timers.not_useful_omp_during_mpi;
343 5982 monitor->omp_load_imbalance_time += macrosample->timers.not_useful_omp_in_lb;
344 5982 monitor->omp_scheduling_time += macrosample->timers.not_useful_omp_in_sched;
345 5982 monitor->omp_outside_parallel_time += macrosample->timers.not_useful_omp_out;
346 5982 monitor->gpu_runtime_time += macrosample->timers.not_useful_gpu;
347
348 /* Counters */
349 5982 monitor->cycles += macrosample->counters.cycles;
350 5982 monitor->instructions += macrosample->counters.instructions;
351
352 /* Stats */
353 5982 monitor->num_mpi_calls += macrosample->stats.num_mpi_calls;
354 5982 monitor->num_omp_parallels += macrosample->stats.num_omp_parallels;
355 5982 monitor->num_omp_tasks += macrosample->stats.num_omp_tasks;
356 5982 monitor->num_gpu_runtime_calls += macrosample->stats.num_gpu_runtime_calls;
357
358 /* GPU Timers */
359 5982 uint64_t mask = macrosample->gpu_mask;
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5982 times.
5982 while (mask) {
361 int gpu = gm_ctz(mask);
362
363 /* Aggregated into monitor */
364 monitor->gpu_useful_time += macrosample->gpu_timers[gpu].useful;
365 monitor->gpu_communication_time += macrosample->gpu_timers[gpu].communication;
366
367 /* Also store the data decomposed by device in the private monitor
368 * data for use during the later MPI node reduction. */
369 monitor_data->gpu_timers[gpu].useful += macrosample->gpu_timers[gpu].useful;
370 monitor_data->gpu_timers[gpu].communication +=
371 macrosample->gpu_timers[gpu].communication;
372
373 mask = gm_clear_lsb(mask);
374 }
375
376 /* Update shared memory only if requested */
377
2/2
✓ Branch 0 taken 3679 times.
✓ Branch 1 taken 2303 times.
5982 if (external_profiler) {
378 3679 shmem_talp__set_times(monitor_data->node_shared_id,
379 monitor->mpi_time,
380 monitor->useful_time);
381 }
382 }
383 }
384 5411 pthread_mutex_unlock(&talp_info->regions_mutex);
385 5411 }
386
387 /* Accumulate values from samples of all threads and update regions */
388 5412 int talp_aggregate_samples_to_regions(talp_info_t *talp_info) {
389
390 /* Observer and unknown threads can't aggregate samples */
391
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5411 times.
5412 if (unlikely(!thread_is_profiled())) return DLB_ERR_PERM;
392
393 /* Accumulate samples from all threads */
394 5411 talp_macrosample_t macrosample = {0};
395 5411 talp_sample_aggregate_all_to_macrosample(talp_info, &macrosample);
396
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5411 times.
5411 if (talp_info->flags.have_gpu) {
398 /* Collect GPU measuremnts up to this point and update macrosample */
399 talp_gpu_collect(macrosample.gpu_timers, MAX_LOCAL_GPUS, &macrosample.gpu_mask);
400 }
401
402 /* Update all started regions */
403 5411 update_regions_with_macrosample(talp_info, &macrosample);
404
405 5411 return DLB_SUCCESS;
406 }
407
408
409 /*********************************************************************************/
410 /* TALP collect functions for 3rd party programs: */
411 /* - It's also safe to call it from a 1st party program */
412 /* - Requires --talp-external-profiler set up in application */
413 /* - Does not need to synchronize with application */
414 /*********************************************************************************/
415
416 /* Function that may be called from a third-party process to compute
417 * node_metrics for a given region */
418 19 int talp_query_pop_node_metrics(const char *name, dlb_node_metrics_t *node_metrics) {
419
420
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18 times.
19 if (name == NULL) {
421 1 name = region_get_global_name();
422 }
423
424 19 int error = DLB_SUCCESS;
425 19 int64_t total_mpi_time = 0;
426 19 int64_t total_useful_time = 0;
427 19 int64_t max_mpi_time = 0;
428 19 int64_t max_useful_time = 0;
429
430 /* Obtain a list of regions in the node associated with given region */
431 19 int max_procs = mu_get_system_size();
432 19 talp_region_list_t *region_list = malloc(max_procs * sizeof(talp_region_list_t));
433 int nelems;
434 19 shmem_talp__get_regionlist(region_list, &nelems, max_procs, name);
435
436 /* Count how many processes have started the region */
437 19 int processes_per_node = 0;
438
439 /* Iterate the PID list and gather times of every process */
440
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 19 times.
51 for (int i = 0; i <nelems; ++i) {
441 32 int64_t mpi_time = region_list[i].mpi_time;
442 32 int64_t useful_time = region_list[i].useful_time;
443
444 /* Accumulate total and max values */
445
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 1 times.
32 if (mpi_time > 0 || useful_time > 0) {
446 31 ++processes_per_node;
447 31 total_mpi_time += mpi_time;
448 31 total_useful_time += useful_time;
449 31 max_mpi_time = max_int64(mpi_time, max_mpi_time);
450 31 max_useful_time = max_int64(useful_time, max_useful_time);
451 }
452 }
453 19 free(region_list);
454
455 #if MPI_LIB
456 int node_id = _node_id;
457 #else
458 19 int node_id = 0;
459 #endif
460
461
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1 times.
19 if (processes_per_node > 0) {
462 /* Compute POP metrics with some inferred values */
463 perf_metrics_mpi_t metrics;
464 18 perf_metrics__infer_mpi_model(
465 &metrics,
466 processes_per_node,
467 total_useful_time,
468 total_mpi_time,
469 max_useful_time);
470
471 /* Initialize structure */
472 18 *node_metrics = (const dlb_node_metrics_t) {
473 .node_id = node_id,
474 .processes_per_node = processes_per_node,
475 .total_useful_time = total_useful_time,
476 .total_mpi_time = total_mpi_time,
477 .max_useful_time = max_useful_time,
478 .max_mpi_time = max_mpi_time,
479 18 .parallel_efficiency = metrics.parallel_efficiency,
480 18 .communication_efficiency = metrics.communication_efficiency,
481 18 .load_balance = metrics.load_balance,
482 };
483 18 snprintf(node_metrics->name, DLB_MONITOR_NAME_MAX, "%s", name);
484 } else {
485 1 error = DLB_ERR_NOENT;
486 }
487
488 19 return error;
489 }
490
491
492 /*********************************************************************************/
493 /* TALP collect functions for 1st party programs */
494 /* - Requires synchronization (MPI or node barrier) among all processes */
495 /*********************************************************************************/
496
497 /* Compute the current POP metrics for the specified monitor. If monitor is NULL,
498 * the global monitoring region is assumed.
499 * Pre-conditions:
500 * - if MPI, the given monitor must have been registered in all MPI ranks
501 * - pop_metrics is an allocated structure
502 */
503 1 int talp_collect_pop_metrics(const subprocess_descriptor_t *spd,
504 dlb_monitor_t *monitor, dlb_pop_metrics_t *pop_metrics) {
505 1 talp_info_t *talp_info = spd->talp_info;
506
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (monitor == NULL) {
507 monitor = talp_info->monitor;
508 }
509
510 /* Stop monitor so that metrics are updated */
511 1 bool resume_region = region_stop(spd, monitor) == DLB_SUCCESS;
512
513 pop_base_metrics_t base_metrics;
514 #ifdef MPI_LIB
515 /* Reduce monitor among all MPI ranks and everbody collects (all-to-all) */
516 perf_metrics__reduce_monitor_into_base_metrics(&base_metrics, monitor, true);
517 #else
518 /* Construct base metrics using only the monitor from this process */
519 1 perf_metrics__local_monitor_into_base_metrics(&base_metrics, monitor, talp_info->flags);
520 #endif
521
522 /* Construct output pop_metrics out of base metrics */
523 1 perf_metrics__base_to_pop_metrics(monitor->name, &base_metrics, pop_metrics);
524
525 /* Resume monitor */
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (resume_region) {
527 region_start(spd, monitor);
528 }
529
530 1 return DLB_SUCCESS;
531 }
532
533 /* Node-collective function to compute node_metrics for a given region */
534 18 int talp_collect_pop_node_metrics(const subprocess_descriptor_t *spd,
535 dlb_monitor_t *monitor, dlb_node_metrics_t *node_metrics) {
536
537 18 talp_info_t *talp_info = spd->talp_info;
538
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
18 monitor = monitor ? monitor : talp_info->monitor;
539 18 monitor_data_t *monitor_data = monitor->_data;
540
541 /* Stop monitor so that metrics are updated */
542 18 bool resume_region = region_stop(spd, monitor) == DLB_SUCCESS;
543
544 /* This functionality needs a shared memory, create a temporary one if needed */
545
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
18 if (!talp_info->flags.have_shmem) {
546 6 shmem_talp__init(spd->options.shm_key, 1);
547 6 shmem_talp__register(spd->id, monitor->avg_cpus, monitor->name,
548 &monitor_data->node_shared_id);
549 }
550
551 /* Update the shared memory with this process' metrics */
552 18 shmem_talp__set_times(monitor_data->node_shared_id,
553 monitor->mpi_time,
554 monitor->useful_time);
555
556 /* Perform a node barrier to ensure everyone has updated their metrics */
557 18 node_barrier(spd, NULL);
558
559 /* Compute node metrics for that region name */
560 18 talp_query_pop_node_metrics(monitor->name, node_metrics);
561
562 /* Remove shared memory if it was a temporary one */
563
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
18 if (!talp_info->flags.have_shmem) {
564 6 shmem_talp__finalize(spd->id);
565 }
566
567 /* Resume monitor */
568
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 if (resume_region) {
569 1 region_start(spd, monitor);
570 }
571
572 18 return DLB_SUCCESS;
573 }
574