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