162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include "debug.h" 362306a36Sopenharmony_ci#include "dsos.h" 462306a36Sopenharmony_ci#include "dso.h" 562306a36Sopenharmony_ci#include "util.h" 662306a36Sopenharmony_ci#include "vdso.h" 762306a36Sopenharmony_ci#include "namespaces.h" 862306a36Sopenharmony_ci#include <errno.h> 962306a36Sopenharmony_ci#include <libgen.h> 1062306a36Sopenharmony_ci#include <stdlib.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <symbol.h> // filename__read_build_id 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int __dso_id__cmp(struct dso_id *a, struct dso_id *b) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci if (a->maj > b->maj) return -1; 1862306a36Sopenharmony_ci if (a->maj < b->maj) return 1; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci if (a->min > b->min) return -1; 2162306a36Sopenharmony_ci if (a->min < b->min) return 1; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (a->ino > b->ino) return -1; 2462306a36Sopenharmony_ci if (a->ino < b->ino) return 1; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * Synthesized MMAP events have zero ino_generation, avoid comparing 2862306a36Sopenharmony_ci * them with MMAP events with actual ino_generation. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * I found it harmful because the mismatch resulted in a new 3162306a36Sopenharmony_ci * dso that did not have a build ID whereas the original dso did have a 3262306a36Sopenharmony_ci * build ID. The build ID was essential because the object was not found 3362306a36Sopenharmony_ci * otherwise. - Adrian 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci if (a->ino_generation && b->ino_generation) { 3662306a36Sopenharmony_ci if (a->ino_generation > b->ino_generation) return -1; 3762306a36Sopenharmony_ci if (a->ino_generation < b->ino_generation) return 1; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic bool dso_id__empty(struct dso_id *id) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci if (!id) 4662306a36Sopenharmony_ci return true; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return !id->maj && !id->min && !id->ino && !id->ino_generation; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void dso__inject_id(struct dso *dso, struct dso_id *id) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci dso->id.maj = id->maj; 5462306a36Sopenharmony_ci dso->id.min = id->min; 5562306a36Sopenharmony_ci dso->id.ino = id->ino; 5662306a36Sopenharmony_ci dso->id.ino_generation = id->ino_generation; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int dso_id__cmp(struct dso_id *a, struct dso_id *b) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci /* 6262306a36Sopenharmony_ci * The second is always dso->id, so zeroes if not set, assume passing 6362306a36Sopenharmony_ci * NULL for a means a zeroed id 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci if (dso_id__empty(a) || dso_id__empty(b)) 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return __dso_id__cmp(a, b); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciint dso__cmp_id(struct dso *a, struct dso *b) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return __dso_id__cmp(&a->id, &b->id); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cibool __dsos__read_build_ids(struct list_head *head, bool with_hits) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci bool have_build_id = false; 7962306a36Sopenharmony_ci struct dso *pos; 8062306a36Sopenharmony_ci struct nscookie nsc; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci list_for_each_entry(pos, head, node) { 8362306a36Sopenharmony_ci if (with_hits && !pos->hit && !dso__is_vdso(pos)) 8462306a36Sopenharmony_ci continue; 8562306a36Sopenharmony_ci if (pos->has_build_id) { 8662306a36Sopenharmony_ci have_build_id = true; 8762306a36Sopenharmony_ci continue; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci nsinfo__mountns_enter(pos->nsinfo, &nsc); 9062306a36Sopenharmony_ci if (filename__read_build_id(pos->long_name, &pos->bid) > 0) { 9162306a36Sopenharmony_ci have_build_id = true; 9262306a36Sopenharmony_ci pos->has_build_id = true; 9362306a36Sopenharmony_ci } else if (errno == ENOENT && pos->nsinfo) { 9462306a36Sopenharmony_ci char *new_name = dso__filename_with_chroot(pos, pos->long_name); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (new_name && filename__read_build_id(new_name, 9762306a36Sopenharmony_ci &pos->bid) > 0) { 9862306a36Sopenharmony_ci have_build_id = true; 9962306a36Sopenharmony_ci pos->has_build_id = true; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci free(new_name); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci nsinfo__mountns_exit(&nsc); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return have_build_id; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int __dso__cmp_long_name(const char *long_name, struct dso_id *id, struct dso *b) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int rc = strcmp(long_name, b->long_name); 11262306a36Sopenharmony_ci return rc ?: dso_id__cmp(id, &b->id); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int __dso__cmp_short_name(const char *short_name, struct dso_id *id, struct dso *b) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int rc = strcmp(short_name, b->short_name); 11862306a36Sopenharmony_ci return rc ?: dso_id__cmp(id, &b->id); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int dso__cmp_short_name(struct dso *a, struct dso *b) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci return __dso__cmp_short_name(a->short_name, &a->id, b); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * Find a matching entry and/or link current entry to RB tree. 12862306a36Sopenharmony_ci * Either one of the dso or name parameter must be non-NULL or the 12962306a36Sopenharmony_ci * function will not work. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistruct dso *__dsos__findnew_link_by_longname_id(struct rb_root *root, struct dso *dso, 13262306a36Sopenharmony_ci const char *name, struct dso_id *id) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct rb_node **p = &root->rb_node; 13562306a36Sopenharmony_ci struct rb_node *parent = NULL; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!name) 13862306a36Sopenharmony_ci name = dso->long_name; 13962306a36Sopenharmony_ci /* 14062306a36Sopenharmony_ci * Find node with the matching name 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci while (*p) { 14362306a36Sopenharmony_ci struct dso *this = rb_entry(*p, struct dso, rb_node); 14462306a36Sopenharmony_ci int rc = __dso__cmp_long_name(name, id, this); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci parent = *p; 14762306a36Sopenharmony_ci if (rc == 0) { 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * In case the new DSO is a duplicate of an existing 15062306a36Sopenharmony_ci * one, print a one-time warning & put the new entry 15162306a36Sopenharmony_ci * at the end of the list of duplicates. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci if (!dso || (dso == this)) 15462306a36Sopenharmony_ci return this; /* Find matching dso */ 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * The core kernel DSOs may have duplicated long name. 15762306a36Sopenharmony_ci * In this case, the short name should be different. 15862306a36Sopenharmony_ci * Comparing the short names to differentiate the DSOs. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci rc = dso__cmp_short_name(dso, this); 16162306a36Sopenharmony_ci if (rc == 0) { 16262306a36Sopenharmony_ci pr_err("Duplicated dso name: %s\n", name); 16362306a36Sopenharmony_ci return NULL; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci if (rc < 0) 16762306a36Sopenharmony_ci p = &parent->rb_left; 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci p = &parent->rb_right; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci if (dso) { 17262306a36Sopenharmony_ci /* Add new node and rebalance tree */ 17362306a36Sopenharmony_ci rb_link_node(&dso->rb_node, parent, p); 17462306a36Sopenharmony_ci rb_insert_color(&dso->rb_node, root); 17562306a36Sopenharmony_ci dso->root = root; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci return NULL; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_civoid __dsos__add(struct dsos *dsos, struct dso *dso) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci list_add_tail(&dso->node, &dsos->head); 18362306a36Sopenharmony_ci __dsos__findnew_link_by_longname_id(&dsos->root, dso, NULL, &dso->id); 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * It is now in the linked list, grab a reference, then garbage collect 18662306a36Sopenharmony_ci * this when needing memory, by looking at LRU dso instances in the 18762306a36Sopenharmony_ci * list with atomic_read(&dso->refcnt) == 1, i.e. no references 18862306a36Sopenharmony_ci * anywhere besides the one for the list, do, under a lock for the 18962306a36Sopenharmony_ci * list: remove it from the list, then a dso__put(), that probably will 19062306a36Sopenharmony_ci * be the last and will then call dso__delete(), end of life. 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * That, or at the end of the 'struct machine' lifetime, when all 19362306a36Sopenharmony_ci * 'struct dso' instances will be removed from the list, in 19462306a36Sopenharmony_ci * dsos__exit(), if they have no other reference from some other data 19562306a36Sopenharmony_ci * structure. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * E.g.: after processing a 'perf.data' file and storing references 19862306a36Sopenharmony_ci * to objects instantiated while processing events, we will have 19962306a36Sopenharmony_ci * references to the 'thread', 'map', 'dso' structs all from 'struct 20062306a36Sopenharmony_ci * hist_entry' instances, but we may not need anything not referenced, 20162306a36Sopenharmony_ci * so we might as well call machines__exit()/machines__delete() and 20262306a36Sopenharmony_ci * garbage collect it. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci dso__get(dso); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_civoid dsos__add(struct dsos *dsos, struct dso *dso) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci down_write(&dsos->lock); 21062306a36Sopenharmony_ci __dsos__add(dsos, dso); 21162306a36Sopenharmony_ci up_write(&dsos->lock); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic struct dso *__dsos__findnew_by_longname_id(struct rb_root *root, const char *name, struct dso_id *id) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci return __dsos__findnew_link_by_longname_id(root, NULL, name, id); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct dso *__dsos__find_id(struct dsos *dsos, const char *name, struct dso_id *id, bool cmp_short) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct dso *pos; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (cmp_short) { 22462306a36Sopenharmony_ci list_for_each_entry(pos, &dsos->head, node) 22562306a36Sopenharmony_ci if (__dso__cmp_short_name(name, id, pos) == 0) 22662306a36Sopenharmony_ci return pos; 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci return __dsos__findnew_by_longname_id(&dsos->root, name, id); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistruct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci return __dsos__find_id(dsos, name, NULL, cmp_short); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void dso__set_basename(struct dso *dso) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci char *base, *lname; 24062306a36Sopenharmony_ci int tid; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) { 24362306a36Sopenharmony_ci if (asprintf(&base, "[JIT] tid %d", tid) < 0) 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci } else { 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * basename() may modify path buffer, so we must pass 24862306a36Sopenharmony_ci * a copy. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci lname = strdup(dso->long_name); 25162306a36Sopenharmony_ci if (!lname) 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * basename() may return a pointer to internal 25662306a36Sopenharmony_ci * storage which is reused in subsequent calls 25762306a36Sopenharmony_ci * so copy the result. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci base = strdup(basename(lname)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci free(lname); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!base) 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci dso__set_short_name(dso, base, true); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct dso *dso = dso__new_id(name, id); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (dso != NULL) { 27462306a36Sopenharmony_ci __dsos__add(dsos, dso); 27562306a36Sopenharmony_ci dso__set_basename(dso); 27662306a36Sopenharmony_ci /* Put dso here because __dsos_add already got it */ 27762306a36Sopenharmony_ci dso__put(dso); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci return dso; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistruct dso *__dsos__addnew(struct dsos *dsos, const char *name) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci return __dsos__addnew_id(dsos, name, NULL); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct dso *dso = __dsos__find_id(dsos, name, id, false); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (dso && dso_id__empty(&dso->id) && !dso_id__empty(id)) 29262306a36Sopenharmony_ci dso__inject_id(dso, id); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return dso ? dso : __dsos__addnew_id(dsos, name, id); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistruct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct dso *dso; 30062306a36Sopenharmony_ci down_write(&dsos->lock); 30162306a36Sopenharmony_ci dso = dso__get(__dsos__findnew_id(dsos, name, id)); 30262306a36Sopenharmony_ci up_write(&dsos->lock); 30362306a36Sopenharmony_ci return dso; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cisize_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, 30762306a36Sopenharmony_ci bool (skip)(struct dso *dso, int parm), int parm) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct dso *pos; 31062306a36Sopenharmony_ci size_t ret = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci list_for_each_entry(pos, head, node) { 31362306a36Sopenharmony_ci char sbuild_id[SBUILD_ID_SIZE]; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (skip && skip(pos, parm)) 31662306a36Sopenharmony_ci continue; 31762306a36Sopenharmony_ci build_id__sprintf(&pos->bid, sbuild_id); 31862306a36Sopenharmony_ci ret += fprintf(fp, "%-40s %s\n", sbuild_id, pos->long_name); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cisize_t __dsos__fprintf(struct list_head *head, FILE *fp) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct dso *pos; 32662306a36Sopenharmony_ci size_t ret = 0; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci list_for_each_entry(pos, head, node) { 32962306a36Sopenharmony_ci ret += dso__fprintf(pos, fp); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return ret; 33362306a36Sopenharmony_ci} 334