• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
__do_read(int fd,void * buf,int size)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 
do_read(void * data,int size)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 */
skip(int size)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 
read4(struct tep_handle * pevent)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 __data2host4(pevent, data);
106 }
107 
read8(struct tep_handle * pevent)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 __data2host8(pevent, data);
115 }
116 
read_string(void)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 
read_proc_kallsyms(struct tep_handle * pevent)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 
read_ftrace_printk(struct tep_handle * pevent)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 
read_header_files(struct tep_handle * pevent)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, 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 
read_ftrace_file(struct tep_handle * pevent,unsigned long long size)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 
read_event_file(struct tep_handle * pevent,char * sys,unsigned long long size)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 		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 
read_ftrace_files(struct tep_handle * pevent)313 static int read_ftrace_files(struct tep_handle *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 
read_event_files(struct tep_handle * pevent)331 static int read_event_files(struct tep_handle *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 				free(sys);
354 				return ret;
355 			}
356 		}
357 		free(sys);
358 	}
359 	return 0;
360 }
361 
read_saved_cmdline(struct tep_handle * pevent)362 static int read_saved_cmdline(struct tep_handle *pevent)
363 {
364 	unsigned long long size;
365 	char *buf;
366 	int ret;
367 
368 	/* it can have 0 size */
369 	size = read8(pevent);
370 	if (!size)
371 		return 0;
372 
373 	buf = malloc(size + 1);
374 	if (buf == NULL) {
375 		pr_debug("memory allocation failure\n");
376 		return -1;
377 	}
378 
379 	ret = do_read(buf, size);
380 	if (ret < 0) {
381 		pr_debug("error reading saved cmdlines\n");
382 		goto out;
383 	}
384 
385 	parse_saved_cmdline(pevent, buf, size);
386 	ret = 0;
387 out:
388 	free(buf);
389 	return ret;
390 }
391 
trace_report(int fd,struct trace_event * tevent,bool __repipe)392 ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
393 {
394 	char buf[BUFSIZ];
395 	char test[] = { 23, 8, 68 };
396 	char *version;
397 	int show_version = 0;
398 	int show_funcs = 0;
399 	int show_printk = 0;
400 	ssize_t size = -1;
401 	int file_bigendian;
402 	int host_bigendian;
403 	int file_long_size;
404 	int file_page_size;
405 	struct tep_handle *pevent = NULL;
406 	int err;
407 
408 	repipe = __repipe;
409 	input_fd = fd;
410 
411 	if (do_read(buf, 3) < 0)
412 		return -1;
413 	if (memcmp(buf, test, 3) != 0) {
414 		pr_debug("no trace data in the file");
415 		return -1;
416 	}
417 
418 	if (do_read(buf, 7) < 0)
419 		return -1;
420 	if (memcmp(buf, "tracing", 7) != 0) {
421 		pr_debug("not a trace file (missing 'tracing' tag)");
422 		return -1;
423 	}
424 
425 	version = read_string();
426 	if (version == NULL)
427 		return -1;
428 	if (show_version)
429 		printf("version = %s\n", version);
430 
431 	if (do_read(buf, 1) < 0) {
432 		free(version);
433 		return -1;
434 	}
435 	file_bigendian = buf[0];
436 	host_bigendian = bigendian();
437 
438 	if (trace_event__init(tevent)) {
439 		pr_debug("trace_event__init failed");
440 		goto out;
441 	}
442 
443 	pevent = tevent->pevent;
444 
445 	tep_set_flag(pevent, TEP_NSEC_OUTPUT);
446 	tep_set_file_bigendian(pevent, file_bigendian);
447 	tep_set_host_bigendian(pevent, host_bigendian);
448 
449 	if (do_read(buf, 1) < 0)
450 		goto out;
451 	file_long_size = buf[0];
452 
453 	file_page_size = read4(pevent);
454 	if (!file_page_size)
455 		goto out;
456 
457 	tep_set_long_size(pevent, file_long_size);
458 	tep_set_page_size(pevent, file_page_size);
459 
460 	err = read_header_files(pevent);
461 	if (err)
462 		goto out;
463 	err = read_ftrace_files(pevent);
464 	if (err)
465 		goto out;
466 	err = read_event_files(pevent);
467 	if (err)
468 		goto out;
469 	err = read_proc_kallsyms(pevent);
470 	if (err)
471 		goto out;
472 	err = read_ftrace_printk(pevent);
473 	if (err)
474 		goto out;
475 	if (atof(version) >= 0.6) {
476 		err = read_saved_cmdline(pevent);
477 		if (err)
478 			goto out;
479 	}
480 
481 	size = trace_data_size;
482 	repipe = false;
483 
484 	if (show_funcs) {
485 		tep_print_funcs(pevent);
486 	} else if (show_printk) {
487 		tep_print_printk(pevent);
488 	}
489 
490 	pevent = NULL;
491 
492 out:
493 	if (pevent)
494 		trace_event__cleanup(tevent);
495 	free(version);
496 	return size;
497 }
498