mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
perf report: Add --sort <call> --call <$regex>
Implement sorting by callchain symbols, --sort <call>. It will create a new column which will show a match to --call $regex or "[unmatched]". Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> LKML-Reference: <new-submission> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
a3d06cc6aa
commit
6e7d6fdcbe
1 changed files with 158 additions and 51 deletions
|
@ -40,11 +40,13 @@ static int dump_trace = 0;
|
||||||
|
|
||||||
static int verbose;
|
static int verbose;
|
||||||
static int full_paths;
|
static int full_paths;
|
||||||
static int collapse_syscalls;
|
|
||||||
|
|
||||||
static unsigned long page_size;
|
static unsigned long page_size;
|
||||||
static unsigned long mmap_window = 32;
|
static unsigned long mmap_window = 32;
|
||||||
|
|
||||||
|
static char *call = "^sys_";
|
||||||
|
static regex_t call_regex;
|
||||||
|
|
||||||
struct ip_chain_event {
|
struct ip_chain_event {
|
||||||
__u16 nr;
|
__u16 nr;
|
||||||
__u16 hv;
|
__u16 hv;
|
||||||
|
@ -463,6 +465,7 @@ struct hist_entry {
|
||||||
struct map *map;
|
struct map *map;
|
||||||
struct dso *dso;
|
struct dso *dso;
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
|
struct symbol *call;
|
||||||
__u64 ip;
|
__u64 ip;
|
||||||
char level;
|
char level;
|
||||||
|
|
||||||
|
@ -483,6 +486,16 @@ struct sort_entry {
|
||||||
size_t (*print)(FILE *fp, struct hist_entry *);
|
size_t (*print)(FILE *fp, struct hist_entry *);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int64_t cmp_null(void *l, void *r)
|
||||||
|
{
|
||||||
|
if (!l && !r)
|
||||||
|
return 0;
|
||||||
|
else if (!l)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* --sort pid */
|
/* --sort pid */
|
||||||
|
|
||||||
static int64_t
|
static int64_t
|
||||||
|
@ -517,14 +530,8 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
|
||||||
char *comm_l = left->thread->comm;
|
char *comm_l = left->thread->comm;
|
||||||
char *comm_r = right->thread->comm;
|
char *comm_r = right->thread->comm;
|
||||||
|
|
||||||
if (!comm_l || !comm_r) {
|
if (!comm_l || !comm_r)
|
||||||
if (!comm_l && !comm_r)
|
return cmp_null(comm_l, comm_r);
|
||||||
return 0;
|
|
||||||
else if (!comm_l)
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return strcmp(comm_l, comm_r);
|
return strcmp(comm_l, comm_r);
|
||||||
}
|
}
|
||||||
|
@ -550,14 +557,8 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||||
struct dso *dso_l = left->dso;
|
struct dso *dso_l = left->dso;
|
||||||
struct dso *dso_r = right->dso;
|
struct dso *dso_r = right->dso;
|
||||||
|
|
||||||
if (!dso_l || !dso_r) {
|
if (!dso_l || !dso_r)
|
||||||
if (!dso_l && !dso_r)
|
return cmp_null(dso_l, dso_r);
|
||||||
return 0;
|
|
||||||
else if (!dso_l)
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return strcmp(dso_l->name, dso_r->name);
|
return strcmp(dso_l->name, dso_r->name);
|
||||||
}
|
}
|
||||||
|
@ -617,7 +618,38 @@ static struct sort_entry sort_sym = {
|
||||||
.print = sort__sym_print,
|
.print = sort__sym_print,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* --sort call */
|
||||||
|
|
||||||
|
static int64_t
|
||||||
|
sort__call_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||||
|
{
|
||||||
|
struct symbol *sym_l = left->call;
|
||||||
|
struct symbol *sym_r = right->call;
|
||||||
|
|
||||||
|
if (!sym_l || !sym_r)
|
||||||
|
return cmp_null(sym_l, sym_r);
|
||||||
|
|
||||||
|
return strcmp(sym_l->name, sym_r->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
sort__call_print(FILE *fp, struct hist_entry *self)
|
||||||
|
{
|
||||||
|
size_t ret = 0;
|
||||||
|
|
||||||
|
ret += fprintf(fp, "%-20s", self->call ? self->call->name : "[unmatched]");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sort_entry sort_call = {
|
||||||
|
.header = "Callchain symbol ",
|
||||||
|
.cmp = sort__call_cmp,
|
||||||
|
.print = sort__call_print,
|
||||||
|
};
|
||||||
|
|
||||||
static int sort__need_collapse = 0;
|
static int sort__need_collapse = 0;
|
||||||
|
static int sort__has_call = 0;
|
||||||
|
|
||||||
struct sort_dimension {
|
struct sort_dimension {
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -630,6 +662,7 @@ static struct sort_dimension sort_dimensions[] = {
|
||||||
{ .name = "comm", .entry = &sort_comm, },
|
{ .name = "comm", .entry = &sort_comm, },
|
||||||
{ .name = "dso", .entry = &sort_dso, },
|
{ .name = "dso", .entry = &sort_dso, },
|
||||||
{ .name = "symbol", .entry = &sort_sym, },
|
{ .name = "symbol", .entry = &sort_sym, },
|
||||||
|
{ .name = "call", .entry = &sort_call, },
|
||||||
};
|
};
|
||||||
|
|
||||||
static LIST_HEAD(hist_entry__sort_list);
|
static LIST_HEAD(hist_entry__sort_list);
|
||||||
|
@ -650,6 +683,18 @@ static int sort_dimension__add(char *tok)
|
||||||
if (sd->entry->collapse)
|
if (sd->entry->collapse)
|
||||||
sort__need_collapse = 1;
|
sort__need_collapse = 1;
|
||||||
|
|
||||||
|
if (sd->entry == &sort_call) {
|
||||||
|
int ret = regcomp(&call_regex, call, REG_EXTENDED);
|
||||||
|
if (ret) {
|
||||||
|
char err[BUFSIZ];
|
||||||
|
|
||||||
|
regerror(ret, &call_regex, err, sizeof(err));
|
||||||
|
fprintf(stderr, "Invalid regex: %s\n%s", call, err);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
sort__has_call = 1;
|
||||||
|
}
|
||||||
|
|
||||||
list_add_tail(&sd->entry->list, &hist_entry__sort_list);
|
list_add_tail(&sd->entry->list, &hist_entry__sort_list);
|
||||||
sd->taken = 1;
|
sd->taken = 1;
|
||||||
|
|
||||||
|
@ -730,13 +775,76 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct symbol *
|
||||||
|
resolve_symbol(struct thread *thread, struct map **mapp,
|
||||||
|
struct dso **dsop, __u64 *ipp)
|
||||||
|
{
|
||||||
|
struct dso *dso = dsop ? *dsop : NULL;
|
||||||
|
struct map *map = mapp ? *mapp : NULL;
|
||||||
|
uint64_t ip = *ipp;
|
||||||
|
|
||||||
|
if (!thread)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (dso)
|
||||||
|
goto got_dso;
|
||||||
|
|
||||||
|
if (map)
|
||||||
|
goto got_map;
|
||||||
|
|
||||||
|
map = thread__find_map(thread, ip);
|
||||||
|
if (map != NULL) {
|
||||||
|
if (mapp)
|
||||||
|
*mapp = map;
|
||||||
|
got_map:
|
||||||
|
ip = map->map_ip(map, ip);
|
||||||
|
*ipp = ip;
|
||||||
|
|
||||||
|
dso = map->dso;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* If this is outside of all known maps,
|
||||||
|
* and is a negative address, try to look it
|
||||||
|
* up in the kernel dso, as it might be a
|
||||||
|
* vsyscall (which executes in user-mode):
|
||||||
|
*/
|
||||||
|
if ((long long)ip < 0)
|
||||||
|
dso = kernel_dso;
|
||||||
|
}
|
||||||
|
dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
|
||||||
|
|
||||||
|
if (dsop)
|
||||||
|
*dsop = dso;
|
||||||
|
|
||||||
|
if (!dso)
|
||||||
|
return NULL;
|
||||||
|
got_dso:
|
||||||
|
return dso->find_symbol(dso, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct symbol *call__match(struct symbol *sym)
|
||||||
|
{
|
||||||
|
if (!sym)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (sym->name && !regexec(&call_regex, sym->name, 0, NULL, 0))
|
||||||
|
return sym;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* collect histogram counts
|
* collect histogram counts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
|
hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
|
||||||
struct symbol *sym, __u64 ip, char level, __u64 count)
|
struct symbol *sym, __u64 ip, struct ip_chain_event *chain,
|
||||||
|
char level, __u64 count)
|
||||||
{
|
{
|
||||||
struct rb_node **p = &hist.rb_node;
|
struct rb_node **p = &hist.rb_node;
|
||||||
struct rb_node *parent = NULL;
|
struct rb_node *parent = NULL;
|
||||||
|
@ -752,6 +860,33 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
|
||||||
};
|
};
|
||||||
int cmp;
|
int cmp;
|
||||||
|
|
||||||
|
if (sort__has_call && chain) {
|
||||||
|
int i, nr = chain->hv;
|
||||||
|
struct symbol *sym;
|
||||||
|
struct dso *dso;
|
||||||
|
__u64 ip;
|
||||||
|
|
||||||
|
for (i = 0; i < chain->kernel; i++) {
|
||||||
|
ip = chain->ips[nr + i];
|
||||||
|
dso = kernel_dso;
|
||||||
|
sym = resolve_symbol(thread, NULL, &dso, &ip);
|
||||||
|
entry.call = call__match(sym);
|
||||||
|
if (entry.call)
|
||||||
|
goto got_call;
|
||||||
|
}
|
||||||
|
nr += i;
|
||||||
|
|
||||||
|
for (i = 0; i < chain->user; i++) {
|
||||||
|
ip = chain->ips[nr + i];
|
||||||
|
sym = resolve_symbol(thread, NULL, NULL, &ip);
|
||||||
|
entry.call = call__match(sym);
|
||||||
|
if (entry.call)
|
||||||
|
goto got_call;
|
||||||
|
}
|
||||||
|
nr += i;
|
||||||
|
}
|
||||||
|
got_call:
|
||||||
|
|
||||||
while (*p != NULL) {
|
while (*p != NULL) {
|
||||||
parent = *p;
|
parent = *p;
|
||||||
he = rb_entry(parent, struct hist_entry, rb_node);
|
he = rb_entry(parent, struct hist_entry, rb_node);
|
||||||
|
@ -955,7 +1090,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
|
||||||
__u64 period = 1;
|
__u64 period = 1;
|
||||||
struct map *map = NULL;
|
struct map *map = NULL;
|
||||||
void *more_data = event->ip.__more_data;
|
void *more_data = event->ip.__more_data;
|
||||||
struct ip_chain_event *chain;
|
struct ip_chain_event *chain = NULL;
|
||||||
|
|
||||||
if (event->header.type & PERF_SAMPLE_PERIOD) {
|
if (event->header.type & PERF_SAMPLE_PERIOD) {
|
||||||
period = *(__u64 *)more_data;
|
period = *(__u64 *)more_data;
|
||||||
|
@ -984,15 +1119,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
|
||||||
for (i = 0; i < chain->nr; i++)
|
for (i = 0; i < chain->nr; i++)
|
||||||
dprintf("..... %2d: %016Lx\n", i, chain->ips[i]);
|
dprintf("..... %2d: %016Lx\n", i, chain->ips[i]);
|
||||||
}
|
}
|
||||||
if (collapse_syscalls) {
|
|
||||||
/*
|
|
||||||
* Find the all-but-last kernel entry
|
|
||||||
* amongst the call-chains - to get
|
|
||||||
* to the level of system calls:
|
|
||||||
*/
|
|
||||||
if (chain->kernel >= 2)
|
|
||||||
ip = chain->ips[chain->kernel-2];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
|
dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
|
||||||
|
@ -1016,22 +1142,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
|
||||||
show = SHOW_USER;
|
show = SHOW_USER;
|
||||||
level = '.';
|
level = '.';
|
||||||
|
|
||||||
map = thread__find_map(thread, ip);
|
|
||||||
if (map != NULL) {
|
|
||||||
ip = map->map_ip(map, ip);
|
|
||||||
dso = map->dso;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* If this is outside of all known maps,
|
|
||||||
* and is a negative address, try to look it
|
|
||||||
* up in the kernel dso, as it might be a
|
|
||||||
* vsyscall (which executes in user-mode):
|
|
||||||
*/
|
|
||||||
if ((long long)ip < 0)
|
|
||||||
dso = kernel_dso;
|
|
||||||
}
|
|
||||||
dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
show = SHOW_HV;
|
show = SHOW_HV;
|
||||||
level = 'H';
|
level = 'H';
|
||||||
|
@ -1039,12 +1149,9 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show & show_mask) {
|
if (show & show_mask) {
|
||||||
struct symbol *sym = NULL;
|
struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
|
||||||
|
|
||||||
if (dso)
|
if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
|
||||||
sym = dso->find_symbol(dso, ip);
|
|
||||||
|
|
||||||
if (hist_entry__add(thread, map, dso, sym, ip, level, period)) {
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"problem incrementing symbol count, skipping event\n");
|
"problem incrementing symbol count, skipping event\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1353,8 +1460,8 @@ static const struct option options[] = {
|
||||||
"sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
|
"sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
|
||||||
OPT_BOOLEAN('P', "full-paths", &full_paths,
|
OPT_BOOLEAN('P', "full-paths", &full_paths,
|
||||||
"Don't shorten the pathnames taking into account the cwd"),
|
"Don't shorten the pathnames taking into account the cwd"),
|
||||||
OPT_BOOLEAN('S', "syscalls", &collapse_syscalls,
|
OPT_STRING('c', "call", &call, "regex",
|
||||||
"show per syscall summary overhead, using call graph"),
|
"regex to use for --sort call"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue