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