xref: /linux/tools/perf/util/dsos.c (revision 7b59a82493b49b715224bfe3b35fae52e48e5fa1)
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"
5*7b59a824SArnaldo Carvalho de Melo #include "map.h"
64a3cec84SArnaldo Carvalho de Melo #include "vdso.h"
74a3cec84SArnaldo Carvalho de Melo #include "namespaces.h"
84a3cec84SArnaldo Carvalho de Melo #include <libgen.h>
94a3cec84SArnaldo Carvalho de Melo #include <stdlib.h>
104a3cec84SArnaldo Carvalho de Melo #include <string.h>
114a3cec84SArnaldo Carvalho de Melo #include <symbol.h> // filename__read_build_id
124a3cec84SArnaldo Carvalho de Melo 
13*7b59a824SArnaldo Carvalho de Melo int dso_id__cmp(struct dso_id *a, struct dso_id *b)
14*7b59a824SArnaldo Carvalho de Melo {
15*7b59a824SArnaldo Carvalho de Melo 	/*
16*7b59a824SArnaldo Carvalho de Melo 	 * The second is always dso->id, so zeroes if not set, assume passing
17*7b59a824SArnaldo Carvalho de Melo 	 * NULL for a means a zeroed id
18*7b59a824SArnaldo Carvalho de Melo 	 */
19*7b59a824SArnaldo Carvalho de Melo 	if (a == NULL)
20*7b59a824SArnaldo Carvalho de Melo 		return 0;
21*7b59a824SArnaldo Carvalho de Melo 
22*7b59a824SArnaldo Carvalho de Melo 	if (a->maj > b->maj) return -1;
23*7b59a824SArnaldo Carvalho de Melo 	if (a->maj < b->maj) return 1;
24*7b59a824SArnaldo Carvalho de Melo 
25*7b59a824SArnaldo Carvalho de Melo 	if (a->min > b->min) return -1;
26*7b59a824SArnaldo Carvalho de Melo 	if (a->min < b->min) return 1;
27*7b59a824SArnaldo Carvalho de Melo 
28*7b59a824SArnaldo Carvalho de Melo 	if (a->ino > b->ino) return -1;
29*7b59a824SArnaldo Carvalho de Melo 	if (a->ino < b->ino) return 1;
30*7b59a824SArnaldo Carvalho de Melo 
31*7b59a824SArnaldo Carvalho de Melo 	if (a->ino_generation > b->ino_generation) return -1;
32*7b59a824SArnaldo Carvalho de Melo 	if (a->ino_generation < b->ino_generation) return 1;
33*7b59a824SArnaldo Carvalho de Melo 
34*7b59a824SArnaldo Carvalho de Melo 	return 0;
35*7b59a824SArnaldo Carvalho de Melo }
36*7b59a824SArnaldo Carvalho de Melo 
374a3cec84SArnaldo Carvalho de Melo bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
384a3cec84SArnaldo Carvalho de Melo {
394a3cec84SArnaldo Carvalho de Melo 	bool have_build_id = false;
404a3cec84SArnaldo Carvalho de Melo 	struct dso *pos;
414a3cec84SArnaldo Carvalho de Melo 	struct nscookie nsc;
424a3cec84SArnaldo Carvalho de Melo 
434a3cec84SArnaldo Carvalho de Melo 	list_for_each_entry(pos, head, node) {
444a3cec84SArnaldo Carvalho de Melo 		if (with_hits && !pos->hit && !dso__is_vdso(pos))
454a3cec84SArnaldo Carvalho de Melo 			continue;
464a3cec84SArnaldo Carvalho de Melo 		if (pos->has_build_id) {
474a3cec84SArnaldo Carvalho de Melo 			have_build_id = true;
484a3cec84SArnaldo Carvalho de Melo 			continue;
494a3cec84SArnaldo Carvalho de Melo 		}
504a3cec84SArnaldo Carvalho de Melo 		nsinfo__mountns_enter(pos->nsinfo, &nsc);
514a3cec84SArnaldo Carvalho de Melo 		if (filename__read_build_id(pos->long_name, pos->build_id,
524a3cec84SArnaldo Carvalho de Melo 					    sizeof(pos->build_id)) > 0) {
534a3cec84SArnaldo Carvalho de Melo 			have_build_id	  = true;
544a3cec84SArnaldo Carvalho de Melo 			pos->has_build_id = true;
554a3cec84SArnaldo Carvalho de Melo 		}
564a3cec84SArnaldo Carvalho de Melo 		nsinfo__mountns_exit(&nsc);
574a3cec84SArnaldo Carvalho de Melo 	}
584a3cec84SArnaldo Carvalho de Melo 
594a3cec84SArnaldo Carvalho de Melo 	return have_build_id;
604a3cec84SArnaldo Carvalho de Melo }
614a3cec84SArnaldo Carvalho de Melo 
624a3cec84SArnaldo Carvalho de Melo /*
634a3cec84SArnaldo Carvalho de Melo  * Find a matching entry and/or link current entry to RB tree.
644a3cec84SArnaldo Carvalho de Melo  * Either one of the dso or name parameter must be non-NULL or the
654a3cec84SArnaldo Carvalho de Melo  * function will not work.
664a3cec84SArnaldo Carvalho de Melo  */
674a3cec84SArnaldo Carvalho de Melo struct dso *__dsos__findnew_link_by_longname(struct rb_root *root, struct dso *dso, const char *name)
684a3cec84SArnaldo Carvalho de Melo {
694a3cec84SArnaldo Carvalho de Melo 	struct rb_node **p = &root->rb_node;
704a3cec84SArnaldo Carvalho de Melo 	struct rb_node  *parent = NULL;
714a3cec84SArnaldo Carvalho de Melo 
724a3cec84SArnaldo Carvalho de Melo 	if (!name)
734a3cec84SArnaldo Carvalho de Melo 		name = dso->long_name;
744a3cec84SArnaldo Carvalho de Melo 	/*
754a3cec84SArnaldo Carvalho de Melo 	 * Find node with the matching name
764a3cec84SArnaldo Carvalho de Melo 	 */
774a3cec84SArnaldo Carvalho de Melo 	while (*p) {
784a3cec84SArnaldo Carvalho de Melo 		struct dso *this = rb_entry(*p, struct dso, rb_node);
794a3cec84SArnaldo Carvalho de Melo 		int rc = strcmp(name, this->long_name);
804a3cec84SArnaldo Carvalho de Melo 
814a3cec84SArnaldo Carvalho de Melo 		parent = *p;
824a3cec84SArnaldo Carvalho de Melo 		if (rc == 0) {
834a3cec84SArnaldo Carvalho de Melo 			/*
844a3cec84SArnaldo Carvalho de Melo 			 * In case the new DSO is a duplicate of an existing
854a3cec84SArnaldo Carvalho de Melo 			 * one, print a one-time warning & put the new entry
864a3cec84SArnaldo Carvalho de Melo 			 * at the end of the list of duplicates.
874a3cec84SArnaldo Carvalho de Melo 			 */
884a3cec84SArnaldo Carvalho de Melo 			if (!dso || (dso == this))
894a3cec84SArnaldo Carvalho de Melo 				return this;	/* Find matching dso */
904a3cec84SArnaldo Carvalho de Melo 			/*
914a3cec84SArnaldo Carvalho de Melo 			 * The core kernel DSOs may have duplicated long name.
924a3cec84SArnaldo Carvalho de Melo 			 * In this case, the short name should be different.
934a3cec84SArnaldo Carvalho de Melo 			 * Comparing the short names to differentiate the DSOs.
944a3cec84SArnaldo Carvalho de Melo 			 */
954a3cec84SArnaldo Carvalho de Melo 			rc = strcmp(dso->short_name, this->short_name);
964a3cec84SArnaldo Carvalho de Melo 			if (rc == 0) {
974a3cec84SArnaldo Carvalho de Melo 				pr_err("Duplicated dso name: %s\n", name);
984a3cec84SArnaldo Carvalho de Melo 				return NULL;
994a3cec84SArnaldo Carvalho de Melo 			}
1004a3cec84SArnaldo Carvalho de Melo 		}
1014a3cec84SArnaldo Carvalho de Melo 		if (rc < 0)
1024a3cec84SArnaldo Carvalho de Melo 			p = &parent->rb_left;
1034a3cec84SArnaldo Carvalho de Melo 		else
1044a3cec84SArnaldo Carvalho de Melo 			p = &parent->rb_right;
1054a3cec84SArnaldo Carvalho de Melo 	}
1064a3cec84SArnaldo Carvalho de Melo 	if (dso) {
1074a3cec84SArnaldo Carvalho de Melo 		/* Add new node and rebalance tree */
1084a3cec84SArnaldo Carvalho de Melo 		rb_link_node(&dso->rb_node, parent, p);
1094a3cec84SArnaldo Carvalho de Melo 		rb_insert_color(&dso->rb_node, root);
1104a3cec84SArnaldo Carvalho de Melo 		dso->root = root;
1114a3cec84SArnaldo Carvalho de Melo 	}
1124a3cec84SArnaldo Carvalho de Melo 	return NULL;
1134a3cec84SArnaldo Carvalho de Melo }
1144a3cec84SArnaldo Carvalho de Melo 
1154a3cec84SArnaldo Carvalho de Melo void __dsos__add(struct dsos *dsos, struct dso *dso)
1164a3cec84SArnaldo Carvalho de Melo {
1174a3cec84SArnaldo Carvalho de Melo 	list_add_tail(&dso->node, &dsos->head);
1184a3cec84SArnaldo Carvalho de Melo 	__dsos__findnew_link_by_longname(&dsos->root, dso, NULL);
1194a3cec84SArnaldo Carvalho de Melo 	/*
1204a3cec84SArnaldo Carvalho de Melo 	 * It is now in the linked list, grab a reference, then garbage collect
1214a3cec84SArnaldo Carvalho de Melo 	 * this when needing memory, by looking at LRU dso instances in the
1224a3cec84SArnaldo Carvalho de Melo 	 * list with atomic_read(&dso->refcnt) == 1, i.e. no references
1234a3cec84SArnaldo Carvalho de Melo 	 * anywhere besides the one for the list, do, under a lock for the
1244a3cec84SArnaldo Carvalho de Melo 	 * list: remove it from the list, then a dso__put(), that probably will
1254a3cec84SArnaldo Carvalho de Melo 	 * be the last and will then call dso__delete(), end of life.
1264a3cec84SArnaldo Carvalho de Melo 	 *
1274a3cec84SArnaldo Carvalho de Melo 	 * That, or at the end of the 'struct machine' lifetime, when all
1284a3cec84SArnaldo Carvalho de Melo 	 * 'struct dso' instances will be removed from the list, in
1294a3cec84SArnaldo Carvalho de Melo 	 * dsos__exit(), if they have no other reference from some other data
1304a3cec84SArnaldo Carvalho de Melo 	 * structure.
1314a3cec84SArnaldo Carvalho de Melo 	 *
1324a3cec84SArnaldo Carvalho de Melo 	 * E.g.: after processing a 'perf.data' file and storing references
1334a3cec84SArnaldo Carvalho de Melo 	 * to objects instantiated while processing events, we will have
1344a3cec84SArnaldo Carvalho de Melo 	 * references to the 'thread', 'map', 'dso' structs all from 'struct
1354a3cec84SArnaldo Carvalho de Melo 	 * hist_entry' instances, but we may not need anything not referenced,
1364a3cec84SArnaldo Carvalho de Melo 	 * so we might as well call machines__exit()/machines__delete() and
1374a3cec84SArnaldo Carvalho de Melo 	 * garbage collect it.
1384a3cec84SArnaldo Carvalho de Melo 	 */
1394a3cec84SArnaldo Carvalho de Melo 	dso__get(dso);
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 	down_write(&dsos->lock);
1454a3cec84SArnaldo Carvalho de Melo 	__dsos__add(dsos, dso);
1464a3cec84SArnaldo Carvalho de Melo 	up_write(&dsos->lock);
1474a3cec84SArnaldo Carvalho de Melo }
1484a3cec84SArnaldo Carvalho de Melo 
1494a3cec84SArnaldo Carvalho de Melo struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
1504a3cec84SArnaldo Carvalho de Melo {
1514a3cec84SArnaldo Carvalho de Melo 	struct dso *pos;
1524a3cec84SArnaldo Carvalho de Melo 
1534a3cec84SArnaldo Carvalho de Melo 	if (cmp_short) {
1544a3cec84SArnaldo Carvalho de Melo 		list_for_each_entry(pos, &dsos->head, node)
1554a3cec84SArnaldo Carvalho de Melo 			if (strcmp(pos->short_name, name) == 0)
1564a3cec84SArnaldo Carvalho de Melo 				return pos;
1574a3cec84SArnaldo Carvalho de Melo 		return NULL;
1584a3cec84SArnaldo Carvalho de Melo 	}
1594a3cec84SArnaldo Carvalho de Melo 	return __dsos__findnew_by_longname(&dsos->root, name);
1604a3cec84SArnaldo Carvalho de Melo }
1614a3cec84SArnaldo Carvalho de Melo 
1624a3cec84SArnaldo Carvalho de Melo struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
1634a3cec84SArnaldo Carvalho de Melo {
1644a3cec84SArnaldo Carvalho de Melo 	struct dso *dso;
1654a3cec84SArnaldo Carvalho de Melo 	down_read(&dsos->lock);
1664a3cec84SArnaldo Carvalho de Melo 	dso = __dsos__find(dsos, name, cmp_short);
1674a3cec84SArnaldo Carvalho de Melo 	up_read(&dsos->lock);
1684a3cec84SArnaldo Carvalho de Melo 	return dso;
1694a3cec84SArnaldo Carvalho de Melo }
1704a3cec84SArnaldo Carvalho de Melo 
1714a3cec84SArnaldo Carvalho de Melo static void dso__set_basename(struct dso *dso)
1724a3cec84SArnaldo Carvalho de Melo {
1734a3cec84SArnaldo Carvalho de Melo 	char *base, *lname;
1744a3cec84SArnaldo Carvalho de Melo 	int tid;
1754a3cec84SArnaldo Carvalho de Melo 
1764a3cec84SArnaldo Carvalho de Melo 	if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) {
1774a3cec84SArnaldo Carvalho de Melo 		if (asprintf(&base, "[JIT] tid %d", tid) < 0)
1784a3cec84SArnaldo Carvalho de Melo 			return;
1794a3cec84SArnaldo Carvalho de Melo 	} else {
1804a3cec84SArnaldo Carvalho de Melo 	      /*
1814a3cec84SArnaldo Carvalho de Melo 	       * basename() may modify path buffer, so we must pass
1824a3cec84SArnaldo Carvalho de Melo                * a copy.
1834a3cec84SArnaldo Carvalho de Melo                */
1844a3cec84SArnaldo Carvalho de Melo 		lname = strdup(dso->long_name);
1854a3cec84SArnaldo Carvalho de Melo 		if (!lname)
1864a3cec84SArnaldo Carvalho de Melo 			return;
1874a3cec84SArnaldo Carvalho de Melo 
1884a3cec84SArnaldo Carvalho de Melo 		/*
1894a3cec84SArnaldo Carvalho de Melo 		 * basename() may return a pointer to internal
1904a3cec84SArnaldo Carvalho de Melo 		 * storage which is reused in subsequent calls
1914a3cec84SArnaldo Carvalho de Melo 		 * so copy the result.
1924a3cec84SArnaldo Carvalho de Melo 		 */
1934a3cec84SArnaldo Carvalho de Melo 		base = strdup(basename(lname));
1944a3cec84SArnaldo Carvalho de Melo 
1954a3cec84SArnaldo Carvalho de Melo 		free(lname);
1964a3cec84SArnaldo Carvalho de Melo 
1974a3cec84SArnaldo Carvalho de Melo 		if (!base)
1984a3cec84SArnaldo Carvalho de Melo 			return;
1994a3cec84SArnaldo Carvalho de Melo 	}
2004a3cec84SArnaldo Carvalho de Melo 	dso__set_short_name(dso, base, true);
2014a3cec84SArnaldo Carvalho de Melo }
2024a3cec84SArnaldo Carvalho de Melo 
2034a3cec84SArnaldo Carvalho de Melo struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
2044a3cec84SArnaldo Carvalho de Melo {
2054a3cec84SArnaldo Carvalho de Melo 	struct dso *dso = dso__new(name);
2064a3cec84SArnaldo Carvalho de Melo 
2074a3cec84SArnaldo Carvalho de Melo 	if (dso != NULL) {
2084a3cec84SArnaldo Carvalho de Melo 		__dsos__add(dsos, dso);
2094a3cec84SArnaldo Carvalho de Melo 		dso__set_basename(dso);
2104a3cec84SArnaldo Carvalho de Melo 		/* Put dso here because __dsos_add already got it */
2114a3cec84SArnaldo Carvalho de Melo 		dso__put(dso);
2124a3cec84SArnaldo Carvalho de Melo 	}
2134a3cec84SArnaldo Carvalho de Melo 	return dso;
2144a3cec84SArnaldo Carvalho de Melo }
2154a3cec84SArnaldo Carvalho de Melo 
2164a3cec84SArnaldo Carvalho de Melo struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
2174a3cec84SArnaldo Carvalho de Melo {
2184a3cec84SArnaldo Carvalho de Melo 	struct dso *dso = __dsos__find(dsos, name, false);
2194a3cec84SArnaldo Carvalho de Melo 
2204a3cec84SArnaldo Carvalho de Melo 	return dso ? dso : __dsos__addnew(dsos, name);
2214a3cec84SArnaldo Carvalho de Melo }
2224a3cec84SArnaldo Carvalho de Melo 
2234a3cec84SArnaldo Carvalho de Melo struct dso *dsos__findnew(struct dsos *dsos, const char *name)
2244a3cec84SArnaldo Carvalho de Melo {
2254a3cec84SArnaldo Carvalho de Melo 	struct dso *dso;
2264a3cec84SArnaldo Carvalho de Melo 	down_write(&dsos->lock);
2274a3cec84SArnaldo Carvalho de Melo 	dso = dso__get(__dsos__findnew(dsos, name));
2284a3cec84SArnaldo Carvalho de Melo 	up_write(&dsos->lock);
2294a3cec84SArnaldo Carvalho de Melo 	return dso;
2304a3cec84SArnaldo Carvalho de Melo }
2314a3cec84SArnaldo Carvalho de Melo 
2324a3cec84SArnaldo Carvalho de Melo size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
2334a3cec84SArnaldo Carvalho de Melo 			       bool (skip)(struct dso *dso, int parm), int parm)
2344a3cec84SArnaldo Carvalho de Melo {
2354a3cec84SArnaldo Carvalho de Melo 	struct dso *pos;
2364a3cec84SArnaldo Carvalho de Melo 	size_t ret = 0;
2374a3cec84SArnaldo Carvalho de Melo 
2384a3cec84SArnaldo Carvalho de Melo 	list_for_each_entry(pos, head, node) {
2394a3cec84SArnaldo Carvalho de Melo 		if (skip && skip(pos, parm))
2404a3cec84SArnaldo Carvalho de Melo 			continue;
2414a3cec84SArnaldo Carvalho de Melo 		ret += dso__fprintf_buildid(pos, fp);
2424a3cec84SArnaldo Carvalho de Melo 		ret += fprintf(fp, " %s\n", pos->long_name);
2434a3cec84SArnaldo Carvalho de Melo 	}
2444a3cec84SArnaldo Carvalho de Melo 	return ret;
2454a3cec84SArnaldo Carvalho de Melo }
2464a3cec84SArnaldo Carvalho de Melo 
2474a3cec84SArnaldo Carvalho de Melo size_t __dsos__fprintf(struct list_head *head, FILE *fp)
2484a3cec84SArnaldo Carvalho de Melo {
2494a3cec84SArnaldo Carvalho de Melo 	struct dso *pos;
2504a3cec84SArnaldo Carvalho de Melo 	size_t ret = 0;
2514a3cec84SArnaldo Carvalho de Melo 
2524a3cec84SArnaldo Carvalho de Melo 	list_for_each_entry(pos, head, node) {
2534a3cec84SArnaldo Carvalho de Melo 		ret += dso__fprintf(pos, fp);
2544a3cec84SArnaldo Carvalho de Melo 	}
2554a3cec84SArnaldo Carvalho de Melo 
2564a3cec84SArnaldo Carvalho de Melo 	return ret;
2574a3cec84SArnaldo Carvalho de Melo }
258