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