#include "data_map.h" #include "symbol.h" #include "util.h" #include "debug.h" static struct perf_file_handler *curr_handler; static unsigned long mmap_window = 32; static char __cwd[PATH_MAX]; static int process_event_stub(event_t *event __used, unsigned long offset __used, unsigned long head __used) { return 0; } void register_perf_file_handler(struct perf_file_handler *handler) { if (!handler->process_sample_event) handler->process_sample_event = process_event_stub; if (!handler->process_mmap_event) handler->process_mmap_event = process_event_stub; if (!handler->process_comm_event) handler->process_comm_event = process_event_stub; if (!handler->process_fork_event) handler->process_fork_event = process_event_stub; if (!handler->process_exit_event) handler->process_exit_event = process_event_stub; if (!handler->process_lost_event) handler->process_lost_event = process_event_stub; if (!handler->process_read_event) handler->process_read_event = process_event_stub; if (!handler->process_throttle_event) handler->process_throttle_event = process_event_stub; if (!handler->process_unthrottle_event) handler->process_unthrottle_event = process_event_stub; curr_handler = handler; } static int process_event(event_t *event, unsigned long offset, unsigned long head) { trace_event(event); switch (event->header.type) { case PERF_RECORD_SAMPLE: return curr_handler->process_sample_event(event, offset, head); case PERF_RECORD_MMAP: return curr_handler->process_mmap_event(event, offset, head); case PERF_RECORD_COMM: return curr_handler->process_comm_event(event, offset, head); case PERF_RECORD_FORK: return curr_handler->process_fork_event(event, offset, head); case PERF_RECORD_EXIT: return curr_handler->process_exit_event(event, offset, head); case PERF_RECORD_LOST: return curr_handler->process_lost_event(event, offset, head); case PERF_RECORD_READ: return curr_handler->process_read_event(event, offset, head); case PERF_RECORD_THROTTLE: return curr_handler->process_throttle_event(event, offset, head); case PERF_RECORD_UNTHROTTLE: return curr_handler->process_unthrottle_event(event, offset, head); default: curr_handler->total_unknown++; return -1; } } int perf_header__read_build_ids(int input, off_t offset, off_t size) { struct build_id_event bev; char filename[PATH_MAX]; off_t limit = offset + size; int err = -1; while (offset < limit) { struct dso *dso; ssize_t len; if (read(input, &bev, sizeof(bev)) != sizeof(bev)) goto out; len = bev.header.size - sizeof(bev); if (read(input, filename, len) != len) goto out; dso = dsos__findnew(filename); if (dso != NULL) dso__set_build_id(dso, &bev.build_id); offset += bev.header.size; } err = 0; out: return err; } int mmap_dispatch_perf_file(struct perf_header **pheader, const char *input_name, int force, int full_paths, int *cwdlen, char **cwd) { int err; struct perf_header *header; unsigned long head, shift; unsigned long offset = 0; struct stat input_stat; size_t page_size; u64 sample_type; event_t *event; uint32_t size; int input; char *buf; if (curr_handler == NULL) { pr_debug("Forgot to register perf file handler\n"); return -EINVAL; } page_size = getpagesize(); input = open(input_name, O_RDONLY); if (input < 0) { pr_err("Failed to open file: %s", input_name); if (!strcmp(input_name, "perf.data")) pr_err(" (try 'perf record' first)"); pr_err("\n"); return -errno; } if (fstat(input, &input_stat) < 0) { pr_err("failed to stat file"); err = -errno; goto out_close; } err = -EACCES; if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { pr_err("file: %s not owned by current user or root\n", input_name); goto out_close; } if (input_stat.st_size == 0) { pr_info("zero-sized file, nothing to do!\n"); goto done; } err = -ENOMEM; header = perf_header__new(); if (header == NULL) goto out_close; err = perf_header__read(header, input); if (err < 0) goto out_delete; *pheader = header; head = header->data_offset; sample_type = perf_header__sample_type(header); err = -EINVAL; if (curr_handler->sample_type_check && curr_handler->sample_type_check(sample_type) < 0) goto out_delete; err = -ENOMEM; if (kernel_maps__init(true) < 0) { pr_err("failed to setup the kernel maps to resolve symbols\n"); goto out_delete; } if (!full_paths) { if (getcwd(__cwd, sizeof(__cwd)) == NULL) { pr_err("failed to get the current directory\n"); err = -errno; goto out_delete; } *cwd = __cwd; *cwdlen = strlen(*cwd); } else { *cwd = NULL; *cwdlen = 0; } shift = page_size * (head / page_size); offset += shift; head -= shift; remap: buf = mmap(NULL, page_size * mmap_window, PROT_READ, MAP_SHARED, input, offset); if (buf == MAP_FAILED) { pr_err("failed to mmap file\n"); err = -errno; goto out_delete; } more: event = (event_t *)(buf + head); size = event->header.size; if (!size) size = 8; if (head + event->header.size >= page_size * mmap_window) { int munmap_ret; shift = page_size * (head / page_size); munmap_ret = munmap(buf, page_size * mmap_window); assert(munmap_ret == 0); offset += shift; head -= shift; goto remap; } size = event->header.size; dump_printf("\n%p [%p]: event: %d\n", (void *)(offset + head), (void *)(long)event->header.size, event->header.type); if (!size || process_event(event, offset, head) < 0) { dump_printf("%p [%p]: skipping unknown header type: %d\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.type); /* * assume we lost track of the stream, check alignment, and * increment a single u64 in the hope to catch on again 'soon'. */ if (unlikely(head & 7)) head &= ~7ULL; size = 8; } head += size; if (offset + head >= header->data_offset + header->data_size) goto done; if (offset + head < (unsigned long)input_stat.st_size) goto more; done: err = 0; out_close: close(input); return err; out_delete: perf_header__delete(header); goto out_close; }