projects/linux·blob master · refs

495 lines · 11.1 KiB

1// SPDX-License-Identifier: GPL-2.0
2#include "debug.h"
3#include "dsos.h"
4#include "dso.h"
5#include "util.h"
6#include "vdso.h"
7#include "namespaces.h"
8#include <errno.h>
9#include <stdlib.h>
10#include <string.h>
11#include <symbol.h> // filename__read_build_id
12#include <unistd.h>
13
14void dsos__init(struct dsos *dsos)
15{
16 init_rwsem(&dsos->lock);
17
18 dsos->cnt = 0;
19 dsos->allocated = 0;
20 dsos->dsos = NULL;
21 dsos->sorted = true;
22}
23
24static void dsos__purge(struct dsos *dsos)
25{
26 down_write(&dsos->lock);
27
28 for (unsigned int i = 0; i < dsos->cnt; i++) {
29 struct dso *dso = dsos->dsos[i];
30
31 dso__set_dsos(dso, NULL);
32 dso__put(dso);
33 }
34
35 zfree(&dsos->dsos);
36 dsos->cnt = 0;
37 dsos->allocated = 0;
38 dsos->sorted = true;
39
40 up_write(&dsos->lock);
41}
42
43void dsos__exit(struct dsos *dsos)
44{
45 dsos__purge(dsos);
46 exit_rwsem(&dsos->lock);
47}
48
49
50static int __dsos__for_each_dso(struct dsos *dsos,
51 int (*cb)(struct dso *dso, void *data),
52 void *data)
53{
54 for (unsigned int i = 0; i < dsos->cnt; i++) {
55 struct dso *dso = dsos->dsos[i];
56 int err;
57
58 err = cb(dso, data);
59 if (err)
60 return err;
61 }
62 return 0;
63}
64
65struct dsos__read_build_ids_cb_args {
66 bool with_hits;
67 bool have_build_id;
68};
69
70static int dsos__read_build_ids_cb(struct dso *dso, void *data)
71{
72 struct dsos__read_build_ids_cb_args *args = data;
73 struct nscookie nsc;
74 struct build_id bid = { .size = 0, };
75
76 if (args->with_hits && !dso__hit(dso) && !dso__is_vdso(dso))
77 return 0;
78 if (dso__has_build_id(dso)) {
79 args->have_build_id = true;
80 return 0;
81 }
82 nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
83 if (filename__read_build_id(dso__long_name(dso), &bid) > 0) {
84 dso__set_build_id(dso, &bid);
85 args->have_build_id = true;
86 } else if (errno == ENOENT && dso__nsinfo(dso)) {
87 char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso));
88
89 if (new_name && filename__read_build_id(new_name, &bid) > 0) {
90 dso__set_build_id(dso, &bid);
91 args->have_build_id = true;
92 }
93 free(new_name);
94 }
95 nsinfo__mountns_exit(&nsc);
96 return 0;
97}
98
99bool dsos__read_build_ids(struct dsos *dsos, bool with_hits)
100{
101 struct dsos__read_build_ids_cb_args args = {
102 .with_hits = with_hits,
103 .have_build_id = false,
104 };
105
106 dsos__for_each_dso(dsos, dsos__read_build_ids_cb, &args);
107 return args.have_build_id;
108}
109
110static int __dso__cmp_long_name(const char *long_name, const struct dso_id *id,
111 const struct dso *b)
112{
113 int rc = strcmp(long_name, dso__long_name(b));
114 return rc ?: dso_id__cmp(id, dso__id_const(b));
115}
116
117static int __dso__cmp_short_name(const char *short_name, const struct dso_id *id,
118 const struct dso *b)
119{
120 int rc = strcmp(short_name, dso__short_name(b));
121 return rc ?: dso_id__cmp(id, dso__id_const(b));
122}
123
124static int dsos__cmp_long_name_id_short_name(const void *va, const void *vb)
125{
126 const struct dso *a = *((const struct dso **)va);
127 const struct dso *b = *((const struct dso **)vb);
128 int rc = strcmp(dso__long_name(a), dso__long_name(b));
129
130 if (!rc) {
131 rc = dso_id__cmp(dso__id_const(a), dso__id_const(b));
132 if (!rc)
133 rc = strcmp(dso__short_name(a), dso__short_name(b));
134 }
135 return rc;
136}
137
138struct dsos__key {
139 const char *long_name;
140 const struct dso_id *id;
141};
142
143static int dsos__cmp_key_long_name_id(const void *vkey, const void *vdso)
144{
145 const struct dsos__key *key = vkey;
146 const struct dso *dso = *((const struct dso **)vdso);
147
148 return __dso__cmp_long_name(key->long_name, key->id, dso);
149}
150
151/*
152 * Find a matching entry and/or link current entry to RB tree.
153 * Either one of the dso or name parameter must be non-NULL or the
154 * function will not work.
155 */
156static struct dso *__dsos__find_by_longname_id(struct dsos *dsos,
157 const char *name,
158 const struct dso_id *id,
159 bool write_locked)
160 SHARED_LOCKS_REQUIRED(dsos->lock)
161{
162 struct dsos__key key = {
163 .long_name = name,
164 .id = id,
165 };
166 struct dso **res;
167
168 if (dsos->dsos == NULL)
169 return NULL;
170
171 if (!dsos->sorted) {
172 if (!write_locked) {
173 struct dso *dso;
174
175 up_read(&dsos->lock);
176 down_write(&dsos->lock);
177 dso = __dsos__find_by_longname_id(dsos, name, id,
178 /*write_locked=*/true);
179 up_write(&dsos->lock);
180 down_read(&dsos->lock);
181 return dso;
182 }
183 qsort(dsos->dsos, dsos->cnt, sizeof(struct dso *),
184 dsos__cmp_long_name_id_short_name);
185 dsos->sorted = true;
186 }
187
188 res = bsearch(&key, dsos->dsos, dsos->cnt, sizeof(struct dso *),
189 dsos__cmp_key_long_name_id);
190 if (!res)
191 return NULL;
192
193 return dso__get(*res);
194}
195
196int __dsos__add(struct dsos *dsos, struct dso *dso)
197{
198 if (!dso)
199 return -EINVAL;
200
201 if (dsos->cnt == dsos->allocated) {
202 unsigned int to_allocate = 2;
203 struct dso **temp;
204
205 if (dsos->allocated > 0)
206 to_allocate = dsos->allocated * 2;
207 temp = realloc(dsos->dsos, sizeof(struct dso *) * to_allocate);
208 if (!temp)
209 return -ENOMEM;
210 dsos->dsos = temp;
211 dsos->allocated = to_allocate;
212 }
213 if (!dsos->sorted) {
214 dsos->dsos[dsos->cnt++] = dso__get(dso);
215 } else {
216 int low = 0, high = dsos->cnt - 1;
217 int insert = dsos->cnt; /* Default to inserting at the end. */
218
219 while (low <= high) {
220 int mid = low + (high - low) / 2;
221 int cmp = dsos__cmp_long_name_id_short_name(&dsos->dsos[mid], &dso);
222
223 if (cmp < 0) {
224 low = mid + 1;
225 } else {
226 high = mid - 1;
227 insert = mid;
228 }
229 }
230 memmove(&dsos->dsos[insert + 1], &dsos->dsos[insert],
231 (dsos->cnt - insert) * sizeof(struct dso *));
232 dsos->cnt++;
233 dsos->dsos[insert] = dso__get(dso);
234 }
235 dso__set_dsos(dso, dsos);
236 return 0;
237}
238
239int dsos__add(struct dsos *dsos, struct dso *dso)
240{
241 int ret;
242
243 down_write(&dsos->lock);
244 ret = __dsos__add(dsos, dso);
245 up_write(&dsos->lock);
246 return ret;
247}
248
249struct dsos__find_id_cb_args {
250 const char *name;
251 const struct dso_id *id;
252 struct dso *res;
253};
254
255static int dsos__find_id_cb(struct dso *dso, void *data)
256{
257 struct dsos__find_id_cb_args *args = data;
258
259 if (__dso__cmp_short_name(args->name, args->id, dso) == 0) {
260 args->res = dso__get(dso);
261 return 1;
262 }
263 return 0;
264
265}
266
267static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, const struct dso_id *id,
268 bool cmp_short, bool write_locked)
269 SHARED_LOCKS_REQUIRED(dsos->lock)
270{
271 struct dso *res;
272
273 if (cmp_short) {
274 struct dsos__find_id_cb_args args = {
275 .name = name,
276 .id = id,
277 .res = NULL,
278 };
279
280 __dsos__for_each_dso(dsos, dsos__find_id_cb, &args);
281 return args.res;
282 }
283 res = __dsos__find_by_longname_id(dsos, name, id, write_locked);
284 return res;
285}
286
287struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
288{
289 struct dso *res;
290
291 down_read(&dsos->lock);
292 res = __dsos__find_id(dsos, name, &dso_id_empty, cmp_short, /*write_locked=*/false);
293 up_read(&dsos->lock);
294 return res;
295}
296
297static void dso__set_basename(struct dso *dso)
298{
299 bool allocated = false;
300 const char *base;
301 int tid;
302
303 if (perf_pid_map_tid(dso__long_name(dso), &tid)) {
304 char *jitname;
305
306 if (asprintf(&jitname, "[JIT] tid %d", tid) < 0)
307 return;
308 allocated = true;
309 base = jitname;
310 } else {
311 base = perf_basename(dso__long_name(dso));
312 }
313 dso__set_short_name(dso, base, allocated);
314}
315
316static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)
317{
318 struct dso *dso = dso__new_id(name, id);
319
320 if (dso != NULL) {
321 /*
322 * The dsos lock is held on entry, so rename the dso before
323 * adding it to avoid needing to take the dsos lock again to say
324 * the array isn't sorted.
325 */
326 dso__set_basename(dso);
327 __dsos__add(dsos, dso);
328 }
329 return dso;
330}
331
332static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)
333 SHARED_LOCKS_REQUIRED(dsos->lock)
334{
335 struct dso *dso = __dsos__find_id(dsos, name, id, false, /*write_locked=*/true);
336
337 if (dso)
338 __dso__improve_id(dso, id);
339
340 return dso ? dso : __dsos__addnew_id(dsos, name, id);
341}
342
343struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)
344{
345 struct dso *dso;
346 down_write(&dsos->lock);
347 dso = __dsos__findnew_id(dsos, name, id);
348 up_write(&dsos->lock);
349 return dso;
350}
351
352struct dsos__fprintf_buildid_cb_args {
353 FILE *fp;
354 bool (*skip)(struct dso *dso, int parm);
355 int parm;
356 size_t ret;
357};
358
359static int dsos__fprintf_buildid_cb(struct dso *dso, void *data)
360{
361 struct dsos__fprintf_buildid_cb_args *args = data;
362 char sbuild_id[SBUILD_ID_SIZE];
363
364 if (args->skip && args->skip(dso, args->parm))
365 return 0;
366 build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id));
367 args->ret += fprintf(args->fp, "%-40s %s\n", sbuild_id, dso__long_name(dso));
368 return 0;
369}
370
371size_t dsos__fprintf_buildid(struct dsos *dsos, FILE *fp,
372 bool (*skip)(struct dso *dso, int parm), int parm)
373{
374 struct dsos__fprintf_buildid_cb_args args = {
375 .fp = fp,
376 .skip = skip,
377 .parm = parm,
378 .ret = 0,
379 };
380
381 dsos__for_each_dso(dsos, dsos__fprintf_buildid_cb, &args);
382 return args.ret;
383}
384
385struct dsos__fprintf_cb_args {
386 FILE *fp;
387 size_t ret;
388};
389
390static int dsos__fprintf_cb(struct dso *dso, void *data)
391{
392 struct dsos__fprintf_cb_args *args = data;
393
394 args->ret += dso__fprintf(dso, args->fp);
395 return 0;
396}
397
398size_t dsos__fprintf(struct dsos *dsos, FILE *fp)
399{
400 struct dsos__fprintf_cb_args args = {
401 .fp = fp,
402 .ret = 0,
403 };
404
405 dsos__for_each_dso(dsos, dsos__fprintf_cb, &args);
406 return args.ret;
407}
408
409static int dsos__hit_all_cb(struct dso *dso, void *data __maybe_unused)
410{
411 dso__set_hit(dso);
412 return 0;
413}
414
415int dsos__hit_all(struct dsos *dsos)
416{
417 return dsos__for_each_dso(dsos, dsos__hit_all_cb, NULL);
418}
419
420struct dso *dsos__findnew_module_dso(struct dsos *dsos,
421 struct machine *machine,
422 struct kmod_path *m,
423 const char *filename)
424{
425 struct dso *dso;
426
427 down_write(&dsos->lock);
428
429 dso = __dsos__find_id(dsos, m->name, &dso_id_empty, /*cmp_short=*/true,
430 /*write_locked=*/true);
431 if (dso) {
432 up_write(&dsos->lock);
433 return dso;
434 }
435 /*
436 * Failed to find the dso so create it. Change the name before adding it
437 * to the array, to avoid unnecessary sorts and potential locking
438 * issues.
439 */
440 dso = dso__new_id(m->name, /*id=*/NULL);
441 if (!dso) {
442 up_write(&dsos->lock);
443 return NULL;
444 }
445 dso__set_basename(dso);
446 dso__set_module_info(dso, m, machine);
447 dso__set_long_name(dso, strdup(filename), true);
448 dso__set_kernel(dso, DSO_SPACE__KERNEL);
449 __dsos__add(dsos, dso);
450
451 up_write(&dsos->lock);
452 return dso;
453}
454
455static int dsos__find_kernel_dso_cb(struct dso *dso, void *data)
456{
457 struct dso **res = data;
458 /*
459 * The cpumode passed to is_kernel_module is not the cpumode of *this*
460 * event. If we insist on passing correct cpumode to is_kernel_module,
461 * we should record the cpumode when we adding this dso to the linked
462 * list.
463 *
464 * However we don't really need passing correct cpumode. We know the
465 * correct cpumode must be kernel mode (if not, we should not link it
466 * onto kernel_dsos list).
467 *
468 * Therefore, we pass PERF_RECORD_MISC_CPUMODE_UNKNOWN.
469 * is_kernel_module() treats it as a kernel cpumode.
470 */
471 if (!dso__kernel(dso) ||
472 is_kernel_module(dso__long_name(dso), PERF_RECORD_MISC_CPUMODE_UNKNOWN))
473 return 0;
474
475 *res = dso__get(dso);
476 return 1;
477}
478
479struct dso *dsos__find_kernel_dso(struct dsos *dsos)
480{
481 struct dso *res = NULL;
482
483 dsos__for_each_dso(dsos, dsos__find_kernel_dso_cb, &res);
484 return res;
485}
486
487int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data), void *data)
488{
489 int err;
490
491 down_read(&dsos->lock);
492 err = __dsos__for_each_dso(dsos, cb, data);
493 up_read(&dsos->lock);
494 return err;
495}