18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include "debug.h" 38c2ecf20Sopenharmony_ci#include "dsos.h" 48c2ecf20Sopenharmony_ci#include "dso.h" 58c2ecf20Sopenharmony_ci#include "vdso.h" 68c2ecf20Sopenharmony_ci#include "namespaces.h" 78c2ecf20Sopenharmony_ci#include <libgen.h> 88c2ecf20Sopenharmony_ci#include <stdlib.h> 98c2ecf20Sopenharmony_ci#include <string.h> 108c2ecf20Sopenharmony_ci#include <symbol.h> // filename__read_build_id 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic int __dso_id__cmp(struct dso_id *a, struct dso_id *b) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci if (a->maj > b->maj) return -1; 158c2ecf20Sopenharmony_ci if (a->maj < b->maj) return 1; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci if (a->min > b->min) return -1; 188c2ecf20Sopenharmony_ci if (a->min < b->min) return 1; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci if (a->ino > b->ino) return -1; 218c2ecf20Sopenharmony_ci if (a->ino < b->ino) return 1; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci /* 248c2ecf20Sopenharmony_ci * Synthesized MMAP events have zero ino_generation, avoid comparing 258c2ecf20Sopenharmony_ci * them with MMAP events with actual ino_generation. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * I found it harmful because the mismatch resulted in a new 288c2ecf20Sopenharmony_ci * dso that did not have a build ID whereas the original dso did have a 298c2ecf20Sopenharmony_ci * build ID. The build ID was essential because the object was not found 308c2ecf20Sopenharmony_ci * otherwise. - Adrian 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci if (a->ino_generation && b->ino_generation) { 338c2ecf20Sopenharmony_ci if (a->ino_generation > b->ino_generation) return -1; 348c2ecf20Sopenharmony_ci if (a->ino_generation < b->ino_generation) return 1; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic bool dso_id__empty(struct dso_id *id) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci if (!id) 438c2ecf20Sopenharmony_ci return true; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return !id->maj && !id->min && !id->ino && !id->ino_generation; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void dso__inject_id(struct dso *dso, struct dso_id *id) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci dso->id.maj = id->maj; 518c2ecf20Sopenharmony_ci dso->id.min = id->min; 528c2ecf20Sopenharmony_ci dso->id.ino = id->ino; 538c2ecf20Sopenharmony_ci dso->id.ino_generation = id->ino_generation; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int dso_id__cmp(struct dso_id *a, struct dso_id *b) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci /* 598c2ecf20Sopenharmony_ci * The second is always dso->id, so zeroes if not set, assume passing 608c2ecf20Sopenharmony_ci * NULL for a means a zeroed id 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci if (dso_id__empty(a) || dso_id__empty(b)) 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return __dso_id__cmp(a, b); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciint dso__cmp_id(struct dso *a, struct dso *b) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return __dso_id__cmp(&a->id, &b->id); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cibool __dsos__read_build_ids(struct list_head *head, bool with_hits) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci bool have_build_id = false; 768c2ecf20Sopenharmony_ci struct dso *pos; 778c2ecf20Sopenharmony_ci struct nscookie nsc; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci list_for_each_entry(pos, head, node) { 808c2ecf20Sopenharmony_ci if (with_hits && !pos->hit && !dso__is_vdso(pos)) 818c2ecf20Sopenharmony_ci continue; 828c2ecf20Sopenharmony_ci if (pos->has_build_id) { 838c2ecf20Sopenharmony_ci have_build_id = true; 848c2ecf20Sopenharmony_ci continue; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci nsinfo__mountns_enter(pos->nsinfo, &nsc); 878c2ecf20Sopenharmony_ci if (filename__read_build_id(pos->long_name, &pos->bid) > 0) { 888c2ecf20Sopenharmony_ci have_build_id = true; 898c2ecf20Sopenharmony_ci pos->has_build_id = true; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci nsinfo__mountns_exit(&nsc); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return have_build_id; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int __dso__cmp_long_name(const char *long_name, struct dso_id *id, struct dso *b) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int rc = strcmp(long_name, b->long_name); 1008c2ecf20Sopenharmony_ci return rc ?: dso_id__cmp(id, &b->id); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int __dso__cmp_short_name(const char *short_name, struct dso_id *id, struct dso *b) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int rc = strcmp(short_name, b->short_name); 1068c2ecf20Sopenharmony_ci return rc ?: dso_id__cmp(id, &b->id); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int dso__cmp_short_name(struct dso *a, struct dso *b) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci return __dso__cmp_short_name(a->short_name, &a->id, b); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * Find a matching entry and/or link current entry to RB tree. 1168c2ecf20Sopenharmony_ci * Either one of the dso or name parameter must be non-NULL or the 1178c2ecf20Sopenharmony_ci * function will not work. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistruct dso *__dsos__findnew_link_by_longname_id(struct rb_root *root, struct dso *dso, 1208c2ecf20Sopenharmony_ci const char *name, struct dso_id *id) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct rb_node **p = &root->rb_node; 1238c2ecf20Sopenharmony_ci struct rb_node *parent = NULL; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!name) 1268c2ecf20Sopenharmony_ci name = dso->long_name; 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * Find node with the matching name 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci while (*p) { 1318c2ecf20Sopenharmony_ci struct dso *this = rb_entry(*p, struct dso, rb_node); 1328c2ecf20Sopenharmony_ci int rc = __dso__cmp_long_name(name, id, this); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci parent = *p; 1358c2ecf20Sopenharmony_ci if (rc == 0) { 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * In case the new DSO is a duplicate of an existing 1388c2ecf20Sopenharmony_ci * one, print a one-time warning & put the new entry 1398c2ecf20Sopenharmony_ci * at the end of the list of duplicates. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci if (!dso || (dso == this)) 1428c2ecf20Sopenharmony_ci return this; /* Find matching dso */ 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * The core kernel DSOs may have duplicated long name. 1458c2ecf20Sopenharmony_ci * In this case, the short name should be different. 1468c2ecf20Sopenharmony_ci * Comparing the short names to differentiate the DSOs. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci rc = dso__cmp_short_name(dso, this); 1498c2ecf20Sopenharmony_ci if (rc == 0) { 1508c2ecf20Sopenharmony_ci pr_err("Duplicated dso name: %s\n", name); 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci if (rc < 0) 1558c2ecf20Sopenharmony_ci p = &parent->rb_left; 1568c2ecf20Sopenharmony_ci else 1578c2ecf20Sopenharmony_ci p = &parent->rb_right; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci if (dso) { 1608c2ecf20Sopenharmony_ci /* Add new node and rebalance tree */ 1618c2ecf20Sopenharmony_ci rb_link_node(&dso->rb_node, parent, p); 1628c2ecf20Sopenharmony_ci rb_insert_color(&dso->rb_node, root); 1638c2ecf20Sopenharmony_ci dso->root = root; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci return NULL; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_civoid __dsos__add(struct dsos *dsos, struct dso *dso) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci list_add_tail(&dso->node, &dsos->head); 1718c2ecf20Sopenharmony_ci __dsos__findnew_link_by_longname_id(&dsos->root, dso, NULL, &dso->id); 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * It is now in the linked list, grab a reference, then garbage collect 1748c2ecf20Sopenharmony_ci * this when needing memory, by looking at LRU dso instances in the 1758c2ecf20Sopenharmony_ci * list with atomic_read(&dso->refcnt) == 1, i.e. no references 1768c2ecf20Sopenharmony_ci * anywhere besides the one for the list, do, under a lock for the 1778c2ecf20Sopenharmony_ci * list: remove it from the list, then a dso__put(), that probably will 1788c2ecf20Sopenharmony_ci * be the last and will then call dso__delete(), end of life. 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * That, or at the end of the 'struct machine' lifetime, when all 1818c2ecf20Sopenharmony_ci * 'struct dso' instances will be removed from the list, in 1828c2ecf20Sopenharmony_ci * dsos__exit(), if they have no other reference from some other data 1838c2ecf20Sopenharmony_ci * structure. 1848c2ecf20Sopenharmony_ci * 1858c2ecf20Sopenharmony_ci * E.g.: after processing a 'perf.data' file and storing references 1868c2ecf20Sopenharmony_ci * to objects instantiated while processing events, we will have 1878c2ecf20Sopenharmony_ci * references to the 'thread', 'map', 'dso' structs all from 'struct 1888c2ecf20Sopenharmony_ci * hist_entry' instances, but we may not need anything not referenced, 1898c2ecf20Sopenharmony_ci * so we might as well call machines__exit()/machines__delete() and 1908c2ecf20Sopenharmony_ci * garbage collect it. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci dso__get(dso); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_civoid dsos__add(struct dsos *dsos, struct dso *dso) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci down_write(&dsos->lock); 1988c2ecf20Sopenharmony_ci __dsos__add(dsos, dso); 1998c2ecf20Sopenharmony_ci up_write(&dsos->lock); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic struct dso *__dsos__findnew_by_longname_id(struct rb_root *root, const char *name, struct dso_id *id) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return __dsos__findnew_link_by_longname_id(root, NULL, name, id); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic struct dso *__dsos__find_id(struct dsos *dsos, const char *name, struct dso_id *id, bool cmp_short) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct dso *pos; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (cmp_short) { 2128c2ecf20Sopenharmony_ci list_for_each_entry(pos, &dsos->head, node) 2138c2ecf20Sopenharmony_ci if (__dso__cmp_short_name(name, id, pos) == 0) 2148c2ecf20Sopenharmony_ci return pos; 2158c2ecf20Sopenharmony_ci return NULL; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci return __dsos__findnew_by_longname_id(&dsos->root, name, id); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistruct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci return __dsos__find_id(dsos, name, NULL, cmp_short); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void dso__set_basename(struct dso *dso) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci char *base, *lname; 2288c2ecf20Sopenharmony_ci int tid; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) { 2318c2ecf20Sopenharmony_ci if (asprintf(&base, "[JIT] tid %d", tid) < 0) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * basename() may modify path buffer, so we must pass 2368c2ecf20Sopenharmony_ci * a copy. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci lname = strdup(dso->long_name); 2398c2ecf20Sopenharmony_ci if (!lname) 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* 2438c2ecf20Sopenharmony_ci * basename() may return a pointer to internal 2448c2ecf20Sopenharmony_ci * storage which is reused in subsequent calls 2458c2ecf20Sopenharmony_ci * so copy the result. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci base = strdup(basename(lname)); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci free(lname); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!base) 2528c2ecf20Sopenharmony_ci return; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci dso__set_short_name(dso, base, true); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct dso *dso = dso__new_id(name, id); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (dso != NULL) { 2628c2ecf20Sopenharmony_ci __dsos__add(dsos, dso); 2638c2ecf20Sopenharmony_ci dso__set_basename(dso); 2648c2ecf20Sopenharmony_ci /* Put dso here because __dsos_add already got it */ 2658c2ecf20Sopenharmony_ci dso__put(dso); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci return dso; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistruct dso *__dsos__addnew(struct dsos *dsos, const char *name) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci return __dsos__addnew_id(dsos, name, NULL); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct dso *dso = __dsos__find_id(dsos, name, id, false); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (dso && dso_id__empty(&dso->id) && !dso_id__empty(id)) 2808c2ecf20Sopenharmony_ci dso__inject_id(dso, id); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return dso ? dso : __dsos__addnew_id(dsos, name, id); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistruct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct dso *dso; 2888c2ecf20Sopenharmony_ci down_write(&dsos->lock); 2898c2ecf20Sopenharmony_ci dso = dso__get(__dsos__findnew_id(dsos, name, id)); 2908c2ecf20Sopenharmony_ci up_write(&dsos->lock); 2918c2ecf20Sopenharmony_ci return dso; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cisize_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, 2958c2ecf20Sopenharmony_ci bool (skip)(struct dso *dso, int parm), int parm) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct dso *pos; 2988c2ecf20Sopenharmony_ci size_t ret = 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci list_for_each_entry(pos, head, node) { 3018c2ecf20Sopenharmony_ci char sbuild_id[SBUILD_ID_SIZE]; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (skip && skip(pos, parm)) 3048c2ecf20Sopenharmony_ci continue; 3058c2ecf20Sopenharmony_ci build_id__sprintf(&pos->bid, sbuild_id); 3068c2ecf20Sopenharmony_ci ret += fprintf(fp, "%-40s %s\n", sbuild_id, pos->long_name); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cisize_t __dsos__fprintf(struct list_head *head, FILE *fp) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct dso *pos; 3148c2ecf20Sopenharmony_ci size_t ret = 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci list_for_each_entry(pos, head, node) { 3178c2ecf20Sopenharmony_ci ret += dso__fprintf(pos, fp); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci} 322