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