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 <traceevent/event-parse.h> |
15 | #include <fcntl.h> |
16 | #include <unistd.h> |
17 | #include <errno.h> |
18 | |
19 | #include "trace-event.h" |
20 | #include "debug.h" |
21 | #include "util.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(fd: input_fd, buf: 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(data: buf, size: 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: &data, size: 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: &data, size: 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(data: buf, size) < 0) { |
183 | free(buf); |
184 | return -1; |
185 | } |
186 | |
187 | buf[size] = '\0'; |
188 | |
189 | parse_ftrace_printk(pevent, file: buf, size); |
190 | |
191 | free(buf); |
192 | return 0; |
193 | } |
194 | |
195 | static int (struct tep_handle *pevent) |
196 | { |
197 | unsigned long long size; |
198 | char *; |
199 | char buf[BUFSIZ]; |
200 | int ret = 0; |
201 | |
202 | if (do_read(data: buf, size: 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(data: 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(data: buf, size: 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(data: 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(data: 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(data: buf, size); |
362 | if (ret < 0) { |
363 | pr_debug("error reading saved cmdlines\n" ); |
364 | goto out; |
365 | } |
366 | buf[ret] = '\0'; |
367 | |
368 | parse_saved_cmdline(pevent, file: buf, size); |
369 | ret = 0; |
370 | out: |
371 | free(buf); |
372 | return ret; |
373 | } |
374 | |
375 | ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) |
376 | { |
377 | char buf[BUFSIZ]; |
378 | char test[] = { 23, 8, 68 }; |
379 | char *version; |
380 | int show_version = 0; |
381 | int show_funcs = 0; |
382 | int show_printk = 0; |
383 | ssize_t size = -1; |
384 | int file_bigendian; |
385 | int host_bigendian; |
386 | int file_long_size; |
387 | int file_page_size; |
388 | struct tep_handle *pevent = NULL; |
389 | int err; |
390 | |
391 | repipe = __repipe; |
392 | input_fd = fd; |
393 | |
394 | if (do_read(data: buf, size: 3) < 0) |
395 | return -1; |
396 | if (memcmp(buf, test, 3) != 0) { |
397 | pr_debug("no trace data in the file" ); |
398 | return -1; |
399 | } |
400 | |
401 | if (do_read(data: buf, size: 7) < 0) |
402 | return -1; |
403 | if (memcmp(buf, "tracing" , 7) != 0) { |
404 | pr_debug("not a trace file (missing 'tracing' tag)" ); |
405 | return -1; |
406 | } |
407 | |
408 | version = read_string(); |
409 | if (version == NULL) |
410 | return -1; |
411 | if (show_version) |
412 | printf("version = %s\n" , version); |
413 | |
414 | if (do_read(data: buf, size: 1) < 0) { |
415 | free(version); |
416 | return -1; |
417 | } |
418 | file_bigendian = buf[0]; |
419 | host_bigendian = host_is_bigendian() ? 1 : 0; |
420 | |
421 | if (trace_event__init(t: tevent)) { |
422 | pr_debug("trace_event__init failed" ); |
423 | goto out; |
424 | } |
425 | |
426 | pevent = tevent->pevent; |
427 | |
428 | tep_set_flag(pevent, TEP_NSEC_OUTPUT); |
429 | tep_set_file_bigendian(pevent, file_bigendian); |
430 | tep_set_local_bigendian(pevent, host_bigendian); |
431 | |
432 | if (do_read(data: buf, size: 1) < 0) |
433 | goto out; |
434 | file_long_size = buf[0]; |
435 | |
436 | file_page_size = read4(pevent); |
437 | if (!file_page_size) |
438 | goto out; |
439 | |
440 | tep_set_long_size(pevent, file_long_size); |
441 | tep_set_page_size(pevent, file_page_size); |
442 | |
443 | err = read_header_files(pevent); |
444 | if (err) |
445 | goto out; |
446 | err = read_ftrace_files(pevent); |
447 | if (err) |
448 | goto out; |
449 | err = read_event_files(pevent); |
450 | if (err) |
451 | goto out; |
452 | err = read_proc_kallsyms(pevent); |
453 | if (err) |
454 | goto out; |
455 | err = read_ftrace_printk(pevent); |
456 | if (err) |
457 | goto out; |
458 | if (atof(version) >= 0.6) { |
459 | err = read_saved_cmdline(pevent); |
460 | if (err) |
461 | goto out; |
462 | } |
463 | |
464 | size = trace_data_size; |
465 | repipe = false; |
466 | |
467 | if (show_funcs) { |
468 | tep_print_funcs(pevent); |
469 | } else if (show_printk) { |
470 | tep_print_printk(pevent); |
471 | } |
472 | |
473 | pevent = NULL; |
474 | |
475 | out: |
476 | if (pevent) |
477 | trace_event__cleanup(t: tevent); |
478 | free(version); |
479 | return size; |
480 | } |
481 | |