1 // SPDX-License-Identifier: GPL-2.0 2 #include <api/fs/fs.h> 3 #include "cpumap.h" 4 #include "debug.h" 5 #include "event.h" 6 #include <assert.h> 7 #include <dirent.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <linux/bitmap.h> 11 #include "asm/bug.h" 12 13 #include <linux/compiler.h> 14 #include <linux/ctype.h> 15 #include <linux/zalloc.h> 16 #include <internal/cpumap.h> 17 18 static struct perf_cpu max_cpu_num; 19 static struct perf_cpu max_present_cpu_num; 20 static int max_node_num; 21 /** 22 * The numa node X as read from /sys/devices/system/node/nodeX indexed by the 23 * CPU number. 24 */ 25 static int *cpunode_map; 26 27 bool perf_record_cpu_map_data__test_bit(int i, 28 const struct perf_record_cpu_map_data *data) 29 { 30 int bit_word32 = i / 32; 31 __u32 bit_mask32 = 1U << (i & 31); 32 int bit_word64 = i / 64; 33 __u64 bit_mask64 = ((__u64)1) << (i & 63); 34 35 return (data->mask32_data.long_size == 4) 36 ? (bit_word32 < data->mask32_data.nr) && 37 (data->mask32_data.mask[bit_word32] & bit_mask32) != 0 38 : (bit_word64 < data->mask64_data.nr) && 39 (data->mask64_data.mask[bit_word64] & bit_mask64) != 0; 40 } 41 42 /* Read ith mask value from data into the given 64-bit sized bitmap */ 43 static void perf_record_cpu_map_data__read_one_mask(const struct perf_record_cpu_map_data *data, 44 int i, unsigned long *bitmap, 45 u16 long_size) 46 { 47 #if __SIZEOF_LONG__ == 8 48 if (long_size == 4) 49 bitmap[0] = data->mask32_data.mask[i]; 50 else 51 bitmap[0] = data->mask64_data.mask[i]; 52 #else 53 if (long_size == 4) { 54 bitmap[0] = data->mask32_data.mask[i]; 55 bitmap[1] = 0; 56 } else { 57 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 58 bitmap[0] = (unsigned long)(data->mask64_data.mask[i] >> 32); 59 bitmap[1] = (unsigned long)data->mask64_data.mask[i]; 60 #else 61 bitmap[0] = (unsigned long)data->mask64_data.mask[i]; 62 bitmap[1] = (unsigned long)(data->mask64_data.mask[i] >> 32); 63 #endif 64 } 65 #endif 66 } 67 static struct perf_cpu_map *cpu_map__from_entries(const struct perf_record_cpu_map_data *data) 68 { 69 /* Snapshot nr — data is mmap'd and could change between reads */ 70 u16 nr = READ_ONCE(data->cpus_data.nr); 71 struct perf_cpu_map *map; 72 73 map = perf_cpu_map__empty_new(nr); 74 if (!map) 75 return NULL; 76 77 for (unsigned int i = 0; i < nr; i++) { 78 u16 cpu = READ_ONCE(data->cpus_data.cpu[i]); 79 /* 80 * Special treatment for -1, which is not real cpu number, 81 * and we need to use (int) -1 to initialize map[i], 82 * otherwise it would become 65535. 83 */ 84 if (cpu == (u16) -1) { 85 RC_CHK_ACCESS(map)->map[i].cpu = -1; 86 } else if (cpu < INT16_MAX) { 87 RC_CHK_ACCESS(map)->map[i].cpu = (int16_t) cpu; 88 } else { 89 pr_err("Invalid cpumap entry %u\n", cpu); 90 perf_cpu_map__put(map); 91 return NULL; 92 } 93 } 94 95 return map; 96 } 97 98 static struct perf_cpu_map *cpu_map__from_mask(const struct perf_record_cpu_map_data *data) 99 { 100 DECLARE_BITMAP(local_copy, 64); 101 int weight = 0, mask_nr; 102 /* Snapshot before validation — data is mmap'd and could change */ 103 u16 long_size = READ_ONCE(data->mask32_data.long_size); 104 struct perf_cpu_map *map; 105 106 /* long_size must be 4 or 8; other values overflow cpus_per_i below */ 107 if (long_size != 4 && long_size != 8) { 108 pr_warning("WARNING: cpu_map mask: unsupported long_size %u\n", long_size); 109 return NULL; 110 } 111 112 mask_nr = READ_ONCE(data->mask32_data.nr); 113 114 for (int i = 0; i < mask_nr; i++) { 115 perf_record_cpu_map_data__read_one_mask(data, i, local_copy, long_size); 116 weight += bitmap_weight(local_copy, 64); 117 } 118 119 map = perf_cpu_map__empty_new(weight); 120 if (!map) 121 return NULL; 122 123 for (int i = 0, j = 0; i < mask_nr; i++) { 124 int cpus_per_i = (i * long_size * BITS_PER_BYTE); 125 int cpu; 126 127 perf_record_cpu_map_data__read_one_mask(data, i, local_copy, long_size); 128 for_each_set_bit(cpu, local_copy, 64) { 129 /* Guard against more set bits than the first pass counted */ 130 if (j >= weight) 131 break; 132 if (cpu + cpus_per_i < INT16_MAX) { 133 RC_CHK_ACCESS(map)->map[j++].cpu = cpu + cpus_per_i; 134 } else { 135 pr_err("Invalid cpumap entry %d\n", cpu + cpus_per_i); 136 perf_cpu_map__put(map); 137 return NULL; 138 } 139 } 140 } 141 return map; 142 143 } 144 145 static struct perf_cpu_map *cpu_map__from_range(const struct perf_record_cpu_map_data *data) 146 { 147 /* Snapshot fields — data is mmap'd and could change between reads */ 148 u16 start_cpu = READ_ONCE(data->range_cpu_data.start_cpu); 149 u16 end_cpu = READ_ONCE(data->range_cpu_data.end_cpu); 150 u16 any_cpu = READ_ONCE(data->range_cpu_data.any_cpu); 151 struct perf_cpu_map *map; 152 unsigned int i = 0; 153 154 if (end_cpu < start_cpu) { 155 pr_warning("WARNING: cpu_map range: end_cpu %u < start_cpu %u\n", 156 end_cpu, start_cpu); 157 return NULL; 158 } 159 160 /* any_cpu is boolean (0 or 1), not a count — clamp to avoid inflated nr */ 161 map = perf_cpu_map__empty_new(end_cpu - start_cpu + 1 + !!any_cpu); 162 if (!map) 163 return NULL; 164 165 if (any_cpu) 166 RC_CHK_ACCESS(map)->map[i++].cpu = -1; 167 168 for (int cpu = start_cpu; cpu <= end_cpu; 169 i++, cpu++) { 170 if (cpu < INT16_MAX) { 171 RC_CHK_ACCESS(map)->map[i].cpu = cpu; 172 } else { 173 pr_err("Invalid cpumap entry %d\n", cpu); 174 perf_cpu_map__put(map); 175 return NULL; 176 } 177 } 178 179 return map; 180 } 181 182 struct perf_cpu_map *cpu_map__new_data(const struct perf_record_cpu_map_data *data) 183 { 184 switch (data->type) { 185 case PERF_CPU_MAP__CPUS: 186 return cpu_map__from_entries(data); 187 case PERF_CPU_MAP__MASK: 188 return cpu_map__from_mask(data); 189 case PERF_CPU_MAP__RANGE_CPUS: 190 return cpu_map__from_range(data); 191 default: 192 pr_err("cpu_map__new_data unknown type %d\n", data->type); 193 return NULL; 194 } 195 } 196 197 size_t cpu_map__fprintf(struct perf_cpu_map *map, FILE *fp) 198 { 199 #define BUFSIZE 1024 200 char buf[BUFSIZE]; 201 202 cpu_map__snprint(map, buf, sizeof(buf)); 203 return fprintf(fp, "%s\n", buf); 204 #undef BUFSIZE 205 } 206 207 struct perf_cpu_map *perf_cpu_map__empty_new(int nr) 208 { 209 struct perf_cpu_map *cpus = perf_cpu_map__alloc(nr); 210 211 if (cpus != NULL) { 212 for (int i = 0; i < nr; i++) 213 RC_CHK_ACCESS(cpus)->map[i].cpu = -1; 214 } 215 216 return cpus; 217 } 218 219 struct cpu_aggr_map *cpu_aggr_map__empty_new(int nr) 220 { 221 struct cpu_aggr_map *cpus = malloc(sizeof(*cpus) + sizeof(struct aggr_cpu_id) * nr); 222 223 if (cpus != NULL) { 224 int i; 225 226 cpus->nr = nr; 227 for (i = 0; i < nr; i++) 228 cpus->map[i] = aggr_cpu_id__empty(); 229 } 230 231 return cpus; 232 } 233 234 static int cpu__get_topology_int(int cpu, const char *name, int *value) 235 { 236 char path[PATH_MAX]; 237 238 snprintf(path, PATH_MAX, 239 "devices/system/cpu/cpu%d/topology/%s", cpu, name); 240 241 return sysfs__read_int(path, value); 242 } 243 244 int cpu__get_socket_id(struct perf_cpu cpu) 245 { 246 int value, ret = cpu__get_topology_int(cpu.cpu, "physical_package_id", &value); 247 return ret ?: value; 248 } 249 250 struct aggr_cpu_id aggr_cpu_id__socket(struct perf_cpu cpu, void *data __maybe_unused) 251 { 252 struct aggr_cpu_id id = aggr_cpu_id__empty(); 253 254 id.socket = cpu__get_socket_id(cpu); 255 return id; 256 } 257 258 static int aggr_cpu_id__cmp(const void *a_pointer, const void *b_pointer) 259 { 260 struct aggr_cpu_id *a = (struct aggr_cpu_id *)a_pointer; 261 struct aggr_cpu_id *b = (struct aggr_cpu_id *)b_pointer; 262 263 if (a->node != b->node) 264 return a->node - b->node; 265 else if (a->socket != b->socket) 266 return a->socket - b->socket; 267 else if (a->die != b->die) 268 return a->die - b->die; 269 else if (a->cluster != b->cluster) 270 return a->cluster - b->cluster; 271 else if (a->cache_lvl != b->cache_lvl) 272 return a->cache_lvl - b->cache_lvl; 273 else if (a->cache != b->cache) 274 return a->cache - b->cache; 275 else if (a->core != b->core) 276 return a->core - b->core; 277 else 278 return a->thread_idx - b->thread_idx; 279 } 280 281 struct cpu_aggr_map *cpu_aggr_map__new(const struct perf_cpu_map *cpus, 282 aggr_cpu_id_get_t get_id, 283 void *data, bool needs_sort) 284 { 285 unsigned int idx; 286 struct perf_cpu cpu; 287 struct cpu_aggr_map *c = cpu_aggr_map__empty_new(perf_cpu_map__nr(cpus)); 288 289 if (!c) 290 return NULL; 291 292 /* Reset size as it may only be partially filled */ 293 c->nr = 0; 294 295 perf_cpu_map__for_each_cpu(cpu, idx, cpus) { 296 bool duplicate = false; 297 struct aggr_cpu_id cpu_id = get_id(cpu, data); 298 299 for (int j = 0; j < c->nr; j++) { 300 if (aggr_cpu_id__equal(&cpu_id, &c->map[j])) { 301 duplicate = true; 302 break; 303 } 304 } 305 if (!duplicate) { 306 c->map[c->nr] = cpu_id; 307 c->nr++; 308 } 309 } 310 /* Trim. */ 311 if (c->nr != (int)perf_cpu_map__nr(cpus)) { 312 struct cpu_aggr_map *trimmed_c = 313 realloc(c, 314 sizeof(struct cpu_aggr_map) + sizeof(struct aggr_cpu_id) * c->nr); 315 316 if (trimmed_c) 317 c = trimmed_c; 318 } 319 320 /* ensure we process id in increasing order */ 321 if (needs_sort) 322 qsort(c->map, c->nr, sizeof(struct aggr_cpu_id), aggr_cpu_id__cmp); 323 324 return c; 325 326 } 327 328 int cpu__get_die_id(struct perf_cpu cpu) 329 { 330 int value, ret = cpu__get_topology_int(cpu.cpu, "die_id", &value); 331 332 return ret ?: value; 333 } 334 335 struct aggr_cpu_id aggr_cpu_id__die(struct perf_cpu cpu, void *data) 336 { 337 struct aggr_cpu_id id; 338 int die; 339 340 die = cpu__get_die_id(cpu); 341 /* There is no die_id on legacy system. */ 342 if (die < 0) 343 die = 0; 344 345 /* 346 * die_id is relative to socket, so start 347 * with the socket ID and then add die to 348 * make a unique ID. 349 */ 350 id = aggr_cpu_id__socket(cpu, data); 351 if (aggr_cpu_id__is_empty(&id)) 352 return id; 353 354 id.die = die; 355 return id; 356 } 357 358 int cpu__get_cluster_id(struct perf_cpu cpu) 359 { 360 int value, ret = cpu__get_topology_int(cpu.cpu, "cluster_id", &value); 361 362 return ret ?: value; 363 } 364 365 struct aggr_cpu_id aggr_cpu_id__cluster(struct perf_cpu cpu, void *data) 366 { 367 int cluster = cpu__get_cluster_id(cpu); 368 struct aggr_cpu_id id; 369 370 /* There is no cluster_id on legacy system. */ 371 if (cluster < 0) 372 cluster = 0; 373 374 id = aggr_cpu_id__die(cpu, data); 375 if (aggr_cpu_id__is_empty(&id)) 376 return id; 377 378 id.cluster = cluster; 379 return id; 380 } 381 382 int cpu__get_core_id(struct perf_cpu cpu) 383 { 384 int value, ret = cpu__get_topology_int(cpu.cpu, "core_id", &value); 385 return ret ?: value; 386 } 387 388 struct aggr_cpu_id aggr_cpu_id__core(struct perf_cpu cpu, void *data) 389 { 390 struct aggr_cpu_id id; 391 int core = cpu__get_core_id(cpu); 392 393 /* aggr_cpu_id__die returns a struct with socket die, and cluster set. */ 394 id = aggr_cpu_id__cluster(cpu, data); 395 if (aggr_cpu_id__is_empty(&id)) 396 return id; 397 398 /* 399 * core_id is relative to socket and die, we need a global id. 400 * So we combine the result from cpu_map__get_die with the core id 401 */ 402 id.core = core; 403 return id; 404 405 } 406 407 struct aggr_cpu_id aggr_cpu_id__cpu(struct perf_cpu cpu, void *data) 408 { 409 struct aggr_cpu_id id; 410 411 /* aggr_cpu_id__core returns a struct with socket, die and core set. */ 412 id = aggr_cpu_id__core(cpu, data); 413 if (aggr_cpu_id__is_empty(&id)) 414 return id; 415 416 id.cpu = cpu; 417 return id; 418 419 } 420 421 struct aggr_cpu_id aggr_cpu_id__node(struct perf_cpu cpu, void *data __maybe_unused) 422 { 423 struct aggr_cpu_id id = aggr_cpu_id__empty(); 424 425 id.node = cpu__get_node(cpu); 426 return id; 427 } 428 429 struct aggr_cpu_id aggr_cpu_id__global(struct perf_cpu cpu, void *data __maybe_unused) 430 { 431 struct aggr_cpu_id id = aggr_cpu_id__empty(); 432 433 /* it always aggregates to the cpu 0 */ 434 cpu.cpu = 0; 435 id.cpu = cpu; 436 return id; 437 } 438 439 /* setup simple routines to easily access node numbers given a cpu number */ 440 static int get_max_num(char *path, int *max) 441 { 442 size_t num; 443 char *buf; 444 int err = 0; 445 446 if (filename__read_str(path, &buf, &num)) 447 return -1; 448 449 buf[num] = '\0'; 450 451 /* empty file — nothing to parse */ 452 if (num == 0) { 453 err = -1; 454 goto out; 455 } 456 457 /* start on the right, to find highest node num */ 458 while (--num) { 459 if ((buf[num] == ',') || (buf[num] == '-')) { 460 num++; 461 break; 462 } 463 } 464 if (sscanf(&buf[num], "%d", max) < 1) { 465 err = -1; 466 goto out; 467 } 468 469 /* convert from 0-based to 1-based */ 470 (*max)++; 471 472 out: 473 free(buf); 474 return err; 475 } 476 477 /* Determine highest possible cpu in the system for sparse allocation */ 478 static void set_max_cpu_num(void) 479 { 480 const char *mnt; 481 char path[PATH_MAX]; 482 int max, ret = -1; 483 484 /* set up default */ 485 max_cpu_num.cpu = 4096; 486 max_present_cpu_num.cpu = 4096; 487 488 mnt = sysfs__mountpoint(); 489 if (!mnt) 490 goto out; 491 492 /* get the highest possible cpu number for a sparse allocation */ 493 ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt); 494 if (ret >= PATH_MAX) { 495 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 496 goto out; 497 } 498 499 ret = get_max_num(path, &max); 500 if (ret) 501 goto out; 502 503 /* 504 * struct perf_cpu.cpu is int16_t (libperf ABI) — clamp to avoid 505 * truncation to negative. See tools/lib/perf/TODO for the ABI 506 * widening plan. 507 */ 508 if (max > INT16_MAX) { 509 pr_warning("WARNING: max possible cpus %d exceeds int16_t, clamping to %d\n", 510 max, INT16_MAX); 511 max = INT16_MAX; 512 } 513 max_cpu_num.cpu = max; 514 515 /* get the highest present cpu number for a sparse allocation */ 516 ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/present", mnt); 517 if (ret >= PATH_MAX) { 518 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 519 goto out; 520 } 521 522 ret = get_max_num(path, &max); 523 524 if (!ret && max > INT16_MAX) { 525 pr_warning("WARNING: max present cpus %d exceeds int16_t, clamping to %d\n", 526 max, INT16_MAX); 527 max = INT16_MAX; 528 } 529 if (!ret) 530 max_present_cpu_num.cpu = max; 531 out: 532 if (ret) 533 pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num.cpu); 534 } 535 536 /* Determine highest possible node in the system for sparse allocation */ 537 static void set_max_node_num(void) 538 { 539 const char *mnt; 540 char path[PATH_MAX]; 541 int ret = -1; 542 543 /* set up default */ 544 max_node_num = 8; 545 546 mnt = sysfs__mountpoint(); 547 if (!mnt) 548 goto out; 549 550 /* get the highest possible cpu number for a sparse allocation */ 551 ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt); 552 if (ret >= PATH_MAX) { 553 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 554 goto out; 555 } 556 557 ret = get_max_num(path, &max_node_num); 558 559 out: 560 if (ret) 561 pr_err("Failed to read max nodes, using default of %d\n", max_node_num); 562 } 563 564 int cpu__max_node(void) 565 { 566 if (unlikely(!max_node_num)) 567 set_max_node_num(); 568 569 return max_node_num; 570 } 571 572 struct perf_cpu cpu__max_cpu(void) 573 { 574 if (unlikely(!max_cpu_num.cpu)) 575 set_max_cpu_num(); 576 577 return max_cpu_num; 578 } 579 580 struct perf_cpu cpu__max_present_cpu(void) 581 { 582 if (unlikely(!max_present_cpu_num.cpu)) 583 set_max_cpu_num(); 584 585 return max_present_cpu_num; 586 } 587 588 589 int cpu__get_node(struct perf_cpu cpu) 590 { 591 if (unlikely(cpunode_map == NULL)) { 592 pr_debug("cpu_map not initialized\n"); 593 return -1; 594 } 595 596 /* cpunode_map allocated for max_cpu_num entries; input may be untrusted */ 597 if (cpu.cpu < 0 || cpu.cpu >= max_cpu_num.cpu) 598 return -1; 599 600 return cpunode_map[cpu.cpu]; 601 } 602 603 static int init_cpunode_map(void) 604 { 605 int i; 606 607 set_max_cpu_num(); 608 set_max_node_num(); 609 610 cpunode_map = calloc(max_cpu_num.cpu, sizeof(int)); 611 if (!cpunode_map) { 612 pr_err("%s: calloc failed\n", __func__); 613 return -1; 614 } 615 616 for (i = 0; i < max_cpu_num.cpu; i++) 617 cpunode_map[i] = -1; 618 619 return 0; 620 } 621 622 int cpu__setup_cpunode_map(void) 623 { 624 struct dirent *dent1, *dent2; 625 DIR *dir1, *dir2; 626 unsigned int cpu, mem; 627 char buf[PATH_MAX]; 628 char path[PATH_MAX]; 629 const char *mnt; 630 int n; 631 632 /* initialize globals */ 633 if (init_cpunode_map()) 634 return -1; 635 636 mnt = sysfs__mountpoint(); 637 if (!mnt) 638 return 0; 639 640 n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt); 641 if (n >= PATH_MAX) { 642 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 643 return -1; 644 } 645 646 dir1 = opendir(path); 647 if (!dir1) 648 return 0; 649 650 /* walk tree and setup map */ 651 while ((dent1 = readdir(dir1)) != NULL) { 652 if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1) 653 continue; 654 655 n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name); 656 if (n >= PATH_MAX) { 657 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 658 continue; 659 } 660 661 dir2 = opendir(buf); 662 if (!dir2) 663 continue; 664 while ((dent2 = readdir(dir2)) != NULL) { 665 if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) 666 continue; 667 /* cpunode_map allocated for max_cpu_num entries */ 668 if (cpu < (unsigned int)max_cpu_num.cpu) 669 cpunode_map[cpu] = mem; 670 } 671 closedir(dir2); 672 } 673 closedir(dir1); 674 return 0; 675 } 676 677 size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size) 678 { 679 int i, start = -1; 680 bool first = true; 681 size_t ret = 0; 682 683 #define COMMA first ? "" : "," 684 685 for (i = 0; i < (int)perf_cpu_map__nr(map) + 1; i++) { 686 struct perf_cpu cpu = { .cpu = INT16_MAX }; 687 bool last = i == (int)perf_cpu_map__nr(map); 688 689 if (!last) 690 cpu = perf_cpu_map__cpu(map, i); 691 692 if (start == -1) { 693 start = i; 694 if (last) { 695 ret += scnprintf(buf + ret, size - ret, 696 "%s%d", COMMA, 697 perf_cpu_map__cpu(map, i).cpu); 698 } 699 } else if (((i - start) != (cpu.cpu - perf_cpu_map__cpu(map, start).cpu)) || last) { 700 int end = i - 1; 701 702 if (start == end) { 703 ret += scnprintf(buf + ret, size - ret, 704 "%s%d", COMMA, 705 perf_cpu_map__cpu(map, start).cpu); 706 } else { 707 ret += scnprintf(buf + ret, size - ret, 708 "%s%d-%d", COMMA, 709 perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); 710 } 711 first = false; 712 start = i; 713 } 714 } 715 716 #undef COMMA 717 718 pr_debug2("cpumask list: %s\n", buf); 719 return ret; 720 } 721 722 static char hex_char(unsigned char val) 723 { 724 if (val < 10) 725 return val + '0'; 726 if (val < 16) 727 return val - 10 + 'a'; 728 return '?'; 729 } 730 731 size_t cpu_map__snprint_mask(struct perf_cpu_map *map, char *buf, size_t size) 732 { 733 unsigned int idx; 734 char *ptr = buf; 735 unsigned char *bitmap; 736 struct perf_cpu c, last_cpu = perf_cpu_map__max(map); 737 738 if (buf == NULL || size == 0) 739 return 0; 740 741 if (last_cpu.cpu < 0) { 742 buf[0] = '\0'; 743 return 0; 744 } 745 746 bitmap = zalloc(last_cpu.cpu / 8 + 1); 747 if (bitmap == NULL) { 748 buf[0] = '\0'; 749 return 0; 750 } 751 752 perf_cpu_map__for_each_cpu_skip_any(c, idx, map) 753 bitmap[c.cpu / 8] |= 1 << (c.cpu % 8); 754 755 for (int cpu = last_cpu.cpu / 4 * 4; cpu >= 0; cpu -= 4) { 756 unsigned char bits = bitmap[cpu / 8]; 757 758 if (cpu % 8) 759 bits >>= 4; 760 else 761 bits &= 0xf; 762 763 *ptr++ = hex_char(bits); 764 if ((cpu % 32) == 0 && cpu > 0) 765 *ptr++ = ','; 766 } 767 *ptr = '\0'; 768 free(bitmap); 769 770 buf[size - 1] = '\0'; 771 return ptr - buf; 772 } 773 774 struct perf_cpu_map *cpu_map__online(void) /* thread unsafe */ 775 { 776 static struct perf_cpu_map *online; 777 778 if (!online) 779 online = perf_cpu_map__new_online_cpus(); /* from /sys/devices/system/cpu/online */ 780 781 return perf_cpu_map__get(online); 782 } 783 784 bool aggr_cpu_id__equal(const struct aggr_cpu_id *a, const struct aggr_cpu_id *b) 785 { 786 return a->thread_idx == b->thread_idx && 787 a->node == b->node && 788 a->socket == b->socket && 789 a->die == b->die && 790 a->cluster == b->cluster && 791 a->cache_lvl == b->cache_lvl && 792 a->cache == b->cache && 793 a->core == b->core && 794 a->cpu.cpu == b->cpu.cpu; 795 } 796 797 bool aggr_cpu_id__is_empty(const struct aggr_cpu_id *a) 798 { 799 return a->thread_idx == -1 && 800 a->node == -1 && 801 a->socket == -1 && 802 a->die == -1 && 803 a->cluster == -1 && 804 a->cache_lvl == -1 && 805 a->cache == -1 && 806 a->core == -1 && 807 a->cpu.cpu == -1; 808 } 809 810 struct aggr_cpu_id aggr_cpu_id__empty(void) 811 { 812 struct aggr_cpu_id ret = { 813 .thread_idx = -1, 814 .node = -1, 815 .socket = -1, 816 .die = -1, 817 .cluster = -1, 818 .cache_lvl = -1, 819 .cache = -1, 820 .core = -1, 821 .cpu = (struct perf_cpu){ .cpu = -1 }, 822 }; 823 return ret; 824 } 825