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 pevent *pevent)
100 {
101 	unsigned int data;
102 
103 	if (do_read(&data, 4) < 0)
104 		return 0;
105 	return __data2host4(pevent, data);
106 }
107 
108 static unsigned long long read8(struct pevent *pevent)
109 {
110 	unsigned long long data;
111 
112 	if (do_read(&data, 8) < 0)
113 		return 0;
114 	return __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 pevent *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 pevent *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 pevent *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 (!pevent_parse_header_page(pevent, header_page, size,
239 				      pevent_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 		pevent_set_long_size(pevent, pevent->header_page_size_size);
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 pevent *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 pevent *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 		free(buf);
302 		goto out;
303 	}
304 
305 	ret = parse_event_file(pevent, buf, size, sys);
306 	if (ret < 0)
307 		pr_debug("error parsing event file.\n");
308 out:
309 	free(buf);
310 	return ret;
311 }
312 
313 static int read_ftrace_files(struct pevent *pevent)
314 {
315 	unsigned long long size;
316 	int count;
317 	int i;
318 	int ret;
319 
320 	count = read4(pevent);
321 
322 	for (i = 0; i < count; i++) {
323 		size = read8(pevent);
324 		ret = read_ftrace_file(pevent, size);
325 		if (ret)
326 			return ret;
327 	}
328 	return 0;
329 }
330 
331 static int read_event_files(struct pevent *pevent)
332 {
333 	unsigned long long size;
334 	char *sys;
335 	int systems;
336 	int count;
337 	int i,x;
338 	int ret;
339 
340 	systems = read4(pevent);
341 
342 	for (i = 0; i < systems; i++) {
343 		sys = read_string();
344 		if (sys == NULL)
345 			return -1;
346 
347 		count = read4(pevent);
348 
349 		for (x=0; x < count; x++) {
350 			size = read8(pevent);
351 			ret = read_event_file(pevent, sys, size);
352 			if (ret)
353 				return ret;
354 		}
355 	}
356 	return 0;
357 }
358 
359 static int read_saved_cmdline(struct pevent *pevent)
360 {
361 	unsigned long long size;
362 	char *buf;
363 	int ret;
364 
365 	/* it can have 0 size */
366 	size = read8(pevent);
367 	if (!size)
368 		return 0;
369 
370 	buf = malloc(size + 1);
371 	if (buf == NULL) {
372 		pr_debug("memory allocation failure\n");
373 		return -1;
374 	}
375 
376 	ret = do_read(buf, size);
377 	if (ret < 0) {
378 		pr_debug("error reading saved cmdlines\n");
379 		goto out;
380 	}
381 
382 	parse_saved_cmdline(pevent, buf, size);
383 	ret = 0;
384 out:
385 	free(buf);
386 	return ret;
387 }
388 
389 ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
390 {
391 	char buf[BUFSIZ];
392 	char test[] = { 23, 8, 68 };
393 	char *version;
394 	int show_version = 0;
395 	int show_funcs = 0;
396 	int show_printk = 0;
397 	ssize_t size = -1;
398 	int file_bigendian;
399 	int host_bigendian;
400 	int file_long_size;
401 	int file_page_size;
402 	struct pevent *pevent = NULL;
403 	int err;
404 
405 	repipe = __repipe;
406 	input_fd = fd;
407 
408 	if (do_read(buf, 3) < 0)
409 		return -1;
410 	if (memcmp(buf, test, 3) != 0) {
411 		pr_debug("no trace data in the file");
412 		return -1;
413 	}
414 
415 	if (do_read(buf, 7) < 0)
416 		return -1;
417 	if (memcmp(buf, "tracing", 7) != 0) {
418 		pr_debug("not a trace file (missing 'tracing' tag)");
419 		return -1;
420 	}
421 
422 	version = read_string();
423 	if (version == NULL)
424 		return -1;
425 	if (show_version)
426 		printf("version = %s\n", version);
427 
428 	if (do_read(buf, 1) < 0) {
429 		free(version);
430 		return -1;
431 	}
432 	file_bigendian = buf[0];
433 	host_bigendian = bigendian();
434 
435 	if (trace_event__init(tevent)) {
436 		pr_debug("trace_event__init failed");
437 		goto out;
438 	}
439 
440 	pevent = tevent->pevent;
441 
442 	pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT);
443 	pevent_set_file_bigendian(pevent, file_bigendian);
444 	pevent_set_host_bigendian(pevent, host_bigendian);
445 
446 	if (do_read(buf, 1) < 0)
447 		goto out;
448 	file_long_size = buf[0];
449 
450 	file_page_size = read4(pevent);
451 	if (!file_page_size)
452 		goto out;
453 
454 	pevent_set_long_size(pevent, file_long_size);
455 	pevent_set_page_size(pevent, file_page_size);
456 
457 	err = read_header_files(pevent);
458 	if (err)
459 		goto out;
460 	err = read_ftrace_files(pevent);
461 	if (err)
462 		goto out;
463 	err = read_event_files(pevent);
464 	if (err)
465 		goto out;
466 	err = read_proc_kallsyms(pevent);
467 	if (err)
468 		goto out;
469 	err = read_ftrace_printk(pevent);
470 	if (err)
471 		goto out;
472 	if (atof(version) >= 0.6) {
473 		err = read_saved_cmdline(pevent);
474 		if (err)
475 			goto out;
476 	}
477 
478 	size = trace_data_size;
479 	repipe = false;
480 
481 	if (show_funcs) {
482 		pevent_print_funcs(pevent);
483 	} else if (show_printk) {
484 		pevent_print_printk(pevent);
485 	}
486 
487 	pevent = NULL;
488 
489 out:
490 	if (pevent)
491 		trace_event__cleanup(tevent);
492 	free(version);
493 	return size;
494 }
495