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