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