diff options
Diffstat (limited to 'tools/perf/util/session.c')
-rw-r--r-- | tools/perf/util/session.c | 325 |
1 files changed, 223 insertions, 102 deletions
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index eed1cb889008..7d88ae5c270f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -14,6 +14,16 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
14 | { | 14 | { |
15 | struct stat input_stat; | 15 | struct stat input_stat; |
16 | 16 | ||
17 | if (!strcmp(self->filename, "-")) { | ||
18 | self->fd_pipe = true; | ||
19 | self->fd = STDIN_FILENO; | ||
20 | |||
21 | if (perf_header__read(self, self->fd) < 0) | ||
22 | pr_err("incompatible file format"); | ||
23 | |||
24 | return 0; | ||
25 | } | ||
26 | |||
17 | self->fd = open(self->filename, O_RDONLY); | 27 | self->fd = open(self->filename, O_RDONLY); |
18 | if (self->fd < 0) { | 28 | if (self->fd < 0) { |
19 | pr_err("failed to open file: %s", self->filename); | 29 | pr_err("failed to open file: %s", self->filename); |
@@ -38,7 +48,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
38 | goto out_close; | 48 | goto out_close; |
39 | } | 49 | } |
40 | 50 | ||
41 | if (perf_header__read(&self->header, self->fd) < 0) { | 51 | if (perf_header__read(self, self->fd) < 0) { |
42 | pr_err("incompatible file format"); | 52 | pr_err("incompatible file format"); |
43 | goto out_close; | 53 | goto out_close; |
44 | } | 54 | } |
@@ -52,9 +62,20 @@ out_close: | |||
52 | return -1; | 62 | return -1; |
53 | } | 63 | } |
54 | 64 | ||
55 | static inline int perf_session__create_kernel_maps(struct perf_session *self) | 65 | void perf_session__update_sample_type(struct perf_session *self) |
56 | { | 66 | { |
57 | return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); | 67 | self->sample_type = perf_header__sample_type(&self->header); |
68 | } | ||
69 | |||
70 | int perf_session__create_kernel_maps(struct perf_session *self) | ||
71 | { | ||
72 | int ret; | ||
73 | struct rb_root *root = &self->kerninfo_root; | ||
74 | |||
75 | ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID); | ||
76 | if (ret >= 0) | ||
77 | ret = map_groups__create_guest_kernel_maps(root); | ||
78 | return ret; | ||
58 | } | 79 | } |
59 | 80 | ||
60 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | 81 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) |
@@ -76,7 +97,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
76 | self->cwd = NULL; | 97 | self->cwd = NULL; |
77 | self->cwdlen = 0; | 98 | self->cwdlen = 0; |
78 | self->unknown_events = 0; | 99 | self->unknown_events = 0; |
79 | map_groups__init(&self->kmaps); | 100 | self->kerninfo_root = RB_ROOT; |
80 | 101 | ||
81 | if (mode == O_RDONLY) { | 102 | if (mode == O_RDONLY) { |
82 | if (perf_session__open(self, force) < 0) | 103 | if (perf_session__open(self, force) < 0) |
@@ -90,7 +111,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
90 | goto out_delete; | 111 | goto out_delete; |
91 | } | 112 | } |
92 | 113 | ||
93 | self->sample_type = perf_header__sample_type(&self->header); | 114 | perf_session__update_sample_type(self); |
94 | out: | 115 | out: |
95 | return self; | 116 | return self; |
96 | out_free: | 117 | out_free: |
@@ -117,22 +138,17 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
117 | return 0; | 138 | return 0; |
118 | } | 139 | } |
119 | 140 | ||
120 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | 141 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, |
121 | struct thread *thread, | 142 | struct thread *thread, |
122 | struct ip_callchain *chain, | 143 | struct ip_callchain *chain, |
123 | struct symbol **parent) | 144 | struct symbol **parent) |
124 | { | 145 | { |
125 | u8 cpumode = PERF_RECORD_MISC_USER; | 146 | u8 cpumode = PERF_RECORD_MISC_USER; |
126 | struct symbol **syms = NULL; | ||
127 | unsigned int i; | 147 | unsigned int i; |
148 | struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); | ||
128 | 149 | ||
129 | if (symbol_conf.use_callchain) { | 150 | if (!syms) |
130 | syms = calloc(chain->nr, sizeof(*syms)); | 151 | return NULL; |
131 | if (!syms) { | ||
132 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
133 | exit(-1); | ||
134 | } | ||
135 | } | ||
136 | 152 | ||
137 | for (i = 0; i < chain->nr; i++) { | 153 | for (i = 0; i < chain->nr; i++) { |
138 | u64 ip = chain->ips[i]; | 154 | u64 ip = chain->ips[i]; |
@@ -152,15 +168,17 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, | |||
152 | continue; | 168 | continue; |
153 | } | 169 | } |
154 | 170 | ||
171 | al.filtered = false; | ||
155 | thread__find_addr_location(thread, self, cpumode, | 172 | thread__find_addr_location(thread, self, cpumode, |
156 | MAP__FUNCTION, ip, &al, NULL); | 173 | MAP__FUNCTION, thread->pid, ip, &al, NULL); |
157 | if (al.sym != NULL) { | 174 | if (al.sym != NULL) { |
158 | if (sort__has_parent && !*parent && | 175 | if (sort__has_parent && !*parent && |
159 | symbol__match_parent_regex(al.sym)) | 176 | symbol__match_parent_regex(al.sym)) |
160 | *parent = al.sym; | 177 | *parent = al.sym; |
161 | if (!symbol_conf.use_callchain) | 178 | if (!symbol_conf.use_callchain) |
162 | break; | 179 | break; |
163 | syms[i] = al.sym; | 180 | syms[i].map = al.map; |
181 | syms[i].sym = al.sym; | ||
164 | } | 182 | } |
165 | } | 183 | } |
166 | 184 | ||
@@ -194,6 +212,14 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | |||
194 | handler->throttle = process_event_stub; | 212 | handler->throttle = process_event_stub; |
195 | if (handler->unthrottle == NULL) | 213 | if (handler->unthrottle == NULL) |
196 | handler->unthrottle = process_event_stub; | 214 | handler->unthrottle = process_event_stub; |
215 | if (handler->attr == NULL) | ||
216 | handler->attr = process_event_stub; | ||
217 | if (handler->event_type == NULL) | ||
218 | handler->event_type = process_event_stub; | ||
219 | if (handler->tracing_data == NULL) | ||
220 | handler->tracing_data = process_event_stub; | ||
221 | if (handler->build_id == NULL) | ||
222 | handler->build_id = process_event_stub; | ||
197 | } | 223 | } |
198 | 224 | ||
199 | static const char *event__name[] = { | 225 | static const char *event__name[] = { |
@@ -207,16 +233,23 @@ static const char *event__name[] = { | |||
207 | [PERF_RECORD_FORK] = "FORK", | 233 | [PERF_RECORD_FORK] = "FORK", |
208 | [PERF_RECORD_READ] = "READ", | 234 | [PERF_RECORD_READ] = "READ", |
209 | [PERF_RECORD_SAMPLE] = "SAMPLE", | 235 | [PERF_RECORD_SAMPLE] = "SAMPLE", |
236 | [PERF_RECORD_HEADER_ATTR] = "ATTR", | ||
237 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", | ||
238 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", | ||
239 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", | ||
210 | }; | 240 | }; |
211 | 241 | ||
212 | unsigned long event__total[PERF_RECORD_MAX]; | 242 | unsigned long event__total[PERF_RECORD_HEADER_MAX]; |
213 | 243 | ||
214 | void event__print_totals(void) | 244 | void event__print_totals(void) |
215 | { | 245 | { |
216 | int i; | 246 | int i; |
217 | for (i = 0; i < PERF_RECORD_MAX; ++i) | 247 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
248 | if (!event__name[i]) | ||
249 | continue; | ||
218 | pr_info("%10s events: %10ld\n", | 250 | pr_info("%10s events: %10ld\n", |
219 | event__name[i], event__total[i]); | 251 | event__name[i], event__total[i]); |
252 | } | ||
220 | } | 253 | } |
221 | 254 | ||
222 | void mem_bswap_64(void *src, int byte_size) | 255 | void mem_bswap_64(void *src, int byte_size) |
@@ -270,6 +303,37 @@ static void event__read_swap(event_t *self) | |||
270 | self->read.id = bswap_64(self->read.id); | 303 | self->read.id = bswap_64(self->read.id); |
271 | } | 304 | } |
272 | 305 | ||
306 | static void event__attr_swap(event_t *self) | ||
307 | { | ||
308 | size_t size; | ||
309 | |||
310 | self->attr.attr.type = bswap_32(self->attr.attr.type); | ||
311 | self->attr.attr.size = bswap_32(self->attr.attr.size); | ||
312 | self->attr.attr.config = bswap_64(self->attr.attr.config); | ||
313 | self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period); | ||
314 | self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type); | ||
315 | self->attr.attr.read_format = bswap_64(self->attr.attr.read_format); | ||
316 | self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events); | ||
317 | self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type); | ||
318 | self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr); | ||
319 | self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len); | ||
320 | |||
321 | size = self->header.size; | ||
322 | size -= (void *)&self->attr.id - (void *)self; | ||
323 | mem_bswap_64(self->attr.id, size); | ||
324 | } | ||
325 | |||
326 | static void event__event_type_swap(event_t *self) | ||
327 | { | ||
328 | self->event_type.event_type.event_id = | ||
329 | bswap_64(self->event_type.event_type.event_id); | ||
330 | } | ||
331 | |||
332 | static void event__tracing_data_swap(event_t *self) | ||
333 | { | ||
334 | self->tracing_data.size = bswap_32(self->tracing_data.size); | ||
335 | } | ||
336 | |||
273 | typedef void (*event__swap_op)(event_t *self); | 337 | typedef void (*event__swap_op)(event_t *self); |
274 | 338 | ||
275 | static event__swap_op event__swap_ops[] = { | 339 | static event__swap_op event__swap_ops[] = { |
@@ -280,7 +344,11 @@ static event__swap_op event__swap_ops[] = { | |||
280 | [PERF_RECORD_LOST] = event__all64_swap, | 344 | [PERF_RECORD_LOST] = event__all64_swap, |
281 | [PERF_RECORD_READ] = event__read_swap, | 345 | [PERF_RECORD_READ] = event__read_swap, |
282 | [PERF_RECORD_SAMPLE] = event__all64_swap, | 346 | [PERF_RECORD_SAMPLE] = event__all64_swap, |
283 | [PERF_RECORD_MAX] = NULL, | 347 | [PERF_RECORD_HEADER_ATTR] = event__attr_swap, |
348 | [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap, | ||
349 | [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap, | ||
350 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | ||
351 | [PERF_RECORD_HEADER_MAX] = NULL, | ||
284 | }; | 352 | }; |
285 | 353 | ||
286 | static int perf_session__process_event(struct perf_session *self, | 354 | static int perf_session__process_event(struct perf_session *self, |
@@ -290,7 +358,7 @@ static int perf_session__process_event(struct perf_session *self, | |||
290 | { | 358 | { |
291 | trace_event(event); | 359 | trace_event(event); |
292 | 360 | ||
293 | if (event->header.type < PERF_RECORD_MAX) { | 361 | if (event->header.type < PERF_RECORD_HEADER_MAX) { |
294 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | 362 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", |
295 | offset + head, event->header.size, | 363 | offset + head, event->header.size, |
296 | event__name[event->header.type]); | 364 | event__name[event->header.type]); |
@@ -320,6 +388,16 @@ static int perf_session__process_event(struct perf_session *self, | |||
320 | return ops->throttle(event, self); | 388 | return ops->throttle(event, self); |
321 | case PERF_RECORD_UNTHROTTLE: | 389 | case PERF_RECORD_UNTHROTTLE: |
322 | return ops->unthrottle(event, self); | 390 | return ops->unthrottle(event, self); |
391 | case PERF_RECORD_HEADER_ATTR: | ||
392 | return ops->attr(event, self); | ||
393 | case PERF_RECORD_HEADER_EVENT_TYPE: | ||
394 | return ops->event_type(event, self); | ||
395 | case PERF_RECORD_HEADER_TRACING_DATA: | ||
396 | /* setup for reading amidst mmap */ | ||
397 | lseek(self->fd, offset + head, SEEK_SET); | ||
398 | return ops->tracing_data(event, self); | ||
399 | case PERF_RECORD_HEADER_BUILD_ID: | ||
400 | return ops->build_id(event, self); | ||
323 | default: | 401 | default: |
324 | self->unknown_events++; | 402 | self->unknown_events++; |
325 | return -1; | 403 | return -1; |
@@ -333,46 +411,6 @@ void perf_event_header__bswap(struct perf_event_header *self) | |||
333 | self->size = bswap_16(self->size); | 411 | self->size = bswap_16(self->size); |
334 | } | 412 | } |
335 | 413 | ||
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) | 414 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) |
377 | { | 415 | { |
378 | struct thread *thread = perf_session__findnew(self, 0); | 416 | struct thread *thread = perf_session__findnew(self, 0); |
@@ -385,6 +423,101 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se | |||
385 | return thread; | 423 | return thread; |
386 | } | 424 | } |
387 | 425 | ||
426 | int do_read(int fd, void *buf, size_t size) | ||
427 | { | ||
428 | void *buf_start = buf; | ||
429 | |||
430 | while (size) { | ||
431 | int ret = read(fd, buf, size); | ||
432 | |||
433 | if (ret <= 0) | ||
434 | return ret; | ||
435 | |||
436 | size -= ret; | ||
437 | buf += ret; | ||
438 | } | ||
439 | |||
440 | return buf - buf_start; | ||
441 | } | ||
442 | |||
443 | #define session_done() (*(volatile int *)(&session_done)) | ||
444 | volatile int session_done; | ||
445 | |||
446 | static int __perf_session__process_pipe_events(struct perf_session *self, | ||
447 | struct perf_event_ops *ops) | ||
448 | { | ||
449 | event_t event; | ||
450 | uint32_t size; | ||
451 | int skip = 0; | ||
452 | u64 head; | ||
453 | int err; | ||
454 | void *p; | ||
455 | |||
456 | perf_event_ops__fill_defaults(ops); | ||
457 | |||
458 | head = 0; | ||
459 | more: | ||
460 | err = do_read(self->fd, &event, sizeof(struct perf_event_header)); | ||
461 | if (err <= 0) { | ||
462 | if (err == 0) | ||
463 | goto done; | ||
464 | |||
465 | pr_err("failed to read event header\n"); | ||
466 | goto out_err; | ||
467 | } | ||
468 | |||
469 | if (self->header.needs_swap) | ||
470 | perf_event_header__bswap(&event.header); | ||
471 | |||
472 | size = event.header.size; | ||
473 | if (size == 0) | ||
474 | size = 8; | ||
475 | |||
476 | p = &event; | ||
477 | p += sizeof(struct perf_event_header); | ||
478 | |||
479 | err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); | ||
480 | if (err <= 0) { | ||
481 | if (err == 0) { | ||
482 | pr_err("unexpected end of event stream\n"); | ||
483 | goto done; | ||
484 | } | ||
485 | |||
486 | pr_err("failed to read event data\n"); | ||
487 | goto out_err; | ||
488 | } | ||
489 | |||
490 | if (size == 0 || | ||
491 | (skip = perf_session__process_event(self, &event, ops, | ||
492 | 0, head)) < 0) { | ||
493 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | ||
494 | head, event.header.size, event.header.type); | ||
495 | /* | ||
496 | * assume we lost track of the stream, check alignment, and | ||
497 | * increment a single u64 in the hope to catch on again 'soon'. | ||
498 | */ | ||
499 | if (unlikely(head & 7)) | ||
500 | head &= ~7ULL; | ||
501 | |||
502 | size = 8; | ||
503 | } | ||
504 | |||
505 | head += size; | ||
506 | |||
507 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
508 | head, event.header.size, event.header.type); | ||
509 | |||
510 | if (skip > 0) | ||
511 | head += skip; | ||
512 | |||
513 | if (!session_done()) | ||
514 | goto more; | ||
515 | done: | ||
516 | err = 0; | ||
517 | out_err: | ||
518 | return err; | ||
519 | } | ||
520 | |||
388 | int __perf_session__process_events(struct perf_session *self, | 521 | int __perf_session__process_events(struct perf_session *self, |
389 | u64 data_offset, u64 data_size, | 522 | u64 data_offset, u64 data_size, |
390 | u64 file_size, struct perf_event_ops *ops) | 523 | u64 file_size, struct perf_event_ops *ops) |
@@ -396,6 +529,10 @@ int __perf_session__process_events(struct perf_session *self, | |||
396 | event_t *event; | 529 | event_t *event; |
397 | uint32_t size; | 530 | uint32_t size; |
398 | char *buf; | 531 | char *buf; |
532 | struct ui_progress *progress = ui_progress__new("Processing events...", | ||
533 | self->size); | ||
534 | if (progress == NULL) | ||
535 | return -1; | ||
399 | 536 | ||
400 | perf_event_ops__fill_defaults(ops); | 537 | perf_event_ops__fill_defaults(ops); |
401 | 538 | ||
@@ -424,6 +561,7 @@ remap: | |||
424 | 561 | ||
425 | more: | 562 | more: |
426 | event = (event_t *)(buf + head); | 563 | event = (event_t *)(buf + head); |
564 | ui_progress__update(progress, offset); | ||
427 | 565 | ||
428 | if (self->header.needs_swap) | 566 | if (self->header.needs_swap) |
429 | perf_event_header__bswap(&event->header); | 567 | perf_event_header__bswap(&event->header); |
@@ -474,6 +612,7 @@ more: | |||
474 | done: | 612 | done: |
475 | err = 0; | 613 | err = 0; |
476 | out_err: | 614 | out_err: |
615 | ui_progress__delete(progress); | ||
477 | return err; | 616 | return err; |
478 | } | 617 | } |
479 | 618 | ||
@@ -502,9 +641,13 @@ out_getcwd_err: | |||
502 | self->cwdlen = strlen(self->cwd); | 641 | self->cwdlen = strlen(self->cwd); |
503 | } | 642 | } |
504 | 643 | ||
505 | err = __perf_session__process_events(self, self->header.data_offset, | 644 | if (!self->fd_pipe) |
506 | self->header.data_size, | 645 | err = __perf_session__process_events(self, |
507 | self->size, ops); | 646 | self->header.data_offset, |
647 | self->header.data_size, | ||
648 | self->size, ops); | ||
649 | else | ||
650 | err = __perf_session__process_pipe_events(self, ops); | ||
508 | out_err: | 651 | out_err: |
509 | return err; | 652 | return err; |
510 | } | 653 | } |
@@ -519,56 +662,34 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg) | |||
519 | return true; | 662 | return true; |
520 | } | 663 | } |
521 | 664 | ||
522 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | 665 | int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, |
523 | const char *symbol_name, | 666 | const char *symbol_name, |
524 | u64 addr) | 667 | u64 addr) |
525 | { | 668 | { |
526 | char *bracket; | 669 | char *bracket; |
527 | enum map_type i; | 670 | enum map_type i; |
671 | struct ref_reloc_sym *ref; | ||
528 | 672 | ||
529 | self->ref_reloc_sym.name = strdup(symbol_name); | 673 | ref = zalloc(sizeof(struct ref_reloc_sym)); |
530 | if (self->ref_reloc_sym.name == NULL) | 674 | if (ref == NULL) |
531 | return -ENOMEM; | 675 | return -ENOMEM; |
532 | 676 | ||
533 | bracket = strchr(self->ref_reloc_sym.name, ']'); | 677 | ref->name = strdup(symbol_name); |
678 | if (ref->name == NULL) { | ||
679 | free(ref); | ||
680 | return -ENOMEM; | ||
681 | } | ||
682 | |||
683 | bracket = strchr(ref->name, ']'); | ||
534 | if (bracket) | 684 | if (bracket) |
535 | *bracket = '\0'; | 685 | *bracket = '\0'; |
536 | 686 | ||
537 | self->ref_reloc_sym.addr = addr; | 687 | ref->addr = addr; |
538 | 688 | ||
539 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 689 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
540 | struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); | 690 | struct kmap *kmap = map__kmap(maps[i]); |
541 | kmap->ref_reloc_sym = &self->ref_reloc_sym; | 691 | kmap->ref_reloc_sym = ref; |
542 | } | 692 | } |
543 | 693 | ||
544 | return 0; | 694 | return 0; |
545 | } | 695 | } |
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 | } | ||