xref: /openbmc/linux/tools/perf/util/map.c (revision 4b8cf846)
1 #include "symbol.h"
2 #include <limits.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include "map.h"
7 
8 const char *map_type__name[MAP__NR_TYPES] = {
9 	[MAP__FUNCTION] = "Functions",
10 	[MAP__VARIABLE] = "Variables",
11 };
12 
13 static inline int is_anon_memory(const char *filename)
14 {
15 	return strcmp(filename, "//anon") == 0;
16 }
17 
18 static int strcommon(const char *pathname, char *cwd, int cwdlen)
19 {
20 	int n = 0;
21 
22 	while (n < cwdlen && pathname[n] == cwd[n])
23 		++n;
24 
25 	return n;
26 }
27 
28 void map__init(struct map *self, enum map_type type,
29 	       u64 start, u64 end, u64 pgoff, struct dso *dso)
30 {
31 	self->type     = type;
32 	self->start    = start;
33 	self->end      = end;
34 	self->pgoff    = pgoff;
35 	self->dso      = dso;
36 	self->map_ip   = map__map_ip;
37 	self->unmap_ip = map__unmap_ip;
38 	RB_CLEAR_NODE(&self->rb_node);
39 }
40 
41 struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename,
42 		     enum map_type type, char *cwd, int cwdlen)
43 {
44 	struct map *self = malloc(sizeof(*self));
45 
46 	if (self != NULL) {
47 		char newfilename[PATH_MAX];
48 		struct dso *dso;
49 		int anon;
50 
51 		if (cwd) {
52 			int n = strcommon(filename, cwd, cwdlen);
53 
54 			if (n == cwdlen) {
55 				snprintf(newfilename, sizeof(newfilename),
56 					 ".%s", filename + n);
57 				filename = newfilename;
58 			}
59 		}
60 
61 		anon = is_anon_memory(filename);
62 
63 		if (anon) {
64 			snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
65 			filename = newfilename;
66 		}
67 
68 		dso = dsos__findnew(filename);
69 		if (dso == NULL)
70 			goto out_delete;
71 
72 		map__init(self, type, start, start + len, pgoff, dso);
73 
74 		if (anon) {
75 set_identity:
76 			self->map_ip = self->unmap_ip = identity__map_ip;
77 		} else if (strcmp(filename, "[vdso]") == 0) {
78 			dso__set_loaded(dso, self->type);
79 			goto set_identity;
80 		}
81 	}
82 	return self;
83 out_delete:
84 	free(self);
85 	return NULL;
86 }
87 
88 void map__delete(struct map *self)
89 {
90 	free(self);
91 }
92 
93 void map__fixup_start(struct map *self)
94 {
95 	struct rb_root *symbols = &self->dso->symbols[self->type];
96 	struct rb_node *nd = rb_first(symbols);
97 	if (nd != NULL) {
98 		struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
99 		self->start = sym->start;
100 	}
101 }
102 
103 void map__fixup_end(struct map *self)
104 {
105 	struct rb_root *symbols = &self->dso->symbols[self->type];
106 	struct rb_node *nd = rb_last(symbols);
107 	if (nd != NULL) {
108 		struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
109 		self->end = sym->end;
110 	}
111 }
112 
113 #define DSO__DELETED "(deleted)"
114 
115 int map__load(struct map *self, symbol_filter_t filter)
116 {
117 	const char *name = self->dso->long_name;
118 	int nr;
119 
120 	if (dso__loaded(self->dso, self->type))
121 		return 0;
122 
123 	nr = dso__load(self->dso, self, filter);
124 	if (nr < 0) {
125 		if (self->dso->has_build_id) {
126 			char sbuild_id[BUILD_ID_SIZE * 2 + 1];
127 
128 			build_id__sprintf(self->dso->build_id,
129 					  sizeof(self->dso->build_id),
130 					  sbuild_id);
131 			pr_warning("%s with build id %s not found",
132 				   name, sbuild_id);
133 		} else
134 			pr_warning("Failed to open %s", name);
135 
136 		pr_warning(", continuing without symbols\n");
137 		return -1;
138 	} else if (nr == 0) {
139 		const size_t len = strlen(name);
140 		const size_t real_len = len - sizeof(DSO__DELETED);
141 
142 		if (len > sizeof(DSO__DELETED) &&
143 		    strcmp(name + real_len + 1, DSO__DELETED) == 0) {
144 			pr_warning("%.*s was updated, restart the long "
145 				   "running apps that use it!\n",
146 				   (int)real_len, name);
147 		} else {
148 			pr_warning("no symbols found in %s, maybe install "
149 				   "a debug package?\n", name);
150 		}
151 
152 		return -1;
153 	}
154 	/*
155 	 * Only applies to the kernel, as its symtabs aren't relative like the
156 	 * module ones.
157 	 */
158 	if (self->dso->kernel)
159 		map__reloc_vmlinux(self);
160 
161 	return 0;
162 }
163 
164 struct symbol *map__find_symbol(struct map *self, u64 addr,
165 				symbol_filter_t filter)
166 {
167 	if (map__load(self, filter) < 0)
168 		return NULL;
169 
170 	return dso__find_symbol(self->dso, self->type, addr);
171 }
172 
173 struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
174 					symbol_filter_t filter)
175 {
176 	if (map__load(self, filter) < 0)
177 		return NULL;
178 
179 	if (!dso__sorted_by_name(self->dso, self->type))
180 		dso__sort_by_name(self->dso, self->type);
181 
182 	return dso__find_symbol_by_name(self->dso, self->type, name);
183 }
184 
185 struct map *map__clone(struct map *self)
186 {
187 	struct map *map = malloc(sizeof(*self));
188 
189 	if (!map)
190 		return NULL;
191 
192 	memcpy(map, self, sizeof(*self));
193 
194 	return map;
195 }
196 
197 int map__overlap(struct map *l, struct map *r)
198 {
199 	if (l->start > r->start) {
200 		struct map *t = l;
201 		l = r;
202 		r = t;
203 	}
204 
205 	if (l->end > r->start)
206 		return 1;
207 
208 	return 0;
209 }
210 
211 size_t map__fprintf(struct map *self, FILE *fp)
212 {
213 	return fprintf(fp, " %Lx-%Lx %Lx %s\n",
214 		       self->start, self->end, self->pgoff, self->dso->name);
215 }
216 
217 /*
218  * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.
219  * map->dso->adjust_symbols==1 for ET_EXEC-like cases.
220  */
221 u64 map__rip_2objdump(struct map *map, u64 rip)
222 {
223 	u64 addr = map->dso->adjust_symbols ?
224 			map->unmap_ip(map, rip) :	/* RIP -> IP */
225 			rip;
226 	return addr;
227 }
228 
229 u64 map__objdump_2ip(struct map *map, u64 addr)
230 {
231 	u64 ip = map->dso->adjust_symbols ?
232 			addr :
233 			map->unmap_ip(map, addr);	/* RIP -> IP */
234 	return ip;
235 }
236 
237 struct symbol *map_groups__find_symbol(struct map_groups *self,
238 				       enum map_type type, u64 addr,
239 				       symbol_filter_t filter)
240 {
241 	struct map *map = map_groups__find(self, type, addr);
242 
243 	if (map != NULL)
244 		return map__find_symbol(map, map->map_ip(map, addr), filter);
245 
246 	return NULL;
247 }
248 
249 static u64 map__reloc_map_ip(struct map *map, u64 ip)
250 {
251 	return ip + (s64)map->pgoff;
252 }
253 
254 static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
255 {
256 	return ip - (s64)map->pgoff;
257 }
258 
259 void map__reloc_vmlinux(struct map *self)
260 {
261 	struct kmap *kmap = map__kmap(self);
262 	s64 reloc;
263 
264 	if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr)
265 		return;
266 
267 	reloc = (kmap->ref_reloc_sym->unrelocated_addr -
268 		 kmap->ref_reloc_sym->addr);
269 
270 	if (!reloc)
271 		return;
272 
273 	self->map_ip   = map__reloc_map_ip;
274 	self->unmap_ip = map__reloc_unmap_ip;
275 	self->pgoff    = reloc;
276 }
277 
278 void maps__insert(struct rb_root *maps, struct map *map)
279 {
280 	struct rb_node **p = &maps->rb_node;
281 	struct rb_node *parent = NULL;
282 	const u64 ip = map->start;
283 	struct map *m;
284 
285 	while (*p != NULL) {
286 		parent = *p;
287 		m = rb_entry(parent, struct map, rb_node);
288 		if (ip < m->start)
289 			p = &(*p)->rb_left;
290 		else
291 			p = &(*p)->rb_right;
292 	}
293 
294 	rb_link_node(&map->rb_node, parent, p);
295 	rb_insert_color(&map->rb_node, maps);
296 }
297 
298 struct map *maps__find(struct rb_root *maps, u64 ip)
299 {
300 	struct rb_node **p = &maps->rb_node;
301 	struct rb_node *parent = NULL;
302 	struct map *m;
303 
304 	while (*p != NULL) {
305 		parent = *p;
306 		m = rb_entry(parent, struct map, rb_node);
307 		if (ip < m->start)
308 			p = &(*p)->rb_left;
309 		else if (ip > m->end)
310 			p = &(*p)->rb_right;
311 		else
312 			return m;
313 	}
314 
315 	return NULL;
316 }
317