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