blob: 59b65d0bd7c1c175225c25041d4e3c535cd3cc01 [file] [log] [blame]
Frederic Weisbecker016e92f2009-10-07 12:47:31 +02001#include "data_map.h"
2#include "symbol.h"
3#include "util.h"
4#include "debug.h"
5
6
7static struct perf_file_handler *curr_handler;
8static unsigned long mmap_window = 32;
9static char __cwd[PATH_MAX];
10
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020011static int process_event_stub(event_t *event __used)
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020012{
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020013 dump_printf(": unhandled!\n");
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020014 return 0;
15}
16
17void register_perf_file_handler(struct perf_file_handler *handler)
18{
19 if (!handler->process_sample_event)
20 handler->process_sample_event = process_event_stub;
21 if (!handler->process_mmap_event)
22 handler->process_mmap_event = process_event_stub;
23 if (!handler->process_comm_event)
24 handler->process_comm_event = process_event_stub;
25 if (!handler->process_fork_event)
26 handler->process_fork_event = process_event_stub;
27 if (!handler->process_exit_event)
28 handler->process_exit_event = process_event_stub;
29 if (!handler->process_lost_event)
30 handler->process_lost_event = process_event_stub;
31 if (!handler->process_read_event)
32 handler->process_read_event = process_event_stub;
33 if (!handler->process_throttle_event)
34 handler->process_throttle_event = process_event_stub;
35 if (!handler->process_unthrottle_event)
36 handler->process_unthrottle_event = process_event_stub;
37
38 curr_handler = handler;
39}
40
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020041static const char *event__name[] = {
42 [0] = "TOTAL",
43 [PERF_RECORD_MMAP] = "MMAP",
44 [PERF_RECORD_LOST] = "LOST",
45 [PERF_RECORD_COMM] = "COMM",
46 [PERF_RECORD_EXIT] = "EXIT",
47 [PERF_RECORD_THROTTLE] = "THROTTLE",
48 [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
49 [PERF_RECORD_FORK] = "FORK",
50 [PERF_RECORD_READ] = "READ",
51 [PERF_RECORD_SAMPLE] = "SAMPLE",
52};
53
54unsigned long event__total[PERF_RECORD_MAX];
55
56void event__print_totals(void)
57{
58 int i;
59 for (i = 0; i < PERF_RECORD_MAX; ++i)
60 pr_info("%10s events: %10ld\n",
61 event__name[i], event__total[i]);
62}
63
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020064static int
65process_event(event_t *event, unsigned long offset, unsigned long head)
66{
67 trace_event(event);
68
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020069 if (event->header.type < PERF_RECORD_MAX) {
70 dump_printf("%p [%p]: PERF_RECORD_%s",
71 (void *)(offset + head),
72 (void *)(long)(event->header.size),
73 event__name[event->header.type]);
74 ++event__total[0];
75 ++event__total[event->header.type];
76 }
77
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020078 switch (event->header.type) {
79 case PERF_RECORD_SAMPLE:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020080 return curr_handler->process_sample_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020081 case PERF_RECORD_MMAP:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020082 return curr_handler->process_mmap_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020083 case PERF_RECORD_COMM:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020084 return curr_handler->process_comm_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020085 case PERF_RECORD_FORK:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020086 return curr_handler->process_fork_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020087 case PERF_RECORD_EXIT:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020088 return curr_handler->process_exit_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020089 case PERF_RECORD_LOST:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020090 return curr_handler->process_lost_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020091 case PERF_RECORD_READ:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020092 return curr_handler->process_read_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020093 case PERF_RECORD_THROTTLE:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020094 return curr_handler->process_throttle_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020095 case PERF_RECORD_UNTHROTTLE:
Arnaldo Carvalho de Melo62daacb2009-11-27 16:29:22 -020096 return curr_handler->process_unthrottle_event(event);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +020097 default:
98 curr_handler->total_unknown++;
99 return -1;
100 }
101}
102
Arnaldo Carvalho de Melo716d69e2009-12-09 20:09:38 -0200103int perf_header__read_build_ids(int input, u64 offset, u64 size)
Arnaldo Carvalho de Melo8d063672009-11-04 18:50:43 -0200104{
Arnaldo Carvalho de Melo8d063672009-11-04 18:50:43 -0200105 struct build_id_event bev;
106 char filename[PATH_MAX];
Arnaldo Carvalho de Melo716d69e2009-12-09 20:09:38 -0200107 u64 limit = offset + size;
Arnaldo Carvalho de Melo8d063672009-11-04 18:50:43 -0200108 int err = -1;
109
Frederic Weisbecker9e827dd2009-11-11 04:51:07 +0100110 while (offset < limit) {
Arnaldo Carvalho de Melo8d063672009-11-04 18:50:43 -0200111 struct dso *dso;
112 ssize_t len;
113
114 if (read(input, &bev, sizeof(bev)) != sizeof(bev))
115 goto out;
116
117 len = bev.header.size - sizeof(bev);
118 if (read(input, filename, len) != len)
119 goto out;
120
121 dso = dsos__findnew(filename);
122 if (dso != NULL)
123 dso__set_build_id(dso, &bev.build_id);
124
125 offset += bev.header.size;
126 }
127 err = 0;
128out:
129 return err;
130}
131
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200132int mmap_dispatch_perf_file(struct perf_header **pheader,
133 const char *input_name,
134 int force,
135 int full_paths,
136 int *cwdlen,
137 char **cwd)
138{
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200139 int err;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200140 struct perf_header *header;
141 unsigned long head, shift;
142 unsigned long offset = 0;
143 struct stat input_stat;
144 size_t page_size;
145 u64 sample_type;
146 event_t *event;
147 uint32_t size;
148 int input;
149 char *buf;
150
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200151 if (curr_handler == NULL) {
152 pr_debug("Forgot to register perf file handler\n");
153 return -EINVAL;
154 }
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200155
156 page_size = getpagesize();
157
158 input = open(input_name, O_RDONLY);
159 if (input < 0) {
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200160 pr_err("Failed to open file: %s", input_name);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200161 if (!strcmp(input_name, "perf.data"))
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200162 pr_err(" (try 'perf record' first)");
163 pr_err("\n");
164 return -errno;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200165 }
166
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200167 if (fstat(input, &input_stat) < 0) {
168 pr_err("failed to stat file");
169 err = -errno;
170 goto out_close;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200171 }
172
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200173 err = -EACCES;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200174 if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200175 pr_err("file: %s not owned by current user or root\n",
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200176 input_name);
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200177 goto out_close;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200178 }
179
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200180 if (input_stat.st_size == 0) {
181 pr_info("zero-sized file, nothing to do!\n");
182 goto done;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200183 }
184
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200185 err = -ENOMEM;
Arnaldo Carvalho de Melo4dc0a042009-11-19 14:55:55 -0200186 header = perf_header__new();
187 if (header == NULL)
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200188 goto out_close;
Arnaldo Carvalho de Melo4dc0a042009-11-19 14:55:55 -0200189
190 err = perf_header__read(header, input);
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200191 if (err < 0)
192 goto out_delete;
Arnaldo Carvalho de Melo4dc0a042009-11-19 14:55:55 -0200193 *pheader = header;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200194 head = header->data_offset;
195
196 sample_type = perf_header__sample_type(header);
197
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200198 err = -EINVAL;
199 if (curr_handler->sample_type_check &&
200 curr_handler->sample_type_check(sample_type) < 0)
201 goto out_delete;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200202
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200203 if (!full_paths) {
204 if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200205 pr_err("failed to get the current directory\n");
206 err = -errno;
207 goto out_delete;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200208 }
209 *cwd = __cwd;
210 *cwdlen = strlen(*cwd);
211 } else {
212 *cwd = NULL;
213 *cwdlen = 0;
214 }
215
216 shift = page_size * (head / page_size);
217 offset += shift;
218 head -= shift;
219
220remap:
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200221 buf = mmap(NULL, page_size * mmap_window, PROT_READ,
222 MAP_SHARED, input, offset);
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200223 if (buf == MAP_FAILED) {
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200224 pr_err("failed to mmap file\n");
225 err = -errno;
226 goto out_delete;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200227 }
228
229more:
230 event = (event_t *)(buf + head);
231
232 size = event->header.size;
233 if (!size)
234 size = 8;
235
236 if (head + event->header.size >= page_size * mmap_window) {
237 int munmap_ret;
238
239 shift = page_size * (head / page_size);
240
241 munmap_ret = munmap(buf, page_size * mmap_window);
242 assert(munmap_ret == 0);
243
244 offset += shift;
245 head -= shift;
246 goto remap;
247 }
248
249 size = event->header.size;
250
251 dump_printf("\n%p [%p]: event: %d\n",
252 (void *)(offset + head),
253 (void *)(long)event->header.size,
254 event->header.type);
255
256 if (!size || process_event(event, offset, head) < 0) {
257
258 dump_printf("%p [%p]: skipping unknown header type: %d\n",
259 (void *)(offset + head),
260 (void *)(long)(event->header.size),
261 event->header.type);
262
263 /*
264 * assume we lost track of the stream, check alignment, and
265 * increment a single u64 in the hope to catch on again 'soon'.
266 */
267
268 if (unlikely(head & 7))
269 head &= ~7ULL;
270
271 size = 8;
272 }
273
274 head += size;
275
276 if (offset + head >= header->data_offset + header->data_size)
277 goto done;
278
279 if (offset + head < (unsigned long)input_stat.st_size)
280 goto more;
281
282done:
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200283 err = 0;
284out_close:
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200285 close(input);
286
Arnaldo Carvalho de Melo6b0cb5f2009-11-19 14:55:57 -0200287 return err;
288out_delete:
289 perf_header__delete(header);
290 goto out_close;
Frederic Weisbecker016e92f2009-10-07 12:47:31 +0200291}