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