502 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 <libgen.h>10#include <stdlib.h>11#include <string.h>12#include <symbol.h> // filename__read_build_id13#include <unistd.h>14 15void dsos__init(struct dsos *dsos)16{17 init_rwsem(&dsos->lock);18 19 dsos->cnt = 0;20 dsos->allocated = 0;21 dsos->dsos = NULL;22 dsos->sorted = true;23}24 25static void dsos__purge(struct dsos *dsos)26{27 down_write(&dsos->lock);28 29 for (unsigned int i = 0; i < dsos->cnt; i++) {30 struct dso *dso = dsos->dsos[i];31 32 dso__set_dsos(dso, NULL);33 dso__put(dso);34 }35 36 zfree(&dsos->dsos);37 dsos->cnt = 0;38 dsos->allocated = 0;39 dsos->sorted = true;40 41 up_write(&dsos->lock);42}43 44void dsos__exit(struct dsos *dsos)45{46 dsos__purge(dsos);47 exit_rwsem(&dsos->lock);48}49 50 51static int __dsos__for_each_dso(struct dsos *dsos,52 int (*cb)(struct dso *dso, void *data),53 void *data)54{55 for (unsigned int i = 0; i < dsos->cnt; i++) {56 struct dso *dso = dsos->dsos[i];57 int err;58 59 err = cb(dso, data);60 if (err)61 return err;62 }63 return 0;64}65 66struct dsos__read_build_ids_cb_args {67 bool with_hits;68 bool have_build_id;69};70 71static int dsos__read_build_ids_cb(struct dso *dso, void *data)72{73 struct dsos__read_build_ids_cb_args *args = data;74 struct nscookie nsc;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), dso__bid(dso)) > 0) {84 args->have_build_id = true;85 dso__set_has_build_id(dso);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, dso__bid(dso)) > 0) {90 args->have_build_id = true;91 dso__set_has_build_id(dso);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 the154 * 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{161 struct dsos__key key = {162 .long_name = name,163 .id = id,164 };165 struct dso **res;166 167 if (dsos->dsos == NULL)168 return NULL;169 170 if (!dsos->sorted) {171 if (!write_locked) {172 struct dso *dso;173 174 up_read(&dsos->lock);175 down_write(&dsos->lock);176 dso = __dsos__find_by_longname_id(dsos, name, id,177 /*write_locked=*/true);178 up_write(&dsos->lock);179 down_read(&dsos->lock);180 return dso;181 }182 qsort(dsos->dsos, dsos->cnt, sizeof(struct dso *),183 dsos__cmp_long_name_id_short_name);184 dsos->sorted = true;185 }186 187 res = bsearch(&key, dsos->dsos, dsos->cnt, sizeof(struct dso *),188 dsos__cmp_key_long_name_id);189 if (!res)190 return NULL;191 192 return dso__get(*res);193}194 195int __dsos__add(struct dsos *dsos, struct dso *dso)196{197 if (dsos->cnt == dsos->allocated) {198 unsigned int to_allocate = 2;199 struct dso **temp;200 201 if (dsos->allocated > 0)202 to_allocate = dsos->allocated * 2;203 temp = realloc(dsos->dsos, sizeof(struct dso *) * to_allocate);204 if (!temp)205 return -ENOMEM;206 dsos->dsos = temp;207 dsos->allocated = to_allocate;208 }209 if (!dsos->sorted) {210 dsos->dsos[dsos->cnt++] = dso__get(dso);211 } else {212 int low = 0, high = dsos->cnt - 1;213 int insert = dsos->cnt; /* Default to inserting at the end. */214 215 while (low <= high) {216 int mid = low + (high - low) / 2;217 int cmp = dsos__cmp_long_name_id_short_name(&dsos->dsos[mid], &dso);218 219 if (cmp < 0) {220 low = mid + 1;221 } else {222 high = mid - 1;223 insert = mid;224 }225 }226 memmove(&dsos->dsos[insert + 1], &dsos->dsos[insert],227 (dsos->cnt - insert) * sizeof(struct dso *));228 dsos->cnt++;229 dsos->dsos[insert] = dso__get(dso);230 }231 dso__set_dsos(dso, dsos);232 return 0;233}234 235int dsos__add(struct dsos *dsos, struct dso *dso)236{237 int ret;238 239 down_write(&dsos->lock);240 ret = __dsos__add(dsos, dso);241 up_write(&dsos->lock);242 return ret;243}244 245struct dsos__find_id_cb_args {246 const char *name;247 const struct dso_id *id;248 struct dso *res;249};250 251static int dsos__find_id_cb(struct dso *dso, void *data)252{253 struct dsos__find_id_cb_args *args = data;254 255 if (__dso__cmp_short_name(args->name, args->id, dso) == 0) {256 args->res = dso__get(dso);257 return 1;258 }259 return 0;260 261}262 263static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, const struct dso_id *id,264 bool cmp_short, bool write_locked)265{266 struct dso *res;267 268 if (cmp_short) {269 struct dsos__find_id_cb_args args = {270 .name = name,271 .id = id,272 .res = NULL,273 };274 275 __dsos__for_each_dso(dsos, dsos__find_id_cb, &args);276 return args.res;277 }278 res = __dsos__find_by_longname_id(dsos, name, id, write_locked);279 return res;280}281 282struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)283{284 struct dso *res;285 286 down_read(&dsos->lock);287 res = __dsos__find_id(dsos, name, NULL, cmp_short, /*write_locked=*/false);288 up_read(&dsos->lock);289 return res;290}291 292static void dso__set_basename(struct dso *dso)293{294 char *base, *lname;295 int tid;296 297 if (perf_pid_map_tid(dso__long_name(dso), &tid)) {298 if (asprintf(&base, "[JIT] tid %d", tid) < 0)299 return;300 } else {301 /*302 * basename() may modify path buffer, so we must pass303 * a copy.304 */305 lname = strdup(dso__long_name(dso));306 if (!lname)307 return;308 309 /*310 * basename() may return a pointer to internal311 * storage which is reused in subsequent calls312 * so copy the result.313 */314 base = strdup(basename(lname));315 316 free(lname);317 318 if (!base)319 return;320 }321 dso__set_short_name(dso, base, true);322}323 324static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)325{326 struct dso *dso = dso__new_id(name, id);327 328 if (dso != NULL) {329 /*330 * The dsos lock is held on entry, so rename the dso before331 * adding it to avoid needing to take the dsos lock again to say332 * the array isn't sorted.333 */334 dso__set_basename(dso);335 __dsos__add(dsos, dso);336 }337 return dso;338}339 340static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)341{342 struct dso *dso = __dsos__find_id(dsos, name, id, false, /*write_locked=*/true);343 344 if (dso && dso_id__empty(dso__id(dso)) && !dso_id__empty(id))345 __dso__inject_id(dso, id);346 347 return dso ? dso : __dsos__addnew_id(dsos, name, id);348}349 350struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)351{352 struct dso *dso;353 down_write(&dsos->lock);354 dso = __dsos__findnew_id(dsos, name, id);355 up_write(&dsos->lock);356 return dso;357}358 359struct dsos__fprintf_buildid_cb_args {360 FILE *fp;361 bool (*skip)(struct dso *dso, int parm);362 int parm;363 size_t ret;364};365 366static int dsos__fprintf_buildid_cb(struct dso *dso, void *data)367{368 struct dsos__fprintf_buildid_cb_args *args = data;369 char sbuild_id[SBUILD_ID_SIZE];370 371 if (args->skip && args->skip(dso, args->parm))372 return 0;373 build_id__sprintf(dso__bid(dso), sbuild_id);374 args->ret += fprintf(args->fp, "%-40s %s\n", sbuild_id, dso__long_name(dso));375 return 0;376}377 378size_t dsos__fprintf_buildid(struct dsos *dsos, FILE *fp,379 bool (*skip)(struct dso *dso, int parm), int parm)380{381 struct dsos__fprintf_buildid_cb_args args = {382 .fp = fp,383 .skip = skip,384 .parm = parm,385 .ret = 0,386 };387 388 dsos__for_each_dso(dsos, dsos__fprintf_buildid_cb, &args);389 return args.ret;390}391 392struct dsos__fprintf_cb_args {393 FILE *fp;394 size_t ret;395};396 397static int dsos__fprintf_cb(struct dso *dso, void *data)398{399 struct dsos__fprintf_cb_args *args = data;400 401 args->ret += dso__fprintf(dso, args->fp);402 return 0;403}404 405size_t dsos__fprintf(struct dsos *dsos, FILE *fp)406{407 struct dsos__fprintf_cb_args args = {408 .fp = fp,409 .ret = 0,410 };411 412 dsos__for_each_dso(dsos, dsos__fprintf_cb, &args);413 return args.ret;414}415 416static int dsos__hit_all_cb(struct dso *dso, void *data __maybe_unused)417{418 dso__set_hit(dso);419 return 0;420}421 422int dsos__hit_all(struct dsos *dsos)423{424 return dsos__for_each_dso(dsos, dsos__hit_all_cb, NULL);425}426 427struct dso *dsos__findnew_module_dso(struct dsos *dsos,428 struct machine *machine,429 struct kmod_path *m,430 const char *filename)431{432 struct dso *dso;433 434 down_write(&dsos->lock);435 436 dso = __dsos__find_id(dsos, m->name, NULL, /*cmp_short=*/true, /*write_locked=*/true);437 if (dso) {438 up_write(&dsos->lock);439 return dso;440 }441 /*442 * Failed to find the dso so create it. Change the name before adding it443 * to the array, to avoid unnecessary sorts and potential locking444 * issues.445 */446 dso = dso__new_id(m->name, /*id=*/NULL);447 if (!dso) {448 up_write(&dsos->lock);449 return NULL;450 }451 dso__set_basename(dso);452 dso__set_module_info(dso, m, machine);453 dso__set_long_name(dso, strdup(filename), true);454 dso__set_kernel(dso, DSO_SPACE__KERNEL);455 __dsos__add(dsos, dso);456 457 up_write(&dsos->lock);458 return dso;459}460 461static int dsos__find_kernel_dso_cb(struct dso *dso, void *data)462{463 struct dso **res = data;464 /*465 * The cpumode passed to is_kernel_module is not the cpumode of *this*466 * event. If we insist on passing correct cpumode to is_kernel_module,467 * we should record the cpumode when we adding this dso to the linked468 * list.469 *470 * However we don't really need passing correct cpumode. We know the471 * correct cpumode must be kernel mode (if not, we should not link it472 * onto kernel_dsos list).473 *474 * Therefore, we pass PERF_RECORD_MISC_CPUMODE_UNKNOWN.475 * is_kernel_module() treats it as a kernel cpumode.476 */477 if (!dso__kernel(dso) ||478 is_kernel_module(dso__long_name(dso), PERF_RECORD_MISC_CPUMODE_UNKNOWN))479 return 0;480 481 *res = dso__get(dso);482 return 1;483}484 485struct dso *dsos__find_kernel_dso(struct dsos *dsos)486{487 struct dso *res = NULL;488 489 dsos__for_each_dso(dsos, dsos__find_kernel_dso_cb, &res);490 return res;491}492 493int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data), void *data)494{495 int err;496 497 down_read(&dsos->lock);498 err = __dsos__for_each_dso(dsos, cb, data);499 up_read(&dsos->lock);500 return err;501}502