Newer
Older

Frederic Weisbecker
committed
init_rem_hits();
fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);
fprintf(fp, "#\n");
fprintf(fp, "# Overhead");
if (show_nr_samples) {
if (field_sep)
fprintf(fp, "%cSamples", *field_sep);
else
fputs(" Samples ", fp);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (field_sep) {
fprintf(fp, "%c%s", *field_sep, se->header);
}
width = strlen(se->header);
if (se->width) {
if (col_width_list_str) {
if (col_width) {
*se->width = atoi(col_width);
col_width = strchr(col_width, ',');
if (col_width)
++col_width;
}
}
width = *se->width = max(*se->width, width);
}
fprintf(fp, " %*s", width, se->header);
fprintf(fp, "\n");
if (field_sep)
goto print_entries;
fprintf(fp, "# ........");
if (show_nr_samples)
fprintf(fp, " ..........");
list_for_each_entry(se, &hist_entry__sort_list, list) {
unsigned int i;
if (se->width)
width = *se->width;
else
width = strlen(se->header);
for (i = 0; i < width; i++)
fprintf(fp, ".");
fprintf(fp, "\n");
fprintf(fp, "#\n");
print_entries:
for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
pos = rb_entry(nd, struct hist_entry, rb_node);
ret += hist_entry__fprintf(fp, pos, total_samples);
if (sort_order == default_sort_order &&
parent_pattern == default_parent_pattern) {
fprintf(fp, "#\n");
fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");
fprintf(fp, "#\n");
}

Frederic Weisbecker
committed
free(rem_sq_bracket);
if (show_threads)
perf_read_values_display(fp, &show_threads_values,
raw_printing_style);
return ret;
}
static unsigned long total = 0,
total_mmap = 0,
total_comm = 0,
total_fork = 0,
total_unknown = 0,
total_lost = 0;
static int validate_chain(struct ip_callchain *chain, event_t *event)
{
unsigned int chain_size;
chain_size = event->header.size;
chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
if (chain->nr*sizeof(u64) > chain_size)
return -1;
return 0;
}
process_sample_event(event_t *event, unsigned long offset, unsigned long head)
{
char level;
int show = 0;
struct dso *dso = NULL;
struct thread *thread;
u64 ip = event->ip.ip;
u64 period = 1;
void *more_data = event->ip.__more_data;
struct ip_callchain *chain = NULL;
thread = threads__findnew(event->ip.pid, &threads, &last_match);
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->header.misc,
event->ip.pid, event->ip.tid,
(long long)period);
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
unsigned int i;
chain = (void *)more_data;
dump_printf("... chain: nr:%Lu\n", chain->nr);
if (validate_chain(chain, event) < 0) {
eprintf("call-chain problem with event, skipping it.\n");
return 0;
}
if (dump_trace) {
for (i = 0; i < chain->nr; i++)
dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]);
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
if (thread == NULL) {
eprintf("problem processing %d event, skipping it.\n",
event->header.type);
return -1;
}
if (comm_list && !strlist__has_entry(comm_list, thread->comm))
return 0;
cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
if (cpumode == PERF_RECORD_MISC_KERNEL) {
show = SHOW_KERNEL;
level = 'k';
dump_printf(" ...... dso: %s\n", dso->name);
} else if (cpumode == PERF_RECORD_MISC_USER) {
show = SHOW_USER;
level = '.';
} else {
show = SHOW_HV;
level = 'H';
dump_printf(" ...... dso: [hypervisor]\n");

Arnaldo Carvalho de Melo
committed
struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);

Arnaldo Carvalho de Melo
committed

Arnaldo Carvalho de Melo
committed
if (dso_list && (!dso || !dso->name ||
!strlist__has_entry(dso_list, dso->name)))

Arnaldo Carvalho de Melo
committed
if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name)))
if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
eprintf("problem incrementing symbol count, skipping event\n");
}

Arnaldo Carvalho de Melo
committed
}
total += period;

Arnaldo Carvalho de Melo
committed
static int
process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread;
struct map *map = map__new(&event->mmap, cwd, cwdlen);
thread = threads__findnew(event->mmap.pid, &threads, &last_match);
dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->mmap.pid,
(void *)(long)event->mmap.start,
(void *)(long)event->mmap.len,
(void *)(long)event->mmap.pgoff,
event->mmap.filename);
if (thread == NULL || map == NULL) {
dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
}
thread__insert_map(thread, map);
total_mmap++;
return 0;
}
static int
process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread;
thread = threads__findnew(event->comm.pid, &threads, &last_match);
dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->comm.comm, event->comm.pid);
if (thread == NULL ||
thread__set_comm_adjust(thread, event->comm.comm)) {
dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");

Arnaldo Carvalho de Melo
committed
}
total_comm++;
return 0;
}
process_task_event(event_t *event, unsigned long offset, unsigned long head)
struct thread *thread;
struct thread *parent;
thread = threads__findnew(event->fork.pid, &threads, &last_match);
parent = threads__findnew(event->fork.ppid, &threads, &last_match);
dump_printf("%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->header.type == PERF_RECORD_FORK ? "FORK" : "EXIT",
event->fork.pid, event->fork.tid,
event->fork.ppid, event->fork.ptid);
/*
* A thread clone will have the same PID for both
* parent and child.
*/
if (thread == parent)
return 0;
if (event->header.type == PERF_RECORD_EXIT)
if (!thread || !parent || thread__fork(thread, parent)) {
dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
return -1;
}
total_fork++;
return 0;
}
static int
process_lost_event(event_t *event, unsigned long offset, unsigned long head)
{
dump_printf("%p [%p]: PERF_RECORD_LOST: id:%Ld: lost:%Ld\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->lost.id,
event->lost.lost);
total_lost += event->lost.lost;
return 0;
}
static int
process_read_event(event_t *event, unsigned long offset, unsigned long head)
{
struct perf_event_attr *attr;
attr = perf_header__find_attr(event->read.id, header);
if (show_threads) {
const char *name = attr ? __event_name(attr->type, attr->config)
: "unknown";
perf_read_values_add_value(&show_threads_values,
event->read.pid, event->read.tid,
event->read.id,
name,
event->read.value);
}
dump_printf("%p [%p]: PERF_RECORD_READ: %d %d %s %Lu\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->read.pid,
event->read.tid,
attr ? __event_name(attr->type, attr->config)
: "FAIL",
event->read.value);
return 0;
}
static int
process_event(event_t *event, unsigned long offset, unsigned long head)
{
switch (event->header.type) {
case PERF_RECORD_SAMPLE:
return process_sample_event(event, offset, head);
case PERF_RECORD_MMAP:
return process_mmap_event(event, offset, head);
case PERF_RECORD_COMM:
return process_comm_event(event, offset, head);
case PERF_RECORD_FORK:
case PERF_RECORD_EXIT:
return process_task_event(event, offset, head);
case PERF_RECORD_LOST:
return process_lost_event(event, offset, head);
case PERF_RECORD_READ:
return process_read_event(event, offset, head);
/*
* We dont process them right now but they are fine:
*/
case PERF_RECORD_THROTTLE:
case PERF_RECORD_UNTHROTTLE:
default:
return -1;
}
return 0;
}
static int __cmd_report(void)
{
int ret, rc = EXIT_FAILURE;
struct thread *idle;
event_t *event;
uint32_t size;
idle = register_idle_thread(&threads, &last_match);
thread__comm_adjust(idle);
if (show_threads)
perf_read_values_init(&show_threads_values);
input = open(input_name, O_RDONLY);
if (input < 0) {
fprintf(stderr, " failed to open file: %s", input_name);
if (!strcmp(input_name, "perf.data"))
fprintf(stderr, " (try 'perf record' first)");
fprintf(stderr, "\n");
if (ret < 0) {
perror("failed to stat file");
exit(-1);
}
if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
fprintf(stderr, "file: %s not owned by current user or root\n", input_name);
fprintf(stderr, "zero-sized file, nothing to do!\n");
exit(0);
}
header = perf_header__read(input);
head = header->data_offset;
sample_type = perf_header__sample_type(header);
if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent) {
fprintf(stderr, "selected --sort parent, but no"
" callchain data. Did you call"
" perf record without -g?\n");
exit(-1);
}
if (callchain) {
fprintf(stderr, "selected -g but no callchain data."
" Did you call perf record without"
" -g?\n");
exit(-1);
}

Frederic Weisbecker
committed
} else if (callchain_param.mode != CHAIN_NONE && !callchain) {
callchain = 1;
if (register_callchain_param(&callchain_param) < 0) {
fprintf(stderr, "Can't register callchain"
" params\n");
exit(-1);
}
if (load_kernel() < 0) {
perror("failed to load kernel symbols");
return EXIT_FAILURE;
}
if (!full_paths) {
if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
perror("failed to get the current directory");
return EXIT_FAILURE;
}
cwdlen = strlen(cwd);
} else {
cwd = NULL;
cwdlen = 0;
}
shift = page_size * (head / page_size);
offset += shift;
head -= shift;
remap:
buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
MAP_SHARED, input, offset);
if (buf == MAP_FAILED) {
perror("failed to mmap file");
exit(-1);
}
more:
event = (event_t *)(buf + head);
size = event->header.size;
if (!size)
size = 8;
if (head + event->header.size >= page_size * mmap_window) {
shift = page_size * (head / page_size);
munmap_ret = munmap(buf, page_size * mmap_window);
assert(munmap_ret == 0);
offset += shift;
head -= shift;
goto remap;
}
size = event->header.size;
dump_printf("\n%p [%p]: event: %d\n",
(void *)(offset + head),
(void *)(long)event->header.size,
event->header.type);
if (!size || process_event(event, offset, head) < 0) {
dump_printf("%p [%p]: skipping unknown header type: %d\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->header.type);
/*
* assume we lost track of the stream, check alignment, and
* increment a single u64 in the hope to catch on again 'soon'.
*/
if (unlikely(head & 7))
head &= ~7ULL;
size = 8;

Arnaldo Carvalho de Melo
committed
if (offset + head >= header->data_offset + header->data_size)
if (offset + head < (unsigned long)input_stat.st_size)

Arnaldo Carvalho de Melo
committed
goto more;

Arnaldo Carvalho de Melo
committed
rc = EXIT_SUCCESS;
close(input);
dump_printf(" IP events: %10ld\n", total);
dump_printf(" mmap events: %10ld\n", total_mmap);
dump_printf(" comm events: %10ld\n", total_comm);
dump_printf(" fork events: %10ld\n", total_fork);
dump_printf(" lost events: %10ld\n", total_lost);
dump_printf(" unknown events: %10ld\n", total_unknown);
if (verbose >= 3)
threads__fprintf(stdout, &threads);
dsos__fprintf(stdout);

Frederic Weisbecker
committed
output__resort(total);
output__fprintf(stdout, total);

Arnaldo Carvalho de Melo
committed
if (show_threads)
perf_read_values_destroy(&show_threads_values);

Arnaldo Carvalho de Melo
committed
return rc;
}
static int
parse_callchain_opt(const struct option *opt __used, const char *arg,
int unset __used)
{

Frederic Weisbecker
committed
char *tok;
char *endptr;
callchain = 1;
if (!arg)
return 0;

Frederic Weisbecker
committed
tok = strtok((char *)arg, ",");
if (!tok)
return -1;
/* get the output mode */
if (!strncmp(tok, "graph", strlen(arg)))

Frederic Weisbecker
committed
callchain_param.mode = CHAIN_GRAPH_ABS;

Frederic Weisbecker
committed
else if (!strncmp(tok, "flat", strlen(arg)))

Frederic Weisbecker
committed
callchain_param.mode = CHAIN_FLAT;
else if (!strncmp(tok, "fractal", strlen(arg)))
callchain_param.mode = CHAIN_GRAPH_REL;

Frederic Weisbecker
committed
else if (!strncmp(tok, "none", strlen(arg))) {
callchain_param.mode = CHAIN_NONE;
callchain = 0;
return 0;
}
else
return -1;

Frederic Weisbecker
committed
/* get the min percentage */
tok = strtok(NULL, ",");
if (!tok)

Frederic Weisbecker
committed
goto setup;

Frederic Weisbecker
committed

Frederic Weisbecker
committed
callchain_param.min_percent = strtod(tok, &endptr);

Frederic Weisbecker
committed
if (tok == endptr)
return -1;

Frederic Weisbecker
committed
setup:
if (register_callchain_param(&callchain_param) < 0) {
fprintf(stderr, "Can't register callchain params\n");
return -1;
}
return 0;
}
static const char * const report_usage[] = {
"perf report [<options>] <command>",
NULL
};
static const struct option options[] = {
OPT_STRING('i', "input", &input_name, "file",
"input file name"),
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show symbol address, etc)"),
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
OPT_BOOLEAN('m', "modules", &modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,
"Show a column with the number of samples"),
OPT_BOOLEAN('T', "threads", &show_threads,
"Show per-thread event counters"),
OPT_STRING(0, "pretty", &pretty_printing_style, "key",
"pretty printing style key: normal raw"),
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",

Ingo Molnar
committed
"sort by key(s): pid, comm, dso, symbol, parent"),
OPT_BOOLEAN('P', "full-paths", &full_paths,
"Don't shorten the pathnames taking into account the cwd"),

Ingo Molnar
committed
OPT_STRING('p', "parent", &parent_pattern, "regex",
"regex filter to identify parent, see: '--sort parent'"),
OPT_BOOLEAN('x', "exclude-other", &exclude_other,
"Only display entries with parent-match"),
OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",

Frederic Weisbecker
committed
"Display callchains using output_type and min percent threshold. "
"Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
"only consider symbols in these dsos"),
OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
"only consider symbols in these comms"),
OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
"only consider these symbols"),
OPT_STRING('w', "column-widths", &col_width_list_str,
"width[,width...]",
"don't try to adjust column width, use these fixed values"),
OPT_STRING('t', "field-separator", &field_sep, "separator",
"separator for columns, no spaces will be added between "
"columns '.' is reserved."),
static void setup_sorting(void)
{
char *tmp, *tok, *str = strdup(sort_order);
for (tok = strtok_r(str, ", ", &tmp);
tok; tok = strtok_r(NULL, ", ", &tmp)) {
if (sort_dimension__add(tok) < 0) {
error("Unknown --sort key: `%s'", tok);
usage_with_options(report_usage, options);
}
}
free(str);
}
static void setup_list(struct strlist **list, const char *list_str,
struct sort_entry *se, const char *list_name,
FILE *fp)
{
if (list_str) {
*list = strlist__new(true, list_str);
if (!*list) {
fprintf(stderr, "problems parsing %s list\n",
list_name);
exit(129);
}
if (strlist__nr_entries(*list) == 1) {
fprintf(fp, "# %s: %s\n", list_name,
strlist__entry(*list, 0)->s);
se->elide = true;
}
int cmd_report(int argc, const char **argv, const char *prefix __used)

Arnaldo Carvalho de Melo
committed
symbol__init();
page_size = getpagesize();
argc = parse_options(argc, argv, options, report_usage, 0);
setup_sorting();
if (parent_pattern != default_parent_pattern) {
sort_dimension__add("parent");
sort_parent.elide = 1;
} else
/*
* Any (unrecognized) arguments left?
*/
if (argc)
usage_with_options(report_usage, options);
setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout);
setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout);
setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout);
if (field_sep && *field_sep == '.') {
fputs("'.' is the only non valid --field-separator argument\n",
stderr);
exit(129);
}