1 /*
2  * Copyright (C) 2008,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 "util.h"
22 #include <dirent.h>
23 #include <mntent.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <pthread.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <stdbool.h>
36 #include <linux/list.h>
37 #include <linux/kernel.h>
38 
39 #include "../perf.h"
40 #include "trace-event.h"
41 #include "debugfs.h"
42 #include "evsel.h"
43 
44 #define VERSION "0.5"
45 
46 #define TRACE_CTRL	"tracing_on"
47 #define TRACE		"trace"
48 #define AVAILABLE	"available_tracers"
49 #define CURRENT		"current_tracer"
50 #define ITER_CTRL	"trace_options"
51 #define MAX_LATENCY	"tracing_max_latency"
52 
53 unsigned int page_size;
54 
55 static const char *output_file = "trace.info";
56 static int output_fd;
57 
58 struct event_list {
59 	struct event_list *next;
60 	const char *event;
61 };
62 
63 struct events {
64 	struct events *sibling;
65 	struct events *children;
66 	struct events *next;
67 	char *name;
68 };
69 
70 
71 void *malloc_or_die(unsigned int size)
72 {
73 	void *data;
74 
75 	data = malloc(size);
76 	if (!data)
77 		die("malloc");
78 	return data;
79 }
80 
81 static const char *find_debugfs(void)
82 {
83 	const char *path = debugfs_mount(NULL);
84 
85 	if (!path)
86 		die("Your kernel not support debugfs filesystem");
87 
88 	return path;
89 }
90 
91 /*
92  * Finds the path to the debugfs/tracing
93  * Allocates the string and stores it.
94  */
95 static const char *find_tracing_dir(void)
96 {
97 	static char *tracing;
98 	static int tracing_found;
99 	const char *debugfs;
100 
101 	if (tracing_found)
102 		return tracing;
103 
104 	debugfs = find_debugfs();
105 
106 	tracing = malloc_or_die(strlen(debugfs) + 9);
107 
108 	sprintf(tracing, "%s/tracing", debugfs);
109 
110 	tracing_found = 1;
111 	return tracing;
112 }
113 
114 static char *get_tracing_file(const char *name)
115 {
116 	const char *tracing;
117 	char *file;
118 
119 	tracing = find_tracing_dir();
120 	if (!tracing)
121 		return NULL;
122 
123 	file = malloc_or_die(strlen(tracing) + strlen(name) + 2);
124 
125 	sprintf(file, "%s/%s", tracing, name);
126 	return file;
127 }
128 
129 static void put_tracing_file(char *file)
130 {
131 	free(file);
132 }
133 
134 static ssize_t calc_data_size;
135 
136 static ssize_t write_or_die(const void *buf, size_t len)
137 {
138 	int ret;
139 
140 	if (calc_data_size) {
141 		calc_data_size += len;
142 		return len;
143 	}
144 
145 	ret = write(output_fd, buf, len);
146 	if (ret < 0)
147 		die("writing to '%s'", output_file);
148 
149 	return ret;
150 }
151 
152 int bigendian(void)
153 {
154 	unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
155 	unsigned int *ptr;
156 
157 	ptr = (unsigned int *)(void *)str;
158 	return *ptr == 0x01020304;
159 }
160 
161 /* unfortunately, you can not stat debugfs or proc files for size */
162 static void record_file(const char *file, size_t hdr_sz)
163 {
164 	unsigned long long size = 0;
165 	char buf[BUFSIZ], *sizep;
166 	off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR);
167 	int r, fd;
168 
169 	fd = open(file, O_RDONLY);
170 	if (fd < 0)
171 		die("Can't read '%s'", file);
172 
173 	/* put in zeros for file size, then fill true size later */
174 	if (hdr_sz)
175 		write_or_die(&size, hdr_sz);
176 
177 	do {
178 		r = read(fd, buf, BUFSIZ);
179 		if (r > 0) {
180 			size += r;
181 			write_or_die(buf, r);
182 		}
183 	} while (r > 0);
184 	close(fd);
185 
186 	/* ugh, handle big-endian hdr_size == 4 */
187 	sizep = (char*)&size;
188 	if (bigendian())
189 		sizep += sizeof(u64) - hdr_sz;
190 
191 	if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0)
192 		die("writing to %s", output_file);
193 }
194 
195 static void read_header_files(void)
196 {
197 	char *path;
198 	struct stat st;
199 
200 	path = get_tracing_file("events/header_page");
201 	if (stat(path, &st) < 0)
202 		die("can't read '%s'", path);
203 
204 	write_or_die("header_page", 12);
205 	record_file(path, 8);
206 	put_tracing_file(path);
207 
208 	path = get_tracing_file("events/header_event");
209 	if (stat(path, &st) < 0)
210 		die("can't read '%s'", path);
211 
212 	write_or_die("header_event", 13);
213 	record_file(path, 8);
214 	put_tracing_file(path);
215 }
216 
217 static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
218 {
219 	while (tps) {
220 		if (!strcmp(sys, tps->name))
221 			return true;
222 		tps = tps->next;
223 	}
224 
225 	return false;
226 }
227 
228 static void copy_event_system(const char *sys, struct tracepoint_path *tps)
229 {
230 	struct dirent *dent;
231 	struct stat st;
232 	char *format;
233 	DIR *dir;
234 	int count = 0;
235 	int ret;
236 
237 	dir = opendir(sys);
238 	if (!dir)
239 		die("can't read directory '%s'", sys);
240 
241 	while ((dent = readdir(dir))) {
242 		if (dent->d_type != DT_DIR ||
243 		    strcmp(dent->d_name, ".") == 0 ||
244 		    strcmp(dent->d_name, "..") == 0 ||
245 		    !name_in_tp_list(dent->d_name, tps))
246 			continue;
247 		format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
248 		sprintf(format, "%s/%s/format", sys, dent->d_name);
249 		ret = stat(format, &st);
250 		free(format);
251 		if (ret < 0)
252 			continue;
253 		count++;
254 	}
255 
256 	write_or_die(&count, 4);
257 
258 	rewinddir(dir);
259 	while ((dent = readdir(dir))) {
260 		if (dent->d_type != DT_DIR ||
261 		    strcmp(dent->d_name, ".") == 0 ||
262 		    strcmp(dent->d_name, "..") == 0 ||
263 		    !name_in_tp_list(dent->d_name, tps))
264 			continue;
265 		format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
266 		sprintf(format, "%s/%s/format", sys, dent->d_name);
267 		ret = stat(format, &st);
268 
269 		if (ret >= 0)
270 			record_file(format, 8);
271 
272 		free(format);
273 	}
274 	closedir(dir);
275 }
276 
277 static void read_ftrace_files(struct tracepoint_path *tps)
278 {
279 	char *path;
280 
281 	path = get_tracing_file("events/ftrace");
282 
283 	copy_event_system(path, tps);
284 
285 	put_tracing_file(path);
286 }
287 
288 static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
289 {
290 	while (tps) {
291 		if (!strcmp(sys, tps->system))
292 			return true;
293 		tps = tps->next;
294 	}
295 
296 	return false;
297 }
298 
299 static void read_event_files(struct tracepoint_path *tps)
300 {
301 	struct dirent *dent;
302 	struct stat st;
303 	char *path;
304 	char *sys;
305 	DIR *dir;
306 	int count = 0;
307 	int ret;
308 
309 	path = get_tracing_file("events");
310 
311 	dir = opendir(path);
312 	if (!dir)
313 		die("can't read directory '%s'", path);
314 
315 	while ((dent = readdir(dir))) {
316 		if (dent->d_type != DT_DIR ||
317 		    strcmp(dent->d_name, ".") == 0 ||
318 		    strcmp(dent->d_name, "..") == 0 ||
319 		    strcmp(dent->d_name, "ftrace") == 0 ||
320 		    !system_in_tp_list(dent->d_name, tps))
321 			continue;
322 		count++;
323 	}
324 
325 	write_or_die(&count, 4);
326 
327 	rewinddir(dir);
328 	while ((dent = readdir(dir))) {
329 		if (dent->d_type != DT_DIR ||
330 		    strcmp(dent->d_name, ".") == 0 ||
331 		    strcmp(dent->d_name, "..") == 0 ||
332 		    strcmp(dent->d_name, "ftrace") == 0 ||
333 		    !system_in_tp_list(dent->d_name, tps))
334 			continue;
335 		sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2);
336 		sprintf(sys, "%s/%s", path, dent->d_name);
337 		ret = stat(sys, &st);
338 		if (ret >= 0) {
339 			write_or_die(dent->d_name, strlen(dent->d_name) + 1);
340 			copy_event_system(sys, tps);
341 		}
342 		free(sys);
343 	}
344 
345 	closedir(dir);
346 	put_tracing_file(path);
347 }
348 
349 static void read_proc_kallsyms(void)
350 {
351 	unsigned int size;
352 	const char *path = "/proc/kallsyms";
353 	struct stat st;
354 	int ret;
355 
356 	ret = stat(path, &st);
357 	if (ret < 0) {
358 		/* not found */
359 		size = 0;
360 		write_or_die(&size, 4);
361 		return;
362 	}
363 	record_file(path, 4);
364 }
365 
366 static void read_ftrace_printk(void)
367 {
368 	unsigned int size;
369 	char *path;
370 	struct stat st;
371 	int ret;
372 
373 	path = get_tracing_file("printk_formats");
374 	ret = stat(path, &st);
375 	if (ret < 0) {
376 		/* not found */
377 		size = 0;
378 		write_or_die(&size, 4);
379 		goto out;
380 	}
381 	record_file(path, 4);
382 
383 out:
384 	put_tracing_file(path);
385 }
386 
387 static struct tracepoint_path *
388 get_tracepoints_path(struct list_head *pattrs)
389 {
390 	struct tracepoint_path path, *ppath = &path;
391 	struct perf_evsel *pos;
392 	int nr_tracepoints = 0;
393 
394 	list_for_each_entry(pos, pattrs, node) {
395 		if (pos->attr.type != PERF_TYPE_TRACEPOINT)
396 			continue;
397 		++nr_tracepoints;
398 		ppath->next = tracepoint_id_to_path(pos->attr.config);
399 		if (!ppath->next)
400 			die("%s\n", "No memory to alloc tracepoints list");
401 		ppath = ppath->next;
402 	}
403 
404 	return nr_tracepoints > 0 ? path.next : NULL;
405 }
406 
407 static void
408 put_tracepoints_path(struct tracepoint_path *tps)
409 {
410 	while (tps) {
411 		struct tracepoint_path *t = tps;
412 
413 		tps = tps->next;
414 		free(t->name);
415 		free(t->system);
416 		free(t);
417 	}
418 }
419 
420 bool have_tracepoints(struct list_head *pattrs)
421 {
422 	struct perf_evsel *pos;
423 
424 	list_for_each_entry(pos, pattrs, node)
425 		if (pos->attr.type == PERF_TYPE_TRACEPOINT)
426 			return true;
427 
428 	return false;
429 }
430 
431 static void tracing_data_header(void)
432 {
433 	char buf[20];
434 
435 	/* just guessing this is someone's birthday.. ;) */
436 	buf[0] = 23;
437 	buf[1] = 8;
438 	buf[2] = 68;
439 	memcpy(buf + 3, "tracing", 7);
440 
441 	write_or_die(buf, 10);
442 
443 	write_or_die(VERSION, strlen(VERSION) + 1);
444 
445 	/* save endian */
446 	if (bigendian())
447 		buf[0] = 1;
448 	else
449 		buf[0] = 0;
450 
451 	write_or_die(buf, 1);
452 
453 	/* save size of long */
454 	buf[0] = sizeof(long);
455 	write_or_die(buf, 1);
456 
457 	/* save page_size */
458 	page_size = sysconf(_SC_PAGESIZE);
459 	write_or_die(&page_size, 4);
460 }
461 
462 struct tracing_data *tracing_data_get(struct list_head *pattrs,
463 				      int fd, bool temp)
464 {
465 	struct tracepoint_path *tps;
466 	struct tracing_data *tdata;
467 
468 	output_fd = fd;
469 
470 	tps = get_tracepoints_path(pattrs);
471 	if (!tps)
472 		return NULL;
473 
474 	tdata = malloc_or_die(sizeof(*tdata));
475 	tdata->temp = temp;
476 	tdata->size = 0;
477 
478 	if (temp) {
479 		int temp_fd;
480 
481 		snprintf(tdata->temp_file, sizeof(tdata->temp_file),
482 			 "/tmp/perf-XXXXXX");
483 		if (!mkstemp(tdata->temp_file))
484 			die("Can't make temp file");
485 
486 		temp_fd = open(tdata->temp_file, O_RDWR);
487 		if (temp_fd < 0)
488 			die("Can't read '%s'", tdata->temp_file);
489 
490 		/*
491 		 * Set the temp file the default output, so all the
492 		 * tracing data are stored into it.
493 		 */
494 		output_fd = temp_fd;
495 	}
496 
497 	tracing_data_header();
498 	read_header_files();
499 	read_ftrace_files(tps);
500 	read_event_files(tps);
501 	read_proc_kallsyms();
502 	read_ftrace_printk();
503 
504 	/*
505 	 * All tracing data are stored by now, we can restore
506 	 * the default output file in case we used temp file.
507 	 */
508 	if (temp) {
509 		tdata->size = lseek(output_fd, 0, SEEK_CUR);
510 		close(output_fd);
511 		output_fd = fd;
512 	}
513 
514 	put_tracepoints_path(tps);
515 	return tdata;
516 }
517 
518 void tracing_data_put(struct tracing_data *tdata)
519 {
520 	if (tdata->temp) {
521 		record_file(tdata->temp_file, 0);
522 		unlink(tdata->temp_file);
523 	}
524 
525 	free(tdata);
526 }
527 
528 int read_tracing_data(int fd, struct list_head *pattrs)
529 {
530 	struct tracing_data *tdata;
531 
532 	/*
533 	 * We work over the real file, so we can write data
534 	 * directly, no temp file is needed.
535 	 */
536 	tdata = tracing_data_get(pattrs, fd, false);
537 	if (!tdata)
538 		return -ENOMEM;
539 
540 	tracing_data_put(tdata);
541 	return 0;
542 }
543