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 }
604