xref: /openbmc/linux/tools/perf/util/dsos.c (revision 0e3149f8)
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