Root/
1 | /* |
2 | * Copyright (C) 2008,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 | #include "util.h" |
22 | #include <dirent.h> |
23 | #include <mntent.h> |
24 | #include <stdio.h> |
25 | #include <stdlib.h> |
26 | #include <string.h> |
27 | #include <stdarg.h> |
28 | #include <sys/types.h> |
29 | #include <sys/stat.h> |
30 | #include <sys/wait.h> |
31 | #include <pthread.h> |
32 | #include <fcntl.h> |
33 | #include <unistd.h> |
34 | #include <errno.h> |
35 | #include <stdbool.h> |
36 | #include <linux/list.h> |
37 | #include <linux/kernel.h> |
38 | |
39 | #include "../perf.h" |
40 | #include "trace-event.h" |
41 | #include "debugfs.h" |
42 | #include "evsel.h" |
43 | |
44 | #define VERSION "0.5" |
45 | |
46 | #define TRACE_CTRL "tracing_on" |
47 | #define TRACE "trace" |
48 | #define AVAILABLE "available_tracers" |
49 | #define CURRENT "current_tracer" |
50 | #define ITER_CTRL "trace_options" |
51 | #define MAX_LATENCY "tracing_max_latency" |
52 | |
53 | unsigned int page_size; |
54 | |
55 | static const char *output_file = "trace.info"; |
56 | static int output_fd; |
57 | |
58 | struct event_list { |
59 | struct event_list *next; |
60 | const char *event; |
61 | }; |
62 | |
63 | struct events { |
64 | struct events *sibling; |
65 | struct events *children; |
66 | struct events *next; |
67 | char *name; |
68 | }; |
69 | |
70 | |
71 | static void *malloc_or_die(unsigned int size) |
72 | { |
73 | void *data; |
74 | |
75 | data = malloc(size); |
76 | if (!data) |
77 | die("malloc"); |
78 | return data; |
79 | } |
80 | |
81 | static const char *find_debugfs(void) |
82 | { |
83 | const char *path = debugfs_mount(NULL); |
84 | |
85 | if (!path) |
86 | die("Your kernel not support debugfs filesystem"); |
87 | |
88 | return path; |
89 | } |
90 | |
91 | /* |
92 | * Finds the path to the debugfs/tracing |
93 | * Allocates the string and stores it. |
94 | */ |
95 | static const char *find_tracing_dir(void) |
96 | { |
97 | static char *tracing; |
98 | static int tracing_found; |
99 | const char *debugfs; |
100 | |
101 | if (tracing_found) |
102 | return tracing; |
103 | |
104 | debugfs = find_debugfs(); |
105 | |
106 | tracing = malloc_or_die(strlen(debugfs) + 9); |
107 | |
108 | sprintf(tracing, "%s/tracing", debugfs); |
109 | |
110 | tracing_found = 1; |
111 | return tracing; |
112 | } |
113 | |
114 | static char *get_tracing_file(const char *name) |
115 | { |
116 | const char *tracing; |
117 | char *file; |
118 | |
119 | tracing = find_tracing_dir(); |
120 | if (!tracing) |
121 | return NULL; |
122 | |
123 | file = malloc_or_die(strlen(tracing) + strlen(name) + 2); |
124 | |
125 | sprintf(file, "%s/%s", tracing, name); |
126 | return file; |
127 | } |
128 | |
129 | static void put_tracing_file(char *file) |
130 | { |
131 | free(file); |
132 | } |
133 | |
134 | static ssize_t calc_data_size; |
135 | |
136 | static ssize_t write_or_die(const void *buf, size_t len) |
137 | { |
138 | int ret; |
139 | |
140 | if (calc_data_size) { |
141 | calc_data_size += len; |
142 | return len; |
143 | } |
144 | |
145 | ret = write(output_fd, buf, len); |
146 | if (ret < 0) |
147 | die("writing to '%s'", output_file); |
148 | |
149 | return ret; |
150 | } |
151 | |
152 | int bigendian(void) |
153 | { |
154 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; |
155 | unsigned int *ptr; |
156 | |
157 | ptr = (unsigned int *)(void *)str; |
158 | return *ptr == 0x01020304; |
159 | } |
160 | |
161 | /* unfortunately, you can not stat debugfs or proc files for size */ |
162 | static void record_file(const char *file, size_t hdr_sz) |
163 | { |
164 | unsigned long long size = 0; |
165 | char buf[BUFSIZ], *sizep; |
166 | off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); |
167 | int r, fd; |
168 | |
169 | fd = open(file, O_RDONLY); |
170 | if (fd < 0) |
171 | die("Can't read '%s'", file); |
172 | |
173 | /* put in zeros for file size, then fill true size later */ |
174 | if (hdr_sz) |
175 | write_or_die(&size, hdr_sz); |
176 | |
177 | do { |
178 | r = read(fd, buf, BUFSIZ); |
179 | if (r > 0) { |
180 | size += r; |
181 | write_or_die(buf, r); |
182 | } |
183 | } while (r > 0); |
184 | close(fd); |
185 | |
186 | /* ugh, handle big-endian hdr_size == 4 */ |
187 | sizep = (char*)&size; |
188 | if (bigendian()) |
189 | sizep += sizeof(u64) - hdr_sz; |
190 | |
191 | if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) |
192 | die("writing to %s", output_file); |
193 | } |
194 | |
195 | static void read_header_files(void) |
196 | { |
197 | char *path; |
198 | struct stat st; |
199 | |
200 | path = get_tracing_file("events/header_page"); |
201 | if (stat(path, &st) < 0) |
202 | die("can't read '%s'", path); |
203 | |
204 | write_or_die("header_page", 12); |
205 | record_file(path, 8); |
206 | put_tracing_file(path); |
207 | |
208 | path = get_tracing_file("events/header_event"); |
209 | if (stat(path, &st) < 0) |
210 | die("can't read '%s'", path); |
211 | |
212 | write_or_die("header_event", 13); |
213 | record_file(path, 8); |
214 | put_tracing_file(path); |
215 | } |
216 | |
217 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
218 | { |
219 | while (tps) { |
220 | if (!strcmp(sys, tps->name)) |
221 | return true; |
222 | tps = tps->next; |
223 | } |
224 | |
225 | return false; |
226 | } |
227 | |
228 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) |
229 | { |
230 | struct dirent *dent; |
231 | struct stat st; |
232 | char *format; |
233 | DIR *dir; |
234 | int count = 0; |
235 | int ret; |
236 | |
237 | dir = opendir(sys); |
238 | if (!dir) |
239 | die("can't read directory '%s'", sys); |
240 | |
241 | while ((dent = readdir(dir))) { |
242 | if (dent->d_type != DT_DIR || |
243 | strcmp(dent->d_name, ".") == 0 || |
244 | strcmp(dent->d_name, "..") == 0 || |
245 | !name_in_tp_list(dent->d_name, tps)) |
246 | continue; |
247 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); |
248 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
249 | ret = stat(format, &st); |
250 | free(format); |
251 | if (ret < 0) |
252 | continue; |
253 | count++; |
254 | } |
255 | |
256 | write_or_die(&count, 4); |
257 | |
258 | rewinddir(dir); |
259 | while ((dent = readdir(dir))) { |
260 | if (dent->d_type != DT_DIR || |
261 | strcmp(dent->d_name, ".") == 0 || |
262 | strcmp(dent->d_name, "..") == 0 || |
263 | !name_in_tp_list(dent->d_name, tps)) |
264 | continue; |
265 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); |
266 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
267 | ret = stat(format, &st); |
268 | |
269 | if (ret >= 0) |
270 | record_file(format, 8); |
271 | |
272 | free(format); |
273 | } |
274 | closedir(dir); |
275 | } |
276 | |
277 | static void read_ftrace_files(struct tracepoint_path *tps) |
278 | { |
279 | char *path; |
280 | |
281 | path = get_tracing_file("events/ftrace"); |
282 | |
283 | copy_event_system(path, tps); |
284 | |
285 | put_tracing_file(path); |
286 | } |
287 | |
288 | static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) |
289 | { |
290 | while (tps) { |
291 | if (!strcmp(sys, tps->system)) |
292 | return true; |
293 | tps = tps->next; |
294 | } |
295 | |
296 | return false; |
297 | } |
298 | |
299 | static void read_event_files(struct tracepoint_path *tps) |
300 | { |
301 | struct dirent *dent; |
302 | struct stat st; |
303 | char *path; |
304 | char *sys; |
305 | DIR *dir; |
306 | int count = 0; |
307 | int ret; |
308 | |
309 | path = get_tracing_file("events"); |
310 | |
311 | dir = opendir(path); |
312 | if (!dir) |
313 | die("can't read directory '%s'", path); |
314 | |
315 | while ((dent = readdir(dir))) { |
316 | if (dent->d_type != DT_DIR || |
317 | strcmp(dent->d_name, ".") == 0 || |
318 | strcmp(dent->d_name, "..") == 0 || |
319 | strcmp(dent->d_name, "ftrace") == 0 || |
320 | !system_in_tp_list(dent->d_name, tps)) |
321 | continue; |
322 | count++; |
323 | } |
324 | |
325 | write_or_die(&count, 4); |
326 | |
327 | rewinddir(dir); |
328 | while ((dent = readdir(dir))) { |
329 | if (dent->d_type != DT_DIR || |
330 | strcmp(dent->d_name, ".") == 0 || |
331 | strcmp(dent->d_name, "..") == 0 || |
332 | strcmp(dent->d_name, "ftrace") == 0 || |
333 | !system_in_tp_list(dent->d_name, tps)) |
334 | continue; |
335 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); |
336 | sprintf(sys, "%s/%s", path, dent->d_name); |
337 | ret = stat(sys, &st); |
338 | if (ret >= 0) { |
339 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); |
340 | copy_event_system(sys, tps); |
341 | } |
342 | free(sys); |
343 | } |
344 | |
345 | closedir(dir); |
346 | put_tracing_file(path); |
347 | } |
348 | |
349 | static void read_proc_kallsyms(void) |
350 | { |
351 | unsigned int size; |
352 | const char *path = "/proc/kallsyms"; |
353 | struct stat st; |
354 | int ret; |
355 | |
356 | ret = stat(path, &st); |
357 | if (ret < 0) { |
358 | /* not found */ |
359 | size = 0; |
360 | write_or_die(&size, 4); |
361 | return; |
362 | } |
363 | record_file(path, 4); |
364 | } |
365 | |
366 | static void read_ftrace_printk(void) |
367 | { |
368 | unsigned int size; |
369 | char *path; |
370 | struct stat st; |
371 | int ret; |
372 | |
373 | path = get_tracing_file("printk_formats"); |
374 | ret = stat(path, &st); |
375 | if (ret < 0) { |
376 | /* not found */ |
377 | size = 0; |
378 | write_or_die(&size, 4); |
379 | goto out; |
380 | } |
381 | record_file(path, 4); |
382 | |
383 | out: |
384 | put_tracing_file(path); |
385 | } |
386 | |
387 | static struct tracepoint_path * |
388 | get_tracepoints_path(struct list_head *pattrs) |
389 | { |
390 | struct tracepoint_path path, *ppath = &path; |
391 | struct perf_evsel *pos; |
392 | int nr_tracepoints = 0; |
393 | |
394 | list_for_each_entry(pos, pattrs, node) { |
395 | if (pos->attr.type != PERF_TYPE_TRACEPOINT) |
396 | continue; |
397 | ++nr_tracepoints; |
398 | ppath->next = tracepoint_id_to_path(pos->attr.config); |
399 | if (!ppath->next) |
400 | die("%s\n", "No memory to alloc tracepoints list"); |
401 | ppath = ppath->next; |
402 | } |
403 | |
404 | return nr_tracepoints > 0 ? path.next : NULL; |
405 | } |
406 | |
407 | static void |
408 | put_tracepoints_path(struct tracepoint_path *tps) |
409 | { |
410 | while (tps) { |
411 | struct tracepoint_path *t = tps; |
412 | |
413 | tps = tps->next; |
414 | free(t->name); |
415 | free(t->system); |
416 | free(t); |
417 | } |
418 | } |
419 | |
420 | bool have_tracepoints(struct list_head *pattrs) |
421 | { |
422 | struct perf_evsel *pos; |
423 | |
424 | list_for_each_entry(pos, pattrs, node) |
425 | if (pos->attr.type == PERF_TYPE_TRACEPOINT) |
426 | return true; |
427 | |
428 | return false; |
429 | } |
430 | |
431 | static void tracing_data_header(void) |
432 | { |
433 | char buf[20]; |
434 | |
435 | /* just guessing this is someone's birthday.. ;) */ |
436 | buf[0] = 23; |
437 | buf[1] = 8; |
438 | buf[2] = 68; |
439 | memcpy(buf + 3, "tracing", 7); |
440 | |
441 | write_or_die(buf, 10); |
442 | |
443 | write_or_die(VERSION, strlen(VERSION) + 1); |
444 | |
445 | /* save endian */ |
446 | if (bigendian()) |
447 | buf[0] = 1; |
448 | else |
449 | buf[0] = 0; |
450 | |
451 | read_trace_init(buf[0], buf[0]); |
452 | |
453 | write_or_die(buf, 1); |
454 | |
455 | /* save size of long */ |
456 | buf[0] = sizeof(long); |
457 | write_or_die(buf, 1); |
458 | |
459 | /* save page_size */ |
460 | page_size = sysconf(_SC_PAGESIZE); |
461 | write_or_die(&page_size, 4); |
462 | } |
463 | |
464 | struct tracing_data *tracing_data_get(struct list_head *pattrs, |
465 | int fd, bool temp) |
466 | { |
467 | struct tracepoint_path *tps; |
468 | struct tracing_data *tdata; |
469 | |
470 | output_fd = fd; |
471 | |
472 | tps = get_tracepoints_path(pattrs); |
473 | if (!tps) |
474 | return NULL; |
475 | |
476 | tdata = malloc_or_die(sizeof(*tdata)); |
477 | tdata->temp = temp; |
478 | tdata->size = 0; |
479 | |
480 | if (temp) { |
481 | int temp_fd; |
482 | |
483 | snprintf(tdata->temp_file, sizeof(tdata->temp_file), |
484 | "/tmp/perf-XXXXXX"); |
485 | if (!mkstemp(tdata->temp_file)) |
486 | die("Can't make temp file"); |
487 | |
488 | temp_fd = open(tdata->temp_file, O_RDWR); |
489 | if (temp_fd < 0) |
490 | die("Can't read '%s'", tdata->temp_file); |
491 | |
492 | /* |
493 | * Set the temp file the default output, so all the |
494 | * tracing data are stored into it. |
495 | */ |
496 | output_fd = temp_fd; |
497 | } |
498 | |
499 | tracing_data_header(); |
500 | read_header_files(); |
501 | read_ftrace_files(tps); |
502 | read_event_files(tps); |
503 | read_proc_kallsyms(); |
504 | read_ftrace_printk(); |
505 | |
506 | /* |
507 | * All tracing data are stored by now, we can restore |
508 | * the default output file in case we used temp file. |
509 | */ |
510 | if (temp) { |
511 | tdata->size = lseek(output_fd, 0, SEEK_CUR); |
512 | close(output_fd); |
513 | output_fd = fd; |
514 | } |
515 | |
516 | put_tracepoints_path(tps); |
517 | return tdata; |
518 | } |
519 | |
520 | void tracing_data_put(struct tracing_data *tdata) |
521 | { |
522 | if (tdata->temp) { |
523 | record_file(tdata->temp_file, 0); |
524 | unlink(tdata->temp_file); |
525 | } |
526 | |
527 | free(tdata); |
528 | } |
529 | |
530 | int read_tracing_data(int fd, struct list_head *pattrs) |
531 | { |
532 | struct tracing_data *tdata; |
533 | |
534 | /* |
535 | * We work over the real file, so we can write data |
536 | * directly, no temp file is needed. |
537 | */ |
538 | tdata = tracing_data_get(pattrs, fd, false); |
539 | if (!tdata) |
540 | return -ENOMEM; |
541 | |
542 | tracing_data_put(tdata); |
543 | return 0; |
544 | } |
545 |
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