forked from Minki/linux
perf/core improvements and fixes:
. Honor user freq/interval properly in record command (Namhyung Kim) . Speedup DWARF unwind (Jiri Olsa) Signed-off-by: Jiri Olsa <jolsa@kernel.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTmcR/AAoJEPZqUSBWB3s9K0IP/2NOpOB2BJLhFXu1BorztBb1 DYmjWKvXhxBSZtGLX/Ifr9AeVYKdI0F28Fe8Gtb80Ra+kHRghCViSQefzg2On7dR UZ1Fb/7rQMxyfNmckYTOAWlCUVQ3BJNioWPDqciKqCOR6Bi4mnEdjSLuAIQ3+S3K BZ+ppl92FRjOwLfSf5Va+7HoKHb+UWq94dgFnQTz+AHj7kWsSMrSjClG/cCoCLR7 unSMAgK8A7woh9m4N6hgQown8fmmUPiDe6+ocdWe2kyKy1Gjgs5PmQCJ0lm1hLjA jRz57bMuR9kPtvKG4aMvJKT2VtQSh+RvfPn+jDvzKnw3NlcOsYfrjJTyZEfIs0r9 XbhdqO2u2CdbOW4zcfl68QogN5kryKYLsC6Pqffmm0ttY1/Dv3jyBMjlr83lB9Y9 od2BHRKOn46MAC/SERbRag/CUD+Cxp3yVHltG/6DBLd1MCmYIqijP30LUp0XuDyP Rj9xeSLLki3OCjdTSivPfVPf3rn1yssJJNYG1dult3IqtBkxTNtHOb0jWp9mRXDR fPC0d244JSlbU3/NUJEZotD5g0hdIhQ6loWcOaq1Li+Bdldt8anF6Mf9fioX5T6n bf+cMRVtKMHN41f36269nLsX0SuHa59hwPs4lS+KsR5APsDQBurt/LTbcBkY26CJ WO0mntURPJm2jWd9jz0B =djnN -----END PGP SIGNATURE----- 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: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
4ba9619505
@ -3,6 +3,8 @@
|
|||||||
*
|
*
|
||||||
* Builtin regression testing command: ever growing number of sanity tests
|
* Builtin regression testing command: ever growing number of sanity tests
|
||||||
*/
|
*/
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "intlist.h"
|
#include "intlist.h"
|
||||||
#include "tests.h"
|
#include "tests.h"
|
||||||
@ -50,9 +52,17 @@ static struct test {
|
|||||||
.func = test__pmu,
|
.func = test__pmu,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.desc = "Test dso data interface",
|
.desc = "Test dso data read",
|
||||||
.func = test__dso_data,
|
.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",
|
.desc = "roundtrip evsel->name check",
|
||||||
.func = test__perf_evsel__roundtrip_name_test,
|
.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;
|
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)
|
static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -200,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pr_debug("\n--- start ---\n");
|
pr_debug("\n--- start ---\n");
|
||||||
err = tests[curr].func();
|
err = run_test(&tests[curr]);
|
||||||
pr_debug("---- end ----\n%s:", tests[curr].desc);
|
pr_debug("---- end ----\n%s:", tests[curr].desc);
|
||||||
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
|
@ -1,22 +1,27 @@
|
|||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <api/fs/fs.h>
|
||||||
|
#include "util.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
#include "tests.h"
|
#include "tests.h"
|
||||||
|
|
||||||
static char *test_file(int size)
|
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;
|
char *templ = buf_templ;
|
||||||
int fd, i;
|
int fd, i;
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
|
|
||||||
|
strcpy(buf_templ, TEMPL);
|
||||||
|
#undef TEMPL
|
||||||
|
|
||||||
fd = mkstemp(templ);
|
fd = mkstemp(templ);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
perror("mkstemp failed");
|
perror("mkstemp failed");
|
||||||
@ -150,3 +155,204 @@ int test__dso_data(void)
|
|||||||
unlink(file);
|
unlink(file);
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void);
|
|||||||
int test__pmu(void);
|
int test__pmu(void);
|
||||||
int test__attr(void);
|
int test__attr(void);
|
||||||
int test__dso_data(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__parse_events(void);
|
||||||
int test__hists_link(void);
|
int test__hists_link(void);
|
||||||
int test__python_use(void);
|
int test__python_use(void);
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#include <asm/bug.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
#include "dso.h"
|
#include "dso.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
@ -136,7 +139,48 @@ int dso__read_binary_type_filename(const struct dso *dso,
|
|||||||
return ret;
|
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;
|
int fd;
|
||||||
char *root_dir = (char *)"";
|
char *root_dir = (char *)"";
|
||||||
@ -154,11 +198,130 @@ static int open_dso(struct dso *dso, struct machine *machine)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = open(name, O_RDONLY);
|
fd = do_open(name);
|
||||||
free(name);
|
free(name);
|
||||||
return fd;
|
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)
|
int dso__data_fd(struct dso *dso, struct machine *machine)
|
||||||
{
|
{
|
||||||
enum dso_binary_type binary_type_data[] = {
|
enum dso_binary_type binary_type_data[] = {
|
||||||
@ -168,8 +331,13 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
|
|||||||
};
|
};
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND)
|
if (dso->data.fd >= 0)
|
||||||
return open_dso(dso, machine);
|
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 {
|
do {
|
||||||
int fd;
|
int fd;
|
||||||
@ -178,7 +346,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
|
|||||||
|
|
||||||
fd = open_dso(dso, machine);
|
fd = open_dso(dso, machine);
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
return fd;
|
return dso->data.fd = fd;
|
||||||
|
|
||||||
} while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND);
|
} 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
|
static ssize_t
|
||||||
dso_cache__read(struct dso *dso, struct machine *machine,
|
dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
|
||||||
u64 offset, u8 *data, ssize_t size)
|
|
||||||
{
|
{
|
||||||
struct dso_cache *cache;
|
struct dso_cache *cache;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
int fd;
|
|
||||||
|
|
||||||
fd = dso__data_fd(dso, machine);
|
|
||||||
if (fd < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
u64 cache_offset;
|
u64 cache_offset;
|
||||||
@ -283,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine,
|
|||||||
cache_offset = offset & DSO__DATA_CACHE_MASK;
|
cache_offset = offset & DSO__DATA_CACHE_MASK;
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
|
||||||
if (-1 == lseek(fd, cache_offset, SEEK_SET))
|
if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE);
|
ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE);
|
||||||
if (ret <= 0)
|
if (ret <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
cache->offset = cache_offset;
|
cache->offset = cache_offset;
|
||||||
cache->size = ret;
|
cache->size = ret;
|
||||||
dso_cache__insert(&dso->cache, cache);
|
dso_cache__insert(&dso->data.cache, cache);
|
||||||
|
|
||||||
ret = dso_cache__memcpy(cache, offset, data, size);
|
ret = dso_cache__memcpy(cache, offset, data, size);
|
||||||
|
|
||||||
@ -301,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine,
|
|||||||
if (ret <= 0)
|
if (ret <= 0)
|
||||||
free(cache);
|
free(cache);
|
||||||
|
|
||||||
close(fd);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
|
static ssize_t dso_cache_read(struct dso *dso, u64 offset,
|
||||||
u64 offset, u8 *data, ssize_t size)
|
u8 *data, ssize_t size)
|
||||||
{
|
{
|
||||||
struct dso_cache *cache;
|
struct dso_cache *cache;
|
||||||
|
|
||||||
cache = dso_cache__find(&dso->cache, offset);
|
cache = dso_cache__find(&dso->data.cache, offset);
|
||||||
if (cache)
|
if (cache)
|
||||||
return dso_cache__memcpy(cache, offset, data, size);
|
return dso_cache__memcpy(cache, offset, data, size);
|
||||||
else
|
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;
|
ssize_t r = 0;
|
||||||
u8 *p = data;
|
u8 *p = data;
|
||||||
@ -326,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
|
|||||||
do {
|
do {
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
ret = dso_cache_read(dso, machine, offset, p, size);
|
ret = dso_cache_read(dso, offset, p, size);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -346,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
|
|||||||
return r;
|
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,
|
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
|
||||||
struct machine *machine, u64 addr,
|
struct machine *machine, u64 addr,
|
||||||
u8 *data, ssize_t size)
|
u8 *data, ssize_t size)
|
||||||
@ -473,7 +699,8 @@ struct dso *dso__new(const char *name)
|
|||||||
dso__set_short_name(dso, dso->name, false);
|
dso__set_short_name(dso, dso->name, false);
|
||||||
for (i = 0; i < MAP__NR_TYPES; ++i)
|
for (i = 0; i < MAP__NR_TYPES; ++i)
|
||||||
dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
|
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->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
|
||||||
dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
|
dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
|
||||||
dso->loaded = 0;
|
dso->loaded = 0;
|
||||||
@ -485,6 +712,7 @@ struct dso *dso__new(const char *name)
|
|||||||
dso->kernel = DSO_TYPE_USER;
|
dso->kernel = DSO_TYPE_USER;
|
||||||
dso->needs_swap = DSO_SWAP__UNSET;
|
dso->needs_swap = DSO_SWAP__UNSET;
|
||||||
INIT_LIST_HEAD(&dso->node);
|
INIT_LIST_HEAD(&dso->node);
|
||||||
|
INIT_LIST_HEAD(&dso->data.open_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dso;
|
return dso;
|
||||||
@ -506,7 +734,8 @@ void dso__delete(struct dso *dso)
|
|||||||
dso->long_name_allocated = false;
|
dso->long_name_allocated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dso_cache__free(&dso->cache);
|
dso__data_close(dso);
|
||||||
|
dso_cache__free(&dso->data.cache);
|
||||||
dso__free_a2l(dso);
|
dso__free_a2l(dso);
|
||||||
zfree(&dso->symsrc_filename);
|
zfree(&dso->symsrc_filename);
|
||||||
free(dso);
|
free(dso);
|
||||||
|
@ -76,7 +76,6 @@ struct dso {
|
|||||||
struct list_head node;
|
struct list_head node;
|
||||||
struct rb_root symbols[MAP__NR_TYPES];
|
struct rb_root symbols[MAP__NR_TYPES];
|
||||||
struct rb_root symbol_names[MAP__NR_TYPES];
|
struct rb_root symbol_names[MAP__NR_TYPES];
|
||||||
struct rb_root cache;
|
|
||||||
void *a2l;
|
void *a2l;
|
||||||
char *symsrc_filename;
|
char *symsrc_filename;
|
||||||
unsigned int a2l_fails;
|
unsigned int a2l_fails;
|
||||||
@ -99,6 +98,15 @@ struct dso {
|
|||||||
const char *long_name;
|
const char *long_name;
|
||||||
u16 long_name_len;
|
u16 long_name_len;
|
||||||
u16 short_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];
|
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,
|
int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
|
||||||
char *root_dir, char *filename, size_t size);
|
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);
|
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,
|
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
|
||||||
u64 offset, u8 *data, ssize_t size);
|
u64 offset, u8 *data, ssize_t size);
|
||||||
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
|
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "../perf.h"
|
#include "../perf.h"
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
#include "build-id.h"
|
#include "build-id.h"
|
||||||
|
#include "perf_regs.h"
|
||||||
|
|
||||||
struct mmap_event {
|
struct mmap_event {
|
||||||
struct perf_event_header header;
|
struct perf_event_header header;
|
||||||
@ -89,6 +90,10 @@ struct regs_dump {
|
|||||||
u64 abi;
|
u64 abi;
|
||||||
u64 mask;
|
u64 mask;
|
||||||
u64 *regs;
|
u64 *regs;
|
||||||
|
|
||||||
|
/* Cached values/mask filled by first register access. */
|
||||||
|
u64 cache_regs[PERF_REGS_MAX];
|
||||||
|
u64 cache_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct stack_dump {
|
struct stack_dump {
|
||||||
|
@ -589,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We default some events to a 1 default interval. But keep
|
* We default some events to have a default interval. But keep
|
||||||
* it a weak assumption overridable by the user.
|
* it a weak assumption overridable by the user.
|
||||||
*/
|
*/
|
||||||
if (!attr->sample_period || (opts->user_freq != UINT_MAX &&
|
if (!attr->sample_period || (opts->user_freq != UINT_MAX ||
|
||||||
opts->user_interval != ULLONG_MAX)) {
|
opts->user_interval != ULLONG_MAX)) {
|
||||||
if (opts->freq) {
|
if (opts->freq) {
|
||||||
perf_evsel__set_sample_bit(evsel, PERIOD);
|
perf_evsel__set_sample_bit(evsel, PERIOD);
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "perf_regs.h"
|
#include "perf_regs.h"
|
||||||
|
#include "event.h"
|
||||||
|
|
||||||
int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
|
int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
|
||||||
{
|
{
|
||||||
int i, idx = 0;
|
int i, idx = 0;
|
||||||
u64 mask = regs->mask;
|
u64 mask = regs->mask;
|
||||||
|
|
||||||
|
if (regs->cache_mask & (1 << id))
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (!(mask & (1 << id)))
|
if (!(mask & (1 << id)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -14,6 +18,10 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
|
|||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
*valp = regs->regs[idx];
|
regs->cache_mask |= (1 << id);
|
||||||
|
regs->cache_regs[id] = regs->regs[idx];
|
||||||
|
|
||||||
|
out:
|
||||||
|
*valp = regs->cache_regs[id];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
#define __PERF_REGS_H
|
#define __PERF_REGS_H
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include "event.h"
|
|
||||||
|
struct regs_dump;
|
||||||
|
|
||||||
#ifdef HAVE_PERF_REGS_SUPPORT
|
#ifdef HAVE_PERF_REGS_SUPPORT
|
||||||
#include <perf_regs.h>
|
#include <perf_regs.h>
|
||||||
@ -11,6 +12,7 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
#define PERF_REGS_MASK 0
|
#define PERF_REGS_MASK 0
|
||||||
|
#define PERF_REGS_MAX 0
|
||||||
|
|
||||||
static inline const char *perf_reg_name(int id __maybe_unused)
|
static inline const char *perf_reg_name(int id __maybe_unused)
|
||||||
{
|
{
|
||||||
|
@ -250,7 +250,6 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
|
|||||||
|
|
||||||
/* Check the .eh_frame section for unwinding info */
|
/* Check the .eh_frame section for unwinding info */
|
||||||
offset = elf_section_offset(fd, ".eh_frame_hdr");
|
offset = elf_section_offset(fd, ".eh_frame_hdr");
|
||||||
close(fd);
|
|
||||||
|
|
||||||
if (offset)
|
if (offset)
|
||||||
ret = unwind_spec_ehframe(dso, machine, offset,
|
ret = unwind_spec_ehframe(dso, machine, offset,
|
||||||
@ -271,7 +270,6 @@ static int read_unwind_spec_debug_frame(struct dso *dso,
|
|||||||
|
|
||||||
/* Check the .debug_frame section for unwinding info */
|
/* Check the .debug_frame section for unwinding info */
|
||||||
*offset = elf_section_offset(fd, ".debug_frame");
|
*offset = elf_section_offset(fd, ".debug_frame");
|
||||||
close(fd);
|
|
||||||
|
|
||||||
if (*offset)
|
if (*offset)
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user