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