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