session.c 3.1 KB
Newer Older
1 2 3 4 5 6
#include <linux/kernel.h>

#include <unistd.h>
#include <sys/types.h>

#include "session.h"
7
#include "sort.h"
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
#include "util.h"

static int perf_session__open(struct perf_session *self, bool force)
{
	struct stat input_stat;

	self->fd = open(self->filename, O_RDONLY);
	if (self->fd < 0) {
		pr_err("failed to open file: %s", self->filename);
		if (!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_header__read(&self->header, self->fd) < 0) {
		pr_err("incompatible file format");
		goto out_close;
	}

	self->size = input_stat.st_size;
	return 0;

out_close:
	close(self->fd);
	self->fd = -1;
	return -1;
}

52
struct perf_session *perf_session__new(const char *filename, int mode, bool force)
53
{
54
	size_t len = filename ? strlen(filename) + 1 : 0;
55 56 57 58 59 60
	struct perf_session *self = zalloc(sizeof(*self) + len);

	if (self == NULL)
		goto out;

	if (perf_header__init(&self->header) < 0)
61
		goto out_free;
62 63

	memcpy(self->filename, filename, len);
64 65
	self->threads = RB_ROOT;
	self->last_match = NULL;
66 67 68
	self->mmap_window = 32;
	self->cwd = NULL;
	self->cwdlen = 0;
69
	map_groups__init(&self->kmaps);
70

71
	if (perf_session__create_kernel_maps(self) < 0)
72 73 74 75
		goto out_delete;

	if (mode == O_RDONLY && perf_session__open(self, force) < 0)
		goto out_delete;
76 77
out:
	return self;
78
out_free:
79 80
	free(self);
	return NULL;
81 82 83
out_delete:
	perf_session__delete(self);
	return NULL;
84 85 86 87 88 89
}

void perf_session__delete(struct perf_session *self)
{
	perf_header__exit(&self->header);
	close(self->fd);
90
	free(self->cwd);
91 92
	free(self);
}
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

static bool symbol__match_parent_regex(struct symbol *sym)
{
	if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
		return 1;

	return 0;
}

struct symbol **perf_session__resolve_callchain(struct perf_session *self,
						struct thread *thread,
						struct ip_callchain *chain,
						struct symbol **parent)
{
	u8 cpumode = PERF_RECORD_MISC_USER;
	struct symbol **syms = NULL;
	unsigned int i;

	if (self->use_callchain) {
		syms = calloc(chain->nr, sizeof(*syms));
		if (!syms) {
			fprintf(stderr, "Can't allocate memory for symbols\n");
			exit(-1);
		}
	}

	for (i = 0; i < chain->nr; i++) {
		u64 ip = chain->ips[i];
		struct addr_location al;

		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:
				break;
			}
			continue;
		}

		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 (!self->use_callchain)
				break;
			syms[i] = al.sym;
		}
	}

	return syms;
}