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 
365 	parse_saved_cmdline(pevent, buf, size);
366 	ret = 0;
367 out:
368 	free(buf);
369 	return ret;
370 }
371 
372 ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
373 {
374 	char buf[BUFSIZ];
375 	char test[] = { 23, 8, 68 };
376 	char *version;
377 	int show_version = 0;
378 	int show_funcs = 0;
379 	int show_printk = 0;
380 	ssize_t size = -1;
381 	int file_bigendian;
382 	int host_bigendian;
383 	int file_long_size;
384 	int file_page_size;
385 	struct tep_handle *pevent = NULL;
386 	int err;
387 
388 	repipe = __repipe;
389 	input_fd = fd;
390 
391 	if (do_read(buf, 3) < 0)
392 		return -1;
393 	if (memcmp(buf, test, 3) != 0) {
394 		pr_debug("no trace data in the file");
395 		return -1;
396 	}
397 
398 	if (do_read(buf, 7) < 0)
399 		return -1;
400 	if (memcmp(buf, "tracing", 7) != 0) {
401 		pr_debug("not a trace file (missing 'tracing' tag)");
402 		return -1;
403 	}
404 
405 	version = read_string();
406 	if (version == NULL)
407 		return -1;
408 	if (show_version)
409 		printf("version = %s\n", version);
410 
411 	if (do_read(buf, 1) < 0) {
412 		free(version);
413 		return -1;
414 	}
415 	file_bigendian = buf[0];
416 	host_bigendian = bigendian();
417 
418 	if (trace_event__init(tevent)) {
419 		pr_debug("trace_event__init failed");
420 		goto out;
421 	}
422 
423 	pevent = tevent->pevent;
424 
425 	tep_set_flag(pevent, TEP_NSEC_OUTPUT);
426 	tep_set_file_bigendian(pevent, file_bigendian);
427 	tep_set_local_bigendian(pevent, host_bigendian);
428 
429 	if (do_read(buf, 1) < 0)
430 		goto out;
431 	file_long_size = buf[0];
432 
433 	file_page_size = read4(pevent);
434 	if (!file_page_size)
435 		goto out;
436 
437 	tep_set_long_size(pevent, file_long_size);
438 	tep_set_page_size(pevent, file_page_size);
439 
440 	err = read_header_files(pevent);
441 	if (err)
442 		goto out;
443 	err = read_ftrace_files(pevent);
444 	if (err)
445 		goto out;
446 	err = read_event_files(pevent);
447 	if (err)
448 		goto out;
449 	err = read_proc_kallsyms(pevent);
450 	if (err)
451 		goto out;
452 	err = read_ftrace_printk(pevent);
453 	if (err)
454 		goto out;
455 	if (atof(version) >= 0.6) {
456 		err = read_saved_cmdline(pevent);
457 		if (err)
458 			goto out;
459 	}
460 
461 	size = trace_data_size;
462 	repipe = false;
463 
464 	if (show_funcs) {
465 		tep_print_funcs(pevent);
466 	} else if (show_printk) {
467 		tep_print_printk(pevent);
468 	}
469 
470 	pevent = NULL;
471 
472 out:
473 	if (pevent)
474 		trace_event__cleanup(tevent);
475 	free(version);
476 	return size;
477 }
478