11d232ec28
New commands need to have Documentation and be added to command-list.txt so that they can appear when 'perf' is called withouth any subcommand: [root@doppio linux-2.6-tip]# perf usage: perf [--version] [--help] COMMAND [ARGS] The most commonly used perf commands are: annotate Read perf.data (created by perf record) and display annotated code archive Create archive with object files with build-ids found in perf.data file bench General framework for benchmark suites buildid-cache Manage build-id cache. buildid-list List the buildids in a perf.data file diff Read two perf.data files and display the differential profile inject Filter to augment the events stream with additional information kmem Tool to trace/measure kernel memory(slab) properties kvm Tool to trace/measure kvm guest os list List all symbolic event types lock Analyze lock events probe Define new dynamic tracepoints record Run a command and record its profile into perf.data report Read perf.data (created by perf record) and display the profile sched Tool to trace/measure scheduler properties (latencies) stat Run a command and gather performance counter statistics test Runs sanity tests. timechart Tool to visualize total system behavior during a workload top System profiling tool. trace Read perf.data (created by perf record) and display trace output See 'perf help COMMAND' for more information on a specific command. [root@doppio linux-2.6-tip]# The new 'perf inject' command hadn't so it wasn't appearing on that list. Also fix the long option, that should have no spaces in it, rename the faulty one to be '--build-ids', instead of '--inject build-ids'. Reported-by: Ingo Molnar <mingo@elte.hu> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Tom Zanussi <tzanussi@gmail.com> LKML-Reference: <new-submission> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
229 lines
4.7 KiB
C
229 lines
4.7 KiB
C
/*
|
|
* 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 dso__read_build_id(struct dso *self)
|
|
{
|
|
if (self->has_build_id)
|
|
return 0;
|
|
|
|
if (filename__read_build_id(self->long_name, self->build_id,
|
|
sizeof(self->build_id)) > 0) {
|
|
self->has_build_id = true;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int dso__inject_build_id(struct dso *self, struct perf_session *session)
|
|
{
|
|
u16 misc = PERF_RECORD_MISC_USER;
|
|
struct machine *machine;
|
|
int err;
|
|
|
|
if (dso__read_build_id(self) < 0) {
|
|
pr_debug("no build_id found for %s\n", self->long_name);
|
|
return -1;
|
|
}
|
|
|
|
machine = perf_session__find_host_machine(session);
|
|
if (machine == NULL) {
|
|
pr_err("Can't find machine for session\n");
|
|
return -1;
|
|
}
|
|
|
|
if (self->kernel)
|
|
misc = PERF_RECORD_MISC_KERNEL;
|
|
|
|
err = event__synthesize_build_id(self, misc, event__repipe,
|
|
machine, session);
|
|
if (err) {
|
|
pr_err("Can't synthesize build_id event for %s\n", self->long_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;
|
|
|
|
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);
|
|
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) {
|
|
dso__inject_build_id(al.map->dso, session);
|
|
/*
|
|
* If this fails, too bad, let the other side
|
|
* account this as unresolved.
|
|
*/
|
|
} 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 0;
|
|
}
|
|
|
|
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', "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();
|
|
}
|