Root/
1 | /* |
2 | * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com> |
3 | * |
4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; version 2 of the License (not later!) |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ |
21 | #define _FILE_OFFSET_BITS 64 |
22 | |
23 | #include <dirent.h> |
24 | #include <stdio.h> |
25 | #include <stdlib.h> |
26 | #include <string.h> |
27 | #include <getopt.h> |
28 | #include <stdarg.h> |
29 | #include <sys/types.h> |
30 | #include <sys/stat.h> |
31 | #include <sys/wait.h> |
32 | #include <sys/mman.h> |
33 | #include <pthread.h> |
34 | #include <fcntl.h> |
35 | #include <unistd.h> |
36 | #include <ctype.h> |
37 | #include <errno.h> |
38 | |
39 | #include "../perf.h" |
40 | #include "util.h" |
41 | #include "trace-event.h" |
42 | |
43 | static int input_fd; |
44 | |
45 | static int read_page; |
46 | |
47 | int file_bigendian; |
48 | int host_bigendian; |
49 | static int long_size; |
50 | |
51 | static unsigned long page_size; |
52 | |
53 | static ssize_t calc_data_size; |
54 | static bool repipe; |
55 | |
56 | static int do_read(int fd, void *buf, int size) |
57 | { |
58 | int rsize = size; |
59 | |
60 | while (size) { |
61 | int ret = read(fd, buf, size); |
62 | |
63 | if (ret <= 0) |
64 | return -1; |
65 | |
66 | if (repipe) { |
67 | int retw = write(STDOUT_FILENO, buf, ret); |
68 | |
69 | if (retw <= 0 || retw != ret) |
70 | die("repiping input file"); |
71 | } |
72 | |
73 | size -= ret; |
74 | buf += ret; |
75 | } |
76 | |
77 | return rsize; |
78 | } |
79 | |
80 | static int read_or_die(void *data, int size) |
81 | { |
82 | int r; |
83 | |
84 | r = do_read(input_fd, data, size); |
85 | if (r <= 0) |
86 | die("reading input file (size expected=%d received=%d)", |
87 | size, r); |
88 | |
89 | if (calc_data_size) |
90 | calc_data_size += r; |
91 | |
92 | return r; |
93 | } |
94 | |
95 | /* If it fails, the next read will report it */ |
96 | static void skip(int size) |
97 | { |
98 | char buf[BUFSIZ]; |
99 | int r; |
100 | |
101 | while (size) { |
102 | r = size > BUFSIZ ? BUFSIZ : size; |
103 | read_or_die(buf, r); |
104 | size -= r; |
105 | }; |
106 | } |
107 | |
108 | static unsigned int read4(void) |
109 | { |
110 | unsigned int data; |
111 | |
112 | read_or_die(&data, 4); |
113 | return __data2host4(data); |
114 | } |
115 | |
116 | static unsigned long long read8(void) |
117 | { |
118 | unsigned long long data; |
119 | |
120 | read_or_die(&data, 8); |
121 | return __data2host8(data); |
122 | } |
123 | |
124 | static char *read_string(void) |
125 | { |
126 | char buf[BUFSIZ]; |
127 | char *str = NULL; |
128 | int size = 0; |
129 | off_t r; |
130 | char c; |
131 | |
132 | for (;;) { |
133 | r = read(input_fd, &c, 1); |
134 | if (r < 0) |
135 | die("reading input file"); |
136 | |
137 | if (!r) |
138 | die("no data"); |
139 | |
140 | if (repipe) { |
141 | int retw = write(STDOUT_FILENO, &c, 1); |
142 | |
143 | if (retw <= 0 || retw != r) |
144 | die("repiping input file string"); |
145 | } |
146 | |
147 | buf[size++] = c; |
148 | |
149 | if (!c) |
150 | break; |
151 | } |
152 | |
153 | if (calc_data_size) |
154 | calc_data_size += size; |
155 | |
156 | str = malloc_or_die(size); |
157 | memcpy(str, buf, size); |
158 | |
159 | return str; |
160 | } |
161 | |
162 | static void read_proc_kallsyms(void) |
163 | { |
164 | unsigned int size; |
165 | char *buf; |
166 | |
167 | size = read4(); |
168 | if (!size) |
169 | return; |
170 | |
171 | buf = malloc_or_die(size + 1); |
172 | read_or_die(buf, size); |
173 | buf[size] = '\0'; |
174 | |
175 | parse_proc_kallsyms(buf, size); |
176 | |
177 | free(buf); |
178 | } |
179 | |
180 | static void read_ftrace_printk(void) |
181 | { |
182 | unsigned int size; |
183 | char *buf; |
184 | |
185 | size = read4(); |
186 | if (!size) |
187 | return; |
188 | |
189 | buf = malloc_or_die(size); |
190 | read_or_die(buf, size); |
191 | |
192 | parse_ftrace_printk(buf, size); |
193 | |
194 | free(buf); |
195 | } |
196 | |
197 | static void read_header_files(void) |
198 | { |
199 | unsigned long long size; |
200 | char *header_event; |
201 | char buf[BUFSIZ]; |
202 | |
203 | read_or_die(buf, 12); |
204 | |
205 | if (memcmp(buf, "header_page", 12) != 0) |
206 | die("did not read header page"); |
207 | |
208 | size = read8(); |
209 | skip(size); |
210 | |
211 | /* |
212 | * The size field in the page is of type long, |
213 | * use that instead, since it represents the kernel. |
214 | */ |
215 | long_size = header_page_size_size; |
216 | |
217 | read_or_die(buf, 13); |
218 | if (memcmp(buf, "header_event", 13) != 0) |
219 | die("did not read header event"); |
220 | |
221 | size = read8(); |
222 | header_event = malloc_or_die(size); |
223 | read_or_die(header_event, size); |
224 | free(header_event); |
225 | } |
226 | |
227 | static void read_ftrace_file(unsigned long long size) |
228 | { |
229 | char *buf; |
230 | |
231 | buf = malloc_or_die(size); |
232 | read_or_die(buf, size); |
233 | parse_ftrace_file(buf, size); |
234 | free(buf); |
235 | } |
236 | |
237 | static void read_event_file(char *sys, unsigned long long size) |
238 | { |
239 | char *buf; |
240 | |
241 | buf = malloc_or_die(size); |
242 | read_or_die(buf, size); |
243 | parse_event_file(buf, size, sys); |
244 | free(buf); |
245 | } |
246 | |
247 | static void read_ftrace_files(void) |
248 | { |
249 | unsigned long long size; |
250 | int count; |
251 | int i; |
252 | |
253 | count = read4(); |
254 | |
255 | for (i = 0; i < count; i++) { |
256 | size = read8(); |
257 | read_ftrace_file(size); |
258 | } |
259 | } |
260 | |
261 | static void read_event_files(void) |
262 | { |
263 | unsigned long long size; |
264 | char *sys; |
265 | int systems; |
266 | int count; |
267 | int i,x; |
268 | |
269 | systems = read4(); |
270 | |
271 | for (i = 0; i < systems; i++) { |
272 | sys = read_string(); |
273 | |
274 | count = read4(); |
275 | for (x=0; x < count; x++) { |
276 | size = read8(); |
277 | read_event_file(sys, size); |
278 | } |
279 | } |
280 | } |
281 | |
282 | struct cpu_data { |
283 | unsigned long long offset; |
284 | unsigned long long size; |
285 | unsigned long long timestamp; |
286 | struct record *next; |
287 | char *page; |
288 | int cpu; |
289 | int index; |
290 | int page_size; |
291 | }; |
292 | |
293 | static struct cpu_data *cpu_data; |
294 | |
295 | static void update_cpu_data_index(int cpu) |
296 | { |
297 | cpu_data[cpu].offset += page_size; |
298 | cpu_data[cpu].size -= page_size; |
299 | cpu_data[cpu].index = 0; |
300 | } |
301 | |
302 | static void get_next_page(int cpu) |
303 | { |
304 | off_t save_seek; |
305 | off_t ret; |
306 | |
307 | if (!cpu_data[cpu].page) |
308 | return; |
309 | |
310 | if (read_page) { |
311 | if (cpu_data[cpu].size <= page_size) { |
312 | free(cpu_data[cpu].page); |
313 | cpu_data[cpu].page = NULL; |
314 | return; |
315 | } |
316 | |
317 | update_cpu_data_index(cpu); |
318 | |
319 | /* other parts of the code may expect the pointer to not move */ |
320 | save_seek = lseek(input_fd, 0, SEEK_CUR); |
321 | |
322 | ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET); |
323 | if (ret == (off_t)-1) |
324 | die("failed to lseek"); |
325 | ret = read(input_fd, cpu_data[cpu].page, page_size); |
326 | if (ret < 0) |
327 | die("failed to read page"); |
328 | |
329 | /* reset the file pointer back */ |
330 | lseek(input_fd, save_seek, SEEK_SET); |
331 | |
332 | return; |
333 | } |
334 | |
335 | munmap(cpu_data[cpu].page, page_size); |
336 | cpu_data[cpu].page = NULL; |
337 | |
338 | if (cpu_data[cpu].size <= page_size) |
339 | return; |
340 | |
341 | update_cpu_data_index(cpu); |
342 | |
343 | cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE, |
344 | input_fd, cpu_data[cpu].offset); |
345 | if (cpu_data[cpu].page == MAP_FAILED) |
346 | die("failed to mmap cpu %d at offset 0x%llx", |
347 | cpu, cpu_data[cpu].offset); |
348 | } |
349 | |
350 | static unsigned int type_len4host(unsigned int type_len_ts) |
351 | { |
352 | if (file_bigendian) |
353 | return (type_len_ts >> 27) & ((1 << 5) - 1); |
354 | else |
355 | return type_len_ts & ((1 << 5) - 1); |
356 | } |
357 | |
358 | static unsigned int ts4host(unsigned int type_len_ts) |
359 | { |
360 | if (file_bigendian) |
361 | return type_len_ts & ((1 << 27) - 1); |
362 | else |
363 | return type_len_ts >> 5; |
364 | } |
365 | |
366 | static int calc_index(void *ptr, int cpu) |
367 | { |
368 | return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page; |
369 | } |
370 | |
371 | struct record *trace_peek_data(int cpu) |
372 | { |
373 | struct record *data; |
374 | void *page = cpu_data[cpu].page; |
375 | int idx = cpu_data[cpu].index; |
376 | void *ptr = page + idx; |
377 | unsigned long long extend; |
378 | unsigned int type_len_ts; |
379 | unsigned int type_len; |
380 | unsigned int delta; |
381 | unsigned int length = 0; |
382 | |
383 | if (cpu_data[cpu].next) |
384 | return cpu_data[cpu].next; |
385 | |
386 | if (!page) |
387 | return NULL; |
388 | |
389 | if (!idx) { |
390 | /* FIXME: handle header page */ |
391 | if (header_page_ts_size != 8) |
392 | die("expected a long long type for timestamp"); |
393 | cpu_data[cpu].timestamp = data2host8(ptr); |
394 | ptr += 8; |
395 | switch (header_page_size_size) { |
396 | case 4: |
397 | cpu_data[cpu].page_size = data2host4(ptr); |
398 | ptr += 4; |
399 | break; |
400 | case 8: |
401 | cpu_data[cpu].page_size = data2host8(ptr); |
402 | ptr += 8; |
403 | break; |
404 | default: |
405 | die("bad long size"); |
406 | } |
407 | ptr = cpu_data[cpu].page + header_page_data_offset; |
408 | } |
409 | |
410 | read_again: |
411 | idx = calc_index(ptr, cpu); |
412 | |
413 | if (idx >= cpu_data[cpu].page_size) { |
414 | get_next_page(cpu); |
415 | return trace_peek_data(cpu); |
416 | } |
417 | |
418 | type_len_ts = data2host4(ptr); |
419 | ptr += 4; |
420 | |
421 | type_len = type_len4host(type_len_ts); |
422 | delta = ts4host(type_len_ts); |
423 | |
424 | switch (type_len) { |
425 | case RINGBUF_TYPE_PADDING: |
426 | if (!delta) |
427 | die("error, hit unexpected end of page"); |
428 | length = data2host4(ptr); |
429 | ptr += 4; |
430 | length *= 4; |
431 | ptr += length; |
432 | goto read_again; |
433 | |
434 | case RINGBUF_TYPE_TIME_EXTEND: |
435 | extend = data2host4(ptr); |
436 | ptr += 4; |
437 | extend <<= TS_SHIFT; |
438 | extend += delta; |
439 | cpu_data[cpu].timestamp += extend; |
440 | goto read_again; |
441 | |
442 | case RINGBUF_TYPE_TIME_STAMP: |
443 | ptr += 12; |
444 | break; |
445 | case 0: |
446 | length = data2host4(ptr); |
447 | ptr += 4; |
448 | die("here! length=%d", length); |
449 | break; |
450 | default: |
451 | length = type_len * 4; |
452 | break; |
453 | } |
454 | |
455 | cpu_data[cpu].timestamp += delta; |
456 | |
457 | data = malloc_or_die(sizeof(*data)); |
458 | memset(data, 0, sizeof(*data)); |
459 | |
460 | data->ts = cpu_data[cpu].timestamp; |
461 | data->size = length; |
462 | data->data = ptr; |
463 | ptr += length; |
464 | |
465 | cpu_data[cpu].index = calc_index(ptr, cpu); |
466 | cpu_data[cpu].next = data; |
467 | |
468 | return data; |
469 | } |
470 | |
471 | struct record *trace_read_data(int cpu) |
472 | { |
473 | struct record *data; |
474 | |
475 | data = trace_peek_data(cpu); |
476 | cpu_data[cpu].next = NULL; |
477 | |
478 | return data; |
479 | } |
480 | |
481 | ssize_t trace_report(int fd, bool __repipe) |
482 | { |
483 | char buf[BUFSIZ]; |
484 | char test[] = { 23, 8, 68 }; |
485 | char *version; |
486 | int show_version = 0; |
487 | int show_funcs = 0; |
488 | int show_printk = 0; |
489 | ssize_t size; |
490 | |
491 | calc_data_size = 1; |
492 | repipe = __repipe; |
493 | |
494 | input_fd = fd; |
495 | |
496 | read_or_die(buf, 3); |
497 | if (memcmp(buf, test, 3) != 0) |
498 | die("no trace data in the file"); |
499 | |
500 | read_or_die(buf, 7); |
501 | if (memcmp(buf, "tracing", 7) != 0) |
502 | die("not a trace file (missing 'tracing' tag)"); |
503 | |
504 | version = read_string(); |
505 | if (show_version) |
506 | printf("version = %s\n", version); |
507 | free(version); |
508 | |
509 | read_or_die(buf, 1); |
510 | file_bigendian = buf[0]; |
511 | host_bigendian = bigendian(); |
512 | |
513 | read_or_die(buf, 1); |
514 | long_size = buf[0]; |
515 | |
516 | page_size = read4(); |
517 | |
518 | read_header_files(); |
519 | |
520 | read_ftrace_files(); |
521 | read_event_files(); |
522 | read_proc_kallsyms(); |
523 | read_ftrace_printk(); |
524 | |
525 | size = calc_data_size - 1; |
526 | calc_data_size = 0; |
527 | repipe = false; |
528 | |
529 | if (show_funcs) { |
530 | print_funcs(); |
531 | return size; |
532 | } |
533 | if (show_printk) { |
534 | print_printk(); |
535 | return size; |
536 | } |
537 | |
538 | return size; |
539 | } |
540 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9