perf tools: Introduce perf_session class

That does all the initialization boilerplate, opening the file,
reading the header, checking if it is valid, etc.

And that will as well have the threads list, kmap (now) global
variable, etc, so that we can handle two (or more) perf.data files
describing sessions to compare.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1260573842-19720-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Arnaldo Carvalho de Melo 2009-12-11 21:24:02 -02:00 committed by Ingo Molnar
parent ea08d8cbd1
commit 94c744b6c0
15 changed files with 206 additions and 181 deletions

View file

@ -356,7 +356,9 @@ LIB_H += util/parse-options.h
LIB_H += util/parse-events.h
LIB_H += util/quote.h
LIB_H += util/util.h
LIB_H += util/header.h
LIB_H += util/help.h
LIB_H += util/session.h
LIB_H += util/strbuf.h
LIB_H += util/string.h
LIB_H += util/strlist.h
@ -405,6 +407,7 @@ LIB_OBJS += util/callchain.o
LIB_OBJS += util/values.o
LIB_OBJS += util/debug.o
LIB_OBJS += util/map.o
LIB_OBJS += util/session.o
LIB_OBJS += util/thread.o
LIB_OBJS += util/trace-event-parse.o
LIB_OBJS += util/trace-event-read.o

View file

@ -25,6 +25,7 @@
#include "util/thread.h"
#include "util/sort.h"
#include "util/hist.h"
#include "util/session.h"
#include "util/data_map.h"
static char const *input_name = "perf.data";
@ -462,21 +463,23 @@ static struct perf_file_handler file_handler = {
static int __cmd_annotate(void)
{
struct perf_header *header;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, force);
struct thread *idle;
int ret;
if (session == NULL)
return -ENOMEM;
idle = register_idle_thread();
register_perf_file_handler(&file_handler);
ret = mmap_dispatch_perf_file(&header, input_name, 0, 0,
&event__cwdlen, &event__cwd);
ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
if (ret)
return ret;
goto out_delete;
if (dump_trace) {
event__print_totals();
return 0;
goto out_delete;
}
if (verbose > 3)
@ -489,6 +492,8 @@ static int __cmd_annotate(void)
output__resort(event__total[0]);
find_annotations();
out_delete:
perf_session__delete(session);
return ret;
}

View file

@ -11,8 +11,8 @@
#include "util/cache.h"
#include "util/data_map.h"
#include "util/debug.h"
#include "util/header.h"
#include "util/parse-options.h"
#include "util/session.h"
#include "util/symbol.h"
static char const *input_name = "perf.data";
@ -55,56 +55,17 @@ static int perf_file_section__process_buildids(struct perf_file_section *self,
static int __cmd_buildid_list(void)
{
int err = -1;
struct perf_header *header;
struct perf_file_header f_header;
struct stat input_stat;
int input = open(input_name, O_RDONLY);
struct perf_session *session = perf_session__new(input_name, O_RDONLY, force);
if (input < 0) {
pr_err("failed to open file: %s", input_name);
if (!strcmp(input_name, "perf.data"))
pr_err(" (try 'perf record' first)");
pr_err("\n");
goto out;
}
if (session == NULL)
return -1;
err = fstat(input, &input_stat);
if (err < 0) {
perror("failed to stat file");
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",
input_name);
goto out_close;
}
if (!input_stat.st_size) {
pr_info("zero-sized file, nothing to do!\n");
goto out_close;
}
err = -1;
header = perf_header__new();
if (header == NULL)
goto out_close;
if (perf_file_header__read(&f_header, header, input) < 0) {
pr_warning("incompatible file format");
goto out_close;
}
err = perf_header__process_sections(header, input,
err = perf_header__process_sections(&session->header, session->fd,
perf_file_section__process_buildids);
if (err >= 0)
dsos__fprintf_buildid(stdout);
if (err < 0)
goto out_close;
dsos__fprintf_buildid(stdout);
out_close:
close(input);
out:
perf_session__delete(session);
return err;
}

View file

@ -6,6 +6,7 @@
#include "util/symbol.h"
#include "util/thread.h"
#include "util/header.h"
#include "util/session.h"
#include "util/parse-options.h"
#include "util/trace-event.h"
@ -20,7 +21,6 @@ typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
static char const *input_name = "perf.data";
static struct perf_header *header;
static u64 sample_type;
static int alloc_flag;
@ -367,11 +367,18 @@ static struct perf_file_handler file_handler = {
static int read_events(void)
{
int err;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
if (session == NULL)
return -ENOMEM;
register_idle_thread();
register_perf_file_handler(&file_handler);
return mmap_dispatch_perf_file(&header, input_name, 0, 0,
&event__cwdlen, &event__cwd);
err = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
perf_session__delete(session);
return err;
}
static double fragmentation(unsigned long n_req, unsigned long n_alloc)

View file

@ -17,6 +17,7 @@
#include "util/header.h"
#include "util/event.h"
#include "util/debug.h"
#include "util/session.h"
#include "util/symbol.h"
#include <unistd.h>
@ -62,7 +63,7 @@ static int nr_cpu = 0;
static int file_new = 1;
struct perf_header *header = NULL;
static struct perf_session *session;
struct mmap_data {
int counter;
@ -216,12 +217,12 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n
{
struct perf_header_attr *h_attr;
if (nr < header->attrs) {
h_attr = header->attr[nr];
if (nr < session->header.attrs) {
h_attr = session->header.attr[nr];
} else {
h_attr = perf_header_attr__new(a);
if (h_attr != NULL)
if (perf_header__add_attr(header, h_attr) < 0) {
if (perf_header__add_attr(&session->header, h_attr) < 0) {
perf_header_attr__delete(h_attr);
h_attr = NULL;
}
@ -395,9 +396,9 @@ static void open_counters(int cpu, pid_t pid)
static void atexit_header(void)
{
header->data_size += bytes_written;
session->header.data_size += bytes_written;
perf_header__write(header, output, true);
perf_header__write(&session->header, output, true);
}
static int __cmd_record(int argc, const char **argv)
@ -440,24 +441,24 @@ static int __cmd_record(int argc, const char **argv)
exit(-1);
}
header = perf_header__new();
if (header == NULL) {
session = perf_session__new(output_name, O_WRONLY, force);
if (session == NULL) {
pr_err("Not enough memory for reading perf file header\n");
return -1;
}
if (!file_new) {
err = perf_header__read(header, output);
err = perf_header__read(&session->header, output);
if (err < 0)
return err;
}
if (raw_samples) {
perf_header__set_feat(header, HEADER_TRACE_INFO);
perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
} else {
for (i = 0; i < nr_counters; i++) {
if (attrs[i].sample_type & PERF_SAMPLE_RAW) {
perf_header__set_feat(header, HEADER_TRACE_INFO);
perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
break;
}
}
@ -481,7 +482,7 @@ static int __cmd_record(int argc, const char **argv)
}
if (file_new) {
err = perf_header__write(header, output, false);
err = perf_header__write(&session->header, output, false);
if (err < 0)
return err;
}

View file

@ -22,6 +22,7 @@
#include "perf.h"
#include "util/debug.h"
#include "util/header.h"
#include "util/session.h"
#include "util/parse-options.h"
#include "util/parse-events.h"
@ -52,7 +53,7 @@ static int exclude_other = 1;
static char callchain_default_opt[] = "fractal,0.5";
static struct perf_header *header;
static struct perf_session *session;
static u64 sample_type;
@ -701,7 +702,7 @@ static int process_read_event(event_t *event)
{
struct perf_event_attr *attr;
attr = perf_header__find_attr(event->read.id, header);
attr = perf_header__find_attr(event->read.id, &session->header);
if (show_threads) {
const char *name = attr ? __event_name(attr->type, attr->config)
@ -766,6 +767,10 @@ static int __cmd_report(void)
struct thread *idle;
int ret;
session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL)
return -ENOMEM;
idle = register_idle_thread();
thread__comm_adjust(idle);
@ -774,14 +779,14 @@ static int __cmd_report(void)
register_perf_file_handler(&file_handler);
ret = mmap_dispatch_perf_file(&header, input_name, force,
full_paths, &event__cwdlen, &event__cwd);
ret = perf_session__process_events(session, full_paths,
&event__cwdlen, &event__cwd);
if (ret)
return ret;
goto out_delete;
if (dump_trace) {
event__print_totals();
return 0;
goto out_delete;
}
if (verbose > 3)
@ -796,7 +801,8 @@ static int __cmd_report(void)
if (show_threads)
perf_read_values_destroy(&show_threads_values);
out_delete:
perf_session__delete(session);
return ret;
}

View file

@ -6,6 +6,7 @@
#include "util/symbol.h"
#include "util/thread.h"
#include "util/header.h"
#include "util/session.h"
#include "util/parse-options.h"
#include "util/trace-event.h"
@ -21,7 +22,6 @@
static char const *input_name = "perf.data";
static struct perf_header *header;
static u64 sample_type;
static char default_sort_order[] = "avg, max, switch, runtime";
@ -1663,11 +1663,18 @@ static struct perf_file_handler file_handler = {
static int read_events(void)
{
int err;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
if (session == NULL)
return -ENOMEM;
register_idle_thread();
register_perf_file_handler(&file_handler);
return mmap_dispatch_perf_file(&header, input_name, 0, 0,
&event__cwdlen, &event__cwd);
err = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
perf_session__delete(session);
return err;
}
static void print_bad_events(void)

View file

@ -1059,15 +1059,17 @@ static struct perf_file_handler file_handler = {
static int __cmd_timechart(void)
{
struct perf_header *header;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
int ret;
if (session == NULL)
return -ENOMEM;
register_perf_file_handler(&file_handler);
ret = mmap_dispatch_perf_file(&header, input_name, 0, 0,
&event__cwdlen, &event__cwd);
ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
if (ret)
return EXIT_FAILURE;
goto out_delete;
process_samples();
@ -1079,8 +1081,9 @@ static int __cmd_timechart(void)
pr_info("Written %2.1f seconds of trace to %s.\n",
(last_time - first_time) / 1000000000.0, output_name);
return EXIT_SUCCESS;
out_delete:
perf_session__delete(session);
return ret;
}
static const char * const timechart_usage[] = {

View file

@ -7,6 +7,7 @@
#include "util/header.h"
#include "util/exec_cmd.h"
#include "util/trace-event.h"
#include "util/session.h"
static char const *script_name;
static char const *generate_script_lang;
@ -61,7 +62,7 @@ static int cleanup_scripting(void)
static char const *input_name = "perf.data";
static struct perf_header *header;
static struct perf_session *session;
static u64 sample_type;
static int process_sample_event(event_t *event)
@ -126,11 +127,18 @@ static struct perf_file_handler file_handler = {
static int __cmd_trace(void)
{
int err;
session = perf_session__new(input_name, O_RDONLY, 0);
if (session == NULL)
return -ENOMEM;
register_idle_thread();
register_perf_file_handler(&file_handler);
return mmap_dispatch_perf_file(&header, input_name,
0, 0, &event__cwdlen, &event__cwd);
err = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
perf_session__delete(session);
return err;
}
struct script_spec {
@ -348,11 +356,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
return -1;
}
header = perf_header__new();
if (header == NULL)
return -1;
perf_header__read(header, input);
perf_header__read(&session->header, input);
err = scripting_ops->generate_script("perf-trace");
goto out;
}

View file

@ -129,23 +129,16 @@ int perf_header__read_build_ids(int input, u64 offset, u64 size)
return err;
}
int mmap_dispatch_perf_file(struct perf_header **pheader,
const char *input_name,
int force,
int full_paths,
int *cwdlen,
char **cwd)
int perf_session__process_events(struct perf_session *self,
int full_paths, int *cwdlen, char **cwd)
{
int err;
struct perf_header *header;
unsigned long head, shift;
unsigned long offset = 0;
struct stat input_stat;
size_t page_size;
u64 sample_type;
event_t *event;
uint32_t size;
int input;
char *buf;
if (curr_handler == NULL) {
@ -155,56 +148,19 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
page_size = getpagesize();
input = open(input_name, O_RDONLY);
if (input < 0) {
pr_err("Failed to open file: %s", input_name);
if (!strcmp(input_name, "perf.data"))
pr_err(" (try 'perf record' first)");
pr_err("\n");
return -errno;
}
if (fstat(input, &input_stat) < 0) {
pr_err("failed to stat file");
err = -errno;
goto out_close;
}
err = -EACCES;
if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
pr_err("file: %s not owned by current user or root\n",
input_name);
goto out_close;
}
if (input_stat.st_size == 0) {
pr_info("zero-sized file, nothing to do!\n");
goto done;
}
err = -ENOMEM;
header = perf_header__new();
if (header == NULL)
goto out_close;
err = perf_header__read(header, input);
if (err < 0)
goto out_delete;
*pheader = header;
head = header->data_offset;
sample_type = perf_header__sample_type(header);
head = self->header.data_offset;
sample_type = perf_header__sample_type(&self->header);
err = -EINVAL;
if (curr_handler->sample_type_check &&
curr_handler->sample_type_check(sample_type) < 0)
goto out_delete;
goto out_err;
if (!full_paths) {
if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
pr_err("failed to get the current directory\n");
err = -errno;
goto out_delete;
goto out_err;
}
*cwd = __cwd;
*cwdlen = strlen(*cwd);
@ -219,11 +175,11 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
remap:
buf = mmap(NULL, page_size * mmap_window, PROT_READ,
MAP_SHARED, input, offset);
MAP_SHARED, self->fd, offset);
if (buf == MAP_FAILED) {
pr_err("failed to mmap file\n");
err = -errno;
goto out_delete;
goto out_err;
}
more:
@ -273,19 +229,14 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
head += size;
if (offset + head >= header->data_offset + header->data_size)
if (offset + head >= self->header.data_offset + self->header.data_size)
goto done;
if (offset + head < (unsigned long)input_stat.st_size)
if (offset + head < self->size)
goto more;
done:
err = 0;
out_close:
close(input);
out_err:
return err;
out_delete:
perf_header__delete(header);
goto out_close;
}

View file

@ -3,6 +3,7 @@
#include "event.h"
#include "header.h"
#include "session.h"
typedef int (*event_type_handler_t)(event_t *);
@ -21,12 +22,8 @@ struct perf_file_handler {
};
void register_perf_file_handler(struct perf_file_handler *handler);
int mmap_dispatch_perf_file(struct perf_header **pheader,
const char *input_name,
int force,
int full_paths,
int *cwdlen,
char **cwd);
int perf_session__process_events(struct perf_session *self,
int full_paths, int *cwdlen, char **cwd);
int perf_header__read_build_ids(int input, u64 offset, u64 file_size);
#endif

View file

@ -58,35 +58,19 @@ int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
return 0;
}
/*
* Create new perf.data header:
*/
struct perf_header *perf_header__new(void)
int perf_header__init(struct perf_header *self)
{
struct perf_header *self = zalloc(sizeof(*self));
if (self != NULL) {
self->size = 1;
self->attr = malloc(sizeof(void *));
if (self->attr == NULL) {
free(self);
self = NULL;
}
}
return self;
self->size = 1;
self->attr = malloc(sizeof(void *));
return self->attr == NULL ? -ENOMEM : 0;
}
void perf_header__delete(struct perf_header *self)
void perf_header__exit(struct perf_header *self)
{
int i;
for (i = 0; i < self->attrs; ++i)
perf_header_attr__delete(self->attr[i]);
perf_header_attr__delete(self->attr[i]);
free(self->attr);
free(self);
}
int perf_header__add_attr(struct perf_header *self,

View file

@ -55,8 +55,8 @@ struct perf_header {
DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
};
struct perf_header *perf_header__new(void);
void perf_header__delete(struct perf_header *self);
int perf_header__init(struct perf_header *self);
void perf_header__exit(struct perf_header *self);
int perf_header__read(struct perf_header *self, int fd);
int perf_header__write(struct perf_header *self, int fd, bool at_exit);

80
tools/perf/util/session.c Normal file
View file

@ -0,0 +1,80 @@
#include <linux/kernel.h>
#include <unistd.h>
#include <sys/types.h>
#include "session.h"
#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;
}
struct perf_session *perf_session__new(const char *filename, int mode, bool force)
{
size_t len = strlen(filename) + 1;
struct perf_session *self = zalloc(sizeof(*self) + len);
if (self == NULL)
goto out;
if (perf_header__init(&self->header) < 0)
goto out_delete;
memcpy(self->filename, filename, len);
if (mode == O_RDONLY && perf_session__open(self, force) < 0) {
perf_session__delete(self);
self = NULL;
}
out:
return self;
out_delete:
free(self);
return NULL;
}
void perf_session__delete(struct perf_session *self)
{
perf_header__exit(&self->header);
close(self->fd);
free(self);
}

16
tools/perf/util/session.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef __PERF_SESSION_H
#define __PERF_SESSION_H
#include "header.h"
struct perf_session {
struct perf_header header;
unsigned long size;
int fd;
char filename[0];
};
struct perf_session *perf_session__new(const char *filename, int mode, bool force);
void perf_session__delete(struct perf_session *self);
#endif /* __PERF_SESSION_H */