trace-event-info.c 11.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License (not later!)
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
21
#include "util.h"
22
#include <dirent.h>
23
#include <mntent.h>
24 25 26 27 28 29 30 31 32 33 34
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#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 <api/fs/tracing_path.h>
42
#include "evsel.h"
43
#include "debug.h"
44

45
#define VERSION "0.6"
46 47 48 49 50 51 52 53 54

static int output_fd;


int bigendian(void)
{
	unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
	unsigned int *ptr;

55
	ptr = (unsigned int *)(void *)str;
56 57 58
	return *ptr == 0x01020304;
}

59
/* unfortunately, you can not stat debugfs or proc files for size */
60
static int record_file(const char *file, ssize_t hdr_sz)
61 62
{
	unsigned long long size = 0;
63 64 65
	char buf[BUFSIZ], *sizep;
	off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR);
	int r, fd;
66
	int err = -EIO;
67 68

	fd = open(file, O_RDONLY);
69 70 71 72
	if (fd < 0) {
		pr_debug("Can't read '%s'", file);
		return -errno;
	}
73

74
	/* put in zeros for file size, then fill true size later */
75 76 77 78
	if (hdr_sz) {
		if (write(output_fd, &size, hdr_sz) != hdr_sz)
			goto out;
	}
79 80 81

	do {
		r = read(fd, buf, BUFSIZ);
82
		if (r > 0) {
83
			size += r;
84 85
			if (write(output_fd, buf, r) != r)
				goto out;
86
		}
87 88
	} while (r > 0);

89 90 91 92 93
	/* ugh, handle big-endian hdr_size == 4 */
	sizep = (char*)&size;
	if (bigendian())
		sizep += sizeof(u64) - hdr_sz;

94 95 96 97
	if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) {
		pr_debug("writing file size failed\n");
		goto out;
	}
98 99 100 101 102

	err = 0;
out:
	close(fd);
	return err;
103 104
}

105
static int record_header_files(void)
106 107
{
	char *path;
108
	struct stat st;
109
	int err = -EIO;
110 111

	path = get_tracing_file("events/header_page");
112 113 114 115
	if (!path) {
		pr_debug("can't get tracing/events/header_page");
		return -ENOMEM;
	}
116

117 118 119 120
	if (stat(path, &st) < 0) {
		pr_debug("can't read '%s'", path);
		goto out;
	}
121

122 123 124 125 126 127 128 129 130 131
	if (write(output_fd, "header_page", 12) != 12) {
		pr_debug("can't write header_page\n");
		goto out;
	}

	if (record_file(path, 8) < 0) {
		pr_debug("can't record header_page file\n");
		goto out;
	}

132 133 134
	put_tracing_file(path);

	path = get_tracing_file("events/header_event");
135 136 137 138 139
	if (!path) {
		pr_debug("can't get tracing/events/header_event");
		err = -ENOMEM;
		goto out;
	}
140

141 142 143 144
	if (stat(path, &st) < 0) {
		pr_debug("can't read '%s'", path);
		goto out;
	}
145

146 147 148 149 150 151 152 153 154 155 156 157
	if (write(output_fd, "header_event", 13) != 13) {
		pr_debug("can't write header_event\n");
		goto out;
	}

	if (record_file(path, 8) < 0) {
		pr_debug("can't record header_event file\n");
		goto out;
	}

	err = 0;
out:
158
	put_tracing_file(path);
159
	return err;
160 161
}

162 163 164 165 166 167 168 169 170 171 172
static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
{
	while (tps) {
		if (!strcmp(sys, tps->name))
			return true;
		tps = tps->next;
	}

	return false;
}

173 174 175 176 177 178
#define for_each_event(dir, dent, tps)				\
	while ((dent = readdir(dir)))				\
		if (dent->d_type == DT_DIR &&			\
		    (strcmp(dent->d_name, ".")) &&		\
		    (strcmp(dent->d_name, "..")))		\

179
static int copy_event_system(const char *sys, struct tracepoint_path *tps)
180 181 182 183 184 185 186
{
	struct dirent *dent;
	struct stat st;
	char *format;
	DIR *dir;
	int count = 0;
	int ret;
187
	int err;
188 189

	dir = opendir(sys);
190 191 192 193
	if (!dir) {
		pr_debug("can't read directory '%s'", sys);
		return -errno;
	}
194

195 196
	for_each_event(dir, dent, tps) {
		if (!name_in_tp_list(dent->d_name, tps))
197
			continue;
198

199
		if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) {
200 201 202
			err = -ENOMEM;
			goto out;
		}
203 204 205 206 207 208 209
		ret = stat(format, &st);
		free(format);
		if (ret < 0)
			continue;
		count++;
	}

210 211 212 213 214
	if (write(output_fd, &count, 4) != 4) {
		err = -EIO;
		pr_debug("can't write count\n");
		goto out;
	}
215 216

	rewinddir(dir);
217 218
	for_each_event(dir, dent, tps) {
		if (!name_in_tp_list(dent->d_name, tps))
219
			continue;
220

221
		if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) {
222 223 224
			err = -ENOMEM;
			goto out;
		}
225 226
		ret = stat(format, &st);

227 228 229 230 231 232 233
		if (ret >= 0) {
			err = record_file(format, 8);
			if (err) {
				free(format);
				goto out;
			}
		}
234 235
		free(format);
	}
236 237
	err = 0;
out:
238
	closedir(dir);
239
	return err;
240 241
}

242
static int record_ftrace_files(struct tracepoint_path *tps)
243 244
{
	char *path;
245
	int ret;
246 247

	path = get_tracing_file("events/ftrace");
248 249 250 251
	if (!path) {
		pr_debug("can't get tracing/events/ftrace");
		return -ENOMEM;
	}
252

253
	ret = copy_event_system(path, tps);
254 255

	put_tracing_file(path);
256 257

	return ret;
258 259
}

260 261 262 263 264 265 266 267 268 269 270
static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
{
	while (tps) {
		if (!strcmp(sys, tps->system))
			return true;
		tps = tps->next;
	}

	return false;
}

271
static int record_event_files(struct tracepoint_path *tps)
272 273 274 275 276 277 278 279
{
	struct dirent *dent;
	struct stat st;
	char *path;
	char *sys;
	DIR *dir;
	int count = 0;
	int ret;
280
	int err;
281 282

	path = get_tracing_file("events");
283 284 285 286
	if (!path) {
		pr_debug("can't get tracing/events");
		return -ENOMEM;
	}
287 288

	dir = opendir(path);
289 290 291 292 293
	if (!dir) {
		err = -errno;
		pr_debug("can't read directory '%s'", path);
		goto out;
	}
294

295 296
	for_each_event(dir, dent, tps) {
		if (strcmp(dent->d_name, "ftrace") == 0 ||
297
		    !system_in_tp_list(dent->d_name, tps))
298
			continue;
299

300
		count++;
301 302
	}

303 304 305 306 307
	if (write(output_fd, &count, 4) != 4) {
		err = -EIO;
		pr_debug("can't write count\n");
		goto out;
	}
308 309

	rewinddir(dir);
310 311
	for_each_event(dir, dent, tps) {
		if (strcmp(dent->d_name, "ftrace") == 0 ||
312
		    !system_in_tp_list(dent->d_name, tps))
313
			continue;
314

315
		if (asprintf(&sys, "%s/%s", path, dent->d_name) < 0) {
316 317 318
			err = -ENOMEM;
			goto out;
		}
319 320
		ret = stat(sys, &st);
		if (ret >= 0) {
321 322 323 324 325 326 327 328
			ssize_t size = strlen(dent->d_name) + 1;

			if (write(output_fd, dent->d_name, size) != size ||
			    copy_event_system(sys, tps) < 0) {
				err = -EIO;
				free(sys);
				goto out;
			}
329 330 331
		}
		free(sys);
	}
332 333
	err = 0;
out:
334
	closedir(dir);
335
	put_tracing_file(path);
336 337

	return err;
338 339
}

340
static int record_proc_kallsyms(void)
341
{
342 343 344 345 346 347 348 349
	unsigned long long size = 0;
	/*
	 * Just to keep older perf.data file parsers happy, record a zero
	 * sized kallsyms file, i.e. do the same thing that was done when
	 * /proc/kallsyms (or something specified via --kallsyms, in a
	 * different path) couldn't be read.
	 */
	return write(output_fd, &size, 4) != 4 ? -EIO : 0;
350 351
}

352
static int record_ftrace_printk(void)
353
{
354
	unsigned int size;
355
	char *path;
356
	struct stat st;
357
	int ret, err = 0;
358 359

	path = get_tracing_file("printk_formats");
360 361 362 363
	if (!path) {
		pr_debug("can't get tracing/printk_formats");
		return -ENOMEM;
	}
364

365 366 367 368
	ret = stat(path, &st);
	if (ret < 0) {
		/* not found */
		size = 0;
369 370
		if (write(output_fd, &size, 4) != 4)
			err = -EIO;
371
		goto out;
372
	}
373
	err = record_file(path, 4);
374

375 376
out:
	put_tracing_file(path);
377
	return err;
378 379
}

380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
static int record_saved_cmdline(void)
{
	unsigned int size;
	char *path;
	struct stat st;
	int ret, err = 0;

	path = get_tracing_file("saved_cmdlines");
	if (!path) {
		pr_debug("can't get tracing/saved_cmdline");
		return -ENOMEM;
	}

	ret = stat(path, &st);
	if (ret < 0) {
		/* not found */
		size = 0;
		if (write(output_fd, &size, 8) != 8)
			err = -EIO;
		goto out;
	}
	err = record_file(path, 8);

out:
	put_tracing_file(path);
	return err;
}

408 409 410 411 412 413 414
static void
put_tracepoints_path(struct tracepoint_path *tps)
{
	while (tps) {
		struct tracepoint_path *t = tps;

		tps = tps->next;
415 416
		zfree(&t->name);
		zfree(&t->system);
417 418 419 420
		free(t);
	}
}

421
static struct tracepoint_path *
422
get_tracepoints_path(struct list_head *pattrs)
423 424
{
	struct tracepoint_path path, *ppath = &path;
425 426
	struct perf_evsel *pos;
	int nr_tracepoints = 0;
427

428 429
	list_for_each_entry(pos, pattrs, node) {
		if (pos->attr.type != PERF_TYPE_TRACEPOINT)
430
			continue;
431
		++nr_tracepoints;
432 433 434 435 436 437 438 439 440 441 442 443 444

		if (pos->name) {
			ppath->next = tracepoint_name_to_path(pos->name);
			if (ppath->next)
				goto next;

			if (strchr(pos->name, ':') == NULL)
				goto try_id;

			goto error;
		}

try_id:
445
		ppath->next = tracepoint_id_to_path(pos->attr.config);
446
		if (!ppath->next) {
447
error:
448 449 450 451
			pr_debug("No memory to alloc tracepoints list\n");
			put_tracepoints_path(&path);
			return NULL;
		}
452
next:
453 454 455
		ppath = ppath->next;
	}

456
	return nr_tracepoints > 0 ? path.next : NULL;
457
}
458

459
bool have_tracepoints(struct list_head *pattrs)
460
{
461
	struct perf_evsel *pos;
462

463 464
	list_for_each_entry(pos, pattrs, node)
		if (pos->attr.type == PERF_TYPE_TRACEPOINT)
465 466 467
			return true;

	return false;
468 469
}

470
static int tracing_data_header(void)
471
{
J
Jiri Olsa 已提交
472
	char buf[20];
473
	ssize_t size;
474

J
Jiri Olsa 已提交
475
	/* just guessing this is someone's birthday.. ;) */
476 477 478 479 480
	buf[0] = 23;
	buf[1] = 8;
	buf[2] = 68;
	memcpy(buf + 3, "tracing", 7);

481 482
	if (write(output_fd, buf, 10) != 10)
		return -1;
483

484 485 486
	size = strlen(VERSION) + 1;
	if (write(output_fd, VERSION, size) != size)
		return -1;
487 488 489 490 491 492 493

	/* save endian */
	if (bigendian())
		buf[0] = 1;
	else
		buf[0] = 0;

494 495
	if (write(output_fd, buf, 1) != 1)
		return -1;
496 497 498

	/* save size of long */
	buf[0] = sizeof(long);
499 500
	if (write(output_fd, buf, 1) != 1)
		return -1;
501 502

	/* save page_size */
503 504 505 506
	if (write(output_fd, &page_size, 4) != 4)
		return -1;

	return 0;
J
Jiri Olsa 已提交
507 508 509 510 511 512 513
}

struct tracing_data *tracing_data_get(struct list_head *pattrs,
				      int fd, bool temp)
{
	struct tracepoint_path *tps;
	struct tracing_data *tdata;
514
	int err;
J
Jiri Olsa 已提交
515 516 517 518 519 520

	output_fd = fd;

	tps = get_tracepoints_path(pattrs);
	if (!tps)
		return NULL;
521

522 523 524 525
	tdata = malloc(sizeof(*tdata));
	if (!tdata)
		return NULL;

J
Jiri Olsa 已提交
526 527 528 529 530 531 532 533
	tdata->temp = temp;
	tdata->size = 0;

	if (temp) {
		int temp_fd;

		snprintf(tdata->temp_file, sizeof(tdata->temp_file),
			 "/tmp/perf-XXXXXX");
534 535 536 537
		if (!mkstemp(tdata->temp_file)) {
			pr_debug("Can't make temp file");
			return NULL;
		}
J
Jiri Olsa 已提交
538 539

		temp_fd = open(tdata->temp_file, O_RDWR);
540 541 542 543
		if (temp_fd < 0) {
			pr_debug("Can't read '%s'", tdata->temp_file);
			return NULL;
		}
J
Jiri Olsa 已提交
544 545 546 547 548 549 550 551

		/*
		 * Set the temp file the default output, so all the
		 * tracing data are stored into it.
		 */
		output_fd = temp_fd;
	}

552 553 554
	err = tracing_data_header();
	if (err)
		goto out;
555
	err = record_header_files();
556 557
	if (err)
		goto out;
558
	err = record_ftrace_files(tps);
559 560
	if (err)
		goto out;
561
	err = record_event_files(tps);
562 563
	if (err)
		goto out;
564
	err = record_proc_kallsyms();
565 566
	if (err)
		goto out;
567
	err = record_ftrace_printk();
568 569 570
	if (err)
		goto out;
	err = record_saved_cmdline();
571

572
out:
J
Jiri Olsa 已提交
573 574 575 576 577 578 579 580 581 582
	/*
	 * All tracing data are stored by now, we can restore
	 * the default output file in case we used temp file.
	 */
	if (temp) {
		tdata->size = lseek(output_fd, 0, SEEK_CUR);
		close(output_fd);
		output_fd = fd;
	}

583 584
	if (err)
		zfree(&tdata);
585

J
Jiri Olsa 已提交
586 587
	put_tracepoints_path(tps);
	return tdata;
588
}
589

590
int tracing_data_put(struct tracing_data *tdata)
591
{
592 593
	int err = 0;

J
Jiri Olsa 已提交
594
	if (tdata->temp) {
595
		err = record_file(tdata->temp_file, 0);
J
Jiri Olsa 已提交
596 597
		unlink(tdata->temp_file);
	}
598

J
Jiri Olsa 已提交
599
	free(tdata);
600
	return err;
J
Jiri Olsa 已提交
601
}
602

J
Jiri Olsa 已提交
603 604
int read_tracing_data(int fd, struct list_head *pattrs)
{
605
	int err;
J
Jiri Olsa 已提交
606
	struct tracing_data *tdata;
607

J
Jiri Olsa 已提交
608 609 610 611 612 613 614 615
	/*
	 * We work over the real file, so we can write data
	 * directly, no temp file is needed.
	 */
	tdata = tracing_data_get(pattrs, fd, false);
	if (!tdata)
		return -ENOMEM;

616 617
	err = tracing_data_put(tdata);
	return err;
618
}