Skip to content
Snippets Groups Projects
session.c 41.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
    
    					    struct perf_tool *tool, u64 file_offset)
    
    	dump_event(session, event, file_offset, NULL);
    
    	/* These events are processed right away */
    
    	case PERF_RECORD_HEADER_ATTR:
    
    		err = tool->attr(tool, event, &session->evlist);
    
    			perf_session__set_id_hdr_size(session);
    
    	case PERF_RECORD_HEADER_TRACING_DATA:
    		/* setup for reading amidst mmap */
    
    		lseek(session->fd, file_offset, SEEK_SET);
    
    		return tool->tracing_data(tool, event, session);
    
    	case PERF_RECORD_HEADER_BUILD_ID:
    
    		return tool->build_id(tool, event, session);
    
    	case PERF_RECORD_FINISHED_ROUND:
    
    		return tool->finished_round(tool, event, session);
    
    static void event_swap(union perf_event *event, bool sample_id_all)
    {
    	perf_event__swap_op swap;
    
    	swap = perf_event__swap_ops[event->header.type];
    	if (swap)
    		swap(event, sample_id_all);
    }
    
    
    static int perf_session__process_event(struct perf_session *session,
    				       union perf_event *event,
    				       struct perf_tool *tool,
    				       u64 file_offset)
    
    	if (session->header.needs_swap)
    
    		event_swap(event, perf_evlist__sample_id_all(session->evlist));
    
    
    	if (event->header.type >= PERF_RECORD_HEADER_MAX)
    		return -EINVAL;
    
    
    	events_stats__inc(&session->stats, event->header.type);
    
    
    	if (event->header.type >= PERF_RECORD_USER_TYPE_START)
    
    		return perf_session__process_user_event(session, event, tool, file_offset);
    
    	/*
    	 * For all kernel events we get the sample data
    	 */
    
    	ret = perf_evlist__parse_sample(session->evlist, event, &sample);
    
    	if (tool->ordered_samples) {
    
    		ret = perf_session_queue_event(session, event, &sample,
    					       file_offset);
    
    	return perf_session_deliver_event(session, event, &sample, tool,
    
    void perf_event_header__bswap(struct perf_event_header *self)
    {
    	self->type = bswap_32(self->type);
    	self->misc = bswap_16(self->misc);
    	self->size = bswap_16(self->size);
    }
    
    
    struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
    {
    
    	return machine__findnew_thread(&session->machines.host, 0, pid);
    
    static struct thread *perf_session__register_idle_thread(struct perf_session *self)
    {
    	struct thread *thread = perf_session__findnew(self, 0);
    
    	if (thread == NULL || thread__set_comm(thread, "swapper")) {
    		pr_err("problem inserting idle task.\n");
    		thread = NULL;
    	}
    
    	return thread;
    }
    
    
    static void perf_session__warn_about_errors(const struct perf_session *session,
    
    					    const struct perf_tool *tool)
    
    	if (tool->lost == perf_event__process_lost &&
    
    	    session->stats.nr_events[PERF_RECORD_LOST] != 0) {
    
    		ui__warning("Processed %d events and lost %d chunks!\n\n"
    			    "Check IO/CPU overload!\n\n",
    
    			    session->stats.nr_events[0],
    			    session->stats.nr_events[PERF_RECORD_LOST]);
    
    	if (session->stats.nr_unknown_events != 0) {
    
    		ui__warning("Found %u unknown events!\n\n"
    			    "Is this an older tool processing a perf.data "
    			    "file generated by a more recent tool?\n\n"
    			    "If that is not the case, consider "
    			    "reporting to linux-kernel@vger.kernel.org.\n\n",
    
    	if (session->stats.nr_unknown_id != 0) {
    
    		ui__warning("%u samples with id not present in the header\n",
    
     	if (session->stats.nr_invalid_chains != 0) {
    
     		ui__warning("Found invalid callchains!\n\n"
     			    "%u out of %u events were discarded for this reason.\n\n"
     			    "Consider reporting to linux-kernel@vger.kernel.org.\n\n",
    
     			    session->stats.nr_invalid_chains,
     			    session->stats.nr_events[PERF_RECORD_SAMPLE]);
    
    	if (session->stats.nr_unprocessable_samples != 0) {
    
    		ui__warning("%u unprocessable samples recorded.\n"
    			    "Do you have a KVM guest running and not using 'perf kvm'?\n",
    
    			    session->stats.nr_unprocessable_samples);
    
    #define session_done()	(*(volatile int *)(&session_done))
    volatile int session_done;
    
    static int __perf_session__process_pipe_events(struct perf_session *self,
    
    	union perf_event *event;
    	uint32_t size, cur_size = 0;
    	void *buf = NULL;
    
    	perf_tool__fill_defaults(tool);
    
    	cur_size = sizeof(union perf_event);
    
    	buf = malloc(cur_size);
    	if (!buf)
    		return -errno;
    
    	event = buf;
    	err = readn(self->fd, event, sizeof(struct perf_event_header));
    
    	if (err <= 0) {
    		if (err == 0)
    			goto done;
    
    		pr_err("failed to read event header\n");
    		goto out_err;
    	}
    
    	if (self->header.needs_swap)
    
    		perf_event_header__bswap(&event->header);
    
    	size = event->header.size;
    
    	if (size < sizeof(struct perf_event_header)) {
    		pr_err("bad event header size\n");
    		goto out_err;
    	}
    
    	if (size > cur_size) {
    		void *new = realloc(buf, size);
    		if (!new) {
    			pr_err("failed to allocate memory to read event\n");
    			goto out_err;
    		}
    		buf = new;
    		cur_size = size;
    		event = buf;
    	}
    	p = event;
    
    	p += sizeof(struct perf_event_header);
    
    
    	if (size - sizeof(struct perf_event_header)) {
    
    		err = readn(self->fd, p, size - sizeof(struct perf_event_header));
    
    		if (err <= 0) {
    			if (err == 0) {
    				pr_err("unexpected end of event stream\n");
    				goto done;
    			}
    
    			pr_err("failed to read event data\n");
    			goto out_err;
    		}
    
    	if ((skip = perf_session__process_event(self, event, tool, head)) < 0) {
    
    		pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
    
    		       head, event->header.size, event->header.type);
    
    		err = -EINVAL;
    		goto out_err;
    
    	}
    
    	head += size;
    
    	if (skip > 0)
    		head += skip;
    
    	if (!session_done())
    		goto more;
    done:
    	err = 0;
    out_err:
    
    	perf_session__warn_about_errors(self, tool);
    
    	perf_session_free_sample_buffers(self);
    
    static union perf_event *
    fetch_mmaped_event(struct perf_session *session,
    		   u64 head, size_t mmap_size, char *buf)
    {
    	union perf_event *event;
    
    	/*
    	 * Ensure we have enough space remaining to read
    	 * the size of the event in the headers.
    	 */
    	if (head + sizeof(event->header) > mmap_size)
    		return NULL;
    
    	event = (union perf_event *)(buf + head);
    
    	if (session->header.needs_swap)
    		perf_event_header__bswap(&event->header);
    
    
    	if (head + event->header.size > mmap_size) {
    		/* We're not fetching the event so swap back again */
    		if (session->header.needs_swap)
    			perf_event_header__bswap(&event->header);
    
    /*
     * On 64bit we can mmap the data file in one go. No need for tiny mmap
     * slices. On 32bit we use 32MB.
     */
    #if BITS_PER_LONG == 64
    #define MMAP_SIZE ULLONG_MAX
    #define NUM_MMAPS 1
    #else
    #define MMAP_SIZE (32 * 1024 * 1024ULL)
    #define NUM_MMAPS 128
    #endif
    
    
    int __perf_session__process_events(struct perf_session *session,
    
    				   u64 file_size, struct perf_tool *tool)
    
    	u64 head, page_offset, file_offset, file_pos, progress_next;
    
    	int err, mmap_prot, mmap_flags, map_idx = 0;
    
    	char *buf, *mmaps[NUM_MMAPS];
    
    	perf_tool__fill_defaults(tool);
    
    	page_offset = page_size * (data_offset / page_size);
    	file_offset = page_offset;
    	head = data_offset - page_offset;
    
    	if (data_offset + data_size < file_size)
    		file_size = data_offset + data_size;
    
    
    	progress_next = file_size / 16;
    
    
    	mmap_size = MMAP_SIZE;
    
    	if (mmap_size > file_size)
    		mmap_size = file_size;
    
    
    	memset(mmaps, 0, sizeof(mmaps));
    
    
    	mmap_prot  = PROT_READ;
    	mmap_flags = MAP_SHARED;
    
    
    	if (session->header.needs_swap) {
    
    		mmap_prot  |= PROT_WRITE;
    		mmap_flags = MAP_PRIVATE;
    	}
    
    	buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd,
    		   file_offset);
    
    	if (buf == MAP_FAILED) {
    		pr_err("failed to mmap file\n");
    		err = -errno;
    		goto out_err;
    	}
    
    	mmaps[map_idx] = buf;
    	map_idx = (map_idx + 1) & (ARRAY_SIZE(mmaps) - 1);
    
    	file_pos = file_offset + head;
    
    	event = fetch_mmaped_event(session, head, mmap_size, buf);
    	if (!event) {
    
    		if (mmaps[map_idx]) {
    			munmap(mmaps[map_idx], mmap_size);
    			mmaps[map_idx] = NULL;
    		}
    
    		page_offset = page_size * (head / page_size);
    		file_offset += page_offset;
    		head -= page_offset;
    
    	if (size < sizeof(struct perf_event_header) ||
    
    	    perf_session__process_event(session, event, tool, file_pos) < 0) {
    
    		pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
    		       file_offset + head, event->header.size,
    		       event->header.type);
    		err = -EINVAL;
    		goto out_err;
    
    	file_pos += size;
    
    	if (file_pos >= progress_next) {
    		progress_next += file_size / 16;
    
    		ui_progress__update(file_pos, file_size,
    				    "Processing events...");
    
    	if (file_pos < file_size)
    
    	/* do the final flush for ordered samples */
    
    	session->ordered_samples.next_flush = ULLONG_MAX;
    
    	err = flush_sample_queue(session, tool);
    
    	ui_progress__finish();
    
    	perf_session__warn_about_errors(session, tool);
    
    	perf_session_free_sample_buffers(session);
    
    int perf_session__process_events(struct perf_session *self,
    
    {
    	int err;
    
    	if (perf_session__register_idle_thread(self) == NULL)
    		return -ENOMEM;
    
    
    	if (!self->fd_pipe)
    		err = __perf_session__process_events(self,
    						     self->header.data_offset,
    						     self->header.data_size,
    
    		err = __perf_session__process_pipe_events(self, tool);
    
    bool perf_session__has_traces(struct perf_session *session, const char *msg)
    
    	struct perf_evsel *evsel;
    
    	list_for_each_entry(evsel, &session->evlist->entries, node) {
    		if (evsel->attr.type == PERF_TYPE_TRACEPOINT)
    			return true;
    
    	pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
    	return false;
    
    int maps__set_kallsyms_ref_reloc_sym(struct map **maps,
    				     const char *symbol_name, u64 addr)
    
    	struct ref_reloc_sym *ref;
    
    	ref = zalloc(sizeof(struct ref_reloc_sym));
    	if (ref == NULL)
    		return -ENOMEM;
    
    	ref->name = strdup(symbol_name);
    	if (ref->name == NULL) {
    		free(ref);
    
    	bracket = strchr(ref->name, ']');
    
    
    	for (i = 0; i < MAP__NR_TYPES; ++i) {
    
    		struct kmap *kmap = map__kmap(maps[i]);
    		kmap->ref_reloc_sym = ref;
    
    
    size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
    {
    
    	return machines__fprintf_dsos(&self->machines, fp);
    
    
    size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
    
    					  bool (skip)(struct dso *dso, int parm), int parm)
    
    	return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm);
    
    
    size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
    {
    	struct perf_evsel *pos;
    	size_t ret = fprintf(fp, "Aggregated stats:\n");
    
    
    	ret += events_stats__fprintf(&session->stats, fp);
    
    
    	list_for_each_entry(pos, &session->evlist->entries, node) {
    
    		ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
    
    		ret += events_stats__fprintf(&pos->hists.stats, fp);
    
    size_t perf_session__fprintf(struct perf_session *session, FILE *fp)
    {
    	/*
    	 * FIXME: Here we have to actually print all the machines in this
    	 * session, not just the host...
    	 */
    
    	return machine__fprintf(&session->machines.host, fp);
    
    struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
    					      unsigned int type)
    {
    	struct perf_evsel *pos;
    
    	list_for_each_entry(pos, &session->evlist->entries, node) {
    		if (pos->attr.type == type)
    			return pos;
    	}
    	return NULL;
    }
    
    
    void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
    			  struct perf_sample *sample, struct machine *machine,
    
    			  unsigned int print_opts, unsigned int stack_depth)
    
    {
    	struct addr_location al;
    	struct callchain_cursor_node *node;
    
    	int print_ip = print_opts & PRINT_IP_OPT_IP;
    	int print_sym = print_opts & PRINT_IP_OPT_SYM;
    	int print_dso = print_opts & PRINT_IP_OPT_DSO;
    	int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET;
    
    	int print_oneline = print_opts & PRINT_IP_OPT_ONELINE;
    	char s = print_oneline ? ' ' : '\t';
    
    	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
    
    		error("problem processing %d event, skipping it.\n",
    			event->header.type);
    		return;
    	}
    
    	if (symbol_conf.use_callchain && sample->callchain) {
    
    
    		if (machine__resolve_callchain(machine, evsel, al.thread,
    
    			if (verbose)
    				error("Failed to resolve callchain. Skipping\n");
    			return;
    		}
    
    		callchain_cursor_commit(&callchain_cursor);
    
    			node = callchain_cursor_current(&callchain_cursor);
    
    				printf("%c%16" PRIx64, s, node->ip);
    
    				if (print_symoffset) {
    					al.addr = node->ip;
    
    					symbol__fprintf_symname_offs(node->sym, &al, stdout);
    				} else
    					symbol__fprintf_symname(node->sym, stdout);
    
    				map__fprintf_dsoname(node->map, stdout);
    
    			callchain_cursor_advance(&callchain_cursor);
    
    		if (print_ip)
    			printf("%16" PRIx64, sample->ip);
    
    
    			if (print_symoffset)
    				symbol__fprintf_symname_offs(al.sym, &al,
    							     stdout);
    			else
    				symbol__fprintf_symname(al.sym, stdout);
    
    			printf(" (");
    			map__fprintf_dsoname(al.map, stdout);
    			printf(")");
    
    
    int perf_session__cpu_bitmap(struct perf_session *session,
    			     const char *cpu_list, unsigned long *cpu_bitmap)
    {
    	int i;
    	struct cpu_map *map;
    
    	for (i = 0; i < PERF_TYPE_MAX; ++i) {
    		struct perf_evsel *evsel;
    
    		evsel = perf_session__find_first_evtype(session, i);
    		if (!evsel)
    			continue;
    
    		if (!(evsel->attr.sample_type & PERF_SAMPLE_CPU)) {
    			pr_err("File does not contain CPU events. "
    			       "Remove -c option to proceed.\n");
    			return -1;
    		}
    	}
    
    	map = cpu_map__new(cpu_list);
    
    	if (map == NULL) {
    		pr_err("Invalid cpu_list\n");
    		return -1;
    	}
    
    
    	for (i = 0; i < map->nr; i++) {
    		int cpu = map->map[i];
    
    		if (cpu >= MAX_NR_CPUS) {
    			pr_err("Requested CPU %d too large. "
    			       "Consider raising MAX_NR_CPUS\n", cpu);
    			return -1;
    		}
    
    		set_bit(cpu, cpu_bitmap);
    	}
    
    	return 0;
    }
    
    
    void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
    				bool full)
    {
    	struct stat st;
    	int ret;
    
    	if (session == NULL || fp == NULL)
    		return;
    
    	ret = fstat(session->fd, &st);
    	if (ret == -1)
    		return;
    
    	fprintf(fp, "# ========\n");
    	fprintf(fp, "# captured on: %s", ctime(&st.st_ctime));
    	perf_header__fprintf_info(session, fp, full);
    	fprintf(fp, "# ========\n#\n");
    }
    
    
    
    int __perf_session__set_tracepoints_handlers(struct perf_session *session,
    					     const struct perf_evsel_str_handler *assocs,
    					     size_t nr_assocs)
    {
    	struct perf_evsel *evsel;
    	size_t i;
    	int err;
    
    	for (i = 0; i < nr_assocs; i++) {
    
    		/*
    		 * Adding a handler for an event not in the session,
    		 * just ignore it.
    		 */
    		evsel = perf_evlist__find_tracepoint_by_name(session->evlist, assocs[i].name);
    
    
    		err = -EEXIST;
    		if (evsel->handler.func != NULL)
    
    		evsel->handler.func = assocs[i].handler;
    	}
    
    	err = 0;
    out:
    	return err;
    }