1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
4  */
5 #include <dirent.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdarg.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/wait.h>
13 #include <sys/mman.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <errno.h>
17 
18 #include "../perf.h"
19 #include "util.h"
20 #include "trace-event.h"
21 #include "debug.h"
22 
23 static int input_fd;
24 
25 static ssize_t trace_data_size;
26 static bool repipe;
27 
28 static int __do_read(int fd, void *buf, int size)
29 {
30 	int rsize = size;
31 
32 	while (size) {
33 		int ret = read(fd, buf, size);
34 
35 		if (ret <= 0)
36 			return -1;
37 
38 		if (repipe) {
39 			int retw = write(STDOUT_FILENO, buf, ret);
40 
41 			if (retw <= 0 || retw != ret) {
42 				pr_debug("repiping input file");
43 				return -1;
44 			}
45 		}
46 
47 		size -= ret;
48 		buf += ret;
49 	}
50 
51 	return rsize;
52 }
53 
54 static int do_read(void *data, int size)
55 {
56 	int r;
57 
58 	r = __do_read(input_fd, data, size);
59 	if (r <= 0) {
60 		pr_debug("reading input file (size expected=%d received=%d)",
61 			 size, r);
62 		return -1;
63 	}
64 
65 	trace_data_size += r;
66 
67 	return r;
68 }
69 
70 /* If it fails, the next read will report it */
71 static void skip(int size)
72 {
73 	char buf[BUFSIZ];
74 	int r;
75 
76 	while (size) {
77 		r = size > BUFSIZ ? BUFSIZ : size;
78 		do_read(buf, r);
79 		size -= r;
80 	};
81 }
82 
83 static unsigned int read4(struct tep_handle *pevent)
84 {
85 	unsigned int data;
86 
87 	if (do_read(&data, 4) < 0)
88 		return 0;
89 	return tep_read_number(pevent, &data, 4);
90 }
91 
92 static unsigned long long read8(struct tep_handle *pevent)
93 {
94 	unsigned long long data;
95 
96 	if (do_read(&data, 8) < 0)
97 		return 0;
98 	return tep_read_number(pevent, &data, 8);
99 }
100 
101 static char *read_string(void)
102 {
103 	char buf[BUFSIZ];
104 	char *str = NULL;
105 	int size = 0;
106 	off_t r;
107 	char c;
108 
109 	for (;;) {
110 		r = read(input_fd, &c, 1);
111 		if (r < 0) {
112 			pr_debug("reading input file");
113 			goto out;
114 		}
115 
116 		if (!r) {
117 			pr_debug("no data");
118 			goto out;
119 		}
120 
121 		if (repipe) {
122 			int retw = write(STDOUT_FILENO, &c, 1);
123 
124 			if (retw <= 0 || retw != r) {
125 				pr_debug("repiping input file string");
126 				goto out;
127 			}
128 		}
129 
130 		buf[size++] = c;
131 
132 		if (!c)
133 			break;
134 	}
135 
136 	trace_data_size += size;
137 
138 	str = malloc(size);
139 	if (str)
140 		memcpy(str, buf, size);
141 out:
142 	return str;
143 }
144 
145 static int read_proc_kallsyms(struct tep_handle *pevent)
146 {
147 	unsigned int size;
148 
149 	size = read4(pevent);
150 	if (!size)
151 		return 0;
152 	/*
153 	 * Just skip it, now that we configure libtraceevent to use the
154 	 * tools/perf/ symbol resolver.
155 	 *
156 	 * We need to skip it so that we can continue parsing old perf.data
157 	 * files, that contains this /proc/kallsyms payload.
158 	 *
159 	 * Newer perf.data files will have just the 4-bytes zeros "kallsyms
160 	 * payload", so that older tools can continue reading it and interpret
161 	 * it as "no kallsyms payload is present".
162 	 */
163 	lseek(input_fd, size, SEEK_CUR);
164 	trace_data_size += size;
165 	return 0;
166 }
167 
168 static int read_ftrace_printk(struct tep_handle *pevent)
169 {
170 	unsigned int size;
171 	char *buf;
172 
173 	/* it can have 0 size */
174 	size = read4(pevent);
175 	if (!size)
176 		return 0;
177 
178 	buf = malloc(size + 1);
179 	if (buf == NULL)
180 		return -1;
181 
182 	if (do_read(buf, size) < 0) {
183 		free(buf);
184 		return -1;
185 	}
186 
187 	buf[size] = '\0';
188 
189 	parse_ftrace_printk(pevent, buf, size);
190 
191 	free(buf);
192 	return 0;
193 }
194 
195 static int read_header_files(struct tep_handle *pevent)
196 {
197 	unsigned long long size;
198 	char *header_page;
199 	char buf[BUFSIZ];
200 	int ret = 0;
201 
202 	if (do_read(buf, 12) < 0)
203 		return -1;
204 
205 	if (memcmp(buf, "header_page", 12) != 0) {
206 		pr_debug("did not read header page");
207 		return -1;
208 	}
209 
210 	size = read8(pevent);
211 
212 	header_page = malloc(size);
213 	if (header_page == NULL)
214 		return -1;
215 
216 	if (do_read(header_page, size) < 0) {
217 		pr_debug("did not read header page");
218 		free(header_page);
219 		return -1;
220 	}
221 
222 	if (!tep_parse_header_page(pevent, header_page, size,
223 				   tep_get_long_size(pevent))) {
224 		/*
225 		 * The commit field in the page is of type long,
226 		 * use that instead, since it represents the kernel.
227 		 */
228 		tep_set_long_size(pevent, tep_get_header_page_size(pevent));
229 	}
230 	free(header_page);
231 
232 	if (do_read(buf, 13) < 0)
233 		return -1;
234 
235 	if (memcmp(buf, "header_event", 13) != 0) {
236 		pr_debug("did not read header event");
237 		return -1;
238 	}
239 
240 	size = read8(pevent);
241 	skip(size);
242 
243 	return ret;
244 }
245 
246 static int read_ftrace_file(struct tep_handle *pevent, unsigned long long size)
247 {
248 	int ret;
249 	char *buf;
250 
251 	buf = malloc(size);
252 	if (buf == NULL) {
253 		pr_debug("memory allocation failure\n");
254 		return -1;
255 	}
256 
257 	ret = do_read(buf, size);
258 	if (ret < 0) {
259 		pr_debug("error reading ftrace file.\n");
260 		goto out;
261 	}
262 
263 	ret = parse_ftrace_file(pevent, buf, size);
264 	if (ret < 0)
265 		pr_debug("error parsing ftrace file.\n");
266 out:
267 	free(buf);
268 	return ret;
269 }
270 
271 static int read_event_file(struct tep_handle *pevent, char *sys,
272 			   unsigned long long size)
273 {
274 	int ret;
275 	char *buf;
276 
277 	buf = malloc(size);
278 	if (buf == NULL) {
279 		pr_debug("memory allocation failure\n");
280 		return -1;
281 	}
282 
283 	ret = do_read(buf, size);
284 	if (ret < 0)
285 		goto out;
286 
287 	ret = parse_event_file(pevent, buf, size, sys);
288 	if (ret < 0)
289 		pr_debug("error parsing event file.\n");
290 out:
291 	free(buf);
292 	return ret;
293 }
294 
295 static int read_ftrace_files(struct tep_handle *pevent)
296 {
297 	unsigned long long size;
298 	int count;
299 	int i;
300 	int ret;
301 
302 	count = read4(pevent);
303 
304 	for (i = 0; i < count; i++) {
305 		size = read8(pevent);
306 		ret = read_ftrace_file(pevent, size);
307 		if (ret)
308 			return ret;
309 	}
310 	return 0;
311 }
312 
313 static int read_event_files(struct tep_handle *pevent)
314 {
315 	unsigned long long size;
316 	char *sys;
317 	int systems;
318 	int count;
319 	int i,x;
320 	int ret;
321 
322 	systems = read4(pevent);
323 
324 	for (i = 0; i < systems; i++) {
325 		sys = read_string();
326 		if (sys == NULL)
327 			return -1;
328 
329 		count = read4(pevent);
330 
331 		for (x=0; x < count; x++) {
332 			size = read8(pevent);
333 			ret = read_event_file(pevent, sys, size);
334 			if (ret) {
335 				free(sys);
336 				return ret;
337 			}
338 		}
339 		free(sys);
340 	}
341 	return 0;
342 }
343 
344 static int read_saved_cmdline(struct tep_handle *pevent)
345 {
346 	unsigned long long size;
347 	char *buf;
348 	int ret;
349 
350 	/* it can have 0 size */
351 	size = read8(pevent);
352 	if (!size)
353 		return 0;
354 
355 	buf = malloc(size + 1);
356 	if (buf == NULL) {
357 		pr_debug("memory allocation failure\n");
358 		return -1;
359 	}
360 
361 	ret = do_read(buf, size);
362 	if (ret < 0) {
363 		pr_debug("error reading saved cmdlines\n");
364 		goto out;
365 	}
366 
367 	parse_saved_cmdline(pevent, buf, size);
368 	ret = 0;
369 out:
370 	free(buf);
371 	return ret;
372 }
373 
374 ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
375 {
376 	char buf[BUFSIZ];
377 	char test[] = { 23, 8, 68 };
378 	char *version;
379 	int show_version = 0;
380 	int show_funcs = 0;
381 	int show_printk = 0;
382 	ssize_t size = -1;
383 	int file_bigendian;
384 	int host_bigendian;
385 	int file_long_size;
386 	int file_page_size;
387 	struct tep_handle *pevent = NULL;
388 	int err;
389 
390 	repipe = __repipe;
391 	input_fd = fd;
392 
393 	if (do_read(buf, 3) < 0)
394 		return -1;
395 	if (memcmp(buf, test, 3) != 0) {
396 		pr_debug("no trace data in the file");
397 		return -1;
398 	}
399 
400 	if (do_read(buf, 7) < 0)
401 		return -1;
402 	if (memcmp(buf, "tracing", 7) != 0) {
403 		pr_debug("not a trace file (missing 'tracing' tag)");
404 		return -1;
405 	}
406 
407 	version = read_string();
408 	if (version == NULL)
409 		return -1;
410 	if (show_version)
411 		printf("version = %s\n", version);
412 
413 	if (do_read(buf, 1) < 0) {
414 		free(version);
415 		return -1;
416 	}
417 	file_bigendian = buf[0];
418 	host_bigendian = bigendian();
419 
420 	if (trace_event__init(tevent)) {
421 		pr_debug("trace_event__init failed");
422 		goto out;
423 	}
424 
425 	pevent = tevent->pevent;
426 
427 	tep_set_flag(pevent, TEP_NSEC_OUTPUT);
428 	tep_set_file_bigendian(pevent, file_bigendian);
429 	tep_set_local_bigendian(pevent, host_bigendian);
430 
431 	if (do_read(buf, 1) < 0)
432 		goto out;
433 	file_long_size = buf[0];
434 
435 	file_page_size = read4(pevent);
436 	if (!file_page_size)
437 		goto out;
438 
439 	tep_set_long_size(pevent, file_long_size);
440 	tep_set_page_size(pevent, file_page_size);
441 
442 	err = read_header_files(pevent);
443 	if (err)
444 		goto out;
445 	err = read_ftrace_files(pevent);
446 	if (err)
447 		goto out;
448 	err = read_event_files(pevent);
449 	if (err)
450 		goto out;
451 	err = read_proc_kallsyms(pevent);
452 	if (err)
453 		goto out;
454 	err = read_ftrace_printk(pevent);
455 	if (err)
456 		goto out;
457 	if (atof(version) >= 0.6) {
458 		err = read_saved_cmdline(pevent);
459 		if (err)
460 			goto out;
461 	}
462 
463 	size = trace_data_size;
464 	repipe = false;
465 
466 	if (show_funcs) {
467 		tep_print_funcs(pevent);
468 	} else if (show_printk) {
469 		tep_print_printk(pevent);
470 	}
471 
472 	pevent = NULL;
473 
474 out:
475 	if (pevent)
476 		trace_event__cleanup(tevent);
477 	free(version);
478 	return size;
479 }
480