beb8cf9f9976df75e77f38dbc5860b37acb5a9a9
[linux-3.10.git] / tools / perf / util / cpumap.c
1 #include "util.h"
2 #include "sysfs.h"
3 #include "../perf.h"
4 #include "cpumap.h"
5 #include <assert.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8
9 static struct cpu_map *cpu_map__default_new(void)
10 {
11         struct cpu_map *cpus;
12         int nr_cpus;
13
14         nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
15         if (nr_cpus < 0)
16                 return NULL;
17
18         cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int));
19         if (cpus != NULL) {
20                 int i;
21                 for (i = 0; i < nr_cpus; ++i)
22                         cpus->map[i] = i;
23
24                 cpus->nr = nr_cpus;
25         }
26
27         return cpus;
28 }
29
30 static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
31 {
32         size_t payload_size = nr_cpus * sizeof(int);
33         struct cpu_map *cpus = malloc(sizeof(*cpus) + payload_size);
34
35         if (cpus != NULL) {
36                 cpus->nr = nr_cpus;
37                 memcpy(cpus->map, tmp_cpus, payload_size);
38         }
39
40         return cpus;
41 }
42
43 struct cpu_map *cpu_map__read(FILE *file)
44 {
45         struct cpu_map *cpus = NULL;
46         int nr_cpus = 0;
47         int *tmp_cpus = NULL, *tmp;
48         int max_entries = 0;
49         int n, cpu, prev;
50         char sep;
51
52         sep = 0;
53         prev = -1;
54         for (;;) {
55                 n = fscanf(file, "%u%c", &cpu, &sep);
56                 if (n <= 0)
57                         break;
58                 if (prev >= 0) {
59                         int new_max = nr_cpus + cpu - prev - 1;
60
61                         if (new_max >= max_entries) {
62                                 max_entries = new_max + MAX_NR_CPUS / 2;
63                                 tmp = realloc(tmp_cpus, max_entries * sizeof(int));
64                                 if (tmp == NULL)
65                                         goto out_free_tmp;
66                                 tmp_cpus = tmp;
67                         }
68
69                         while (++prev < cpu)
70                                 tmp_cpus[nr_cpus++] = prev;
71                 }
72                 if (nr_cpus == max_entries) {
73                         max_entries += MAX_NR_CPUS;
74                         tmp = realloc(tmp_cpus, max_entries * sizeof(int));
75                         if (tmp == NULL)
76                                 goto out_free_tmp;
77                         tmp_cpus = tmp;
78                 }
79
80                 tmp_cpus[nr_cpus++] = cpu;
81                 if (n == 2 && sep == '-')
82                         prev = cpu;
83                 else
84                         prev = -1;
85                 if (n == 1 || sep == '\n')
86                         break;
87         }
88
89         if (nr_cpus > 0)
90                 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
91         else
92                 cpus = cpu_map__default_new();
93 out_free_tmp:
94         free(tmp_cpus);
95         return cpus;
96 }
97
98 static struct cpu_map *cpu_map__read_all_cpu_map(void)
99 {
100         struct cpu_map *cpus = NULL;
101         FILE *onlnf;
102
103         onlnf = fopen("/sys/devices/system/cpu/online", "r");
104         if (!onlnf)
105                 return cpu_map__default_new();
106
107         cpus = cpu_map__read(onlnf);
108         fclose(onlnf);
109         return cpus;
110 }
111
112 struct cpu_map *cpu_map__new(const char *cpu_list)
113 {
114         struct cpu_map *cpus = NULL;
115         unsigned long start_cpu, end_cpu = 0;
116         char *p = NULL;
117         int i, nr_cpus = 0;
118         int *tmp_cpus = NULL, *tmp;
119         int max_entries = 0;
120
121         if (!cpu_list)
122                 return cpu_map__read_all_cpu_map();
123
124         if (!isdigit(*cpu_list))
125                 goto out;
126
127         while (isdigit(*cpu_list)) {
128                 p = NULL;
129                 start_cpu = strtoul(cpu_list, &p, 0);
130                 if (start_cpu >= INT_MAX
131                     || (*p != '\0' && *p != ',' && *p != '-'))
132                         goto invalid;
133
134                 if (*p == '-') {
135                         cpu_list = ++p;
136                         p = NULL;
137                         end_cpu = strtoul(cpu_list, &p, 0);
138
139                         if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
140                                 goto invalid;
141
142                         if (end_cpu < start_cpu)
143                                 goto invalid;
144                 } else {
145                         end_cpu = start_cpu;
146                 }
147
148                 for (; start_cpu <= end_cpu; start_cpu++) {
149                         /* check for duplicates */
150                         for (i = 0; i < nr_cpus; i++)
151                                 if (tmp_cpus[i] == (int)start_cpu)
152                                         goto invalid;
153
154                         if (nr_cpus == max_entries) {
155                                 max_entries += MAX_NR_CPUS;
156                                 tmp = realloc(tmp_cpus, max_entries * sizeof(int));
157                                 if (tmp == NULL)
158                                         goto invalid;
159                                 tmp_cpus = tmp;
160                         }
161                         tmp_cpus[nr_cpus++] = (int)start_cpu;
162                 }
163                 if (*p)
164                         ++p;
165
166                 cpu_list = p;
167         }
168
169         if (nr_cpus > 0)
170                 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
171         else
172                 cpus = cpu_map__default_new();
173 invalid:
174         free(tmp_cpus);
175 out:
176         return cpus;
177 }
178
179 size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp)
180 {
181         int i;
182         size_t printed = fprintf(fp, "%d cpu%s: ",
183                                  map->nr, map->nr > 1 ? "s" : "");
184         for (i = 0; i < map->nr; ++i)
185                 printed += fprintf(fp, "%s%d", i ? ", " : "", map->map[i]);
186
187         return printed + fprintf(fp, "\n");
188 }
189
190 struct cpu_map *cpu_map__dummy_new(void)
191 {
192         struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
193
194         if (cpus != NULL) {
195                 cpus->nr = 1;
196                 cpus->map[0] = -1;
197         }
198
199         return cpus;
200 }
201
202 void cpu_map__delete(struct cpu_map *map)
203 {
204         free(map);
205 }
206
207 int cpu_map__get_socket(struct cpu_map *map, int idx)
208 {
209         FILE *fp;
210         const char *mnt;
211         char path[PATH_MAX];
212         int cpu, ret;
213
214         if (idx > map->nr)
215                 return -1;
216
217         cpu = map->map[idx];
218
219         mnt = sysfs_find_mountpoint();
220         if (!mnt)
221                 return -1;
222
223         snprintf(path, PATH_MAX,
224                 "%s/devices/system/cpu/cpu%d/topology/physical_package_id",
225                 mnt, cpu);
226
227         fp = fopen(path, "r");
228         if (!fp)
229                 return -1;
230         ret = fscanf(fp, "%d", &cpu);
231         fclose(fp);
232         return ret == 1 ? cpu : -1;
233 }
234
235 static int cmp_ids(const void *a, const void *b)
236 {
237         return *(int *)a - *(int *)b;
238 }
239
240 static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
241                               int (*f)(struct cpu_map *map, int cpu))
242 {
243         struct cpu_map *c;
244         int nr = cpus->nr;
245         int cpu, s1, s2;
246
247         /* allocate as much as possible */
248         c = calloc(1, sizeof(*c) + nr * sizeof(int));
249         if (!c)
250                 return -1;
251
252         for (cpu = 0; cpu < nr; cpu++) {
253                 s1 = f(cpus, cpu);
254                 for (s2 = 0; s2 < c->nr; s2++) {
255                         if (s1 == c->map[s2])
256                                 break;
257                 }
258                 if (s2 == c->nr) {
259                         c->map[c->nr] = s1;
260                         c->nr++;
261                 }
262         }
263         /* ensure we process id in increasing order */
264         qsort(c->map, c->nr, sizeof(int), cmp_ids);
265
266         *res = c;
267         return 0;
268 }
269
270 int cpu_map__get_core(struct cpu_map *map, int idx)
271 {
272         FILE *fp;
273         const char *mnt;
274         char path[PATH_MAX];
275         int cpu, ret, s;
276
277         if (idx > map->nr)
278                 return -1;
279
280         cpu = map->map[idx];
281
282         mnt = sysfs_find_mountpoint();
283         if (!mnt)
284                 return -1;
285
286         snprintf(path, PATH_MAX,
287                 "%s/devices/system/cpu/cpu%d/topology/core_id",
288                 mnt, cpu);
289
290         fp = fopen(path, "r");
291         if (!fp)
292                 return -1;
293         ret = fscanf(fp, "%d", &cpu);
294         fclose(fp);
295         if (ret != 1)
296                 return -1;
297
298         s = cpu_map__get_socket(map, idx);
299         if (s == -1)
300                 return -1;
301
302         /*
303          * encode socket in upper 16 bits
304          * core_id is relative to socket, and
305          * we need a global id. So we combine
306          * socket+ core id
307          */
308         return (s << 16) | (cpu & 0xffff);
309 }
310
311 int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
312 {
313         return cpu_map__build_map(cpus, sockp, cpu_map__get_socket);
314 }
315
316 int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)
317 {
318         return cpu_map__build_map(cpus, corep, cpu_map__get_core);
319 }