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#include <sys/mman.h>
9
10#include "session.h"
11#include "sort.h"
12#include "util.h"
13
14static int perf_session__open(struct perf_session *self, bool force)
15{
16 struct stat input_stat;
17
18 if (!strcmp(self->filename, "-")) {
19 self->fd_pipe = true;
20 self->fd = STDIN_FILENO;
21
22 if (perf_header__read(self, self->fd) < 0)
23 pr_err("incompatible file format");
24
25 return 0;
26 }
27
28 self->fd = open(self->filename, O_RDONLY);
29 if (self->fd < 0) {
30 pr_err("failed to open file: %s", self->filename);
31 if (!strcmp(self->filename, "perf.data"))
32 pr_err(" (try 'perf record' first)");
33 pr_err("\n");
34 return -errno;
35 }
36
37 if (fstat(self->fd, &input_stat) < 0)
38 goto out_close;
39
40 if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
41 pr_err("file %s not owned by current user or root\n",
42 self->filename);
43 goto out_close;
44 }
45
46 if (!input_stat.st_size) {
47 pr_info("zero-sized file (%s), nothing to do!\n",
48 self->filename);
49 goto out_close;
50 }
51
52 if (perf_header__read(self, self->fd) < 0) {
53 pr_err("incompatible file format");
54 goto out_close;
55 }
56
57 self->size = input_stat.st_size;
58 return 0;
59
60out_close:
61 close(self->fd);
62 self->fd = -1;
63 return -1;
64}
65
66void perf_session__update_sample_type(struct perf_session *self)
67{
68 self->sample_type = perf_header__sample_type(&self->header);
69}
70
71int perf_session__create_kernel_maps(struct perf_session *self)
72{
73 int ret = machine__create_kernel_maps(&self->host_machine);
74
75 if (ret >= 0)
76 ret = machines__create_guest_kernel_maps(&self->machines);
77 return ret;
78}
79
80struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe)
81{
82 size_t len = filename ? strlen(filename) + 1 : 0;
83 struct perf_session *self = zalloc(sizeof(*self) + len);
84
85 if (self == NULL)
86 goto out;
87
88 if (perf_header__init(&self->header) < 0)
89 goto out_free;
90
91 memcpy(self->filename, filename, len);
92 self->threads = RB_ROOT;
93 INIT_LIST_HEAD(&self->dead_threads);
94 self->hists_tree = RB_ROOT;
95 self->last_match = NULL;
96 self->mmap_window = 32;
97 self->cwd = NULL;
98 self->cwdlen = 0;
99 self->machines = RB_ROOT;
100 self->repipe = repipe;
101 INIT_LIST_HEAD(&self->ordered_samples.samples_head);
102 machine__init(&self->host_machine, "", HOST_KERNEL_ID);
103
104 if (mode == O_RDONLY) {
105 if (perf_session__open(self, force) < 0)
106 goto out_delete;
107 } else if (mode == O_WRONLY) {
108
109
110
111
112 if (perf_session__create_kernel_maps(self) < 0)
113 goto out_delete;
114 }
115
116 perf_session__update_sample_type(self);
117out:
118 return self;
119out_free:
120 free(self);
121 return NULL;
122out_delete:
123 perf_session__delete(self);
124 return NULL;
125}
126
127void perf_session__delete(struct perf_session *self)
128{
129 perf_header__exit(&self->header);
130 close(self->fd);
131 free(self->cwd);
132 free(self);
133}
134
135void perf_session__remove_thread(struct perf_session *self, struct thread *th)
136{
137 rb_erase(&th->rb_node, &self->threads);
138
139
140
141
142 list_add_tail(&th->node, &self->dead_threads);
143}
144
145static bool symbol__match_parent_regex(struct symbol *sym)
146{
147 if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
148 return 1;
149
150 return 0;
151}
152
153struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
154 struct thread *thread,
155 struct ip_callchain *chain,
156 struct symbol **parent)
157{
158 u8 cpumode = PERF_RECORD_MISC_USER;
159 unsigned int i;
160 struct map_symbol *syms = calloc(chain->nr, sizeof(*syms));
161
162 if (!syms)
163 return NULL;
164
165 for (i = 0; i < chain->nr; i++) {
166 u64 ip = chain->ips[i];
167 struct addr_location al;
168
169 if (ip >= PERF_CONTEXT_MAX) {
170 switch (ip) {
171 case PERF_CONTEXT_HV:
172 cpumode = PERF_RECORD_MISC_HYPERVISOR; break;
173 case PERF_CONTEXT_KERNEL:
174 cpumode = PERF_RECORD_MISC_KERNEL; break;
175 case PERF_CONTEXT_USER:
176 cpumode = PERF_RECORD_MISC_USER; break;
177 default:
178 break;
179 }
180 continue;
181 }
182
183 al.filtered = false;
184 thread__find_addr_location(thread, self, cpumode,
185 MAP__FUNCTION, thread->pid, ip, &al, NULL);
186 if (al.sym != NULL) {
187 if (sort__has_parent && !*parent &&
188 symbol__match_parent_regex(al.sym))
189 *parent = al.sym;
190 if (!symbol_conf.use_callchain)
191 break;
192 syms[i].map = al.map;
193 syms[i].sym = al.sym;
194 }
195 }
196
197 return syms;
198}
199
200static int process_event_stub(event_t *event __used,
201 struct perf_session *session __used)
202{
203 dump_printf(": unhandled!\n");
204 return 0;
205}
206
207static int process_finished_round_stub(event_t *event __used,
208 struct perf_session *session __used,
209 struct perf_event_ops *ops __used)
210{
211 dump_printf(": unhandled!\n");
212 return 0;
213}
214
215static int process_finished_round(event_t *event,
216 struct perf_session *session,
217 struct perf_event_ops *ops);
218
219static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
220{
221 if (handler->sample == NULL)
222 handler->sample = process_event_stub;
223 if (handler->mmap == NULL)
224 handler->mmap = process_event_stub;
225 if (handler->comm == NULL)
226 handler->comm = process_event_stub;
227 if (handler->fork == NULL)
228 handler->fork = process_event_stub;
229 if (handler->exit == NULL)
230 handler->exit = process_event_stub;
231 if (handler->lost == NULL)
232 handler->lost = process_event_stub;
233 if (handler->read == NULL)
234 handler->read = process_event_stub;
235 if (handler->throttle == NULL)
236 handler->throttle = process_event_stub;
237 if (handler->unthrottle == NULL)
238 handler->unthrottle = process_event_stub;
239 if (handler->attr == NULL)
240 handler->attr = process_event_stub;
241 if (handler->event_type == NULL)
242 handler->event_type = process_event_stub;
243 if (handler->tracing_data == NULL)
244 handler->tracing_data = process_event_stub;
245 if (handler->build_id == NULL)
246 handler->build_id = process_event_stub;
247 if (handler->finished_round == NULL) {
248 if (handler->ordered_samples)
249 handler->finished_round = process_finished_round;
250 else
251 handler->finished_round = process_finished_round_stub;
252 }
253}
254
255void mem_bswap_64(void *src, int byte_size)
256{
257 u64 *m = src;
258
259 while (byte_size > 0) {
260 *m = bswap_64(*m);
261 byte_size -= sizeof(u64);
262 ++m;
263 }
264}
265
266static void event__all64_swap(event_t *self)
267{
268 struct perf_event_header *hdr = &self->header;
269 mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr));
270}
271
272static void event__comm_swap(event_t *self)
273{
274 self->comm.pid = bswap_32(self->comm.pid);
275 self->comm.tid = bswap_32(self->comm.tid);
276}
277
278static void event__mmap_swap(event_t *self)
279{
280 self->mmap.pid = bswap_32(self->mmap.pid);
281 self->mmap.tid = bswap_32(self->mmap.tid);
282 self->mmap.start = bswap_64(self->mmap.start);
283 self->mmap.len = bswap_64(self->mmap.len);
284 self->mmap.pgoff = bswap_64(self->mmap.pgoff);
285}
286
287static void event__task_swap(event_t *self)
288{
289 self->fork.pid = bswap_32(self->fork.pid);
290 self->fork.tid = bswap_32(self->fork.tid);
291 self->fork.ppid = bswap_32(self->fork.ppid);
292 self->fork.ptid = bswap_32(self->fork.ptid);
293 self->fork.time = bswap_64(self->fork.time);
294}
295
296static void event__read_swap(event_t *self)
297{
298 self->read.pid = bswap_32(self->read.pid);
299 self->read.tid = bswap_32(self->read.tid);
300 self->read.value = bswap_64(self->read.value);
301 self->read.time_enabled = bswap_64(self->read.time_enabled);
302 self->read.time_running = bswap_64(self->read.time_running);
303 self->read.id = bswap_64(self->read.id);
304}
305
306static 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
326static 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
332static void event__tracing_data_swap(event_t *self)
333{
334 self->tracing_data.size = bswap_32(self->tracing_data.size);
335}
336
337typedef void (*event__swap_op)(event_t *self);
338
339static event__swap_op event__swap_ops[] = {
340 [PERF_RECORD_MMAP] = event__mmap_swap,
341 [PERF_RECORD_COMM] = event__comm_swap,
342 [PERF_RECORD_FORK] = event__task_swap,
343 [PERF_RECORD_EXIT] = event__task_swap,
344 [PERF_RECORD_LOST] = event__all64_swap,
345 [PERF_RECORD_READ] = event__read_swap,
346 [PERF_RECORD_SAMPLE] = event__all64_swap,
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,
352};
353
354struct sample_queue {
355 u64 timestamp;
356 struct sample_event *event;
357 struct list_head list;
358};
359
360static void flush_sample_queue(struct perf_session *s,
361 struct perf_event_ops *ops)
362{
363 struct list_head *head = &s->ordered_samples.samples_head;
364 u64 limit = s->ordered_samples.next_flush;
365 struct sample_queue *tmp, *iter;
366
367 if (!ops->ordered_samples || !limit)
368 return;
369
370 list_for_each_entry_safe(iter, tmp, head, list) {
371 if (iter->timestamp > limit)
372 return;
373
374 if (iter == s->ordered_samples.last_inserted)
375 s->ordered_samples.last_inserted = NULL;
376
377 ops->sample((event_t *)iter->event, s);
378
379 s->ordered_samples.last_flush = iter->timestamp;
380 list_del(&iter->list);
381 free(iter->event);
382 free(iter);
383 }
384}
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425static int process_finished_round(event_t *event __used,
426 struct perf_session *session,
427 struct perf_event_ops *ops)
428{
429 flush_sample_queue(session, ops);
430 session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
431
432 return 0;
433}
434
435static void __queue_sample_end(struct sample_queue *new, struct list_head *head)
436{
437 struct sample_queue *iter;
438
439 list_for_each_entry_reverse(iter, head, list) {
440 if (iter->timestamp < new->timestamp) {
441 list_add(&new->list, &iter->list);
442 return;
443 }
444 }
445
446 list_add(&new->list, head);
447}
448
449static void __queue_sample_before(struct sample_queue *new,
450 struct sample_queue *iter,
451 struct list_head *head)
452{
453 list_for_each_entry_continue_reverse(iter, head, list) {
454 if (iter->timestamp < new->timestamp) {
455 list_add(&new->list, &iter->list);
456 return;
457 }
458 }
459
460 list_add(&new->list, head);
461}
462
463static void __queue_sample_after(struct sample_queue *new,
464 struct sample_queue *iter,
465 struct list_head *head)
466{
467 list_for_each_entry_continue(iter, head, list) {
468 if (iter->timestamp > new->timestamp) {
469 list_add_tail(&new->list, &iter->list);
470 return;
471 }
472 }
473 list_add_tail(&new->list, head);
474}
475
476
477static void __queue_sample_event(struct sample_queue *new,
478 struct perf_session *s)
479{
480 struct sample_queue *last_inserted = s->ordered_samples.last_inserted;
481 struct list_head *head = &s->ordered_samples.samples_head;
482
483
484 if (!last_inserted) {
485 __queue_sample_end(new, head);
486 return;
487 }
488
489
490
491
492
493
494
495
496 if (last_inserted->timestamp >= new->timestamp)
497 __queue_sample_before(new, last_inserted, head);
498 else
499 __queue_sample_after(new, last_inserted, head);
500}
501
502static int queue_sample_event(event_t *event, struct sample_data *data,
503 struct perf_session *s)
504{
505 u64 timestamp = data->time;
506 struct sample_queue *new;
507
508
509 if (timestamp < s->ordered_samples.last_flush) {
510 printf("Warning: Timestamp below last timeslice flush\n");
511 return -EINVAL;
512 }
513
514 new = malloc(sizeof(*new));
515 if (!new)
516 return -ENOMEM;
517
518 new->timestamp = timestamp;
519
520 new->event = malloc(event->header.size);
521 if (!new->event) {
522 free(new);
523 return -ENOMEM;
524 }
525
526 memcpy(new->event, event, event->header.size);
527
528 __queue_sample_event(new, s);
529 s->ordered_samples.last_inserted = new;
530
531 if (new->timestamp > s->ordered_samples.max_timestamp)
532 s->ordered_samples.max_timestamp = new->timestamp;
533
534 return 0;
535}
536
537static int perf_session__process_sample(event_t *event, struct perf_session *s,
538 struct perf_event_ops *ops)
539{
540 struct sample_data data;
541
542 if (!ops->ordered_samples)
543 return ops->sample(event, s);
544
545 bzero(&data, sizeof(struct sample_data));
546 event__parse_sample(event, s->sample_type, &data);
547
548 queue_sample_event(event, &data, s);
549
550 return 0;
551}
552
553static int perf_session__process_event(struct perf_session *self,
554 event_t *event,
555 struct perf_event_ops *ops,
556 u64 offset, u64 head)
557{
558 trace_event(event);
559
560 if (event->header.type < PERF_RECORD_HEADER_MAX) {
561 dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
562 offset + head, event->header.size,
563 event__name[event->header.type]);
564 hists__inc_nr_events(&self->hists, event->header.type);
565 }
566
567 if (self->header.needs_swap && event__swap_ops[event->header.type])
568 event__swap_ops[event->header.type](event);
569
570 switch (event->header.type) {
571 case PERF_RECORD_SAMPLE:
572 return perf_session__process_sample(event, self, ops);
573 case PERF_RECORD_MMAP:
574 return ops->mmap(event, self);
575 case PERF_RECORD_COMM:
576 return ops->comm(event, self);
577 case PERF_RECORD_FORK:
578 return ops->fork(event, self);
579 case PERF_RECORD_EXIT:
580 return ops->exit(event, self);
581 case PERF_RECORD_LOST:
582 return ops->lost(event, self);
583 case PERF_RECORD_READ:
584 return ops->read(event, self);
585 case PERF_RECORD_THROTTLE:
586 return ops->throttle(event, self);
587 case PERF_RECORD_UNTHROTTLE:
588 return ops->unthrottle(event, self);
589 case PERF_RECORD_HEADER_ATTR:
590 return ops->attr(event, self);
591 case PERF_RECORD_HEADER_EVENT_TYPE:
592 return ops->event_type(event, self);
593 case PERF_RECORD_HEADER_TRACING_DATA:
594
595 lseek(self->fd, offset + head, SEEK_SET);
596 return ops->tracing_data(event, self);
597 case PERF_RECORD_HEADER_BUILD_ID:
598 return ops->build_id(event, self);
599 case PERF_RECORD_FINISHED_ROUND:
600 return ops->finished_round(event, self, ops);
601 default:
602 ++self->hists.stats.nr_unknown_events;
603 return -1;
604 }
605}
606
607void perf_event_header__bswap(struct perf_event_header *self)
608{
609 self->type = bswap_32(self->type);
610 self->misc = bswap_16(self->misc);
611 self->size = bswap_16(self->size);
612}
613
614static struct thread *perf_session__register_idle_thread(struct perf_session *self)
615{
616 struct thread *thread = perf_session__findnew(self, 0);
617
618 if (thread == NULL || thread__set_comm(thread, "swapper")) {
619 pr_err("problem inserting idle task.\n");
620 thread = NULL;
621 }
622
623 return thread;
624}
625
626int do_read(int fd, void *buf, size_t size)
627{
628 void *buf_start = buf;
629
630 while (size) {
631 int ret = read(fd, buf, size);
632
633 if (ret <= 0)
634 return ret;
635
636 size -= ret;
637 buf += ret;
638 }
639
640 return buf - buf_start;
641}
642
643#define session_done() (*(volatile int *)(&session_done))
644volatile int session_done;
645
646static int __perf_session__process_pipe_events(struct perf_session *self,
647 struct perf_event_ops *ops)
648{
649 event_t event;
650 uint32_t size;
651 int skip = 0;
652 u64 head;
653 int err;
654 void *p;
655
656 perf_event_ops__fill_defaults(ops);
657
658 head = 0;
659more:
660 err = do_read(self->fd, &event, sizeof(struct perf_event_header));
661 if (err <= 0) {
662 if (err == 0)
663 goto done;
664
665 pr_err("failed to read event header\n");
666 goto out_err;
667 }
668
669 if (self->header.needs_swap)
670 perf_event_header__bswap(&event.header);
671
672 size = event.header.size;
673 if (size == 0)
674 size = 8;
675
676 p = &event;
677 p += sizeof(struct perf_event_header);
678
679 if (size - sizeof(struct perf_event_header)) {
680 err = do_read(self->fd, p,
681 size - sizeof(struct perf_event_header));
682 if (err <= 0) {
683 if (err == 0) {
684 pr_err("unexpected end of event stream\n");
685 goto done;
686 }
687
688 pr_err("failed to read event data\n");
689 goto out_err;
690 }
691 }
692
693 if (size == 0 ||
694 (skip = perf_session__process_event(self, &event, ops,
695 0, head)) < 0) {
696 dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
697 head, event.header.size, event.header.type);
698
699
700
701
702 if (unlikely(head & 7))
703 head &= ~7ULL;
704
705 size = 8;
706 }
707
708 head += size;
709
710 dump_printf("\n%#Lx [%#x]: event: %d\n",
711 head, event.header.size, event.header.type);
712
713 if (skip > 0)
714 head += skip;
715
716 if (!session_done())
717 goto more;
718done:
719 err = 0;
720out_err:
721 return err;
722}
723
724int __perf_session__process_events(struct perf_session *self,
725 u64 data_offset, u64 data_size,
726 u64 file_size, struct perf_event_ops *ops)
727{
728 int err, mmap_prot, mmap_flags;
729 u64 head, shift;
730 u64 offset = 0;
731 size_t page_size;
732 event_t *event;
733 uint32_t size;
734 char *buf;
735 struct ui_progress *progress = ui_progress__new("Processing events...",
736 self->size);
737 if (progress == NULL)
738 return -1;
739
740 perf_event_ops__fill_defaults(ops);
741
742 page_size = sysconf(_SC_PAGESIZE);
743
744 head = data_offset;
745 shift = page_size * (head / page_size);
746 offset += shift;
747 head -= shift;
748
749 mmap_prot = PROT_READ;
750 mmap_flags = MAP_SHARED;
751
752 if (self->header.needs_swap) {
753 mmap_prot |= PROT_WRITE;
754 mmap_flags = MAP_PRIVATE;
755 }
756remap:
757 buf = mmap(NULL, page_size * self->mmap_window, mmap_prot,
758 mmap_flags, self->fd, offset);
759 if (buf == MAP_FAILED) {
760 pr_err("failed to mmap file\n");
761 err = -errno;
762 goto out_err;
763 }
764
765more:
766 event = (event_t *)(buf + head);
767 ui_progress__update(progress, offset);
768
769 if (self->header.needs_swap)
770 perf_event_header__bswap(&event->header);
771 size = event->header.size;
772 if (size == 0)
773 size = 8;
774
775 if (head + event->header.size >= page_size * self->mmap_window) {
776 int munmap_ret;
777
778 shift = page_size * (head / page_size);
779
780 munmap_ret = munmap(buf, page_size * self->mmap_window);
781 assert(munmap_ret == 0);
782
783 offset += shift;
784 head -= shift;
785 goto remap;
786 }
787
788 size = event->header.size;
789
790 dump_printf("\n%#Lx [%#x]: event: %d\n",
791 offset + head, event->header.size, event->header.type);
792
793 if (size == 0 ||
794 perf_session__process_event(self, event, ops, offset, head) < 0) {
795 dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
796 offset + head, event->header.size,
797 event->header.type);
798
799
800
801
802 if (unlikely(head & 7))
803 head &= ~7ULL;
804
805 size = 8;
806 }
807
808 head += size;
809
810 if (offset + head >= data_offset + data_size)
811 goto done;
812
813 if (offset + head < file_size)
814 goto more;
815done:
816 err = 0;
817
818 self->ordered_samples.next_flush = ULLONG_MAX;
819 flush_sample_queue(self, ops);
820out_err:
821 ui_progress__delete(progress);
822 return err;
823}
824
825int perf_session__process_events(struct perf_session *self,
826 struct perf_event_ops *ops)
827{
828 int err;
829
830 if (perf_session__register_idle_thread(self) == NULL)
831 return -ENOMEM;
832
833 if (!symbol_conf.full_paths) {
834 char bf[PATH_MAX];
835
836 if (getcwd(bf, sizeof(bf)) == NULL) {
837 err = -errno;
838out_getcwd_err:
839 pr_err("failed to get the current directory\n");
840 goto out_err;
841 }
842 self->cwd = strdup(bf);
843 if (self->cwd == NULL) {
844 err = -ENOMEM;
845 goto out_getcwd_err;
846 }
847 self->cwdlen = strlen(self->cwd);
848 }
849
850 if (!self->fd_pipe)
851 err = __perf_session__process_events(self,
852 self->header.data_offset,
853 self->header.data_size,
854 self->size, ops);
855 else
856 err = __perf_session__process_pipe_events(self, ops);
857out_err:
858 return err;
859}
860
861bool perf_session__has_traces(struct perf_session *self, const char *msg)
862{
863 if (!(self->sample_type & PERF_SAMPLE_RAW)) {
864 pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
865 return false;
866 }
867
868 return true;
869}
870
871int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
872 const char *symbol_name,
873 u64 addr)
874{
875 char *bracket;
876 enum map_type i;
877 struct ref_reloc_sym *ref;
878
879 ref = zalloc(sizeof(struct ref_reloc_sym));
880 if (ref == NULL)
881 return -ENOMEM;
882
883 ref->name = strdup(symbol_name);
884 if (ref->name == NULL) {
885 free(ref);
886 return -ENOMEM;
887 }
888
889 bracket = strchr(ref->name, ']');
890 if (bracket)
891 *bracket = '\0';
892
893 ref->addr = addr;
894
895 for (i = 0; i < MAP__NR_TYPES; ++i) {
896 struct kmap *kmap = map__kmap(maps[i]);
897 kmap->ref_reloc_sym = ref;
898 }
899
900 return 0;
901}
902
903size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
904{
905 return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) +
906 __dsos__fprintf(&self->host_machine.user_dsos, fp) +
907 machines__fprintf_dsos(&self->machines, fp);
908}
909
910size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
911 bool with_hits)
912{
913 size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
914 return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
915}
916