14a3cec84SArnaldo Carvalho de Melo // SPDX-License-Identifier: GPL-2.0 24a3cec84SArnaldo Carvalho de Melo #include "debug.h" 34a3cec84SArnaldo Carvalho de Melo #include "dsos.h" 44a3cec84SArnaldo Carvalho de Melo #include "dso.h" 54a3cec84SArnaldo Carvalho de Melo #include "vdso.h" 64a3cec84SArnaldo Carvalho de Melo #include "namespaces.h" 74a3cec84SArnaldo Carvalho de Melo #include <libgen.h> 84a3cec84SArnaldo Carvalho de Melo #include <stdlib.h> 94a3cec84SArnaldo Carvalho de Melo #include <string.h> 104a3cec84SArnaldo Carvalho de Melo #include <symbol.h> // filename__read_build_id 114a3cec84SArnaldo Carvalho de Melo 120e3149f8SArnaldo Carvalho de Melo static int __dso_id__cmp(struct dso_id *a, struct dso_id *b) 137b59a824SArnaldo Carvalho de Melo { 147b59a824SArnaldo Carvalho de Melo if (a->maj > b->maj) return -1; 157b59a824SArnaldo Carvalho de Melo if (a->maj < b->maj) return 1; 167b59a824SArnaldo Carvalho de Melo 177b59a824SArnaldo Carvalho de Melo if (a->min > b->min) return -1; 187b59a824SArnaldo Carvalho de Melo if (a->min < b->min) return 1; 197b59a824SArnaldo Carvalho de Melo 207b59a824SArnaldo Carvalho de Melo if (a->ino > b->ino) return -1; 217b59a824SArnaldo Carvalho de Melo if (a->ino < b->ino) return 1; 227b59a824SArnaldo Carvalho de Melo 237b59a824SArnaldo Carvalho de Melo if (a->ino_generation > b->ino_generation) return -1; 247b59a824SArnaldo Carvalho de Melo if (a->ino_generation < b->ino_generation) return 1; 257b59a824SArnaldo Carvalho de Melo 267b59a824SArnaldo Carvalho de Melo return 0; 277b59a824SArnaldo Carvalho de Melo } 287b59a824SArnaldo Carvalho de Melo 290e3149f8SArnaldo Carvalho de Melo static int dso_id__cmp(struct dso_id *a, struct dso_id *b) 300e3149f8SArnaldo Carvalho de Melo { 310e3149f8SArnaldo Carvalho de Melo /* 320e3149f8SArnaldo Carvalho de Melo * The second is always dso->id, so zeroes if not set, assume passing 330e3149f8SArnaldo Carvalho de Melo * NULL for a means a zeroed id 340e3149f8SArnaldo Carvalho de Melo */ 350e3149f8SArnaldo Carvalho de Melo if (a == NULL) 360e3149f8SArnaldo Carvalho de Melo return 0; 370e3149f8SArnaldo Carvalho de Melo 380e3149f8SArnaldo Carvalho de Melo return __dso_id__cmp(a, b); 390e3149f8SArnaldo Carvalho de Melo } 400e3149f8SArnaldo Carvalho de Melo 410e3149f8SArnaldo Carvalho de Melo int dso__cmp_id(struct dso *a, struct dso *b) 420e3149f8SArnaldo Carvalho de Melo { 430e3149f8SArnaldo Carvalho de Melo return __dso_id__cmp(&a->id, &b->id); 440e3149f8SArnaldo Carvalho de Melo } 450e3149f8SArnaldo Carvalho de Melo 464a3cec84SArnaldo Carvalho de Melo bool __dsos__read_build_ids(struct list_head *head, bool with_hits) 474a3cec84SArnaldo Carvalho de Melo { 484a3cec84SArnaldo Carvalho de Melo bool have_build_id = false; 494a3cec84SArnaldo Carvalho de Melo struct dso *pos; 504a3cec84SArnaldo Carvalho de Melo struct nscookie nsc; 514a3cec84SArnaldo Carvalho de Melo 524a3cec84SArnaldo Carvalho de Melo list_for_each_entry(pos, head, node) { 534a3cec84SArnaldo Carvalho de Melo if (with_hits && !pos->hit && !dso__is_vdso(pos)) 544a3cec84SArnaldo Carvalho de Melo continue; 554a3cec84SArnaldo Carvalho de Melo if (pos->has_build_id) { 564a3cec84SArnaldo Carvalho de Melo have_build_id = true; 574a3cec84SArnaldo Carvalho de Melo continue; 584a3cec84SArnaldo Carvalho de Melo } 594a3cec84SArnaldo Carvalho de Melo nsinfo__mountns_enter(pos->nsinfo, &nsc); 604a3cec84SArnaldo Carvalho de Melo if (filename__read_build_id(pos->long_name, pos->build_id, 614a3cec84SArnaldo Carvalho de Melo sizeof(pos->build_id)) > 0) { 624a3cec84SArnaldo Carvalho de Melo have_build_id = true; 634a3cec84SArnaldo Carvalho de Melo pos->has_build_id = true; 644a3cec84SArnaldo Carvalho de Melo } 654a3cec84SArnaldo Carvalho de Melo nsinfo__mountns_exit(&nsc); 664a3cec84SArnaldo Carvalho de Melo } 674a3cec84SArnaldo Carvalho de Melo 684a3cec84SArnaldo Carvalho de Melo return have_build_id; 694a3cec84SArnaldo Carvalho de Melo } 704a3cec84SArnaldo Carvalho de Melo 710e3149f8SArnaldo Carvalho de Melo static int __dso__cmp_long_name(const char *long_name, struct dso_id *id, struct dso *b) 720e3149f8SArnaldo Carvalho de Melo { 730e3149f8SArnaldo Carvalho de Melo int rc = strcmp(long_name, b->long_name); 740e3149f8SArnaldo Carvalho de Melo return rc ?: dso_id__cmp(id, &b->id); 750e3149f8SArnaldo Carvalho de Melo } 760e3149f8SArnaldo Carvalho de Melo 770e3149f8SArnaldo Carvalho de Melo static int __dso__cmp_short_name(const char *short_name, struct dso_id *id, struct dso *b) 780e3149f8SArnaldo Carvalho de Melo { 790e3149f8SArnaldo Carvalho de Melo int rc = strcmp(short_name, b->short_name); 800e3149f8SArnaldo Carvalho de Melo return rc ?: dso_id__cmp(id, &b->id); 810e3149f8SArnaldo Carvalho de Melo } 820e3149f8SArnaldo Carvalho de Melo 830e3149f8SArnaldo Carvalho de Melo static int dso__cmp_short_name(struct dso *a, struct dso *b) 840e3149f8SArnaldo Carvalho de Melo { 850e3149f8SArnaldo Carvalho de Melo return __dso__cmp_short_name(a->short_name, &a->id, b); 860e3149f8SArnaldo Carvalho de Melo } 870e3149f8SArnaldo Carvalho de Melo 884a3cec84SArnaldo Carvalho de Melo /* 894a3cec84SArnaldo Carvalho de Melo * Find a matching entry and/or link current entry to RB tree. 904a3cec84SArnaldo Carvalho de Melo * Either one of the dso or name parameter must be non-NULL or the 914a3cec84SArnaldo Carvalho de Melo * function will not work. 924a3cec84SArnaldo Carvalho de Melo */ 930e3149f8SArnaldo Carvalho de Melo struct dso *__dsos__findnew_link_by_longname_id(struct rb_root *root, struct dso *dso, 940e3149f8SArnaldo Carvalho de Melo const char *name, struct dso_id *id) 954a3cec84SArnaldo Carvalho de Melo { 964a3cec84SArnaldo Carvalho de Melo struct rb_node **p = &root->rb_node; 974a3cec84SArnaldo Carvalho de Melo struct rb_node *parent = NULL; 984a3cec84SArnaldo Carvalho de Melo 994a3cec84SArnaldo Carvalho de Melo if (!name) 1004a3cec84SArnaldo Carvalho de Melo name = dso->long_name; 1014a3cec84SArnaldo Carvalho de Melo /* 1024a3cec84SArnaldo Carvalho de Melo * Find node with the matching name 1034a3cec84SArnaldo Carvalho de Melo */ 1044a3cec84SArnaldo Carvalho de Melo while (*p) { 1054a3cec84SArnaldo Carvalho de Melo struct dso *this = rb_entry(*p, struct dso, rb_node); 1060e3149f8SArnaldo Carvalho de Melo int rc = __dso__cmp_long_name(name, id, this); 1074a3cec84SArnaldo Carvalho de Melo 1084a3cec84SArnaldo Carvalho de Melo parent = *p; 1094a3cec84SArnaldo Carvalho de Melo if (rc == 0) { 1104a3cec84SArnaldo Carvalho de Melo /* 1114a3cec84SArnaldo Carvalho de Melo * In case the new DSO is a duplicate of an existing 1124a3cec84SArnaldo Carvalho de Melo * one, print a one-time warning & put the new entry 1134a3cec84SArnaldo Carvalho de Melo * at the end of the list of duplicates. 1144a3cec84SArnaldo Carvalho de Melo */ 1154a3cec84SArnaldo Carvalho de Melo if (!dso || (dso == this)) 1164a3cec84SArnaldo Carvalho de Melo return this; /* Find matching dso */ 1174a3cec84SArnaldo Carvalho de Melo /* 1184a3cec84SArnaldo Carvalho de Melo * The core kernel DSOs may have duplicated long name. 1194a3cec84SArnaldo Carvalho de Melo * In this case, the short name should be different. 1204a3cec84SArnaldo Carvalho de Melo * Comparing the short names to differentiate the DSOs. 1214a3cec84SArnaldo Carvalho de Melo */ 1220e3149f8SArnaldo Carvalho de Melo rc = dso__cmp_short_name(dso, this); 1234a3cec84SArnaldo Carvalho de Melo if (rc == 0) { 1244a3cec84SArnaldo Carvalho de Melo pr_err("Duplicated dso name: %s\n", name); 1254a3cec84SArnaldo Carvalho de Melo return NULL; 1264a3cec84SArnaldo Carvalho de Melo } 1274a3cec84SArnaldo Carvalho de Melo } 1284a3cec84SArnaldo Carvalho de Melo if (rc < 0) 1294a3cec84SArnaldo Carvalho de Melo p = &parent->rb_left; 1304a3cec84SArnaldo Carvalho de Melo else 1314a3cec84SArnaldo Carvalho de Melo p = &parent->rb_right; 1324a3cec84SArnaldo Carvalho de Melo } 1334a3cec84SArnaldo Carvalho de Melo if (dso) { 1344a3cec84SArnaldo Carvalho de Melo /* Add new node and rebalance tree */ 1354a3cec84SArnaldo Carvalho de Melo rb_link_node(&dso->rb_node, parent, p); 1364a3cec84SArnaldo Carvalho de Melo rb_insert_color(&dso->rb_node, root); 1374a3cec84SArnaldo Carvalho de Melo dso->root = root; 1384a3cec84SArnaldo Carvalho de Melo } 1394a3cec84SArnaldo Carvalho de Melo return NULL; 1404a3cec84SArnaldo Carvalho de Melo } 1414a3cec84SArnaldo Carvalho de Melo 1424a3cec84SArnaldo Carvalho de Melo void __dsos__add(struct dsos *dsos, struct dso *dso) 1434a3cec84SArnaldo Carvalho de Melo { 1444a3cec84SArnaldo Carvalho de Melo list_add_tail(&dso->node, &dsos->head); 1450e3149f8SArnaldo Carvalho de Melo __dsos__findnew_link_by_longname_id(&dsos->root, dso, NULL, &dso->id); 1464a3cec84SArnaldo Carvalho de Melo /* 1474a3cec84SArnaldo Carvalho de Melo * It is now in the linked list, grab a reference, then garbage collect 1484a3cec84SArnaldo Carvalho de Melo * this when needing memory, by looking at LRU dso instances in the 1494a3cec84SArnaldo Carvalho de Melo * list with atomic_read(&dso->refcnt) == 1, i.e. no references 1504a3cec84SArnaldo Carvalho de Melo * anywhere besides the one for the list, do, under a lock for the 1514a3cec84SArnaldo Carvalho de Melo * list: remove it from the list, then a dso__put(), that probably will 1524a3cec84SArnaldo Carvalho de Melo * be the last and will then call dso__delete(), end of life. 1534a3cec84SArnaldo Carvalho de Melo * 1544a3cec84SArnaldo Carvalho de Melo * That, or at the end of the 'struct machine' lifetime, when all 1554a3cec84SArnaldo Carvalho de Melo * 'struct dso' instances will be removed from the list, in 1564a3cec84SArnaldo Carvalho de Melo * dsos__exit(), if they have no other reference from some other data 1574a3cec84SArnaldo Carvalho de Melo * structure. 1584a3cec84SArnaldo Carvalho de Melo * 1594a3cec84SArnaldo Carvalho de Melo * E.g.: after processing a 'perf.data' file and storing references 1604a3cec84SArnaldo Carvalho de Melo * to objects instantiated while processing events, we will have 1614a3cec84SArnaldo Carvalho de Melo * references to the 'thread', 'map', 'dso' structs all from 'struct 1624a3cec84SArnaldo Carvalho de Melo * hist_entry' instances, but we may not need anything not referenced, 1634a3cec84SArnaldo Carvalho de Melo * so we might as well call machines__exit()/machines__delete() and 1644a3cec84SArnaldo Carvalho de Melo * garbage collect it. 1654a3cec84SArnaldo Carvalho de Melo */ 1664a3cec84SArnaldo Carvalho de Melo dso__get(dso); 1674a3cec84SArnaldo Carvalho de Melo } 1684a3cec84SArnaldo Carvalho de Melo 1694a3cec84SArnaldo Carvalho de Melo void dsos__add(struct dsos *dsos, struct dso *dso) 1704a3cec84SArnaldo Carvalho de Melo { 1714a3cec84SArnaldo Carvalho de Melo down_write(&dsos->lock); 1724a3cec84SArnaldo Carvalho de Melo __dsos__add(dsos, dso); 1734a3cec84SArnaldo Carvalho de Melo up_write(&dsos->lock); 1744a3cec84SArnaldo Carvalho de Melo } 1754a3cec84SArnaldo Carvalho de Melo 1760e3149f8SArnaldo Carvalho de Melo static struct dso *__dsos__findnew_by_longname_id(struct rb_root *root, const char *name, struct dso_id *id) 1770e3149f8SArnaldo Carvalho de Melo { 1780e3149f8SArnaldo Carvalho de Melo return __dsos__findnew_link_by_longname_id(root, NULL, name, id); 1790e3149f8SArnaldo Carvalho de Melo } 1800e3149f8SArnaldo Carvalho de Melo 1810e3149f8SArnaldo Carvalho de Melo static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, struct dso_id *id, bool cmp_short) 1824a3cec84SArnaldo Carvalho de Melo { 1834a3cec84SArnaldo Carvalho de Melo struct dso *pos; 1844a3cec84SArnaldo Carvalho de Melo 1854a3cec84SArnaldo Carvalho de Melo if (cmp_short) { 1864a3cec84SArnaldo Carvalho de Melo list_for_each_entry(pos, &dsos->head, node) 1870e3149f8SArnaldo Carvalho de Melo if (__dso__cmp_short_name(name, id, pos) == 0) 1884a3cec84SArnaldo Carvalho de Melo return pos; 1894a3cec84SArnaldo Carvalho de Melo return NULL; 1904a3cec84SArnaldo Carvalho de Melo } 1910e3149f8SArnaldo Carvalho de Melo return __dsos__findnew_by_longname_id(&dsos->root, name, id); 1920e3149f8SArnaldo Carvalho de Melo } 1930e3149f8SArnaldo Carvalho de Melo 1940e3149f8SArnaldo Carvalho de Melo struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short) 1950e3149f8SArnaldo Carvalho de Melo { 1960e3149f8SArnaldo Carvalho de Melo return __dsos__find_id(dsos, name, NULL, cmp_short); 1974a3cec84SArnaldo Carvalho de Melo } 1984a3cec84SArnaldo Carvalho de Melo 1994a3cec84SArnaldo Carvalho de Melo static void dso__set_basename(struct dso *dso) 2004a3cec84SArnaldo Carvalho de Melo { 2014a3cec84SArnaldo Carvalho de Melo char *base, *lname; 2024a3cec84SArnaldo Carvalho de Melo int tid; 2034a3cec84SArnaldo Carvalho de Melo 2044a3cec84SArnaldo Carvalho de Melo if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) { 2054a3cec84SArnaldo Carvalho de Melo if (asprintf(&base, "[JIT] tid %d", tid) < 0) 2064a3cec84SArnaldo Carvalho de Melo return; 2074a3cec84SArnaldo Carvalho de Melo } else { 2084a3cec84SArnaldo Carvalho de Melo /* 2094a3cec84SArnaldo Carvalho de Melo * basename() may modify path buffer, so we must pass 2104a3cec84SArnaldo Carvalho de Melo * a copy. 2114a3cec84SArnaldo Carvalho de Melo */ 2124a3cec84SArnaldo Carvalho de Melo lname = strdup(dso->long_name); 2134a3cec84SArnaldo Carvalho de Melo if (!lname) 2144a3cec84SArnaldo Carvalho de Melo return; 2154a3cec84SArnaldo Carvalho de Melo 2164a3cec84SArnaldo Carvalho de Melo /* 2174a3cec84SArnaldo Carvalho de Melo * basename() may return a pointer to internal 2184a3cec84SArnaldo Carvalho de Melo * storage which is reused in subsequent calls 2194a3cec84SArnaldo Carvalho de Melo * so copy the result. 2204a3cec84SArnaldo Carvalho de Melo */ 2214a3cec84SArnaldo Carvalho de Melo base = strdup(basename(lname)); 2224a3cec84SArnaldo Carvalho de Melo 2234a3cec84SArnaldo Carvalho de Melo free(lname); 2244a3cec84SArnaldo Carvalho de Melo 2254a3cec84SArnaldo Carvalho de Melo if (!base) 2264a3cec84SArnaldo Carvalho de Melo return; 2274a3cec84SArnaldo Carvalho de Melo } 2284a3cec84SArnaldo Carvalho de Melo dso__set_short_name(dso, base, true); 2294a3cec84SArnaldo Carvalho de Melo } 2304a3cec84SArnaldo Carvalho de Melo 2310e3149f8SArnaldo Carvalho de Melo static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 2324a3cec84SArnaldo Carvalho de Melo { 2330e3149f8SArnaldo Carvalho de Melo struct dso *dso = dso__new_id(name, id); 2344a3cec84SArnaldo Carvalho de Melo 2354a3cec84SArnaldo Carvalho de Melo if (dso != NULL) { 2364a3cec84SArnaldo Carvalho de Melo __dsos__add(dsos, dso); 2374a3cec84SArnaldo Carvalho de Melo dso__set_basename(dso); 2384a3cec84SArnaldo Carvalho de Melo /* Put dso here because __dsos_add already got it */ 2394a3cec84SArnaldo Carvalho de Melo dso__put(dso); 2404a3cec84SArnaldo Carvalho de Melo } 2414a3cec84SArnaldo Carvalho de Melo return dso; 2424a3cec84SArnaldo Carvalho de Melo } 2434a3cec84SArnaldo Carvalho de Melo 2440e3149f8SArnaldo Carvalho de Melo struct dso *__dsos__addnew(struct dsos *dsos, const char *name) 2454a3cec84SArnaldo Carvalho de Melo { 2460e3149f8SArnaldo Carvalho de Melo return __dsos__addnew_id(dsos, name, NULL); 2474a3cec84SArnaldo Carvalho de Melo } 2484a3cec84SArnaldo Carvalho de Melo 2490e3149f8SArnaldo Carvalho de Melo static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 2500e3149f8SArnaldo Carvalho de Melo { 2510e3149f8SArnaldo Carvalho de Melo struct dso *dso = __dsos__find_id(dsos, name, id, false); 2520e3149f8SArnaldo Carvalho de Melo return dso ? dso : __dsos__addnew_id(dsos, name, id); 2530e3149f8SArnaldo Carvalho de Melo } 2540e3149f8SArnaldo Carvalho de Melo 2550e3149f8SArnaldo Carvalho de Melo struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id) 2564a3cec84SArnaldo Carvalho de Melo { 2574a3cec84SArnaldo Carvalho de Melo struct dso *dso; 2584a3cec84SArnaldo Carvalho de Melo down_write(&dsos->lock); 2590e3149f8SArnaldo Carvalho de Melo dso = dso__get(__dsos__findnew_id(dsos, name, id)); 2604a3cec84SArnaldo Carvalho de Melo up_write(&dsos->lock); 2614a3cec84SArnaldo Carvalho de Melo return dso; 2624a3cec84SArnaldo Carvalho de Melo } 2634a3cec84SArnaldo Carvalho de Melo 2644a3cec84SArnaldo Carvalho de Melo size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, 2654a3cec84SArnaldo Carvalho de Melo bool (skip)(struct dso *dso, int parm), int parm) 2664a3cec84SArnaldo Carvalho de Melo { 2674a3cec84SArnaldo Carvalho de Melo struct dso *pos; 2684a3cec84SArnaldo Carvalho de Melo size_t ret = 0; 2694a3cec84SArnaldo Carvalho de Melo 2704a3cec84SArnaldo Carvalho de Melo list_for_each_entry(pos, head, node) { 2714a3cec84SArnaldo Carvalho de Melo if (skip && skip(pos, parm)) 2724a3cec84SArnaldo Carvalho de Melo continue; 2734a3cec84SArnaldo Carvalho de Melo ret += dso__fprintf_buildid(pos, fp); 2744a3cec84SArnaldo Carvalho de Melo ret += fprintf(fp, " %s\n", pos->long_name); 2754a3cec84SArnaldo Carvalho de Melo } 2764a3cec84SArnaldo Carvalho de Melo return ret; 2774a3cec84SArnaldo Carvalho de Melo } 2784a3cec84SArnaldo Carvalho de Melo 2794a3cec84SArnaldo Carvalho de Melo size_t __dsos__fprintf(struct list_head *head, FILE *fp) 2804a3cec84SArnaldo Carvalho de Melo { 2814a3cec84SArnaldo Carvalho de Melo struct dso *pos; 2824a3cec84SArnaldo Carvalho de Melo size_t ret = 0; 2834a3cec84SArnaldo Carvalho de Melo 2844a3cec84SArnaldo Carvalho de Melo list_for_each_entry(pos, head, node) { 2854a3cec84SArnaldo Carvalho de Melo ret += dso__fprintf(pos, fp); 2864a3cec84SArnaldo Carvalho de Melo } 2874a3cec84SArnaldo Carvalho de Melo 2884a3cec84SArnaldo Carvalho de Melo return ret; 2894a3cec84SArnaldo Carvalho de Melo } 290