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
  19#include "util/parse-options.h"
  20
  21#include <linux/list.h>
  22
  23struct perf_inject {
  24        struct perf_tool tool;
  25        bool             build_ids;
  26        bool             sched_stat;
  27        const char       *input_name;
  28        int              pipe_output,
  29                         output;
  30        u64              bytes_written;
  31        struct list_head samples;
  32};
  33
  34struct event_entry {
  35        struct list_head node;
  36        u32              tid;
  37        union perf_event event[0];
  38};
  39
  40static int perf_event__repipe_synth(struct perf_tool *tool,
  41                                    union perf_event *event)
  42{
  43        struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
  44        uint32_t size;
  45        void *buf = event;
  46
  47        size = event->header.size;
  48
  49        while (size) {
  50                int ret = write(inject->output, buf, size);
  51                if (ret < 0)
  52                        return -errno;
  53
  54                size -= ret;
  55                buf += ret;
  56                inject->bytes_written += ret;
  57        }
  58
  59        return 0;
  60}
  61
  62static int perf_event__repipe_op2_synth(struct perf_tool *tool,
  63                                        union perf_event *event,
  64                                        struct perf_session *session
  65                                        __maybe_unused)
  66{
  67        return perf_event__repipe_synth(tool, event);
  68}
  69
  70static int perf_event__repipe_attr(struct perf_tool *tool,
  71                                   union perf_event *event,
  72                                   struct perf_evlist **pevlist)
  73{
  74        int ret;
  75
  76        ret = perf_event__process_attr(tool, event, pevlist);
  77        if (ret)
  78                return ret;
  79
  80        return perf_event__repipe_synth(tool, event);
  81}
  82
  83static int perf_event__repipe(struct perf_tool *tool,
  84                              union perf_event *event,
  85                              struct perf_sample *sample __maybe_unused,
  86                              struct machine *machine __maybe_unused)
  87{
  88        return perf_event__repipe_synth(tool, event);
  89}
  90
  91typedef int (*inject_handler)(struct perf_tool *tool,
  92                              union perf_event *event,
  93                              struct perf_sample *sample,
  94                              struct perf_evsel *evsel,
  95                              struct machine *machine);
  96
  97static int perf_event__repipe_sample(struct perf_tool *tool,
  98                                     union perf_event *event,
  99                                     struct perf_sample *sample,
 100                                     struct perf_evsel *evsel,
 101                                     struct machine *machine)
 102{
 103        if (evsel->handler.func) {
 104                inject_handler f = evsel->handler.func;
 105                return f(tool, event, sample, evsel, machine);
 106        }
 107
 108        build_id__mark_dso_hit(tool, event, sample, evsel, machine);
 109
 110        return perf_event__repipe_synth(tool, event);
 111}
 112
 113static int perf_event__repipe_mmap(struct perf_tool *tool,
 114                                   union perf_event *event,
 115                                   struct perf_sample *sample,
 116                                   struct machine *machine)
 117{
 118        int err;
 119
 120        err = perf_event__process_mmap(tool, event, sample, machine);
 121        perf_event__repipe(tool, event, sample, machine);
 122
 123        return err;
 124}
 125
 126static int perf_event__repipe_mmap2(struct perf_tool *tool,
 127                                   union perf_event *event,
 128                                   struct perf_sample *sample,
 129                                   struct machine *machine)
 130{
 131        int err;
 132
 133        err = perf_event__process_mmap2(tool, event, sample, machine);
 134        perf_event__repipe(tool, event, sample, machine);
 135
 136        return err;
 137}
 138
 139static int perf_event__repipe_fork(struct perf_tool *tool,
 140                                   union perf_event *event,
 141                                   struct perf_sample *sample,
 142                                   struct machine *machine)
 143{
 144        int err;
 145
 146        err = perf_event__process_fork(tool, event, sample, machine);
 147        perf_event__repipe(tool, event, sample, machine);
 148
 149        return err;
 150}
 151
 152static int perf_event__repipe_tracing_data(struct perf_tool *tool,
 153                                           union perf_event *event,
 154                                           struct perf_session *session)
 155{
 156        int err;
 157
 158        perf_event__repipe_synth(tool, event);
 159        err = perf_event__process_tracing_data(tool, event, session);
 160
 161        return err;
 162}
 163
 164static int dso__read_build_id(struct dso *self)
 165{
 166        if (self->has_build_id)
 167                return 0;
 168
 169        if (filename__read_build_id(self->long_name, self->build_id,
 170                                    sizeof(self->build_id)) > 0) {
 171                self->has_build_id = true;
 172                return 0;
 173        }
 174
 175        return -1;
 176}
 177
 178static int dso__inject_build_id(struct dso *self, struct perf_tool *tool,
 179                                struct machine *machine)
 180{
 181        u16 misc = PERF_RECORD_MISC_USER;
 182        int err;
 183
 184        if (dso__read_build_id(self) < 0) {
 185                pr_debug("no build_id found for %s\n", self->long_name);
 186                return -1;
 187        }
 188
 189        if (self->kernel)
 190                misc = PERF_RECORD_MISC_KERNEL;
 191
 192        err = perf_event__synthesize_build_id(tool, self, misc, perf_event__repipe,
 193                                              machine);
 194        if (err) {
 195                pr_err("Can't synthesize build_id event for %s\n", self->long_name);
 196                return -1;
 197        }
 198
 199        return 0;
 200}
 201
 202static int perf_event__inject_buildid(struct perf_tool *tool,
 203                                      union perf_event *event,
 204                                      struct perf_sample *sample,
 205                                      struct perf_evsel *evsel __maybe_unused,
 206                                      struct machine *machine)
 207{
 208        struct addr_location al;
 209        struct thread *thread;
 210        u8 cpumode;
 211
 212        cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 213
 214        thread = machine__findnew_thread(machine, sample->pid, sample->pid);
 215        if (thread == NULL) {
 216                pr_err("problem processing %d event, skipping it.\n",
 217                       event->header.type);
 218                goto repipe;
 219        }
 220
 221        thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
 222                              sample->ip, &al);
 223
 224        if (al.map != NULL) {
 225                if (!al.map->dso->hit) {
 226                        al.map->dso->hit = 1;
 227                        if (map__load(al.map, NULL) >= 0) {
 228                                dso__inject_build_id(al.map->dso, tool, machine);
 229                                /*
 230                                 * If this fails, too bad, let the other side
 231                                 * account this as unresolved.
 232                                 */
 233                        } else {
 234#ifdef LIBELF_SUPPORT
 235                                pr_warning("no symbols found in %s, maybe "
 236                                           "install a debug package?\n",
 237                                           al.map->dso->long_name);
 238#endif
 239                        }
 240                }
 241        }
 242
 243repipe:
 244        perf_event__repipe(tool, event, sample, machine);
 245        return 0;
 246}
 247
 248static int perf_inject__sched_process_exit(struct perf_tool *tool,
 249                                           union perf_event *event __maybe_unused,
 250                                           struct perf_sample *sample,
 251                                           struct perf_evsel *evsel __maybe_unused,
 252                                           struct machine *machine __maybe_unused)
 253{
 254        struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
 255        struct event_entry *ent;
 256
 257        list_for_each_entry(ent, &inject->samples, node) {
 258                if (sample->tid == ent->tid) {
 259                        list_del_init(&ent->node);
 260                        free(ent);
 261                        break;
 262                }
 263        }
 264
 265        return 0;
 266}
 267
 268static int perf_inject__sched_switch(struct perf_tool *tool,
 269                                     union perf_event *event,
 270                                     struct perf_sample *sample,
 271                                     struct perf_evsel *evsel,
 272                                     struct machine *machine)
 273{
 274        struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
 275        struct event_entry *ent;
 276
 277        perf_inject__sched_process_exit(tool, event, sample, evsel, machine);
 278
 279        ent = malloc(event->header.size + sizeof(struct event_entry));
 280        if (ent == NULL) {
 281                color_fprintf(stderr, PERF_COLOR_RED,
 282                             "Not enough memory to process sched switch event!");
 283                return -1;
 284        }
 285
 286        ent->tid = sample->tid;
 287        memcpy(&ent->event, event, event->header.size);
 288        list_add(&ent->node, &inject->samples);
 289        return 0;
 290}
 291
 292static int perf_inject__sched_stat(struct perf_tool *tool,
 293                                   union perf_event *event __maybe_unused,
 294                                   struct perf_sample *sample,
 295                                   struct perf_evsel *evsel,
 296                                   struct machine *machine)
 297{
 298        struct event_entry *ent;
 299        union perf_event *event_sw;
 300        struct perf_sample sample_sw;
 301        struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
 302        u32 pid = perf_evsel__intval(evsel, sample, "pid");
 303
 304        list_for_each_entry(ent, &inject->samples, node) {
 305                if (pid == ent->tid)
 306                        goto found;
 307        }
 308
 309        return 0;
 310found:
 311        event_sw = &ent->event[0];
 312        perf_evsel__parse_sample(evsel, event_sw, &sample_sw);
 313
 314        sample_sw.period = sample->period;
 315        sample_sw.time   = sample->time;
 316        perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
 317                                      evsel->attr.sample_regs_user,
 318                                      evsel->attr.read_format, &sample_sw,
 319                                      false);
 320        build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
 321        return perf_event__repipe(tool, event_sw, &sample_sw, machine);
 322}
 323
 324static void sig_handler(int sig __maybe_unused)
 325{
 326        session_done = 1;
 327}
 328
 329static int perf_evsel__check_stype(struct perf_evsel *evsel,
 330                                   u64 sample_type, const char *sample_msg)
 331{
 332        struct perf_event_attr *attr = &evsel->attr;
 333        const char *name = perf_evsel__name(evsel);
 334
 335        if (!(attr->sample_type & sample_type)) {
 336                pr_err("Samples for %s event do not have %s attribute set.",
 337                        name, sample_msg);
 338                return -EINVAL;
 339        }
 340
 341        return 0;
 342}
 343
 344static int __cmd_inject(struct perf_inject *inject)
 345{
 346        struct perf_session *session;
 347        int ret = -EINVAL;
 348
 349        signal(SIGINT, sig_handler);
 350
 351        if (inject->build_ids || inject->sched_stat) {
 352                inject->tool.mmap         = perf_event__repipe_mmap;
 353                inject->tool.mmap2        = perf_event__repipe_mmap2;
 354                inject->tool.fork         = perf_event__repipe_fork;
 355                inject->tool.tracing_data = perf_event__repipe_tracing_data;
 356        }
 357
 358        session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool);
 359        if (session == NULL)
 360                return -ENOMEM;
 361
 362        if (inject->build_ids) {
 363                inject->tool.sample = perf_event__inject_buildid;
 364        } else if (inject->sched_stat) {
 365                struct perf_evsel *evsel;
 366
 367                inject->tool.ordered_samples = true;
 368
 369                list_for_each_entry(evsel, &session->evlist->entries, node) {
 370                        const char *name = perf_evsel__name(evsel);
 371
 372                        if (!strcmp(name, "sched:sched_switch")) {
 373                                if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))
 374                                        return -EINVAL;
 375
 376                                evsel->handler.func = perf_inject__sched_switch;
 377                        } else if (!strcmp(name, "sched:sched_process_exit"))
 378                                evsel->handler.func = perf_inject__sched_process_exit;
 379                        else if (!strncmp(name, "sched:sched_stat_", 17))
 380                                evsel->handler.func = perf_inject__sched_stat;
 381                }
 382        }
 383
 384        if (!inject->pipe_output)
 385                lseek(inject->output, session->header.data_offset, SEEK_SET);
 386
 387        ret = perf_session__process_events(session, &inject->tool);
 388
 389        if (!inject->pipe_output) {
 390                session->header.data_size = inject->bytes_written;
 391                perf_session__write_header(session, session->evlist, inject->output, true);
 392        }
 393
 394        perf_session__delete(session);
 395
 396        return ret;
 397}
 398
 399int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
 400{
 401        struct perf_inject inject = {
 402                .tool = {
 403                        .sample         = perf_event__repipe_sample,
 404                        .mmap           = perf_event__repipe,
 405                        .mmap2          = perf_event__repipe,
 406                        .comm           = perf_event__repipe,
 407                        .fork           = perf_event__repipe,
 408                        .exit           = perf_event__repipe,
 409                        .lost           = perf_event__repipe,
 410                        .read           = perf_event__repipe_sample,
 411                        .throttle       = perf_event__repipe,
 412                        .unthrottle     = perf_event__repipe,
 413                        .attr           = perf_event__repipe_attr,
 414                        .tracing_data   = perf_event__repipe_op2_synth,
 415                        .finished_round = perf_event__repipe_op2_synth,
 416                        .build_id       = perf_event__repipe_op2_synth,
 417                },
 418                .input_name  = "-",
 419                .samples = LIST_HEAD_INIT(inject.samples),
 420        };
 421        const char *output_name = "-";
 422        const struct option options[] = {
 423                OPT_BOOLEAN('b', "build-ids", &inject.build_ids,
 424                            "Inject build-ids into the output stream"),
 425                OPT_STRING('i', "input", &inject.input_name, "file",
 426                           "input file name"),
 427                OPT_STRING('o', "output", &output_name, "file",
 428                           "output file name"),
 429                OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
 430                            "Merge sched-stat and sched-switch for getting events "
 431                            "where and how long tasks slept"),
 432                OPT_INCR('v', "verbose", &verbose,
 433                         "be more verbose (show build ids, etc)"),
 434                OPT_END()
 435        };
 436        const char * const inject_usage[] = {
 437                "perf inject [<options>]",
 438                NULL
 439        };
 440
 441        argc = parse_options(argc, argv, options, inject_usage, 0);
 442
 443        /*
 444         * Any (unrecognized) arguments left?
 445         */
 446        if (argc)
 447                usage_with_options(inject_usage, options);
 448
 449        if (!strcmp(output_name, "-")) {
 450                inject.pipe_output = 1;
 451                inject.output = STDOUT_FILENO;
 452        } else {
 453                inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC,
 454                                                  S_IRUSR | S_IWUSR);
 455                if (inject.output < 0) {
 456                        perror("failed to create output file");
 457                        return -1;
 458                }
 459        }
 460
 461        if (symbol__init() < 0)
 462                return -1;
 463
 464        return __cmd_inject(&inject);
 465}
 466