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