cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dsos.c (8250B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include "debug.h"
      3#include "dsos.h"
      4#include "dso.h"
      5#include "util.h"
      6#include "vdso.h"
      7#include "namespaces.h"
      8#include <errno.h>
      9#include <libgen.h>
     10#include <stdlib.h>
     11#include <string.h>
     12#include <symbol.h> // filename__read_build_id
     13#include <unistd.h>
     14
     15static int __dso_id__cmp(struct dso_id *a, struct dso_id *b)
     16{
     17	if (a->maj > b->maj) return -1;
     18	if (a->maj < b->maj) return 1;
     19
     20	if (a->min > b->min) return -1;
     21	if (a->min < b->min) return 1;
     22
     23	if (a->ino > b->ino) return -1;
     24	if (a->ino < b->ino) return 1;
     25
     26	if (a->ino_generation > b->ino_generation) return -1;
     27	if (a->ino_generation < b->ino_generation) return 1;
     28
     29	return 0;
     30}
     31
     32static bool dso_id__empty(struct dso_id *id)
     33{
     34	if (!id)
     35		return true;
     36
     37	return !id->maj && !id->min && !id->ino && !id->ino_generation;
     38}
     39
     40static void dso__inject_id(struct dso *dso, struct dso_id *id)
     41{
     42	dso->id.maj = id->maj;
     43	dso->id.min = id->min;
     44	dso->id.ino = id->ino;
     45	dso->id.ino_generation = id->ino_generation;
     46}
     47
     48static int dso_id__cmp(struct dso_id *a, struct dso_id *b)
     49{
     50	/*
     51	 * The second is always dso->id, so zeroes if not set, assume passing
     52	 * NULL for a means a zeroed id
     53	 */
     54	if (dso_id__empty(a) || dso_id__empty(b))
     55		return 0;
     56
     57	return __dso_id__cmp(a, b);
     58}
     59
     60int dso__cmp_id(struct dso *a, struct dso *b)
     61{
     62	return __dso_id__cmp(&a->id, &b->id);
     63}
     64
     65bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
     66{
     67	bool have_build_id = false;
     68	struct dso *pos;
     69	struct nscookie nsc;
     70
     71	list_for_each_entry(pos, head, node) {
     72		if (with_hits && !pos->hit && !dso__is_vdso(pos))
     73			continue;
     74		if (pos->has_build_id) {
     75			have_build_id = true;
     76			continue;
     77		}
     78		nsinfo__mountns_enter(pos->nsinfo, &nsc);
     79		if (filename__read_build_id(pos->long_name, &pos->bid) > 0) {
     80			have_build_id	  = true;
     81			pos->has_build_id = true;
     82		} else if (errno == ENOENT && pos->nsinfo) {
     83			char *new_name = filename_with_chroot(pos->nsinfo->pid,
     84							      pos->long_name);
     85
     86			if (new_name && filename__read_build_id(new_name,
     87								&pos->bid) > 0) {
     88				have_build_id = true;
     89				pos->has_build_id = true;
     90			}
     91			free(new_name);
     92		}
     93		nsinfo__mountns_exit(&nsc);
     94	}
     95
     96	return have_build_id;
     97}
     98
     99static int __dso__cmp_long_name(const char *long_name, struct dso_id *id, struct dso *b)
    100{
    101	int rc = strcmp(long_name, b->long_name);
    102	return rc ?: dso_id__cmp(id, &b->id);
    103}
    104
    105static int __dso__cmp_short_name(const char *short_name, struct dso_id *id, struct dso *b)
    106{
    107	int rc = strcmp(short_name, b->short_name);
    108	return rc ?: dso_id__cmp(id, &b->id);
    109}
    110
    111static int dso__cmp_short_name(struct dso *a, struct dso *b)
    112{
    113	return __dso__cmp_short_name(a->short_name, &a->id, b);
    114}
    115
    116/*
    117 * Find a matching entry and/or link current entry to RB tree.
    118 * Either one of the dso or name parameter must be non-NULL or the
    119 * function will not work.
    120 */
    121struct dso *__dsos__findnew_link_by_longname_id(struct rb_root *root, struct dso *dso,
    122						const char *name, struct dso_id *id)
    123{
    124	struct rb_node **p = &root->rb_node;
    125	struct rb_node  *parent = NULL;
    126
    127	if (!name)
    128		name = dso->long_name;
    129	/*
    130	 * Find node with the matching name
    131	 */
    132	while (*p) {
    133		struct dso *this = rb_entry(*p, struct dso, rb_node);
    134		int rc = __dso__cmp_long_name(name, id, this);
    135
    136		parent = *p;
    137		if (rc == 0) {
    138			/*
    139			 * In case the new DSO is a duplicate of an existing
    140			 * one, print a one-time warning & put the new entry
    141			 * at the end of the list of duplicates.
    142			 */
    143			if (!dso || (dso == this))
    144				return this;	/* Find matching dso */
    145			/*
    146			 * The core kernel DSOs may have duplicated long name.
    147			 * In this case, the short name should be different.
    148			 * Comparing the short names to differentiate the DSOs.
    149			 */
    150			rc = dso__cmp_short_name(dso, this);
    151			if (rc == 0) {
    152				pr_err("Duplicated dso name: %s\n", name);
    153				return NULL;
    154			}
    155		}
    156		if (rc < 0)
    157			p = &parent->rb_left;
    158		else
    159			p = &parent->rb_right;
    160	}
    161	if (dso) {
    162		/* Add new node and rebalance tree */
    163		rb_link_node(&dso->rb_node, parent, p);
    164		rb_insert_color(&dso->rb_node, root);
    165		dso->root = root;
    166	}
    167	return NULL;
    168}
    169
    170void __dsos__add(struct dsos *dsos, struct dso *dso)
    171{
    172	list_add_tail(&dso->node, &dsos->head);
    173	__dsos__findnew_link_by_longname_id(&dsos->root, dso, NULL, &dso->id);
    174	/*
    175	 * It is now in the linked list, grab a reference, then garbage collect
    176	 * this when needing memory, by looking at LRU dso instances in the
    177	 * list with atomic_read(&dso->refcnt) == 1, i.e. no references
    178	 * anywhere besides the one for the list, do, under a lock for the
    179	 * list: remove it from the list, then a dso__put(), that probably will
    180	 * be the last and will then call dso__delete(), end of life.
    181	 *
    182	 * That, or at the end of the 'struct machine' lifetime, when all
    183	 * 'struct dso' instances will be removed from the list, in
    184	 * dsos__exit(), if they have no other reference from some other data
    185	 * structure.
    186	 *
    187	 * E.g.: after processing a 'perf.data' file and storing references
    188	 * to objects instantiated while processing events, we will have
    189	 * references to the 'thread', 'map', 'dso' structs all from 'struct
    190	 * hist_entry' instances, but we may not need anything not referenced,
    191	 * so we might as well call machines__exit()/machines__delete() and
    192	 * garbage collect it.
    193	 */
    194	dso__get(dso);
    195}
    196
    197void dsos__add(struct dsos *dsos, struct dso *dso)
    198{
    199	down_write(&dsos->lock);
    200	__dsos__add(dsos, dso);
    201	up_write(&dsos->lock);
    202}
    203
    204static struct dso *__dsos__findnew_by_longname_id(struct rb_root *root, const char *name, struct dso_id *id)
    205{
    206	return __dsos__findnew_link_by_longname_id(root, NULL, name, id);
    207}
    208
    209static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, struct dso_id *id, bool cmp_short)
    210{
    211	struct dso *pos;
    212
    213	if (cmp_short) {
    214		list_for_each_entry(pos, &dsos->head, node)
    215			if (__dso__cmp_short_name(name, id, pos) == 0)
    216				return pos;
    217		return NULL;
    218	}
    219	return __dsos__findnew_by_longname_id(&dsos->root, name, id);
    220}
    221
    222struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
    223{
    224	return __dsos__find_id(dsos, name, NULL, cmp_short);
    225}
    226
    227static void dso__set_basename(struct dso *dso)
    228{
    229	char *base, *lname;
    230	int tid;
    231
    232	if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) {
    233		if (asprintf(&base, "[JIT] tid %d", tid) < 0)
    234			return;
    235	} else {
    236	      /*
    237	       * basename() may modify path buffer, so we must pass
    238               * a copy.
    239               */
    240		lname = strdup(dso->long_name);
    241		if (!lname)
    242			return;
    243
    244		/*
    245		 * basename() may return a pointer to internal
    246		 * storage which is reused in subsequent calls
    247		 * so copy the result.
    248		 */
    249		base = strdup(basename(lname));
    250
    251		free(lname);
    252
    253		if (!base)
    254			return;
    255	}
    256	dso__set_short_name(dso, base, true);
    257}
    258
    259static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
    260{
    261	struct dso *dso = dso__new_id(name, id);
    262
    263	if (dso != NULL) {
    264		__dsos__add(dsos, dso);
    265		dso__set_basename(dso);
    266		/* Put dso here because __dsos_add already got it */
    267		dso__put(dso);
    268	}
    269	return dso;
    270}
    271
    272struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
    273{
    274	return __dsos__addnew_id(dsos, name, NULL);
    275}
    276
    277static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
    278{
    279	struct dso *dso = __dsos__find_id(dsos, name, id, false);
    280
    281	if (dso && dso_id__empty(&dso->id) && !dso_id__empty(id))
    282		dso__inject_id(dso, id);
    283
    284	return dso ? dso : __dsos__addnew_id(dsos, name, id);
    285}
    286
    287struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
    288{
    289	struct dso *dso;
    290	down_write(&dsos->lock);
    291	dso = dso__get(__dsos__findnew_id(dsos, name, id));
    292	up_write(&dsos->lock);
    293	return dso;
    294}
    295
    296size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
    297			       bool (skip)(struct dso *dso, int parm), int parm)
    298{
    299	struct dso *pos;
    300	size_t ret = 0;
    301
    302	list_for_each_entry(pos, head, node) {
    303		char sbuild_id[SBUILD_ID_SIZE];
    304
    305		if (skip && skip(pos, parm))
    306			continue;
    307		build_id__sprintf(&pos->bid, sbuild_id);
    308		ret += fprintf(fp, "%-40s %s\n", sbuild_id, pos->long_name);
    309	}
    310	return ret;
    311}
    312
    313size_t __dsos__fprintf(struct list_head *head, FILE *fp)
    314{
    315	struct dso *pos;
    316	size_t ret = 0;
    317
    318	list_for_each_entry(pos, head, node) {
    319		ret += dso__fprintf(pos, fp);
    320	}
    321
    322	return ret;
    323}