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 "trace-event.h"
19 #include "debug.h"
20 
21 static int input_fd;
22 
23 static ssize_t trace_data_size;
24 static bool repipe;
25 
26 static int __do_read(int fd, void *buf, int size)
27 {
28 	int rsize = size;
29 
30 	while (size) {
31 		int ret = read(fd, buf, size);
32 
33 		if (ret <= 0)
34 			return -1;
35 
36 		if (repipe) {
37 			int retw = write(STDOUT_FILENO, buf, ret);
38 
39 			if (retw <= 0 || retw != ret) {
40 				pr_debug("repiping input file");
41 				return -1;
42 			}
43 		}
44 
45 		size -= ret;
46 		buf += ret;
47 	}
48 
49 	return rsize;
50 }
51 
52 static int do_read(void *data, int size)
53 {
54 	int r;
55 
56 	r = __do_read(input_fd, data, size);
57 	if (r <= 0) {
58 		pr_debug("reading input file (size expected=%d received=%d)",
59 			 size, r);
60 		return -1;
61 	}
62 
63 	trace_data_size += r;
64 
65 	return r;
66 }
67 
68 /* If it fails, the next read will report it */
69 static void skip(int size)
70 {
71 	char buf[BUFSIZ];
72 	int r;
73 
74 	while (size) {
75 		r = size > BUFSIZ ? BUFSIZ : size;
76 		do_read(buf, r);
77 		size -= r;
78 	}
79 }
80 
81 static unsigned int read4(struct tep_handle *pevent)
82 {
83 	unsigned int data;
84 
85 	if (do_read(&data, 4) < 0)
86 		return 0;
87 	return tep_read_number(pevent, &data, 4);
88 }
89 
90 static unsigned long long read8(struct tep_handle *pevent)
91 {
92 	unsigned long long data;
93 
94 	if (do_read(&data, 8) < 0)
95 		return 0;
96 	return tep_read_number(pevent, &data, 8);
97 }
98 
99 static char *read_string(void)
100 {
101 	char buf[BUFSIZ];
102 	char *str = NULL;
103 	int size = 0;
104 	off_t r;
105 	char c;
106 
107 	for (;;) {
108 		r = read(input_fd, &c, 1);
109 		if (r < 0) {
110 			pr_debug("reading input file");
111 			goto out;
112 		}
113 
114 		if (!r) {
115 			pr_debug("no data");
116 			goto out;
117 		}
118 
119 		if (repipe) {
120 			int retw = write(STDOUT_FILENO, &c, 1);
121 
122 			if (retw <= 0 || retw != r) {
123 				pr_debug("repiping input file string");
124 				goto out;
125 			}
126 		}
127 
128 		buf[size++] = c;
129 
130 		if (!c)
131 			break;
132 	}
133 
134 	trace_data_size += size;
135 
136 	str = malloc(size);
137 	if (str)
138 		memcpy(str, buf, size);
139 out:
140 	return str;
141 }
142 
143 static int read_proc_kallsyms(struct tep_handle *pevent)
144 {
145 	unsigned int size;
146 
147 	size = read4(pevent);
148 	if (!size)
149 		return 0;
150 	/*
151 	 * Just skip it, now that we configure libtraceevent to use the
152 	 * tools/perf/ symbol resolver.
153 	 *
154 	 * We need to skip it so that we can continue parsing old perf.data
155 	 * files, that contains this /proc/kallsyms payload.
156 	 *
157 	 * Newer perf.data files will have just the 4-bytes zeros "kallsyms
158 	 * payload", so that older tools can continue reading it and interpret
159 	 * it as "no kallsyms payload is present".
160 	 */
161 	lseek(input_fd, size, SEEK_CUR);
162 	trace_data_size += size;
163 	return 0;
164 }
165 
166 static int read_ftrace_printk(struct tep_handle *pevent)
167 {
168 	unsigned int size;
169 	char *buf;
170 
171 	/* it can have 0 size */
172 	size = read4(pevent);
173 	if (!size)
174 		return 0;
175 
176 	buf = malloc(size + 1);
177 	if (buf == NULL)
178 		return -1;
179 
180 	if (do_read(buf, size) < 0) {
181 		free(buf);
182 		return -1;
183 	}
184 
185 	buf[size] = '\0';
186 
187 	parse_ftrace_printk(pevent, buf, size);
188 
189 	free(buf);
190 	return 0;
191 }
192 
193 static int read_header_files(struct tep_handle *pevent)
194 {
195 	unsigned long long size;
196 	char *header_page;
197 	char buf[BUFSIZ];
198 	int ret = 0;
199 
200 	if (do_read(buf, 12) < 0)
201 		return -1;
202 
203 	if (memcmp(buf, "header_page", 12) != 0) {
204 		pr_debug("did not read header page");
205 		return -1;
206 	}
207 
208 	size = read8(pevent);
209 
210 	header_page = malloc(size);
211 	if (header_page == NULL)
212 		return -1;
213 
214 	if (do_read(header_page, size) < 0) {
215 		pr_debug("did not read header page");
216 		free(header_page);
217 		return -1;
218 	}
219 
220 	if (!tep_parse_header_page(pevent, header_page, size,
221 				   tep_get_long_size(pevent))) {
222 		/*
223 		 * The commit field in the page is of type long,
224 		 * use that instead, since it represents the kernel.
225 		 */
226 		tep_set_long_size(pevent, tep_get_header_page_size(pevent));
227 	}
228 	free(header_page);
229 
230 	if (do_read(buf, 13) < 0)
231 		return -1;
232 
233 	if (memcmp(buf, "header_event", 13) != 0) {
234 		pr_debug("did not read header event");
235 		return -1;
236 	}
237 
238 	size = read8(pevent);
239 	skip(size);
240 
241 	return ret;
242 }
243 
244 static int read_ftrace_file(struct tep_handle *pevent, unsigned long long size)
245 {
246 	int ret;
247 	char *buf;
248 
249 	buf = malloc(size);
250 	if (buf == NULL) {
251 		pr_debug("memory allocation failure\n");
252 		return -1;
253 	}
254 
255 	ret = do_read(buf, size);
256 	if (ret < 0) {
257 		pr_debug("error reading ftrace file.\n");
258 		goto out;
259 	}
260 
261 	ret = parse_ftrace_file(pevent, buf, size);
262 	if (ret < 0)
263 		pr_debug("error parsing ftrace file.\n");
264 out:
265 	free(buf);
266 	return ret;
267 }
268 
269 static int read_event_file(struct tep_handle *pevent, char *sys,
270 			   unsigned long long size)
271 {
272 	int ret;
273 	char *buf;
274 
275 	buf = malloc(size);
276 	if (buf == NULL) {
277 		pr_debug("memory allocation failure\n");
278 		return -1;
279 	}
280 
281 	ret = do_read(buf, size);
282 	if (ret < 0)
283 		goto out;
284 
285 	ret = parse_event_file(pevent, buf, size, sys);
286 	if (ret < 0)
287 		pr_debug("error parsing event file.\n");
288 out:
289 	free(buf);
290 	return ret;
291 }
292 
293 static int read_ftrace_files(struct tep_handle *pevent)
294 {
295 	unsigned long long size;
296 	int count;
297 	int i;
298 	int ret;
299 
300 	count = read4(pevent);
301 
302 	for (i = 0; i < count; i++) {
303 		size = read8(pevent);
304 		ret = read_ftrace_file(pevent, size);
305 		if (ret)
306 			return ret;
307 	}
308 	return 0;
309 }
310 
311 static int read_event_files(struct tep_handle *pevent)
312 {
313 	unsigned long long size;
314 	char *sys;
315 	int systems;
316 	int count;
317 	int i,x;
318 	int ret;
319 
320 	systems = read4(pevent);
321 
322 	for (i = 0; i < systems; i++) {
323 		sys = read_string();
324 		if (sys == NULL)
325 			return -1;
326 
327 		count = read4(pevent);
328 
329 		for (x=0; x < count; x++) {
330 			size = read8(pevent);
331 			ret = read_event_file(pevent, sys, size);
332 			if (ret) {
333 				free(sys);
334 				return ret;
335 			}
336 		}
337 		free(sys);
338 	}
339 	return 0;
340 }
341 
342 static int read_saved_cmdline(struct tep_handle *pevent)
343 {
344 	unsigned long long size;
345 	char *buf;
346 	int ret;
347 
348 	/* it can have 0 size */
349 	size = read8(pevent);
350 	if (!size)
351 		return 0;
352 
353 	buf = malloc(size + 1);
354 	if (buf == NULL) {
355 		pr_debug("memory allocation failure\n");
356 		return -1;
357 	}
358 
359 	ret = do_read(buf, size);
360 	if (ret < 0) {
361 		pr_debug("error reading saved cmdlines\n");
362 		goto out;
363 	}
364 	buf[ret] = '\0';
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