Skip to content

Commit

Permalink
perf: add perf-inject builtin
Browse files Browse the repository at this point in the history
Currently, perf 'live mode' writes build-ids at the end of the
session, which isn't actually useful for processing live mode events.

What would be better would be to have the build-ids sent before any of
the samples that reference them, which can be done by processing the
event stream and retrieving the build-ids on the first hit.  Doing
that in perf-record itself, however, is off-limits.

This patch introduces perf-inject, which does the same job while
leaving perf-record untouched.  Normal mode perf still records the
build-ids at the end of the session as it should, but for live mode,
perf-inject can be injected in between the record and report steps
e.g.:

perf record -o - ./hackbench 10 | perf inject -v -b | perf report -v -i -

perf-inject reads a perf-record event stream and repipes it to stdout.
At any point the processing code can inject other events into the
event stream - in this case build-ids (-b option) are read and
injected as needed into the event stream.

Build-ids are just the first user of perf-inject - potentially
anything that needs userspace processing to augment the trace stream
with additional information could make use of this facility.

Cc: Ingo Molnar <mingo@elte.hu>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
LKML-Reference: <1272696080-16435-3-git-send-email-tzanussi@gmail.com>
Signed-off-by: Tom Zanussi <tzanussi@gmail.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
  • Loading branch information
Tom Zanussi authored and Arnaldo Carvalho de Melo committed May 2, 2010
1 parent 789688f commit 454c407
Show file tree
Hide file tree
Showing 20 changed files with 293 additions and 24 deletions.
1 change: 1 addition & 0 deletions tools/perf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
BUILTIN_OBJS += $(OUTPUT)builtin-test.o
BUILTIN_OBJS += $(OUTPUT)builtin-inject.o

PERFLIBS = $(LIB_FILE)

Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-annotate.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ static int __cmd_annotate(void)
int ret;
struct perf_session *session;

session = perf_session__new(input_name, O_RDONLY, force);
session = perf_session__new(input_name, O_RDONLY, force, false);
if (session == NULL)
return -ENOMEM;

Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-buildid-list.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static int __cmd_buildid_list(void)
int err = -1;
struct perf_session *session;

session = perf_session__new(input_name, O_RDONLY, force);
session = perf_session__new(input_name, O_RDONLY, force, false);
if (session == NULL)
return -1;

Expand Down
4 changes: 2 additions & 2 deletions tools/perf/builtin-diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ static int __cmd_diff(void)
int ret, i;
struct perf_session *session[2];

session[0] = perf_session__new(input_old, O_RDONLY, force);
session[1] = perf_session__new(input_new, O_RDONLY, force);
session[0] = perf_session__new(input_old, O_RDONLY, force, false);
session[1] = perf_session__new(input_new, O_RDONLY, force, false);
if (session[0] == NULL || session[1] == NULL)
return -ENOMEM;

Expand Down
228 changes: 228 additions & 0 deletions tools/perf/builtin-inject.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/*
* 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/session.h"
#include "util/debug.h"

#include "util/parse-options.h"

static char const *input_name = "-";
static bool inject_build_ids;

static int event__repipe(event_t *event __used,
struct perf_session *session __used)
{
uint32_t size;
void *buf = event;

size = event->header.size;

while (size) {
int ret = write(STDOUT_FILENO, buf, size);
if (ret < 0)
return -errno;

size -= ret;
buf += ret;
}

return 0;
}

static int event__repipe_mmap(event_t *self, struct perf_session *session)
{
int err;

err = event__process_mmap(self, session);
event__repipe(self, session);

return err;
}

static int event__repipe_task(event_t *self, struct perf_session *session)
{
int err;

err = event__process_task(self, session);
event__repipe(self, session);

return err;
}

static int event__repipe_tracing_data(event_t *self,
struct perf_session *session)
{
int err;

event__repipe(self, session);
err = event__process_tracing_data(self, session);

return err;
}

static int read_buildid(struct map *self, struct perf_session *session)
{
const char *name = self->dso->long_name;
int err;

if (filename__read_build_id(self->dso->long_name, self->dso->build_id,
sizeof(self->dso->build_id)) > 0) {
char sbuild_id[BUILD_ID_SIZE * 2 + 1];

self->dso->has_build_id = true;

build_id__sprintf(self->dso->build_id,
sizeof(self->dso->build_id),
sbuild_id);
pr_debug("build id found for %s: %s\n", self->dso->long_name,
sbuild_id);
}

if (self->dso->has_build_id) {
u16 misc = PERF_RECORD_MISC_USER;
struct machine *machine;

misc = self->dso->kernel ? PERF_RECORD_MISC_KERNEL : misc;

machine = perf_session__find_host_machine(session);
if (!machine) {
pr_err("Can't find machine for session\n");
return -1;
}

err = event__synthesize_build_id(self->dso, misc,
event__repipe, machine,
session);
if (err) {
pr_err("Can't synthesize build_id event for %s\n",
name);
return -1;
}
} else {
pr_debug("no build_id found for %s\n", name);
return -1;
}

return 0;
}

static int event__inject_buildid(event_t *event, struct perf_session *session)
{
struct addr_location al;
struct thread *thread;
u8 cpumode;
int err = 0;

cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;

thread = perf_session__findnew(session, event->ip.pid);
if (thread == NULL) {
pr_err("problem processing %d event, skipping it.\n",
event->header.type);
err = -1;
goto repipe;
}

thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
event->ip.pid, event->ip.ip, &al);

if (al.map != NULL) {
if (!al.map->dso->hit) {
al.map->dso->hit = 1;
if (map__load(al.map, NULL) >= 0)
read_buildid(al.map, session);
else
pr_warning("no symbols found in %s, maybe "
"install a debug package?\n",
al.map->dso->long_name);
}
}

repipe:
event__repipe(event, session);
return err;
}

struct perf_event_ops inject_ops = {
.sample = event__repipe,
.mmap = event__repipe,
.comm = event__repipe,
.fork = event__repipe,
.exit = event__repipe,
.lost = event__repipe,
.read = event__repipe,
.throttle = event__repipe,
.unthrottle = event__repipe,
.attr = event__repipe,
.event_type = event__repipe,
.tracing_data = event__repipe,
.build_id = event__repipe,
};

extern volatile int session_done;

static void sig_handler(int sig __attribute__((__unused__)))
{
session_done = 1;
}

static int __cmd_inject(void)
{
struct perf_session *session;
int ret = -EINVAL;

signal(SIGINT, sig_handler);

if (inject_build_ids) {
inject_ops.sample = event__inject_buildid;
inject_ops.mmap = event__repipe_mmap;
inject_ops.fork = event__repipe_task;
inject_ops.tracing_data = event__repipe_tracing_data;
}

session = perf_session__new(input_name, O_RDONLY, false, true);
if (session == NULL)
return -ENOMEM;

ret = perf_session__process_events(session, &inject_ops);

perf_session__delete(session);

return ret;
}

static const char * const report_usage[] = {
"perf inject [<options>]",
NULL
};

static const struct option options[] = {
OPT_BOOLEAN('b', "inject build-ids", &inject_build_ids,
"Inject build-ids into the output stream"),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show build ids, etc)"),
OPT_END()
};

int cmd_inject(int argc, const char **argv, const char *prefix __used)
{
argc = parse_options(argc, argv, options, report_usage, 0);

/*
* Any (unrecognized) arguments left?
*/
if (argc)
usage_with_options(report_usage, options);

if (symbol__init() < 0)
return -1;

return __cmd_inject();
}
2 changes: 1 addition & 1 deletion tools/perf/builtin-kmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ static void sort_result(void)
static int __cmd_kmem(void)
{
int err = -EINVAL;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false);
if (session == NULL)
return -ENOMEM;

Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ static struct perf_event_ops eops = {

static int read_events(void)
{
session = perf_session__new(input_name, O_RDONLY, 0);
session = perf_session__new(input_name, O_RDONLY, 0, false);
if (!session)
die("Initializing perf session failed\n");

Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-record.c
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ static int __cmd_record(int argc, const char **argv)
}

session = perf_session__new(output_name, O_WRONLY,
write_mode == WRITE_FORCE);
write_mode == WRITE_FORCE, false);
if (session == NULL) {
pr_err("Not enough memory for reading perf file header\n");
return -1;
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-report.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ static int __cmd_report(void)

signal(SIGINT, sig_handler);

session = perf_session__new(input_name, O_RDONLY, force);
session = perf_session__new(input_name, O_RDONLY, force, false);
if (session == NULL)
return -ENOMEM;

Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -1660,7 +1660,7 @@ static struct perf_event_ops event_ops = {
static int read_events(void)
{
int err = -EINVAL;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false);
if (session == NULL)
return -ENOMEM;

Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-timechart.c
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ static struct perf_event_ops event_ops = {

static int __cmd_timechart(void)
{
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false);
int ret = -EINVAL;

if (session == NULL)
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-top.c
Original file line number Diff line number Diff line change
Expand Up @@ -1287,7 +1287,7 @@ static int __cmd_top(void)
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
*/
struct perf_session *session = perf_session__new(NULL, O_WRONLY, false);
struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false);
if (session == NULL)
return -ENOMEM;

Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
if (!script_name)
setup_pager();

session = perf_session__new(input_name, O_RDONLY, 0);
session = perf_session__new(input_name, O_RDONLY, 0, false);
if (session == NULL)
return -ENOMEM;

Expand Down
1 change: 1 addition & 0 deletions tools/perf/builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ extern int cmd_kmem(int argc, const char **argv, const char *prefix);
extern int cmd_lock(int argc, const char **argv, const char *prefix);
extern int cmd_kvm(int argc, const char **argv, const char *prefix);
extern int cmd_test(int argc, const char **argv, const char *prefix);
extern int cmd_inject(int argc, const char **argv, const char *prefix);

#endif
1 change: 1 addition & 0 deletions tools/perf/perf.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "lock", cmd_lock, 0 },
{ "kvm", cmd_kvm, 0 },
{ "test", cmd_test, 0 },
{ "inject", cmd_inject, 0 },
};
unsigned int i;
static const char ext[] = STRIP_EXTENSION;
Expand Down
Loading

0 comments on commit 454c407

Please sign in to comment.