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