perf tools: Add perf_data_file__open interface to data object
authorJiri Olsa <jolsa@redhat.com>
Tue, 15 Oct 2013 14:27:33 +0000 (16:27 +0200)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 21 Oct 2013 20:33:24 +0000 (17:33 -0300)
Adding perf_data_file__open interface to data object to open the
perf.data file for both read and write.

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1381847254-28809-3-git-send-email-jolsa@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Makefile.perf
tools/perf/builtin-record.c
tools/perf/builtin-top.c
tools/perf/util/data.c [new file with mode: 0644]
tools/perf/util/data.h
tools/perf/util/session.c
tools/perf/util/session.h

index c873e039aafbb719756090c28e3b02682db0c7ba..326a26e5fc1c40bd7f75734305a126ecd6b29db8 100644 (file)
@@ -365,6 +365,7 @@ LIB_OBJS += $(OUTPUT)util/vdso.o
 LIB_OBJS += $(OUTPUT)util/stat.o
 LIB_OBJS += $(OUTPUT)util/record.o
 LIB_OBJS += $(OUTPUT)util/srcline.o
+LIB_OBJS += $(OUTPUT)util/data.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
index 4ea46ffbfc1c230c119a26059cbaf569350c3c93..428e28f3e677ec03ab338bb4499ee689bd6c81d1 100644 (file)
@@ -345,8 +345,6 @@ out:
 
 static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
 {
-       struct stat st;
-       int flags;
        int err, feat;
        unsigned long waking = 0;
        const bool forks = argc > 0;
@@ -355,7 +353,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
        struct perf_record_opts *opts = &rec->opts;
        struct perf_evlist *evsel_list = rec->evlist;
        struct perf_data_file *file = &rec->file;
-       const char *output_name = file->path;
        struct perf_session *session;
        bool disabled = false;
 
@@ -367,35 +364,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
        signal(SIGUSR1, sig_handler);
        signal(SIGTERM, sig_handler);
 
-       if (!output_name) {
-               if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
-                       file->is_pipe = true;
-               else
-                       file->path = output_name = "perf.data";
-       }
-       if (output_name) {
-               if (!strcmp(output_name, "-"))
-                       file->is_pipe = true;
-               else if (!stat(output_name, &st) && st.st_size) {
-                       char oldname[PATH_MAX];
-                       snprintf(oldname, sizeof(oldname), "%s.old",
-                                output_name);
-                       unlink(oldname);
-                       rename(output_name, oldname);
-               }
-       }
-
-       flags = O_CREAT|O_RDWR|O_TRUNC;
-
-       if (file->is_pipe)
-               file->fd = STDOUT_FILENO;
-       else
-               file->fd = open(output_name, flags, S_IRUSR | S_IWUSR);
-       if (file->fd < 0) {
-               perror("failed to create output file");
-               return -1;
-       }
-
        session = perf_session__new(file, false, NULL);
        if (session == NULL) {
                pr_err("Not enough memory for reading perf file header\n");
@@ -586,7 +554,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
        fprintf(stderr,
                "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
                (double)rec->bytes_written / 1024.0 / 1024.0,
-               output_name,
+               file->path,
                rec->bytes_written / 24);
 
        return 0;
index 752bebeac3aa8086488225ba81d22226ddb03e51..d934f707ee742719a90537e072c8d56939bb8eaf 100644 (file)
@@ -929,15 +929,8 @@ static int __cmd_top(struct perf_top *top)
        struct perf_record_opts *opts = &top->record_opts;
        pthread_t thread;
        int ret;
-       struct perf_data_file file = {
-               .mode = PERF_DATA_MODE_WRITE,
-       };
 
-       /*
-        * FIXME: perf_session__new should allow passing a O_MMAP, so that all this
-        * mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
-        */
-       top->session = perf_session__new(&file, false, NULL);
+       top->session = perf_session__new(NULL, false, NULL);
        if (top->session == NULL)
                return -ENOMEM;
 
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c
new file mode 100644 (file)
index 0000000..7d09faf
--- /dev/null
@@ -0,0 +1,120 @@
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "data.h"
+#include "util.h"
+
+static bool check_pipe(struct perf_data_file *file)
+{
+       struct stat st;
+       bool is_pipe = false;
+       int fd = perf_data_file__is_read(file) ?
+                STDIN_FILENO : STDOUT_FILENO;
+
+       if (!file->path) {
+               if (!fstat(fd, &st) && S_ISFIFO(st.st_mode))
+                       is_pipe = true;
+       } else {
+               if (!strcmp(file->path, "-"))
+                       is_pipe = true;
+       }
+
+       if (is_pipe)
+               file->fd = fd;
+
+       return file->is_pipe = is_pipe;
+}
+
+static int check_backup(struct perf_data_file *file)
+{
+       struct stat st;
+
+       if (!stat(file->path, &st) && st.st_size) {
+               /* TODO check errors properly */
+               char oldname[PATH_MAX];
+               snprintf(oldname, sizeof(oldname), "%s.old",
+                        file->path);
+               unlink(oldname);
+               rename(file->path, oldname);
+       }
+
+       return 0;
+}
+
+static int open_file_read(struct perf_data_file *file)
+{
+       struct stat st;
+       int fd;
+
+       fd = open(file->path, O_RDONLY);
+       if (fd < 0) {
+               int err = errno;
+
+               pr_err("failed to open %s: %s", file->path, strerror(err));
+               if (err == ENOENT && !strcmp(file->path, "perf.data"))
+                       pr_err("  (try 'perf record' first)");
+               pr_err("\n");
+               return -err;
+       }
+
+       if (fstat(fd, &st) < 0)
+               goto out_close;
+
+       if (!file->force && st.st_uid && (st.st_uid != geteuid())) {
+               pr_err("file %s not owned by current user or root\n",
+                      file->path);
+               goto out_close;
+       }
+
+       if (!st.st_size) {
+               pr_info("zero-sized file (%s), nothing to do!\n",
+                       file->path);
+               goto out_close;
+       }
+
+       file->size = st.st_size;
+       return fd;
+
+ out_close:
+       close(fd);
+       return -1;
+}
+
+static int open_file_write(struct perf_data_file *file)
+{
+       if (check_backup(file))
+               return -1;
+
+       return open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
+}
+
+static int open_file(struct perf_data_file *file)
+{
+       int fd;
+
+       fd = perf_data_file__is_read(file) ?
+            open_file_read(file) : open_file_write(file);
+
+       file->fd = fd;
+       return fd < 0 ? -1 : 0;
+}
+
+int perf_data_file__open(struct perf_data_file *file)
+{
+       if (check_pipe(file))
+               return 0;
+
+       if (!file->path)
+               file->path = "perf.data";
+
+       return open_file(file);
+}
+
+void perf_data_file__close(struct perf_data_file *file)
+{
+       close(file->fd);
+}
index ffa0186e600057d4e305c813f8064908adac818a..d6c262e42f4728aab1ef8dba7566f00477f002e6 100644 (file)
@@ -13,6 +13,7 @@ struct perf_data_file {
        int fd;
        bool is_pipe;
        bool force;
+       unsigned long size;
        enum perf_data_mode mode;
 };
 
@@ -26,4 +27,7 @@ static inline bool perf_data_file__is_write(struct perf_data_file *file)
        return file->mode == PERF_DATA_MODE_WRITE;
 }
 
+int perf_data_file__open(struct perf_data_file *file);
+void perf_data_file__close(struct perf_data_file *file);
+
 #endif /* __PERF_DATA_H */
index e3f63df1d57cb4b231ee0bfcf321799761bb1e86..d857c18d2eeb516863b2f4fcfd96f184a1b72d00 100644 (file)
 #include "perf_regs.h"
 #include "vdso.h"
 
-static int perf_session__open(struct perf_session *self, bool force)
+static int perf_session__open(struct perf_session *self)
 {
-       struct stat input_stat;
-
-       if (!strcmp(self->filename, "-")) {
-               self->fd_pipe = true;
-               self->fd = STDIN_FILENO;
-
+       if (self->fd_pipe) {
                if (perf_session__read_header(self) < 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) < 0) {
                pr_err("incompatible file format (rerun with -v to learn more)");
-               goto out_close;
+               return -1;
        }
 
        if (!perf_evlist__valid_sample_type(self->evlist)) {
                pr_err("non matching sample_type");
-               goto out_close;
+               return -1;
        }
 
        if (!perf_evlist__valid_sample_id_all(self->evlist)) {
                pr_err("non matching sample_id_all");
-               goto out_close;
+               return -1;
        }
 
        if (!perf_evlist__valid_read_format(self->evlist)) {
                pr_err("non matching read_format");
-               goto out_close;
+               return -1;
        }
 
-       self->size = input_stat.st_size;
        return 0;
-
-out_close:
-       close(self->fd);
-       self->fd = -1;
-       return -1;
 }
 
 void perf_session__set_id_hdr_size(struct perf_session *session)
@@ -110,35 +72,35 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
                                       bool repipe, struct perf_tool *tool)
 {
        struct perf_session *self;
-       const char *filename = file->path;
-       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)
+       self = zalloc(sizeof(*self));
+       if (!self)
                goto out;
 
-       memcpy(self->filename, filename, len);
        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);
        machines__init(&self->machines);
 
-       if (perf_data_file__is_read(file)) {
-               if (perf_session__open(self, file->force) < 0)
+       if (file) {
+               if (perf_data_file__open(file))
                        goto out_delete;
-               perf_session__set_id_hdr_size(self);
-       } else if (perf_data_file__is_write(file)) {
+
+               self->fd       = file->fd;
+               self->fd_pipe  = file->is_pipe;
+               self->filename = file->path;
+               self->size     = file->size;
+
+               if (perf_data_file__is_read(file)) {
+                       if (perf_session__open(self) < 0)
+                               goto out_close;
+
+                       perf_session__set_id_hdr_size(self);
+               }
+       }
+
+       if (!file || perf_data_file__is_write(file)) {
                /*
                 * In O_RDONLY mode this will be performed when reading the
                 * kernel MMAP event, in perf_event__process_mmap().
@@ -153,10 +115,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
                tool->ordered_samples = false;
        }
 
-out:
        return self;
-out_delete:
+
+ out_close:
+       perf_data_file__close(file);
+ out_delete:
        perf_session__delete(self);
+ out:
        return NULL;
 }
 
index f2f6251fd62c0c44aa2800152c34eee43673e3a3..e1ca2d0ae541a13bd7cef2887d8ec3e0ca704793 100644 (file)
@@ -39,7 +39,7 @@ struct perf_session {
        bool                    fd_pipe;
        bool                    repipe;
        struct ordered_samples  ordered_samples;
-       char                    filename[1];
+       const char              *filename;
 };
 
 #define PRINT_IP_OPT_IP                (1<<0)