diff options
author | Jiri Olsa <jolsa@redhat.com> | 2013-10-15 10:27:33 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2013-10-21 16:33:24 -0400 |
commit | 6a4d98d787b38a130a67e78b64182b419899623a (patch) | |
tree | bd72dfaa6b4e93b0d304320dd373d0150724e29d | |
parent | f5fc14124c5cefdd052a2b2a6a3f0ed531540113 (diff) |
perf tools: Add perf_data_file__open interface to data object
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>
-rw-r--r-- | tools/perf/Makefile.perf | 1 | ||||
-rw-r--r-- | tools/perf/builtin-record.c | 34 | ||||
-rw-r--r-- | tools/perf/builtin-top.c | 9 | ||||
-rw-r--r-- | tools/perf/util/data.c | 120 | ||||
-rw-r--r-- | tools/perf/util/data.h | 4 | ||||
-rw-r--r-- | tools/perf/util/session.c | 95 | ||||
-rw-r--r-- | tools/perf/util/session.h | 2 |
7 files changed, 158 insertions, 107 deletions
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index c873e039aafb..326a26e5fc1c 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -365,6 +365,7 @@ LIB_OBJS += $(OUTPUT)util/vdso.o | |||
365 | LIB_OBJS += $(OUTPUT)util/stat.o | 365 | LIB_OBJS += $(OUTPUT)util/stat.o |
366 | LIB_OBJS += $(OUTPUT)util/record.o | 366 | LIB_OBJS += $(OUTPUT)util/record.o |
367 | LIB_OBJS += $(OUTPUT)util/srcline.o | 367 | LIB_OBJS += $(OUTPUT)util/srcline.o |
368 | LIB_OBJS += $(OUTPUT)util/data.o | ||
368 | 369 | ||
369 | LIB_OBJS += $(OUTPUT)ui/setup.o | 370 | LIB_OBJS += $(OUTPUT)ui/setup.o |
370 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 371 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4ea46ffbfc1c..428e28f3e677 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -345,8 +345,6 @@ out: | |||
345 | 345 | ||
346 | static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | 346 | static int __cmd_record(struct perf_record *rec, int argc, const char **argv) |
347 | { | 347 | { |
348 | struct stat st; | ||
349 | int flags; | ||
350 | int err, feat; | 348 | int err, feat; |
351 | unsigned long waking = 0; | 349 | unsigned long waking = 0; |
352 | const bool forks = argc > 0; | 350 | const bool forks = argc > 0; |
@@ -355,7 +353,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
355 | struct perf_record_opts *opts = &rec->opts; | 353 | struct perf_record_opts *opts = &rec->opts; |
356 | struct perf_evlist *evsel_list = rec->evlist; | 354 | struct perf_evlist *evsel_list = rec->evlist; |
357 | struct perf_data_file *file = &rec->file; | 355 | struct perf_data_file *file = &rec->file; |
358 | const char *output_name = file->path; | ||
359 | struct perf_session *session; | 356 | struct perf_session *session; |
360 | bool disabled = false; | 357 | bool disabled = false; |
361 | 358 | ||
@@ -367,35 +364,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
367 | signal(SIGUSR1, sig_handler); | 364 | signal(SIGUSR1, sig_handler); |
368 | signal(SIGTERM, sig_handler); | 365 | signal(SIGTERM, sig_handler); |
369 | 366 | ||
370 | if (!output_name) { | ||
371 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) | ||
372 | file->is_pipe = true; | ||
373 | else | ||
374 | file->path = output_name = "perf.data"; | ||
375 | } | ||
376 | if (output_name) { | ||
377 | if (!strcmp(output_name, "-")) | ||
378 | file->is_pipe = true; | ||
379 | else if (!stat(output_name, &st) && st.st_size) { | ||
380 | char oldname[PATH_MAX]; | ||
381 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
382 | output_name); | ||
383 | unlink(oldname); | ||
384 | rename(output_name, oldname); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | flags = O_CREAT|O_RDWR|O_TRUNC; | ||
389 | |||
390 | if (file->is_pipe) | ||
391 | file->fd = STDOUT_FILENO; | ||
392 | else | ||
393 | file->fd = open(output_name, flags, S_IRUSR | S_IWUSR); | ||
394 | if (file->fd < 0) { | ||
395 | perror("failed to create output file"); | ||
396 | return -1; | ||
397 | } | ||
398 | |||
399 | session = perf_session__new(file, false, NULL); | 367 | session = perf_session__new(file, false, NULL); |
400 | if (session == NULL) { | 368 | if (session == NULL) { |
401 | pr_err("Not enough memory for reading perf file header\n"); | 369 | 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) | |||
586 | fprintf(stderr, | 554 | fprintf(stderr, |
587 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", | 555 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", |
588 | (double)rec->bytes_written / 1024.0 / 1024.0, | 556 | (double)rec->bytes_written / 1024.0 / 1024.0, |
589 | output_name, | 557 | file->path, |
590 | rec->bytes_written / 24); | 558 | rec->bytes_written / 24); |
591 | 559 | ||
592 | return 0; | 560 | return 0; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 752bebeac3aa..d934f707ee74 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -929,15 +929,8 @@ static int __cmd_top(struct perf_top *top) | |||
929 | struct perf_record_opts *opts = &top->record_opts; | 929 | struct perf_record_opts *opts = &top->record_opts; |
930 | pthread_t thread; | 930 | pthread_t thread; |
931 | int ret; | 931 | int ret; |
932 | struct perf_data_file file = { | ||
933 | .mode = PERF_DATA_MODE_WRITE, | ||
934 | }; | ||
935 | 932 | ||
936 | /* | 933 | top->session = perf_session__new(NULL, false, NULL); |
937 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | ||
938 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | ||
939 | */ | ||
940 | top->session = perf_session__new(&file, false, NULL); | ||
941 | if (top->session == NULL) | 934 | if (top->session == NULL) |
942 | return -ENOMEM; | 935 | return -ENOMEM; |
943 | 936 | ||
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c new file mode 100644 index 000000000000..7d09faf85cf1 --- /dev/null +++ b/tools/perf/util/data.c | |||
@@ -0,0 +1,120 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <linux/kernel.h> | ||
3 | #include <sys/types.h> | ||
4 | #include <sys/stat.h> | ||
5 | #include <unistd.h> | ||
6 | #include <string.h> | ||
7 | |||
8 | #include "data.h" | ||
9 | #include "util.h" | ||
10 | |||
11 | static bool check_pipe(struct perf_data_file *file) | ||
12 | { | ||
13 | struct stat st; | ||
14 | bool is_pipe = false; | ||
15 | int fd = perf_data_file__is_read(file) ? | ||
16 | STDIN_FILENO : STDOUT_FILENO; | ||
17 | |||
18 | if (!file->path) { | ||
19 | if (!fstat(fd, &st) && S_ISFIFO(st.st_mode)) | ||
20 | is_pipe = true; | ||
21 | } else { | ||
22 | if (!strcmp(file->path, "-")) | ||
23 | is_pipe = true; | ||
24 | } | ||
25 | |||
26 | if (is_pipe) | ||
27 | file->fd = fd; | ||
28 | |||
29 | return file->is_pipe = is_pipe; | ||
30 | } | ||
31 | |||
32 | static int check_backup(struct perf_data_file *file) | ||
33 | { | ||
34 | struct stat st; | ||
35 | |||
36 | if (!stat(file->path, &st) && st.st_size) { | ||
37 | /* TODO check errors properly */ | ||
38 | char oldname[PATH_MAX]; | ||
39 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
40 | file->path); | ||
41 | unlink(oldname); | ||
42 | rename(file->path, oldname); | ||
43 | } | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int open_file_read(struct perf_data_file *file) | ||
49 | { | ||
50 | struct stat st; | ||
51 | int fd; | ||
52 | |||
53 | fd = open(file->path, O_RDONLY); | ||
54 | if (fd < 0) { | ||
55 | int err = errno; | ||
56 | |||
57 | pr_err("failed to open %s: %s", file->path, strerror(err)); | ||
58 | if (err == ENOENT && !strcmp(file->path, "perf.data")) | ||
59 | pr_err(" (try 'perf record' first)"); | ||
60 | pr_err("\n"); | ||
61 | return -err; | ||
62 | } | ||
63 | |||
64 | if (fstat(fd, &st) < 0) | ||
65 | goto out_close; | ||
66 | |||
67 | if (!file->force && st.st_uid && (st.st_uid != geteuid())) { | ||
68 | pr_err("file %s not owned by current user or root\n", | ||
69 | file->path); | ||
70 | goto out_close; | ||
71 | } | ||
72 | |||
73 | if (!st.st_size) { | ||
74 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
75 | file->path); | ||
76 | goto out_close; | ||
77 | } | ||
78 | |||
79 | file->size = st.st_size; | ||
80 | return fd; | ||
81 | |||
82 | out_close: | ||
83 | close(fd); | ||
84 | return -1; | ||
85 | } | ||
86 | |||
87 | static int open_file_write(struct perf_data_file *file) | ||
88 | { | ||
89 | if (check_backup(file)) | ||
90 | return -1; | ||
91 | |||
92 | return open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); | ||
93 | } | ||
94 | |||
95 | static int open_file(struct perf_data_file *file) | ||
96 | { | ||
97 | int fd; | ||
98 | |||
99 | fd = perf_data_file__is_read(file) ? | ||
100 | open_file_read(file) : open_file_write(file); | ||
101 | |||
102 | file->fd = fd; | ||
103 | return fd < 0 ? -1 : 0; | ||
104 | } | ||
105 | |||
106 | int perf_data_file__open(struct perf_data_file *file) | ||
107 | { | ||
108 | if (check_pipe(file)) | ||
109 | return 0; | ||
110 | |||
111 | if (!file->path) | ||
112 | file->path = "perf.data"; | ||
113 | |||
114 | return open_file(file); | ||
115 | } | ||
116 | |||
117 | void perf_data_file__close(struct perf_data_file *file) | ||
118 | { | ||
119 | close(file->fd); | ||
120 | } | ||
diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index ffa0186e6000..d6c262e42f47 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h | |||
@@ -13,6 +13,7 @@ struct perf_data_file { | |||
13 | int fd; | 13 | int fd; |
14 | bool is_pipe; | 14 | bool is_pipe; |
15 | bool force; | 15 | bool force; |
16 | unsigned long size; | ||
16 | enum perf_data_mode mode; | 17 | enum perf_data_mode mode; |
17 | }; | 18 | }; |
18 | 19 | ||
@@ -26,4 +27,7 @@ static inline bool perf_data_file__is_write(struct perf_data_file *file) | |||
26 | return file->mode == PERF_DATA_MODE_WRITE; | 27 | return file->mode == PERF_DATA_MODE_WRITE; |
27 | } | 28 | } |
28 | 29 | ||
30 | int perf_data_file__open(struct perf_data_file *file); | ||
31 | void perf_data_file__close(struct perf_data_file *file); | ||
32 | |||
29 | #endif /* __PERF_DATA_H */ | 33 | #endif /* __PERF_DATA_H */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e3f63df1d57c..d857c18d2eeb 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -16,73 +16,35 @@ | |||
16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
17 | #include "vdso.h" | 17 | #include "vdso.h" |
18 | 18 | ||
19 | static int perf_session__open(struct perf_session *self, bool force) | 19 | static int perf_session__open(struct perf_session *self) |
20 | { | 20 | { |
21 | struct stat input_stat; | 21 | if (self->fd_pipe) { |
22 | |||
23 | if (!strcmp(self->filename, "-")) { | ||
24 | self->fd_pipe = true; | ||
25 | self->fd = STDIN_FILENO; | ||
26 | |||
27 | if (perf_session__read_header(self) < 0) | 22 | if (perf_session__read_header(self) < 0) |
28 | pr_err("incompatible file format (rerun with -v to learn more)"); | 23 | pr_err("incompatible file format (rerun with -v to learn more)"); |
29 | |||
30 | return 0; | 24 | return 0; |
31 | } | 25 | } |
32 | 26 | ||
33 | self->fd = open(self->filename, O_RDONLY); | ||
34 | if (self->fd < 0) { | ||
35 | int err = errno; | ||
36 | |||
37 | pr_err("failed to open %s: %s", self->filename, strerror(err)); | ||
38 | if (err == ENOENT && !strcmp(self->filename, "perf.data")) | ||
39 | pr_err(" (try 'perf record' first)"); | ||
40 | pr_err("\n"); | ||
41 | return -errno; | ||
42 | } | ||
43 | |||
44 | if (fstat(self->fd, &input_stat) < 0) | ||
45 | goto out_close; | ||
46 | |||
47 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
48 | pr_err("file %s not owned by current user or root\n", | ||
49 | self->filename); | ||
50 | goto out_close; | ||
51 | } | ||
52 | |||
53 | if (!input_stat.st_size) { | ||
54 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
55 | self->filename); | ||
56 | goto out_close; | ||
57 | } | ||
58 | |||
59 | if (perf_session__read_header(self) < 0) { | 27 | if (perf_session__read_header(self) < 0) { |
60 | pr_err("incompatible file format (rerun with -v to learn more)"); | 28 | pr_err("incompatible file format (rerun with -v to learn more)"); |
61 | goto out_close; | 29 | return -1; |
62 | } | 30 | } |
63 | 31 | ||
64 | if (!perf_evlist__valid_sample_type(self->evlist)) { | 32 | if (!perf_evlist__valid_sample_type(self->evlist)) { |
65 | pr_err("non matching sample_type"); | 33 | pr_err("non matching sample_type"); |
66 | goto out_close; | 34 | return -1; |
67 | } | 35 | } |
68 | 36 | ||
69 | if (!perf_evlist__valid_sample_id_all(self->evlist)) { | 37 | if (!perf_evlist__valid_sample_id_all(self->evlist)) { |
70 | pr_err("non matching sample_id_all"); | 38 | pr_err("non matching sample_id_all"); |
71 | goto out_close; | 39 | return -1; |
72 | } | 40 | } |
73 | 41 | ||
74 | if (!perf_evlist__valid_read_format(self->evlist)) { | 42 | if (!perf_evlist__valid_read_format(self->evlist)) { |
75 | pr_err("non matching read_format"); | 43 | pr_err("non matching read_format"); |
76 | goto out_close; | 44 | return -1; |
77 | } | 45 | } |
78 | 46 | ||
79 | self->size = input_stat.st_size; | ||
80 | return 0; | 47 | return 0; |
81 | |||
82 | out_close: | ||
83 | close(self->fd); | ||
84 | self->fd = -1; | ||
85 | return -1; | ||
86 | } | 48 | } |
87 | 49 | ||
88 | void perf_session__set_id_hdr_size(struct perf_session *session) | 50 | 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, | |||
110 | bool repipe, struct perf_tool *tool) | 72 | bool repipe, struct perf_tool *tool) |
111 | { | 73 | { |
112 | struct perf_session *self; | 74 | struct perf_session *self; |
113 | const char *filename = file->path; | ||
114 | struct stat st; | ||
115 | size_t len; | ||
116 | |||
117 | if (!filename || !strlen(filename)) { | ||
118 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) | ||
119 | filename = "-"; | ||
120 | else | ||
121 | filename = "perf.data"; | ||
122 | } | ||
123 | 75 | ||
124 | len = strlen(filename); | 76 | self = zalloc(sizeof(*self)); |
125 | self = zalloc(sizeof(*self) + len); | 77 | if (!self) |
126 | |||
127 | if (self == NULL) | ||
128 | goto out; | 78 | goto out; |
129 | 79 | ||
130 | memcpy(self->filename, filename, len); | ||
131 | self->repipe = repipe; | 80 | self->repipe = repipe; |
132 | INIT_LIST_HEAD(&self->ordered_samples.samples); | 81 | INIT_LIST_HEAD(&self->ordered_samples.samples); |
133 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 82 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); |
134 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 83 | INIT_LIST_HEAD(&self->ordered_samples.to_free); |
135 | machines__init(&self->machines); | 84 | machines__init(&self->machines); |
136 | 85 | ||
137 | if (perf_data_file__is_read(file)) { | 86 | if (file) { |
138 | if (perf_session__open(self, file->force) < 0) | 87 | if (perf_data_file__open(file)) |
139 | goto out_delete; | 88 | goto out_delete; |
140 | perf_session__set_id_hdr_size(self); | 89 | |
141 | } else if (perf_data_file__is_write(file)) { | 90 | self->fd = file->fd; |
91 | self->fd_pipe = file->is_pipe; | ||
92 | self->filename = file->path; | ||
93 | self->size = file->size; | ||
94 | |||
95 | if (perf_data_file__is_read(file)) { | ||
96 | if (perf_session__open(self) < 0) | ||
97 | goto out_close; | ||
98 | |||
99 | perf_session__set_id_hdr_size(self); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | if (!file || perf_data_file__is_write(file)) { | ||
142 | /* | 104 | /* |
143 | * In O_RDONLY mode this will be performed when reading the | 105 | * In O_RDONLY mode this will be performed when reading the |
144 | * kernel MMAP event, in perf_event__process_mmap(). | 106 | * kernel MMAP event, in perf_event__process_mmap(). |
@@ -153,10 +115,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
153 | tool->ordered_samples = false; | 115 | tool->ordered_samples = false; |
154 | } | 116 | } |
155 | 117 | ||
156 | out: | ||
157 | return self; | 118 | return self; |
158 | out_delete: | 119 | |
120 | out_close: | ||
121 | perf_data_file__close(file); | ||
122 | out_delete: | ||
159 | perf_session__delete(self); | 123 | perf_session__delete(self); |
124 | out: | ||
160 | return NULL; | 125 | return NULL; |
161 | } | 126 | } |
162 | 127 | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f2f6251fd62c..e1ca2d0ae541 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -39,7 +39,7 @@ struct perf_session { | |||
39 | bool fd_pipe; | 39 | bool fd_pipe; |
40 | bool repipe; | 40 | bool repipe; |
41 | struct ordered_samples ordered_samples; | 41 | struct ordered_samples ordered_samples; |
42 | char filename[1]; | 42 | const char *filename; |
43 | }; | 43 | }; |
44 | 44 | ||
45 | #define PRINT_IP_OPT_IP (1<<0) | 45 | #define PRINT_IP_OPT_IP (1<<0) |