GCC Code Coverage Report


Directory: src/
File: src/support/mask_utils.c
Date: 2026-06-05 08:54:23
Exec Total Coverage
Lines: 691 712 97.1%
Functions: 66 66 100.0%
Branches: 412 492 83.7%

Line Branch Exec Source
1 /*********************************************************************************/
2 /* Copyright 2009-2024 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/mask_utils.h"
25
26 #include "support/debug.h"
27 #include "support/dlb_common.h"
28
29 #ifdef HWLOC_LIB
30 #include <hwloc.h>
31 #include <hwloc/bitmap.h>
32 #include <hwloc/glibc-sched.h>
33 #endif
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <dirent.h>
37
38 #include <sched.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <limits.h>
43 #include <ctype.h>
44 #include <sys/types.h>
45 #include <regex.h>
46
47 #ifdef IS_BGQ_MACHINE
48 static void parse_mask_from_file(const char *filename, cpu_set_t *mask)
49 __attribute__((unused));
50 static int parse_hwloc(void) __attribute__((unused));
51 static void parse_system_files(void) __attribute__((unused));
52 #endif
53
54
55 /*********************************************************************************/
56 /* mu_cpuset_t: custom cpuset type for mask utils */
57 /*********************************************************************************/
58
59 /* Initial values to accomodate up to CPU_SETSIZE CPUs.
60 * Later, they are reduced according to the machine specification */
61 static unsigned int mu_cpuset_setsize = CPU_SETSIZE;
62 static size_t mu_cpuset_alloc_size = CPU_ALLOC_SIZE(CPU_SETSIZE);
63 static size_t mu_cpuset_num_ulongs = CPU_ALLOC_SIZE(CPU_SETSIZE) / sizeof(unsigned long);
64
65
66 1173 static inline void mu_cpuset_from_glibc_sched_affinity(mu_cpuset_t *mu_cpuset,
67 const cpu_set_t *cpu_set) {
68 4692 *mu_cpuset = (const mu_cpuset_t) {
69 1173 .set = CPU_ALLOC(mu_cpuset_setsize),
70 .alloc_size = mu_cpuset_alloc_size,
71 1173 .count = CPU_COUNT_S(mu_cpuset_alloc_size, cpu_set),
72 1173 .first_cpuid = mu_get_first_cpu(cpu_set),
73 1173 .last_cpuid = mu_get_last_cpu(cpu_set),
74 };
75 1173 memcpy(mu_cpuset->set, cpu_set, mu_cpuset_alloc_size);
76 1173 }
77
78 #ifdef HWLOC_LIB
79 288 static inline void mu_cpuset_from_hwloc_bitmap(mu_cpuset_t *mu_cpuset,
80 hwloc_const_bitmap_t bitmap, hwloc_topology_t topology) {
81 288 *mu_cpuset = (const mu_cpuset_t) {
82 288 .set = CPU_ALLOC(mu_cpuset_setsize),
83 .alloc_size = mu_cpuset_alloc_size,
84 288 .count = hwloc_bitmap_weight(bitmap),
85 288 .first_cpuid = hwloc_bitmap_first(bitmap),
86 288 .last_cpuid = hwloc_bitmap_last(bitmap),
87 };
88 288 hwloc_cpuset_to_glibc_sched_affinity(topology, bitmap, mu_cpuset->set,
89 mu_cpuset_alloc_size);
90 288 }
91 #endif
92
93
94 /*********************************************************************************/
95 /* Mask utils system info */
96 /*********************************************************************************/
97
98 /* mask_utils main structure with system info */
99 typedef struct {
100 unsigned int num_nodes;
101 unsigned int num_cores;
102 unsigned int num_cpus;
103 mu_cpuset_t sys_mask;
104 mu_cpuset_t* node_masks;
105 mu_cpuset_t* core_masks_by_coreid;
106 mu_cpuset_t** core_masks_by_cpuid;
107 } mu_system_loc_t;
108
109 enum { BITS_PER_BYTE = 8 };
110 enum { CPUS_PER_ULONG = sizeof(unsigned long) * BITS_PER_BYTE };
111
112 static mu_system_loc_t sys = {0};
113 static bool mu_initialized = false;
114
115 83 static void init_mu_struct(void) {
116 83 sys = (const mu_system_loc_t) {};
117 83 }
118
119 /* This function (re-)initializes 'sys' with the given cpu sets.
120 * It is used for specific set-ups, fallback, or testing purposes */
121 67 static void init_system_masks(const cpu_set_t *sys_mask,
122 const cpu_set_t *core_masks, unsigned int num_cores,
123 const cpu_set_t *node_masks, unsigned int num_nodes) {
124
125 /* De-allocate structures if already initialized */
126
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 10 times.
67 if (mu_initialized) {
127 57 mu_finalize();
128 } else {
129 10 init_mu_struct();
130 }
131
132 /*** System ***/
133 67 sys.num_cpus = mu_get_last_cpu(sys_mask) + 1;
134 67 mu_cpuset_setsize = sys.num_cpus;
135 67 mu_cpuset_num_ulongs = (mu_cpuset_setsize + CPUS_PER_ULONG - 1) / CPUS_PER_ULONG;
136 67 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
137 67 sys.sys_mask = (const mu_cpuset_t) {
138 67 .set = CPU_ALLOC(mu_cpuset_setsize),
139 67 .count = CPU_COUNT_S(mu_cpuset_alloc_size, sys_mask),
140 .first_cpuid = 0,
141 67 .last_cpuid = mu_cpuset_setsize - 1,
142 };
143 67 memcpy(sys.sys_mask.set, sys_mask, mu_cpuset_alloc_size);
144
145 /*** Cores ***/
146 67 sys.num_cores = num_cores;
147 67 sys.core_masks_by_coreid = malloc(sys.num_cores * sizeof(mu_cpuset_t));
148 67 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
149
2/2
✓ Branch 0 taken 1096 times.
✓ Branch 1 taken 67 times.
1163 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
150 1096 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
151 1096 mu_cpuset_from_glibc_sched_affinity(core_cpuset, &core_masks[core_id]);
152 1096 for (int cpuid = core_cpuset->first_cpuid;
153
4/4
✓ Branch 0 taken 1184 times.
✓ Branch 1 taken 1095 times.
✓ Branch 2 taken 1183 times.
✓ Branch 3 taken 1 times.
2279 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
154 1183 cpuid = mu_get_next_cpu(core_cpuset->set, cpuid)) {
155 /* Save reference to another array indexed by cpuid */
156 1183 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
157 }
158 }
159
160 /*** NUMA Nodes ***/
161 67 sys.num_nodes = num_nodes;
162 67 sys.node_masks = malloc(sys.num_nodes * sizeof(mu_cpuset_t));
163
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 67 times.
139 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
164 72 mu_cpuset_from_glibc_sched_affinity(&sys.node_masks[node_id], &node_masks[node_id]);
165 }
166
167 67 mu_initialized = true;
168 67 }
169
170
171 /* This function (re-)initializes 'sys' given an overall number of resources.
172 * It is used for specific set-ups, fallback, or testing purposes */
173 55 static void init_system(unsigned int num_cpus, unsigned int num_cores,
174 unsigned int num_nodes) {
175
176 /*** System ***/
177 cpu_set_t sys_mask;
178 55 CPU_ZERO(&sys_mask);
179
2/2
✓ Branch 0 taken 1088 times.
✓ Branch 1 taken 55 times.
1143 for (unsigned int cpuid = 0; cpuid < num_cpus; ++cpuid) {
180
1/2
✓ Branch 0 taken 1088 times.
✗ Branch 1 not taken.
1088 CPU_SET(cpuid, &sys_mask);
181 }
182
183 /*** Cores ***/
184 55 unsigned int cpus_per_core = num_cpus / num_cores;
185 55 cpu_set_t *core_masks = calloc(num_cores, sizeof(cpu_set_t));
186
2/2
✓ Branch 0 taken 1048 times.
✓ Branch 1 taken 55 times.
1103 for (unsigned int core_id = 0; core_id < num_cores; ++core_id) {
187 1048 for (unsigned int cpuid = core_id * cpus_per_core;
188
2/2
✓ Branch 0 taken 1088 times.
✓ Branch 1 taken 1048 times.
2136 cpuid < (core_id+1) * cpus_per_core; ++cpuid) {
189
1/2
✓ Branch 0 taken 1088 times.
✗ Branch 1 not taken.
1088 CPU_SET(cpuid, &core_masks[core_id]);
190 }
191 }
192
193 /*** NUMA Nodes ***/
194 55 unsigned int cpus_per_node = num_cpus / num_nodes;
195 55 cpu_set_t *node_masks = calloc(num_nodes, sizeof(cpu_set_t));
196
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 55 times.
113 for (unsigned int node_id = 0; node_id < num_nodes; ++node_id) {
197 58 for (unsigned int cpuid = node_id * cpus_per_node;
198
2/2
✓ Branch 0 taken 1088 times.
✓ Branch 1 taken 58 times.
1146 cpuid < (node_id+1) * cpus_per_node; ++cpuid) {
199
1/2
✓ Branch 0 taken 1088 times.
✗ Branch 1 not taken.
1088 CPU_SET(cpuid, &node_masks[node_id]);
200 }
201 }
202
203 55 init_system_masks(&sys_mask, core_masks, num_cores, node_masks, num_nodes);
204 55 free(core_masks);
205 55 free(node_masks);
206 55 }
207
208 72 static int parse_hwloc(void) {
209 #ifdef HWLOC_LIB
210 /* Check runtime library compatibility */
211 72 unsigned int hwloc_version = hwloc_get_api_version();
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
72 if (hwloc_version >> 16 != HWLOC_API_VERSION >> 16) {
213 warning("Detected incompatible HWLOC runtime library");
214 return -1;
215 }
216
217 hwloc_topology_t topology;
218 72 hwloc_topology_init(&topology);
219 72 hwloc_topology_load(topology);
220
221 /*** System ***/
222 72 hwloc_obj_t machine = hwloc_get_obj_by_type(topology, HWLOC_OBJ_MACHINE, 0);
223 72 sys.num_cpus = hwloc_bitmap_last(machine->cpuset) + 1;
224 72 mu_cpuset_setsize = sys.num_cpus;
225 #if HWLOC_API_VERSION >= 0x00020100
226 72 mu_cpuset_num_ulongs = hwloc_bitmap_nr_ulongs(machine->cpuset);
227 #else
228 mu_cpuset_num_ulongs = (mu_cpuset_setsize + CPUS_PER_ULONG - 1) / CPUS_PER_ULONG;
229 #endif
230 72 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
231 72 mu_cpuset_from_hwloc_bitmap(&sys.sys_mask, machine->cpuset, topology);
232
233 /*** Cores ***/
234 72 hwloc_obj_type_t core = HWLOC_OBJ_CORE;
235 72 sys.num_cores = hwloc_get_nbobjs_by_type(topology, core);
236 72 sys.core_masks_by_coreid = calloc(sys.num_cores, sizeof(mu_cpuset_t));
237 72 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
238
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 72 times.
216 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
239 144 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, core, core_id);
240 144 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
241 144 mu_cpuset_from_hwloc_bitmap(core_cpuset, obj->cpuset, topology);
242 144 for (int cpuid = core_cpuset->first_cpuid;
243
3/4
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
288 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
244 144 cpuid = hwloc_bitmap_next(obj->cpuset, cpuid)) {
245 /* Save reference to another array indexed by cpuid */
246 144 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
247 }
248 }
249
250 /*** NUMA Nodes ***/
251 72 hwloc_obj_type_t node = HWLOC_OBJ_NODE;
252 72 sys.num_nodes = hwloc_get_nbobjs_by_type(topology, node);
253 72 sys.node_masks = calloc(sys.num_nodes, sizeof(mu_cpuset_t));
254
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 72 times.
144 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
255 72 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, node, node_id);
256 72 mu_cpuset_from_hwloc_bitmap(&sys.node_masks[node_id],
257 72 obj->cpuset, topology);
258 }
259
260 72 hwloc_topology_destroy(topology);
261
262 72 return 0;
263 #else
264 return -1;
265 #endif
266 }
267
268 4 static void parse_mask_from_file(const char *filename, cpu_set_t *mask) {
269
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (access(filename, F_OK) == 0) {
270 enum { BUF_LEN = CPU_SETSIZE*7 };
271 char buf[BUF_LEN];
272 4 FILE *fd = fopen(filename, "r");
273
274
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!fgets(buf, BUF_LEN, fd)) {
275 fatal("cannot read %s\n", filename);
276 }
277 4 fclose(fd);
278
279 4 size_t len = strlen(buf);
280
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (buf[len - 1] == '\n')
281 4 buf[len - 1] = '\0';
282
283 4 mu_parse_mask(buf, mask);
284 }
285 4 }
286
287 2 static int parse_int_from_file(const char *filename) {
288 2 int value = -1;
289
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (access(filename, F_OK) == 0) {
290 enum { BUF_LEN = 16 };
291 char buf[BUF_LEN];
292 2 FILE *fd = fopen(filename, "r");
293
294
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!fgets(buf, BUF_LEN, fd)) {
295 fatal("cannot read %s\n", filename);
296 }
297 2 fclose(fd);
298
299 2 value = strtol(buf, NULL, 10);
300 }
301 2 return value;
302 }
303
304 1 static int cmp_mu_cpuset(const void *a, const void *b) {
305 1 const mu_cpuset_t *set_a = a;
306 1 const mu_cpuset_t *set_b = b;
307
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (set_a->count == 0) return 1;
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (set_b->count == 0) return -1;
310 1 return set_a->first_cpuid - set_b->first_cpuid;
311 }
312
313 #define PATH_SYSTEM_MASK "/sys/devices/system/cpu/present"
314 #define PATH_SYSTEM_CPUS "/sys/devices/system/cpu"
315 #define PATH_SYSTEM_NODE "/sys/devices/system/node"
316 1 static void parse_system_files(void) {
317 /*** System ***/
318 cpu_set_t system_mask;
319 1 parse_mask_from_file(PATH_SYSTEM_MASK, &system_mask);
320 1 sys.num_cpus = mu_get_last_cpu(&system_mask) + 1;
321 1 mu_cpuset_setsize = sys.num_cpus;
322 1 mu_cpuset_num_ulongs = (mu_cpuset_setsize + CPUS_PER_ULONG - 1) / CPUS_PER_ULONG;
323 1 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
324 1 mu_cpuset_from_glibc_sched_affinity(&sys.sys_mask, &system_mask);
325
326 /*** Cores ***/
327
328 /* This array contains references to core_masks. It is initialized to 0
329 * to account for possible missing CPU ids */
330 1 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
331
332 /* The number of cores is not known beforehand, and the number indicates
333 * the capacity to hold the last core id, rather than the valid cores */
334 1 int num_cores = 0;
335
336 /* Only if core_id info is not reliable */
337 1 bool core_masks_array_needs_reordering = false;
338
339 cpu_set_t empty_mask;
340 1 CPU_ZERO(&empty_mask);
341
342 1 DIR *dir = opendir(PATH_SYSTEM_CPUS);
343
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (dir) {
344 struct dirent *d;
345
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 1 times.
21 while ((d = readdir(dir))) {
346
3/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
20 if (d && d->d_type == DT_DIR
347
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 && strncmp(d->d_name, "cpu", 3) == 0
348
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 && isdigit(d->d_name[3]) ) {
349
350 enum { SYSPATH_MAX = 64 };
351 char filename[SYSPATH_MAX];
352
353 /* Get CPU id */
354 2 int cpu_id = strtol(d->d_name+3, NULL, 10);
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 fatal_cond(cpu_id < 0, "Error parsing cpu_id");
356
357 /* Get core id */
358 2 snprintf(filename, SYSPATH_MAX, PATH_SYSTEM_CPUS
359 2 "/%.8s/topology/core_id", d->d_name);
360 2 int core_id = parse_int_from_file(filename);
361
362 /* Get core CPUs list */
363 cpu_set_t core_mask;
364 2 CPU_ZERO(&core_mask);
365 2 snprintf(filename, SYSPATH_MAX, PATH_SYSTEM_CPUS
366 2 "/%.8s/topology/thread_siblings_list", d->d_name);
367 2 parse_mask_from_file(filename, &core_mask);
368
369 /* Reallocate core_masks_by_coreid as needed */
370
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (core_id >= num_cores) {
371 /* Realloc, saving prev_num_cores */
372 1 int prev_num_cores = num_cores;
373 1 num_cores = core_id + 1;
374 1 void *p = realloc(sys.core_masks_by_coreid,
375 num_cores * sizeof(mu_cpuset_t));
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 fatal_cond(!p, "realloc failed in %s", __func__);
377 1 sys.core_masks_by_coreid = p;
378
379 /* Everytime we reallocate, we need to initialize
380 * [prev_size..new_size] with empty elements */
381
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int i = prev_num_cores; i < num_cores; ++i) {
382 1 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[i];
383 1 mu_cpuset_from_glibc_sched_affinity(core_cpuset, &empty_mask);
384 }
385 }
386
387 /* Save core mask */
388 2 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
389
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (core_cpuset->count == 0) {
390 /* Save core mask */
391 1 mu_cpuset_from_glibc_sched_affinity(core_cpuset, &core_mask);
392
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 } else if (CPU_EQUAL_S(mu_cpuset_alloc_size, core_cpuset->set, &core_mask)) {
393 /* Core mask already saved */
394 } else {
395 /* If we get here it means that different masks have same
396 * core_id, so we cannot trust its value to index masks.
397 * Use whatever core_id to save the mask and then reorder array
398 * with virtual core_ids */
399
400 /* Find first unused core_id and whether this mask is also
401 * present with another core id */
402 1 int new_coreid = -1;
403 1 bool already_present = false;
404
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int c = 0; c < num_cores; ++c) {
405 1 mu_cpuset_t *core = &sys.core_masks_by_coreid[c];
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (core->count == 0) {
407 if (new_coreid == -1) {
408 new_coreid = c;
409 }
410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (CPU_EQUAL_S(mu_cpuset_alloc_size, core->set, &core_mask)) {
411 already_present = true;
412 break;
413 }
414 }
415
416 /* Continue to next CPU if the core mask has already been registered */
417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (already_present) {
418 continue;
419 }
420
421 /* Relloacate if we didn't find en empty spot nor the same mask */
422
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (new_coreid == -1) {
423 1 new_coreid = num_cores++;
424 1 void *p = realloc(sys.core_masks_by_coreid,
425 num_cores * sizeof(mu_cpuset_t));
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 fatal_cond(!p, "realloc failed in %s", __func__);
427 1 sys.core_masks_by_coreid = p;
428 }
429
430 /* Finally save core mask */
431 1 mu_cpuset_t *new_core = &sys.core_masks_by_coreid[new_coreid];
432 1 mu_cpuset_from_glibc_sched_affinity(new_core, &core_mask);
433
434 /* Set boolean to do post-processing */
435 1 core_masks_array_needs_reordering = true;
436 }
437 }
438 }
439 1 closedir(dir);
440 }
441 1 sys.num_cores = num_cores;
442
443 /* Only if replacing core_id by virtual core id */
444
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (core_masks_array_needs_reordering) {
445 1 qsort(sys.core_masks_by_coreid, num_cores, sizeof(mu_cpuset_t), cmp_mu_cpuset);
446 }
447
448 /* Add core mask references to array indexed by CPU id */
449
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
450 2 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
451 2 for (int cpuid = core_cpuset->first_cpuid;
452
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
453 2 cpuid = mu_get_next_cpu(core_cpuset->set, cpuid)) {
454 2 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
455 }
456 }
457
458 /*** NUMA Nodes ***/
459 1 int num_nodes = 0;
460 1 dir = opendir(PATH_SYSTEM_NODE);
461
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (dir) {
462 struct dirent *d;
463
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
12 while ((d = readdir(dir))) {
464
3/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 7 times.
11 if (d && d->d_type == DT_DIR
465
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 && strncmp(d->d_name, "node", 4) == 0
466
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 && isdigit(d->d_name[4]) ) {
467
468 /* Get node id */
469 1 int node_id = strtol(d->d_name+4, NULL, 10);
470
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 fatal_cond(node_id < 0 || node_id > 1024, "Error parsing node_id");
471
472 /* Get node CPUs list */
473 cpu_set_t node_mask;
474 1 CPU_ZERO(&node_mask);
475 char filename[64];
476 1 snprintf(filename, 64, PATH_SYSTEM_NODE "/%.10s/cpulist", d->d_name);
477 1 parse_mask_from_file(filename, &node_mask);
478
479 /* Save node mask */
480
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (CPU_COUNT(&node_mask) > 0) {
481 1 int prev_num_nodes = num_nodes;
482 1 num_nodes = max_int(num_nodes, node_id + 1);
483 1 mu_cpuset_t *p = realloc(sys.node_masks, num_nodes*sizeof(mu_cpuset_t));
484
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 fatal_cond(!p, "realloc failed");
485 1 sys.node_masks = p;
486
487 /* Everytime we reallocate, we need to initialize
488 * [prev_size..new_size-1] with empty elements */
489
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 for (int i = prev_num_nodes; i < node_id; ++i) {
490 mu_cpuset_t *node_cpuset = &sys.node_masks[i];
491 mu_cpuset_from_glibc_sched_affinity(node_cpuset, &empty_mask);
492 }
493
494 /* New value */
495 1 mu_cpuset_from_glibc_sched_affinity(&sys.node_masks[node_id], &node_mask);
496 }
497 }
498 }
499 1 closedir(dir);
500 }
501 1 sys.num_nodes = num_nodes;
502
503 /* Fallback if some info could not be parsed */
504
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (sys.sys_mask.count == 0) {
505 int nproc_onln = sysconf(_SC_NPROCESSORS_ONLN);
506 fatal_cond(nproc_onln <= 0, "Cannot obtain system size. Contact us at "
507 PACKAGE_BUGREPORT " or configure DLB with HWLOC support.");
508 init_system(nproc_onln, nproc_onln, 1);
509 }
510 1 }
511
512
513 /*********************************************************************************/
514 /* Mask utils public functions */
515 /*********************************************************************************/
516
517 166 void mu_init( void ) {
518
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 94 times.
166 if ( !mu_initialized ) {
519 72 init_mu_struct();
520
521 #if defined IS_BGQ_MACHINE
522 enum { BGQ_NUM_CPUS = 64 };
523 enum { BGQ_NUM_CORES = 16 };
524 enum { BGQ_NUM_NODES = 1 };
525 init_system(BGQ_NUM_CPUS, BGQ_NUM_CORES, BGQ_NUM_NODES);
526 #else
527 /* Try to parse HW info from HWLOC first */
528
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
72 if (parse_hwloc() != 0) {
529 /* Fallback to system files if needed */
530 parse_system_files();
531 }
532
533 72 mu_initialized = true;
534 #endif
535 }
536 166 }
537
538 /* This function used to be declared as destructor but it may be dangerous
539 * with the OpenMP / DLB finalization at destruction time. */
540 103 void mu_finalize( void ) {
541
542 103 CPU_FREE(sys.sys_mask.set);
543
544 /* Nodes */
545
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 103 times.
209 for (unsigned int i = 0; i < sys.num_nodes; ++i) {
546 106 CPU_FREE(sys.node_masks[i].set);
547 }
548 103 free(sys.node_masks);
549
550 /* Cores per core id */
551
2/2
✓ Branch 0 taken 908 times.
✓ Branch 1 taken 103 times.
1011 for (unsigned int i = 0; i < sys.num_cores; ++i) {
552 908 CPU_FREE(sys.core_masks_by_coreid[i].set);
553 }
554 103 free(sys.core_masks_by_coreid);
555
556 /* Cores per CPU id (just references) */
557 103 free(sys.core_masks_by_cpuid);
558
559 103 sys = (const mu_system_loc_t) {};
560 103 mu_initialized = false;
561 103 mu_cpuset_setsize = CPU_SETSIZE;
562 103 mu_cpuset_alloc_size = CPU_ALLOC_SIZE(CPU_SETSIZE);
563 103 mu_cpuset_num_ulongs = CPU_ALLOC_SIZE(CPU_SETSIZE) / sizeof(unsigned long);
564 103 }
565
566 9656 int mu_get_system_size( void ) {
567
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 9628 times.
9656 if (unlikely(!mu_initialized)) mu_init();
568 9656 return sys.sys_mask.last_cpuid + 1;
569 }
570
571 154 void mu_get_system_mask(cpu_set_t *mask) {
572
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 152 times.
154 if (unlikely(!mu_initialized)) mu_init();
573 154 CPU_ZERO(mask);
574 154 memcpy(mask, sys.sys_mask.set, mu_cpuset_alloc_size);
575 154 }
576
577 12 int mu_get_system_hwthreads_per_core(void) {
578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (unlikely(!mu_initialized)) mu_init();
579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 fatal_cond(sys.core_masks_by_coreid[0].count == 0,
580 "Number of hardware threads per core is 0. Please report bug at " PACKAGE_BUGREPORT);
581 12 return sys.core_masks_by_coreid[0].count;
582 }
583
584 12 int mu_get_system_num_nodes(void) {
585 12 return sys.num_nodes;
586 }
587
588 7 int mu_get_system_cores_per_node(void) {
589 7 return mu_count_cores(sys.node_masks[0].set);
590 }
591
592 8 void mu_get_system_description(print_buffer_t *buffer) {
593
594 #define TAB " "
595
596 /* Should be enough to hold up a list of > 1024 CPUs */
597 enum { LINE_BUFFER_SIZE = 8096 };
598 char line[LINE_BUFFER_SIZE];
599 char *l;
600
601 8 printbuffer_init(buffer);
602
603 // CPUs: N (C cores x T threads)
604 8 l = line;
605 8 l += sprintf(line, TAB"CPUs: %d", mu_get_system_size());
606
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
8 if (mu_system_has_smt()) {
607 4 sprintf(l, " (%d cores x %d threads)",
608 mu_get_num_cores(), mu_get_system_hwthreads_per_core());
609 } else {
610 4 sprintf(l, " (%d cores)", mu_get_num_cores());
611 }
612 8 printbuffer_append(buffer, line);
613
614 // NUMA nodes: N (C cores per NUMA node)
615 8 l = line;
616 8 l += sprintf(line, TAB"NUMA nodes: %d", mu_get_system_num_nodes());
617
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if (sys.num_nodes > 1) {
618 3 sprintf(l, " (%d cores per NUMA node)", mu_get_system_cores_per_node());
619 }
620 8 printbuffer_append(buffer, line);
621
622 // System mask
623 8 printbuffer_append_no_newline(buffer, TAB"System mask: ");
624 8 printbuffer_append(buffer, mu_to_str(sys.sys_mask.set));
625
626 // NUMA node masks
627 8 printbuffer_append_no_newline(buffer, TAB"NUMA node masks: ");
628
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 8 times.
21 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
629 13 printbuffer_append_no_newline(buffer, mu_to_str(sys.node_masks[node_id].set));
630
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
13 if (node_id + 1 < sys.num_nodes) {
631 5 printbuffer_append_no_newline(buffer, ", ");
632 }
633 }
634 8 printbuffer_append(buffer, "");
635
636 // Core masks
637 8 printbuffer_append_no_newline(buffer, TAB"Core masks: ");
638
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 8 times.
366 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
639 358 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
640
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 if (core_cpuset->count > 0) {
641 358 printbuffer_append_no_newline(buffer, mu_to_str(core_cpuset->set));
642
2/2
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 8 times.
358 if (core_id + 1 < sys.num_cores) {
643 350 printbuffer_append_no_newline(buffer, ", ");
644 }
645 }
646 }
647 8 printbuffer_append(buffer, "");
648 8 }
649
650 90 bool mu_system_has_smt(void) {
651
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
90 if (unlikely(!mu_initialized)) mu_init();
652 90 return sys.core_masks_by_coreid[0].count > 1;
653 }
654
655 9 int mu_get_num_cores(void) {
656
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (unlikely(!mu_initialized)) mu_init();
657
658 /* Not likely, but sys.num_cores may not be the number of valid cores */
659 9 int num_cores = 0;
660
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 9 times.
369 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
661
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 if (sys.core_masks_by_coreid[core_id].count > 0) {
662 360 ++num_cores;
663 }
664 }
665
666 9 return num_cores;
667 }
668
669 11804 int mu_get_core_id(int cpuid) {
670
671
4/4
✓ Branch 0 taken 11786 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 11783 times.
11804 if (cpuid < 0 || (unsigned)cpuid >= sys.num_cpus) return -1;
672
673
2/2
✓ Branch 0 taken 121880 times.
✓ Branch 1 taken 1 times.
121881 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
674
5/6
✓ Branch 0 taken 121880 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11782 times.
✓ Branch 3 taken 110098 times.
✓ Branch 4 taken 11782 times.
✓ Branch 5 taken 110098 times.
121880 if (CPU_ISSET_S(cpuid, mu_cpuset_alloc_size,
675 sys.core_masks_by_coreid[core_id].set)) {
676 11782 return core_id;
677 }
678 }
679
680 1 return -1;
681 }
682
683 392 const mu_cpuset_t* mu_get_core_mask(int cpuid) {
684
685
3/4
✓ Branch 0 taken 392 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 390 times.
392 if (cpuid < 0 || (unsigned)cpuid >= sys.num_cpus) return NULL;
686
687 390 return sys.core_masks_by_cpuid[cpuid];
688 }
689
690 43 const mu_cpuset_t* mu_get_core_mask_by_coreid(int core_id) {
691
692
2/4
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 43 times.
43 if (core_id < 0 || (unsigned)core_id >= sys.num_cores) return NULL;
693
694 43 return &sys.core_masks_by_coreid[core_id];
695 }
696
697 /* Return Mask of full NUMA nodes covering at least 1 CPU of cpuset:
698 * e.g.:
699 * node0: [0-3]
700 * node1: [4-7]
701 * cpuset: [1-7]
702 * returns [0-7]
703 */
704 42 void mu_get_nodes_intersecting_with_cpuset(cpu_set_t *node_set, const cpu_set_t *cpuset) {
705
706 42 CPU_ZERO(node_set);
707
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 42 times.
86 for (unsigned int i=0; i<sys.num_nodes; ++i) {
708 cpu_set_t intxn;
709
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 44 times.
88 CPU_AND_S(mu_cpuset_alloc_size, &intxn, sys.node_masks[i].set, cpuset);
710
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 5 times.
44 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
711
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
78 CPU_OR_S(mu_cpuset_alloc_size, node_set, node_set, sys.node_masks[i].set);
712 }
713 }
714 42 }
715
716 /* Return Mask of full NUMA nodes containing all CPUs in cpuset:
717 * e.g.:
718 * node0: [0-3]
719 * node1: [4-7]
720 * cpuset: [1-7]
721 * returns [4-7]
722 */
723 2 void mu_get_nodes_subset_of_cpuset(cpu_set_t *node_set, const cpu_set_t *cpuset) {
724
725 2 CPU_ZERO(node_set);
726
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (unsigned int i=0; i<sys.num_nodes; ++i) {
727
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (mu_is_subset(sys.node_masks[i].set, cpuset)) {
728
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 CPU_OR_S(mu_cpuset_alloc_size, node_set, node_set, sys.node_masks[i].set);
729 }
730 }
731 2 }
732
733 /* Return Mask of cores covering at least 1 CPU of cpuset:
734 * e.g.:
735 * node0: [0-1]
736 * node1: [2-3]
737 * cpuset: [1-3]
738 * returns [0-3]
739 */
740 5 void mu_get_cores_intersecting_with_cpuset(cpu_set_t *core_set, const cpu_set_t *cpuset) {
741 5 CPU_ZERO(core_set);
742
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5 times.
25 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
743 20 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
744 cpu_set_t intxn;
745
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 CPU_AND_S(mu_cpuset_alloc_size, &intxn, core_cpuset->set, cpuset);
746
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 11 times.
20 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
747
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
18 CPU_OR_S(mu_cpuset_alloc_size, core_set, core_set, core_cpuset->set);
748 }
749 }
750 5 }
751
752 /* Return Mask of cores containing all CPUs in cpuset:
753 * e.g.:
754 * core0: [0-1]
755 * core1: [2-3]
756 * cpuset: [1-3]
757 * returns [2-3]
758 */
759 5 void mu_get_cores_subset_of_cpuset(cpu_set_t *core_set, const cpu_set_t *cpuset) {
760 5 CPU_ZERO(core_set);
761
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5 times.
25 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
762 20 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
763
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 14 times.
20 if (mu_is_subset(core_cpuset->set, cpuset)) {
764
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 CPU_OR_S(mu_cpuset_alloc_size, core_set, core_set, core_cpuset->set);
765 }
766 }
767 5 }
768
769 /* Return the next enabled CPU in mask which pertains to the next core after
770 * prev_cpu, or -1 if not found. */
771 44 int mu_get_cpu_next_core(const cpu_set_t *mask, int prev_cpu) {
772
773
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 43 times.
44 if (unlikely(prev_cpu < -1)) return -1;
774
775 43 int prev_core = mu_get_core_id(prev_cpu);
776 43 int next_cpu = mu_get_next_cpu(mask, prev_cpu);
777 43 int next_core = mu_get_core_id(next_cpu);
778
779 43 while (next_cpu != -1
780
4/4
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 27 times.
75 && next_core <= prev_core) {
781 32 next_cpu = mu_get_next_cpu(mask, next_cpu);
782 32 next_core = mu_get_core_id(next_cpu);
783 }
784
785
3/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
43 return (next_cpu == -1 || (unsigned)next_cpu >= sys.num_cpus) ? -1 : next_cpu;
786 }
787
788 /* We define as "complete" those cores that all the CPUs defined by
789 * sys.core_masks_by_coreid are enabled. */
790
791 /* Return the number of complete cores in the mask.
792 * e.g.:
793 * core0: [0-1]
794 * core1: [2-3]
795 * core2: [4-5]
796 * cpuset: [0-4]
797 * returns 2
798 */
799 33 int mu_count_cores(const cpu_set_t *mask) {
800
801 33 int cores_count = 0;
802
803
2/2
✓ Branch 0 taken 450 times.
✓ Branch 1 taken 33 times.
483 for (unsigned int coreid = 0; coreid < sys.num_cores; coreid++) {
804 // Check if we have the complete set of CPUs form the core
805
2/2
✓ Branch 1 taken 246 times.
✓ Branch 2 taken 204 times.
450 if (mu_is_subset(sys.core_masks_by_coreid[coreid].set, mask)) {
806 246 cores_count++;
807 }
808 }
809
810 33 return cores_count;
811 }
812
813 /* Return the number of complete cores in the mask.
814 * e.g.:
815 * core0: [0-1]
816 * core1: [2-3]
817 * core2: [4-5]
818 * cpuset: [0-4]
819 * returns 3
820 */
821 6 int mu_count_cores_intersecting_with_cpuset(const cpu_set_t *mask) {
822
823 6 int cores_count = 0;
824
825
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 6 times.
66 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
826 60 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
827 cpu_set_t intxn;
828
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
120 CPU_AND_S(mu_cpuset_alloc_size, &intxn, core_cpuset->set, mask);
829
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 28 times.
60 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
830 32 cores_count++;
831 }
832 }
833
834 6 return cores_count;
835 }
836
837 /* Return the id of the last complete core in the mask if any, otherwise return -1.
838 * e.g.:
839 * core0: [0-1]
840 * core1: [2-3]
841 * core2: [4-5]
842 * cpuset: [0-3]
843 * returns 1 (node1)
844 */
845 24 int mu_get_last_coreid(const cpu_set_t *mask){
846
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 2 times.
170 for (int coreid = sys.num_cores-1; coreid >= 0 ; coreid--) {
847 // Check if we have the complete set of CPUs form the core
848
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 146 times.
168 if (mu_is_subset(sys.core_masks_by_coreid[coreid].set, mask)) {
849 22 return coreid;
850 }
851 }
852
853 2 return -1;
854 }
855
856 /* Disables the CPUs of the last complete core in the mask and returns its
857 * coreid if any, otherwise return -1.
858 * e.g.:
859 * core0: [0-1]
860 * core1: [2-3]
861 * core2: [4-5]
862 * cpuset: [2-5]
863 * returns 2 (node2)
864 * updated cpuset: [2-3]
865 */
866 22 int mu_take_last_coreid(cpu_set_t *mask) {
867 22 int last_coreid = mu_get_last_coreid(mask);
868
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
22 if (last_coreid == -1) return -1;
869 20 mu_xor(mask, mask, sys.core_masks_by_coreid[last_coreid].set);
870 20 return last_coreid;
871 }
872
873 /* Enables all the CPUs of the core
874 * e.g.:
875 * core0: [0-1]
876 * core1: [2-3]
877 * core2: [4-5]
878 * cpuset: []
879 * coreid: 1
880 * updated cpuset: [2-3]
881 */
882 2 void mu_set_core(cpu_set_t *mask, int coreid){
883 2 mu_or(mask, mask, sys.core_masks_by_coreid[coreid].set);
884 2 }
885
886 /* Disables all the CPUs of the core
887 * e.g.:
888 * core0: [0-1]
889 * core1: [2-3]
890 * core2: [4-5]
891 * cpuset: [0-5]
892 * coreid: 1
893 * updated cpuset: [0-1,4-5]
894 */
895 2 void mu_unset_core(cpu_set_t *mask, int coreid){
896 2 mu_subtract(mask, mask, sys.core_masks_by_coreid[coreid].set);
897 2 }
898
899 /* Basic mask utils functions that do not need to read system's topology,
900 * i.e., mostly mask operations */
901
902 2 void mu_zero(cpu_set_t *result) {
903 2 CPU_ZERO_S(mu_cpuset_alloc_size, result);
904 2 }
905
906 13 void mu_and(cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
907
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 13 times.
41 CPU_AND_S(mu_cpuset_alloc_size, result, mask1, mask2);
908 13 }
909
910 43 void mu_or(cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
911
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 43 times.
101 CPU_OR_S(mu_cpuset_alloc_size, result, mask1, mask2);
912 43 }
913
914 20 void mu_xor (cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
915
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 CPU_XOR_S(mu_cpuset_alloc_size, result, mask1, mask2);
916 20 }
917
918 4 bool mu_equal(const cpu_set_t *mask1, const cpu_set_t *mask2) {
919 4 return CPU_EQUAL_S(mu_cpuset_alloc_size, mask1, mask2) != 0;
920 }
921
922 /* Returns true is all bits in subset are set in superset */
923 760 bool mu_is_subset(const cpu_set_t *subset, const cpu_set_t *superset) {
924 // The condition is true if the intersection is identical to subset
925 cpu_set_t intxn;
926
2/2
✓ Branch 0 taken 973 times.
✓ Branch 1 taken 760 times.
1733 CPU_AND_S(mu_cpuset_alloc_size, &intxn, subset, superset);
927 760 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset);
928 }
929
930 /* Returns true is all bits in superset are set in subset */
931 9 bool mu_is_superset(const cpu_set_t *superset, const cpu_set_t *subset) {
932 // The condition is true if the intersection is identical to subset
933 cpu_set_t intxn;
934
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 9 times.
153 CPU_AND_S(mu_cpuset_alloc_size, &intxn, superset, subset);
935 9 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset);
936 }
937
938 /* Returns true is all bits in subset are set in superset and they're not equal */
939 19 bool mu_is_proper_subset(const cpu_set_t *subset, const cpu_set_t *superset) {
940 cpu_set_t intxn;
941
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 19 times.
173 CPU_AND_S(mu_cpuset_alloc_size, &intxn, subset, superset);
942 19 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset)
943
4/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 3 times.
19 && !CPU_EQUAL_S(mu_cpuset_alloc_size, subset, superset);
944 }
945
946 /* Returns true is all bits in superset are set in subset and they're not equal */
947 22 bool mu_is_proper_superset(const cpu_set_t *superset, const cpu_set_t *subset) {
948 cpu_set_t intxn;
949
2/2
✓ Branch 0 taken 157 times.
✓ Branch 1 taken 22 times.
179 CPU_AND_S(mu_cpuset_alloc_size, &intxn, superset, subset);
950 22 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset)
951
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 3 times.
22 && !CPU_EQUAL_S(mu_cpuset_alloc_size, superset, subset);
952 }
953
954 /* Return true if any bit is present in both sets */
955 23 bool mu_intersects(const cpu_set_t *mask1, const cpu_set_t *mask2) {
956 cpu_set_t intxn;
957
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 23 times.
181 CPU_AND_S(mu_cpuset_alloc_size, &intxn, mask1, mask2);
958 23 return CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0;
959 }
960
961 /* Return the number of bits set in mask */
962 97 int mu_count(const cpu_set_t *mask) {
963 97 return CPU_COUNT_S(mu_cpuset_alloc_size, mask);
964 }
965
966 /* Return the minuend after subtracting the bits in subtrahend */
967 708 void mu_subtract(cpu_set_t *result, const cpu_set_t *minuend, const cpu_set_t *subtrahend) {
968 cpu_set_t xor;
969
2/2
✓ Branch 0 taken 768 times.
✓ Branch 1 taken 708 times.
1476 CPU_XOR_S(mu_cpuset_alloc_size, &xor, minuend, subtrahend);
970
2/2
✓ Branch 0 taken 768 times.
✓ Branch 1 taken 708 times.
1476 CPU_AND_S(mu_cpuset_alloc_size, result, minuend, &xor);
971 708 }
972
973 /* Return the one and only enabled CPU in mask, or -1 if count != 1 */
974 8 int mu_get_single_cpu(const cpu_set_t *mask) {
975
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 5 times.
8 if (CPU_COUNT_S(mu_cpuset_alloc_size, mask) == 1) {
976 3 return mu_get_first_cpu(mask);
977 }
978 5 return -1;
979 }
980
981 /* some of the following functions have been inspired by:
982 * https://github.com/open-mpi/hwloc/blob/master/hwloc/bitmap.c */
983
984 /* Return the first enabled CPU in mask, or -1 if mask is empty */
985 4932 int mu_get_first_cpu(const cpu_set_t *mask) {
986
987
2/2
✓ Branch 0 taken 6585 times.
✓ Branch 1 taken 47 times.
6632 for (unsigned int i = 0; i < mu_cpuset_num_ulongs; ++i) {
988 6585 unsigned long bits = mask->__bits[i];
989
2/2
✓ Branch 0 taken 4885 times.
✓ Branch 1 taken 1700 times.
6585 if (bits) {
990 4885 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
991 }
992 }
993
994 47 return -1;
995 }
996
997 /* Return the last enabled CPU in mask, or -1 if mask is empty */
998 1279 int mu_get_last_cpu(const cpu_set_t *mask) {
999
1000
2/2
✓ Branch 0 taken 2813 times.
✓ Branch 1 taken 3 times.
2816 for (unsigned int i = mu_cpuset_num_ulongs; i-- > 0; ) {
1001 2813 unsigned long bits = mask->__bits[i];
1002
2/2
✓ Branch 0 taken 1276 times.
✓ Branch 1 taken 1537 times.
2813 if (bits) {
1003 /* glibc does not provide a fls function, there are more optimal
1004 * solutions, but this function is not that critical */
1005 1276 int cpuid = CPUS_PER_ULONG * i;
1006
2/2
✓ Branch 0 taken 29282 times.
✓ Branch 1 taken 1276 times.
30558 while (bits >>= 1) {
1007 29282 ++cpuid;
1008 }
1009 1276 return cpuid;
1010 }
1011 }
1012
1013 3 return -1;
1014 }
1015
1016 /* Return the next enabled CPU in mask after prev, or -1 if not found */
1017 7616 int mu_get_next_cpu(const cpu_set_t *mask, int prev) {
1018
1019
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7615 times.
7616 if (unlikely(prev < -1)) return -1;
1020
1021 7615 for (unsigned int i = (prev + 1) / CPUS_PER_ULONG;
1022
2/2
✓ Branch 0 taken 10085 times.
✓ Branch 1 taken 3397 times.
13482 i < mu_cpuset_num_ulongs; ++i) {
1023 10085 unsigned long bits = mask->__bits[i];
1024
1025 /* mask bitmap only if previous cpu belong to current iteration */
1026
4/4
✓ Branch 0 taken 10083 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7519 times.
✓ Branch 3 taken 2564 times.
10085 if (prev >= 0 && (unsigned)prev / CPUS_PER_ULONG == i) {
1027 7519 bits &= ULONG_MAX << (prev % CPUS_PER_ULONG + 1);
1028 }
1029
1030
2/2
✓ Branch 0 taken 4218 times.
✓ Branch 1 taken 5867 times.
10085 if (bits) {
1031 4218 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
1032 }
1033 }
1034
1035 3397 return -1;
1036 }
1037
1038 /* Return the next unset CPU in mask after prev, or -1 if not found */
1039 1626 int mu_get_next_unset(const cpu_set_t *mask, int prev) {
1040
1041
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1625 times.
1626 if (unlikely(prev < -1)) return -1;
1042
1043 1625 for (unsigned int i = (prev + 1) / CPUS_PER_ULONG;
1044
2/2
✓ Branch 0 taken 1683 times.
✓ Branch 1 taken 27 times.
1710 i < mu_cpuset_num_ulongs; ++i) {
1045 1683 unsigned long bits = ~(mask->__bits[i]);
1046
1047 /* mask bitmap only if previous cpu belong to current iteration */
1048
4/4
✓ Branch 0 taken 1682 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1599 times.
✓ Branch 3 taken 83 times.
1683 if (prev >= 0 && (unsigned)prev / CPUS_PER_ULONG == i) {
1049 1599 bits &= ULONG_MAX << (prev % CPUS_PER_ULONG + 1);
1050 }
1051
1052
2/2
✓ Branch 0 taken 1598 times.
✓ Branch 1 taken 85 times.
1683 if (bits) {
1053 1598 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
1054 }
1055 }
1056
1057 27 return -1;
1058 }
1059
1060 // mu_to_str and mu_parse_mask functions are used by DLB utilities
1061 // We export their dynamic symbols to avoid code duplication,
1062 // although they do not belong to the public API
1063 DLB_EXPORT_SYMBOL
1064 1567 const char* mu_to_str( const cpu_set_t *mask ) {
1065
1066 static __thread char buffer[CPU_SETSIZE*4];
1067 1567 char *b = buffer;
1068 1567 *(b++) = '[';
1069 1567 bool entry_made = false;
1070
2/2
✓ Branch 1 taken 1612 times.
✓ Branch 2 taken 1567 times.
3179 for (int cpuid = mu_get_first_cpu(mask); cpuid >= 0;
1071 1612 cpuid = mu_get_next_cpu(mask, cpuid)) {
1072
1073 /* Find interval distance */
1074 1612 int next_unset = mu_get_next_unset(mask, cpuid);
1075 3200 int distance = next_unset > 0 ? next_unset - 1 - cpuid
1076
2/2
✓ Branch 0 taken 1588 times.
✓ Branch 1 taken 24 times.
1612 : mu_get_last_cpu(mask) - cpuid;
1077
1078 /* Add ',' separator for subsequent entries */
1079
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 1540 times.
1612 if (entry_made) {
1080 72 *(b++) = ',';
1081 } else {
1082 1540 entry_made = true;
1083 }
1084
1085 /* Write element, pair or range */
1086
2/2
✓ Branch 0 taken 1264 times.
✓ Branch 1 taken 348 times.
1612 if (distance == 0) {
1087 1264 b += sprintf(b, "%d", cpuid);
1088
2/2
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 146 times.
348 } else if (distance == 1) {
1089 202 b += sprintf(b, "%d,%d", cpuid, cpuid+1);
1090 202 ++cpuid;
1091 } else {
1092 146 b += sprintf(b, "%d-%d", cpuid, cpuid+distance);
1093 146 cpuid += distance;
1094 }
1095 }
1096 1567 *(b++) = ']';
1097 1567 *b = '\0';
1098
1099 1567 return buffer;
1100 }
1101
1102 39 static void parse_64_bits_mask(cpu_set_t *mask, unsigned int offset, const char *str, int base) {
1103 39 unsigned long long number = strtoull(str, NULL, base);
1104 39 unsigned int i = offset;
1105
3/4
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 344 times.
✗ Branch 3 not taken.
383 while (number > 0 && i < mu_cpuset_setsize) {
1106
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 278 times.
344 if (number & 1) {
1107
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 CPU_SET(i, mask);
1108 }
1109 344 ++i;
1110 344 number = number >> 1;
1111 }
1112 39 }
1113
1114 DLB_EXPORT_SYMBOL
1115 244 void mu_parse_mask( const char *str, cpu_set_t *mask ) {
1116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 244 times.
247 if (!str) return;
1117
1118 244 size_t str_len = strnlen(str, CPU_SETSIZE+1);
1119
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 243 times.
244 if (str_len > CPU_SETSIZE) return;
1120
1121 243 CPU_ZERO( mask );
1122
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 241 times.
243 if (str_len == 0) return;
1123
1124 regex_t regex_bitmask;
1125 regex_t regex_hexmask;
1126 regex_t regex_range;
1127 regex_t old_regex_bitmask;
1128
1129 /* Compile regular expressions */
1130
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 241 times.
241 if ( regcomp(&regex_bitmask, "^0[bB][0-1]+$", REG_EXTENDED|REG_NOSUB) ) {
1131 fatal0( "Could not compile regex");
1132 }
1133
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 241 times.
241 if ( regcomp(&regex_hexmask, "^0[xX][0-9,a-f,A-F]+$", REG_EXTENDED|REG_NOSUB) ) {
1134 fatal0( "Could not compile regex");
1135 }
1136
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 241 times.
241 if ( regcomp(&regex_range, "^[0-9,-]+$", REG_EXTENDED|REG_NOSUB) ) {
1137 fatal0( "Could not compile regex");
1138 }
1139
1140 /***** Deprecated *****/
1141
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 241 times.
241 if ( regcomp(&old_regex_bitmask, "^[0-1][0-1]+[bB]$", REG_EXTENDED|REG_NOSUB) ) {
1142 fatal0( "Could not compile regex");
1143 }
1144 /* Regular expression matches OLD bitmask, e.g.: 11110011b */
1145
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 238 times.
241 if ( !regexec(&old_regex_bitmask, str, 0, NULL, 0) ) {
1146 3 warning("The binary form xxxxb is deprecated, please use 0bxxxx.");
1147 // Parse
1148
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
22 for (unsigned int i=0; i<str_len; i++) {
1149
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
19 if ( str[i] == '1' && i < mu_cpuset_setsize ) {
1150
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 CPU_SET( i, mask );
1151 }
1152 }
1153 }
1154 /**********************/
1155
1156 /* Regular expression matches bitmask, e.g.: 0b11100001 */
1157
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 229 times.
238 else if ( !regexec(&regex_bitmask, str, 0, NULL, 0) ) {
1158 /* Ignore '0b' */
1159 9 str += 2;
1160
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (strlen(str) <= 64) {
1161 6 parse_64_bits_mask(mask, 0, str, 2);
1162 } else {
1163 /* parse in chunks of 64 bits */
1164 3 char *str_copy = strdup(str);
1165 char *start_ptr;
1166 3 char *end_ptr = str_copy + strlen(str_copy);
1167 3 unsigned int offset = 0;
1168 do {
1169
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 start_ptr = strlen(str_copy) < 64 ? str_copy : end_ptr - 64;
1170 12 parse_64_bits_mask(mask, offset, start_ptr, 2);
1171 12 offset += 64;
1172 12 end_ptr = start_ptr;
1173 12 *end_ptr = '\0';
1174
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 } while (strlen(str_copy) > 0);
1175 3 free(str_copy);
1176 }
1177 }
1178
1179 /* Regular expression matches hexmask, e.g.: 0xE1 */
1180
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 219 times.
229 else if ( !regexec(&regex_hexmask, str, 0, NULL, 0) ) {
1181 /* Ignore '0x' */
1182 10 str += 2;
1183
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
10 if (strlen(str) <= 16) {
1184 7 parse_64_bits_mask(mask, 0, str, 16);
1185 } else {
1186 /* parse in chunks of 64 bits (16 hex digits) */
1187 3 char *str_copy = strdup(str);
1188 char *start_ptr;
1189 3 char *end_ptr = str_copy + strlen(str_copy);
1190 3 unsigned int offset = 0;
1191 do {
1192
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 start_ptr = strlen(str_copy) < 16 ? str_copy : end_ptr - 16;
1193 14 parse_64_bits_mask(mask, offset, start_ptr, 16);
1194 14 offset += 64;
1195 14 end_ptr = start_ptr;
1196 14 *end_ptr = '\0';
1197
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
14 } while (strlen(str_copy) > 0);
1198 3 free(str_copy);
1199 }
1200 }
1201
1202 /* Regular expression matches range, e.g.: 0,5-7 */
1203
1/2
✓ Branch 1 taken 219 times.
✗ Branch 2 not taken.
219 else if ( !regexec(&regex_range, str, 0, NULL, 0) ) {
1204 // Parse
1205 219 const char *ptr = str;
1206 char *endptr;
1207
2/2
✓ Branch 0 taken 334 times.
✓ Branch 1 taken 219 times.
553 while ( ptr < str+strlen(str) ) {
1208 // Discard junk at the left
1209
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 313 times.
334 if ( !isdigit(*ptr) ) { ptr++; continue; }
1210
1211 313 unsigned long start_ = strtoul( ptr, &endptr, 10 );
1212 313 unsigned long start = start_ < mu_cpuset_setsize ? start_ : mu_cpuset_setsize;
1213 313 ptr = endptr;
1214
1215 // Single element
1216
5/6
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 73 times.
✓ Branch 2 taken 91 times.
✓ Branch 3 taken 149 times.
✓ Branch 4 taken 164 times.
✗ Branch 5 not taken.
313 if ( (*ptr == ',' || *ptr == '\0') && start < mu_cpuset_setsize ) {
1217
1/2
✓ Branch 0 taken 164 times.
✗ Branch 1 not taken.
164 CPU_SET( start, mask );
1218 164 ptr++;
1219 164 continue;
1220 }
1221 // Range
1222
1/2
✓ Branch 0 taken 149 times.
✗ Branch 1 not taken.
149 else if ( *ptr == '-' ) {
1223 // Discard '-' and possible junk
1224 149 ptr++;
1225
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 149 times.
149 if ( !isdigit(*ptr) ) { ptr++; continue; }
1226
1227 149 unsigned long end_ = strtoul( ptr, &endptr, 10 );
1228 149 unsigned long end = end_ < mu_cpuset_setsize ? end_ : mu_cpuset_setsize;
1229 149 ptr = endptr;
1230
1231 // Valid range
1232
1/2
✓ Branch 0 taken 149 times.
✗ Branch 1 not taken.
149 if ( end > start ) {
1233
4/4
✓ Branch 0 taken 1944 times.
✓ Branch 1 taken 148 times.
✓ Branch 2 taken 1943 times.
✓ Branch 3 taken 1 times.
2092 for ( unsigned long i=start; i<=end && i<mu_cpuset_setsize; i++ ) {
1234
1/2
✓ Branch 0 taken 1943 times.
✗ Branch 1 not taken.
1943 CPU_SET( i, mask );
1235 }
1236 }
1237 149 continue;
1238 }
1239 // Unexpected token
1240 else { }
1241 }
1242 }
1243 /* Regular expression does not match */
1244 else { }
1245
1246 241 regfree(&regex_bitmask);
1247 241 regfree(&regex_hexmask);
1248 241 regfree(&regex_range);
1249 241 regfree(&old_regex_bitmask);
1250
1251
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 241 times.
241 if ( CPU_COUNT(mask) == 0 ) {
1252 warning( "Parsed mask \"%s\" does not seem to be a valid mask\n", str );
1253 }
1254 }
1255
1256 /* Equivalent to mu_to_str, but generate quoted string in str up to namelen-1 bytes */
1257 8 void mu_get_quoted_mask(const cpu_set_t *mask, char *str, size_t namelen) {
1258
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (namelen < 2)
1259 return;
1260
1261 8 char *b = str;
1262 8 *(b++) = '"';
1263 8 size_t bytes = 1;
1264 8 bool entry_made = false;
1265
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
16 for (int cpuid = mu_get_first_cpu(mask); cpuid >= 0;
1266 8 cpuid = mu_get_next_cpu(mask, cpuid)) {
1267
1268 /* Find interval distance */
1269 8 int next_unset = mu_get_next_unset(mask, cpuid);
1270 14 int distance = next_unset > 0 ? next_unset - 1 - cpuid
1271
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 : mu_get_last_cpu(mask) - cpuid;
1272
1273 /* Add ',' separator for subsequent entries */
1274
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (entry_made) {
1275
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (bytes+1 < namelen) {
1276 1 *(b++) = ',';
1277 1 ++bytes;
1278 }
1279 } else {
1280 7 entry_made = true;
1281 }
1282
1283 /* Write element, pair or range */
1284
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if (distance == 0) {
1285 3 int len = snprintf(NULL, 0, "%d", cpuid);
1286
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (bytes+len < namelen) {
1287 3 b += sprintf(b, "%d", cpuid);
1288 3 bytes += len;
1289 }
1290
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 } else if (distance == 1) {
1291 3 int len = snprintf(NULL, 0, "%d,%d", cpuid, cpuid+1);
1292
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (bytes+len < namelen) {
1293 3 b += sprintf(b, "%d,%d", cpuid, cpuid+1);
1294 3 bytes += len;
1295 3 ++cpuid;
1296 }
1297 } else {
1298 2 int len = snprintf(NULL, 0, "%d-%d", cpuid, cpuid+distance);
1299
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (bytes+len < namelen) {
1300 2 b += sprintf(b, "%d-%d", cpuid, cpuid+distance);
1301 2 bytes += len;
1302 2 cpuid += distance;
1303 }
1304 }
1305 }
1306
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (bytes+1 < namelen) {
1307 8 *(b++) = '"';
1308 8 ++bytes;
1309 }
1310 8 *b = '\0';
1311 }
1312
1313 1 char * mu_parse_to_slurm_format(const cpu_set_t *mask) {
1314 1 char *str = malloc((mu_cpuset_setsize >> 2) + 3);
1315 if (str < 0)
1316 return NULL;
1317 1 unsigned int offset = 2;
1318 1 unsigned long long val = 0;
1319 1 const int threshold = 4;
1320 1 sprintf(str, "0x");
1321
2/2
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 1 times.
131 for (int cpuid = mu_get_last_cpu(mask); cpuid >= 0; --cpuid) {
1322
5/6
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 121 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 121 times.
130 if(CPU_ISSET(cpuid, mask)) {
1323 9 val |= 1 << (cpuid % threshold);
1324 }
1325
4/4
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 97 times.
130 if (cpuid > 0 && cpuid % threshold == 0) {
1326 32 sprintf(str+offset, "%llx", val);
1327 32 val = 0;
1328 32 offset++;
1329 }
1330 }
1331 1 sprintf(str+offset, "%llx", val);
1332 1 return str;
1333 }
1334
1335 4 bool mu_equivalent_masks(const char *str1, const char *str2) {
1336 cpu_set_t mask1, mask2;
1337 4 mu_parse_mask(str1, &mask1);
1338 4 mu_parse_mask(str2, &mask2);
1339 4 return CPU_EQUAL(&mask1, &mask2);
1340 }
1341
1342
1343 3120 static int cmp_cpuids(cpuid_t cpuid1, cpuid_t cpuid2) {
1344 3120 int cpu1_core_id = mu_get_core_id(cpuid1);
1345 3120 int cpu2_core_id = mu_get_core_id(cpuid2);
1346
2/2
✓ Branch 0 taken 480 times.
✓ Branch 1 taken 2640 times.
3120 if (cpu1_core_id == cpu2_core_id) {
1347 480 return cpuid1 - cpuid2;
1348 } else {
1349 2640 return cpu1_core_id - cpu2_core_id;
1350 }
1351 }
1352
1353 /* Compare CPUs so that:
1354 * - owned CPUs first, in ascending order
1355 * - non-owned later, starting from the first owned, then ascending
1356 * e.g.: system: [0-7], owned: [3-5]
1357 * cpu_list = {4,5,6,7,0,1,2,3}
1358 */
1359 1543 int mu_cmp_cpuids_by_ownership(const void *cpuid1, const void *cpuid2, void *mask) {
1360 /* Expand arguments */
1361 1543 cpuid_t _cpuid1 = *(cpuid_t*)cpuid1;
1362 1543 cpuid_t _cpuid2 = *(cpuid_t*)cpuid2;
1363 1543 cpu_set_t *process_mask = mask;
1364
1365
5/6
✓ Branch 0 taken 1543 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 737 times.
✓ Branch 3 taken 806 times.
✓ Branch 4 taken 737 times.
✓ Branch 5 taken 806 times.
1543 if (CPU_ISSET(_cpuid1, process_mask)) {
1366
5/6
✓ Branch 0 taken 737 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 529 times.
✓ Branch 3 taken 208 times.
✓ Branch 4 taken 529 times.
✓ Branch 5 taken 208 times.
737 if (CPU_ISSET(_cpuid2, process_mask)) {
1367 /* both CPUs are owned: ascending order */
1368 529 return cmp_cpuids(_cpuid1, _cpuid2);
1369 } else {
1370 /* cpuid2 is NOT owned and cpuid1 IS */
1371 208 return -1;
1372 }
1373 } else {
1374
5/6
✓ Branch 0 taken 806 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 223 times.
✓ Branch 3 taken 583 times.
✓ Branch 4 taken 223 times.
✓ Branch 5 taken 583 times.
806 if (CPU_ISSET(_cpuid2, process_mask)) {
1375 /* cpuid2 IS owned and cpuid1 is NOT */
1376 223 return 1;
1377 } else {
1378 /* none is owned */
1379 583 int first_cpu = mu_get_first_cpu(process_mask);
1380 583 int first_core = mu_get_core_id(first_cpu);
1381 583 int cpu1_core_id = mu_get_core_id(_cpuid1);
1382 583 int cpu2_core_id = mu_get_core_id(_cpuid2);
1383
2/2
✓ Branch 0 taken 351 times.
✓ Branch 1 taken 232 times.
583 if ((cpu1_core_id > first_core
1384
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 304 times.
351 && cpu2_core_id > first_core)
1385
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 47 times.
279 || (cpu1_core_id < first_core
1386
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 58 times.
232 && cpu2_core_id < first_core)) {
1387 /* Both CPUs are either before or after the process mask */
1388 478 return cmp_cpuids(_cpuid1, _cpuid2);
1389 } else {
1390 /* Compare with respect to process mask */
1391 105 return cmp_cpuids(first_cpu, _cpuid1);
1392 }
1393 }
1394 }
1395 }
1396
1397 /* Compare CPUs so that:
1398 * - CPUs are sorted according to the affinity array:
1399 * * affinity: array of cpu_set_t, each position represents a level
1400 * in the affinity, the last position is an empty cpu set.
1401 * (PRE: each affinity level is a superset of the previous level mask)
1402 * - Sorted by affinity level in ascending order
1403 * - non-owned later, starting from the first owned, then ascending
1404 * e.g.: affinity: {{6-7}, {4-7}, {0-7}, {}}
1405 * sorted_cpu_list = {6,7,4,5,0,1,2,3}
1406 */
1407 2739 int mu_cmp_cpuids_by_affinity(const void *cpuid1, const void *cpuid2, void *affinity) {
1408 /* Expand arguments */
1409 2739 cpuid_t _cpuid1 = *(cpuid_t*)cpuid1;
1410 2739 cpuid_t _cpuid2 = *(cpuid_t*)cpuid2;
1411 2739 cpu_set_t *_affinity = affinity;
1412
1413 /* Find affinity level of each CPU */
1414 2739 int cpu1_level = 0;
1415 2739 int cpu2_level = 0;
1416 2739 cpu_set_t *mask = _affinity;
1417
2/2
✓ Branch 1 taken 6143 times.
✓ Branch 2 taken 2739 times.
8882 while(CPU_COUNT(mask) > 0) {
1418
5/6
✓ Branch 0 taken 6143 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3940 times.
✓ Branch 3 taken 2203 times.
✓ Branch 4 taken 2203 times.
✓ Branch 5 taken 3940 times.
6143 if (!CPU_ISSET(_cpuid1, mask)) {
1419 2203 ++cpu1_level;
1420 }
1421
5/6
✓ Branch 0 taken 6143 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3869 times.
✓ Branch 3 taken 2274 times.
✓ Branch 4 taken 2274 times.
✓ Branch 5 taken 3869 times.
6143 if (!CPU_ISSET(_cpuid2, mask)) {
1422 2274 ++cpu2_level;
1423 }
1424 6143 ++mask;
1425 }
1426
1427 /* If levels differ, sort levels in ascending order */
1428
2/2
✓ Branch 0 taken 731 times.
✓ Branch 1 taken 2008 times.
2739 if (cpu1_level != cpu2_level) {
1429 731 return cpu1_level - cpu2_level;
1430 }
1431
1432 /* If both are level 0, sort in ascending order */
1433
2/2
✓ Branch 0 taken 712 times.
✓ Branch 1 taken 1296 times.
2008 if (cpu1_level == 0) {
1434 712 return cmp_cpuids(_cpuid1, _cpuid2);
1435 }
1436
1437 /* If both are level 1, sort from the first CPU in level 0 */
1438 /* e.g.: level0: [2,3], level1: [0,7] -> [4,5,6,7,0,1] */
1439
2/2
✓ Branch 0 taken 1015 times.
✓ Branch 1 taken 281 times.
1296 if (cpu1_level == 1) {
1440 1015 cpu_set_t *level0_mask = _affinity;
1441 1015 int first_cpu = mu_get_first_cpu(level0_mask);
1442 1015 int first_core = mu_get_core_id(first_cpu);
1443 1015 int cpu1_core_id = mu_get_core_id(_cpuid1);
1444 1015 int cpu2_core_id = mu_get_core_id(_cpuid2);
1445
2/2
✓ Branch 0 taken 806 times.
✓ Branch 1 taken 209 times.
1015 if ((cpu1_core_id > first_core
1446
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 790 times.
806 && cpu2_core_id > first_core)
1447
2/2
✓ Branch 0 taken 209 times.
✓ Branch 1 taken 16 times.
225 || (cpu1_core_id < first_core
1448
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 108 times.
209 && cpu2_core_id < first_core)) {
1449 /* Both CPUs are either before or after the process mask */
1450 891 return cmp_cpuids(_cpuid1, _cpuid2);
1451 } else {
1452 /* Compare with respect to process mask */
1453 124 return cmp_cpuids(first_cpu, _cpuid1);
1454 }
1455 }
1456
1457 /* TODO: compute numa distance */
1458 /* Levels 2+, sort in ascending order */
1459 281 return cmp_cpuids(_cpuid1, _cpuid2);
1460 }
1461
1462
1463 /*********************************************************************************/
1464 /* Mask utils testing functions */
1465 /*********************************************************************************/
1466
1467 68 static void print_sys_info(void) {
1468
1469
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 25 times.
68 verbose(VB_AFFINITY, "System mask: %s", mu_to_str(sys.sys_mask.set));
1470
1471
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 68 times.
141 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
1472
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 60 times.
73 verbose(VB_AFFINITY, "Node %d mask: %s",
1473 node_id, mu_to_str(sys.node_masks[node_id].set));
1474 }
1475
1476
2/2
✓ Branch 0 taken 1098 times.
✓ Branch 1 taken 68 times.
1166 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
1477 1098 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
1478
1/2
✓ Branch 0 taken 1098 times.
✗ Branch 1 not taken.
1098 if (core_cpuset->count > 0) {
1479
2/2
✓ Branch 0 taken 420 times.
✓ Branch 1 taken 678 times.
1098 verbose(VB_AFFINITY, "Core %d mask: %s",
1480 core_id, mu_to_str(core_cpuset->set));
1481 }
1482 }
1483
1484 68 for (int cpuid = sys.sys_mask.first_cpuid;
1485
4/4
✓ Branch 0 taken 1324 times.
✓ Branch 1 taken 67 times.
✓ Branch 2 taken 1323 times.
✓ Branch 3 taken 1 times.
1391 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
1486 1323 cpuid = mu_get_next_cpu(sys.sys_mask.set, cpuid)) {
1487 1323 const mu_cpuset_t *core_cpuset = sys.core_masks_by_cpuid[cpuid];
1488
3/4
✓ Branch 0 taken 1185 times.
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 1185 times.
✗ Branch 3 not taken.
1323 if (core_cpuset && core_cpuset->count > 0) {
1489
2/2
✓ Branch 0 taken 447 times.
✓ Branch 1 taken 738 times.
1185 verbose(VB_AFFINITY, "CPU %d core mask: %s",
1490 cpuid, mu_to_str(core_cpuset->set));
1491 }
1492 }
1493 68 }
1494
1495 2 bool mu_testing_is_initialized(void) {
1496 2 return mu_initialized;
1497 }
1498
1499 52 void mu_testing_set_sys_size(int size) {
1500 52 init_system(size, size, 1);
1501 52 print_sys_info();
1502 52 }
1503
1504 3 void mu_testing_set_sys(unsigned int num_cpus, unsigned int num_cores,
1505 unsigned int num_nodes) {
1506 3 init_system(num_cpus, num_cores, num_nodes);
1507 3 print_sys_info();
1508 3 }
1509
1510 12 void mu_testing_set_sys_masks(const cpu_set_t *sys_mask,
1511 const cpu_set_t *core_masks, unsigned int num_cores,
1512 const cpu_set_t *node_masks, unsigned int num_nodes) {
1513 12 init_system_masks(sys_mask, core_masks, num_cores, node_masks, num_nodes);
1514 12 print_sys_info();
1515 12 }
1516
1517 1 void mu_testing_init_nohwloc(void) {
1518 1 init_mu_struct();
1519 1 parse_system_files();
1520 1 mu_initialized = true;
1521 1 print_sys_info();
1522 1 }
1523