linux/tools/perf/util/cputopo.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <sys/param.h>
   3#include <sys/utsname.h>
   4#include <inttypes.h>
   5#include <stdlib.h>
   6#include <string.h>
   7#include <api/fs/fs.h>
   8#include <linux/zalloc.h>
   9#include <perf/cpumap.h>
  10
  11#include "cputopo.h"
  12#include "cpumap.h"
  13#include "debug.h"
  14#include "env.h"
  15#include "pmu-hybrid.h"
  16
  17#define CORE_SIB_FMT \
  18        "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
  19#define DIE_SIB_FMT \
  20        "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
  21#define THRD_SIB_FMT \
  22        "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
  23#define THRD_SIB_FMT_NEW \
  24        "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
  25#define NODE_ONLINE_FMT \
  26        "%s/devices/system/node/online"
  27#define NODE_MEMINFO_FMT \
  28        "%s/devices/system/node/node%d/meminfo"
  29#define NODE_CPULIST_FMT \
  30        "%s/devices/system/node/node%d/cpulist"
  31
  32static int build_cpu_topology(struct cpu_topology *tp, int cpu)
  33{
  34        FILE *fp;
  35        char filename[MAXPATHLEN];
  36        char *buf = NULL, *p;
  37        size_t len = 0;
  38        ssize_t sret;
  39        u32 i = 0;
  40        int ret = -1;
  41
  42        scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT,
  43                  sysfs__mountpoint(), cpu);
  44        fp = fopen(filename, "r");
  45        if (!fp)
  46                goto try_dies;
  47
  48        sret = getline(&buf, &len, fp);
  49        fclose(fp);
  50        if (sret <= 0)
  51                goto try_dies;
  52
  53        p = strchr(buf, '\n');
  54        if (p)
  55                *p = '\0';
  56
  57        for (i = 0; i < tp->core_sib; i++) {
  58                if (!strcmp(buf, tp->core_siblings[i]))
  59                        break;
  60        }
  61        if (i == tp->core_sib) {
  62                tp->core_siblings[i] = buf;
  63                tp->core_sib++;
  64                buf = NULL;
  65                len = 0;
  66        }
  67        ret = 0;
  68
  69try_dies:
  70        if (!tp->die_siblings)
  71                goto try_threads;
  72
  73        scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
  74                  sysfs__mountpoint(), cpu);
  75        fp = fopen(filename, "r");
  76        if (!fp)
  77                goto try_threads;
  78
  79        sret = getline(&buf, &len, fp);
  80        fclose(fp);
  81        if (sret <= 0)
  82                goto try_threads;
  83
  84        p = strchr(buf, '\n');
  85        if (p)
  86                *p = '\0';
  87
  88        for (i = 0; i < tp->die_sib; i++) {
  89                if (!strcmp(buf, tp->die_siblings[i]))
  90                        break;
  91        }
  92        if (i == tp->die_sib) {
  93                tp->die_siblings[i] = buf;
  94                tp->die_sib++;
  95                buf = NULL;
  96                len = 0;
  97        }
  98        ret = 0;
  99
 100try_threads:
 101        scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW,
 102                  sysfs__mountpoint(), cpu);
 103        if (access(filename, F_OK) == -1) {
 104                scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
 105                          sysfs__mountpoint(), cpu);
 106        }
 107        fp = fopen(filename, "r");
 108        if (!fp)
 109                goto done;
 110
 111        if (getline(&buf, &len, fp) <= 0)
 112                goto done;
 113
 114        p = strchr(buf, '\n');
 115        if (p)
 116                *p = '\0';
 117
 118        for (i = 0; i < tp->thread_sib; i++) {
 119                if (!strcmp(buf, tp->thread_siblings[i]))
 120                        break;
 121        }
 122        if (i == tp->thread_sib) {
 123                tp->thread_siblings[i] = buf;
 124                tp->thread_sib++;
 125                buf = NULL;
 126        }
 127        ret = 0;
 128done:
 129        if (fp)
 130                fclose(fp);
 131        free(buf);
 132        return ret;
 133}
 134
 135void cpu_topology__delete(struct cpu_topology *tp)
 136{
 137        u32 i;
 138
 139        if (!tp)
 140                return;
 141
 142        for (i = 0 ; i < tp->core_sib; i++)
 143                zfree(&tp->core_siblings[i]);
 144
 145        if (tp->die_sib) {
 146                for (i = 0 ; i < tp->die_sib; i++)
 147                        zfree(&tp->die_siblings[i]);
 148        }
 149
 150        for (i = 0 ; i < tp->thread_sib; i++)
 151                zfree(&tp->thread_siblings[i]);
 152
 153        free(tp);
 154}
 155
 156static bool has_die_topology(void)
 157{
 158        char filename[MAXPATHLEN];
 159        struct utsname uts;
 160
 161        if (uname(&uts) < 0)
 162                return false;
 163
 164        if (strncmp(uts.machine, "x86_64", 6))
 165                return false;
 166
 167        scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
 168                  sysfs__mountpoint(), 0);
 169        if (access(filename, F_OK) == -1)
 170                return false;
 171
 172        return true;
 173}
 174
 175struct cpu_topology *cpu_topology__new(void)
 176{
 177        struct cpu_topology *tp = NULL;
 178        void *addr;
 179        u32 nr, i, nr_addr;
 180        size_t sz;
 181        long ncpus;
 182        int ret = -1;
 183        struct perf_cpu_map *map;
 184        bool has_die = has_die_topology();
 185
 186        ncpus = cpu__max_present_cpu();
 187
 188        /* build online CPU map */
 189        map = perf_cpu_map__new(NULL);
 190        if (map == NULL) {
 191                pr_debug("failed to get system cpumap\n");
 192                return NULL;
 193        }
 194
 195        nr = (u32)(ncpus & UINT_MAX);
 196
 197        sz = nr * sizeof(char *);
 198        if (has_die)
 199                nr_addr = 3;
 200        else
 201                nr_addr = 2;
 202        addr = calloc(1, sizeof(*tp) + nr_addr * sz);
 203        if (!addr)
 204                goto out_free;
 205
 206        tp = addr;
 207        addr += sizeof(*tp);
 208        tp->core_siblings = addr;
 209        addr += sz;
 210        if (has_die) {
 211                tp->die_siblings = addr;
 212                addr += sz;
 213        }
 214        tp->thread_siblings = addr;
 215
 216        for (i = 0; i < nr; i++) {
 217                if (!cpu_map__has(map, i))
 218                        continue;
 219
 220                ret = build_cpu_topology(tp, i);
 221                if (ret < 0)
 222                        break;
 223        }
 224
 225out_free:
 226        perf_cpu_map__put(map);
 227        if (ret) {
 228                cpu_topology__delete(tp);
 229                tp = NULL;
 230        }
 231        return tp;
 232}
 233
 234static int load_numa_node(struct numa_topology_node *node, int nr)
 235{
 236        char str[MAXPATHLEN];
 237        char field[32];
 238        char *buf = NULL, *p;
 239        size_t len = 0;
 240        int ret = -1;
 241        FILE *fp;
 242        u64 mem;
 243
 244        node->node = (u32) nr;
 245
 246        scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
 247                  sysfs__mountpoint(), nr);
 248        fp = fopen(str, "r");
 249        if (!fp)
 250                return -1;
 251
 252        while (getline(&buf, &len, fp) > 0) {
 253                /* skip over invalid lines */
 254                if (!strchr(buf, ':'))
 255                        continue;
 256                if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
 257                        goto err;
 258                if (!strcmp(field, "MemTotal:"))
 259                        node->mem_total = mem;
 260                if (!strcmp(field, "MemFree:"))
 261                        node->mem_free = mem;
 262                if (node->mem_total && node->mem_free)
 263                        break;
 264        }
 265
 266        fclose(fp);
 267        fp = NULL;
 268
 269        scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
 270                  sysfs__mountpoint(), nr);
 271
 272        fp = fopen(str, "r");
 273        if (!fp)
 274                return -1;
 275
 276        if (getline(&buf, &len, fp) <= 0)
 277                goto err;
 278
 279        p = strchr(buf, '\n');
 280        if (p)
 281                *p = '\0';
 282
 283        node->cpus = buf;
 284        fclose(fp);
 285        return 0;
 286
 287err:
 288        free(buf);
 289        if (fp)
 290                fclose(fp);
 291        return ret;
 292}
 293
 294struct numa_topology *numa_topology__new(void)
 295{
 296        struct perf_cpu_map *node_map = NULL;
 297        struct numa_topology *tp = NULL;
 298        char path[MAXPATHLEN];
 299        char *buf = NULL;
 300        size_t len = 0;
 301        u32 nr, i;
 302        FILE *fp;
 303        char *c;
 304
 305        scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
 306                  sysfs__mountpoint());
 307
 308        fp = fopen(path, "r");
 309        if (!fp)
 310                return NULL;
 311
 312        if (getline(&buf, &len, fp) <= 0)
 313                goto out;
 314
 315        c = strchr(buf, '\n');
 316        if (c)
 317                *c = '\0';
 318
 319        node_map = perf_cpu_map__new(buf);
 320        if (!node_map)
 321                goto out;
 322
 323        nr = (u32) node_map->nr;
 324
 325        tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
 326        if (!tp)
 327                goto out;
 328
 329        tp->nr = nr;
 330
 331        for (i = 0; i < nr; i++) {
 332                if (load_numa_node(&tp->nodes[i], node_map->map[i])) {
 333                        numa_topology__delete(tp);
 334                        tp = NULL;
 335                        break;
 336                }
 337        }
 338
 339out:
 340        free(buf);
 341        fclose(fp);
 342        perf_cpu_map__put(node_map);
 343        return tp;
 344}
 345
 346void numa_topology__delete(struct numa_topology *tp)
 347{
 348        u32 i;
 349
 350        for (i = 0; i < tp->nr; i++)
 351                zfree(&tp->nodes[i].cpus);
 352
 353        free(tp);
 354}
 355
 356static int load_hybrid_node(struct hybrid_topology_node *node,
 357                            struct perf_pmu *pmu)
 358{
 359        const char *sysfs;
 360        char path[PATH_MAX];
 361        char *buf = NULL, *p;
 362        FILE *fp;
 363        size_t len = 0;
 364
 365        node->pmu_name = strdup(pmu->name);
 366        if (!node->pmu_name)
 367                return -1;
 368
 369        sysfs = sysfs__mountpoint();
 370        if (!sysfs)
 371                goto err;
 372
 373        snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
 374        fp = fopen(path, "r");
 375        if (!fp)
 376                goto err;
 377
 378        if (getline(&buf, &len, fp) <= 0) {
 379                fclose(fp);
 380                goto err;
 381        }
 382
 383        p = strchr(buf, '\n');
 384        if (p)
 385                *p = '\0';
 386
 387        fclose(fp);
 388        node->cpus = buf;
 389        return 0;
 390
 391err:
 392        zfree(&node->pmu_name);
 393        free(buf);
 394        return -1;
 395}
 396
 397struct hybrid_topology *hybrid_topology__new(void)
 398{
 399        struct perf_pmu *pmu;
 400        struct hybrid_topology *tp = NULL;
 401        u32 nr, i = 0;
 402
 403        nr = perf_pmu__hybrid_pmu_num();
 404        if (nr == 0)
 405                return NULL;
 406
 407        tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
 408        if (!tp)
 409                return NULL;
 410
 411        tp->nr = nr;
 412        perf_pmu__for_each_hybrid_pmu(pmu) {
 413                if (load_hybrid_node(&tp->nodes[i], pmu)) {
 414                        hybrid_topology__delete(tp);
 415                        return NULL;
 416                }
 417                i++;
 418        }
 419
 420        return tp;
 421}
 422
 423void hybrid_topology__delete(struct hybrid_topology *tp)
 424{
 425        u32 i;
 426
 427        for (i = 0; i < tp->nr; i++) {
 428                zfree(&tp->nodes[i].pmu_name);
 429                zfree(&tp->nodes[i].cpus);
 430        }
 431
 432        free(tp);
 433}
 434