xref: /openbmc/linux/tools/perf/util/session.c (revision 6122e4e4)
1 #define _LARGEFILE64_SOURCE
2 #define _FILE_OFFSET_BITS 64
3 
4 #include <linux/kernel.h>
5 
6 #include <byteswap.h>
7 #include <unistd.h>
8 #include <sys/types.h>
9 
10 #include "session.h"
11 #include "sort.h"
12 #include "util.h"
13 
14 static int perf_session__open(struct perf_session *self, bool force)
15 {
16 	struct stat input_stat;
17 
18 	self->fd = open(self->filename, O_RDONLY|O_LARGEFILE);
19 	if (self->fd < 0) {
20 		pr_err("failed to open file: %s", self->filename);
21 		if (!strcmp(self->filename, "perf.data"))
22 			pr_err("  (try 'perf record' first)");
23 		pr_err("\n");
24 		return -errno;
25 	}
26 
27 	if (fstat(self->fd, &input_stat) < 0)
28 		goto out_close;
29 
30 	if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
31 		pr_err("file %s not owned by current user or root\n",
32 		       self->filename);
33 		goto out_close;
34 	}
35 
36 	if (!input_stat.st_size) {
37 		pr_info("zero-sized file (%s), nothing to do!\n",
38 			self->filename);
39 		goto out_close;
40 	}
41 
42 	if (perf_header__read(&self->header, self->fd) < 0) {
43 		pr_err("incompatible file format");
44 		goto out_close;
45 	}
46 
47 	self->size = input_stat.st_size;
48 	return 0;
49 
50 out_close:
51 	close(self->fd);
52 	self->fd = -1;
53 	return -1;
54 }
55 
56 static inline int perf_session__create_kernel_maps(struct perf_session *self)
57 {
58 	return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps);
59 }
60 
61 struct perf_session *perf_session__new(const char *filename, int mode, bool force)
62 {
63 	size_t len = filename ? strlen(filename) + 1 : 0;
64 	struct perf_session *self = zalloc(sizeof(*self) + len);
65 
66 	if (self == NULL)
67 		goto out;
68 
69 	if (perf_header__init(&self->header) < 0)
70 		goto out_free;
71 
72 	memcpy(self->filename, filename, len);
73 	self->threads = RB_ROOT;
74 	self->last_match = NULL;
75 	self->mmap_window = 32;
76 	self->cwd = NULL;
77 	self->cwdlen = 0;
78 	self->unknown_events = 0;
79 	map_groups__init(&self->kmaps);
80 
81 	if (mode == O_RDONLY) {
82 		if (perf_session__open(self, force) < 0)
83 			goto out_delete;
84 	} else if (mode == O_WRONLY) {
85 		/*
86 		 * In O_RDONLY mode this will be performed when reading the
87 		 * kernel MMAP event, in event__process_mmap().
88 		 */
89 		if (perf_session__create_kernel_maps(self) < 0)
90 			goto out_delete;
91 	}
92 
93 	self->sample_type = perf_header__sample_type(&self->header);
94 out:
95 	return self;
96 out_free:
97 	free(self);
98 	return NULL;
99 out_delete:
100 	perf_session__delete(self);
101 	return NULL;
102 }
103 
104 void perf_session__delete(struct perf_session *self)
105 {
106 	perf_header__exit(&self->header);
107 	close(self->fd);
108 	free(self->cwd);
109 	free(self);
110 }
111 
112 static bool symbol__match_parent_regex(struct symbol *sym)
113 {
114 	if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
115 		return 1;
116 
117 	return 0;
118 }
119 
120 struct symbol **perf_session__resolve_callchain(struct perf_session *self,
121 						struct thread *thread,
122 						struct ip_callchain *chain,
123 						struct symbol **parent)
124 {
125 	u8 cpumode = PERF_RECORD_MISC_USER;
126 	struct symbol **syms = NULL;
127 	unsigned int i;
128 
129 	if (symbol_conf.use_callchain) {
130 		syms = calloc(chain->nr, sizeof(*syms));
131 		if (!syms) {
132 			fprintf(stderr, "Can't allocate memory for symbols\n");
133 			exit(-1);
134 		}
135 	}
136 
137 	for (i = 0; i < chain->nr; i++) {
138 		u64 ip = chain->ips[i];
139 		struct addr_location al;
140 
141 		if (ip >= PERF_CONTEXT_MAX) {
142 			switch (ip) {
143 			case PERF_CONTEXT_HV:
144 				cpumode = PERF_RECORD_MISC_HYPERVISOR;	break;
145 			case PERF_CONTEXT_KERNEL:
146 				cpumode = PERF_RECORD_MISC_KERNEL;	break;
147 			case PERF_CONTEXT_USER:
148 				cpumode = PERF_RECORD_MISC_USER;	break;
149 			default:
150 				break;
151 			}
152 			continue;
153 		}
154 
155 		thread__find_addr_location(thread, self, cpumode,
156 					   MAP__FUNCTION, ip, &al, NULL);
157 		if (al.sym != NULL) {
158 			if (sort__has_parent && !*parent &&
159 			    symbol__match_parent_regex(al.sym))
160 				*parent = al.sym;
161 			if (!symbol_conf.use_callchain)
162 				break;
163 			syms[i] = al.sym;
164 		}
165 	}
166 
167 	return syms;
168 }
169 
170 static int process_event_stub(event_t *event __used,
171 			      struct perf_session *session __used)
172 {
173 	dump_printf(": unhandled!\n");
174 	return 0;
175 }
176 
177 static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
178 {
179 	if (handler->sample == NULL)
180 		handler->sample = process_event_stub;
181 	if (handler->mmap == NULL)
182 		handler->mmap = process_event_stub;
183 	if (handler->comm == NULL)
184 		handler->comm = process_event_stub;
185 	if (handler->fork == NULL)
186 		handler->fork = process_event_stub;
187 	if (handler->exit == NULL)
188 		handler->exit = process_event_stub;
189 	if (handler->lost == NULL)
190 		handler->lost = process_event_stub;
191 	if (handler->read == NULL)
192 		handler->read = process_event_stub;
193 	if (handler->throttle == NULL)
194 		handler->throttle = process_event_stub;
195 	if (handler->unthrottle == NULL)
196 		handler->unthrottle = process_event_stub;
197 }
198 
199 static const char *event__name[] = {
200 	[0]			 = "TOTAL",
201 	[PERF_RECORD_MMAP]	 = "MMAP",
202 	[PERF_RECORD_LOST]	 = "LOST",
203 	[PERF_RECORD_COMM]	 = "COMM",
204 	[PERF_RECORD_EXIT]	 = "EXIT",
205 	[PERF_RECORD_THROTTLE]	 = "THROTTLE",
206 	[PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
207 	[PERF_RECORD_FORK]	 = "FORK",
208 	[PERF_RECORD_READ]	 = "READ",
209 	[PERF_RECORD_SAMPLE]	 = "SAMPLE",
210 };
211 
212 unsigned long event__total[PERF_RECORD_MAX];
213 
214 void event__print_totals(void)
215 {
216 	int i;
217 	for (i = 0; i < PERF_RECORD_MAX; ++i)
218 		pr_info("%10s events: %10ld\n",
219 			event__name[i], event__total[i]);
220 }
221 
222 void mem_bswap_64(void *src, int byte_size)
223 {
224 	u64 *m = src;
225 
226 	while (byte_size > 0) {
227 		*m = bswap_64(*m);
228 		byte_size -= sizeof(u64);
229 		++m;
230 	}
231 }
232 
233 static void event__all64_swap(event_t *self)
234 {
235 	struct perf_event_header *hdr = &self->header;
236 	mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr));
237 }
238 
239 static void event__comm_swap(event_t *self)
240 {
241 	self->comm.pid = bswap_32(self->comm.pid);
242 	self->comm.tid = bswap_32(self->comm.tid);
243 }
244 
245 static void event__mmap_swap(event_t *self)
246 {
247 	self->mmap.pid	 = bswap_32(self->mmap.pid);
248 	self->mmap.tid	 = bswap_32(self->mmap.tid);
249 	self->mmap.start = bswap_64(self->mmap.start);
250 	self->mmap.len	 = bswap_64(self->mmap.len);
251 	self->mmap.pgoff = bswap_64(self->mmap.pgoff);
252 }
253 
254 static void event__task_swap(event_t *self)
255 {
256 	self->fork.pid	= bswap_32(self->fork.pid);
257 	self->fork.tid	= bswap_32(self->fork.tid);
258 	self->fork.ppid	= bswap_32(self->fork.ppid);
259 	self->fork.ptid	= bswap_32(self->fork.ptid);
260 	self->fork.time	= bswap_64(self->fork.time);
261 }
262 
263 static void event__read_swap(event_t *self)
264 {
265 	self->read.pid		= bswap_32(self->read.pid);
266 	self->read.tid		= bswap_32(self->read.tid);
267 	self->read.value	= bswap_64(self->read.value);
268 	self->read.time_enabled	= bswap_64(self->read.time_enabled);
269 	self->read.time_running	= bswap_64(self->read.time_running);
270 	self->read.id		= bswap_64(self->read.id);
271 }
272 
273 typedef void (*event__swap_op)(event_t *self);
274 
275 static event__swap_op event__swap_ops[] = {
276 	[PERF_RECORD_MMAP]   = event__mmap_swap,
277 	[PERF_RECORD_COMM]   = event__comm_swap,
278 	[PERF_RECORD_FORK]   = event__task_swap,
279 	[PERF_RECORD_EXIT]   = event__task_swap,
280 	[PERF_RECORD_LOST]   = event__all64_swap,
281 	[PERF_RECORD_READ]   = event__read_swap,
282 	[PERF_RECORD_SAMPLE] = event__all64_swap,
283 	[PERF_RECORD_MAX]    = NULL,
284 };
285 
286 static int perf_session__process_event(struct perf_session *self,
287 				       event_t *event,
288 				       struct perf_event_ops *ops,
289 				       u64 offset, u64 head)
290 {
291 	trace_event(event);
292 
293 	if (event->header.type < PERF_RECORD_MAX) {
294 		dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
295 			    offset + head, event->header.size,
296 			    event__name[event->header.type]);
297 		++event__total[0];
298 		++event__total[event->header.type];
299 	}
300 
301 	if (self->header.needs_swap && event__swap_ops[event->header.type])
302 		event__swap_ops[event->header.type](event);
303 
304 	switch (event->header.type) {
305 	case PERF_RECORD_SAMPLE:
306 		return ops->sample(event, self);
307 	case PERF_RECORD_MMAP:
308 		return ops->mmap(event, self);
309 	case PERF_RECORD_COMM:
310 		return ops->comm(event, self);
311 	case PERF_RECORD_FORK:
312 		return ops->fork(event, self);
313 	case PERF_RECORD_EXIT:
314 		return ops->exit(event, self);
315 	case PERF_RECORD_LOST:
316 		return ops->lost(event, self);
317 	case PERF_RECORD_READ:
318 		return ops->read(event, self);
319 	case PERF_RECORD_THROTTLE:
320 		return ops->throttle(event, self);
321 	case PERF_RECORD_UNTHROTTLE:
322 		return ops->unthrottle(event, self);
323 	default:
324 		self->unknown_events++;
325 		return -1;
326 	}
327 }
328 
329 void perf_event_header__bswap(struct perf_event_header *self)
330 {
331 	self->type = bswap_32(self->type);
332 	self->misc = bswap_16(self->misc);
333 	self->size = bswap_16(self->size);
334 }
335 
336 int perf_header__read_build_ids(struct perf_header *self,
337 				int input, u64 offset, u64 size)
338 {
339 	struct build_id_event bev;
340 	char filename[PATH_MAX];
341 	u64 limit = offset + size;
342 	int err = -1;
343 
344 	while (offset < limit) {
345 		struct dso *dso;
346 		ssize_t len;
347 		struct list_head *head = &dsos__user;
348 
349 		if (read(input, &bev, sizeof(bev)) != sizeof(bev))
350 			goto out;
351 
352 		if (self->needs_swap)
353 			perf_event_header__bswap(&bev.header);
354 
355 		len = bev.header.size - sizeof(bev);
356 		if (read(input, filename, len) != len)
357 			goto out;
358 
359 		if (bev.header.misc & PERF_RECORD_MISC_KERNEL)
360 			head = &dsos__kernel;
361 
362 		dso = __dsos__findnew(head, filename);
363 		if (dso != NULL) {
364 			dso__set_build_id(dso, &bev.build_id);
365 			if (head == &dsos__kernel && filename[0] == '[')
366 				dso->kernel = 1;
367 		}
368 
369 		offset += bev.header.size;
370 	}
371 	err = 0;
372 out:
373 	return err;
374 }
375 
376 static struct thread *perf_session__register_idle_thread(struct perf_session *self)
377 {
378 	struct thread *thread = perf_session__findnew(self, 0);
379 
380 	if (thread == NULL || thread__set_comm(thread, "swapper")) {
381 		pr_err("problem inserting idle task.\n");
382 		thread = NULL;
383 	}
384 
385 	return thread;
386 }
387 
388 int __perf_session__process_events(struct perf_session *self,
389 				   u64 data_offset, u64 data_size,
390 				   u64 file_size, struct perf_event_ops *ops)
391 {
392 	int err, mmap_prot, mmap_flags;
393 	u64 head, shift;
394 	u64 offset = 0;
395 	size_t	page_size;
396 	event_t *event;
397 	uint32_t size;
398 	char *buf;
399 
400 	perf_event_ops__fill_defaults(ops);
401 
402 	page_size = sysconf(_SC_PAGESIZE);
403 
404 	head = data_offset;
405 	shift = page_size * (head / page_size);
406 	offset += shift;
407 	head -= shift;
408 
409 	mmap_prot  = PROT_READ;
410 	mmap_flags = MAP_SHARED;
411 
412 	if (self->header.needs_swap) {
413 		mmap_prot  |= PROT_WRITE;
414 		mmap_flags = MAP_PRIVATE;
415 	}
416 remap:
417 	buf = mmap(NULL, page_size * self->mmap_window, mmap_prot,
418 		   mmap_flags, self->fd, offset);
419 	if (buf == MAP_FAILED) {
420 		pr_err("failed to mmap file\n");
421 		err = -errno;
422 		goto out_err;
423 	}
424 
425 more:
426 	event = (event_t *)(buf + head);
427 
428 	if (self->header.needs_swap)
429 		perf_event_header__bswap(&event->header);
430 	size = event->header.size;
431 	if (size == 0)
432 		size = 8;
433 
434 	if (head + event->header.size >= page_size * self->mmap_window) {
435 		int munmap_ret;
436 
437 		shift = page_size * (head / page_size);
438 
439 		munmap_ret = munmap(buf, page_size * self->mmap_window);
440 		assert(munmap_ret == 0);
441 
442 		offset += shift;
443 		head -= shift;
444 		goto remap;
445 	}
446 
447 	size = event->header.size;
448 
449 	dump_printf("\n%#Lx [%#x]: event: %d\n",
450 		    offset + head, event->header.size, event->header.type);
451 
452 	if (size == 0 ||
453 	    perf_session__process_event(self, event, ops, offset, head) < 0) {
454 		dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
455 			    offset + head, event->header.size,
456 			    event->header.type);
457 		/*
458 		 * assume we lost track of the stream, check alignment, and
459 		 * increment a single u64 in the hope to catch on again 'soon'.
460 		 */
461 		if (unlikely(head & 7))
462 			head &= ~7ULL;
463 
464 		size = 8;
465 	}
466 
467 	head += size;
468 
469 	if (offset + head >= data_offset + data_size)
470 		goto done;
471 
472 	if (offset + head < file_size)
473 		goto more;
474 done:
475 	err = 0;
476 out_err:
477 	return err;
478 }
479 
480 int perf_session__process_events(struct perf_session *self,
481 				 struct perf_event_ops *ops)
482 {
483 	int err;
484 
485 	if (perf_session__register_idle_thread(self) == NULL)
486 		return -ENOMEM;
487 
488 	if (!symbol_conf.full_paths) {
489 		char bf[PATH_MAX];
490 
491 		if (getcwd(bf, sizeof(bf)) == NULL) {
492 			err = -errno;
493 out_getcwd_err:
494 			pr_err("failed to get the current directory\n");
495 			goto out_err;
496 		}
497 		self->cwd = strdup(bf);
498 		if (self->cwd == NULL) {
499 			err = -ENOMEM;
500 			goto out_getcwd_err;
501 		}
502 		self->cwdlen = strlen(self->cwd);
503 	}
504 
505 	err = __perf_session__process_events(self, self->header.data_offset,
506 					     self->header.data_size,
507 					     self->size, ops);
508 out_err:
509 	return err;
510 }
511 
512 bool perf_session__has_traces(struct perf_session *self, const char *msg)
513 {
514 	if (!(self->sample_type & PERF_SAMPLE_RAW)) {
515 		pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
516 		return false;
517 	}
518 
519 	return true;
520 }
521 
522 int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self,
523 					     const char *symbol_name,
524 					     u64 addr)
525 {
526 	char *bracket;
527 	enum map_type i;
528 
529 	self->ref_reloc_sym.name = strdup(symbol_name);
530 	if (self->ref_reloc_sym.name == NULL)
531 		return -ENOMEM;
532 
533 	bracket = strchr(self->ref_reloc_sym.name, ']');
534 	if (bracket)
535 		*bracket = '\0';
536 
537 	self->ref_reloc_sym.addr = addr;
538 
539 	for (i = 0; i < MAP__NR_TYPES; ++i) {
540 		struct kmap *kmap = map__kmap(self->vmlinux_maps[i]);
541 		kmap->ref_reloc_sym = &self->ref_reloc_sym;
542 	}
543 
544 	return 0;
545 }
546 
547 static u64 map__reloc_map_ip(struct map *map, u64 ip)
548 {
549 	return ip + (s64)map->pgoff;
550 }
551 
552 static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
553 {
554 	return ip - (s64)map->pgoff;
555 }
556 
557 void map__reloc_vmlinux(struct map *self)
558 {
559 	struct kmap *kmap = map__kmap(self);
560 	s64 reloc;
561 
562 	if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr)
563 		return;
564 
565 	reloc = (kmap->ref_reloc_sym->unrelocated_addr -
566 		 kmap->ref_reloc_sym->addr);
567 
568 	if (!reloc)
569 		return;
570 
571 	self->map_ip   = map__reloc_map_ip;
572 	self->unmap_ip = map__reloc_unmap_ip;
573 	self->pgoff    = reloc;
574 }
575