Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ba96195 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge tag 'perf-core-for-mingo' of...

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf

 into perf/core

Pull perf/core improvements and fixes from Jiri Olsa:

  * Honor user freq/interval properly in record command (Namhyung Kim)

  * Speedup DWARF unwind (Jiri Olsa)

Signed-off-by: default avatarJiri Olsa <jolsa@kernel.org>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 7184062b 45dc1bb5
Loading
Loading
Loading
Loading
+40 −2
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@
 *
 * Builtin regression testing command: ever growing number of sanity tests
 */
#include <unistd.h>
#include <string.h>
#include "builtin.h"
#include "intlist.h"
#include "tests.h"
@@ -50,9 +52,17 @@ static struct test {
		.func = test__pmu,
	},
	{
		.desc = "Test dso data interface",
		.desc = "Test dso data read",
		.func = test__dso_data,
	},
	{
		.desc = "Test dso data cache",
		.func = test__dso_data_cache,
	},
	{
		.desc = "Test dso data reopen",
		.func = test__dso_data_reopen,
	},
	{
		.desc = "roundtrip evsel->name check",
		.func = test__perf_evsel__roundtrip_name_test,
@@ -172,6 +182,34 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
	return false;
}

static int run_test(struct test *test)
{
	int status, err = -1, child = fork();

	if (child < 0) {
		pr_err("failed to fork test: %s\n", strerror(errno));
		return -1;
	}

	if (!child) {
		pr_debug("test child forked, pid %d\n", getpid());
		err = test->func();
		exit(err);
	}

	wait(&status);

	if (WIFEXITED(status)) {
		err = WEXITSTATUS(status);
		pr_debug("test child finished with %d\n", err);
	} else if (WIFSIGNALED(status)) {
		err = -1;
		pr_debug("test child interrupted\n");
	}

	return err;
}

static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
{
	int i = 0;
@@ -200,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
		}

		pr_debug("\n--- start ---\n");
		err = tests[curr].func();
		err = run_test(&tests[curr]);
		pr_debug("---- end ----\n%s:", tests[curr].desc);

		switch (err) {
+210 −4
Original line number Diff line number Diff line
#include "util.h"

#include <stdlib.h>
#include <linux/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#include <sys/time.h>
#include <sys/resource.h>
#include <api/fs/fs.h>
#include "util.h"
#include "machine.h"
#include "symbol.h"
#include "tests.h"

static char *test_file(int size)
{
	static char buf_templ[] = "/tmp/test-XXXXXX";
#define TEMPL "/tmp/perf-test-XXXXXX"
	static char buf_templ[sizeof(TEMPL)];
	char *templ = buf_templ;
	int fd, i;
	unsigned char *buf;

	strcpy(buf_templ, TEMPL);
#undef TEMPL

	fd = mkstemp(templ);
	if (fd < 0) {
		perror("mkstemp failed");
@@ -150,3 +155,204 @@ int test__dso_data(void)
	unlink(file);
	return 0;
}

static long open_files_cnt(void)
{
	char path[PATH_MAX];
	struct dirent *dent;
	DIR *dir;
	long nr = 0;

	scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint());
	pr_debug("fd path: %s\n", path);

	dir = opendir(path);
	TEST_ASSERT_VAL("failed to open fd directory", dir);

	while ((dent = readdir(dir)) != NULL) {
		if (!strcmp(dent->d_name, ".") ||
		    !strcmp(dent->d_name, ".."))
			continue;

		nr++;
	}

	closedir(dir);
	return nr - 1;
}

static struct dso **dsos;

static int dsos__create(int cnt, int size)
{
	int i;

	dsos = malloc(sizeof(dsos) * cnt);
	TEST_ASSERT_VAL("failed to alloc dsos array", dsos);

	for (i = 0; i < cnt; i++) {
		char *file;

		file = test_file(size);
		TEST_ASSERT_VAL("failed to get dso file", file);

		dsos[i] = dso__new(file);
		TEST_ASSERT_VAL("failed to get dso", dsos[i]);
	}

	return 0;
}

static void dsos__delete(int cnt)
{
	int i;

	for (i = 0; i < cnt; i++) {
		struct dso *dso = dsos[i];

		unlink(dso->name);
		dso__delete(dso);
	}

	free(dsos);
}

static int set_fd_limit(int n)
{
	struct rlimit rlim;

	if (getrlimit(RLIMIT_NOFILE, &rlim))
		return -1;

	pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n);

	rlim.rlim_cur = n;
	return setrlimit(RLIMIT_NOFILE, &rlim);
}

int test__dso_data_cache(void)
{
	struct machine machine;
	long nr_end, nr = open_files_cnt();
	int dso_cnt, limit, i, fd;

	memset(&machine, 0, sizeof(machine));

	/* set as system limit */
	limit = nr * 4;
	TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit));

	/* and this is now our dso open FDs limit + 1 extra */
	dso_cnt = limit / 2 + 1;
	TEST_ASSERT_VAL("failed to create dsos\n",
		!dsos__create(dso_cnt, TEST_FILE_SIZE));

	for (i = 0; i < (dso_cnt - 1); i++) {
		struct dso *dso = dsos[i];

		/*
		 * Open dsos via dso__data_fd or dso__data_read_offset.
		 * Both opens the data file and keep it open.
		 */
		if (i % 2) {
			fd = dso__data_fd(dso, &machine);
			TEST_ASSERT_VAL("failed to get fd", fd > 0);
		} else {
			#define BUFSIZE 10
			u8 buf[BUFSIZE];
			ssize_t n;

			n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE);
			TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE);
		}
	}

	/* open +1 dso over the allowed limit */
	fd = dso__data_fd(dsos[i], &machine);
	TEST_ASSERT_VAL("failed to get fd", fd > 0);

	/* should force the first one to be closed */
	TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1);

	/* cleanup everything */
	dsos__delete(dso_cnt);

	/* Make sure we did not leak any file descriptor. */
	nr_end = open_files_cnt();
	pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
	TEST_ASSERT_VAL("failed leadking files", nr == nr_end);
	return 0;
}

int test__dso_data_reopen(void)
{
	struct machine machine;
	long nr_end, nr = open_files_cnt();
	int fd, fd_extra;

#define dso_0 (dsos[0])
#define dso_1 (dsos[1])
#define dso_2 (dsos[2])

	memset(&machine, 0, sizeof(machine));

	/*
	 * Test scenario:
	 * - create 3 dso objects
	 * - set process file descriptor limit to current
	 *   files count + 3
	 * - test that the first dso gets closed when we
	 *   reach the files count limit
	 */

	/* Make sure we are able to open 3 fds anyway */
	TEST_ASSERT_VAL("failed to set file limit",
			!set_fd_limit((nr + 3)));

	TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE));

	/* open dso_0 */
	fd = dso__data_fd(dso_0, &machine);
	TEST_ASSERT_VAL("failed to get fd", fd > 0);

	/* open dso_1 */
	fd = dso__data_fd(dso_1, &machine);
	TEST_ASSERT_VAL("failed to get fd", fd > 0);

	/*
	 * open extra file descriptor and we just
	 * reached the files count limit
	 */
	fd_extra = open("/dev/null", O_RDONLY);
	TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0);

	/* open dso_2 */
	fd = dso__data_fd(dso_2, &machine);
	TEST_ASSERT_VAL("failed to get fd", fd > 0);

	/*
	 * dso_0 should get closed, because we reached
	 * the file descriptor limit
	 */
	TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1);

	/* open dso_0 */
	fd = dso__data_fd(dso_0, &machine);
	TEST_ASSERT_VAL("failed to get fd", fd > 0);

	/*
	 * dso_1 should get closed, because we reached
	 * the file descriptor limit
	 */
	TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1);

	/* cleanup everything */
	close(fd_extra);
	dsos__delete(3);

	/* Make sure we did not leak any file descriptor. */
	nr_end = open_files_cnt();
	pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
	TEST_ASSERT_VAL("failed leadking files", nr == nr_end);
	return 0;
}
+2 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void);
int test__pmu(void);
int test__attr(void);
int test__dso_data(void);
int test__dso_data_cache(void);
int test__dso_data_reopen(void);
int test__parse_events(void);
int test__hists_link(void);
int test__python_use(void);
+254 −25
Original line number Diff line number Diff line
#include <asm/bug.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "symbol.h"
#include "dso.h"
#include "machine.h"
@@ -136,7 +139,48 @@ int dso__read_binary_type_filename(const struct dso *dso,
	return ret;
}

static int open_dso(struct dso *dso, struct machine *machine)
/*
 * Global list of open DSOs and the counter.
 */
static LIST_HEAD(dso__data_open);
static long dso__data_open_cnt;

static void dso__list_add(struct dso *dso)
{
	list_add_tail(&dso->data.open_entry, &dso__data_open);
	dso__data_open_cnt++;
}

static void dso__list_del(struct dso *dso)
{
	list_del(&dso->data.open_entry);
	WARN_ONCE(dso__data_open_cnt <= 0,
		  "DSO data fd counter out of bounds.");
	dso__data_open_cnt--;
}

static void close_first_dso(void);

static int do_open(char *name)
{
	int fd;

	do {
		fd = open(name, O_RDONLY);
		if (fd >= 0)
			return fd;

		pr_debug("dso open failed, mmap: %s\n", strerror(errno));
		if (!dso__data_open_cnt || errno != EMFILE)
			break;

		close_first_dso();
	} while (1);

	return -1;
}

static int __open_dso(struct dso *dso, struct machine *machine)
{
	int fd;
	char *root_dir = (char *)"";
@@ -154,11 +198,130 @@ static int open_dso(struct dso *dso, struct machine *machine)
		return -EINVAL;
	}

	fd = open(name, O_RDONLY);
	fd = do_open(name);
	free(name);
	return fd;
}

static void check_data_close(void);

/**
 * dso_close - Open DSO data file
 * @dso: dso object
 *
 * Open @dso's data file descriptor and updates
 * list/count of open DSO objects.
 */
static int open_dso(struct dso *dso, struct machine *machine)
{
	int fd = __open_dso(dso, machine);

	if (fd > 0) {
		dso__list_add(dso);
		/*
		 * Check if we crossed the allowed number
		 * of opened DSOs and close one if needed.
		 */
		check_data_close();
	}

	return fd;
}

static void close_data_fd(struct dso *dso)
{
	if (dso->data.fd >= 0) {
		close(dso->data.fd);
		dso->data.fd = -1;
		dso->data.file_size = 0;
		dso__list_del(dso);
	}
}

/**
 * dso_close - Close DSO data file
 * @dso: dso object
 *
 * Close @dso's data file descriptor and updates
 * list/count of open DSO objects.
 */
static void close_dso(struct dso *dso)
{
	close_data_fd(dso);
}

static void close_first_dso(void)
{
	struct dso *dso;

	dso = list_first_entry(&dso__data_open, struct dso, data.open_entry);
	close_dso(dso);
}

static rlim_t get_fd_limit(void)
{
	struct rlimit l;
	rlim_t limit = 0;

	/* Allow half of the current open fd limit. */
	if (getrlimit(RLIMIT_NOFILE, &l) == 0) {
		if (l.rlim_cur == RLIM_INFINITY)
			limit = l.rlim_cur;
		else
			limit = l.rlim_cur / 2;
	} else {
		pr_err("failed to get fd limit\n");
		limit = 1;
	}

	return limit;
}

static bool may_cache_fd(void)
{
	static rlim_t limit;

	if (!limit)
		limit = get_fd_limit();

	if (limit == RLIM_INFINITY)
		return true;

	return limit > (rlim_t) dso__data_open_cnt;
}

/*
 * Check and close LRU dso if we crossed allowed limit
 * for opened dso file descriptors. The limit is half
 * of the RLIMIT_NOFILE files opened.
*/
static void check_data_close(void)
{
	bool cache_fd = may_cache_fd();

	if (!cache_fd)
		close_first_dso();
}

/**
 * dso__data_close - Close DSO data file
 * @dso: dso object
 *
 * External interface to close @dso's data file descriptor.
 */
void dso__data_close(struct dso *dso)
{
	close_dso(dso);
}

/**
 * dso__data_fd - Get dso's data file descriptor
 * @dso: dso object
 * @machine: machine object
 *
 * External interface to find dso's file, open it and
 * returns file descriptor.
 */
int dso__data_fd(struct dso *dso, struct machine *machine)
{
	enum dso_binary_type binary_type_data[] = {
@@ -168,8 +331,13 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
	};
	int i = 0;

	if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND)
		return open_dso(dso, machine);
	if (dso->data.fd >= 0)
		return dso->data.fd;

	if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) {
		dso->data.fd = open_dso(dso, machine);
		return dso->data.fd;
	}

	do {
		int fd;
@@ -178,7 +346,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine)

		fd = open_dso(dso, machine);
		if (fd >= 0)
			return fd;
			return dso->data.fd = fd;

	} while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND);

@@ -260,16 +428,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset,
}

static ssize_t
dso_cache__read(struct dso *dso, struct machine *machine,
		 u64 offset, u8 *data, ssize_t size)
dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
{
	struct dso_cache *cache;
	ssize_t ret;
	int fd;

	fd = dso__data_fd(dso, machine);
	if (fd < 0)
		return -1;

	do {
		u64 cache_offset;
@@ -283,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine,
		cache_offset = offset & DSO__DATA_CACHE_MASK;
		ret = -EINVAL;

		if (-1 == lseek(fd, cache_offset, SEEK_SET))
		if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET))
			break;

		ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE);
		ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE);
		if (ret <= 0)
			break;

		cache->offset = cache_offset;
		cache->size   = ret;
		dso_cache__insert(&dso->cache, cache);
		dso_cache__insert(&dso->data.cache, cache);

		ret = dso_cache__memcpy(cache, offset, data, size);

@@ -301,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine,
	if (ret <= 0)
		free(cache);

	close(fd);
	return ret;
}

static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
			      u64 offset, u8 *data, ssize_t size)
static ssize_t dso_cache_read(struct dso *dso, u64 offset,
			      u8 *data, ssize_t size)
{
	struct dso_cache *cache;

	cache = dso_cache__find(&dso->cache, offset);
	cache = dso_cache__find(&dso->data.cache, offset);
	if (cache)
		return dso_cache__memcpy(cache, offset, data, size);
	else
		return dso_cache__read(dso, machine, offset, data, size);
		return dso_cache__read(dso, offset, data, size);
}

ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
			      u64 offset, u8 *data, ssize_t size)
/*
 * Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks
 * in the rb_tree. Any read to already cached data is served
 * by cached data.
 */
static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
{
	ssize_t r = 0;
	u8 *p = data;
@@ -326,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
	do {
		ssize_t ret;

		ret = dso_cache_read(dso, machine, offset, p, size);
		ret = dso_cache_read(dso, offset, p, size);
		if (ret < 0)
			return ret;

@@ -346,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
	return r;
}

static int data_file_size(struct dso *dso)
{
	struct stat st;

	if (!dso->data.file_size) {
		if (fstat(dso->data.fd, &st)) {
			pr_err("dso mmap failed, fstat: %s\n", strerror(errno));
			return -1;
		}
		dso->data.file_size = st.st_size;
	}

	return 0;
}

static ssize_t data_read_offset(struct dso *dso, u64 offset,
				u8 *data, ssize_t size)
{
	if (data_file_size(dso))
		return -1;

	/* Check the offset sanity. */
	if (offset > dso->data.file_size)
		return -1;

	if (offset + size < offset)
		return -1;

	return cached_read(dso, offset, data, size);
}

/**
 * dso__data_read_offset - Read data from dso file offset
 * @dso: dso object
 * @machine: machine object
 * @offset: file offset
 * @data: buffer to store data
 * @size: size of the @data buffer
 *
 * External interface to read data from dso file offset. Open
 * dso data file and use cached_read to get the data.
 */
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
			      u64 offset, u8 *data, ssize_t size)
{
	if (dso__data_fd(dso, machine) < 0)
		return -1;

	return data_read_offset(dso, offset, data, size);
}

/**
 * dso__data_read_addr - Read data from dso address
 * @dso: dso object
 * @machine: machine object
 * @add: virtual memory address
 * @data: buffer to store data
 * @size: size of the @data buffer
 *
 * External interface to read data from dso address.
 */
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
			    struct machine *machine, u64 addr,
			    u8 *data, ssize_t size)
@@ -473,7 +699,8 @@ struct dso *dso__new(const char *name)
		dso__set_short_name(dso, dso->name, false);
		for (i = 0; i < MAP__NR_TYPES; ++i)
			dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
		dso->cache = RB_ROOT;
		dso->data.cache = RB_ROOT;
		dso->data.fd = -1;
		dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
		dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
		dso->loaded = 0;
@@ -485,6 +712,7 @@ struct dso *dso__new(const char *name)
		dso->kernel = DSO_TYPE_USER;
		dso->needs_swap = DSO_SWAP__UNSET;
		INIT_LIST_HEAD(&dso->node);
		INIT_LIST_HEAD(&dso->data.open_entry);
	}

	return dso;
@@ -506,7 +734,8 @@ void dso__delete(struct dso *dso)
		dso->long_name_allocated = false;
	}

	dso_cache__free(&dso->cache);
	dso__data_close(dso);
	dso_cache__free(&dso->data.cache);
	dso__free_a2l(dso);
	zfree(&dso->symsrc_filename);
	free(dso);
+49 −1
Original line number Diff line number Diff line
@@ -76,7 +76,6 @@ struct dso {
	struct list_head node;
	struct rb_root	 symbols[MAP__NR_TYPES];
	struct rb_root	 symbol_names[MAP__NR_TYPES];
	struct rb_root	 cache;
	void		 *a2l;
	char		 *symsrc_filename;
	unsigned int	 a2l_fails;
@@ -99,6 +98,15 @@ struct dso {
	const char	 *long_name;
	u16		 long_name_len;
	u16		 short_name_len;

	/* dso data file */
	struct {
		struct rb_root	 cache;
		int		 fd;
		size_t		 file_size;
		struct list_head open_entry;
	} data;

	char		 name[0];
};

@@ -141,7 +149,47 @@ char dso__symtab_origin(const struct dso *dso);
int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
				   char *root_dir, char *filename, size_t size);

/*
 * The dso__data_* external interface provides following functions:
 *   dso__data_fd
 *   dso__data_close
 *   dso__data_read_offset
 *   dso__data_read_addr
 *
 * Please refer to the dso.c object code for each function and
 * arguments documentation. Following text tries to explain the
 * dso file descriptor caching.
 *
 * The dso__data* interface allows caching of opened file descriptors
 * to speed up the dso data accesses. The idea is to leave the file
 * descriptor opened ideally for the whole life of the dso object.
 *
 * The current usage of the dso__data_* interface is as follows:
 *
 * Get DSO's fd:
 *   int fd = dso__data_fd(dso, machine);
 *   USE 'fd' SOMEHOW
 *
 * Read DSO's data:
 *   n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE);
 *   n = dso__data_read_addr(dso_0, &machine, 0, buf, BUFSIZE);
 *
 * Eventually close DSO's fd:
 *   dso__data_close(dso);
 *
 * It is not necessary to close the DSO object data file. Each time new
 * DSO data file is opened, the limit (RLIMIT_NOFILE/2) is checked. Once
 * it is crossed, the oldest opened DSO object is closed.
 *
 * The dso__delete function calls close_dso function to ensure the
 * data file descriptor gets closed/unmapped before the dso object
 * is freed.
 *
 * TODO
*/
int dso__data_fd(struct dso *dso, struct machine *machine);
void dso__data_close(struct dso *dso);

ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
			      u64 offset, u8 *data, ssize_t size);
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
Loading