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