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/tracing_path.h> 42 #include "evsel.h" 43 #include "debug.h" 44 45 #define VERSION "0.6" 46 47 static int output_fd; 48 49 50 int 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 */ 60 static 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; 100 out: 101 close(fd); 102 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 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; 157 out: 158 put_tracing_file(path); 159 return err; 160 } 161 162 static 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 173 #define for_each_event(dir, dent, tps) \ 174 while ((dent = readdir(dir))) \ 175 if (dent->d_type == DT_DIR && \ 176 (strcmp(dent->d_name, ".")) && \ 177 (strcmp(dent->d_name, ".."))) \ 178 179 static int copy_event_system(const char *sys, struct tracepoint_path *tps) 180 { 181 struct dirent *dent; 182 struct stat st; 183 char *format; 184 DIR *dir; 185 int count = 0; 186 int ret; 187 int err; 188 189 dir = opendir(sys); 190 if (!dir) { 191 pr_debug("can't read directory '%s'", sys); 192 return -errno; 193 } 194 195 for_each_event(dir, dent, tps) { 196 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 err = -ENOMEM; 201 goto out; 202 } 203 ret = stat(format, &st); 204 free(format); 205 if (ret < 0) 206 continue; 207 count++; 208 } 209 210 if (write(output_fd, &count, 4) != 4) { 211 err = -EIO; 212 pr_debug("can't write count\n"); 213 goto out; 214 } 215 216 rewinddir(dir); 217 for_each_event(dir, dent, tps) { 218 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 err = -ENOMEM; 223 goto out; 224 } 225 ret = stat(format, &st); 226 227 if (ret >= 0) { 228 err = record_file(format, 8); 229 if (err) { 230 free(format); 231 goto out; 232 } 233 } 234 free(format); 235 } 236 err = 0; 237 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 if (!path) { 249 pr_debug("can't get tracing/events/ftrace"); 250 return -ENOMEM; 251 } 252 253 ret = copy_event_system(path, tps); 254 255 put_tracing_file(path); 256 257 return ret; 258 } 259 260 static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) 261 { 262 while (tps) { 263 if (!strcmp(sys, tps->system)) 264 return true; 265 tps = tps->next; 266 } 267 268 return false; 269 } 270 271 static int record_event_files(struct tracepoint_path *tps) 272 { 273 struct dirent *dent; 274 struct stat st; 275 char *path; 276 char *sys; 277 DIR *dir; 278 int count = 0; 279 int ret; 280 int err; 281 282 path = get_tracing_file("events"); 283 if (!path) { 284 pr_debug("can't get tracing/events"); 285 return -ENOMEM; 286 } 287 288 dir = opendir(path); 289 if (!dir) { 290 err = -errno; 291 pr_debug("can't read directory '%s'", path); 292 goto out; 293 } 294 295 for_each_event(dir, dent, tps) { 296 if (strcmp(dent->d_name, "ftrace") == 0 || 297 !system_in_tp_list(dent->d_name, tps)) 298 continue; 299 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 for_each_event(dir, dent, tps) { 311 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 err = -ENOMEM; 317 goto out; 318 } 319 ret = stat(sys, &st); 320 if (ret >= 0) { 321 ssize_t size = strlen(dent->d_name) + 1; 322 323 if (write(output_fd, dent->d_name, size) != size || 324 copy_event_system(sys, tps) < 0) { 325 err = -EIO; 326 free(sys); 327 goto out; 328 } 329 } 330 free(sys); 331 } 332 err = 0; 333 out: 334 closedir(dir); 335 put_tracing_file(path); 336 337 return err; 338 } 339 340 static int record_proc_kallsyms(void) 341 { 342 unsigned long long size = 0; 343 /* 344 * Just to keep older perf.data file parsers happy, record a zero 345 * sized kallsyms file, i.e. do the same thing that was done when 346 * /proc/kallsyms (or something specified via --kallsyms, in a 347 * different path) couldn't be read. 348 */ 349 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 if (!path) { 361 pr_debug("can't get tracing/printk_formats"); 362 return -ENOMEM; 363 } 364 365 ret = stat(path, &st); 366 if (ret < 0) { 367 /* not found */ 368 size = 0; 369 if (write(output_fd, &size, 4) != 4) 370 err = -EIO; 371 goto out; 372 } 373 err = record_file(path, 4); 374 375 out: 376 put_tracing_file(path); 377 return err; 378 } 379 380 static int record_saved_cmdline(void) 381 { 382 unsigned int size; 383 char *path; 384 struct stat st; 385 int ret, err = 0; 386 387 path = get_tracing_file("saved_cmdlines"); 388 if (!path) { 389 pr_debug("can't get tracing/saved_cmdline"); 390 return -ENOMEM; 391 } 392 393 ret = stat(path, &st); 394 if (ret < 0) { 395 /* not found */ 396 size = 0; 397 if (write(output_fd, &size, 8) != 8) 398 err = -EIO; 399 goto out; 400 } 401 err = record_file(path, 8); 402 403 out: 404 put_tracing_file(path); 405 return err; 406 } 407 408 static void 409 put_tracepoints_path(struct tracepoint_path *tps) 410 { 411 while (tps) { 412 struct tracepoint_path *t = tps; 413 414 tps = tps->next; 415 zfree(&t->name); 416 zfree(&t->system); 417 free(t); 418 } 419 } 420 421 static struct tracepoint_path * 422 get_tracepoints_path(struct list_head *pattrs) 423 { 424 struct tracepoint_path path, *ppath = &path; 425 struct perf_evsel *pos; 426 int nr_tracepoints = 0; 427 428 list_for_each_entry(pos, pattrs, node) { 429 if (pos->attr.type != PERF_TYPE_TRACEPOINT) 430 continue; 431 ++nr_tracepoints; 432 433 if (pos->name) { 434 ppath->next = tracepoint_name_to_path(pos->name); 435 if (ppath->next) 436 goto next; 437 438 if (strchr(pos->name, ':') == NULL) 439 goto try_id; 440 441 goto error; 442 } 443 444 try_id: 445 ppath->next = tracepoint_id_to_path(pos->attr.config); 446 if (!ppath->next) { 447 error: 448 pr_debug("No memory to alloc tracepoints list\n"); 449 put_tracepoints_path(&path); 450 return NULL; 451 } 452 next: 453 ppath = ppath->next; 454 } 455 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 list_for_each_entry(pos, pattrs, node) 464 if (pos->attr.type == PERF_TYPE_TRACEPOINT) 465 return true; 466 467 return false; 468 } 469 470 static int tracing_data_header(void) 471 { 472 char buf[20]; 473 ssize_t size; 474 475 /* just guessing this is someone's birthday.. ;) */ 476 buf[0] = 23; 477 buf[1] = 8; 478 buf[2] = 68; 479 memcpy(buf + 3, "tracing", 7); 480 481 if (write(output_fd, buf, 10) != 10) 482 return -1; 483 484 size = strlen(VERSION) + 1; 485 if (write(output_fd, VERSION, size) != size) 486 return -1; 487 488 /* save endian */ 489 if (bigendian()) 490 buf[0] = 1; 491 else 492 buf[0] = 0; 493 494 if (write(output_fd, buf, 1) != 1) 495 return -1; 496 497 /* save size of long */ 498 buf[0] = sizeof(long); 499 if (write(output_fd, buf, 1) != 1) 500 return -1; 501 502 /* save page_size */ 503 if (write(output_fd, &page_size, 4) != 4) 504 return -1; 505 506 return 0; 507 } 508 509 struct tracing_data *tracing_data_get(struct list_head *pattrs, 510 int fd, bool temp) 511 { 512 struct tracepoint_path *tps; 513 struct tracing_data *tdata; 514 int err; 515 516 output_fd = fd; 517 518 tps = get_tracepoints_path(pattrs); 519 if (!tps) 520 return NULL; 521 522 tdata = malloc(sizeof(*tdata)); 523 if (!tdata) 524 return NULL; 525 526 tdata->temp = temp; 527 tdata->size = 0; 528 529 if (temp) { 530 int temp_fd; 531 532 snprintf(tdata->temp_file, sizeof(tdata->temp_file), 533 "/tmp/perf-XXXXXX"); 534 if (!mkstemp(tdata->temp_file)) { 535 pr_debug("Can't make temp file"); 536 return NULL; 537 } 538 539 temp_fd = open(tdata->temp_file, O_RDWR); 540 if (temp_fd < 0) { 541 pr_debug("Can't read '%s'", tdata->temp_file); 542 return NULL; 543 } 544 545 /* 546 * Set the temp file the default output, so all the 547 * tracing data are stored into it. 548 */ 549 output_fd = temp_fd; 550 } 551 552 err = tracing_data_header(); 553 if (err) 554 goto out; 555 err = record_header_files(); 556 if (err) 557 goto out; 558 err = record_ftrace_files(tps); 559 if (err) 560 goto out; 561 err = record_event_files(tps); 562 if (err) 563 goto out; 564 err = record_proc_kallsyms(); 565 if (err) 566 goto out; 567 err = record_ftrace_printk(); 568 if (err) 569 goto out; 570 err = record_saved_cmdline(); 571 572 out: 573 /* 574 * All tracing data are stored by now, we can restore 575 * the default output file in case we used temp file. 576 */ 577 if (temp) { 578 tdata->size = lseek(output_fd, 0, SEEK_CUR); 579 close(output_fd); 580 output_fd = fd; 581 } 582 583 if (err) 584 zfree(&tdata); 585 586 put_tracepoints_path(tps); 587 return tdata; 588 } 589 590 int tracing_data_put(struct tracing_data *tdata) 591 { 592 int err = 0; 593 594 if (tdata->temp) { 595 err = record_file(tdata->temp_file, 0); 596 unlink(tdata->temp_file); 597 } 598 599 free(tdata); 600 return err; 601 } 602 603 int read_tracing_data(int fd, struct list_head *pattrs) 604 { 605 int err; 606 struct tracing_data *tdata; 607 608 /* 609 * We work over the real file, so we can write data 610 * directly, no temp file is needed. 611 */ 612 tdata = tracing_data_get(pattrs, fd, false); 613 if (!tdata) 614 return -ENOMEM; 615 616 err = tracing_data_put(tdata); 617 return err; 618 } 619