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