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 = get_events_file("header_page"); 107 struct stat st; 108 int err = -EIO; 109 110 if (!path) { 111 pr_debug("can't get tracing/events/header_page"); 112 return -ENOMEM; 113 } 114 115 if (stat(path, &st) < 0) { 116 pr_debug("can't read '%s'", path); 117 goto out; 118 } 119 120 if (write(output_fd, "header_page", 12) != 12) { 121 pr_debug("can't write header_page\n"); 122 goto out; 123 } 124 125 if (record_file(path, 8) < 0) { 126 pr_debug("can't record header_page file\n"); 127 goto out; 128 } 129 130 put_events_file(path); 131 132 path = get_events_file("header_event"); 133 if (!path) { 134 pr_debug("can't get tracing/events/header_event"); 135 err = -ENOMEM; 136 goto out; 137 } 138 139 if (stat(path, &st) < 0) { 140 pr_debug("can't read '%s'", path); 141 goto out; 142 } 143 144 if (write(output_fd, "header_event", 13) != 13) { 145 pr_debug("can't write header_event\n"); 146 goto out; 147 } 148 149 if (record_file(path, 8) < 0) { 150 pr_debug("can't record header_event file\n"); 151 goto out; 152 } 153 154 err = 0; 155 out: 156 put_events_file(path); 157 return err; 158 } 159 160 static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) 161 { 162 while (tps) { 163 if (!strcmp(sys, tps->name)) 164 return true; 165 tps = tps->next; 166 } 167 168 return false; 169 } 170 171 #define for_each_event(dir, dent, tps) \ 172 while ((dent = readdir(dir))) \ 173 if (dent->d_type == DT_DIR && \ 174 (strcmp(dent->d_name, ".")) && \ 175 (strcmp(dent->d_name, ".."))) \ 176 177 static int copy_event_system(const char *sys, struct tracepoint_path *tps) 178 { 179 struct dirent *dent; 180 struct stat st; 181 char *format; 182 DIR *dir; 183 int count = 0; 184 int ret; 185 int err; 186 187 dir = opendir(sys); 188 if (!dir) { 189 pr_debug("can't read directory '%s'", sys); 190 return -errno; 191 } 192 193 for_each_event(dir, dent, tps) { 194 if (!name_in_tp_list(dent->d_name, tps)) 195 continue; 196 197 if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) { 198 err = -ENOMEM; 199 goto out; 200 } 201 ret = stat(format, &st); 202 free(format); 203 if (ret < 0) 204 continue; 205 count++; 206 } 207 208 if (write(output_fd, &count, 4) != 4) { 209 err = -EIO; 210 pr_debug("can't write count\n"); 211 goto out; 212 } 213 214 rewinddir(dir); 215 for_each_event(dir, dent, tps) { 216 if (!name_in_tp_list(dent->d_name, tps)) 217 continue; 218 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; 235 out: 236 closedir(dir); 237 return err; 238 } 239 240 static int record_ftrace_files(struct tracepoint_path *tps) 241 { 242 char *path; 243 int ret; 244 245 path = get_events_file("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 258 static 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 269 static 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 for_each_event(dir, dent, tps) { 294 if (strcmp(dent->d_name, "ftrace") == 0 || 295 !system_in_tp_list(dent->d_name, tps)) 296 continue; 297 298 count++; 299 } 300 301 if (write(output_fd, &count, 4) != 4) { 302 err = -EIO; 303 pr_debug("can't write count\n"); 304 goto out; 305 } 306 307 rewinddir(dir); 308 for_each_event(dir, dent, tps) { 309 if (strcmp(dent->d_name, "ftrace") == 0 || 310 !system_in_tp_list(dent->d_name, tps)) 311 continue; 312 313 if (asprintf(&sys, "%s/%s", path, dent->d_name) < 0) { 314 err = -ENOMEM; 315 goto out; 316 } 317 ret = stat(sys, &st); 318 if (ret >= 0) { 319 ssize_t size = strlen(dent->d_name) + 1; 320 321 if (write(output_fd, dent->d_name, size) != size || 322 copy_event_system(sys, tps) < 0) { 323 err = -EIO; 324 free(sys); 325 goto out; 326 } 327 } 328 free(sys); 329 } 330 err = 0; 331 out: 332 closedir(dir); 333 put_tracing_file(path); 334 335 return err; 336 } 337 338 static int record_proc_kallsyms(void) 339 { 340 unsigned long long size = 0; 341 /* 342 * Just to keep older perf.data file parsers happy, record a zero 343 * sized kallsyms file, i.e. do the same thing that was done when 344 * /proc/kallsyms (or something specified via --kallsyms, in a 345 * different path) couldn't be read. 346 */ 347 return write(output_fd, &size, 4) != 4 ? -EIO : 0; 348 } 349 350 static int record_ftrace_printk(void) 351 { 352 unsigned int size; 353 char *path; 354 struct stat st; 355 int ret, err = 0; 356 357 path = get_tracing_file("printk_formats"); 358 if (!path) { 359 pr_debug("can't get tracing/printk_formats"); 360 return -ENOMEM; 361 } 362 363 ret = stat(path, &st); 364 if (ret < 0) { 365 /* not found */ 366 size = 0; 367 if (write(output_fd, &size, 4) != 4) 368 err = -EIO; 369 goto out; 370 } 371 err = record_file(path, 4); 372 373 out: 374 put_tracing_file(path); 375 return err; 376 } 377 378 static int record_saved_cmdline(void) 379 { 380 unsigned int size; 381 char *path; 382 struct stat st; 383 int ret, err = 0; 384 385 path = get_tracing_file("saved_cmdlines"); 386 if (!path) { 387 pr_debug("can't get tracing/saved_cmdline"); 388 return -ENOMEM; 389 } 390 391 ret = stat(path, &st); 392 if (ret < 0) { 393 /* not found */ 394 size = 0; 395 if (write(output_fd, &size, 8) != 8) 396 err = -EIO; 397 goto out; 398 } 399 err = record_file(path, 8); 400 401 out: 402 put_tracing_file(path); 403 return err; 404 } 405 406 static void 407 put_tracepoints_path(struct tracepoint_path *tps) 408 { 409 while (tps) { 410 struct tracepoint_path *t = tps; 411 412 tps = tps->next; 413 zfree(&t->name); 414 zfree(&t->system); 415 free(t); 416 } 417 } 418 419 static struct tracepoint_path * 420 get_tracepoints_path(struct list_head *pattrs) 421 { 422 struct tracepoint_path path, *ppath = &path; 423 struct perf_evsel *pos; 424 int nr_tracepoints = 0; 425 426 list_for_each_entry(pos, pattrs, node) { 427 if (pos->attr.type != PERF_TYPE_TRACEPOINT) 428 continue; 429 ++nr_tracepoints; 430 431 if (pos->name) { 432 ppath->next = tracepoint_name_to_path(pos->name); 433 if (ppath->next) 434 goto next; 435 436 if (strchr(pos->name, ':') == NULL) 437 goto try_id; 438 439 goto error; 440 } 441 442 try_id: 443 ppath->next = tracepoint_id_to_path(pos->attr.config); 444 if (!ppath->next) { 445 error: 446 pr_debug("No memory to alloc tracepoints list\n"); 447 put_tracepoints_path(&path); 448 return NULL; 449 } 450 next: 451 ppath = ppath->next; 452 } 453 454 return nr_tracepoints > 0 ? path.next : NULL; 455 } 456 457 bool have_tracepoints(struct list_head *pattrs) 458 { 459 struct perf_evsel *pos; 460 461 list_for_each_entry(pos, pattrs, node) 462 if (pos->attr.type == PERF_TYPE_TRACEPOINT) 463 return true; 464 465 return false; 466 } 467 468 static int tracing_data_header(void) 469 { 470 char buf[20]; 471 ssize_t size; 472 473 /* just guessing this is someone's birthday.. ;) */ 474 buf[0] = 23; 475 buf[1] = 8; 476 buf[2] = 68; 477 memcpy(buf + 3, "tracing", 7); 478 479 if (write(output_fd, buf, 10) != 10) 480 return -1; 481 482 size = strlen(VERSION) + 1; 483 if (write(output_fd, VERSION, size) != size) 484 return -1; 485 486 /* save endian */ 487 if (bigendian()) 488 buf[0] = 1; 489 else 490 buf[0] = 0; 491 492 if (write(output_fd, buf, 1) != 1) 493 return -1; 494 495 /* save size of long */ 496 buf[0] = sizeof(long); 497 if (write(output_fd, buf, 1) != 1) 498 return -1; 499 500 /* save page_size */ 501 if (write(output_fd, &page_size, 4) != 4) 502 return -1; 503 504 return 0; 505 } 506 507 struct tracing_data *tracing_data_get(struct list_head *pattrs, 508 int fd, bool temp) 509 { 510 struct tracepoint_path *tps; 511 struct tracing_data *tdata; 512 int err; 513 514 output_fd = fd; 515 516 tps = get_tracepoints_path(pattrs); 517 if (!tps) 518 return NULL; 519 520 tdata = malloc(sizeof(*tdata)); 521 if (!tdata) 522 return NULL; 523 524 tdata->temp = temp; 525 tdata->size = 0; 526 527 if (temp) { 528 int temp_fd; 529 530 snprintf(tdata->temp_file, sizeof(tdata->temp_file), 531 "/tmp/perf-XXXXXX"); 532 if (!mkstemp(tdata->temp_file)) { 533 pr_debug("Can't make temp file"); 534 return NULL; 535 } 536 537 temp_fd = open(tdata->temp_file, O_RDWR); 538 if (temp_fd < 0) { 539 pr_debug("Can't read '%s'", tdata->temp_file); 540 return NULL; 541 } 542 543 /* 544 * Set the temp file the default output, so all the 545 * tracing data are stored into it. 546 */ 547 output_fd = temp_fd; 548 } 549 550 err = tracing_data_header(); 551 if (err) 552 goto out; 553 err = record_header_files(); 554 if (err) 555 goto out; 556 err = record_ftrace_files(tps); 557 if (err) 558 goto out; 559 err = record_event_files(tps); 560 if (err) 561 goto out; 562 err = record_proc_kallsyms(); 563 if (err) 564 goto out; 565 err = record_ftrace_printk(); 566 if (err) 567 goto out; 568 err = record_saved_cmdline(); 569 570 out: 571 /* 572 * All tracing data are stored by now, we can restore 573 * the default output file in case we used temp file. 574 */ 575 if (temp) { 576 tdata->size = lseek(output_fd, 0, SEEK_CUR); 577 close(output_fd); 578 output_fd = fd; 579 } 580 581 if (err) 582 zfree(&tdata); 583 584 put_tracepoints_path(tps); 585 return tdata; 586 } 587 588 int tracing_data_put(struct tracing_data *tdata) 589 { 590 int err = 0; 591 592 if (tdata->temp) { 593 err = record_file(tdata->temp_file, 0); 594 unlink(tdata->temp_file); 595 } 596 597 free(tdata); 598 return err; 599 } 600 601 int read_tracing_data(int fd, struct list_head *pattrs) 602 { 603 int err; 604 struct tracing_data *tdata; 605 606 /* 607 * We work over the real file, so we can write data 608 * directly, no temp file is needed. 609 */ 610 tdata = tracing_data_get(pattrs, fd, false); 611 if (!tdata) 612 return -ENOMEM; 613 614 err = tracing_data_put(tdata); 615 return err; 616 } 617