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