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