1603 lines · 41.0 KB · C
RawDownload
#define _FILE_OFFSET_BITS 64 #include <linux/kernel.h> #include <byteswap.h>#include <unistd.h>#include <sys/types.h>#include <sys/mman.h> #include "evlist.h"#include "evsel.h"#include "session.h"#include "tool.h"#include "sort.h"#include "util.h"#include "cpumap.h" static int perf_session__open(struct perf_session *self, bool force){	struct stat input_stat; 	if (!strcmp(self->filename, "-")) {		self->fd_pipe = true;		self->fd = STDIN_FILENO; 		if (perf_session__read_header(self, self->fd) < 0)			pr_err("incompatible file format (rerun with -v to learn more)"); 		return 0;	} 	self->fd = open(self->filename, O_RDONLY);	if (self->fd < 0) {		int err = errno; 		pr_err("failed to open %s: %s", self->filename, strerror(err));		if (err == ENOENT && !strcmp(self->filename, "perf.data"))			pr_err("  (try 'perf record' first)");		pr_err("\n");		return -errno;	} 	if (fstat(self->fd, &input_stat) < 0)		goto out_close; 	if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {		pr_err("file %s not owned by current user or root\n",		       self->filename);		goto out_close;	} 	if (!input_stat.st_size) {		pr_info("zero-sized file (%s), nothing to do!\n",			self->filename);		goto out_close;	} 	if (perf_session__read_header(self, self->fd) < 0) {		pr_err("incompatible file format (rerun with -v to learn more)");		goto out_close;	} 	if (!perf_evlist__valid_sample_type(self->evlist)) {		pr_err("non matching sample_type");		goto out_close;	} 	if (!perf_evlist__valid_sample_id_all(self->evlist)) {		pr_err("non matching sample_id_all");		goto out_close;	} 	self->size = input_stat.st_size;	return 0; out_close:	close(self->fd);	self->fd = -1;	return -1;} void perf_session__update_sample_type(struct perf_session *self){	self->sample_type = perf_evlist__sample_type(self->evlist);	self->sample_size = __perf_evsel__sample_size(self->sample_type);	self->sample_id_all = perf_evlist__sample_id_all(self->evlist);	self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);	self->host_machine.id_hdr_size = self->id_hdr_size;} int perf_session__create_kernel_maps(struct perf_session *self){	int ret = machine__create_kernel_maps(&self->host_machine); 	if (ret >= 0)		ret = machines__create_guest_kernel_maps(&self->machines);	return ret;} static void perf_session__destroy_kernel_maps(struct perf_session *self){	machine__destroy_kernel_maps(&self->host_machine);	machines__destroy_guest_kernel_maps(&self->machines);} struct perf_session *perf_session__new(const char *filename, int mode,				       bool force, bool repipe,				       struct perf_tool *tool){	struct perf_session *self;	struct stat st;	size_t len; 	if (!filename || !strlen(filename)) {		if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))			filename = "-";		else			filename = "perf.data";	} 	len = strlen(filename);	self = zalloc(sizeof(*self) + len); 	if (self == NULL)		goto out; 	memcpy(self->filename, filename, len);	/*	 * 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	self->mmap_window = ULLONG_MAX;#else	self->mmap_window = 32 * 1024 * 1024ULL;#endif	self->machines = RB_ROOT;	self->repipe = repipe;	INIT_LIST_HEAD(&self->ordered_samples.samples);	INIT_LIST_HEAD(&self->ordered_samples.sample_cache);	INIT_LIST_HEAD(&self->ordered_samples.to_free);	machine__init(&self->host_machine, "", HOST_KERNEL_ID);	hists__init(&self->hists); 	if (mode == O_RDONLY) {		if (perf_session__open(self, force) < 0)			goto out_delete;		perf_session__update_sample_type(self);	} else if (mode == O_WRONLY) {		/*		 * In O_RDONLY mode this will be performed when reading the		 * kernel MMAP event, in perf_event__process_mmap().		 */		if (perf_session__create_kernel_maps(self) < 0)			goto out_delete;	} 	if (tool && tool->ordering_requires_timestamps &&	    tool->ordered_samples && !self->sample_id_all) {		dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");		tool->ordered_samples = false;	} out:	return self;out_delete:	perf_session__delete(self);	return NULL;} static void machine__delete_dead_threads(struct machine *machine){	struct thread *n, *t; 	list_for_each_entry_safe(t, n, &machine->dead_threads, node) {		list_del(&t->node);		thread__delete(t);	}} static void perf_session__delete_dead_threads(struct perf_session *session){	machine__delete_dead_threads(&session->host_machine);} static void machine__delete_threads(struct machine *self){	struct rb_node *nd = rb_first(&self->threads); 	while (nd) {		struct thread *t = rb_entry(nd, struct thread, rb_node); 		rb_erase(&t->rb_node, &self->threads);		nd = rb_next(nd);		thread__delete(t);	}} static void perf_session__delete_threads(struct perf_session *session){	machine__delete_threads(&session->host_machine);} void perf_session__delete(struct perf_session *self){	perf_session__destroy_kernel_maps(self);	perf_session__delete_dead_threads(self);	perf_session__delete_threads(self);	machine__exit(&self->host_machine);	close(self->fd);	free(self);} void machine__remove_thread(struct machine *self, struct thread *th){	self->last_match = NULL;	rb_erase(&th->rb_node, &self->threads);	/*	 * We may have references to this thread, for instance in some hist_entry	 * instances, so just move them to a separate list.	 */	list_add_tail(&th->node, &self->dead_threads);} static bool symbol__match_parent_regex(struct symbol *sym){	if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))		return 1; 	return 0;} static const u8 cpumodes[] = {	PERF_RECORD_MISC_USER,	PERF_RECORD_MISC_KERNEL,	PERF_RECORD_MISC_GUEST_USER,	PERF_RECORD_MISC_GUEST_KERNEL};#define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) static void ip__resolve_ams(struct machine *self, struct thread *thread,			    struct addr_map_symbol *ams,			    u64 ip){	struct addr_location al;	size_t i;	u8 m; 	memset(&al, 0, sizeof(al)); 	for (i = 0; i < NCPUMODES; i++) {		m = cpumodes[i];		/*		 * We cannot use the header.misc hint to determine whether a		 * branch stack address is user, kernel, guest, hypervisor.		 * Branches may straddle the kernel/user/hypervisor boundaries.		 * Thus, we have to try consecutively until we find a match		 * or else, the symbol is unknown		 */		thread__find_addr_location(thread, self, m, MAP__FUNCTION,				ip, &al, NULL);		if (al.sym)			goto found;	}found:	ams->addr = ip;	ams->al_addr = al.addr;	ams->sym = al.sym;	ams->map = al.map;} struct branch_info *machine__resolve_bstack(struct machine *self,					    struct thread *thr,					    struct branch_stack *bs){	struct branch_info *bi;	unsigned int i; 	bi = calloc(bs->nr, sizeof(struct branch_info));	if (!bi)		return NULL; 	for (i = 0; i < bs->nr; i++) {		ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to);		ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from);		bi[i].flags = bs->entries[i].flags;	}	return bi;} int machine__resolve_callchain(struct machine *self,			       struct perf_evsel *evsel __used,			       struct thread *thread,			       struct ip_callchain *chain,			       struct symbol **parent){	u8 cpumode = PERF_RECORD_MISC_USER;	unsigned int i;	int err; 	callchain_cursor_reset(&callchain_cursor); 	if (chain->nr > PERF_MAX_STACK_DEPTH) {		pr_warning("corrupted callchain. skipping...\n");		return 0;	} 	for (i = 0; i < chain->nr; i++) {		u64 ip;		struct addr_location al; 		if (callchain_param.order == ORDER_CALLEE)			ip = chain->ips[i];		else			ip = chain->ips[chain->nr - i - 1]; 		if (ip >= PERF_CONTEXT_MAX) {			switch (ip) {			case PERF_CONTEXT_HV:				cpumode = PERF_RECORD_MISC_HYPERVISOR;	break;			case PERF_CONTEXT_KERNEL:				cpumode = PERF_RECORD_MISC_KERNEL;	break;			case PERF_CONTEXT_USER:				cpumode = PERF_RECORD_MISC_USER;	break;			default:				pr_debug("invalid callchain context: "					 "%"PRId64"\n", (s64) ip);				/*				 * It seems the callchain is corrupted.				 * Discard all.				 */				callchain_cursor_reset(&callchain_cursor);				return 0;			}			continue;		} 		al.filtered = false;		thread__find_addr_location(thread, self, cpumode,					   MAP__FUNCTION, ip, &al, NULL);		if (al.sym != NULL) {			if (sort__has_parent && !*parent &&			    symbol__match_parent_regex(al.sym))				*parent = al.sym;			if (!symbol_conf.use_callchain)				break;		} 		err = callchain_cursor_append(&callchain_cursor,					      ip, al.map, al.sym);		if (err)			return err;	} 	return 0;} static int process_event_synth_tracing_data_stub(union perf_event *event __used,						 struct perf_session *session __used){	dump_printf(": unhandled!\n");	return 0;} static int process_event_synth_attr_stub(union perf_event *event __used,					 struct perf_evlist **pevlist __used){	dump_printf(": unhandled!\n");	return 0;} static int process_event_sample_stub(struct perf_tool *tool __used,				     union perf_event *event __used,				     struct perf_sample *sample __used,				     struct perf_evsel *evsel __used,				     struct machine *machine __used){	dump_printf(": unhandled!\n");	return 0;} static int process_event_stub(struct perf_tool *tool __used,			      union perf_event *event __used,			      struct perf_sample *sample __used,			      struct machine *machine __used){	dump_printf(": unhandled!\n");	return 0;} static int process_finished_round_stub(struct perf_tool *tool __used,				       union perf_event *event __used,				       struct perf_session *perf_session __used){	dump_printf(": unhandled!\n");	return 0;} static int process_event_type_stub(struct perf_tool *tool __used,				   union perf_event *event __used){	dump_printf(": unhandled!\n");	return 0;} static int process_finished_round(struct perf_tool *tool,				  union perf_event *event,				  struct perf_session *session); static void perf_tool__fill_defaults(struct perf_tool *tool){	if (tool->sample == NULL)		tool->sample = process_event_sample_stub;	if (tool->mmap == NULL)		tool->mmap = process_event_stub;	if (tool->comm == NULL)		tool->comm = process_event_stub;	if (tool->fork == NULL)		tool->fork = process_event_stub;	if (tool->exit == NULL)		tool->exit = process_event_stub;	if (tool->lost == NULL)		tool->lost = perf_event__process_lost;	if (tool->read == NULL)		tool->read = process_event_sample_stub;	if (tool->throttle == NULL)		tool->throttle = process_event_stub;	if (tool->unthrottle == NULL)		tool->unthrottle = process_event_stub;	if (tool->attr == NULL)		tool->attr = process_event_synth_attr_stub;	if (tool->event_type == NULL)		tool->event_type = process_event_type_stub;	if (tool->tracing_data == NULL)		tool->tracing_data = process_event_synth_tracing_data_stub;	if (tool->build_id == NULL)		tool->build_id = process_finished_round_stub;	if (tool->finished_round == NULL) {		if (tool->ordered_samples)			tool->finished_round = process_finished_round;		else			tool->finished_round = process_finished_round_stub;	}} void mem_bswap_64(void *src, int byte_size){	u64 *m = src; 	while (byte_size > 0) {		*m = bswap_64(*m);		byte_size -= sizeof(u64);		++m;	}} static void swap_sample_id_all(union perf_event *event, void *data){	void *end = (void *) event + event->header.size;	int size = end - data; 	BUG_ON(size % sizeof(u64));	mem_bswap_64(data, size);} static void perf_event__all64_swap(union perf_event *event,				   bool sample_id_all __used){	struct perf_event_header *hdr = &event->header;	mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr));} static void perf_event__comm_swap(union perf_event *event, bool sample_id_all){	event->comm.pid = bswap_32(event->comm.pid);	event->comm.tid = bswap_32(event->comm.tid); 	if (sample_id_all) {		void *data = &event->comm.comm; 		data += ALIGN(strlen(data) + 1, sizeof(u64));		swap_sample_id_all(event, data);	}} static void perf_event__mmap_swap(union perf_event *event,				  bool sample_id_all){	event->mmap.pid	  = bswap_32(event->mmap.pid);	event->mmap.tid	  = bswap_32(event->mmap.tid);	event->mmap.start = bswap_64(event->mmap.start);	event->mmap.len	  = bswap_64(event->mmap.len);	event->mmap.pgoff = bswap_64(event->mmap.pgoff); 	if (sample_id_all) {		void *data = &event->mmap.filename; 		data += ALIGN(strlen(data) + 1, sizeof(u64));		swap_sample_id_all(event, data);	}

Lines 1–500 of 1603.