2009-08-17 16:18:08 +02:00
|
|
|
#include "builtin.h"
|
|
|
|
|
|
|
|
#include "util/util.h"
|
|
|
|
#include "util/cache.h"
|
|
|
|
#include "util/symbol.h"
|
|
|
|
#include "util/thread.h"
|
|
|
|
#include "util/header.h"
|
|
|
|
|
|
|
|
#include "util/parse-options.h"
|
|
|
|
|
|
|
|
#include "perf.h"
|
|
|
|
#include "util/debug.h"
|
|
|
|
|
|
|
|
#include "util/trace-event.h"
|
2009-10-07 12:47:31 +02:00
|
|
|
#include "util/data_map.h"
|
2009-08-17 16:18:08 +02:00
|
|
|
|
|
|
|
static char const *input_name = "perf.data";
|
|
|
|
|
|
|
|
static struct perf_header *header;
|
|
|
|
static u64 sample_type;
|
|
|
|
|
2009-11-27 19:29:22 +01:00
|
|
|
static int process_sample_event(event_t *event)
|
2009-08-17 16:18:08 +02:00
|
|
|
{
|
|
|
|
u64 ip = event->ip.ip;
|
2009-09-03 12:00:22 +02:00
|
|
|
u64 timestamp = -1;
|
2009-09-02 20:20:38 +02:00
|
|
|
u32 cpu = -1;
|
2009-08-17 16:18:08 +02:00
|
|
|
u64 period = 1;
|
|
|
|
void *more_data = event->ip.__more_data;
|
2009-10-13 16:16:29 +02:00
|
|
|
struct thread *thread = threads__findnew(event->ip.pid);
|
2009-08-17 16:18:08 +02:00
|
|
|
|
2009-09-03 12:00:22 +02:00
|
|
|
if (sample_type & PERF_SAMPLE_TIME) {
|
|
|
|
timestamp = *(u64 *)more_data;
|
|
|
|
more_data += sizeof(u64);
|
|
|
|
}
|
|
|
|
|
2009-09-02 20:20:38 +02:00
|
|
|
if (sample_type & PERF_SAMPLE_CPU) {
|
|
|
|
cpu = *(u32 *)more_data;
|
|
|
|
more_data += sizeof(u32);
|
|
|
|
more_data += sizeof(u32); /* reserved */
|
|
|
|
}
|
|
|
|
|
2009-08-17 16:18:08 +02:00
|
|
|
if (sample_type & PERF_SAMPLE_PERIOD) {
|
|
|
|
period = *(u64 *)more_data;
|
|
|
|
more_data += sizeof(u64);
|
|
|
|
}
|
|
|
|
|
2009-11-27 19:29:22 +01:00
|
|
|
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
|
2009-08-17 16:18:08 +02:00
|
|
|
event->header.misc,
|
|
|
|
event->ip.pid, event->ip.tid,
|
|
|
|
(void *)(long)ip,
|
|
|
|
(long long)period);
|
|
|
|
|
|
|
|
if (thread == NULL) {
|
2009-10-21 21:34:06 +02:00
|
|
|
pr_debug("problem processing %d event, skipping it.\n",
|
|
|
|
event->header.type);
|
2009-08-17 16:18:08 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-10-17 08:43:17 +02:00
|
|
|
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
|
|
|
|
|
2009-08-17 16:18:08 +02:00
|
|
|
if (sample_type & PERF_SAMPLE_RAW) {
|
|
|
|
struct {
|
|
|
|
u32 size;
|
|
|
|
char data[0];
|
|
|
|
} *raw = more_data;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME: better resolve from pid from the struct trace_entry
|
|
|
|
* field, although it should be the same than this perf
|
|
|
|
* event pid
|
|
|
|
*/
|
2009-09-03 12:00:22 +02:00
|
|
|
print_event(cpu, raw->data, raw->size, timestamp, thread->comm);
|
2009-08-17 16:18:08 +02:00
|
|
|
}
|
2009-11-27 19:29:22 +01:00
|
|
|
event__stats.total += period;
|
2009-08-17 16:18:08 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-07 12:47:31 +02:00
|
|
|
static int sample_type_check(u64 type)
|
2009-08-17 16:18:08 +02:00
|
|
|
{
|
2009-10-07 12:47:31 +02:00
|
|
|
sample_type = type;
|
2009-08-17 16:18:08 +02:00
|
|
|
|
2009-10-07 12:47:31 +02:00
|
|
|
if (!(sample_type & PERF_SAMPLE_RAW)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"No trace sample to read. Did you call perf record "
|
|
|
|
"without -R?");
|
2009-08-17 16:18:08 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-07 12:47:31 +02:00
|
|
|
static struct perf_file_handler file_handler = {
|
|
|
|
.process_sample_event = process_sample_event,
|
2009-11-27 19:29:22 +01:00
|
|
|
.process_comm_event = event__process_comm,
|
2009-10-07 12:47:31 +02:00
|
|
|
.sample_type_check = sample_type_check,
|
|
|
|
};
|
|
|
|
|
2009-08-17 16:18:08 +02:00
|
|
|
static int __cmd_trace(void)
|
|
|
|
{
|
2009-10-13 16:16:29 +02:00
|
|
|
register_idle_thread();
|
2009-10-07 12:47:31 +02:00
|
|
|
register_perf_file_handler(&file_handler);
|
2009-08-17 16:18:08 +02:00
|
|
|
|
2009-11-24 15:05:15 +01:00
|
|
|
return mmap_dispatch_perf_file(&header, input_name,
|
2009-11-27 19:29:22 +01:00
|
|
|
0, 0, &event__cwdlen, &event__cwd);
|
2009-08-17 16:18:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char * const annotate_usage[] = {
|
|
|
|
"perf trace [<options>] <command>",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option options[] = {
|
|
|
|
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
|
|
|
"dump raw trace in ASCII"),
|
|
|
|
OPT_BOOLEAN('v', "verbose", &verbose,
|
|
|
|
"be more verbose (show symbol address, etc)"),
|
2009-10-14 21:43:42 +02:00
|
|
|
OPT_BOOLEAN('l', "latency", &latency_format,
|
|
|
|
"show latency attributes (irqs/preemption disabled, etc)"),
|
2009-08-21 20:56:03 +02:00
|
|
|
OPT_END()
|
2009-08-17 16:18:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
|
|
|
{
|
2009-10-30 19:28:24 +01:00
|
|
|
symbol__init(0);
|
2009-08-17 16:18:08 +02:00
|
|
|
|
|
|
|
argc = parse_options(argc, argv, options, annotate_usage, 0);
|
|
|
|
if (argc) {
|
|
|
|
/*
|
|
|
|
* Special case: if there's an argument left then assume tha
|
|
|
|
* it's a symbol filter:
|
|
|
|
*/
|
|
|
|
if (argc > 1)
|
|
|
|
usage_with_options(annotate_usage, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
setup_pager();
|
|
|
|
|
|
|
|
return __cmd_trace();
|
|
|
|
}
|