// SPDX-License-Identifier: GPL-2.0
/*
* builtin-inject.c
*
* Builtin inject command: Examine the live mode (stdin) event stream
* and repipe it to stdout while optionally injecting additional
* events into it.
*/
#include "builtin.h"
#include "perf.h"
#include "util/color.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/map.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/debug.h"
#include "util/build-id.h"
#include "util/data.h"
#include "util/auxtrace.h"
#include "util/jit.h"
#include "util/symbol.h"
#include "util/thread.h"
#include <subcmd/parse-options.h>
#include <linux/list.h>
#include <errno.h>
#include <signal.h>
struct perf_inject {
struct perf_tool tool;
struct perf_session *session;
bool build_ids;
bool sched_stat;
bool have_auxtrace;
bool strip;
bool jit_mode;
const char *input_name;
struct perf_data output;
u64 bytes_written;
u64 aux_id;
struct list_head samples;
struct itrace_synth_opts itrace_synth_opts;
};
struct event_entry {
struct list_head node;
u32 tid;
union perf_event event[0];
};
static int output_bytes(struct perf_inject *inject, void *buf, size_t sz)
{
ssize_t size;
size = perf_data__write(&inject->output, buf, sz);
if (size < 0)
return -errno;
inject->bytes_written += size;
return 0;
}
static int perf_event__repipe_synth(struct perf_tool *tool,
union perf_event *event)
{
struct perf_inject *inject = container_of(tool, struct perf_inject,
tool);
return output_bytes(inject, event, event->header.size);
}
static int perf_event__repipe_oe_synth(struct perf_tool *tool,
union perf_event *event,
struct ordered_events *oe __maybe_unused)
{
return perf_event__repipe_synth(tool, event);
}
#ifdef HAVE_JITDUMP
static int perf_event__drop_oe(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct ordered_events *oe __maybe_unused)
{
return 0;
}
#endif
static int perf_event__repipe_op2_synth(struct perf_session *session,
union perf_event *event)
{
return perf_event__repipe_synth(session->tool, event);
}
static int perf_event__repipe_attr(struct perf_tool *tool,
union perf_event *event,
struct perf_evlist **pevlist)
{
struct perf_inject *inject = container_of(tool, struct perf_inject,
tool);
int ret;
ret = perf_event__process_attr(tool, event, pevlist);
if (ret)
return ret;
if (!inject->output.is_pipe)
return 0;
return perf_event__repipe_synth(tool, event);
}
#ifdef HAVE_AUXTRACE_SUPPORT
static int copy_bytes(struct perf_inject *inject, int fd, off_t size)
{
char buf[4096];
ssize_t ssz;
int ret;
while (size > 0) {
ssz = read(fd, buf, min(size, (off_t)sizeof(buf)));
if (ssz < 0)
return -errno;
ret = output_bytes(inject, buf, ssz);
if (ret)
return ret;
size -= ssz;
}
return 0;
}
static s64 perf_event__repipe_auxtrace(struct perf_session *session,
union perf_event *event)
{
struct perf_tool *tool = session->tool;
struct perf_inject *inject = container_of(tool, struct perf_inject,
tool);
int ret;
inject->have_auxtrace = true;
if (!inject->output.is_pipe) {
off_t offset;
offset = lseek(inject->output.file.fd, 0, SEEK_CUR);
if (offset == -1)
return -errno;
ret = auxtrace_index__auxtrace_event(&session->auxtrace_index,
event, offset);
if (ret < 0)
return ret;
}
if (perf_data__is_pipe(session->data) || !session->one_mmap) {
ret = output_bytes(inject, event, event->header.size);
if (ret < 0)
return ret;
ret = copy_bytes(inject, perf_data__fd(session->data),
event->auxtrace.size);
} else {
ret = output_bytes(inject, event,
event->header.size + event->auxtrace.size);
}
if (ret < 0)
return ret;
return event->auxtrace.size;
}
#else
static s64
perf_event__repipe_auxtrace(struct perf_session *session