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