GCC Code Coverage Report


Directory: src/
File: src/support/mask_utils.c
Date: 2025-11-21 10:34:40
Exec Total Coverage
Lines: 656 675 97.2%
Functions: 63 63 100.0%
Branches: 397 476 83.4%

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 1165 static inline void mu_cpuset_from_glibc_sched_affinity(mu_cpuset_t *mu_cpuset,
67 const cpu_set_t *cpu_set) {
68 4660 *mu_cpuset = (const mu_cpuset_t) {
69 1165 .set = CPU_ALLOC(mu_cpuset_setsize),
70 .alloc_size = mu_cpuset_alloc_size,
71 1165 .count = CPU_COUNT_S(mu_cpuset_alloc_size, cpu_set),
72 1165 .first_cpuid = mu_get_first_cpu(cpu_set),
73 1165 .last_cpuid = mu_get_last_cpu(cpu_set),
74 };
75 1165 memcpy(mu_cpuset->set, cpu_set, mu_cpuset_alloc_size);
76 1165 }
77
78 #ifdef HWLOC_LIB
79 700 static inline void mu_cpuset_from_hwloc_bitmap(mu_cpuset_t *mu_cpuset,
80 hwloc_const_bitmap_t bitmap, hwloc_topology_t topology) {
81 700 *mu_cpuset = (const mu_cpuset_t) {
82 700 .set = CPU_ALLOC(mu_cpuset_setsize),
83 .alloc_size = mu_cpuset_alloc_size,
84 700 .count = hwloc_bitmap_weight(bitmap),
85 700 .first_cpuid = hwloc_bitmap_first(bitmap),
86 700 .last_cpuid = hwloc_bitmap_last(bitmap),
87 };
88 700 hwloc_cpuset_to_glibc_sched_affinity(topology, bitmap, mu_cpuset->set,
89 mu_cpuset_alloc_size);
90 700 }
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 80 static void init_mu_struct(void) {
116 80 sys = (const mu_system_loc_t) {};
117 80 }
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 65 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 56 times.
✓ Branch 1 taken 9 times.
65 if (mu_initialized) {
127 56 mu_finalize();
128 } else {
129 9 init_mu_struct();
130 }
131
132 /*** System ***/
133 65 sys.num_cpus = mu_get_last_cpu(sys_mask) + 1;
134 65 mu_cpuset_setsize = sys.num_cpus;
135 65 mu_cpuset_num_ulongs = (mu_cpuset_setsize + CPUS_PER_ULONG - 1) / CPUS_PER_ULONG;
136 65 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
137 65 sys.sys_mask = (const mu_cpuset_t) {
138 65 .set = CPU_ALLOC(mu_cpuset_setsize),
139 65 .count = CPU_COUNT_S(mu_cpuset_alloc_size, sys_mask),
140 .first_cpuid = 0,
141 65 .last_cpuid = mu_cpuset_setsize - 1,
142 };
143 65 memcpy(sys.sys_mask.set, sys_mask, mu_cpuset_alloc_size);
144
145 /*** Cores ***/
146 65 sys.num_cores = num_cores;
147 65 sys.core_masks_by_coreid = malloc(sys.num_cores * sizeof(mu_cpuset_t));
148 65 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
149
2/2
✓ Branch 0 taken 1084 times.
✓ Branch 1 taken 65 times.
1149 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
150 1084 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
151 1084 mu_cpuset_from_glibc_sched_affinity(core_cpuset, &core_masks[core_id]);
152 1084 for (int cpuid = core_cpuset->first_cpuid;
153
4/4
✓ Branch 0 taken 1172 times.
✓ Branch 1 taken 1083 times.
✓ Branch 2 taken 1171 times.
✓ Branch 3 taken 1 times.
2255 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
154 1171 cpuid = mu_get_next_cpu(core_cpuset->set, cpuid)) {
155 /* Save reference to another array indexed by cpuid */
156 1171 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
157 }
158 }
159
160 /*** NUMA Nodes ***/
161 65 sys.num_nodes = num_nodes;
162 65 sys.node_masks = malloc(sys.num_nodes * sizeof(mu_cpuset_t));
163
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 65 times.
135 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
164 70 mu_cpuset_from_glibc_sched_affinity(&sys.node_masks[node_id], &node_masks[node_id]);
165 }
166
167 65 mu_initialized = true;
168 65 }
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 53 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 53 CPU_ZERO(&sys_mask);
179
2/2
✓ Branch 0 taken 1076 times.
✓ Branch 1 taken 53 times.
1129 for (unsigned int cpuid = 0; cpuid < num_cpus; ++cpuid) {
180
1/2
✓ Branch 0 taken 1076 times.
✗ Branch 1 not taken.
1076 CPU_SET(cpuid, &sys_mask);
181 }
182
183 /*** Cores ***/
184 53 unsigned int cpus_per_core = num_cpus / num_cores;
185 53 cpu_set_t *core_masks = calloc(num_cores, sizeof(cpu_set_t));
186
2/2
✓ Branch 0 taken 1036 times.
✓ Branch 1 taken 53 times.
1089 for (unsigned int core_id = 0; core_id < num_cores; ++core_id) {
187 1036 for (unsigned int cpuid = core_id * cpus_per_core;
188
2/2
✓ Branch 0 taken 1076 times.
✓ Branch 1 taken 1036 times.
2112 cpuid < (core_id+1) * cpus_per_core; ++cpuid) {
189
1/2
✓ Branch 0 taken 1076 times.
✗ Branch 1 not taken.
1076 CPU_SET(cpuid, &core_masks[core_id]);
190 }
191 }
192
193 /*** NUMA Nodes ***/
194 53 unsigned int cpus_per_node = num_cpus / num_nodes;
195 53 cpu_set_t *node_masks = calloc(num_nodes, sizeof(cpu_set_t));
196
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 53 times.
109 for (unsigned int node_id = 0; node_id < num_nodes; ++node_id) {
197 56 for (unsigned int cpuid = node_id * cpus_per_node;
198
2/2
✓ Branch 0 taken 1076 times.
✓ Branch 1 taken 56 times.
1132 cpuid < (node_id+1) * cpus_per_node; ++cpuid) {
199
1/2
✓ Branch 0 taken 1076 times.
✗ Branch 1 not taken.
1076 CPU_SET(cpuid, &node_masks[node_id]);
200 }
201 }
202
203 53 init_system_masks(&sys_mask, core_masks, num_cores, node_masks, num_nodes);
204 53 free(core_masks);
205 53 free(node_masks);
206 53 }
207
208 70 static int parse_hwloc(void) {
209 #ifdef HWLOC_LIB
210 /* Check runtime library compatibility */
211 70 unsigned int hwloc_version = hwloc_get_api_version();
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 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 70 hwloc_topology_init(&topology);
219 70 hwloc_topology_load(topology);
220
221 /*** System ***/
222 70 hwloc_obj_t machine = hwloc_get_obj_by_type(topology, HWLOC_OBJ_MACHINE, 0);
223 70 sys.num_cpus = hwloc_bitmap_last(machine->cpuset) + 1;
224 70 mu_cpuset_setsize = sys.num_cpus;
225 #if HWLOC_API_VERSION >= 0x00020100
226 70 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 70 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
231 70 mu_cpuset_from_hwloc_bitmap(&sys.sys_mask, machine->cpuset, topology);
232
233 /*** Cores ***/
234 70 hwloc_obj_type_t core = HWLOC_OBJ_CORE;
235 70 sys.num_cores = hwloc_get_nbobjs_by_type(topology, core);
236 70 sys.core_masks_by_coreid = calloc(sys.num_cores, sizeof(mu_cpuset_t));
237 70 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
238
2/2
✓ Branch 0 taken 560 times.
✓ Branch 1 taken 70 times.
630 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
239 560 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, core, core_id);
240 560 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
241 560 mu_cpuset_from_hwloc_bitmap(core_cpuset, obj->cpuset, topology);
242 560 for (int cpuid = core_cpuset->first_cpuid;
243
3/4
✓ Branch 0 taken 560 times.
✓ Branch 1 taken 560 times.
✓ Branch 2 taken 560 times.
✗ Branch 3 not taken.
1120 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
244 560 cpuid = hwloc_bitmap_next(obj->cpuset, cpuid)) {
245 /* Save reference to another array indexed by cpuid */
246 560 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
247 }
248 }
249
250 /*** NUMA Nodes ***/
251 70 hwloc_obj_type_t node = HWLOC_OBJ_NODE;
252 70 sys.num_nodes = hwloc_get_nbobjs_by_type(topology, node);
253 70 sys.node_masks = calloc(sys.num_nodes, sizeof(mu_cpuset_t));
254
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 70 times.
140 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
255 70 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, node, node_id);
256 70 mu_cpuset_from_hwloc_bitmap(&sys.node_masks[node_id],
257 70 obj->cpuset, topology);
258 }
259
260 70 hwloc_topology_destroy(topology);
261
262 70 return 0;
263 #else
264 return -1;
265 #endif
266 }
267
268 10 static void parse_mask_from_file(const char *filename, cpu_set_t *mask) {
269
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 if (access(filename, F_OK) == 0) {
270 enum { BUF_LEN = CPU_SETSIZE*7 };
271 char buf[BUF_LEN];
272 10 FILE *fd = fopen(filename, "r");
273
274
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if (!fgets(buf, BUF_LEN, fd)) {
275 fatal("cannot read %s\n", filename);
276 }
277 10 fclose(fd);
278
279 10 size_t len = strlen(buf);
280
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (buf[len - 1] == '\n')
281 10 buf[len - 1] = '\0';
282
283 10 mu_parse_mask(buf, mask);
284 }
285 10 }
286
287 8 static int parse_int_from_file(const char *filename) {
288 8 int value = -1;
289
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (access(filename, F_OK) == 0) {
290 enum { BUF_LEN = 16 };
291 char buf[BUF_LEN];
292 8 FILE *fd = fopen(filename, "r");
293
294
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!fgets(buf, BUF_LEN, fd)) {
295 fatal("cannot read %s\n", filename);
296 }
297 8 fclose(fd);
298
299 8 value = strtol(buf, NULL, 10);
300 }
301 8 return value;
302 }
303
304 15 static int cmp_mu_cpuset(const void *a, const void *b) {
305 15 const mu_cpuset_t *set_a = a;
306 15 const mu_cpuset_t *set_b = b;
307
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (set_a->count == 0) return 1;
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (set_b->count == 0) return -1;
310 15 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 26 times.
✓ Branch 2 taken 1 times.
27 while ((d = readdir(dir))) {
346
3/4
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 10 times.
26 if (d && d->d_type == DT_DIR
347
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
16 && strncmp(d->d_name, "cpu", 3) == 0
348
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 && isdigit(d->d_name[3]) ) {
349
350 enum { SYSPATH_MAX = 64 };
351 char filename[SYSPATH_MAX];
352
353 /* Get CPU id */
354 8 int cpu_id = strtol(d->d_name+3, NULL, 10);
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 fatal_cond(cpu_id < 0, "Error parsing cpu_id");
356
357 /* Get core id */
358 8 snprintf(filename, SYSPATH_MAX, PATH_SYSTEM_CPUS
359 8 "/%.8s/topology/core_id", d->d_name);
360 8 int core_id = parse_int_from_file(filename);
361
362 /* Get core CPUs list */
363 cpu_set_t core_mask;
364 8 CPU_ZERO(&core_mask);
365 8 snprintf(filename, SYSPATH_MAX, PATH_SYSTEM_CPUS
366 8 "/%.8s/topology/thread_siblings_list", d->d_name);
367 8 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 7 times.
8 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 8 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
389
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 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 7 times.
✗ Branch 1 not taken.
7 } 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 7 int new_coreid = -1;
403 7 bool already_present = false;
404
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 7 times.
35 for (int c = 0; c < num_cores; ++c) {
405 28 mu_cpuset_t *core = &sys.core_masks_by_coreid[c];
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (core->count == 0) {
407 if (new_coreid == -1) {
408 new_coreid = c;
409 }
410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 } 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 7 times.
7 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 7 times.
✗ Branch 1 not taken.
7 if (new_coreid == -1) {
423 7 new_coreid = num_cores++;
424 7 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 7 times.
7 fatal_cond(!p, "realloc failed in %s", __func__);
427 7 sys.core_masks_by_coreid = p;
428 }
429
430 /* Finally save core mask */
431 7 mu_cpuset_t *new_core = &sys.core_masks_by_coreid[new_coreid];
432 7 mu_cpuset_from_glibc_sched_affinity(new_core, &core_mask);
433
434 /* Set boolean to do post-processing */
435 7 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 8 times.
✓ Branch 1 taken 1 times.
9 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
450 8 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
451 8 for (int cpuid = core_cpuset->first_cpuid;
452
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
16 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
453 8 cpuid = mu_get_next_cpu(core_cpuset->set, cpuid)) {
454 8 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 num_nodes = max_int(num_nodes, node_id + 1);
482 1 mu_cpuset_t *p = realloc(sys.node_masks, num_nodes*sizeof(mu_cpuset_t));
483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 fatal_cond(!p, "realloc failed");
484 1 sys.node_masks = p;
485 1 mu_cpuset_from_glibc_sched_affinity(&sys.node_masks[node_id], &node_mask);
486 }
487 }
488 }
489 1 closedir(dir);
490 }
491 1 sys.num_nodes = num_nodes;
492
493 /* Fallback if some info could not be parsed */
494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (sys.sys_mask.count == 0) {
495 int nproc_onln = sysconf(_SC_NPROCESSORS_ONLN);
496 fatal_cond(nproc_onln <= 0, "Cannot obtain system size. Contact us at "
497 PACKAGE_BUGREPORT " or configure DLB with HWLOC support.");
498 init_system(nproc_onln, nproc_onln, 1);
499 }
500 1 }
501
502 136 static void print_sys_info(void) {
503
504
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 74 times.
136 verbose(VB_AFFINITY, "System mask: %s", mu_to_str(sys.sys_mask.set));
505
506
2/2
✓ Branch 0 taken 141 times.
✓ Branch 1 taken 136 times.
277 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
507
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 125 times.
141 verbose(VB_AFFINITY, "Node %d mask: %s",
508 node_id, mu_to_str(sys.node_masks[node_id].set));
509 }
510
511
2/2
✓ Branch 0 taken 1652 times.
✓ Branch 1 taken 136 times.
1788 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
512 1652 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
513
1/2
✓ Branch 0 taken 1652 times.
✗ Branch 1 not taken.
1652 if (core_cpuset->count > 0) {
514
2/2
✓ Branch 0 taken 450 times.
✓ Branch 1 taken 1202 times.
1652 verbose(VB_AFFINITY, "Core %d mask: %s",
515 core_id, mu_to_str(core_cpuset->set));
516 }
517 }
518
519 #if DEBUG_VERSION
520 136 for (int cpuid = sys.sys_mask.first_cpuid;
521
4/4
✓ Branch 0 taken 1878 times.
✓ Branch 1 taken 135 times.
✓ Branch 2 taken 1877 times.
✓ Branch 3 taken 1 times.
2013 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
522 1877 cpuid = mu_get_next_cpu(sys.sys_mask.set, cpuid)) {
523 1877 const mu_cpuset_t *core_cpuset = sys.core_masks_by_cpuid[cpuid];
524
3/4
✓ Branch 0 taken 1739 times.
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 1739 times.
✗ Branch 3 not taken.
1877 if (core_cpuset && core_cpuset->count > 0) {
525
2/2
✓ Branch 0 taken 477 times.
✓ Branch 1 taken 1262 times.
1739 verbose(VB_AFFINITY, "CPU %d core mask: %s",
526 cpuid, mu_to_str(core_cpuset->set));
527 }
528 }
529 #endif
530 136 }
531
532
533 /*********************************************************************************/
534 /* Mask utils public functions */
535 /*********************************************************************************/
536
537 153 void mu_init( void ) {
538
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 83 times.
153 if ( !mu_initialized ) {
539 70 init_mu_struct();
540
541 #if defined IS_BGQ_MACHINE
542 enum { BGQ_NUM_CPUS = 64 };
543 enum { BGQ_NUM_CORES = 16 };
544 enum { BGQ_NUM_NODES = 1 };
545 init_system(BGQ_NUM_CPUS, BGQ_NUM_CORES, BGQ_NUM_NODES);
546 #else
547 /* Try to parse HW info from HWLOC first */
548
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
70 if (parse_hwloc() != 0) {
549 /* Fallback to system files if needed */
550 parse_system_files();
551 }
552
553 70 mu_initialized = true;
554 #endif
555 70 print_sys_info();
556 }
557 153 }
558
559 /* This function used to be declared as destructor but it may be dangerous
560 * with the OpenMP / DLB finalization at destruction time. */
561 102 void mu_finalize( void ) {
562
563 102 CPU_FREE(sys.sys_mask.set);
564
565 /* Nodes */
566
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 102 times.
207 for (unsigned int i = 0; i < sys.num_nodes; ++i) {
567 105 CPU_FREE(sys.node_masks[i].set);
568 }
569 102 free(sys.node_masks);
570
571 /* Cores per core id */
572
2/2
✓ Branch 0 taken 1116 times.
✓ Branch 1 taken 102 times.
1218 for (unsigned int i = 0; i < sys.num_cores; ++i) {
573 1116 CPU_FREE(sys.core_masks_by_coreid[i].set);
574 }
575 102 free(sys.core_masks_by_coreid);
576
577 /* Cores per CPU id (just references) */
578 102 free(sys.core_masks_by_cpuid);
579
580 102 sys = (const mu_system_loc_t) {};
581 102 mu_initialized = false;
582 102 mu_cpuset_setsize = CPU_SETSIZE;
583 102 mu_cpuset_alloc_size = CPU_ALLOC_SIZE(CPU_SETSIZE);
584 102 mu_cpuset_num_ulongs = CPU_ALLOC_SIZE(CPU_SETSIZE) / sizeof(unsigned long);
585 102 }
586
587 9795 int mu_get_system_size( void ) {
588
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 9770 times.
9795 if (unlikely(!mu_initialized)) mu_init();
589 9795 return sys.sys_mask.last_cpuid + 1;
590 }
591
592 144 void mu_get_system_mask(cpu_set_t *mask) {
593
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 142 times.
144 if (unlikely(!mu_initialized)) mu_init();
594 144 CPU_ZERO(mask);
595 144 memcpy(mask, sys.sys_mask.set, mu_cpuset_alloc_size);
596 144 }
597
598 8 int mu_get_system_hwthreads_per_core(void) {
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (unlikely(!mu_initialized)) mu_init();
600
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 fatal_cond(sys.core_masks_by_coreid[0].count == 0,
601 "Number of hardware threads per core is 0. Please report bug at " PACKAGE_BUGREPORT);
602 8 return sys.core_masks_by_coreid[0].count;
603 }
604
605 77 bool mu_system_has_smt(void) {
606
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 if (unlikely(!mu_initialized)) mu_init();
607 77 return sys.core_masks_by_coreid[0].count > 1;
608 }
609
610 1 int mu_get_num_cores(void) {
611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (unlikely(!mu_initialized)) mu_init();
612
613 /* Not likely, but sys.num_cores may not be the number of valid cores */
614 1 int num_cores = 0;
615
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
616
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (sys.core_masks_by_coreid[core_id].count > 0) {
617 8 ++num_cores;
618 }
619 }
620
621 1 return num_cores;
622 }
623
624 11972 int mu_get_core_id(int cpuid) {
625
626
4/4
✓ Branch 0 taken 11954 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 11951 times.
11972 if (cpuid < 0 || (unsigned)cpuid >= sys.num_cpus) return -1;
627
628
2/2
✓ Branch 0 taken 122760 times.
✓ Branch 1 taken 1 times.
122761 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
629
5/6
✓ Branch 0 taken 122760 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11950 times.
✓ Branch 3 taken 110810 times.
✓ Branch 4 taken 11950 times.
✓ Branch 5 taken 110810 times.
122760 if (CPU_ISSET_S(cpuid, mu_cpuset_alloc_size,
630 sys.core_masks_by_coreid[core_id].set)) {
631 11950 return core_id;
632 }
633 }
634
635 1 return -1;
636 }
637
638 394 const mu_cpuset_t* mu_get_core_mask(int cpuid) {
639
640
3/4
✓ Branch 0 taken 394 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 392 times.
394 if (cpuid < 0 || (unsigned)cpuid >= sys.num_cpus) return NULL;
641
642 392 return sys.core_masks_by_cpuid[cpuid];
643 }
644
645 45 const mu_cpuset_t* mu_get_core_mask_by_coreid(int core_id) {
646
647
2/4
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 45 times.
45 if (core_id < 0 || (unsigned)core_id >= sys.num_cores) return NULL;
648
649 45 return &sys.core_masks_by_coreid[core_id];
650 }
651
652 /* Return Mask of full NUMA nodes covering at least 1 CPU of cpuset:
653 * e.g.:
654 * node0: [0-3]
655 * node1: [4-7]
656 * cpuset: [1-7]
657 * returns [0-7]
658 */
659 42 void mu_get_nodes_intersecting_with_cpuset(cpu_set_t *node_set, const cpu_set_t *cpuset) {
660
661 42 CPU_ZERO(node_set);
662
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 42 times.
86 for (unsigned int i=0; i<sys.num_nodes; ++i) {
663 cpu_set_t intxn;
664
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);
665
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 5 times.
44 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
666
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);
667 }
668 }
669 42 }
670
671 /* Return Mask of full NUMA nodes containing all CPUs in cpuset:
672 * e.g.:
673 * node0: [0-3]
674 * node1: [4-7]
675 * cpuset: [1-7]
676 * returns [4-7]
677 */
678 2 void mu_get_nodes_subset_of_cpuset(cpu_set_t *node_set, const cpu_set_t *cpuset) {
679
680 2 CPU_ZERO(node_set);
681
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (unsigned int i=0; i<sys.num_nodes; ++i) {
682
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (mu_is_subset(sys.node_masks[i].set, cpuset)) {
683
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);
684 }
685 }
686 2 }
687
688 /* Return Mask of cores covering at least 1 CPU of cpuset:
689 * e.g.:
690 * node0: [0-1]
691 * node1: [2-3]
692 * cpuset: [1-3]
693 * returns [0-3]
694 */
695 5 void mu_get_cores_intersecting_with_cpuset(cpu_set_t *core_set, const cpu_set_t *cpuset) {
696 5 CPU_ZERO(core_set);
697
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) {
698 20 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
699 cpu_set_t intxn;
700
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);
701
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 11 times.
20 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
702
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);
703 }
704 }
705 5 }
706
707 /* Return Mask of cores containing all CPUs in cpuset:
708 * e.g.:
709 * core0: [0-1]
710 * core1: [2-3]
711 * cpuset: [1-3]
712 * returns [2-3]
713 */
714 5 void mu_get_cores_subset_of_cpuset(cpu_set_t *core_set, const cpu_set_t *cpuset) {
715 5 CPU_ZERO(core_set);
716
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) {
717 20 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
718
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 14 times.
20 if (mu_is_subset(core_cpuset->set, cpuset)) {
719
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);
720 }
721 }
722 5 }
723
724 /* Return the next enabled CPU in mask which pertains to the next core after
725 * prev_cpu, or -1 if not found. */
726 44 int mu_get_cpu_next_core(const cpu_set_t *mask, int prev_cpu) {
727
728
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 43 times.
44 if (unlikely(prev_cpu < -1)) return -1;
729
730 43 int prev_core = mu_get_core_id(prev_cpu);
731 43 int next_cpu = mu_get_next_cpu(mask, prev_cpu);
732 43 int next_core = mu_get_core_id(next_cpu);
733
734 43 while (next_cpu != -1
735
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) {
736 32 next_cpu = mu_get_next_cpu(mask, next_cpu);
737 32 next_core = mu_get_core_id(next_cpu);
738 }
739
740
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;
741 }
742
743 /* We define as "complete" those cores that all the CPUs defined by
744 * sys.core_masks_by_coreid are enabled. */
745
746 /* Return the number of complete cores in the mask.
747 * e.g.:
748 * core0: [0-1]
749 * core1: [2-3]
750 * core2: [4-5]
751 * cpuset: [0-4]
752 * returns 2
753 */
754 26 int mu_count_cores(const cpu_set_t *mask) {
755
756 26 int cores_count = 0;
757
758
2/2
✓ Branch 0 taken 332 times.
✓ Branch 1 taken 26 times.
358 for (unsigned int coreid = 0; coreid < sys.num_cores; coreid++) {
759 // Check if we have the complete set of CPUs form the core
760
2/2
✓ Branch 1 taken 154 times.
✓ Branch 2 taken 178 times.
332 if (mu_is_subset(sys.core_masks_by_coreid[coreid].set, mask)) {
761 154 cores_count++;
762 }
763 }
764
765 26 return cores_count;
766 }
767
768 /* Return the number of complete cores in the mask.
769 * e.g.:
770 * core0: [0-1]
771 * core1: [2-3]
772 * core2: [4-5]
773 * cpuset: [0-4]
774 * returns 3
775 */
776 6 int mu_count_cores_intersecting_with_cpuset(const cpu_set_t *mask) {
777
778 6 int cores_count = 0;
779
780
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) {
781 60 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
782 cpu_set_t intxn;
783
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);
784
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 28 times.
60 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
785 32 cores_count++;
786 }
787 }
788
789 6 return cores_count;
790 }
791
792 /* Return the id of the last complete core in the mask if any, otherwise return -1.
793 * e.g.:
794 * core0: [0-1]
795 * core1: [2-3]
796 * core2: [4-5]
797 * cpuset: [0-3]
798 * returns 1 (node1)
799 */
800 24 int mu_get_last_coreid(const cpu_set_t *mask){
801
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 2 times.
170 for (int coreid = sys.num_cores-1; coreid >= 0 ; coreid--) {
802 // Check if we have the complete set of CPUs form the core
803
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)) {
804 22 return coreid;
805 }
806 }
807
808 2 return -1;
809 }
810
811 /* Disables the CPUs of the last complete core in the mask and returns its
812 * coreid if any, otherwise return -1.
813 * e.g.:
814 * core0: [0-1]
815 * core1: [2-3]
816 * core2: [4-5]
817 * cpuset: [2-5]
818 * returns 2 (node2)
819 * updated cpuset: [2-3]
820 */
821 22 int mu_take_last_coreid(cpu_set_t *mask) {
822 22 int last_coreid = mu_get_last_coreid(mask);
823
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
22 if (last_coreid == -1) return -1;
824 20 mu_xor(mask, mask, sys.core_masks_by_coreid[last_coreid].set);
825 20 return last_coreid;
826 }
827
828 /* Enables all the CPUs of the core
829 * e.g.:
830 * core0: [0-1]
831 * core1: [2-3]
832 * core2: [4-5]
833 * cpuset: []
834 * coreid: 1
835 * updated cpuset: [2-3]
836 */
837 2 void mu_set_core(cpu_set_t *mask, int coreid){
838 2 mu_or(mask, mask, sys.core_masks_by_coreid[coreid].set);
839 2 }
840
841 /* Disables all the CPUs of the core
842 * e.g.:
843 * core0: [0-1]
844 * core1: [2-3]
845 * core2: [4-5]
846 * cpuset: [0-5]
847 * coreid: 1
848 * updated cpuset: [0-1,4-5]
849 */
850 2 void mu_unset_core(cpu_set_t *mask, int coreid){
851 2 mu_subtract(mask, mask, sys.core_masks_by_coreid[coreid].set);
852 2 }
853
854 /* Basic mask utils functions that do not need to read system's topology,
855 * i.e., mostly mask operations */
856
857 2 void mu_zero(cpu_set_t *result) {
858 2 CPU_ZERO_S(mu_cpuset_alloc_size, result);
859 2 }
860
861 13 void mu_and(cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
862
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 13 times.
41 CPU_AND_S(mu_cpuset_alloc_size, result, mask1, mask2);
863 13 }
864
865 43 void mu_or(cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
866
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 43 times.
101 CPU_OR_S(mu_cpuset_alloc_size, result, mask1, mask2);
867 43 }
868
869 20 void mu_xor (cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
870
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 CPU_XOR_S(mu_cpuset_alloc_size, result, mask1, mask2);
871 20 }
872
873 4 bool mu_equal(const cpu_set_t *mask1, const cpu_set_t *mask2) {
874 4 return CPU_EQUAL_S(mu_cpuset_alloc_size, mask1, mask2) != 0;
875 }
876
877 /* Returns true is all bits in subset are set in superset */
878 652 bool mu_is_subset(const cpu_set_t *subset, const cpu_set_t *superset) {
879 // The condition is true if the intersection is identical to subset
880 cpu_set_t intxn;
881
2/2
✓ Branch 0 taken 787 times.
✓ Branch 1 taken 652 times.
1439 CPU_AND_S(mu_cpuset_alloc_size, &intxn, subset, superset);
882 652 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset);
883 }
884
885 /* Returns true is all bits in superset are set in subset */
886 9 bool mu_is_superset(const cpu_set_t *superset, const cpu_set_t *subset) {
887 // The condition is true if the intersection is identical to subset
888 cpu_set_t intxn;
889
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 9 times.
153 CPU_AND_S(mu_cpuset_alloc_size, &intxn, superset, subset);
890 9 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset);
891 }
892
893 /* Returns true is all bits in subset are set in superset and they're not equal */
894 16 bool mu_is_proper_subset(const cpu_set_t *subset, const cpu_set_t *superset) {
895 cpu_set_t intxn;
896
2/2
✓ Branch 0 taken 151 times.
✓ Branch 1 taken 16 times.
167 CPU_AND_S(mu_cpuset_alloc_size, &intxn, subset, superset);
897 16 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset)
898
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 3 times.
16 && !CPU_EQUAL_S(mu_cpuset_alloc_size, subset, superset);
899 }
900
901 /* Returns true is all bits in superset are set in subset and they're not equal */
902 19 bool mu_is_proper_superset(const cpu_set_t *superset, const cpu_set_t *subset) {
903 cpu_set_t intxn;
904
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 19 times.
173 CPU_AND_S(mu_cpuset_alloc_size, &intxn, superset, subset);
905 19 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset)
906
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 3 times.
19 && !CPU_EQUAL_S(mu_cpuset_alloc_size, superset, subset);
907 }
908
909 /* Return true if any bit is present in both sets */
910 23 bool mu_intersects(const cpu_set_t *mask1, const cpu_set_t *mask2) {
911 cpu_set_t intxn;
912
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 23 times.
181 CPU_AND_S(mu_cpuset_alloc_size, &intxn, mask1, mask2);
913 23 return CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0;
914 }
915
916 /* Return the number of bits set in mask */
917 97 int mu_count(const cpu_set_t *mask) {
918 97 return CPU_COUNT_S(mu_cpuset_alloc_size, mask);
919 }
920
921 /* Return the minuend after subtracting the bits in subtrahend */
922 750 void mu_subtract(cpu_set_t *result, const cpu_set_t *minuend, const cpu_set_t *subtrahend) {
923 cpu_set_t xor;
924
2/2
✓ Branch 0 taken 810 times.
✓ Branch 1 taken 750 times.
1560 CPU_XOR_S(mu_cpuset_alloc_size, &xor, minuend, subtrahend);
925
2/2
✓ Branch 0 taken 810 times.
✓ Branch 1 taken 750 times.
1560 CPU_AND_S(mu_cpuset_alloc_size, result, minuend, &xor);
926 750 }
927
928 /* Return the one and only enabled CPU in mask, or -1 if count != 1 */
929 8 int mu_get_single_cpu(const cpu_set_t *mask) {
930
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 5 times.
8 if (CPU_COUNT_S(mu_cpuset_alloc_size, mask) == 1) {
931 3 return mu_get_first_cpu(mask);
932 }
933 5 return -1;
934 }
935
936 /* some of the following functions have been inspired by:
937 * https://github.com/open-mpi/hwloc/blob/master/hwloc/bitmap.c */
938
939 /* Return the first enabled CPU in mask, or -1 if mask is empty */
940 4579 int mu_get_first_cpu(const cpu_set_t *mask) {
941
942
2/2
✓ Branch 0 taken 5825 times.
✓ Branch 1 taken 51 times.
5876 for (unsigned int i = 0; i < mu_cpuset_num_ulongs; ++i) {
943 5825 unsigned long bits = mask->__bits[i];
944
2/2
✓ Branch 0 taken 4528 times.
✓ Branch 1 taken 1297 times.
5825 if (bits) {
945 4528 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
946 }
947 }
948
949 51 return -1;
950 }
951
952 /* Return the last enabled CPU in mask, or -1 if mask is empty */
953 1263 int mu_get_last_cpu(const cpu_set_t *mask) {
954
955
2/2
✓ Branch 0 taken 2767 times.
✓ Branch 1 taken 3 times.
2770 for (unsigned int i = mu_cpuset_num_ulongs; i-- > 0; ) {
956 2767 unsigned long bits = mask->__bits[i];
957
2/2
✓ Branch 0 taken 1260 times.
✓ Branch 1 taken 1507 times.
2767 if (bits) {
958 /* glibc does not provide a fls function, there are more optimal
959 * solutions, but this function is not that critical */
960 1260 int cpuid = CPUS_PER_ULONG * i;
961
2/2
✓ Branch 0 taken 28895 times.
✓ Branch 1 taken 1260 times.
30155 while (bits >>= 1) {
962 28895 ++cpuid;
963 }
964 1260 return cpuid;
965 }
966 }
967
968 3 return -1;
969 }
970
971 /* Return the next enabled CPU in mask after prev, or -1 if not found */
972 7985 int mu_get_next_cpu(const cpu_set_t *mask, int prev) {
973
974
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7984 times.
7985 if (unlikely(prev < -1)) return -1;
975
976 7984 for (unsigned int i = (prev + 1) / CPUS_PER_ULONG;
977
2/2
✓ Branch 0 taken 9976 times.
✓ Branch 1 taken 3111 times.
13087 i < mu_cpuset_num_ulongs; ++i) {
978 9976 unsigned long bits = mask->__bits[i];
979
980 /* mask bitmap only if previous cpu belong to current iteration */
981
4/4
✓ Branch 0 taken 9974 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7898 times.
✓ Branch 3 taken 2076 times.
9976 if (prev >= 0 && (unsigned)prev / CPUS_PER_ULONG == i) {
982 7898 bits &= ULONG_MAX << (prev % CPUS_PER_ULONG + 1);
983 }
984
985
2/2
✓ Branch 0 taken 4873 times.
✓ Branch 1 taken 5103 times.
9976 if (bits) {
986 4873 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
987 }
988 }
989
990 3111 return -1;
991 }
992
993 /* Return the next unset CPU in mask after prev, or -1 if not found */
994 1306 int mu_get_next_unset(const cpu_set_t *mask, int prev) {
995
996
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1305 times.
1306 if (unlikely(prev < -1)) return -1;
997
998 1305 for (unsigned int i = (prev + 1) / CPUS_PER_ULONG;
999
2/2
✓ Branch 0 taken 1354 times.
✓ Branch 1 taken 21 times.
1375 i < mu_cpuset_num_ulongs; ++i) {
1000 1354 unsigned long bits = ~(mask->__bits[i]);
1001
1002 /* mask bitmap only if previous cpu belong to current iteration */
1003
4/4
✓ Branch 0 taken 1353 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1284 times.
✓ Branch 3 taken 69 times.
1354 if (prev >= 0 && (unsigned)prev / CPUS_PER_ULONG == i) {
1004 1284 bits &= ULONG_MAX << (prev % CPUS_PER_ULONG + 1);
1005 }
1006
1007
2/2
✓ Branch 0 taken 1284 times.
✓ Branch 1 taken 70 times.
1354 if (bits) {
1008 1284 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
1009 }
1010 }
1011
1012 21 return -1;
1013 }
1014
1015 // mu_to_str and mu_parse_mask functions are used by DLB utilities
1016 // We export their dynamic symbols to avoid code duplication,
1017 // although they do not belong to the public API
1018 DLB_EXPORT_SYMBOL
1019 1261 const char* mu_to_str( const cpu_set_t *mask ) {
1020
1021 static __thread char buffer[CPU_SETSIZE*4];
1022 1261 char *b = buffer;
1023 1261 *(b++) = '[';
1024 1261 bool entry_made = false;
1025
2/2
✓ Branch 1 taken 1292 times.
✓ Branch 2 taken 1261 times.
2553 for (int cpuid = mu_get_first_cpu(mask); cpuid >= 0;
1026 1292 cpuid = mu_get_next_cpu(mask, cpuid)) {
1027
1028 /* Find interval distance */
1029 1292 int next_unset = mu_get_next_unset(mask, cpuid);
1030 2566 int distance = next_unset > 0 ? next_unset - 1 - cpuid
1031
2/2
✓ Branch 0 taken 1274 times.
✓ Branch 1 taken 18 times.
1292 : mu_get_last_cpu(mask) - cpuid;
1032
1033 /* Add ',' separator for subsequent entries */
1034
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 1230 times.
1292 if (entry_made) {
1035 62 *(b++) = ',';
1036 } else {
1037 1230 entry_made = true;
1038 }
1039
1040 /* Write element, pair or range */
1041
2/2
✓ Branch 0 taken 972 times.
✓ Branch 1 taken 320 times.
1292 if (distance == 0) {
1042 972 b += sprintf(b, "%d", cpuid);
1043
2/2
✓ Branch 0 taken 149 times.
✓ Branch 1 taken 171 times.
320 } else if (distance == 1) {
1044 149 b += sprintf(b, "%d,%d", cpuid, cpuid+1);
1045 149 ++cpuid;
1046 } else {
1047 171 b += sprintf(b, "%d-%d", cpuid, cpuid+distance);
1048 171 cpuid += distance;
1049 }
1050 }
1051 1261 *(b++) = ']';
1052 1261 *b = '\0';
1053
1054 1261 return buffer;
1055 }
1056
1057 39 static void parse_64_bits_mask(cpu_set_t *mask, unsigned int offset, const char *str, int base) {
1058 39 unsigned long long number = strtoull(str, NULL, base);
1059 39 unsigned int i = offset;
1060
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) {
1061
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 278 times.
344 if (number & 1) {
1062
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 CPU_SET(i, mask);
1063 }
1064 344 ++i;
1065 344 number = number >> 1;
1066 }
1067 39 }
1068
1069 DLB_EXPORT_SYMBOL
1070 247 void mu_parse_mask( const char *str, cpu_set_t *mask ) {
1071
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 247 times.
250 if (!str) return;
1072
1073 247 size_t str_len = strnlen(str, CPU_SETSIZE+1);
1074
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 246 times.
247 if (str_len > CPU_SETSIZE) return;
1075
1076 246 CPU_ZERO( mask );
1077
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 244 times.
246 if (str_len == 0) return;
1078
1079 regex_t regex_bitmask;
1080 regex_t regex_hexmask;
1081 regex_t regex_range;
1082 regex_t old_regex_bitmask;
1083
1084 /* Compile regular expressions */
1085
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
244 if ( regcomp(&regex_bitmask, "^0[bB][0-1]+$", REG_EXTENDED|REG_NOSUB) ) {
1086 fatal0( "Could not compile regex");
1087 }
1088
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
244 if ( regcomp(&regex_hexmask, "^0[xX][0-9,a-f,A-F]+$", REG_EXTENDED|REG_NOSUB) ) {
1089 fatal0( "Could not compile regex");
1090 }
1091
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
244 if ( regcomp(&regex_range, "^[0-9,-]+$", REG_EXTENDED|REG_NOSUB) ) {
1092 fatal0( "Could not compile regex");
1093 }
1094
1095 /***** Deprecated *****/
1096
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
244 if ( regcomp(&old_regex_bitmask, "^[0-1][0-1]+[bB]$", REG_EXTENDED|REG_NOSUB) ) {
1097 fatal0( "Could not compile regex");
1098 }
1099 /* Regular expression matches OLD bitmask, e.g.: 11110011b */
1100
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 241 times.
244 if ( !regexec(&old_regex_bitmask, str, 0, NULL, 0) ) {
1101 3 warning("The binary form xxxxb is deprecated, please use 0bxxxx.");
1102 // Parse
1103
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
22 for (unsigned int i=0; i<str_len; i++) {
1104
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 ) {
1105
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 CPU_SET( i, mask );
1106 }
1107 }
1108 }
1109 /**********************/
1110
1111 /* Regular expression matches bitmask, e.g.: 0b11100001 */
1112
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 232 times.
241 else if ( !regexec(&regex_bitmask, str, 0, NULL, 0) ) {
1113 /* Ignore '0b' */
1114 9 str += 2;
1115
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (strlen(str) <= 64) {
1116 6 parse_64_bits_mask(mask, 0, str, 2);
1117 } else {
1118 /* parse in chunks of 64 bits */
1119 3 char *str_copy = strdup(str);
1120 char *start_ptr;
1121 3 char *end_ptr = str_copy + strlen(str_copy);
1122 3 unsigned int offset = 0;
1123 do {
1124
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 start_ptr = strlen(str_copy) < 64 ? str_copy : end_ptr - 64;
1125 12 parse_64_bits_mask(mask, offset, start_ptr, 2);
1126 12 offset += 64;
1127 12 end_ptr = start_ptr;
1128 12 *end_ptr = '\0';
1129
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 } while (strlen(str_copy) > 0);
1130 3 free(str_copy);
1131 }
1132 }
1133
1134 /* Regular expression matches hexmask, e.g.: 0xE1 */
1135
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 222 times.
232 else if ( !regexec(&regex_hexmask, str, 0, NULL, 0) ) {
1136 /* Ignore '0x' */
1137 10 str += 2;
1138
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
10 if (strlen(str) <= 16) {
1139 7 parse_64_bits_mask(mask, 0, str, 16);
1140 } else {
1141 /* parse in chunks of 64 bits (16 hex digits) */
1142 3 char *str_copy = strdup(str);
1143 char *start_ptr;
1144 3 char *end_ptr = str_copy + strlen(str_copy);
1145 3 unsigned int offset = 0;
1146 do {
1147
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 start_ptr = strlen(str_copy) < 16 ? str_copy : end_ptr - 16;
1148 14 parse_64_bits_mask(mask, offset, start_ptr, 16);
1149 14 offset += 64;
1150 14 end_ptr = start_ptr;
1151 14 *end_ptr = '\0';
1152
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
14 } while (strlen(str_copy) > 0);
1153 3 free(str_copy);
1154 }
1155 }
1156
1157 /* Regular expression matches range, e.g.: 0,5-7 */
1158
1/2
✓ Branch 1 taken 222 times.
✗ Branch 2 not taken.
222 else if ( !regexec(&regex_range, str, 0, NULL, 0) ) {
1159 // Parse
1160 222 const char *ptr = str;
1161 char *endptr;
1162
2/2
✓ Branch 0 taken 338 times.
✓ Branch 1 taken 222 times.
560 while ( ptr < str+strlen(str) ) {
1163 // Discard junk at the left
1164
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 316 times.
338 if ( !isdigit(*ptr) ) { ptr++; continue; }
1165
1166 316 unsigned long start_ = strtoul( ptr, &endptr, 10 );
1167 316 unsigned long start = start_ < mu_cpuset_setsize ? start_ : mu_cpuset_setsize;
1168 316 ptr = endptr;
1169
1170 // Single element
1171
5/6
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 95 times.
✓ Branch 3 taken 149 times.
✓ Branch 4 taken 167 times.
✗ Branch 5 not taken.
316 if ( (*ptr == ',' || *ptr == '\0') && start < mu_cpuset_setsize ) {
1172
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
167 CPU_SET( start, mask );
1173 167 ptr++;
1174 167 continue;
1175 }
1176 // Range
1177
1/2
✓ Branch 0 taken 149 times.
✗ Branch 1 not taken.
149 else if ( *ptr == '-' ) {
1178 // Discard '-' and possible junk
1179 149 ptr++;
1180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 149 times.
149 if ( !isdigit(*ptr) ) { ptr++; continue; }
1181
1182 149 unsigned long end_ = strtoul( ptr, &endptr, 10 );
1183 149 unsigned long end = end_ < mu_cpuset_setsize ? end_ : mu_cpuset_setsize;
1184 149 ptr = endptr;
1185
1186 // Valid range
1187
1/2
✓ Branch 0 taken 149 times.
✗ Branch 1 not taken.
149 if ( end > start ) {
1188
3/4
✓ Branch 0 taken 1955 times.
✓ Branch 1 taken 149 times.
✓ Branch 2 taken 1955 times.
✗ Branch 3 not taken.
2104 for ( unsigned long i=start; i<=end && i<mu_cpuset_setsize; i++ ) {
1189
1/2
✓ Branch 0 taken 1955 times.
✗ Branch 1 not taken.
1955 CPU_SET( i, mask );
1190 }
1191 }
1192 149 continue;
1193 }
1194 // Unexpected token
1195 else { }
1196 }
1197 }
1198 /* Regular expression does not match */
1199 else { }
1200
1201 244 regfree(&regex_bitmask);
1202 244 regfree(&regex_hexmask);
1203 244 regfree(&regex_range);
1204 244 regfree(&old_regex_bitmask);
1205
1206
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 244 times.
244 if ( CPU_COUNT(mask) == 0 ) {
1207 warning( "Parsed mask \"%s\" does not seem to be a valid mask\n", str );
1208 }
1209 }
1210
1211 /* Equivalent to mu_to_str, but generate quoted string in str up to namelen-1 bytes */
1212 8 void mu_get_quoted_mask(const cpu_set_t *mask, char *str, size_t namelen) {
1213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (namelen < 2)
1214 return;
1215
1216 8 char *b = str;
1217 8 *(b++) = '"';
1218 8 size_t bytes = 1;
1219 8 bool entry_made = false;
1220
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
16 for (int cpuid = mu_get_first_cpu(mask); cpuid >= 0;
1221 8 cpuid = mu_get_next_cpu(mask, cpuid)) {
1222
1223 /* Find interval distance */
1224 8 int next_unset = mu_get_next_unset(mask, cpuid);
1225 14 int distance = next_unset > 0 ? next_unset - 1 - cpuid
1226
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 : mu_get_last_cpu(mask) - cpuid;
1227
1228 /* Add ',' separator for subsequent entries */
1229
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (entry_made) {
1230
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (bytes+1 < namelen) {
1231 1 *(b++) = ',';
1232 1 ++bytes;
1233 }
1234 } else {
1235 7 entry_made = true;
1236 }
1237
1238 /* Write element, pair or range */
1239
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if (distance == 0) {
1240 3 int len = snprintf(NULL, 0, "%d", cpuid);
1241
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (bytes+len < namelen) {
1242 3 b += sprintf(b, "%d", cpuid);
1243 3 bytes += len;
1244 }
1245
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 } else if (distance == 1) {
1246 3 int len = snprintf(NULL, 0, "%d,%d", cpuid, cpuid+1);
1247
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (bytes+len < namelen) {
1248 3 b += sprintf(b, "%d,%d", cpuid, cpuid+1);
1249 3 bytes += len;
1250 3 ++cpuid;
1251 }
1252 } else {
1253 2 int len = snprintf(NULL, 0, "%d-%d", cpuid, cpuid+distance);
1254
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (bytes+len < namelen) {
1255 2 b += sprintf(b, "%d-%d", cpuid, cpuid+distance);
1256 2 bytes += len;
1257 2 cpuid += distance;
1258 }
1259 }
1260 }
1261
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (bytes+1 < namelen) {
1262 8 *(b++) = '"';
1263 8 ++bytes;
1264 }
1265 8 *b = '\0';
1266 }
1267
1268 1 char * mu_parse_to_slurm_format(const cpu_set_t *mask) {
1269 1 char *str = malloc((mu_cpuset_setsize >> 2) + 3);
1270 if (str < 0)
1271 return NULL;
1272 1 unsigned int offset = 2;
1273 1 unsigned long long val = 0;
1274 1 const int threshold = 4;
1275 1 sprintf(str, "0x");
1276
2/2
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 1 times.
131 for (int cpuid = mu_get_last_cpu(mask); cpuid >= 0; --cpuid) {
1277
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)) {
1278 9 val |= 1 << (cpuid % threshold);
1279 }
1280
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) {
1281 32 sprintf(str+offset, "%llx", val);
1282 32 val = 0;
1283 32 offset++;
1284 }
1285 }
1286 1 sprintf(str+offset, "%llx", val);
1287 1 return str;
1288 }
1289
1290 4 bool mu_equivalent_masks(const char *str1, const char *str2) {
1291 cpu_set_t mask1, mask2;
1292 4 mu_parse_mask(str1, &mask1);
1293 4 mu_parse_mask(str2, &mask2);
1294 4 return CPU_EQUAL(&mask1, &mask2);
1295 }
1296
1297
1298 3164 static int cmp_cpuids(cpuid_t cpuid1, cpuid_t cpuid2) {
1299 3164 int cpu1_core_id = mu_get_core_id(cpuid1);
1300 3164 int cpu2_core_id = mu_get_core_id(cpuid2);
1301
2/2
✓ Branch 0 taken 480 times.
✓ Branch 1 taken 2684 times.
3164 if (cpu1_core_id == cpu2_core_id) {
1302 480 return cpuid1 - cpuid2;
1303 } else {
1304 2684 return cpu1_core_id - cpu2_core_id;
1305 }
1306 }
1307
1308 /* Compare CPUs so that:
1309 * - owned CPUs first, in ascending order
1310 * - non-owned later, starting from the first owned, then ascending
1311 * e.g.: system: [0-7], owned: [3-5]
1312 * cpu_list = {4,5,6,7,0,1,2,3}
1313 */
1314 1543 int mu_cmp_cpuids_by_ownership(const void *cpuid1, const void *cpuid2, void *mask) {
1315 /* Expand arguments */
1316 1543 cpuid_t _cpuid1 = *(cpuid_t*)cpuid1;
1317 1543 cpuid_t _cpuid2 = *(cpuid_t*)cpuid2;
1318 1543 cpu_set_t *process_mask = mask;
1319
1320
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)) {
1321
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)) {
1322 /* both CPUs are owned: ascending order */
1323 529 return cmp_cpuids(_cpuid1, _cpuid2);
1324 } else {
1325 /* cpuid2 is NOT owned and cpuid1 IS */
1326 208 return -1;
1327 }
1328 } else {
1329
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)) {
1330 /* cpuid2 IS owned and cpuid1 is NOT */
1331 223 return 1;
1332 } else {
1333 /* none is owned */
1334 583 int first_cpu = mu_get_first_cpu(process_mask);
1335 583 int first_core = mu_get_core_id(first_cpu);
1336 583 int cpu1_core_id = mu_get_core_id(_cpuid1);
1337 583 int cpu2_core_id = mu_get_core_id(_cpuid2);
1338
2/2
✓ Branch 0 taken 351 times.
✓ Branch 1 taken 232 times.
583 if ((cpu1_core_id > first_core
1339
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 304 times.
351 && cpu2_core_id > first_core)
1340
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 47 times.
279 || (cpu1_core_id < first_core
1341
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 58 times.
232 && cpu2_core_id < first_core)) {
1342 /* Both CPUs are either before or after the process mask */
1343 478 return cmp_cpuids(_cpuid1, _cpuid2);
1344 } else {
1345 /* Compare with respect to process mask */
1346 105 return cmp_cpuids(first_cpu, _cpuid1);
1347 }
1348 }
1349 }
1350 }
1351
1352 /* Compare CPUs so that:
1353 * - CPUs are sorted according to the affinity array:
1354 * * affinity: array of cpu_set_t, each position represents a level
1355 * in the affinity, the last position is an empty cpu set.
1356 * (PRE: each affinity level is a superset of the previous level mask)
1357 * - Sorted by affinity level in ascending order
1358 * - non-owned later, starting from the first owned, then ascending
1359 * e.g.: affinity: {{6-7}, {4-7}, {0-7}, {}}
1360 * sorted_cpu_list = {6,7,4,5,0,1,2,3}
1361 */
1362 2783 int mu_cmp_cpuids_by_affinity(const void *cpuid1, const void *cpuid2, void *affinity) {
1363 /* Expand arguments */
1364 2783 cpuid_t _cpuid1 = *(cpuid_t*)cpuid1;
1365 2783 cpuid_t _cpuid2 = *(cpuid_t*)cpuid2;
1366 2783 cpu_set_t *_affinity = affinity;
1367
1368 /* Find affinity level of each CPU */
1369 2783 int cpu1_level = 0;
1370 2783 int cpu2_level = 0;
1371 2783 cpu_set_t *mask = _affinity;
1372
2/2
✓ Branch 1 taken 6231 times.
✓ Branch 2 taken 2783 times.
9014 while(CPU_COUNT(mask) > 0) {
1373
5/6
✓ Branch 0 taken 6231 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4028 times.
✓ Branch 3 taken 2203 times.
✓ Branch 4 taken 2203 times.
✓ Branch 5 taken 4028 times.
6231 if (!CPU_ISSET(_cpuid1, mask)) {
1374 2203 ++cpu1_level;
1375 }
1376
5/6
✓ Branch 0 taken 6231 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3957 times.
✓ Branch 3 taken 2274 times.
✓ Branch 4 taken 2274 times.
✓ Branch 5 taken 3957 times.
6231 if (!CPU_ISSET(_cpuid2, mask)) {
1377 2274 ++cpu2_level;
1378 }
1379 6231 ++mask;
1380 }
1381
1382 /* If levels differ, sort levels in ascending order */
1383
2/2
✓ Branch 0 taken 731 times.
✓ Branch 1 taken 2052 times.
2783 if (cpu1_level != cpu2_level) {
1384 731 return cpu1_level - cpu2_level;
1385 }
1386
1387 /* If both are level 0, sort in ascending order */
1388
2/2
✓ Branch 0 taken 756 times.
✓ Branch 1 taken 1296 times.
2052 if (cpu1_level == 0) {
1389 756 return cmp_cpuids(_cpuid1, _cpuid2);
1390 }
1391
1392 /* If both are level 1, sort from the first CPU in level 0 */
1393 /* e.g.: level0: [2,3], level1: [0,7] -> [4,5,6,7,0,1] */
1394
2/2
✓ Branch 0 taken 1015 times.
✓ Branch 1 taken 281 times.
1296 if (cpu1_level == 1) {
1395 1015 cpu_set_t *level0_mask = _affinity;
1396 1015 int first_cpu = mu_get_first_cpu(level0_mask);
1397 1015 int first_core = mu_get_core_id(first_cpu);
1398 1015 int cpu1_core_id = mu_get_core_id(_cpuid1);
1399 1015 int cpu2_core_id = mu_get_core_id(_cpuid2);
1400
2/2
✓ Branch 0 taken 806 times.
✓ Branch 1 taken 209 times.
1015 if ((cpu1_core_id > first_core
1401
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 790 times.
806 && cpu2_core_id > first_core)
1402
2/2
✓ Branch 0 taken 209 times.
✓ Branch 1 taken 16 times.
225 || (cpu1_core_id < first_core
1403
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 108 times.
209 && cpu2_core_id < first_core)) {
1404 /* Both CPUs are either before or after the process mask */
1405 891 return cmp_cpuids(_cpuid1, _cpuid2);
1406 } else {
1407 /* Compare with respect to process mask */
1408 124 return cmp_cpuids(first_cpu, _cpuid1);
1409 }
1410 }
1411
1412 /* TODO: compute numa distance */
1413 /* Levels 2+, sort in ascending order */
1414 281 return cmp_cpuids(_cpuid1, _cpuid2);
1415 }
1416
1417
1418 /*********************************************************************************/
1419 /* Mask utils testing functions */
1420 /*********************************************************************************/
1421
1422 2 bool mu_testing_is_initialized(void) {
1423 2 return mu_initialized;
1424 }
1425
1426 50 void mu_testing_set_sys_size(int size) {
1427 50 init_system(size, size, 1);
1428 50 print_sys_info();
1429 50 }
1430
1431 3 void mu_testing_set_sys(unsigned int num_cpus, unsigned int num_cores,
1432 unsigned int num_nodes) {
1433 3 init_system(num_cpus, num_cores, num_nodes);
1434 3 print_sys_info();
1435 3 }
1436
1437 12 void mu_testing_set_sys_masks(const cpu_set_t *sys_mask,
1438 const cpu_set_t *core_masks, unsigned int num_cores,
1439 const cpu_set_t *node_masks, unsigned int num_nodes) {
1440 12 init_system_masks(sys_mask, core_masks, num_cores, node_masks, num_nodes);
1441 12 print_sys_info();
1442 12 }
1443
1444 1 void mu_testing_init_nohwloc(void) {
1445 1 init_mu_struct();
1446 1 parse_system_files();
1447 1 mu_initialized = true;
1448 1 print_sys_info();
1449 1 }
1450