Merge remote-tracking branch 'torvalds/master' into perf/core
[linux-2.6-microblaze.git] / tools / perf / util / cputopo.c
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
32 static 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
69 try_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
100 try_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;
128 done:
129         if (fp)
130                 fclose(fp);
131         free(buf);
132         return ret;
133 }
134
135 void 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
156 static 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
175 struct 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
225 out_free:
226         perf_cpu_map__put(map);
227         if (ret) {
228                 cpu_topology__delete(tp);
229                 tp = NULL;
230         }
231         return tp;
232 }
233
234 static 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
287 err:
288         free(buf);
289         if (fp)
290                 fclose(fp);
291         return ret;
292 }
293
294 struct 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
339 out:
340         free(buf);
341         fclose(fp);
342         perf_cpu_map__put(node_map);
343         return tp;
344 }
345
346 void 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
356 static 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
391 err:
392         zfree(&node->pmu_name);
393         free(buf);
394         return -1;
395 }
396
397 struct 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
423 void 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 }