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