Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
trace-event-info.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008,2009, Steven Rostedt <[email protected]>
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 }