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