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