linux/tools/perf/builtin-inject.c
<<
>>
Prefs
   1/*
   2 * builtin-inject.c
   3 *
   4 * Builtin inject command: Examine the live mode (stdin) event stream
   5 * and repipe it to stdout while optionally injecting additional
   6 * events into it.
   7 */
   8#include "builtin.h"
   9
  10#include "perf.h"
  11#include "util/color.h"
  12#include "util/evlist.h"
  13#include "util/evsel.h"
  14#include "util/session.h"
  15#include "util/tool.h"
  16#include "util/debug.h"
  17#include "util/build-id.h"
  18#include "util/data.h"
  19#include "util/auxtrace.h"
  20
  21#include "util/parse-options.h"
  22
  23#include <linux/list.h>
  24
  25struct perf_inject {
  26        struct perf_tool        tool;
  27        struct perf_session     *session;
  28        bool                    build_ids;
  29        bool                    sched_stat;
  30        bool                    have_auxtrace;
  31        const char              *input_name;
  32        struct perf_data_file   output;
  33        u64                     bytes_written;
  34        struct list_head        samples;
  35        struct itrace_synth_opts itrace_synth_opts;
  36};
  37
  38struct event_entry {
  39        struct list_head node;
  40        u32              tid;
  41        union perf_event event[0];
  42};
  43
  44static int output_bytes(struct perf_inject *inject, void *buf, size_t sz)
  45{
  46        ssize_t size;
  47
  48        size = perf_data_file__write(&inject->output, buf, sz);
  49        if (size < 0)
  50                return -errno;
  51
  52        inject->bytes_written += size;
  53        return 0;
  54}
  55
  56static int perf_event__repipe_synth(struct perf_tool *tool,
  57                                    union perf_event *event)
  58{
  59        struct perf_inject *inject = container_of(tool, struct perf_inject,
  60                                                  tool);
  61
  62        return output_bytes(inject, event, event->header.size);
  63}
  64
  65static int perf_event__repipe_oe_synth(struct perf_tool *tool,
  66                                       union perf_event *event,
  67                                       struct ordered_events *oe __maybe_unused)
  68{
  69        return perf_event__repipe_synth(tool, event);
  70}
  71
  72static int perf_event__repipe_op2_synth(struct perf_tool *tool,
  73                                        union perf_event *event,
  74                                        struct perf_session *session
  75                                        __maybe_unused)
  76{
  77        return perf_event__repipe_synth(tool, event);
  78}
  79
  80static int perf_event__repipe_attr(struct perf_tool *tool,
  81                                   union perf_event *event,
  82                                   struct perf_evlist **pevlist)
  83{
  84        struct perf_inject *inject = container_of(tool, struct perf_inject,
  85                                                  tool);
  86        int ret;
  87
  88        ret = perf_event__process_attr(tool, event, pevlist);
  89        if (ret)
  90                return ret;
  91
  92        if (!inject->output.is_pipe)
  93                return 0;
  94
  95        return perf_event__repipe_synth(tool, event);
  96}
  97
  98#ifdef HAVE_AUXTRACE_SUPPORT
  99
 100static int copy_bytes(struct perf_inject *inject, int fd, off_t size)
 101{
 102        char buf[4096];
 103        ssize_t ssz;
 104        int ret;
 105
 106        while (size > 0) {
 107                ssz = read(fd, buf, min(size, (off_t)sizeof(buf)));
 108                if (ssz < 0)
 109                        return -errno;
 110                ret = output_bytes(inject, buf, ssz);
 111                if (ret)
 112                        return ret;
 113                size -= ssz;
 114        }
 115
 116        return 0;
 117}
 118
 119static s64 perf_event__repipe_auxtrace(struct perf_tool *tool,
 120                                       union perf_event *event,
 121                                       struct perf_session *session
 122                                       __maybe_unused)
 123{
 124        struct perf_inject *inject = container_of(tool, struct perf_inject,
 125                                                  tool);
 126        int ret;
 127
 128        inject->have_auxtrace = true;
 129
 130        if (!inject->output.is_pipe) {
 131                off_t offset;
 132
 133                offset = lseek(inject->output.fd, 0, SEEK_CUR);
 134                if (offset == -1)
 135                        return -errno;
 136                ret = auxtrace_index__auxtrace_event(&session->auxtrace_index,
 137                                                     event, offset);
 138                if (ret < 0)
 139                        return ret;
 140        }
 141
 142        if (perf_data_file__is_pipe(session->file) || !session->one_mmap) {
 143                ret = output_bytes(inject, event, event->header.size);
 144                if (ret < 0)
 145                        return ret;
 146                ret = copy_bytes(inject, perf_data_file__fd(session->file),
 147                                 event->auxtrace.size);
 148        } else {
 149                ret = output_bytes(inject, event,
 150                                   event->header.size + event->auxtrace.size);
 151        }
 152        if (ret < 0)
 153                return ret;
 154
 155        return event->auxtrace.size;
 156}
 157
 158#else
 159
 160static s64
 161perf_event__repipe_auxtrace(struct perf_tool *tool __maybe_unused,
 162                            union perf_event *event __maybe_unused,
 163                            struct perf_session *session __maybe_unused)
 164{
 165        pr_err("AUX area tracing not supported\n");
 166        return -EINVAL;
 167}
 168
 169#endif
 170
 171static int perf_event__repipe(struct perf_tool *tool,
 172                              union perf_event *event,
 173                              struct perf_sample *sample __maybe_unused,
 174                              struct machine *machine __maybe_unused)
 175{
 176        return perf_event__repipe_synth(tool, event);
 177}
 178
 179typedef int (*inject_handler)(struct perf_tool *tool,
 180                              union perf_event *event,
 181                              struct perf_sample *sample,
 182                              struct perf_evsel *evsel,
 183                              struct machine *machine);
 184
 185static int perf_event__repipe_sample(struct perf_tool *tool,
 186                                     union perf_event *event,
 187                                     struct perf_sample *sample,
 188                                     struct perf_evsel *evsel,
 189                                     struct machine *machine)
 190{
 191        if (evsel->handler) {
 192                inject_handler f = evsel->handler;
 193                return f(tool, event, sample, evsel, machine);
 194        }
 195
 196        build_id__mark_dso_hit(tool, event, sample, evsel, machine);
 197
 198        return perf_event__repipe_synth(tool, event);
 199}
 200
 201static int perf_event__repipe_mmap(struct perf_tool *tool,
 202                                   union perf_event *event,
 203                                   struct perf_sample *sample,
 204                                   struct machine *machine)
 205{
 206        int err;
 207
 208        err = perf_event__process_mmap(tool, event, sample, machine);
 209        perf_event__repipe(tool, event, sample, machine);
 210
 211        return err;
 212}
 213
 214static int perf_event__repipe_mmap2(struct perf_tool *tool,
 215                                   union perf_event *event,
 216                                   struct perf_sample *sample,
 217                                   struct machine *machine)
 218{
 219        int err;
 220
 221        err = perf_event__process_mmap2(tool, event, sample, machine);
 222        perf_event__repipe(tool, event, sample, machine);
 223
 224        return err;
 225}
 226
 227static int perf_event__repipe_fork(struct perf_tool *tool,
 228                                   union perf_event *event,
 229                                   struct perf_sample *sample,
 230                                   struct machine *machine)
 231{
 232        int err;
 233
 234        err = perf_event__process_fork(tool, event, sample, machine);
 235        perf_event__repipe(tool, event, sample, machine);
 236
 237        return err;
 238}
 239
 240static int perf_event__repipe_comm(struct perf_tool *tool,
 241                                   union perf_event *event,
 242                                   struct perf_sample *sample,
 243                                   struct machine *machine)
 244{
 245        int err;
 246
 247        err = perf_event__process_comm(tool, event, sample, machine);
 248        perf_event__repipe(tool, event, sample, machine);
 249
 250        return err;
 251}
 252
 253static int perf_event__repipe_exit(struct perf_tool *tool,
 254                                   union perf_event *event,
 255                                   struct perf_sample *sample,
 256                                   struct machine *machine)
 257{
 258        int err;
 259
 260        err = perf_event__process_exit(tool, event, sample, machine);
 261        perf_event__repipe(tool, event, sample, machine);
 262
 263        return err;
 264}
 265
 266static int perf_event__repipe_tracing_data(struct perf_tool *tool,
 267                                           union perf_event *event,
 268                                           struct perf_session *session)
 269{
 270        int err;
 271
 272        perf_event__repipe_synth(tool, event);
 273        err = perf_event__process_tracing_data(tool, event, session);
 274
 275        return err;
 276}
 277
 278static int perf_event__repipe_id_index(struct perf_tool *tool,
 279                                       union perf_event *event,
 280                                       struct perf_session *session)
 281{
 282        int err;
 283
 284        perf_event__repipe_synth(tool, event);
 285        err = perf_event__process_id_index(tool, event, session);
 286
 287        return err;
 288}
 289
 290static int dso__read_build_id(struct dso *dso)
 291{
 292        if (dso->has_build_id)
 293                return 0;
 294
 295        if (filename__read_build_id(dso->long_name, dso->build_id,
 296                                    sizeof(dso->build_id)) > 0) {
 297                dso->has_build_id = true;
 298                return 0;
 299        }
 300
 301        return -1;
 302}
 303
 304static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool,
 305                                struct machine *machine)
 306{
 307        u16 misc = PERF_RECORD_MISC_USER;
 308        int err;
 309
 310        if (dso__read_build_id(dso) < 0) {
 311                pr_debug("no build_id found for %s\n", dso->long_name);
 312                return -1;
 313        }
 314
 315        if (dso->kernel)
 316                misc = PERF_RECORD_MISC_KERNEL;
 317
 318        err = perf_event__synthesize_build_id(tool, dso, misc, perf_event__repipe,
 319                                              machine);
 320        if (err) {
 321                pr_err("Can't synthesize build_id event for %s\n", dso->long_name);
 322                return -1;
 323        }
 324
 325        return 0;
 326}
 327
 328static int perf_event__inject_buildid(struct perf_tool *tool,
 329                                      union perf_event *event,
 330                                      struct perf_sample *sample,
 331                                      struct perf_evsel *evsel __maybe_unused,
 332                                      struct machine *machine)
 333{
 334        struct addr_location al;
 335        struct thread *thread;
 336        u8 cpumode;
 337
 338        cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 339
 340        thread = machine__findnew_thread(machine, sample->pid, sample->tid);
 341        if (thread == NULL) {
 342                pr_err("problem processing %d event, skipping it.\n",
 343                       event->header.type);
 344                goto repipe;
 345        }
 346
 347        thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
 348
 349        if (al.map != NULL) {
 350                if (!al.map->dso->hit) {
 351                        al.map->dso->hit = 1;
 352                        if (map__load(al.map, NULL) >= 0) {
 353                                dso__inject_build_id(al.map->dso, tool, machine);
 354                                /*
 355                                 * If this fails, too bad, let the other side
 356                                 * account this as unresolved.
 357                                 */
 358                        } else {
 359#ifdef HAVE_LIBELF_SUPPORT
 360                                pr_warning("no symbols found in %s, maybe "
 361                                           "install a debug package?\n",
 362                                           al.map->dso->long_name);
 363#endif
 364                        }
 365                }
 366        }
 367
 368        thread__put(thread);
 369repipe:
 370        perf_event__repipe(tool, event, sample, machine);
 371        return 0;
 372}
 373
 374static int perf_inject__sched_process_exit(struct perf_tool *tool,
 375                                           union perf_event *event __maybe_unused,
 376                                           struct perf_sample *sample,
 377                                           struct perf_evsel *evsel __maybe_unused,
 378                                           struct machine *machine __maybe_unused)
 379{
 380        struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
 381        struct event_entry *ent;
 382
 383        list_for_each_entry(ent, &inject->samples, node) {
 384                if (sample->tid == ent->tid) {
 385                        list_del_init(&ent->node);
 386                        free(ent);
 387                        break;
 388                }
 389        }
 390
 391        return 0;
 392}
 393
 394static int perf_inject__sched_switch(struct perf_tool *tool,
 395                                     union perf_event *event,
 396                                     struct perf_sample *sample,
 397                                     struct perf_evsel *evsel,
 398                                     struct machine *machine)
 399{
 400        struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
 401        struct event_entry *ent;
 402
 403        perf_inject__sched_process_exit(tool, event, sample, evsel, machine);
 404
 405        ent = malloc(event->header.size + sizeof(struct event_entry));
 406        if (ent == NULL) {
 407                color_fprintf(stderr, PERF_COLOR_RED,
 408                             "Not enough memory to process sched switch event!");
 409                return -1;
 410        }
 411
 412        ent->tid = sample->tid;
 413        memcpy(&ent->event, event, event->header.size);
 414        list_add(&ent->node, &inject->samples);
 415        return 0;
 416}
 417
 418static int perf_inject__sched_stat(struct perf_tool *tool,
 419                                   union perf_event *event __maybe_unused,
 420                                   struct perf_sample *sample,
 421                                   struct perf_evsel *evsel,
 422                                   struct machine *machine)
 423{
 424        struct event_entry *ent;
 425        union perf_event *event_sw;
 426        struct perf_sample sample_sw;
 427        struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
 428        u32 pid = perf_evsel__intval(evsel, sample, "pid");
 429
 430        list_for_each_entry(ent, &inject->samples, node) {
 431                if (pid == ent->tid)
 432                        goto found;
 433        }
 434
 435        return 0;
 436found:
 437        event_sw = &ent->event[0];
 438        perf_evsel__parse_sample(evsel, event_sw, &sample_sw);
 439
 440        sample_sw.period = sample->period;
 441        sample_sw.time   = sample->time;
 442        perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
 443                                      evsel->attr.read_format, &sample_sw,
 444                                      false);
 445        build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
 446        return perf_event__repipe(tool, event_sw, &sample_sw, machine);
 447}
 448
 449static void sig_handler(int sig __maybe_unused)
 450{
 451        session_done = 1;
 452}
 453
 454static int perf_evsel__check_stype(struct perf_evsel *evsel,
 455                                   u64 sample_type, const char *sample_msg)
 456{
 457        struct perf_event_attr *attr = &evsel->attr;
 458        const char *name = perf_evsel__name(evsel);
 459
 460        if (!(attr->sample_type & sample_type)) {
 461                pr_err("Samples for %s event do not have %s attribute set.",
 462                        name, sample_msg);
 463                return -EINVAL;
 464        }
 465
 466        return 0;
 467}
 468
 469static int __cmd_inject(struct perf_inject *inject)
 470{
 471        int ret = -EINVAL;
 472        struct perf_session *session = inject->session;
 473        struct perf_data_file *file_out = &inject->output;
 474        int fd = perf_data_file__fd(file_out);
 475        u64 output_data_offset;
 476
 477        signal(SIGINT, sig_handler);
 478
 479        if (inject->build_ids || inject->sched_stat ||
 480            inject->itrace_synth_opts.set) {
 481                inject->tool.mmap         = perf_event__repipe_mmap;
 482                inject->tool.mmap2        = perf_event__repipe_mmap2;
 483                inject->tool.fork         = perf_event__repipe_fork;
 484                inject->tool.tracing_data = perf_event__repipe_tracing_data;
 485        }
 486
 487        output_data_offset = session->header.data_offset;
 488
 489        if (inject->build_ids) {
 490                inject->tool.sample = perf_event__inject_buildid;
 491        } else if (inject->sched_stat) {
 492                struct perf_evsel *evsel;
 493
 494                evlist__for_each(session->evlist, evsel) {
 495                        const char *name = perf_evsel__name(evsel);
 496
 497                        if (!strcmp(name, "sched:sched_switch")) {
 498                                if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))
 499                                        return -EINVAL;
 500
 501                                evsel->handler = perf_inject__sched_switch;
 502                        } else if (!strcmp(name, "sched:sched_process_exit"))
 503                                evsel->handler = perf_inject__sched_process_exit;
 504                        else if (!strncmp(name, "sched:sched_stat_", 17))
 505                                evsel->handler = perf_inject__sched_stat;
 506                }
 507        } else if (inject->itrace_synth_opts.set) {
 508                session->itrace_synth_opts = &inject->itrace_synth_opts;
 509                inject->itrace_synth_opts.inject = true;
 510                inject->tool.comm           = perf_event__repipe_comm;
 511                inject->tool.exit           = perf_event__repipe_exit;
 512                inject->tool.id_index       = perf_event__repipe_id_index;
 513                inject->tool.auxtrace_info  = perf_event__process_auxtrace_info;
 514                inject->tool.auxtrace       = perf_event__process_auxtrace;
 515                inject->tool.ordered_events = true;
 516                inject->tool.ordering_requires_timestamps = true;
 517                /* Allow space in the header for new attributes */
 518                output_data_offset = 4096;
 519        }
 520
 521        if (!inject->itrace_synth_opts.set)
 522                auxtrace_index__free(&session->auxtrace_index);
 523
 524        if (!file_out->is_pipe)
 525                lseek(fd, output_data_offset, SEEK_SET);
 526
 527        ret = perf_session__process_events(session);
 528
 529        if (!file_out->is_pipe) {
 530                if (inject->build_ids) {
 531                        perf_header__set_feat(&session->header,
 532                                              HEADER_BUILD_ID);
 533                        if (inject->have_auxtrace)
 534                                dsos__hit_all(session);
 535                }
 536                /*
 537                 * The AUX areas have been removed and replaced with
 538                 * synthesized hardware events, so clear the feature flag.
 539                 */
 540                if (inject->itrace_synth_opts.set)
 541                        perf_header__clear_feat(&session->header,
 542                                                HEADER_AUXTRACE);
 543                session->header.data_offset = output_data_offset;
 544                session->header.data_size = inject->bytes_written;
 545                perf_session__write_header(session, session->evlist, fd, true);
 546        }
 547
 548        return ret;
 549}
 550
 551int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
 552{
 553        struct perf_inject inject = {
 554                .tool = {
 555                        .sample         = perf_event__repipe_sample,
 556                        .mmap           = perf_event__repipe,
 557                        .mmap2          = perf_event__repipe,
 558                        .comm           = perf_event__repipe,
 559                        .fork           = perf_event__repipe,
 560                        .exit           = perf_event__repipe,
 561                        .lost           = perf_event__repipe,
 562                        .aux            = perf_event__repipe,
 563                        .itrace_start   = perf_event__repipe,
 564                        .context_switch = perf_event__repipe,
 565                        .read           = perf_event__repipe_sample,
 566                        .throttle       = perf_event__repipe,
 567                        .unthrottle     = perf_event__repipe,
 568                        .attr           = perf_event__repipe_attr,
 569                        .tracing_data   = perf_event__repipe_op2_synth,
 570                        .auxtrace_info  = perf_event__repipe_op2_synth,
 571                        .auxtrace       = perf_event__repipe_auxtrace,
 572                        .auxtrace_error = perf_event__repipe_op2_synth,
 573                        .finished_round = perf_event__repipe_oe_synth,
 574                        .build_id       = perf_event__repipe_op2_synth,
 575                        .id_index       = perf_event__repipe_op2_synth,
 576                },
 577                .input_name  = "-",
 578                .samples = LIST_HEAD_INIT(inject.samples),
 579                .output = {
 580                        .path = "-",
 581                        .mode = PERF_DATA_MODE_WRITE,
 582                },
 583        };
 584        struct perf_data_file file = {
 585                .mode = PERF_DATA_MODE_READ,
 586        };
 587        int ret;
 588
 589        const struct option options[] = {
 590                OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
 591                            "Inject build-ids into the output stream"),
 592                OPT_STRING('i', "input", &inject.input_name, "file",
 593                           "input file name"),
 594                OPT_STRING('o', "output", &inject.output.path, "file",
 595                           "output file name"),
 596                OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
 597                            "Merge sched-stat and sched-switch for getting events "
 598                            "where and how long tasks slept"),
 599                OPT_INCR('v', "verbose", &verbose,
 600                         "be more verbose (show build ids, etc)"),
 601                OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file",
 602                           "kallsyms pathname"),
 603                OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
 604                OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts,
 605                                    NULL, "opts", "Instruction Tracing options",
 606                                    itrace_parse_synth_opts),
 607                OPT_END()
 608        };
 609        const char * const inject_usage[] = {
 610                "perf inject [<options>]",
 611                NULL
 612        };
 613
 614        argc = parse_options(argc, argv, options, inject_usage, 0);
 615
 616        /*
 617         * Any (unrecognized) arguments left?
 618         */
 619        if (argc)
 620                usage_with_options(inject_usage, options);
 621
 622        if (perf_data_file__open(&inject.output)) {
 623                perror("failed to create output file");
 624                return -1;
 625        }
 626
 627        inject.tool.ordered_events = inject.sched_stat;
 628
 629        file.path = inject.input_name;
 630        inject.session = perf_session__new(&file, true, &inject.tool);
 631        if (inject.session == NULL)
 632                return -1;
 633
 634        ret = symbol__init(&inject.session->header.env);
 635        if (ret < 0)
 636                goto out_delete;
 637
 638        ret = __cmd_inject(&inject);
 639
 640out_delete:
 641        perf_session__delete(inject.session);
 642        return ret;
 643}
 644