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