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