From dd2ee78dd8e4c6d6f1a333fd60c3dd27d1b07042 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Mar 2010 20:12:43 -0300 Subject: perf tools: Add missing bytes printed in hist_entry__fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need those to properly size the browser widht in the newt TUI. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268349164-5822-4-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/hist.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index bdcfd6190b21..d43be344a889 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -455,11 +455,11 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -static size_t hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, - u64 session_total) +size_t hist_entry__fprintf(struct hist_entry *self, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp, + u64 session_total) { struct sort_entry *se; u64 count, total; @@ -485,9 +485,9 @@ static size_t hist_entry__fprintf(struct hist_entry *self, if (symbol_conf.show_nr_samples) { if (sep) - fprintf(fp, "%c%lld", *sep, count); + ret += fprintf(fp, "%c%lld", *sep, count); else - fprintf(fp, "%11lld", count); + ret += fprintf(fp, "%11lld", count); } if (pair_session) { @@ -518,9 +518,9 @@ static size_t hist_entry__fprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - fprintf(fp, "%c%s", *sep, bf); + ret += fprintf(fp, "%c%s", *sep, bf); else - fprintf(fp, "%6.6s", bf); + ret += fprintf(fp, "%6.6s", bf); } } @@ -528,7 +528,7 @@ static size_t hist_entry__fprintf(struct hist_entry *self, if (se->elide) continue; - fprintf(fp, "%s", sep ?: " "); + ret += fprintf(fp, "%s", sep ?: " "); ret += se->print(fp, self, se->width ? *se->width : 0); } @@ -544,8 +544,8 @@ static size_t hist_entry__fprintf(struct hist_entry *self, left_margin -= thread__comm_len(self->thread); } - hist_entry_callchain__fprintf(fp, self, session_total, - left_margin); + ret += hist_entry_callchain__fprintf(fp, self, session_total, + left_margin); } return ret; -- cgit v1.2.3 From 3997d3776a6e89586e76a0ef355bfbbd8a76966c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 12 Mar 2010 12:46:48 -0300 Subject: perf hist: Don't fprintf the callgraph unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [root@doppio ~]# perf report -i newt.data | head -10 # Samples: 11999679868 # # Overhead Command Shared Object Symbol # ........ ....... ............................. ...... # 63.61% perf libslang.so.2.1.4 [.] SLsmg_write_chars 6.30% perf perf [.] symbols__find 2.19% perf libnewt.so.0.52.10 [.] newtListboxAppendEntry 2.08% perf libslang.so.2.1.4 [.] SLsmg_write_chars@plt 1.99% perf libc-2.10.2.so [.] _IO_vfprintf_internal [root@doppio ~]# Not good, the newt form for report works, but slang has to eat the cost of the additional callgraph lines everytime it prints a line, and the callgraph doesn't appear on the screen, so move the callgraph printing to a separate function and don't use it in newt.c. Newt tree widgets are being investigated to properly support callgraphs, but till that gets merged, lets remove this huge overhead and show at least the symbol overheads for a callgraph rich perf.data with good performance. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268408808-13595-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/hist.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d43be344a889..1a4e8376d843 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -532,23 +532,23 @@ size_t hist_entry__fprintf(struct hist_entry *self, ret += se->print(fp, self, se->width ? *se->width : 0); } - ret += fprintf(fp, "\n"); - - if (symbol_conf.use_callchain) { - int left_margin = 0; + return ret + fprintf(fp, "\n"); +} - if (sort__first_dimension == SORT_COMM) { - se = list_first_entry(&hist_entry__sort_list, typeof(*se), - list); - left_margin = se->width ? *se->width : 0; - left_margin -= thread__comm_len(self->thread); - } +static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, + u64 session_total) +{ + int left_margin = 0; - ret += hist_entry_callchain__fprintf(fp, self, session_total, - left_margin); + if (sort__first_dimension == SORT_COMM) { + struct sort_entry *se = list_first_entry(&hist_entry__sort_list, + typeof(*se), list); + left_margin = se->width ? *se->width : 0; + left_margin -= thread__comm_len(self->thread); } - return ret; + return hist_entry_callchain__fprintf(fp, self, session_total, + left_margin); } size_t perf_session__fprintf_hists(struct rb_root *hists, @@ -655,6 +655,10 @@ print_entries: } ret += hist_entry__fprintf(h, pair, show_displacement, displacement, fp, session_total); + + if (symbol_conf.use_callchain) + ret += hist_entry__fprintf_callchain(h, fp, session_total); + if (h->map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, fp); -- cgit v1.2.3 From 301fde27c7fcd0380b02b175d547e894ff65d78a Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 22 Mar 2010 13:09:33 -0300 Subject: perf: Fix orphan callchain branches Callchains have markers inside their capture to tell we enter a context (kernel, user, ...). Those are not displayed in the callchains but they are incidentally an active part of the radix tree where callchains are stored, just like any other address. If we have the two following callchains: addr1 -> addr2 -> user context -> addr3 addr1 -> addr2 -> user context -> addr4 addr1 -> addr2 -> addr 5 This is pretty common if addr1 and addr2 are part of an interrupt path, addr3 and addr4 are user addresses and addr5 is a kernel non interrupt path. This will be stored as follows in the tree: addr1 addr2 / \ / addr5 user context / \ addr3 addr4 But we ignore the context markers in the report, hence the addr3 and addr4 will appear as orphan branches: |--28.30%-- hrtimer_interrupt | smp_apic_timer_interrupt | apic_timer_interrupt | | <------------- here, no parent! | | | | | |--11.11%-- 0x7fae7bccb875 | | | | | |--11.11%-- 0xffffffffff60013b | | | | | |--11.11%-- __pthread_mutex_lock_internal | | | | | |--11.11%-- __errno_location Fix this by removing the context markers when we process the callchains to the tree. Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Frederic Weisbecker Cc: Paul Mackerras Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo LKML-Reference: <1269274173-20328-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/builtin-report.c | 6 ++- tools/perf/util/callchain.c | 109 ++++++++++++++++++++++++++++++++------------ tools/perf/util/callchain.h | 4 +- tools/perf/util/hist.c | 5 -- 4 files changed, 88 insertions(+), 36 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 1f9f8695f055..d609afbd1a35 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -83,6 +83,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, { struct symbol **syms = NULL, *parent = NULL; bool hit; + int err; struct hist_entry *he; struct event_stat_id *stats; struct perf_event_attr *attr; @@ -109,8 +110,11 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (symbol_conf.use_callchain) { if (!hit) callchain_init(&he->callchain); - append_chain(&he->callchain, data->callchain, syms); + err = append_chain(&he->callchain, data->callchain, syms); free(syms); + + if (err) + return err; } return 0; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index b3b71258272a..883844eb4b0a 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, Frederic Weisbecker + * Copyright (C) 2009-2010, Frederic Weisbecker * * Handle the callchains from the stream in an ad-hoc radix tree and then * sort them in an rbtree. @@ -183,12 +183,23 @@ create_child(struct callchain_node *parent, bool inherit_children) return new; } + +struct resolved_ip { + u64 ip; + struct symbol *sym; +}; + +struct resolved_chain { + u64 nr; + struct resolved_ip ips[0]; +}; + + /* * Fill the node with callchain values */ static void -fill_node(struct callchain_node *node, struct ip_callchain *chain, - int start, struct symbol **syms) +fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) { unsigned int i; @@ -200,8 +211,8 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, perror("not enough memory for the code path tree"); return; } - call->ip = chain->ips[i]; - call->sym = syms[i]; + call->ip = chain->ips[i].ip; + call->sym = chain->ips[i].sym; list_add_tail(&call->list, &node->val); } node->val_nr = chain->nr - start; @@ -210,13 +221,13 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, } static void -add_child(struct callchain_node *parent, struct ip_callchain *chain, - int start, struct symbol **syms) +add_child(struct callchain_node *parent, struct resolved_chain *chain, + int start) { struct callchain_node *new; new = create_child(parent, false); - fill_node(new, chain, start, syms); + fill_node(new, chain, start); new->children_hit = 0; new->hit = 1; @@ -228,9 +239,8 @@ add_child(struct callchain_node *parent, struct ip_callchain *chain, * Then create another child to host the given callchain of new branch */ static void -split_add_child(struct callchain_node *parent, struct ip_callchain *chain, - struct callchain_list *to_split, int idx_parents, int idx_local, - struct symbol **syms) +split_add_child(struct callchain_node *parent, struct resolved_chain *chain, + struct callchain_list *to_split, int idx_parents, int idx_local) { struct callchain_node *new; struct list_head *old_tail; @@ -257,7 +267,7 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, /* create a new child for the new branch if any */ if (idx_total < chain->nr) { parent->hit = 0; - add_child(parent, chain, idx_total, syms); + add_child(parent, chain, idx_total); parent->children_hit++; } else { parent->hit = 1; @@ -265,32 +275,33 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, } static int -__append_chain(struct callchain_node *root, struct ip_callchain *chain, - unsigned int start, struct symbol **syms); +__append_chain(struct callchain_node *root, struct resolved_chain *chain, + unsigned int start); static void -__append_chain_children(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms, unsigned int start) +__append_chain_children(struct callchain_node *root, + struct resolved_chain *chain, + unsigned int start) { struct callchain_node *rnode; /* lookup in childrens */ chain_for_each_child(rnode, root) { - unsigned int ret = __append_chain(rnode, chain, start, syms); + unsigned int ret = __append_chain(rnode, chain, start); if (!ret) goto inc_children_hit; } /* nothing in children, add to the current node */ - add_child(root, chain, start, syms); + add_child(root, chain, start); inc_children_hit: root->children_hit++; } static int -__append_chain(struct callchain_node *root, struct ip_callchain *chain, - unsigned int start, struct symbol **syms) +__append_chain(struct callchain_node *root, struct resolved_chain *chain, + unsigned int start) { struct callchain_list *cnode; unsigned int i = start; @@ -302,13 +313,19 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, * anywhere inside a function. */ list_for_each_entry(cnode, &root->val, list) { + struct symbol *sym; + if (i == chain->nr) break; - if (cnode->sym && syms[i]) { - if (cnode->sym->start != syms[i]->start) + + sym = chain->ips[i].sym; + + if (cnode->sym && sym) { + if (cnode->sym->start != sym->start) break; - } else if (cnode->ip != chain->ips[i]) + } else if (cnode->ip != chain->ips[i].ip) break; + if (!found) found = true; i++; @@ -320,7 +337,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, /* we match only a part of the node. Split it and add the new chain */ if (i - start < root->val_nr) { - split_add_child(root, chain, cnode, start, i - start, syms); + split_add_child(root, chain, cnode, start, i - start); return 0; } @@ -331,15 +348,51 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, } /* We match the node and still have a part remaining */ - __append_chain_children(root, chain, syms, i); + __append_chain_children(root, chain, i); return 0; } -void append_chain(struct callchain_node *root, struct ip_callchain *chain, +static void +filter_context(struct ip_callchain *old, struct resolved_chain *new, + struct symbol **syms) +{ + int i, j = 0; + + for (i = 0; i < (int)old->nr; i++) { + if (old->ips[i] >= PERF_CONTEXT_MAX) + continue; + + new->ips[j].ip = old->ips[i]; + new->ips[j].sym = syms[i]; + j++; + } + + new->nr = j; +} + + +int append_chain(struct callchain_node *root, struct ip_callchain *chain, struct symbol **syms) { + struct resolved_chain *filtered; + if (!chain->nr) - return; - __append_chain_children(root, chain, syms, 0); + return 0; + + filtered = malloc(sizeof(*filtered) + + chain->nr * sizeof(struct resolved_ip)); + if (!filtered) + return -ENOMEM; + + filter_context(chain, filtered, syms); + + if (!filtered->nr) + goto end; + + __append_chain_children(root, filtered, 0); +end: + free(filtered); + + return 0; } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index ad4626de4c2b..bbd76da27f22 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -56,6 +56,6 @@ static inline u64 cumul_hits(struct callchain_node *node) } int register_callchain_param(struct callchain_param *param); -void append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms); +int append_chain(struct callchain_node *root, struct ip_callchain *chain, + struct symbol **syms); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c37da8b88573..5843a9c572ad 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -328,8 +328,6 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, left_margin); i = 0; list_for_each_entry(chain, &child->val, list) { - if (chain->ip >= PERF_CONTEXT_MAX) - continue; ret += ipchain__fprintf_graph(fp, chain, depth, new_depth_mask, i++, new_total, @@ -368,9 +366,6 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, int ret = 0; list_for_each_entry(chain, &self->val, list) { - if (chain->ip >= PERF_CONTEXT_MAX) - continue; - if (!i++ && sort__first_dimension == SORT_SYM) continue; -- cgit v1.2.3 From 59fd53062f71011a68d03f4cd0ba93d822ac3249 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:17 -0300 Subject: perf tools: Introduce struct map_symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That will be in both struct hist_entry and struct callchain_list, so that the TUI can store a pointer to the pair (map, symbol) in the trees where hist_entries and callchain_lists are present, to allow precise annotation instead of looking for the first symbol with the selected name. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-4-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/builtin-annotate.c | 34 +++++++++++++++++----------------- tools/perf/util/hist.c | 8 +++++--- tools/perf/util/newt.c | 4 ++-- tools/perf/util/sort.c | 22 +++++++++++----------- tools/perf/util/sort.h | 3 +-- tools/perf/util/symbol.h | 5 +++++ 6 files changed, 41 insertions(+), 35 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ce9b1ef784b0..887e8e04a6f9 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -69,13 +69,13 @@ static int sym__alloc_hist(struct symbol *self) static int annotate__hist_hit(struct hist_entry *he, u64 ip) { unsigned int sym_size, offset; - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; struct sym_priv *priv; struct sym_hist *h; he->count++; - if (!sym || !he->map) + if (!sym || !he->ms.map) return 0; priv = symbol__priv(sym); @@ -85,7 +85,7 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) sym_size = sym->end - sym->start; offset = ip - sym->start; - pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip)); + pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); if (offset >= sym_size) return 0; @@ -94,8 +94,8 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) h->sum++; h->ip[offset]++; - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start, - he->sym->name, ip, ip - he->sym->start, h->ip[offset]); + pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, + he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); return 0; } @@ -187,7 +187,7 @@ static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, static int parse_line(FILE *file, struct hist_entry *he, struct list_head *head) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; struct objdump_line *objdump_line; char *line = NULL, *tmp, *tmp2; size_t line_len; @@ -226,7 +226,7 @@ static int parse_line(FILE *file, struct hist_entry *he, } if (line_ip != -1) { - u64 start = map__rip_2objdump(he->map, sym->start); + u64 start = map__rip_2objdump(he->ms.map, sym->start); offset = line_ip - start; } @@ -244,7 +244,7 @@ static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; static const char *prev_line; static const char *prev_color; @@ -327,7 +327,7 @@ static void insert_source_line(struct sym_ext *sym_ext) static void free_source_line(struct hist_entry *he, int len) { - struct sym_priv *priv = symbol__priv(he->sym); + struct sym_priv *priv = symbol__priv(he->ms.sym); struct sym_ext *sym_ext = priv->ext; int i; @@ -346,7 +346,7 @@ static void free_source_line(struct hist_entry *he, int len) static void get_source_line(struct hist_entry *he, int len, const char *filename) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; u64 start; int i; char cmd[PATH_MAX * 2]; @@ -361,7 +361,7 @@ get_source_line(struct hist_entry *he, int len, const char *filename) if (!priv->ext) return; - start = he->map->unmap_ip(he->map, sym->start); + start = he->ms.map->unmap_ip(he->ms.map, sym->start); for (i = 0; i < len; i++) { char *path = NULL; @@ -425,7 +425,7 @@ static void print_summary(const char *filename) static void hist_entry__print_hits(struct hist_entry *self) { - struct symbol *sym = self->sym; + struct symbol *sym = self->ms.sym; struct sym_priv *priv = symbol__priv(sym); struct sym_hist *h = priv->hist; u64 len = sym->end - sym->start, offset; @@ -439,9 +439,9 @@ static void hist_entry__print_hits(struct hist_entry *self) static void annotate_sym(struct hist_entry *he) { - struct map *map = he->map; + struct map *map = he->ms.map; struct dso *dso = map->dso; - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; char command[PATH_MAX*2]; @@ -526,17 +526,17 @@ static void perf_session__find_annotations(struct perf_session *self) struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); struct sym_priv *priv; - if (he->sym == NULL) + if (he->ms.sym == NULL) continue; - priv = symbol__priv(he->sym); + priv = symbol__priv(he->ms.sym); if (priv->hist == NULL) continue; annotate_sym(he); /* * Since we have a hist_entry per IP for the same symbol, free - * he->sym->hist to signal we already processed this symbol. + * he->ms.sym->hist to signal we already processed this symbol. */ free(priv->hist); priv->hist = NULL; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5843a9c572ad..4eefb52a8661 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -22,8 +22,10 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct hist_entry *he; struct hist_entry entry = { .thread = al->thread, - .map = al->map, - .sym = al->sym, + .ms = { + .map = al->map, + .sym = al->sym, + }, .ip = al->addr, .level = al->level, .count = count, @@ -654,7 +656,7 @@ print_entries: if (symbol_conf.use_callchain) ret += hist_entry__fprintf_callchain(h, fp, session_total); - if (h->map == NULL && verbose > 1) { + if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index a3465a0ad70d..25cd2e1f4251 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -287,9 +287,9 @@ static size_t hist_entry__append_browser(struct hist_entry *self, indexes[0] = NEWT_ARG_APPEND; indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, self->sym, indexes); + newt_checkbox_tree__add(tree, s, self->ms.sym, indexes); } else - newtListboxAppendEntry(tree, s, self->sym); + newtListboxAppendEntry(tree, s, self->ms.sym); return strlen(s); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cb0f327de9e8..9b80c13cae46 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -131,8 +131,8 @@ sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) int64_t sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) { - struct dso *dso_l = left->map ? left->map->dso : NULL; - struct dso *dso_r = right->map ? right->map->dso : NULL; + struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; + struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; const char *dso_name_l, *dso_name_r; if (!dso_l || !dso_r) @@ -152,9 +152,9 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) size_t sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) { - if (self->map && self->map->dso) { - const char *dso_name = !verbose ? self->map->dso->short_name : - self->map->dso->long_name; + if (self->ms.map && self->ms.map->dso) { + const char *dso_name = !verbose ? self->ms.map->dso->short_name : + self->ms.map->dso->long_name; return repsep_fprintf(fp, "%-*s", width, dso_name); } @@ -168,11 +168,11 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) { u64 ip_l, ip_r; - if (left->sym == right->sym) + if (left->ms.sym == right->ms.sym) return 0; - ip_l = left->sym ? left->sym->start : left->ip; - ip_r = right->sym ? right->sym->start : right->ip; + ip_l = left->ms.sym ? left->ms.sym->start : left->ip; + ip_r = right->ms.sym ? right->ms.sym->start : right->ip; return (int64_t)(ip_r - ip_l); } @@ -184,13 +184,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) size_t ret = 0; if (verbose) { - char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; + char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); } ret += repsep_fprintf(fp, "[%c] ", self->level); - if (self->sym) - ret += repsep_fprintf(fp, "%s", self->sym->name); + if (self->ms.sym) + ret += repsep_fprintf(fp, "%s", self->ms.sym->name); else ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 753f9ea99fb0..598568696f97 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -45,8 +45,7 @@ struct hist_entry { struct rb_node rb_node; u64 count; struct thread *thread; - struct map *map; - struct symbol *sym; + struct map_symbol ms; u64 ip; char level; struct symbol *parent; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 0da2455d5b90..a4a894b8ea03 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -88,6 +88,11 @@ struct ref_reloc_sym { u64 unrelocated_addr; }; +struct map_symbol { + struct map *map; + struct symbol *sym; +}; + struct addr_location { struct thread *thread; struct map *map; -- cgit v1.2.3 From b3c9ac0846c654dea4df095999ee202e8b4cb253 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:18 -0300 Subject: perf callchains: Store the map together with the symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need this to know where a symbol in a callchain came from, for various reasons, among them precise annotation from a TUI/GUI tool. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/builtin-report.c | 3 ++- tools/perf/util/callchain.c | 21 ++++++++++----------- tools/perf/util/callchain.h | 4 ++-- tools/perf/util/hist.c | 14 +++++++------- tools/perf/util/newt.c | 8 ++++---- tools/perf/util/session.c | 13 +++++++------ tools/perf/util/session.h | 8 ++++---- 7 files changed, 36 insertions(+), 35 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d609afbd1a35..6ab16980dd66 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -81,7 +81,8 @@ static int perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, struct sample_data *data) { - struct symbol **syms = NULL, *parent = NULL; + struct map_symbol *syms = NULL; + struct symbol *parent = NULL; bool hit; int err; struct hist_entry *he; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 883844eb4b0a..db628af6d20d 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -185,8 +185,8 @@ create_child(struct callchain_node *parent, bool inherit_children) struct resolved_ip { - u64 ip; - struct symbol *sym; + u64 ip; + struct map_symbol ms; }; struct resolved_chain { @@ -212,7 +212,7 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) return; } call->ip = chain->ips[i].ip; - call->sym = chain->ips[i].sym; + call->ms = chain->ips[i].ms; list_add_tail(&call->list, &node->val); } node->val_nr = chain->nr - start; @@ -318,10 +318,10 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, if (i == chain->nr) break; - sym = chain->ips[i].sym; + sym = chain->ips[i].ms.sym; - if (cnode->sym && sym) { - if (cnode->sym->start != sym->start) + if (cnode->ms.sym && sym) { + if (cnode->ms.sym->start != sym->start) break; } else if (cnode->ip != chain->ips[i].ip) break; @@ -353,9 +353,8 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, return 0; } -static void -filter_context(struct ip_callchain *old, struct resolved_chain *new, - struct symbol **syms) +static void filter_context(struct ip_callchain *old, struct resolved_chain *new, + struct map_symbol *syms) { int i, j = 0; @@ -364,7 +363,7 @@ filter_context(struct ip_callchain *old, struct resolved_chain *new, continue; new->ips[j].ip = old->ips[i]; - new->ips[j].sym = syms[i]; + new->ips[j].ms = syms[i]; j++; } @@ -373,7 +372,7 @@ filter_context(struct ip_callchain *old, struct resolved_chain *new, int append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms) + struct map_symbol *syms) { struct resolved_chain *filtered; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index bbd76da27f22..8a7e8bbd0fda 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -39,7 +39,7 @@ struct callchain_param { struct callchain_list { u64 ip; - struct symbol *sym; + struct map_symbol ms; struct list_head list; }; @@ -57,5 +57,5 @@ static inline u64 cumul_hits(struct callchain_node *node) int register_callchain_param(struct callchain_param *param); int append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms); + struct map_symbol *syms); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 4eefb52a8661..09e09e78cb62 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -260,8 +260,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, } else ret += fprintf(fp, "%s", " "); } - if (chain->sym) - ret += fprintf(fp, "%s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, "%s\n", chain->ms.sym->name); else ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); @@ -280,7 +280,7 @@ static void init_rem_hits(void) } strcpy(rem_sq_bracket->name, "[...]"); - rem_hits.sym = rem_sq_bracket; + rem_hits.ms.sym = rem_sq_bracket; } static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, @@ -382,8 +382,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, } else ret += callchain__fprintf_left_margin(fp, left_margin); - if (chain->sym) - ret += fprintf(fp, " %s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); } @@ -408,8 +408,8 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, list_for_each_entry(chain, &self->val, list) { if (chain->ip >= PERF_CONTEXT_MAX) continue; - if (chain->sym) - ret += fprintf(fp, " %s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 25cd2e1f4251..f6953ca89bbc 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -104,8 +104,8 @@ static void newt_checkbox_tree__add(newtComponent tree, const char *str, static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { - if (self->sym) - return self->sym->name; + if (self->ms.sym) + return self->ms.sym->name; snprintf(bf, bfsize, "%#Lx", self->ip); return bf; @@ -157,7 +157,7 @@ static void __callchain__append_graph_browser(struct callchain_node *self, indexes[depth + 2] = NEWT_ARG_LAST; ++chain_idx; } - newt_checkbox_tree__add(tree, str, chain->sym, indexes); + newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); free(alloc_str); ++printed; } @@ -193,7 +193,7 @@ static void callchain__append_graph_browser(struct callchain_node *self, continue; str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, chain->sym, indexes); + newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); } indexes[1] = parent_idx; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index eed1cb889008..2cef3730cd99 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -117,13 +117,13 @@ static bool symbol__match_parent_regex(struct symbol *sym) return 0; } -struct symbol **perf_session__resolve_callchain(struct perf_session *self, - struct thread *thread, - struct ip_callchain *chain, - struct symbol **parent) +struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent) { u8 cpumode = PERF_RECORD_MISC_USER; - struct symbol **syms = NULL; + struct map_symbol *syms = NULL; unsigned int i; if (symbol_conf.use_callchain) { @@ -160,7 +160,8 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, *parent = al.sym; if (!symbol_conf.use_callchain) break; - syms[i] = al.sym; + syms[i].map = al.map; + syms[i].sym = al.sym; } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 34d73395baac..631f8157fc17 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -57,10 +57,10 @@ int __perf_session__process_events(struct perf_session *self, int perf_session__process_events(struct perf_session *self, struct perf_event_ops *event_ops); -struct symbol **perf_session__resolve_callchain(struct perf_session *self, - struct thread *thread, - struct ip_callchain *chain, - struct symbol **parent); +struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent); bool perf_session__has_traces(struct perf_session *self, const char *msg); -- cgit v1.2.3 From c6e718ff8cdcf5e7855077687720b37c4a07650a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 26 Mar 2010 12:11:06 -0300 Subject: perf symbols: Move more map_groups methods to map.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While writing a standalone test app that uses the symbol system to find kernel space symbols I noticed these also need to be moved. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 2 +- tools/perf/util/map.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/map.h | 11 ++- tools/perf/util/thread.c | 169 +---------------------------------------------- 4 files changed, 180 insertions(+), 170 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 09e09e78cb62..de3190102cc8 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -658,7 +658,7 @@ print_entries: if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, - MAP__FUNCTION, fp); + MAP__FUNCTION, verbose, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); } } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 9f2963f9ee9a..e21f98001734 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -1,4 +1,5 @@ #include "symbol.h" +#include #include #include #include @@ -234,6 +235,37 @@ u64 map__objdump_2ip(struct map *map, u64 addr) return ip; } +void map_groups__init(struct map_groups *self) +{ + int i; + for (i = 0; i < MAP__NR_TYPES; ++i) { + self->maps[i] = RB_ROOT; + INIT_LIST_HEAD(&self->removed_maps[i]); + } +} + +void map_groups__flush(struct map_groups *self) +{ + int type; + + for (type = 0; type < MAP__NR_TYPES; type++) { + struct rb_root *root = &self->maps[type]; + struct rb_node *next = rb_first(root); + + while (next) { + struct map *pos = rb_entry(next, struct map, rb_node); + next = rb_next(&pos->rb_node); + rb_erase(&pos->rb_node, root); + /* + * We may have references to this map, for + * instance in some hist_entry instances, so + * just move them to a separate list. + */ + list_add_tail(&pos->node, &self->removed_maps[pos->type]); + } + } +} + struct symbol *map_groups__find_symbol(struct map_groups *self, enum map_type type, u64 addr, symbol_filter_t filter) @@ -246,6 +278,142 @@ struct symbol *map_groups__find_symbol(struct map_groups *self, return NULL; } +size_t __map_groups__fprintf_maps(struct map_groups *self, + enum map_type type, int verbose, FILE *fp) +{ + size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); + struct rb_node *nd; + + for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { + struct map *pos = rb_entry(nd, struct map, rb_node); + printed += fprintf(fp, "Map:"); + printed += map__fprintf(pos, fp); + if (verbose > 2) { + printed += dso__fprintf(pos->dso, type, fp); + printed += fprintf(fp, "--\n"); + } + } + + return printed; +} + +size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) +{ + size_t printed = 0, i; + for (i = 0; i < MAP__NR_TYPES; ++i) + printed += __map_groups__fprintf_maps(self, i, verbose, fp); + return printed; +} + +static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, + enum map_type type, + int verbose, FILE *fp) +{ + struct map *pos; + size_t printed = 0; + + list_for_each_entry(pos, &self->removed_maps[type], node) { + printed += fprintf(fp, "Map:"); + printed += map__fprintf(pos, fp); + if (verbose > 1) { + printed += dso__fprintf(pos->dso, type, fp); + printed += fprintf(fp, "--\n"); + } + } + return printed; +} + +static size_t map_groups__fprintf_removed_maps(struct map_groups *self, + int verbose, FILE *fp) +{ + size_t printed = 0, i; + for (i = 0; i < MAP__NR_TYPES; ++i) + printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); + return printed; +} + +size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) +{ + size_t printed = map_groups__fprintf_maps(self, verbose, fp); + printed += fprintf(fp, "Removed maps:\n"); + return printed + map_groups__fprintf_removed_maps(self, verbose, fp); +} + +int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, + int verbose, FILE *fp) +{ + struct rb_root *root = &self->maps[map->type]; + struct rb_node *next = rb_first(root); + + while (next) { + struct map *pos = rb_entry(next, struct map, rb_node); + next = rb_next(&pos->rb_node); + + if (!map__overlap(pos, map)) + continue; + + if (verbose >= 2) { + fputs("overlapping maps:\n", fp); + map__fprintf(map, fp); + map__fprintf(pos, fp); + } + + rb_erase(&pos->rb_node, root); + /* + * We may have references to this map, for instance in some + * hist_entry instances, so just move them to a separate + * list. + */ + list_add_tail(&pos->node, &self->removed_maps[map->type]); + /* + * Now check if we need to create new maps for areas not + * overlapped by the new map: + */ + if (map->start > pos->start) { + struct map *before = map__clone(pos); + + if (before == NULL) + return -ENOMEM; + + before->end = map->start - 1; + map_groups__insert(self, before); + if (verbose >= 2) + map__fprintf(before, fp); + } + + if (map->end < pos->end) { + struct map *after = map__clone(pos); + + if (after == NULL) + return -ENOMEM; + + after->start = map->end + 1; + map_groups__insert(self, after); + if (verbose >= 2) + map__fprintf(after, fp); + } + } + + return 0; +} + +/* + * XXX This should not really _copy_ te maps, but refcount them. + */ +int map_groups__clone(struct map_groups *self, + struct map_groups *parent, enum map_type type) +{ + struct rb_node *nd; + for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { + struct map *map = rb_entry(nd, struct map, rb_node); + struct map *new = map__clone(map); + if (new == NULL) + return -ENOMEM; + map_groups__insert(self, new); + } + return 0; +} + static u64 map__reloc_map_ip(struct map *map, u64 ip) { return ip + (s64)map->pgoff; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 6a703fa74707..4e7a11da8ffe 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -97,11 +97,14 @@ struct map_groups { }; size_t __map_groups__fprintf_maps(struct map_groups *self, - enum map_type type, FILE *fp); + enum map_type type, int verbose, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); void map_groups__init(struct map_groups *self); -size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); +int map_groups__clone(struct map_groups *self, + struct map_groups *parent, enum map_type type); +size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); +size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); static inline void map_groups__insert(struct map_groups *self, struct map *map) { @@ -125,6 +128,9 @@ static inline struct symbol *map_groups__find_function(struct map_groups *self, return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); } +int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, + int verbose, FILE *fp); + struct map *map_groups__find_by_name(struct map_groups *self, enum map_type type, const char *name); int __map_groups__create_kernel_maps(struct map_groups *self, @@ -134,5 +140,6 @@ int map_groups__create_kernel_maps(struct map_groups *self, struct map *vmlinux_maps[MAP__NR_TYPES]); struct map *map_groups__new_module(struct map_groups *self, u64 start, const char *filename); +void map_groups__flush(struct map_groups *self); #endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 9bbe27d75306..1f7ecd47f499 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -38,15 +38,6 @@ failure: return ret; } -void map_groups__init(struct map_groups *self) -{ - int i; - for (i = 0; i < MAP__NR_TYPES; ++i) { - self->maps[i] = RB_ROOT; - INIT_LIST_HEAD(&self->removed_maps[i]); - } -} - static struct thread *thread__new(pid_t pid) { struct thread *self = zalloc(sizeof(*self)); @@ -62,28 +53,6 @@ static struct thread *thread__new(pid_t pid) return self; } -static void map_groups__flush(struct map_groups *self) -{ - int type; - - for (type = 0; type < MAP__NR_TYPES; type++) { - struct rb_root *root = &self->maps[type]; - struct rb_node *next = rb_first(root); - - while (next) { - struct map *pos = rb_entry(next, struct map, rb_node); - next = rb_next(&pos->rb_node); - rb_erase(&pos->rb_node, root); - /* - * We may have references to this map, for - * instance in some hist_entry instances, so - * just move them to a separate list. - */ - list_add_tail(&pos->node, &self->removed_maps[pos->type]); - } - } -} - int thread__set_comm(struct thread *self, const char *comm) { int err; @@ -110,69 +79,10 @@ int thread__comm_len(struct thread *self) return self->comm_len; } -size_t __map_groups__fprintf_maps(struct map_groups *self, - enum map_type type, FILE *fp) -{ - size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); - struct rb_node *nd; - - for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node); - printed += fprintf(fp, "Map:"); - printed += map__fprintf(pos, fp); - if (verbose > 2) { - printed += dso__fprintf(pos->dso, type, fp); - printed += fprintf(fp, "--\n"); - } - } - - return printed; -} - -size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) -{ - size_t printed = 0, i; - for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_maps(self, i, fp); - return printed; -} - -static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, - enum map_type type, FILE *fp) -{ - struct map *pos; - size_t printed = 0; - - list_for_each_entry(pos, &self->removed_maps[type], node) { - printed += fprintf(fp, "Map:"); - printed += map__fprintf(pos, fp); - if (verbose > 1) { - printed += dso__fprintf(pos->dso, type, fp); - printed += fprintf(fp, "--\n"); - } - } - return printed; -} - -static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) -{ - size_t printed = 0, i; - for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_removed_maps(self, i, fp); - return printed; -} - -static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) -{ - size_t printed = map_groups__fprintf_maps(self, fp); - printed += fprintf(fp, "Removed maps:\n"); - return printed + map_groups__fprintf_removed_maps(self, fp); -} - static size_t thread__fprintf(struct thread *self, FILE *fp) { return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + - map_groups__fprintf(&self->mg, fp); + map_groups__fprintf(&self->mg, verbose, fp); } struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) @@ -214,87 +124,12 @@ struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) return th; } -static int map_groups__fixup_overlappings(struct map_groups *self, - struct map *map) -{ - struct rb_root *root = &self->maps[map->type]; - struct rb_node *next = rb_first(root); - - while (next) { - struct map *pos = rb_entry(next, struct map, rb_node); - next = rb_next(&pos->rb_node); - - if (!map__overlap(pos, map)) - continue; - - if (verbose >= 2) { - fputs("overlapping maps:\n", stderr); - map__fprintf(map, stderr); - map__fprintf(pos, stderr); - } - - rb_erase(&pos->rb_node, root); - /* - * We may have references to this map, for instance in some - * hist_entry instances, so just move them to a separate - * list. - */ - list_add_tail(&pos->node, &self->removed_maps[map->type]); - /* - * Now check if we need to create new maps for areas not - * overlapped by the new map: - */ - if (map->start > pos->start) { - struct map *before = map__clone(pos); - - if (before == NULL) - return -ENOMEM; - - before->end = map->start - 1; - map_groups__insert(self, before); - if (verbose >= 2) - map__fprintf(before, stderr); - } - - if (map->end < pos->end) { - struct map *after = map__clone(pos); - - if (after == NULL) - return -ENOMEM; - - after->start = map->end + 1; - map_groups__insert(self, after); - if (verbose >= 2) - map__fprintf(after, stderr); - } - } - - return 0; -} - void thread__insert_map(struct thread *self, struct map *map) { - map_groups__fixup_overlappings(&self->mg, map); + map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); map_groups__insert(&self->mg, map); } -/* - * XXX This should not really _copy_ te maps, but refcount them. - */ -static int map_groups__clone(struct map_groups *self, - struct map_groups *parent, enum map_type type) -{ - struct rb_node *nd; - for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { - struct map *map = rb_entry(nd, struct map, rb_node); - struct map *new = map__clone(map); - if (new == NULL) - return -ENOMEM; - map_groups__insert(self, new); - } - return 0; -} - int thread__fork(struct thread *self, struct thread *parent) { int i; -- cgit v1.2.3 From 5f4d3f8816461300ce54505c9117bf85b3044aa0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 26 Mar 2010 21:16:22 -0300 Subject: perf report: Add progress bars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For when we are processing the events and inserting the entries in the browser. Experimentation here: naming "ui_something" we may be treading into creating a TUI/GUI set of routines that can then be implemented in terms of multiple backends. Also the time it takes for adding things to the "browser" takes, visually (I guess I should do some profiling here ;-) ), more time than for processing the events... That means we probably need to create a custom hist_entry browser, so that we reuse the structures we have in place instead of duplicating them in newt. But progress was made and at least we can see something while long files are being loaded, that must be one of UI 101 bullet points :-) Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 7 +-- tools/perf/util/debug.h | 16 +++++++ tools/perf/util/hist.c | 5 ++- tools/perf/util/hist.h | 2 +- tools/perf/util/newt.c | 103 ++++++++++++++++++++++++++++++++++---------- tools/perf/util/session.c | 6 +++ tools/perf/util/session.h | 12 ++++-- 7 files changed, 119 insertions(+), 32 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6ab16980dd66..381918515a5c 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -303,13 +303,14 @@ static int __cmd_report(void) next = rb_first(&session->stats_by_id); while (next) { struct event_stat_id *stats; + u64 nr_hists; stats = rb_entry(next, struct event_stat_id, rb_node); perf_session__collapse_resort(&stats->hists); - perf_session__output_resort(&stats->hists, stats->stats.total); - + nr_hists = perf_session__output_resort(&stats->hists, + stats->stats.total); if (use_browser) - perf_session__browse_hists(&stats->hists, + perf_session__browse_hists(&stats->hists, nr_hists, stats->stats.total, help); else { if (rb_first(&session->stats_by_id) == diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 0172edf3f153..5cb0a1b1401a 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -10,13 +10,29 @@ extern int dump_trace; int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); +struct ui_progress; + #ifdef NO_NEWT_SUPPORT static inline int browser__show_help(const char *format __used, va_list ap __used) { return 0; } + +static inline struct ui_progress *ui_progress__new(const char *title __used, + u64 total __used) +{ + return (struct ui_progress *)1; +} + +static inline void ui_progress__update(struct ui_progress *self __used, + u64 curr __used) {} + +static inline void ui_progress__delete(struct ui_progress *self __used) {} #else int browser__show_help(const char *format, va_list ap); +struct ui_progress *ui_progress__new(const char *title, u64 total); +void ui_progress__update(struct ui_progress *self, u64 curr); +void ui_progress__delete(struct ui_progress *self); #endif #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index de3190102cc8..a46d09332462 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -185,12 +185,13 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, rb_insert_color(&he->rb_node, root); } -void perf_session__output_resort(struct rb_root *hists, u64 total_samples) +u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; + u64 nr_hists = 0; min_callchain_hits = total_samples * (callchain_param.min_percent / 100); @@ -205,9 +206,11 @@ void perf_session__output_resort(struct rb_root *hists, u64 total_samples) rb_erase(&n->rb_node, hists); perf_session__insert_output_hist_entry(&tmp, n, min_callchain_hits); + ++nr_hists; } *hists = tmp; + return nr_hists; } static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index fe366ce5db45..da6a8c1320fa 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -25,7 +25,7 @@ size_t hist_entry__fprintf(struct hist_entry *self, u64 session_total); void hist_entry__free(struct hist_entry *); -void perf_session__output_resort(struct rb_root *hists, u64 total_samples); +u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); void perf_session__collapse_resort(struct rb_root *hists); size_t perf_session__fprintf_hists(struct rb_root *hists, struct perf_session *pair, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e99bcc8d1939..b0210ae5b93c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -12,6 +12,72 @@ #include "sort.h" #include "symbol.h" +struct ui_progress { + newtComponent form, scale; +}; + +struct ui_progress *ui_progress__new(const char *title, u64 total) +{ + struct ui_progress *self = malloc(sizeof(*self)); + + if (self != NULL) { + int cols; + newtGetScreenSize(&cols, NULL); + cols -= 4; + newtCenteredWindow(cols, 1, title); + self->form = newtForm(NULL, NULL, 0); + if (self->form == NULL) + goto out_free_self; + self->scale = newtScale(0, 0, cols, total); + if (self->scale == NULL) + goto out_free_form; + newtFormAddComponents(self->form, self->scale, NULL); + newtRefresh(); + } + + return self; + +out_free_form: + newtFormDestroy(self->form); +out_free_self: + free(self); + return NULL; +} + +void ui_progress__update(struct ui_progress *self, u64 curr) +{ + newtScaleSet(self->scale, curr); + newtRefresh(); +} + +void ui_progress__delete(struct ui_progress *self) +{ + newtFormDestroy(self->form); + newtPopWindow(); + free(self); +} + +static char browser__last_msg[1024]; + +int browser__show_help(const char *format, va_list ap) +{ + int ret; + static int backlog; + + ret = vsnprintf(browser__last_msg + backlog, + sizeof(browser__last_msg) - backlog, format, ap); + backlog += ret; + + if (browser__last_msg[backlog - 1] == '\n') { + newtPopHelpLine(); + newtPushHelpLine(browser__last_msg); + newtRefresh(); + backlog = 0; + } + + return ret; +} + static void newt_form__set_exit_keys(newtComponent self) { newtFormAddHotKey(self, NEWT_KEY_ESCAPE); @@ -364,8 +430,8 @@ static void perf_session__selection(newtComponent self, void *data) *symbol_ptr = newt__symbol_tree_get_current(self); } -void perf_session__browse_hists(struct rb_root *hists, u64 session_total, - const char *helpline) +int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, + u64 session_total, const char *helpline) { struct sort_entry *se; struct rb_node *nd; @@ -378,6 +444,12 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, newtComponent form, tree; struct newtExitStruct es; const struct map_symbol *selection; + u64 curr_hist = 0; + struct ui_progress *progress; + + progress = ui_progress__new("Adding entries to the browser...", nr_hists); + if (progress == NULL) + return -1; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); @@ -419,8 +491,13 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, tree, session_total, idx++); + ++curr_hist; + if (curr_hist % 5) + ui_progress__update(progress, curr_hist); } + ui_progress__delete(progress); + if (max_len > cols) max_len = cols - 3; @@ -480,27 +557,7 @@ do_annotate: newtFormDestroy(form); newtPopWindow(); -} - -static char browser__last_msg[1024]; - -int browser__show_help(const char *format, va_list ap) -{ - int ret; - static int backlog; - - ret = vsnprintf(browser__last_msg + backlog, - sizeof(browser__last_msg) - backlog, format, ap); - backlog += ret; - - if (browser__last_msg[backlog - 1] == '\n') { - newtPopHelpLine(); - newtPushHelpLine(browser__last_msg); - newtRefresh(); - backlog = 0; - } - - return ret; + return 0; } void setup_browser(void) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 76b4ac689df9..32765cdca058 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -397,6 +397,10 @@ int __perf_session__process_events(struct perf_session *self, event_t *event; uint32_t size; char *buf; + struct ui_progress *progress = ui_progress__new("Processing events...", + self->size); + if (progress == NULL) + return -1; perf_event_ops__fill_defaults(ops); @@ -425,6 +429,7 @@ remap: more: event = (event_t *)(buf + head); + ui_progress__update(progress, offset); if (self->header.needs_swap) perf_event_header__bswap(&event->header); @@ -475,6 +480,7 @@ more: done: err = 0; out_err: + ui_progress__delete(progress); return err; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 631f8157fc17..6a15daeda577 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -88,11 +88,15 @@ static inline struct map * } #ifdef NO_NEWT_SUPPORT -static inline void perf_session__browse_hists(struct rb_root *hists __used, +static inline int perf_session__browse_hists(struct rb_root *hists __used, + u64 nr_hists __used, u64 session_total __used, - const char *helpline __used) {} + const char *helpline __used) +{ + return 0; +} #else -void perf_session__browse_hists(struct rb_root *hists, u64 session_total, - const char *helpline); +int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, + u64 session_total, const char *helpline); #endif #endif /* __PERF_SESSION_H */ -- cgit v1.2.3 From a4e3b956a820162b7c1d616117b4f23b6017f504 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 31 Mar 2010 11:33:40 -0300 Subject: perf hist: Replace ->print() routines by ->snprintf() equivalents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Then hist_entry__fprintf will just us the newly introduced hist_entry__snprintf, add the newline and fprintf it to the supplied FILE descriptor. This allows us to remove the use_browser checking in the color_printf routines, that now got color_snprintf variants too. The newt TUI browser (and other GUIs that may come in the future) don't have to worry about stdio specific stuff in the strings they get from the se->snprintf routines and instead use whatever means to do the equivalent. Also the newt TUI browser don't have to use the fmemopen() hack, instead it can use the se->snprintf routines directly. For now tho use the hist_entry__snprintf routine to reduce the patch size. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/color.c | 53 ++++++++++++++++++++++++++--- tools/perf/util/color.h | 4 +++ tools/perf/util/hist.c | 54 ++++++++++++++++++++---------- tools/perf/util/hist.h | 7 +++- tools/perf/util/newt.c | 53 +++-------------------------- tools/perf/util/sort.c | 88 ++++++++++++++++++++++++++----------------------- tools/perf/util/sort.h | 4 +-- 7 files changed, 149 insertions(+), 114 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 9da01914e0af..e191eb9a667f 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -166,6 +166,31 @@ int perf_color_default_config(const char *var, const char *value, void *cb) return perf_default_config(var, value, cb); } +static int __color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args, const char *trail) +{ + int r = 0; + + /* + * Auto-detect: + */ + if (perf_use_color_default < 0) { + if (isatty(1) || pager_in_use()) + perf_use_color_default = 1; + else + perf_use_color_default = 0; + } + + if (perf_use_color_default && *color) + r += snprintf(bf, size, "%s", color); + r += vsnprintf(bf + r, size - r, fmt, args); + if (perf_use_color_default && *color) + r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); + if (trail) + r += snprintf(bf + r, size - r, "%s", trail); + return r; +} + static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args, const char *trail) { @@ -191,11 +216,28 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, return r; } +int color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args) +{ + return __color_vsnprintf(bf, size, color, fmt, args, NULL); +} + int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args) { return __color_vfprintf(fp, color, fmt, args, NULL); } +int color_snprintf(char *bf, size_t size, const char *color, + const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + r = color_vsnprintf(bf, size, color, fmt, args); + va_end(args); + return r; +} int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) { @@ -203,10 +245,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) int r; va_start(args, fmt); - if (use_browser) - r = vfprintf(fp, fmt, args); - else - r = color_vfprintf(fp, color, fmt, args); + r = color_vfprintf(fp, color, fmt, args); va_end(args); return r; } @@ -277,3 +316,9 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) return r; } + +int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) +{ + const char *color = get_percent_color(percent); + return color_snprintf(bf, size, color, fmt, percent); +} diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 24e8809210bb..dea082b79602 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -32,10 +32,14 @@ int perf_color_default_config(const char *var, const char *value, void *cb); int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); void color_parse(const char *value, const char *var, char *dst); void color_parse_mem(const char *value, int len, const char *var, char *dst); +int color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args); int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); +int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); +int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index a46d09332462..f0794913d575 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -455,16 +455,17 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -size_t hist_entry__fprintf(struct hist_entry *self, +int hist_entry__snprintf(struct hist_entry *self, + char *s, size_t size, struct perf_session *pair_session, bool show_displacement, - long displacement, FILE *fp, + long displacement, bool color, u64 session_total) { struct sort_entry *se; u64 count, total; const char *sep = symbol_conf.field_sep; - size_t ret; + int ret; if (symbol_conf.exclude_other && !self->parent) return 0; @@ -477,17 +478,22 @@ size_t hist_entry__fprintf(struct hist_entry *self, total = session_total; } - if (total) - ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); - else - ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); + if (total) { + if (color) + ret = percent_color_snprintf(s, size, + sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); + else + ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); + } else + ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); if (symbol_conf.show_nr_samples) { if (sep) - ret += fprintf(fp, "%c%lld", *sep, count); + ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); else - ret += fprintf(fp, "%11lld", count); + ret += snprintf(s + ret, size - ret, "%11lld", count); } if (pair_session) { @@ -507,9 +513,9 @@ size_t hist_entry__fprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - ret += fprintf(fp, "%c%s", *sep, bf); + ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); else - ret += fprintf(fp, "%11.11s", bf); + ret += snprintf(s + ret, size - ret, "%11.11s", bf); if (show_displacement) { if (displacement) @@ -518,9 +524,9 @@ size_t hist_entry__fprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - ret += fprintf(fp, "%c%s", *sep, bf); + ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); else - ret += fprintf(fp, "%6.6s", bf); + ret += snprintf(s + ret, size - ret, "%6.6s", bf); } } @@ -528,11 +534,25 @@ size_t hist_entry__fprintf(struct hist_entry *self, if (se->elide) continue; - ret += fprintf(fp, "%s", sep ?: " "); - ret += se->print(fp, self, se->width ? *se->width : 0); + ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); + ret += se->snprintf(self, s + ret, size - ret, + se->width ? *se->width : 0); } - return ret + fprintf(fp, "\n"); + return ret; +} + +int hist_entry__fprintf(struct hist_entry *self, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp, + u64 session_total) +{ + char bf[512]; + hist_entry__snprintf(self, bf, sizeof(bf), pair_session, + show_displacement, displacement, + true, session_total); + return fprintf(fp, "%s\n", bf); } static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index da6a8c1320fa..ad17f0ad798b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -18,11 +18,16 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, u64 count, bool *hit); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -size_t hist_entry__fprintf(struct hist_entry *self, +int hist_entry__fprintf(struct hist_entry *self, struct perf_session *pair_session, bool show_displacement, long displacement, FILE *fp, u64 session_total); +int hist_entry__snprintf(struct hist_entry *self, + char *bf, size_t size, + struct perf_session *pair_session, + bool show_displacement, long displacement, + bool color, u64 session_total); void hist_entry__free(struct hist_entry *); u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index b0210ae5b93c..edd628f5337b 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -294,60 +294,17 @@ static void hist_entry__append_callchain_browser(struct hist_entry *self, } } -/* - * FIXME: get lib/string.c linked with perf somehow - */ -static char *skip_spaces(const char *str) -{ - while (isspace(*str)) - ++str; - return (char *)str; -} - -static char *strim(char *s) -{ - size_t size; - char *end; - - s = skip_spaces(s); - size = strlen(s); - if (!size) - return s; - - end = s + size - 1; - while (end >= s && isspace(*end)) - end--; - *(end + 1) = '\0'; - - return s; -} - static size_t hist_entry__append_browser(struct hist_entry *self, newtComponent tree, u64 total) { - char bf[1024], *s; - FILE *fp; + char s[256]; + size_t ret; if (symbol_conf.exclude_other && !self->parent) return 0; - fp = fmemopen(bf, sizeof(bf), "w"); - if (fp == NULL) - return 0; - - hist_entry__fprintf(self, NULL, false, 0, fp, total); - fclose(fp); - - /* - * FIXME: We shouldn't need to trim, as the printing routines shouldn't - * add spaces it in the first place, the stdio output routines should - * call a __snprintf method instead of the current __print (that - * actually is a __fprintf) one, but get the raw string and _then_ add - * the newline, as this is a detail of stdio printing, not needed in - * other UIs, e.g. newt. - */ - s = strim(bf); - + ret = hist_entry__snprintf(self, s, sizeof(s), NULL, + false, 0, false, total); if (symbol_conf.use_callchain) { int indexes[2]; @@ -357,7 +314,7 @@ static size_t hist_entry__append_browser(struct hist_entry *self, } else newtListboxAppendEntry(tree, s, &self->ms); - return strlen(s); + return ret; } static void map_symbol__annotate_browser(const struct map_symbol *self) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9b80c13cae46..31329a1cd324 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -18,10 +18,21 @@ char * field_sep; LIST_HEAD(hist_entry__sort_list); +static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); + struct sort_entry sort_thread = { .header = "Command: Pid", .cmp = sort__thread_cmp, - .print = sort__thread_print, + .snprintf = hist_entry__thread_snprintf, .width = &threads__col_width, }; @@ -29,27 +40,27 @@ struct sort_entry sort_comm = { .header = "Command", .cmp = sort__comm_cmp, .collapse = sort__comm_collapse, - .print = sort__comm_print, + .snprintf = hist_entry__comm_snprintf, .width = &comms__col_width, }; struct sort_entry sort_dso = { .header = "Shared Object", .cmp = sort__dso_cmp, - .print = sort__dso_print, + .snprintf = hist_entry__dso_snprintf, .width = &dsos__col_width, }; struct sort_entry sort_sym = { .header = "Symbol", .cmp = sort__sym_cmp, - .print = sort__sym_print, + .snprintf = hist_entry__sym_snprintf, }; struct sort_entry sort_parent = { .header = "Parent symbol", .cmp = sort__parent_cmp, - .print = sort__parent_print, + .snprintf = hist_entry__parent_snprintf, .width = &parent_symbol__col_width, }; @@ -85,45 +96,38 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) return right->thread->pid - left->thread->pid; } -int repsep_fprintf(FILE *fp, const char *fmt, ...) +static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) { int n; va_list ap; va_start(ap, fmt); - if (!field_sep) - n = vfprintf(fp, fmt, ap); - else { - char *bf = NULL; - n = vasprintf(&bf, fmt, ap); - if (n > 0) { - char *sep = bf; - - while (1) { - sep = strchr(sep, *field_sep); - if (sep == NULL) - break; - *sep = '.'; - } + n = vsnprintf(bf, size, fmt, ap); + if (field_sep && n > 0) { + char *sep = bf; + + while (1) { + sep = strchr(sep, *field_sep); + if (sep == NULL) + break; + *sep = '.'; } - fputs(bf, fp); - free(bf); } va_end(ap); return n; } -size_t -sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%*s:%5d", width - 6, + return repsep_snprintf(bf, size, "%*s:%5d", width, self->thread->comm ?: "", self->thread->pid); } -size_t -sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%*s", width, self->thread->comm); + return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); } /* --sort dso */ @@ -149,16 +153,16 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(dso_name_l, dso_name_r); } -size_t -sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { if (self->ms.map && self->ms.map->dso) { const char *dso_name = !verbose ? self->ms.map->dso->short_name : self->ms.map->dso->long_name; - return repsep_fprintf(fp, "%-*s", width, dso_name); + return repsep_snprintf(bf, size, "%-*s", width, dso_name); } - return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); + return repsep_snprintf(bf, size, "%*Lx", width, self->ip); } /* --sort symbol */ @@ -177,22 +181,22 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) return (int64_t)(ip_r - ip_l); } - -size_t -sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) +static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width __used) { size_t ret = 0; if (verbose) { char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; - ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); + ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); } - ret += repsep_fprintf(fp, "[%c] ", self->level); + ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); if (self->ms.sym) - ret += repsep_fprintf(fp, "%s", self->ms.sym->name); + ret += repsep_snprintf(bf + ret, size - ret, "%s", + self->ms.sym->name); else - ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); + ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); return ret; } @@ -231,10 +235,10 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(sym_l->name, sym_r->name); } -size_t -sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%-*s", width, + return repsep_snprintf(bf, size, "%-*s", width, self->parent ? self->parent->name : "[other]"); } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 598568696f97..439ec5fa0f5f 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -76,7 +76,8 @@ struct sort_entry { int64_t (*cmp)(struct hist_entry *, struct hist_entry *); int64_t (*collapse)(struct hist_entry *, struct hist_entry *); - size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); + int (*snprintf)(struct hist_entry *self, char *bf, size_t size, + unsigned int width); unsigned int *width; bool elide; }; @@ -86,7 +87,6 @@ extern struct list_head hist_entry__sort_list; void setup_sorting(const char * const usagestr[], const struct option *opts); -extern int repsep_fprintf(FILE *fp, const char *fmt, ...); extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); -- cgit v1.2.3 From b9fb93047756c5e4129dfda7591612de61b0e877 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 2 Apr 2010 09:50:42 -0300 Subject: perf hist: Only allocate callchain_node if processing callchains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The struct callchain_node size is 120 bytes, that are never used when there are no callchains or '-g none' is specified, so conditionally allocate it, reducing sizeof(struct hist_entry) from 210 bytes to only 96, greatly speeding the non-callchain processing. LKML-Reference: Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 4 ++-- tools/perf/util/hist.c | 5 +++-- tools/perf/util/sort.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 381918515a5c..1fb13e5fd1f9 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -110,8 +110,8 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (symbol_conf.use_callchain) { if (!hit) - callchain_init(&he->callchain); - err = append_chain(&he->callchain, data->callchain, syms); + callchain_init(he->callchain); + err = append_chain(he->callchain, data->callchain, syms); free(syms); if (err) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f0794913d575..18cf8b321608 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -50,7 +50,8 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, p = &(*p)->rb_right; } - he = malloc(sizeof(*he)); + he = malloc(sizeof(*he) + (symbol_conf.use_callchain ? + sizeof(struct callchain_node) : 0)); if (!he) return NULL; *he = entry; @@ -168,7 +169,7 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, struct hist_entry *iter; if (symbol_conf.use_callchain) - callchain_param.sort(&he->sorted_chain, &he->callchain, + callchain_param.sort(&he->sorted_chain, he->callchain, min_callchain_hits, &callchain_param); while (*p != NULL) { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 439ec5fa0f5f..5bf2b744e7b2 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -49,12 +49,12 @@ struct hist_entry { u64 ip; char level; struct symbol *parent; - struct callchain_node callchain; union { unsigned long position; struct hist_entry *pair; struct rb_root sorted_chain; }; + struct callchain_node callchain[0]; }; enum sort_type { -- cgit v1.2.3 From fcd1498405c2c88ac632e7c3c3fce3213d9196db Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 14 Apr 2010 19:11:29 +0200 Subject: perf tools: Fix accidentally preprocessed snprintf callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct sort_entry has a callback named snprintf that turns an entry into a string result. But there are glibc versions that implement snprintf through a macro. The following expression is then going to get the snprintf call preprocessed: ent->snprintf(...) to finally end up in a build error: util/hist.c: Dans la fonction «hist_entry__snprintf» : util/hist.c:539: erreur: «struct sort_entry» has no member named «__builtin___snprintf_chk» To fix this, prepend struct sort_entry callbacks with an "se_" prefix. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 28 ++++++++++++++-------------- tools/perf/util/sort.c | 42 +++++++++++++++++++++--------------------- tools/perf/util/sort.h | 12 ++++++------ 3 files changed, 41 insertions(+), 41 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 18cf8b321608..9c2b8743cef6 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -68,7 +68,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; list_for_each_entry(se, &hist_entry__sort_list, list) { - cmp = se->cmp(left, right); + cmp = se->se_cmp(left, right); if (cmp) break; } @@ -85,7 +85,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) list_for_each_entry(se, &hist_entry__sort_list, list) { int64_t (*f)(struct hist_entry *, struct hist_entry *); - f = se->collapse ?: se->cmp; + f = se->se_collapse ?: se->se_cmp; cmp = f(left, right); if (cmp) @@ -536,8 +536,8 @@ int hist_entry__snprintf(struct hist_entry *self, continue; ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); - ret += se->snprintf(self, s + ret, size - ret, - se->width ? *se->width : 0); + ret += se->se_snprintf(self, s + ret, size - ret, + se->se_width ? *se->se_width : 0); } return ret; @@ -564,7 +564,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, if (sort__first_dimension == SORT_COMM) { struct sort_entry *se = list_first_entry(&hist_entry__sort_list, typeof(*se), list); - left_margin = se->width ? *se->width : 0; + left_margin = se->se_width ? *se->se_width : 0; left_margin -= thread__comm_len(self->thread); } @@ -615,22 +615,22 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, if (se->elide) continue; if (sep) { - fprintf(fp, "%c%s", *sep, se->header); + fprintf(fp, "%c%s", *sep, se->se_header); continue; } - width = strlen(se->header); - if (se->width) { + width = strlen(se->se_header); + if (se->se_width) { if (symbol_conf.col_width_list_str) { if (col_width) { - *se->width = atoi(col_width); + *se->se_width = atoi(col_width); col_width = strchr(col_width, ','); if (col_width) ++col_width; } } - width = *se->width = max(*se->width, width); + width = *se->se_width = max(*se->se_width, width); } - fprintf(fp, " %*s", width, se->header); + fprintf(fp, " %*s", width, se->se_header); } fprintf(fp, "\n"); @@ -652,10 +652,10 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, continue; fprintf(fp, " "); - if (se->width) - width = *se->width; + if (se->se_width) + width = *se->se_width; else - width = strlen(se->header); + width = strlen(se->se_header); for (i = 0; i < width; i++) fprintf(fp, "."); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9d24d4b2c8fb..da30b305fba0 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -30,38 +30,38 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width); struct sort_entry sort_thread = { - .header = "Command: Pid", - .cmp = sort__thread_cmp, - .snprintf = hist_entry__thread_snprintf, - .width = &threads__col_width, + .se_header = "Command: Pid", + .se_cmp = sort__thread_cmp, + .se_snprintf = hist_entry__thread_snprintf, + .se_width = &threads__col_width, }; struct sort_entry sort_comm = { - .header = "Command", - .cmp = sort__comm_cmp, - .collapse = sort__comm_collapse, - .snprintf = hist_entry__comm_snprintf, - .width = &comms__col_width, + .se_header = "Command", + .se_cmp = sort__comm_cmp, + .se_collapse = sort__comm_collapse, + .se_snprintf = hist_entry__comm_snprintf, + .se_width = &comms__col_width, }; struct sort_entry sort_dso = { - .header = "Shared Object", - .cmp = sort__dso_cmp, - .snprintf = hist_entry__dso_snprintf, - .width = &dsos__col_width, + .se_header = "Shared Object", + .se_cmp = sort__dso_cmp, + .se_snprintf = hist_entry__dso_snprintf, + .se_width = &dsos__col_width, }; struct sort_entry sort_sym = { - .header = "Symbol", - .cmp = sort__sym_cmp, - .snprintf = hist_entry__sym_snprintf, + .se_header = "Symbol", + .se_cmp = sort__sym_cmp, + .se_snprintf = hist_entry__sym_snprintf, }; struct sort_entry sort_parent = { - .header = "Parent symbol", - .cmp = sort__parent_cmp, - .snprintf = hist_entry__parent_snprintf, - .width = &parent_symbol__col_width, + .se_header = "Parent symbol", + .se_cmp = sort__parent_cmp, + .se_snprintf = hist_entry__parent_snprintf, + .se_width = &parent_symbol__col_width, }; struct sort_dimension { @@ -255,7 +255,7 @@ int sort_dimension__add(const char *tok) if (strncasecmp(tok, sd->name, strlen(tok))) continue; - if (sd->entry->collapse) + if (sd->entry->se_collapse) sort__need_collapse = 1; if (sd->entry == &sort_parent) { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 6d7b4be70609..1d857aa2c01f 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -78,13 +78,13 @@ enum sort_type { struct sort_entry { struct list_head list; - const char *header; + const char *se_header; - int64_t (*cmp)(struct hist_entry *, struct hist_entry *); - int64_t (*collapse)(struct hist_entry *, struct hist_entry *); - int (*snprintf)(struct hist_entry *self, char *bf, size_t size, - unsigned int width); - unsigned int *width; + int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); + int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); + int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, + unsigned int width); + unsigned int *se_width; bool elide; }; -- cgit v1.2.3 From a1645ce12adb6c9cc9e19d7695466204e3f017fe Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Mon, 19 Apr 2010 13:32:50 +0800 Subject: perf: 'perf kvm' tool for monitoring guest performance from host Here is the patch of userspace perf tool. Signed-off-by: Zhang Yanmin Signed-off-by: Avi Kivity --- tools/perf/Documentation/perf-kvm.txt | 67 ++++++ tools/perf/Makefile | 1 + tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-buildid-list.c | 2 +- tools/perf/builtin-diff.c | 6 +- tools/perf/builtin-kmem.c | 11 +- tools/perf/builtin-kvm.c | 144 +++++++++++++ tools/perf/builtin-record.c | 63 +++++- tools/perf/builtin-report.c | 6 +- tools/perf/builtin-top.c | 75 +++++-- tools/perf/builtin.h | 1 + tools/perf/command-list.txt | 1 + tools/perf/perf.c | 1 + tools/perf/perf.h | 2 + tools/perf/util/build-id.c | 2 +- tools/perf/util/event.c | 280 ++++++++++++++++++------- tools/perf/util/event.h | 10 +- tools/perf/util/header.c | 213 +++++++++++++++---- tools/perf/util/header.h | 1 + tools/perf/util/hist.c | 72 ++++++- tools/perf/util/hist.h | 3 + tools/perf/util/map.c | 139 ++++++++++++- tools/perf/util/map.h | 75 +++++-- tools/perf/util/probe-event.c | 7 +- tools/perf/util/session.c | 77 +++---- tools/perf/util/session.h | 28 +-- tools/perf/util/sort.h | 5 + tools/perf/util/symbol.c | 382 ++++++++++++++++++++++++++++------ tools/perf/util/symbol.h | 43 ++-- tools/perf/util/thread.h | 4 +- 30 files changed, 1407 insertions(+), 316 deletions(-) create mode 100644 tools/perf/Documentation/perf-kvm.txt create mode 100644 tools/perf/builtin-kvm.c (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt new file mode 100644 index 000000000000..93400a0f17f0 --- /dev/null +++ b/tools/perf/Documentation/perf-kvm.txt @@ -0,0 +1,67 @@ +perf-kvm(1) +============== + +NAME +---- +perf-kvm - Tool to trace/measure kvm guest os + +SYNOPSIS +-------- +[verse] +'perf kvm' [--host] [--guest] [--guestmount= + [--guestkallsyms= --guestmodules= | --guestvmlinux=]] + {top|record|report|diff|buildid-list} +'perf kvm' [--host] [--guest] [--guestkallsyms= --guestmodules= + | --guestvmlinux=] {top|record|report|diff|buildid-list} + +DESCRIPTION +----------- +There are a couple of variants of perf kvm: + + 'perf kvm [options] top ' to generates and displays + a performance counter profile of guest os in realtime + of an arbitrary workload. + + 'perf kvm record ' to record the performance couinter profile + of an arbitrary workload and save it into a perf data file. If both + --host and --guest are input, the perf data file name is perf.data.kvm. + If there is no --host but --guest, the file name is perf.data.guest. + If there is no --guest but --host, the file name is perf.data.host. + + 'perf kvm report' to display the performance counter profile information + recorded via perf kvm record. + + 'perf kvm diff' to displays the performance difference amongst two perf.data + files captured via perf record. + + 'perf kvm buildid-list' to display the buildids found in a perf data file, + so that other tools can be used to fetch packages with matching symbol tables + for use by perf report. + +OPTIONS +------- +--host=:: + Collect host side perforamnce profile. +--guest=:: + Collect guest side perforamnce profile. +--guestmount=:: + Guest os root file system mount directory. Users mounts guest os + root directories under by a specific filesystem access method, + typically, sshfs. For example, start 2 guest os. The one's pid is 8888 + and the other's is 9999. + #mkdir ~/guestmount; cd ~/guestmount + #sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/ + #sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/ + #perf kvm --host --guest --guestmount=~/guestmount top +--guestkallsyms=:: + Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest + kernel symbols. Users copy it out from guest os. +--guestmodules=:: + Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest + kernel module information. Users copy it out from guest os. +--guestvmlinux=:: + Guest os kernel vmlinux. + +SEE ALSO +-------- +linkperf:perf-top[1] perf-record[1] perf-report[1] perf-diff[1] perf-buildid-list[1] diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 57b3569716dd..3cb3449a9645 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -472,6 +472,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-trace.o BUILTIN_OBJS += $(OUTPUT)builtin-probe.o BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o BUILTIN_OBJS += $(OUTPUT)builtin-lock.o +BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o PERFLIBS = $(LIB_FILE) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 06eaebe10d04..f924b4332be6 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -571,7 +571,7 @@ static int __cmd_annotate(void) perf_session__fprintf(session, stdout); if (verbose > 2) - dsos__fprintf(stdout); + dsos__fprintf(&session->kerninfo_root, stdout); perf_session__collapse_resort(&session->hists); perf_session__output_resort(&session->hists, session->event_total[0]); diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index af2ad8b92f76..623afe3fdcb8 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -46,7 +46,7 @@ static int __cmd_buildid_list(void) if (with_hits) perf_session__process_events(session, &build_id__mark_dso_hit_ops); - dsos__fprintf_buildid(stdout, with_hits); + dsos__fprintf_buildid(&session->kerninfo_root, stdout, with_hits); perf_session__delete(session); return err; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3a1d94d75dce..207e860591e2 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -33,7 +33,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return -ENOMEM; if (hit) - he->count += count; + __perf_session__add_count(he, al, count); return 0; } @@ -225,6 +225,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used) input_new = argv[1]; } else input_new = argv[0]; + } else if (symbol_conf.default_guest_vmlinux_name || + symbol_conf.default_guest_kallsyms) { + input_old = "perf.data.host"; + input_new = "perf.data.guest"; } symbol_conf.exclude_other = false; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 513aa8a55db6..db474bbf3322 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -351,6 +351,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, int n_lines, int is_caller) { struct rb_node *next; + struct kernel_info *kerninfo; printf("%.102s\n", graph_dotted_line); printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); @@ -359,10 +360,16 @@ static void __print_result(struct rb_root *root, struct perf_session *session, next = rb_first(root); + kerninfo = kerninfo__findhost(&session->kerninfo_root); + if (!kerninfo) { + pr_err("__print_result: couldn't find kernel information\n"); + return; + } while (next && n_lines--) { struct alloc_stat *data = rb_entry(next, struct alloc_stat, node); struct symbol *sym = NULL; + struct map_groups *kmaps = &kerninfo->kmaps; struct map *map; char buf[BUFSIZ]; u64 addr; @@ -370,8 +377,8 @@ static void __print_result(struct rb_root *root, struct perf_session *session, if (is_caller) { addr = data->call_site; if (!raw_ip) - sym = map_groups__find_function(&session->kmaps, - addr, &map, NULL); + sym = map_groups__find_function(kmaps, addr, + &map, NULL); } else addr = data->ptr; diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c new file mode 100644 index 000000000000..a4c7cae45024 --- /dev/null +++ b/tools/perf/builtin-kvm.c @@ -0,0 +1,144 @@ +#include "builtin.h" +#include "perf.h" + +#include "util/util.h" +#include "util/cache.h" +#include "util/symbol.h" +#include "util/thread.h" +#include "util/header.h" +#include "util/session.h" + +#include "util/parse-options.h" +#include "util/trace-event.h" + +#include "util/debug.h" + +#include + +#include +#include +#include + +static char *file_name; +static char name_buffer[256]; + +int perf_host = 1; +int perf_guest; + +static const char * const kvm_usage[] = { + "perf kvm [] {top|record|report|diff|buildid-list}", + NULL +}; + +static const struct option kvm_options[] = { + OPT_STRING('i', "input", &file_name, "file", + "Input file name"), + OPT_STRING('o', "output", &file_name, "file", + "Output file name"), + OPT_BOOLEAN(0, "guest", &perf_guest, + "Collect guest os data"), + OPT_BOOLEAN(0, "host", &perf_host, + "Collect guest os data"), + OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", + "guest mount directory under which every guest os" + " instance has a subdir"), + OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name, + "file", "file saving guest os vmlinux"), + OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms, + "file", "file saving guest os /proc/kallsyms"), + OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, + "file", "file saving guest os /proc/modules"), + OPT_END() +}; + +static int __cmd_record(int argc, const char **argv) +{ + int rec_argc, i = 0, j; + const char **rec_argv; + + rec_argc = argc + 2; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + rec_argv[i++] = strdup("record"); + rec_argv[i++] = strdup("-o"); + rec_argv[i++] = strdup(file_name); + for (j = 1; j < argc; j++, i++) + rec_argv[i] = argv[j]; + + BUG_ON(i != rec_argc); + + return cmd_record(i, rec_argv, NULL); +} + +static int __cmd_report(int argc, const char **argv) +{ + int rec_argc, i = 0, j; + const char **rec_argv; + + rec_argc = argc + 2; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + rec_argv[i++] = strdup("report"); + rec_argv[i++] = strdup("-i"); + rec_argv[i++] = strdup(file_name); + for (j = 1; j < argc; j++, i++) + rec_argv[i] = argv[j]; + + BUG_ON(i != rec_argc); + + return cmd_report(i, rec_argv, NULL); +} + +static int __cmd_buildid_list(int argc, const char **argv) +{ + int rec_argc, i = 0, j; + const char **rec_argv; + + rec_argc = argc + 2; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + rec_argv[i++] = strdup("buildid-list"); + rec_argv[i++] = strdup("-i"); + rec_argv[i++] = strdup(file_name); + for (j = 1; j < argc; j++, i++) + rec_argv[i] = argv[j]; + + BUG_ON(i != rec_argc); + + return cmd_buildid_list(i, rec_argv, NULL); +} + +int cmd_kvm(int argc, const char **argv, const char *prefix __used) +{ + perf_host = perf_guest = 0; + + argc = parse_options(argc, argv, kvm_options, kvm_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + if (!argc) + usage_with_options(kvm_usage, kvm_options); + + if (!perf_host) + perf_guest = 1; + + if (!file_name) { + if (perf_host && !perf_guest) + sprintf(name_buffer, "perf.data.host"); + else if (!perf_host && perf_guest) + sprintf(name_buffer, "perf.data.guest"); + else + sprintf(name_buffer, "perf.data.kvm"); + file_name = name_buffer; + } + + if (!strncmp(argv[0], "rec", 3)) + return __cmd_record(argc, argv); + else if (!strncmp(argv[0], "rep", 3)) + return __cmd_report(argc, argv); + else if (!strncmp(argv[0], "diff", 4)) + return cmd_diff(argc, argv, NULL); + else if (!strncmp(argv[0], "top", 3)) + return cmd_top(argc, argv, NULL); + else if (!strncmp(argv[0], "buildid-list", 12)) + return __cmd_buildid_list(argc, argv); + else + usage_with_options(kvm_usage, kvm_options); + + return 0; +} diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a1b99eeac3c0..27f992aca8b5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -456,6 +456,52 @@ static void atexit_header(void) } } +static void event__synthesize_guest_os(struct kernel_info *kerninfo, + void *data __attribute__((unused))) +{ + int err; + char *guest_kallsyms; + char path[PATH_MAX]; + + if (is_host_kernel(kerninfo)) + return; + + /* + *As for guest kernel when processing subcommand record&report, + *we arrange module mmap prior to guest kernel mmap and trigger + *a preload dso because default guest module symbols are loaded + *from guest kallsyms instead of /lib/modules/XXX/XXX. This + *method is used to avoid symbol missing when the first addr is + *in module instead of in guest kernel. + */ + err = event__synthesize_modules(process_synthesized_event, + session, + kerninfo); + if (err < 0) + pr_err("Couldn't record guest kernel [%d]'s reference" + " relocation symbol.\n", kerninfo->pid); + + if (is_default_guest(kerninfo)) + guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms; + else { + sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + guest_kallsyms = path; + } + + /* + * We use _stext for guest kernel because guest kernel's /proc/kallsyms + * have no _text sometimes. + */ + err = event__synthesize_kernel_mmap(process_synthesized_event, + session, kerninfo, "_text"); + if (err < 0) + err = event__synthesize_kernel_mmap(process_synthesized_event, + session, kerninfo, "_stext"); + if (err < 0) + pr_err("Couldn't record guest kernel [%d]'s reference" + " relocation symbol.\n", kerninfo->pid); +} + static int __cmd_record(int argc, const char **argv) { int i, counter; @@ -467,6 +513,7 @@ static int __cmd_record(int argc, const char **argv) int child_ready_pipe[2], go_pipe[2]; const bool forks = argc > 0; char buf; + struct kernel_info *kerninfo; page_size = sysconf(_SC_PAGE_SIZE); @@ -635,21 +682,31 @@ static int __cmd_record(int argc, const char **argv) advance_output(err); } + kerninfo = kerninfo__findhost(&session->kerninfo_root); + if (!kerninfo) { + pr_err("Couldn't find native kernel information.\n"); + return -1; + } + err = event__synthesize_kernel_mmap(process_synthesized_event, - session, "_text"); + session, kerninfo, "_text"); if (err < 0) err = event__synthesize_kernel_mmap(process_synthesized_event, - session, "_stext"); + session, kerninfo, "_stext"); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; } - err = event__synthesize_modules(process_synthesized_event, session); + err = event__synthesize_modules(process_synthesized_event, + session, kerninfo); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; } + if (perf_guest) + kerninfo__process_allkernels(&session->kerninfo_root, + event__synthesize_guest_os, session); if (!system_wide && profile_cpu == -1) event__synthesize_thread(target_tid, process_synthesized_event, diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 7da5fb365264..816edae7c5b2 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -108,7 +108,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return -ENOMEM; if (hit) - he->count += data->period; + __perf_session__add_count(he, al, data->period); if (symbol_conf.use_callchain) { if (!hit) @@ -313,7 +313,7 @@ static int __cmd_report(void) perf_session__fprintf(session, stdout); if (verbose > 2) - dsos__fprintf(stdout); + dsos__fprintf(&session->kerninfo_root, stdout); next = rb_first(&session->stats_by_id); while (next) { @@ -450,6 +450,8 @@ static const struct option options[] = { "sort by key(s): pid, comm, dso, symbol, parent"), OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, "Don't shorten the pathnames taking into account the cwd"), + OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, + "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", "regex filter to identify parent, see: '--sort parent'"), OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 40f24dd46ef4..dfd7ea7dabdd 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -420,8 +420,9 @@ static double sym_weight(const struct sym_entry *sym) } static long samples; -static long userspace_samples; +static long kernel_samples, us_samples; static long exact_samples; +static long guest_us_samples, guest_kernel_samples; static const char CONSOLE_CLEAR[] = ""; static void __list_insert_active_sym(struct sym_entry *syme) @@ -461,7 +462,10 @@ static void print_sym_table(void) int printed = 0, j; int counter, snap = !display_weighted ? sym_counter : 0; float samples_per_sec = samples/delay_secs; - float ksamples_per_sec = (samples-userspace_samples)/delay_secs; + float ksamples_per_sec = kernel_samples/delay_secs; + float us_samples_per_sec = (us_samples)/delay_secs; + float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs; + float guest_us_samples_per_sec = (guest_us_samples)/delay_secs; float esamples_percent = (100.0*exact_samples)/samples; float sum_ksamples = 0.0; struct sym_entry *syme, *n; @@ -470,7 +474,8 @@ static void print_sym_table(void) int sym_width = 0, dso_width = 0, dso_short_width = 0; const int win_width = winsize.ws_col - 1; - samples = userspace_samples = exact_samples = 0; + samples = us_samples = kernel_samples = exact_samples = 0; + guest_kernel_samples = guest_us_samples = 0; /* Sort the active symbols */ pthread_mutex_lock(&active_symbols_lock); @@ -501,10 +506,30 @@ static void print_sym_table(void) puts(CONSOLE_CLEAR); printf("%-*.*s\n", win_width, win_width, graph_dotted_line); - printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% exact: %4.1f%% [", - samples_per_sec, - 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)), - esamples_percent); + if (!perf_guest) { + printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%" + " exact: %4.1f%% [", + samples_per_sec, + 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / + samples_per_sec)), + esamples_percent); + } else { + printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" + " guest kernel:%4.1f%% guest us:%4.1f%%" + " exact: %4.1f%% [", + samples_per_sec, + 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) / + samples_per_sec)), + 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) / + samples_per_sec)), + 100.0 - (100.0 * ((samples_per_sec - + guest_kernel_samples_per_sec) / + samples_per_sec)), + 100.0 - (100.0 * ((samples_per_sec - + guest_us_samples_per_sec) / + samples_per_sec)), + esamples_percent); + } if (nr_counters == 1 || !display_weighted) { printf("%Ld", (u64)attrs[0].sample_period); @@ -597,7 +622,6 @@ static void print_sym_table(void) syme = rb_entry(nd, struct sym_entry, rb_node); sym = sym_entry__symbol(syme); - if (++printed > print_entries || (int)syme->snap_count < count_filter) continue; @@ -761,7 +785,7 @@ static int key_mapped(int c) return 0; } -static void handle_keypress(int c) +static void handle_keypress(struct perf_session *session, int c) { if (!key_mapped(c)) { struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; @@ -830,7 +854,7 @@ static void handle_keypress(int c) case 'Q': printf("exiting.\n"); if (dump_symtab) - dsos__fprintf(stderr); + dsos__fprintf(&session->kerninfo_root, stderr); exit(0); case 's': prompt_symbol(&sym_filter_entry, "Enter details symbol"); @@ -866,6 +890,7 @@ static void *display_thread(void *arg __used) struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; struct termios tc, save; int delay_msecs, c; + struct perf_session *session = (struct perf_session *) arg; tcgetattr(0, &save); tc = save; @@ -886,7 +911,7 @@ repeat: c = getc(stdin); tcsetattr(0, TCSAFLUSH, &save); - handle_keypress(c); + handle_keypress(session, c); goto repeat; return NULL; @@ -957,24 +982,46 @@ static void event__process_sample(const event_t *self, u64 ip = self->ip.ip; struct sym_entry *syme; struct addr_location al; + struct kernel_info *kerninfo; u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; ++samples; switch (origin) { case PERF_RECORD_MISC_USER: - ++userspace_samples; + ++us_samples; if (hide_user_symbols) return; + kerninfo = kerninfo__findhost(&session->kerninfo_root); break; case PERF_RECORD_MISC_KERNEL: + ++kernel_samples; if (hide_kernel_symbols) return; + kerninfo = kerninfo__findhost(&session->kerninfo_root); break; + case PERF_RECORD_MISC_GUEST_KERNEL: + ++guest_kernel_samples; + kerninfo = kerninfo__find(&session->kerninfo_root, + self->ip.pid); + break; + case PERF_RECORD_MISC_GUEST_USER: + ++guest_us_samples; + /* + * TODO: we don't process guest user from host side + * except simple counting. + */ + return; default: return; } + if (!kerninfo && perf_guest) { + pr_err("Can't find guest [%d]'s kernel information\n", + self->ip.pid); + return; + } + if (self->header.misc & PERF_RECORD_MISC_EXACT) exact_samples++; @@ -994,7 +1041,7 @@ static void event__process_sample(const event_t *self, * --hide-kernel-symbols, even if the user specifies an * invalid --vmlinux ;-) */ - if (al.map == session->vmlinux_maps[MAP__FUNCTION] && + if (al.map == kerninfo->vmlinux_maps[MAP__FUNCTION] && RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { pr_err("The %s file can't be used\n", symbol_conf.vmlinux_name); @@ -1261,7 +1308,7 @@ static int __cmd_top(void) perf_session__mmap_read(session); - if (pthread_create(&thread, NULL, display_thread, NULL)) { + if (pthread_create(&thread, NULL, display_thread, session)) { printf("Could not create display thread.\n"); exit(-1); } diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 10fe49e7048a..ab28bca92e52 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -32,5 +32,6 @@ extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_probe(int argc, const char **argv, const char *prefix); 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); #endif diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index db6ee94d4a8e..2a1162d413a8 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -19,3 +19,4 @@ perf-trace mainporcelain common perf-probe mainporcelain common perf-kmem mainporcelain common perf-lock mainporcelain common +perf-kvm mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index d4be55b6cd34..985cdb4bd005 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -307,6 +307,7 @@ static void handle_internal_command(int argc, const char **argv) { "probe", cmd_probe, 0 }, { "kmem", cmd_kmem, 0 }, { "lock", cmd_lock, 0 }, + { "kvm", cmd_kvm, 0 }, }; unsigned int i; static const char ext[] = STRIP_EXTENSION; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index ec212748d651..02821febb704 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -131,4 +131,6 @@ struct ip_callchain { u64 ips[0]; }; +extern int perf_host, perf_guest; + #endif diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 04904b35ba81..0f60a3906808 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -24,7 +24,7 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) } thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, - event->ip.ip, &al); + event->ip.pid, event->ip.ip, &al); if (al.map != NULL) al.map->dso->hit = 1; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 571fb25f7eb9..e3fa8d3d11b4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -112,7 +112,11 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, event_t ev = { .header = { .type = PERF_RECORD_MMAP, - .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ + /* + * Just like the kernel, see __perf_event_mmap + * in kernel/perf_event.c + */ + .misc = PERF_RECORD_MISC_USER, }, }; int n; @@ -167,11 +171,23 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, } int event__synthesize_modules(event__handler_t process, - struct perf_session *session) + struct perf_session *session, + struct kernel_info *kerninfo) { struct rb_node *nd; + struct map_groups *kmaps = &kerninfo->kmaps; + u16 misc; - for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); + /* + * kernel uses 0 for user space maps, see kernel/perf_event.c + * __perf_event_mmap + */ + if (is_host_kernel(kerninfo)) + misc = PERF_RECORD_MISC_KERNEL; + else + misc = PERF_RECORD_MISC_GUEST_KERNEL; + + for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { event_t ev; size_t size; @@ -182,12 +198,13 @@ int event__synthesize_modules(event__handler_t process, size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); memset(&ev, 0, sizeof(ev)); - ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ + ev.mmap.header.misc = misc; ev.mmap.header.type = PERF_RECORD_MMAP; ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); ev.mmap.start = pos->start; ev.mmap.len = pos->end - pos->start; + ev.mmap.pid = kerninfo->pid; memcpy(ev.mmap.filename, pos->dso->long_name, pos->dso->long_name_len + 1); @@ -250,13 +267,18 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start) int event__synthesize_kernel_mmap(event__handler_t process, struct perf_session *session, + struct kernel_info *kerninfo, const char *symbol_name) { size_t size; + const char *filename, *mmap_name; + char path[PATH_MAX]; + char name_buff[PATH_MAX]; + struct map *map; + event_t ev = { .header = { .type = PERF_RECORD_MMAP, - .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ }, }; /* @@ -266,16 +288,37 @@ int event__synthesize_kernel_mmap(event__handler_t process, */ struct process_symbol_args args = { .name = symbol_name, }; - if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) + mmap_name = kern_mmap_name(kerninfo, name_buff); + if (is_host_kernel(kerninfo)) { + /* + * kernel uses PERF_RECORD_MISC_USER for user space maps, + * see kernel/perf_event.c __perf_event_mmap + */ + ev.header.misc = PERF_RECORD_MISC_KERNEL; + filename = "/proc/kallsyms"; + } else { + ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; + if (is_default_guest(kerninfo)) + filename = (char *) symbol_conf.default_guest_kallsyms; + else { + sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + filename = path; + } + } + + if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) return -ENOENT; + map = kerninfo->vmlinux_maps[MAP__FUNCTION]; size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), - "[kernel.kallsyms.%s]", symbol_name) + 1; + "%s%s", mmap_name, symbol_name) + 1; size = ALIGN(size, sizeof(u64)); - ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); + ev.mmap.header.size = (sizeof(ev.mmap) - + (sizeof(ev.mmap.filename) - size)); ev.mmap.pgoff = args.start; - ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; - ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; + ev.mmap.start = map->start; + ev.mmap.len = map->end - ev.mmap.start; + ev.mmap.pid = kerninfo->pid; return process(&ev, session); } @@ -329,22 +372,50 @@ int event__process_lost(event_t *self, struct perf_session *session) return 0; } -int event__process_mmap(event_t *self, struct perf_session *session) +static void event_set_kernel_mmap_len(struct map **maps, event_t *self) +{ + maps[MAP__FUNCTION]->start = self->mmap.start; + maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; + /* + * Be a bit paranoid here, some perf.data file came with + * a zero sized synthesized MMAP event for the kernel. + */ + if (maps[MAP__FUNCTION]->end == 0) + maps[MAP__FUNCTION]->end = ~0UL; +} + +static int event__process_kernel_mmap(event_t *self, + struct perf_session *session) { - struct thread *thread; struct map *map; + char kmmap_prefix[PATH_MAX]; + struct kernel_info *kerninfo; + enum dso_kernel_type kernel_type; + bool is_kernel_mmap; + + kerninfo = kerninfo__findnew(&session->kerninfo_root, self->mmap.pid); + if (!kerninfo) { + pr_err("Can't find id %d's kerninfo\n", self->mmap.pid); + goto out_problem; + } - dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", - self->mmap.pid, self->mmap.tid, self->mmap.start, - self->mmap.len, self->mmap.pgoff, self->mmap.filename); + kern_mmap_name(kerninfo, kmmap_prefix); + if (is_host_kernel(kerninfo)) + kernel_type = DSO_TYPE_KERNEL; + else + kernel_type = DSO_TYPE_GUEST_KERNEL; - if (self->mmap.pid == 0) { - static const char kmmap_prefix[] = "[kernel.kallsyms."; + is_kernel_mmap = memcmp(self->mmap.filename, + kmmap_prefix, + strlen(kmmap_prefix)) == 0; + if (self->mmap.filename[0] == '/' || + (!is_kernel_mmap && self->mmap.filename[0] == '[')) { - if (self->mmap.filename[0] == '/') { - char short_module_name[1024]; - char *name = strrchr(self->mmap.filename, '/'), *dot; + char short_module_name[1024]; + char *name, *dot; + if (self->mmap.filename[0] == '/') { + name = strrchr(self->mmap.filename, '/'); if (name == NULL) goto out_problem; @@ -352,59 +423,86 @@ int event__process_mmap(event_t *self, struct perf_session *session) dot = strrchr(name, '.'); if (dot == NULL) goto out_problem; - snprintf(short_module_name, sizeof(short_module_name), - "[%.*s]", (int)(dot - name), name); + "[%.*s]", (int)(dot - name), name); strxfrchar(short_module_name, '-', '_'); - - map = perf_session__new_module_map(session, - self->mmap.start, - self->mmap.filename); - if (map == NULL) - goto out_problem; - - name = strdup(short_module_name); - if (name == NULL) - goto out_problem; - - map->dso->short_name = name; - map->end = map->start + self->mmap.len; - } else if (memcmp(self->mmap.filename, kmmap_prefix, - sizeof(kmmap_prefix) - 1) == 0) { - const char *symbol_name = (self->mmap.filename + - sizeof(kmmap_prefix) - 1); + } else + strcpy(short_module_name, self->mmap.filename); + + map = map_groups__new_module(&kerninfo->kmaps, + self->mmap.start, + self->mmap.filename, + kerninfo); + if (map == NULL) + goto out_problem; + + name = strdup(short_module_name); + if (name == NULL) + goto out_problem; + + map->dso->short_name = name; + map->end = map->start + self->mmap.len; + } else if (is_kernel_mmap) { + const char *symbol_name = (self->mmap.filename + + strlen(kmmap_prefix)); + /* + * Should be there already, from the build-id table in + * the header. + */ + struct dso *kernel = __dsos__findnew(&kerninfo->dsos__kernel, + kmmap_prefix); + if (kernel == NULL) + goto out_problem; + + kernel->kernel = kernel_type; + if (__map_groups__create_kernel_maps(&kerninfo->kmaps, + kerninfo->vmlinux_maps, kernel) < 0) + goto out_problem; + + event_set_kernel_mmap_len(kerninfo->vmlinux_maps, self); + perf_session__set_kallsyms_ref_reloc_sym(kerninfo->vmlinux_maps, + symbol_name, + self->mmap.pgoff); + if (is_default_guest(kerninfo)) { /* - * Should be there already, from the build-id table in - * the header. + * preload dso of guest kernel and modules */ - struct dso *kernel = __dsos__findnew(&dsos__kernel, - "[kernel.kallsyms]"); - if (kernel == NULL) - goto out_problem; - - kernel->kernel = 1; - if (__perf_session__create_kernel_maps(session, kernel) < 0) - goto out_problem; + dso__load(kernel, + kerninfo->vmlinux_maps[MAP__FUNCTION], + NULL); + } + } + return 0; +out_problem: + return -1; +} - session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; - session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; - /* - * Be a bit paranoid here, some perf.data file came with - * a zero sized synthesized MMAP event for the kernel. - */ - if (session->vmlinux_maps[MAP__FUNCTION]->end == 0) - session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; +int event__process_mmap(event_t *self, struct perf_session *session) +{ + struct kernel_info *kerninfo; + struct thread *thread; + struct map *map; + u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + int ret = 0; - perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, - self->mmap.pgoff); - } + dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", + self->mmap.pid, self->mmap.tid, self->mmap.start, + self->mmap.len, self->mmap.pgoff, self->mmap.filename); + + if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || + cpumode == PERF_RECORD_MISC_KERNEL) { + ret = event__process_kernel_mmap(self, session); + if (ret < 0) + goto out_problem; return 0; } thread = perf_session__findnew(session, self->mmap.pid); - map = map__new(self->mmap.start, self->mmap.len, self->mmap.pgoff, - self->mmap.pid, self->mmap.filename, MAP__FUNCTION, - session->cwd, session->cwdlen); + kerninfo = kerninfo__findhost(&session->kerninfo_root); + map = map__new(&kerninfo->dsos__user, self->mmap.start, + self->mmap.len, self->mmap.pgoff, + self->mmap.pid, self->mmap.filename, + MAP__FUNCTION, session->cwd, session->cwdlen); if (thread == NULL || map == NULL) goto out_problem; @@ -444,22 +542,52 @@ int event__process_task(event_t *self, struct perf_session *session) void thread__find_addr_map(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, u64 addr, + enum map_type type, pid_t pid, u64 addr, struct addr_location *al) { struct map_groups *mg = &self->mg; + struct kernel_info *kerninfo = NULL; al->thread = self; al->addr = addr; + al->cpumode = cpumode; + al->filtered = false; - if (cpumode == PERF_RECORD_MISC_KERNEL) { + if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { al->level = 'k'; - mg = &session->kmaps; - } else if (cpumode == PERF_RECORD_MISC_USER) + kerninfo = kerninfo__findhost(&session->kerninfo_root); + mg = &kerninfo->kmaps; + } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { al->level = '.'; - else { - al->level = 'H'; + kerninfo = kerninfo__findhost(&session->kerninfo_root); + } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { + al->level = 'g'; + kerninfo = kerninfo__find(&session->kerninfo_root, pid); + if (!kerninfo) { + al->map = NULL; + return; + } + mg = &kerninfo->kmaps; + } else { + /* + * 'u' means guest os user space. + * TODO: We don't support guest user space. Might support late. + */ + if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) + al->level = 'u'; + else + al->level = 'H'; al->map = NULL; + + if ((cpumode == PERF_RECORD_MISC_GUEST_USER || + cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && + !perf_guest) + al->filtered = true; + if ((cpumode == PERF_RECORD_MISC_USER || + cpumode == PERF_RECORD_MISC_KERNEL) && + !perf_host) + al->filtered = true; + return; } try_again: @@ -474,8 +602,11 @@ try_again: * "[vdso]" dso, but for now lets use the old trick of looking * in the whole kernel symbol list. */ - if ((long long)al->addr < 0 && mg != &session->kmaps) { - mg = &session->kmaps; + if ((long long)al->addr < 0 && + cpumode == PERF_RECORD_MISC_KERNEL && + kerninfo && + mg != &kerninfo->kmaps) { + mg = &kerninfo->kmaps; goto try_again; } } else @@ -484,11 +615,11 @@ try_again: void thread__find_addr_location(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, u64 addr, + enum map_type type, pid_t pid, u64 addr, struct addr_location *al, symbol_filter_t filter) { - thread__find_addr_map(self, session, cpumode, type, addr, al); + thread__find_addr_map(self, session, cpumode, type, pid, addr, al); if (al->map != NULL) al->sym = map__find_symbol(al->map, al->addr, filter); else @@ -524,7 +655,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, - self->ip.ip, al); + self->ip.pid, self->ip.ip, al); dump_printf(" ...... dso: %s\n", al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : ""); @@ -554,7 +685,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) goto out_filtered; - al->filtered = false; return 0; out_filtered: diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index e5740ea140ab..4af2ed5d48ad 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -79,6 +79,7 @@ struct sample_data { struct build_id_event { struct perf_event_header header; + pid_t pid; u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; char filename[]; }; @@ -154,10 +155,13 @@ int event__synthesize_thread(pid_t pid, event__handler_t process, void event__synthesize_threads(event__handler_t process, struct perf_session *session); int event__synthesize_kernel_mmap(event__handler_t process, - struct perf_session *session, - const char *symbol_name); + struct perf_session *session, + struct kernel_info *kerninfo, + const char *symbol_name); + int event__synthesize_modules(event__handler_t process, - struct perf_session *session); + struct perf_session *session, + struct kernel_info *kerninfo); int event__process_comm(event_t *self, struct perf_session *session); int event__process_lost(event_t *self, struct perf_session *session); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 628173ba689e..75d016768021 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -190,7 +190,8 @@ static int write_padded(int fd, const void *bf, size_t count, continue; \ else -static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) +static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, + u16 misc, int fd) { struct dso *pos; @@ -205,6 +206,7 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) len = ALIGN(len, NAME_ALIGN); memset(&b, 0, sizeof(b)); memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); + b.pid = pid; b.header.misc = misc; b.header.size = sizeof(b) + len; err = do_write(fd, &b, sizeof(b)); @@ -219,13 +221,33 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) return 0; } -static int dsos__write_buildid_table(int fd) +static int dsos__write_buildid_table(struct perf_header *header, int fd) { - int err = __dsos__write_buildid_table(&dsos__kernel, - PERF_RECORD_MISC_KERNEL, fd); - if (err == 0) - err = __dsos__write_buildid_table(&dsos__user, - PERF_RECORD_MISC_USER, fd); + struct perf_session *session = container_of(header, + struct perf_session, header); + struct rb_node *nd; + int err = 0; + u16 kmisc, umisc; + + for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + if (is_host_kernel(pos)) { + kmisc = PERF_RECORD_MISC_KERNEL; + umisc = PERF_RECORD_MISC_USER; + } else { + kmisc = PERF_RECORD_MISC_GUEST_KERNEL; + umisc = PERF_RECORD_MISC_GUEST_USER; + } + + err = __dsos__write_buildid_table(&pos->dsos__kernel, pos->pid, + kmisc, fd); + if (err == 0) + err = __dsos__write_buildid_table(&pos->dsos__user, + pos->pid, umisc, fd); + if (err) + break; + } return err; } @@ -342,9 +364,12 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) return err; } -static int dsos__cache_build_ids(void) +static int dsos__cache_build_ids(struct perf_header *self) { - int err_kernel, err_user; + struct perf_session *session = container_of(self, + struct perf_session, header); + struct rb_node *nd; + int ret = 0; char debugdir[PATH_MAX]; snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), @@ -353,9 +378,30 @@ static int dsos__cache_build_ids(void) if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) return -1; - err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); - err_user = __dsos__cache_build_ids(&dsos__user, debugdir); - return err_kernel || err_user ? -1 : 0; + for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + ret |= __dsos__cache_build_ids(&pos->dsos__kernel, debugdir); + ret |= __dsos__cache_build_ids(&pos->dsos__user, debugdir); + } + return ret ? -1 : 0; +} + +static bool dsos__read_build_ids(struct perf_header *self, bool with_hits) +{ + bool ret = false; + struct perf_session *session = container_of(self, + struct perf_session, header); + struct rb_node *nd; + + for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + ret |= __dsos__read_build_ids(&pos->dsos__kernel, with_hits); + ret |= __dsos__read_build_ids(&pos->dsos__user, with_hits); + } + + return ret; } static int perf_header__adds_write(struct perf_header *self, int fd) @@ -366,7 +412,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) u64 sec_start; int idx = 0, err; - if (dsos__read_build_ids(true)) + if (dsos__read_build_ids(self, true)) perf_header__set_feat(self, HEADER_BUILD_ID); nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); @@ -401,14 +447,14 @@ static int perf_header__adds_write(struct perf_header *self, int fd) /* Write build-ids */ buildid_sec->offset = lseek(fd, 0, SEEK_CUR); - err = dsos__write_buildid_table(fd); + err = dsos__write_buildid_table(self, fd); if (err < 0) { pr_debug("failed to write buildid table\n"); goto out_free; } buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; - dsos__cache_build_ids(); + dsos__cache_build_ids(self); } lseek(fd, sec_start, SEEK_SET); @@ -633,6 +679,85 @@ int perf_file_header__read(struct perf_file_header *self, return 0; } +static int __event_process_build_id(struct build_id_event *bev, + char *filename, + struct perf_session *session) +{ + int err = -1; + struct list_head *head; + struct kernel_info *kerninfo; + u16 misc; + struct dso *dso; + enum dso_kernel_type dso_type; + + kerninfo = kerninfo__findnew(&session->kerninfo_root, bev->pid); + if (!kerninfo) + goto out; + + misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + switch (misc) { + case PERF_RECORD_MISC_KERNEL: + dso_type = DSO_TYPE_KERNEL; + head = &kerninfo->dsos__kernel; + break; + case PERF_RECORD_MISC_GUEST_KERNEL: + dso_type = DSO_TYPE_GUEST_KERNEL; + head = &kerninfo->dsos__kernel; + break; + case PERF_RECORD_MISC_USER: + case PERF_RECORD_MISC_GUEST_USER: + dso_type = DSO_TYPE_USER; + head = &kerninfo->dsos__user; + break; + default: + goto out; + } + + dso = __dsos__findnew(head, filename); + if (dso != NULL) { + dso__set_build_id(dso, &bev->build_id); + if (filename[0] == '[') + dso->kernel = dso_type; + } + + err = 0; +out: + return err; +} + +static int perf_header__read_build_ids(struct perf_header *self, + int input, u64 offset, u64 size) +{ + struct perf_session *session = container_of(self, + struct perf_session, header); + struct build_id_event bev; + char filename[PATH_MAX]; + u64 limit = offset + size; + int err = -1; + + while (offset < limit) { + ssize_t len; + + if (read(input, &bev, sizeof(bev)) != sizeof(bev)) + goto out; + + if (self->needs_swap) + perf_event_header__bswap(&bev.header); + + len = bev.header.size - sizeof(bev); + if (read(input, filename, len) != len) + goto out; + + __event_process_build_id(&bev, filename, session); + + offset += bev.header.size; + } + err = 0; +out: + return err; +} + static int perf_file_section__process(struct perf_file_section *self, struct perf_header *ph, int feat, int fd) @@ -989,6 +1114,7 @@ int event__process_tracing_data(event_t *self, int event__synthesize_build_id(struct dso *pos, u16 misc, event__handler_t process, + struct kernel_info *kerninfo, struct perf_session *session) { event_t ev; @@ -1005,6 +1131,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; ev.build_id.header.misc = misc; + ev.build_id.pid = kerninfo->pid; ev.build_id.header.size = sizeof(ev.build_id) + len; memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); @@ -1015,6 +1142,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, static int __event_synthesize_build_ids(struct list_head *head, u16 misc, event__handler_t process, + struct kernel_info *kerninfo, struct perf_session *session) { struct dso *pos; @@ -1024,7 +1152,8 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, if (!pos->hit) continue; - err = event__synthesize_build_id(pos, misc, process, session); + err = event__synthesize_build_id(pos, misc, process, + kerninfo, session); if (err < 0) return err; } @@ -1035,44 +1164,48 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, int event__synthesize_build_ids(event__handler_t process, struct perf_session *session) { - int err; + int err = 0; + u16 kmisc, umisc; + struct kernel_info *pos; + struct rb_node *nd; - if (!dsos__read_build_ids(true)) + if (!dsos__read_build_ids(&session->header, true)) return 0; - err = __event_synthesize_build_ids(&dsos__kernel, - PERF_RECORD_MISC_KERNEL, - process, session); - if (err == 0) - err = __event_synthesize_build_ids(&dsos__user, - PERF_RECORD_MISC_USER, - process, session); + for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { + pos = rb_entry(nd, struct kernel_info, rb_node); + if (is_host_kernel(pos)) { + kmisc = PERF_RECORD_MISC_KERNEL; + umisc = PERF_RECORD_MISC_USER; + } else { + kmisc = PERF_RECORD_MISC_GUEST_KERNEL; + umisc = PERF_RECORD_MISC_GUEST_USER; + } + + err = __event_synthesize_build_ids(&pos->dsos__kernel, + kmisc, process, pos, session); + if (err == 0) + err = __event_synthesize_build_ids(&pos->dsos__user, + umisc, process, pos, session); + if (err) + break; + } if (err < 0) { pr_debug("failed to synthesize build ids\n"); return err; } - dsos__cache_build_ids(); + dsos__cache_build_ids(&session->header); return 0; } int event__process_build_id(event_t *self, - struct perf_session *session __unused) + struct perf_session *session) { - struct list_head *head = &dsos__user; - struct dso *dso; - - if (self->build_id.header.misc & PERF_RECORD_MISC_KERNEL) - head = &dsos__kernel; - - dso = __dsos__findnew(head, self->build_id.filename); - if (dso != NULL) { - dso__set_build_id(dso, &self->build_id.build_id); - if (head == &dsos__kernel && self->build_id.filename[0] == '[') - dso->kernel = 1; - } - + __event_process_build_id(&self->build_id, + self->build_id.filename, + session); return 0; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 4214e2375650..275915458148 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -120,6 +120,7 @@ int event__process_tracing_data(event_t *self, int event__synthesize_build_id(struct dso *pos, u16 misc, event__handler_t process, + struct kernel_info *kerninfo, struct perf_session *session); int event__synthesize_build_ids(event__handler_t process, struct perf_session *session); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9c2b8743cef6..ad6b22dde27f 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -8,6 +8,30 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; +void __perf_session__add_count(struct hist_entry *he, + struct addr_location *al, + u64 count) +{ + he->count += count; + + switch (al->cpumode) { + case PERF_RECORD_MISC_KERNEL: + he->count_sys += count; + break; + case PERF_RECORD_MISC_USER: + he->count_us += count; + break; + case PERF_RECORD_MISC_GUEST_KERNEL: + he->count_guest_sys += count; + break; + case PERF_RECORD_MISC_GUEST_USER: + he->count_guest_us += count; + break; + default: + break; + } +} + /* * histogram, sorted on item, collects counts */ @@ -464,7 +488,7 @@ int hist_entry__snprintf(struct hist_entry *self, u64 session_total) { struct sort_entry *se; - u64 count, total; + u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; const char *sep = symbol_conf.field_sep; int ret; @@ -474,9 +498,17 @@ int hist_entry__snprintf(struct hist_entry *self, if (pair_session) { count = self->pair ? self->pair->count : 0; total = pair_session->events_stats.total; + count_sys = self->pair ? self->pair->count_sys : 0; + count_us = self->pair ? self->pair->count_us : 0; + count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; + count_guest_us = self->pair ? self->pair->count_guest_us : 0; } else { count = self->count; total = session_total; + count_sys = self->count_sys; + count_us = self->count_us; + count_guest_sys = self->count_guest_sys; + count_guest_us = self->count_guest_us; } if (total) { @@ -487,6 +519,26 @@ int hist_entry__snprintf(struct hist_entry *self, else ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", (count * 100.0) / total); + if (symbol_conf.show_cpu_utilization) { + ret += percent_color_snprintf(s + ret, size - ret, + sep ? "%.2f" : " %6.2f%%", + (count_sys * 100.0) / total); + ret += percent_color_snprintf(s + ret, size - ret, + sep ? "%.2f" : " %6.2f%%", + (count_us * 100.0) / total); + if (perf_guest) { + ret += percent_color_snprintf(s + ret, + size - ret, + sep ? "%.2f" : " %6.2f%%", + (count_guest_sys * 100.0) / + total); + ret += percent_color_snprintf(s + ret, + size - ret, + sep ? "%.2f" : " %6.2f%%", + (count_guest_us * 100.0) / + total); + } + } } else ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); @@ -597,6 +649,24 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, fputs(" Samples ", fp); } + if (symbol_conf.show_cpu_utilization) { + if (sep) { + ret += fprintf(fp, "%csys", *sep); + ret += fprintf(fp, "%cus", *sep); + if (perf_guest) { + ret += fprintf(fp, "%cguest sys", *sep); + ret += fprintf(fp, "%cguest us", *sep); + } + } else { + ret += fprintf(fp, " sys "); + ret += fprintf(fp, " us "); + if (perf_guest) { + ret += fprintf(fp, " guest sys "); + ret += fprintf(fp, " guest us "); + } + } + } + if (pair) { if (sep) ret += fprintf(fp, "%cDelta", *sep); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ad17f0ad798b..9df1c340ec92 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -12,6 +12,9 @@ struct addr_location; struct symbol; struct rb_root; +void __perf_session__add_count(struct hist_entry *he, + struct addr_location *al, + u64 count); struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct addr_location *al, struct symbol *parent, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 37913b241bdf..7facd016ec97 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "map.h" const char *map_type__name[MAP__NR_TYPES] = { @@ -37,9 +38,11 @@ void map__init(struct map *self, enum map_type type, self->map_ip = map__map_ip; self->unmap_ip = map__unmap_ip; RB_CLEAR_NODE(&self->rb_node); + self->groups = NULL; } -struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, +struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, + u64 pgoff, u32 pid, char *filename, enum map_type type, char *cwd, int cwdlen) { struct map *self = malloc(sizeof(*self)); @@ -66,7 +69,7 @@ struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, filename = newfilename; } - dso = dsos__findnew(filename); + dso = __dsos__findnew(dsos__list, filename); if (dso == NULL) goto out_delete; @@ -242,6 +245,7 @@ void map_groups__init(struct map_groups *self) self->maps[i] = RB_ROOT; INIT_LIST_HEAD(&self->removed_maps[i]); } + self->this_kerninfo = NULL; } void map_groups__flush(struct map_groups *self) @@ -508,3 +512,134 @@ struct map *maps__find(struct rb_root *maps, u64 ip) return NULL; } + +struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, + pid_t pid, const char *root_dir) +{ + struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node *parent = NULL; + struct kernel_info *kerninfo, *pos; + + kerninfo = malloc(sizeof(struct kernel_info)); + if (!kerninfo) + return NULL; + + kerninfo->pid = pid; + map_groups__init(&kerninfo->kmaps); + kerninfo->root_dir = strdup(root_dir); + RB_CLEAR_NODE(&kerninfo->rb_node); + INIT_LIST_HEAD(&kerninfo->dsos__user); + INIT_LIST_HEAD(&kerninfo->dsos__kernel); + kerninfo->kmaps.this_kerninfo = kerninfo; + + while (*p != NULL) { + parent = *p; + pos = rb_entry(parent, struct kernel_info, rb_node); + if (pid < pos->pid) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&kerninfo->rb_node, parent, p); + rb_insert_color(&kerninfo->rb_node, kerninfo_root); + + return kerninfo; +} + +struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid) +{ + struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node *parent = NULL; + struct kernel_info *kerninfo; + struct kernel_info *default_kerninfo = NULL; + + while (*p != NULL) { + parent = *p; + kerninfo = rb_entry(parent, struct kernel_info, rb_node); + if (pid < kerninfo->pid) + p = &(*p)->rb_left; + else if (pid > kerninfo->pid) + p = &(*p)->rb_right; + else + return kerninfo; + if (!kerninfo->pid) + default_kerninfo = kerninfo; + } + + return default_kerninfo; +} + +struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root) +{ + struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node *parent = NULL; + struct kernel_info *kerninfo; + pid_t pid = HOST_KERNEL_ID; + + while (*p != NULL) { + parent = *p; + kerninfo = rb_entry(parent, struct kernel_info, rb_node); + if (pid < kerninfo->pid) + p = &(*p)->rb_left; + else if (pid > kerninfo->pid) + p = &(*p)->rb_right; + else + return kerninfo; + } + + return NULL; +} + +struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid) +{ + char path[PATH_MAX]; + const char *root_dir; + int ret; + struct kernel_info *kerninfo = kerninfo__find(kerninfo_root, pid); + + if (!kerninfo || kerninfo->pid != pid) { + if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID) + root_dir = ""; + else { + if (!symbol_conf.guestmount) + goto out; + sprintf(path, "%s/%d", symbol_conf.guestmount, pid); + ret = access(path, R_OK); + if (ret) { + pr_err("Can't access file %s\n", path); + goto out; + } + root_dir = path; + } + kerninfo = add_new_kernel_info(kerninfo_root, pid, root_dir); + } + +out: + return kerninfo; +} + +void kerninfo__process_allkernels(struct rb_root *kerninfo_root, + process_kernel_info process, + void *data) +{ + struct rb_node *nd; + + for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + process(pos, data); + } +} + +char *kern_mmap_name(struct kernel_info *kerninfo, char *buff) +{ + if (is_host_kernel(kerninfo)) + sprintf(buff, "[%s]", "kernel.kallsyms"); + else if (is_default_guest(kerninfo)) + sprintf(buff, "[%s]", "guest.kernel.kallsyms"); + else + sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", kerninfo->pid); + + return buff; +} diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 2031278cc06a..30d38d634e09 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -19,6 +19,7 @@ extern const char *map_type__name[MAP__NR_TYPES]; struct dso; struct ref_reloc_sym; struct map_groups; +struct kernel_info; struct map { union { @@ -36,6 +37,7 @@ struct map { u64 (*unmap_ip)(struct map *, u64); struct dso *dso; + struct map_groups *groups; }; struct kmap { @@ -43,6 +45,26 @@ struct kmap { struct map_groups *kmaps; }; +struct map_groups { + struct rb_root maps[MAP__NR_TYPES]; + struct list_head removed_maps[MAP__NR_TYPES]; + struct kernel_info *this_kerninfo; +}; + +/* Native host kernel uses -1 as pid index in kernel_info */ +#define HOST_KERNEL_ID (-1) +#define DEFAULT_GUEST_KERNEL_ID (0) + +struct kernel_info { + struct rb_node rb_node; + pid_t pid; + char *root_dir; + struct list_head dsos__user; + struct list_head dsos__kernel; + struct map_groups kmaps; + struct map *vmlinux_maps[MAP__NR_TYPES]; +}; + static inline struct kmap *map__kmap(struct map *self) { return (struct kmap *)(self + 1); @@ -74,7 +96,8 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); void map__init(struct map *self, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso); -struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, +struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, + u64 pgoff, u32 pid, char *filename, enum map_type type, char *cwd, int cwdlen); void map__delete(struct map *self); struct map *map__clone(struct map *self); @@ -91,11 +114,6 @@ void map__fixup_end(struct map *self); void map__reloc_vmlinux(struct map *self); -struct map_groups { - struct rb_root maps[MAP__NR_TYPES]; - struct list_head removed_maps[MAP__NR_TYPES]; -}; - size_t __map_groups__fprintf_maps(struct map_groups *self, enum map_type type, int verbose, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); @@ -106,9 +124,40 @@ int map_groups__clone(struct map_groups *self, size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); +struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, + pid_t pid, const char *root_dir); +struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid); +struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid); +struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root); +char *kern_mmap_name(struct kernel_info *kerninfo, char *buff); + +/* + * Default guest kernel is defined by parameter --guestkallsyms + * and --guestmodules + */ +static inline int is_default_guest(struct kernel_info *kerninfo) +{ + if (!kerninfo) + return 0; + return kerninfo->pid == DEFAULT_GUEST_KERNEL_ID; +} + +static inline int is_host_kernel(struct kernel_info *kerninfo) +{ + if (!kerninfo) + return 0; + return kerninfo->pid == HOST_KERNEL_ID; +} + +typedef void (*process_kernel_info)(struct kernel_info *kerninfo, void *data); +void kerninfo__process_allkernels(struct rb_root *kerninfo_root, + process_kernel_info process, + void *data); + static inline void map_groups__insert(struct map_groups *self, struct map *map) { - maps__insert(&self->maps[map->type], map); + maps__insert(&self->maps[map->type], map); + map->groups = self; } static inline struct map *map_groups__find(struct map_groups *self, @@ -148,13 +197,11 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, struct map *map_groups__find_by_name(struct map_groups *self, enum map_type type, const char *name); -int __map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES], - struct dso *kernel); -int map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES]); -struct map *map_groups__new_module(struct map_groups *self, u64 start, - const char *filename); +struct map *map_groups__new_module(struct map_groups *self, + u64 start, + const char *filename, + struct kernel_info *kerninfo); + void map_groups__flush(struct map_groups *self); #endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5bf8ab034466..3967f8f63d0d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -78,6 +78,7 @@ static struct map *kmaps[MAP__NR_TYPES]; /* Initialize symbol maps and path of vmlinux */ static int init_vmlinux(void) { + struct dso *kernel; int ret; symbol_conf.sort_by_name = true; @@ -91,8 +92,12 @@ static int init_vmlinux(void) goto out; } + kernel = dso__new_kernel(symbol_conf.vmlinux_name); + if (kernel == NULL) + die("Failed to create kernel dso."); + map_groups__init(&kmap_groups); - ret = map_groups__create_kernel_maps(&kmap_groups, kmaps); + ret = __map_groups__create_kernel_maps(&kmap_groups, kmaps, kernel); if (ret < 0) pr_debug("Failed to create kernel maps.\n"); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0fdf3ebef1e9..7d88ae5c270f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -67,6 +67,17 @@ void perf_session__update_sample_type(struct perf_session *self) self->sample_type = perf_header__sample_type(&self->header); } +int perf_session__create_kernel_maps(struct perf_session *self) +{ + int ret; + struct rb_root *root = &self->kerninfo_root; + + ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID); + if (ret >= 0) + ret = map_groups__create_guest_kernel_maps(root); + return ret; +} + struct perf_session *perf_session__new(const char *filename, int mode, bool force) { size_t len = filename ? strlen(filename) + 1 : 0; @@ -86,7 +97,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->cwd = NULL; self->cwdlen = 0; self->unknown_events = 0; - map_groups__init(&self->kmaps); + self->kerninfo_root = RB_ROOT; if (mode == O_RDONLY) { if (perf_session__open(self, force) < 0) @@ -157,8 +168,9 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, continue; } + al.filtered = false; thread__find_addr_location(thread, self, cpumode, - MAP__FUNCTION, ip, &al, NULL); + MAP__FUNCTION, thread->pid, ip, &al, NULL); if (al.sym != NULL) { if (sort__has_parent && !*parent && symbol__match_parent_regex(al.sym)) @@ -399,46 +411,6 @@ void perf_event_header__bswap(struct perf_event_header *self) self->size = bswap_16(self->size); } -int perf_header__read_build_ids(struct perf_header *self, - int input, u64 offset, u64 size) -{ - struct build_id_event bev; - char filename[PATH_MAX]; - u64 limit = offset + size; - int err = -1; - - while (offset < limit) { - struct dso *dso; - ssize_t len; - struct list_head *head = &dsos__user; - - if (read(input, &bev, sizeof(bev)) != sizeof(bev)) - goto out; - - if (self->needs_swap) - perf_event_header__bswap(&bev.header); - - len = bev.header.size - sizeof(bev); - if (read(input, filename, len) != len) - goto out; - - if (bev.header.misc & PERF_RECORD_MISC_KERNEL) - head = &dsos__kernel; - - dso = __dsos__findnew(head, filename); - if (dso != NULL) { - dso__set_build_id(dso, &bev.build_id); - if (head == &dsos__kernel && filename[0] == '[') - dso->kernel = 1; - } - - offset += bev.header.size; - } - err = 0; -out: - return err; -} - static struct thread *perf_session__register_idle_thread(struct perf_session *self) { struct thread *thread = perf_session__findnew(self, 0); @@ -690,26 +662,33 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg) return true; } -int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, +int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, u64 addr) { char *bracket; enum map_type i; + struct ref_reloc_sym *ref; + + ref = zalloc(sizeof(struct ref_reloc_sym)); + if (ref == NULL) + return -ENOMEM; - self->ref_reloc_sym.name = strdup(symbol_name); - if (self->ref_reloc_sym.name == NULL) + ref->name = strdup(symbol_name); + if (ref->name == NULL) { + free(ref); return -ENOMEM; + } - bracket = strchr(self->ref_reloc_sym.name, ']'); + bracket = strchr(ref->name, ']'); if (bracket) *bracket = '\0'; - self->ref_reloc_sym.addr = addr; + ref->addr = addr; for (i = 0; i < MAP__NR_TYPES; ++i) { - struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); - kmap->ref_reloc_sym = &self->ref_reloc_sym; + struct kmap *kmap = map__kmap(maps[i]); + kmap->ref_reloc_sym = ref; } return 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0ac14d42dc28..5e47c87b9266 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -15,17 +15,15 @@ struct perf_session { struct perf_header header; unsigned long size; unsigned long mmap_window; - struct map_groups kmaps; struct rb_root threads; struct thread *last_match; - struct map *vmlinux_maps[MAP__NR_TYPES]; + struct rb_root kerninfo_root; struct events_stats events_stats; struct rb_root stats_by_id; unsigned long event_total[PERF_RECORD_MAX]; unsigned long unknown_events; struct rb_root hists; u64 sample_type; - struct ref_reloc_sym ref_reloc_sym; int fd; bool fd_pipe; int cwdlen; @@ -69,33 +67,13 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, bool perf_session__has_traces(struct perf_session *self, const char *msg); -int perf_header__read_build_ids(struct perf_header *self, int input, - u64 offset, u64 file_size); - -int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, +int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, u64 addr); void mem_bswap_64(void *src, int byte_size); -static inline int __perf_session__create_kernel_maps(struct perf_session *self, - struct dso *kernel) -{ - return __map_groups__create_kernel_maps(&self->kmaps, - self->vmlinux_maps, kernel); -} - -static inline int perf_session__create_kernel_maps(struct perf_session *self) -{ - return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); -} - -static inline struct map * - perf_session__new_module_map(struct perf_session *self, - u64 start, const char *filename) -{ - return map_groups__new_module(&self->kmaps, start, filename); -} +int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 1d857aa2c01f..b7c54eeed9c9 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -44,6 +44,11 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; u64 count; + u64 count_sys; + u64 count_us; + u64 count_guest_sys; + u64 count_guest_us; + /* * XXX WARNING! * thread _has_ to come after ms, see diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index f3d4151e46a1..e782e7db16c5 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -28,6 +28,8 @@ static void dsos__add(struct list_head *head, struct dso *dso); static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); static int dso__load_kernel_sym(struct dso *self, struct map *map, symbol_filter_t filter); +static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, + symbol_filter_t filter); static int vmlinux_path__nr_entries; static char **vmlinux_path; @@ -186,6 +188,7 @@ struct dso *dso__new(const char *name) self->loaded = 0; self->sorted_by_name = 0; self->has_build_id = 0; + self->kernel = DSO_TYPE_USER; } return self; @@ -402,12 +405,9 @@ int kallsyms__parse(const char *filename, void *arg, char *symbol_name; line_len = getline(&line, &n, file); - if (line_len < 0) + if (line_len < 0 || !line) break; - if (!line) - goto out_failure; - line[--line_len] = '\0'; /* \n */ len = hex2u64(line, &start); @@ -459,6 +459,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, * map__split_kallsyms, when we have split the maps per module */ symbols__insert(root, sym); + return 0; } @@ -483,6 +484,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, symbol_filter_t filter) { struct map_groups *kmaps = map__kmap(map)->kmaps; + struct kernel_info *kerninfo = kmaps->this_kerninfo; struct map *curr_map = map; struct symbol *pos; int count = 0; @@ -504,15 +506,33 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, *module++ = '\0'; if (strcmp(curr_map->dso->short_name, module)) { - curr_map = map_groups__find_by_name(kmaps, map->type, module); + if (curr_map != map && + self->kernel == DSO_TYPE_GUEST_KERNEL && + is_default_guest(kerninfo)) { + /* + * We assume all symbols of a module are + * continuous in * kallsyms, so curr_map + * points to a module and all its + * symbols are in its kmap. Mark it as + * loaded. + */ + dso__set_loaded(curr_map->dso, + curr_map->type); + } + + curr_map = map_groups__find_by_name(kmaps, + map->type, module); if (curr_map == NULL) { - pr_debug("/proc/{kallsyms,modules} " + pr_err("%s/proc/{kallsyms,modules} " "inconsistency while looking " - "for \"%s\" module!\n", module); - return -1; + "for \"%s\" module!\n", + kerninfo->root_dir, module); + curr_map = map; + goto discard_symbol; } - if (curr_map->dso->loaded) + if (curr_map->dso->loaded && + !is_default_guest(kmaps->this_kerninfo)) goto discard_symbol; } /* @@ -525,13 +545,21 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, char dso_name[PATH_MAX]; struct dso *dso; - snprintf(dso_name, sizeof(dso_name), "[kernel].%d", - kernel_range++); + if (self->kernel == DSO_TYPE_GUEST_KERNEL) + snprintf(dso_name, sizeof(dso_name), + "[guest.kernel].%d", + kernel_range++); + else + snprintf(dso_name, sizeof(dso_name), + "[kernel].%d", + kernel_range++); dso = dso__new(dso_name); if (dso == NULL) return -1; + dso->kernel = self->kernel; + curr_map = map__new2(pos->start, dso, map->type); if (curr_map == NULL) { dso__delete(dso); @@ -555,6 +583,12 @@ discard_symbol: rb_erase(&pos->rb_node, root); } } + if (curr_map != map && + self->kernel == DSO_TYPE_GUEST_KERNEL && + is_default_guest(kmaps->this_kerninfo)) { + dso__set_loaded(curr_map->dso, curr_map->type); + } + return count; } @@ -565,7 +599,10 @@ int dso__load_kallsyms(struct dso *self, const char *filename, return -1; symbols__fixup_end(&self->symbols[map->type]); - self->origin = DSO__ORIG_KERNEL; + if (self->kernel == DSO_TYPE_GUEST_KERNEL) + self->origin = DSO__ORIG_GUEST_KERNEL; + else + self->origin = DSO__ORIG_KERNEL; return dso__split_kallsyms(self, map, filter); } @@ -952,7 +989,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, nr_syms = shdr.sh_size / shdr.sh_entsize; memset(&sym, 0, sizeof(sym)); - if (!self->kernel) { + if (self->kernel == DSO_TYPE_USER) { self->adjust_symbols = (ehdr.e_type == ET_EXEC || elf_section_by_name(elf, &ehdr, &shdr, ".gnu.prelink_undo", @@ -984,7 +1021,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, section_name = elf_sec__name(&shdr, secstrs); - if (self->kernel || kmodule) { + if (self->kernel != DSO_TYPE_USER || kmodule) { char dso_name[PATH_MAX]; if (strcmp(section_name, @@ -1011,6 +1048,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, curr_dso = dso__new(dso_name); if (curr_dso == NULL) goto out_elf_end; + curr_dso->kernel = self->kernel; curr_map = map__new2(start, curr_dso, map->type); if (curr_map == NULL) { @@ -1021,7 +1059,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, curr_map->unmap_ip = identity__map_ip; curr_dso->origin = self->origin; map_groups__insert(kmap->kmaps, curr_map); - dsos__add(&dsos__kernel, curr_dso); + dsos__add(&self->node, curr_dso); dso__set_loaded(curr_dso, map->type); } else curr_dso = curr_map->dso; @@ -1083,7 +1121,7 @@ static bool dso__build_id_equal(const struct dso *self, u8 *build_id) return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; } -static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) +bool __dsos__read_build_ids(struct list_head *head, bool with_hits) { bool have_build_id = false; struct dso *pos; @@ -1101,13 +1139,6 @@ static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) return have_build_id; } -bool dsos__read_build_ids(bool with_hits) -{ - bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits), - ubuildids = __dsos__read_build_ids(&dsos__user, with_hits); - return kbuildids || ubuildids; -} - /* * Align offset to 4 bytes as needed for note name and descriptor data. */ @@ -1242,6 +1273,8 @@ char dso__symtab_origin(const struct dso *self) [DSO__ORIG_BUILDID] = 'b', [DSO__ORIG_DSO] = 'd', [DSO__ORIG_KMODULE] = 'K', + [DSO__ORIG_GUEST_KERNEL] = 'g', + [DSO__ORIG_GUEST_KMODULE] = 'G', }; if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) @@ -1257,11 +1290,20 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) char build_id_hex[BUILD_ID_SIZE * 2 + 1]; int ret = -1; int fd; + struct kernel_info *kerninfo; + const char *root_dir; dso__set_loaded(self, map->type); - if (self->kernel) + if (self->kernel == DSO_TYPE_KERNEL) return dso__load_kernel_sym(self, map, filter); + else if (self->kernel == DSO_TYPE_GUEST_KERNEL) + return dso__load_guest_kernel_sym(self, map, filter); + + if (map->groups && map->groups->this_kerninfo) + kerninfo = map->groups->this_kerninfo; + else + kerninfo = NULL; name = malloc(size); if (!name) @@ -1315,6 +1357,13 @@ more: case DSO__ORIG_DSO: snprintf(name, size, "%s", self->long_name); break; + case DSO__ORIG_GUEST_KMODULE: + if (map->groups && map->groups->this_kerninfo) + root_dir = map->groups->this_kerninfo->root_dir; + else + root_dir = ""; + snprintf(name, size, "%s%s", root_dir, self->long_name); + break; default: goto out; @@ -1368,7 +1417,8 @@ struct map *map_groups__find_by_name(struct map_groups *self, return NULL; } -static int dso__kernel_module_get_build_id(struct dso *self) +static int dso__kernel_module_get_build_id(struct dso *self, + const char *root_dir) { char filename[PATH_MAX]; /* @@ -1378,8 +1428,8 @@ static int dso__kernel_module_get_build_id(struct dso *self) const char *name = self->short_name + 1; snprintf(filename, sizeof(filename), - "/sys/module/%.*s/notes/.note.gnu.build-id", - (int)strlen(name - 1), name); + "%s/sys/module/%.*s/notes/.note.gnu.build-id", + root_dir, (int)strlen(name) - 1, name); if (sysfs__read_build_id(filename, self->build_id, sizeof(self->build_id)) == 0) @@ -1388,7 +1438,8 @@ static int dso__kernel_module_get_build_id(struct dso *self) return 0; } -static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_name) +static int map_groups__set_modules_path_dir(struct map_groups *self, + const char *dir_name) { struct dirent *dent; DIR *dir = opendir(dir_name); @@ -1400,8 +1451,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n while ((dent = readdir(dir)) != NULL) { char path[PATH_MAX]; + struct stat st; + + /*sshfs might return bad dent->d_type, so we have to stat*/ + sprintf(path, "%s/%s", dir_name, dent->d_name); + if (stat(path, &st)) + continue; - if (dent->d_type == DT_DIR) { + if (S_ISDIR(st.st_mode)) { if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; @@ -1433,7 +1490,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n if (long_name == NULL) goto failure; dso__set_long_name(map->dso, long_name); - dso__kernel_module_get_build_id(map->dso); + dso__kernel_module_get_build_id(map->dso, ""); } } @@ -1443,16 +1500,46 @@ failure: return -1; } -static int map_groups__set_modules_path(struct map_groups *self) +static char *get_kernel_version(const char *root_dir) { - struct utsname uts; + char version[PATH_MAX]; + FILE *file; + char *name, *tmp; + const char *prefix = "Linux version "; + + sprintf(version, "%s/proc/version", root_dir); + file = fopen(version, "r"); + if (!file) + return NULL; + + version[0] = '\0'; + tmp = fgets(version, sizeof(version), file); + fclose(file); + + name = strstr(version, prefix); + if (!name) + return NULL; + name += strlen(prefix); + tmp = strchr(name, ' '); + if (tmp) + *tmp = '\0'; + + return strdup(name); +} + +static int map_groups__set_modules_path(struct map_groups *self, + const char *root_dir) +{ + char *version; char modules_path[PATH_MAX]; - if (uname(&uts) < 0) + version = get_kernel_version(root_dir); + if (!version) return -1; - snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", - uts.release); + snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", + root_dir, version); + free(version); return map_groups__set_modules_path_dir(self, modules_path); } @@ -1477,11 +1564,13 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) } struct map *map_groups__new_module(struct map_groups *self, u64 start, - const char *filename) + const char *filename, + struct kernel_info *kerninfo) { struct map *map; - struct dso *dso = __dsos__findnew(&dsos__kernel, filename); + struct dso *dso; + dso = __dsos__findnew(&kerninfo->dsos__kernel, filename); if (dso == NULL) return NULL; @@ -1489,21 +1578,37 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start, if (map == NULL) return NULL; - dso->origin = DSO__ORIG_KMODULE; + if (is_host_kernel(kerninfo)) + dso->origin = DSO__ORIG_KMODULE; + else + dso->origin = DSO__ORIG_GUEST_KMODULE; map_groups__insert(self, map); return map; } -static int map_groups__create_modules(struct map_groups *self) +static int map_groups__create_modules(struct kernel_info *kerninfo) { char *line = NULL; size_t n; - FILE *file = fopen("/proc/modules", "r"); + FILE *file; struct map *map; + const char *root_dir; + const char *modules; + char path[PATH_MAX]; + + if (is_default_guest(kerninfo)) + modules = symbol_conf.default_guest_modules; + else { + sprintf(path, "%s/proc/modules", kerninfo->root_dir); + modules = path; + } + file = fopen(modules, "r"); if (file == NULL) return -1; + root_dir = kerninfo->root_dir; + while (!feof(file)) { char name[PATH_MAX]; u64 start; @@ -1532,16 +1637,17 @@ static int map_groups__create_modules(struct map_groups *self) *sep = '\0'; snprintf(name, sizeof(name), "[%s]", line); - map = map_groups__new_module(self, start, name); + map = map_groups__new_module(&kerninfo->kmaps, + start, name, kerninfo); if (map == NULL) goto out_delete_line; - dso__kernel_module_get_build_id(map->dso); + dso__kernel_module_get_build_id(map->dso, root_dir); } free(line); fclose(file); - return map_groups__set_modules_path(self); + return map_groups__set_modules_path(&kerninfo->kmaps, root_dir); out_delete_line: free(line); @@ -1708,8 +1814,57 @@ out_fixup: return err; } -LIST_HEAD(dsos__user); -LIST_HEAD(dsos__kernel); +static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, + symbol_filter_t filter) +{ + int err; + const char *kallsyms_filename = NULL; + struct kernel_info *kerninfo; + char path[PATH_MAX]; + + if (!map->groups) { + pr_debug("Guest kernel map hasn't the point to groups\n"); + return -1; + } + kerninfo = map->groups->this_kerninfo; + + if (is_default_guest(kerninfo)) { + /* + * if the user specified a vmlinux filename, use it and only + * it, reporting errors to the user if it cannot be used. + * Or use file guest_kallsyms inputted by user on commandline + */ + if (symbol_conf.default_guest_vmlinux_name != NULL) { + err = dso__load_vmlinux(self, map, + symbol_conf.default_guest_vmlinux_name, filter); + goto out_try_fixup; + } + + kallsyms_filename = symbol_conf.default_guest_kallsyms; + if (!kallsyms_filename) + return -1; + } else { + sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + kallsyms_filename = path; + } + + err = dso__load_kallsyms(self, kallsyms_filename, map, filter); + if (err > 0) + pr_debug("Using %s for symbols\n", kallsyms_filename); + +out_try_fixup: + if (err > 0) { + if (kallsyms_filename != NULL) { + kern_mmap_name(kerninfo, path); + dso__set_long_name(self, + strdup(path)); + } + map__fixup_start(map); + map__fixup_end(map); + } + + return err; +} static void dsos__add(struct list_head *head, struct dso *dso) { @@ -1752,10 +1907,16 @@ static void __dsos__fprintf(struct list_head *head, FILE *fp) } } -void dsos__fprintf(FILE *fp) +void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp) { - __dsos__fprintf(&dsos__kernel, fp); - __dsos__fprintf(&dsos__user, fp); + struct rb_node *nd; + + for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + __dsos__fprintf(&pos->dsos__kernel, fp); + __dsos__fprintf(&pos->dsos__user, fp); + } } static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, @@ -1773,10 +1934,21 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, return ret; } -size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) +size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, + FILE *fp, bool with_hits) { - return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + - __dsos__fprintf_buildid(&dsos__user, fp, with_hits)); + struct rb_node *nd; + size_t ret = 0; + + for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + ret += __dsos__fprintf_buildid(&pos->dsos__kernel, + fp, with_hits); + ret += __dsos__fprintf_buildid(&pos->dsos__user, + fp, with_hits); + } + return ret; } struct dso *dso__new_kernel(const char *name) @@ -1785,28 +1957,59 @@ struct dso *dso__new_kernel(const char *name) if (self != NULL) { dso__set_short_name(self, "[kernel]"); - self->kernel = 1; + self->kernel = DSO_TYPE_KERNEL; + } + + return self; +} + +static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo, + const char *name) +{ + char buff[PATH_MAX]; + struct dso *self; + + kern_mmap_name(kerninfo, buff); + self = dso__new(name ?: buff); + if (self != NULL) { + dso__set_short_name(self, "[guest.kernel]"); + self->kernel = DSO_TYPE_GUEST_KERNEL; } return self; } -void dso__read_running_kernel_build_id(struct dso *self) +void dso__read_running_kernel_build_id(struct dso *self, + struct kernel_info *kerninfo) { - if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, + char path[PATH_MAX]; + + if (is_default_guest(kerninfo)) + return; + sprintf(path, "%s/sys/kernel/notes", kerninfo->root_dir); + if (sysfs__read_build_id(path, self->build_id, sizeof(self->build_id)) == 0) self->has_build_id = true; } -static struct dso *dsos__create_kernel(const char *vmlinux) +static struct dso *dsos__create_kernel(struct kernel_info *kerninfo) { - struct dso *kernel = dso__new_kernel(vmlinux); + const char *vmlinux_name = NULL; + struct dso *kernel; - if (kernel != NULL) { - dso__read_running_kernel_build_id(kernel); - dsos__add(&dsos__kernel, kernel); + if (is_host_kernel(kerninfo)) { + vmlinux_name = symbol_conf.vmlinux_name; + kernel = dso__new_kernel(vmlinux_name); + } else { + if (is_default_guest(kerninfo)) + vmlinux_name = symbol_conf.default_guest_vmlinux_name; + kernel = dso__new_guest_kernel(kerninfo, vmlinux_name); } + if (kernel != NULL) { + dso__read_running_kernel_build_id(kernel, kerninfo); + dsos__add(&kerninfo->dsos__kernel, kernel); + } return kernel; } @@ -1950,23 +2153,29 @@ out_free_comm_list: return -1; } -int map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES]) +int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid) { - struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); + struct kernel_info *kerninfo; + struct dso *kernel; + kerninfo = kerninfo__findnew(kerninfo_root, pid); + if (kerninfo == NULL) + return -1; + kernel = dsos__create_kernel(kerninfo); if (kernel == NULL) return -1; - if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0) + if (__map_groups__create_kernel_maps(&kerninfo->kmaps, + kerninfo->vmlinux_maps, kernel) < 0) return -1; - if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) + if (symbol_conf.use_modules && + map_groups__create_modules(kerninfo) < 0) pr_debug("Problems creating module maps, continuing anyway...\n"); /* * Now that we have all the maps created, just set the ->end of them: */ - map_groups__fixup_end(self); + map_groups__fixup_end(&kerninfo->kmaps); return 0; } @@ -2012,3 +2221,46 @@ char *strxfrchar(char *s, char from, char to) return s; } + +int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) +{ + int ret = 0; + struct dirent **namelist = NULL; + int i, items = 0; + char path[PATH_MAX]; + pid_t pid; + + if (symbol_conf.default_guest_vmlinux_name || + symbol_conf.default_guest_modules || + symbol_conf.default_guest_kallsyms) { + map_groups__create_kernel_maps(kerninfo_root, + DEFAULT_GUEST_KERNEL_ID); + } + + if (symbol_conf.guestmount) { + items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); + if (items <= 0) + return -ENOENT; + for (i = 0; i < items; i++) { + if (!isdigit(namelist[i]->d_name[0])) { + /* Filter out . and .. */ + continue; + } + pid = atoi(namelist[i]->d_name); + sprintf(path, "%s/%s/proc/kallsyms", + symbol_conf.guestmount, + namelist[i]->d_name); + ret = access(path, R_OK); + if (ret) { + pr_debug("Can't access file %s\n", path); + goto failure; + } + map_groups__create_kernel_maps(kerninfo_root, + pid); + } +failure: + free(namelist); + } + + return ret; +} diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 757fae3f5ee0..478f5ab37787 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -69,10 +69,15 @@ struct symbol_conf { show_nr_samples, use_callchain, exclude_other, - full_paths; + full_paths, + show_cpu_utilization; const char *vmlinux_name, *field_sep; - char *dso_list_str, + const char *default_guest_vmlinux_name, + *default_guest_kallsyms, + *default_guest_modules; + const char *guestmount; + char *dso_list_str, *comm_list_str, *sym_list_str, *col_width_list_str; @@ -106,6 +111,13 @@ struct addr_location { u64 addr; char level; bool filtered; + unsigned int cpumode; +}; + +enum dso_kernel_type { + DSO_TYPE_USER = 0, + DSO_TYPE_KERNEL, + DSO_TYPE_GUEST_KERNEL }; struct dso { @@ -115,7 +127,7 @@ struct dso { u8 adjust_symbols:1; u8 slen_calculated:1; u8 has_build_id:1; - u8 kernel:1; + enum dso_kernel_type kernel; u8 hit:1; u8 annotate_warned:1; unsigned char origin; @@ -143,34 +155,30 @@ static inline void dso__set_loaded(struct dso *self, enum map_type type) void dso__sort_by_name(struct dso *self, enum map_type type); -extern struct list_head dsos__user, dsos__kernel; - struct dso *__dsos__findnew(struct list_head *head, const char *name); -static inline struct dso *dsos__findnew(const char *name) -{ - return __dsos__findnew(&dsos__user, name); -} - int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_vmlinux_path(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, symbol_filter_t filter); -void dsos__fprintf(FILE *fp); -size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); +void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp); +size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, + FILE *fp, bool with_hits); size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); enum dso_origin { DSO__ORIG_KERNEL = 0, + DSO__ORIG_GUEST_KERNEL, DSO__ORIG_JAVA_JIT, DSO__ORIG_BUILD_ID_CACHE, DSO__ORIG_FEDORA, DSO__ORIG_UBUNTU, DSO__ORIG_BUILDID, DSO__ORIG_DSO, + DSO__ORIG_GUEST_KMODULE, DSO__ORIG_KMODULE, DSO__ORIG_NOT_FOUND, }; @@ -178,19 +186,26 @@ enum dso_origin { char dso__symtab_origin(const struct dso *self); void dso__set_long_name(struct dso *self, char *name); void dso__set_build_id(struct dso *self, void *build_id); -void dso__read_running_kernel_build_id(struct dso *self); +void dso__read_running_kernel_build_id(struct dso *self, + struct kernel_info *kerninfo); struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, const char *name); int filename__read_build_id(const char *filename, void *bf, size_t size); int sysfs__read_build_id(const char *filename, void *bf, size_t size); -bool dsos__read_build_ids(bool with_hits); +bool __dsos__read_build_ids(struct list_head *head, bool with_hits); int build_id__sprintf(const u8 *self, int len, char *bf); int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); +int __map_groups__create_kernel_maps(struct map_groups *self, + struct map *vmlinux_maps[MAP__NR_TYPES], + struct dso *kernel); +int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid); +int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root); + int symbol__init(void); bool symbol_type__is_a(char symbol_type, enum map_type map_type); diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 9c488fcadec9..1dfd9ff8bdcd 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -33,12 +33,12 @@ static inline struct map *thread__find_map(struct thread *self, void thread__find_addr_map(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, u64 addr, + enum map_type type, pid_t pid, u64 addr, struct addr_location *al); void thread__find_addr_location(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, u64 addr, + enum map_type type, pid_t pid, u64 addr, struct addr_location *al, symbol_filter_t filter); #endif /* __PERF_THREAD_H */ -- cgit v1.2.3 From 28e2a106d16046ca792722795f809e3f80a5af80 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 13:02:23 -0300 Subject: perf hist: Simplify the insertion of new hist_entry instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And with that fix at least one bug: The first hit for an entry, the one that calls malloc to create a new instance in __perf_session__add_hist_entry, wasn't adding the count to the per cpumode (PERF_RECORD_MISC_USER, etc) total variable. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 9 +++------ tools/perf/builtin-diff.c | 14 +++----------- tools/perf/builtin-report.c | 12 ++---------- tools/perf/util/hist.c | 36 +++++++++++++++++++++++------------- tools/perf/util/hist.h | 5 +---- 5 files changed, 32 insertions(+), 44 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ee154b58772b..c7ac45a59ed5 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -72,8 +72,6 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) struct sym_priv *priv; struct sym_hist *h; - he->count++; - if (!sym || !he->ms.map) return 0; @@ -99,9 +97,8 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) } static int perf_session__add_hist_entry(struct perf_session *self, - struct addr_location *al, u64 count) + struct addr_location *al) { - bool hit; struct hist_entry *he; if (sym_hist_filter != NULL && @@ -115,7 +112,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return 0; } - he = __perf_session__add_hist_entry(&self->hists, al, NULL, count, &hit); + he = __perf_session__add_hist_entry(&self->hists, al, NULL, 1); if (he == NULL) return -ENOMEM; @@ -135,7 +132,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) return -1; } - if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) { + if (!al.filtered && perf_session__add_hist_entry(session, &al)) { pr_warning("problem incrementing symbol count, " "skipping event\n"); return -1; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 4cce68f23686..613a5c4f6d83 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -25,17 +25,9 @@ static bool show_displacement; static int perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, u64 count) { - bool hit; - struct hist_entry *he = __perf_session__add_hist_entry(&self->hists, - al, NULL, - count, &hit); - if (he == NULL) - return -ENOMEM; - - if (hit) - __perf_session__add_count(he, al, count); - - return 0; + if (__perf_session__add_hist_entry(&self->hists, al, NULL, count) != NULL) + return 0; + return -ENOMEM; } static int diff__process_sample_event(event_t *event, struct perf_session *session) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3a70c5807c04..5e2f47f88ec6 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -82,7 +82,6 @@ static int perf_session__add_hist_entry(struct perf_session *self, { struct map_symbol *syms = NULL; struct symbol *parent = NULL; - bool hit; int err = -ENOMEM; struct hist_entry *he; struct event_stat_id *stats; @@ -103,19 +102,12 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (stats == NULL) goto out_free_syms; he = __perf_session__add_hist_entry(&stats->hists, al, parent, - data->period, &hit); + data->period); if (he == NULL) goto out_free_syms; - - if (hit) - __perf_session__add_count(he, al, data->period); - err = 0; - if (symbol_conf.use_callchain) { - if (!hit) - callchain_init(he->callchain); + if (symbol_conf.use_callchain) err = append_chain(he->callchain, data->callchain, syms); - } out_free_syms: free(syms); return err; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index ad6b22dde27f..e0c8a722e11f 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -8,13 +8,10 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -void __perf_session__add_count(struct hist_entry *he, - struct addr_location *al, - u64 count) +static void perf_session__add_cpumode_count(struct hist_entry *he, + unsigned int cpumode, u64 count) { - he->count += count; - - switch (al->cpumode) { + switch (cpumode) { case PERF_RECORD_MISC_KERNEL: he->count_sys += count; break; @@ -36,10 +33,24 @@ void __perf_session__add_count(struct hist_entry *he, * histogram, sorted on item, collects counts */ +static struct hist_entry *hist_entry__new(struct hist_entry *template) +{ + size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0; + struct hist_entry *self = malloc(sizeof(*self) + callchain_size); + + if (self != NULL) { + *self = *template; + if (symbol_conf.use_callchain) + callchain_init(self->callchain); + } + + return self; +} + struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct addr_location *al, struct symbol *sym_parent, - u64 count, bool *hit) + u64 count) { struct rb_node **p = &hists->rb_node; struct rb_node *parent = NULL; @@ -64,8 +75,8 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, cmp = hist_entry__cmp(&entry, he); if (!cmp) { - *hit = true; - return he; + he->count += count; + goto out; } if (cmp < 0) @@ -74,14 +85,13 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, p = &(*p)->rb_right; } - he = malloc(sizeof(*he) + (symbol_conf.use_callchain ? - sizeof(struct callchain_node) : 0)); + he = hist_entry__new(&entry); if (!he) return NULL; - *he = entry; rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, hists); - *hit = false; +out: + perf_session__add_cpumode_count(he, al->cpumode, count); return he; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9df1c340ec92..b49013adb34b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -12,13 +12,10 @@ struct addr_location; struct symbol; struct rb_root; -void __perf_session__add_count(struct hist_entry *he, - struct addr_location *al, - u64 count); struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct addr_location *al, struct symbol *parent, - u64 count, bool *hit); + u64 count); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); int hist_entry__fprintf(struct hist_entry *self, -- cgit v1.2.3 From 232a5c948da5e23dff27e48180abf4a4238f7602 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 20:28:10 -0300 Subject: perf report: Allow limiting the number of entries to print in callchains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Works by adding a third parameter to the '-g' argument, after the graph type and minimum percentage, for example: [root@doppio linux-2.6-tip]# perf report -g fractal,0.5,2 Will show only the first two symbols where at least 0.5% of the samples took place. All the other symbols that don't fall outside these constraints will be put together in the last entry, prefixed with "[...]" and the total percentage for them. Suggested-by: Arjan van de Ven Acked-by: Arjan van de Ven Cc: Arjan van de Ven Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 5 ++++- tools/perf/util/callchain.h | 1 + tools/perf/util/hist.c | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5e2f47f88ec6..642a6d8eb5dc 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -343,7 +343,7 @@ static int parse_callchain_opt(const struct option *opt __used, const char *arg, int unset) { - char *tok; + char *tok, *tok2; char *endptr; /* @@ -388,10 +388,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, if (!tok) goto setup; + tok2 = strtok(NULL, ","); callchain_param.min_percent = strtod(tok, &endptr); if (tok == endptr) return -1; + if (tok2) + callchain_param.print_limit = strtod(tok2, &endptr); setup: if (register_callchain_param(&callchain_param) < 0) { fprintf(stderr, "Can't register callchain params\n"); diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 0f4da093cbd8..1cba1f5504e7 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -34,6 +34,7 @@ typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *, struct callchain_param { enum chain_mode mode; + u32 print_limit; double min_percent; sort_chain_func_t sort; }; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e0c8a722e11f..0f154a530dfd 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -333,6 +333,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, u64 remaining; size_t ret = 0; int i; + uint entries_printed = 0; if (callchain_param.mode == CHAIN_GRAPH_REL) new_total = self->children_hit; @@ -379,6 +380,8 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, new_depth_mask | (1 << depth), left_margin); node = next; + if (++entries_printed == callchain_param.print_limit) + break; } if (callchain_param.mode == CHAIN_GRAPH_REL && @@ -404,6 +407,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, bool printed = false; int i = 0; int ret = 0; + u32 entries_printed = 0; list_for_each_entry(chain, &self->val, list) { if (!i++ && sort__first_dimension == SORT_SYM) @@ -424,6 +428,9 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); + + if (++entries_printed == callchain_param.print_limit) + break; } ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); @@ -462,6 +469,7 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, struct rb_node *rb_node; struct callchain_node *chain; size_t ret = 0; + u32 entries_printed = 0; rb_node = rb_first(&self->sorted_chain); while (rb_node) { @@ -484,6 +492,8 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, break; } ret += fprintf(fp, "\n"); + if (++entries_printed == callchain_param.print_limit) + break; rb_node = rb_next(rb_node); } -- cgit v1.2.3 From 1c02c4d2e92f2097f1bba63ec71560b0e05a7f36 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 10 May 2010 13:04:11 -0300 Subject: perf hist: Introduce hists class and move lots of methods to it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cbbc79a we introduced support for multiple events by introducing a new "event_stat_id" struct and then made several perf_session methods receive a point to it instead of a pointer to perf_session, and kept the event_stats and hists rb_tree in perf_session. While working on the new newt based browser, I realised that it would be better to introduce a new class, "hists" (short for "histograms"), renaming the "event_stat_id" struct and the perf_session methods that were really "hists" methods, as they manipulate only struct hists members, not touching anything in the other perf_session members. Other optimizations, such as calculating the maximum lenght of a symbol name present in an hists instance will be possible as we add them, avoiding a re-traversal just for finding that information. The rationale for the name "hists" to replace "event_stat_id" is that we may have multiple sets of hists for the same event_stat id, as, for instance, the 'perf diff' tool has, so event stat id is not what characterizes what this struct and the functions that manipulate it do. Cc: Eric B Munson Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 17 ++++---- tools/perf/builtin-diff.c | 50 +++++++++++------------ tools/perf/builtin-report.c | 71 ++++++++++++++++----------------- tools/perf/builtin-trace.c | 2 +- tools/perf/util/event.c | 2 +- tools/perf/util/event.h | 14 ------- tools/perf/util/hist.c | 92 +++++++++++++++++++------------------------ tools/perf/util/hist.h | 48 ++++++++++++---------- tools/perf/util/session.c | 2 +- tools/perf/util/session.h | 12 ++++-- 10 files changed, 146 insertions(+), 164 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index c7ac45a59ed5..3940964161b3 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -96,8 +96,7 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) return 0; } -static int perf_session__add_hist_entry(struct perf_session *self, - struct addr_location *al) +static int hists__add_entry(struct hists *self, struct addr_location *al) { struct hist_entry *he; @@ -112,7 +111,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return 0; } - he = __perf_session__add_hist_entry(&self->hists, al, NULL, 1); + he = __hists__add_entry(self, al, NULL, 1); if (he == NULL) return -ENOMEM; @@ -132,7 +131,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) return -1; } - if (!al.filtered && perf_session__add_hist_entry(session, &al)) { + if (!al.filtered && hists__add_entry(&session->hists, &al)) { pr_warning("problem incrementing symbol count, " "skipping event\n"); return -1; @@ -514,11 +513,11 @@ static void annotate_sym(struct hist_entry *he) free_source_line(he, len); } -static void perf_session__find_annotations(struct perf_session *self) +static void hists__find_annotations(struct hists *self) { struct rb_node *nd; - for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); struct sym_priv *priv; @@ -570,9 +569,9 @@ static int __cmd_annotate(void) if (verbose > 2) perf_session__fprintf_dsos(session, stdout); - perf_session__collapse_resort(&session->hists); - perf_session__output_resort(&session->hists, session->event_total[0]); - perf_session__find_annotations(session); + hists__collapse_resort(&session->hists); + hists__output_resort(&session->hists); + hists__find_annotations(&session->hists); out_delete: perf_session__delete(session); diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 613a5c4f6d83..3a95a0260a5b 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -22,10 +22,10 @@ static char diff__default_sort_order[] = "dso,symbol"; static bool force; static bool show_displacement; -static int perf_session__add_hist_entry(struct perf_session *self, - struct addr_location *al, u64 count) +static int hists__add_entry(struct hists *self, + struct addr_location *al, u64 count) { - if (__perf_session__add_hist_entry(&self->hists, al, NULL, count) != NULL) + if (__hists__add_entry(self, al, NULL, count) != NULL) return 0; return -ENOMEM; } @@ -49,12 +49,12 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi event__parse_sample(event, session->sample_type, &data); - if (perf_session__add_hist_entry(session, &al, data.period)) { + if (hists__add_entry(&session->hists, &al, data.period)) { pr_warning("problem incrementing symbol count, skipping event\n"); return -1; } - session->events_stats.total += data.period; + session->hists.stats.total += data.period; return 0; } @@ -87,35 +87,34 @@ static void perf_session__insert_hist_entry_by_name(struct rb_root *root, rb_insert_color(&he->rb_node, root); } -static void perf_session__resort_hist_entries(struct perf_session *self) +static void hists__resort_entries(struct hists *self) { unsigned long position = 1; struct rb_root tmp = RB_ROOT; - struct rb_node *next = rb_first(&self->hists); + struct rb_node *next = rb_first(&self->entries); while (next != NULL) { struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, &self->hists); + rb_erase(&n->rb_node, &self->entries); n->position = position++; perf_session__insert_hist_entry_by_name(&tmp, n); } - self->hists = tmp; + self->entries = tmp; } -static void perf_session__set_hist_entries_positions(struct perf_session *self) +static void hists__set_positions(struct hists *self) { - perf_session__output_resort(&self->hists, self->events_stats.total); - perf_session__resort_hist_entries(self); + hists__output_resort(self); + hists__resort_entries(self); } -static struct hist_entry * -perf_session__find_hist_entry(struct perf_session *self, - struct hist_entry *he) +static struct hist_entry *hists__find_entry(struct hists *self, + struct hist_entry *he) { - struct rb_node *n = self->hists.rb_node; + struct rb_node *n = self->entries.rb_node; while (n) { struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); @@ -132,14 +131,13 @@ perf_session__find_hist_entry(struct perf_session *self, return NULL; } -static void perf_session__match_hists(struct perf_session *old_session, - struct perf_session *new_session) +static void hists__match(struct hists *older, struct hists *newer) { struct rb_node *nd; - for (nd = rb_first(&new_session->hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) { struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); - pos->pair = perf_session__find_hist_entry(old_session, pos); + pos->pair = hists__find_entry(older, pos); } } @@ -159,15 +157,13 @@ static int __cmd_diff(void) goto out_delete; } - perf_session__output_resort(&session[1]->hists, - session[1]->events_stats.total); + hists__output_resort(&session[1]->hists); if (show_displacement) - perf_session__set_hist_entries_positions(session[0]); + hists__set_positions(&session[0]->hists); - perf_session__match_hists(session[0], session[1]); - perf_session__fprintf_hists(&session[1]->hists, session[0], - show_displacement, stdout, - session[1]->events_stats.total); + hists__match(&session[0]->hists, &session[1]->hists); + hists__fprintf(&session[1]->hists, &session[0]->hists, + show_displacement, stdout); out_delete: for (i = 0; i < 2; ++i) perf_session__delete(session[i]); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 642a6d8eb5dc..53077fd973f0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -44,16 +44,17 @@ static char *pretty_printing_style = default_pretty_printing_style; static char callchain_default_opt[] = "fractal,0.5"; -static struct event_stat_id *get_stats(struct perf_session *self, - u64 event_stream, u32 type, u64 config) +static struct hists *perf_session__hists_findnew(struct perf_session *self, + u64 event_stream, u32 type, + u64 config) { - struct rb_node **p = &self->stats_by_id.rb_node; + struct rb_node **p = &self->hists_tree.rb_node; struct rb_node *parent = NULL; - struct event_stat_id *iter, *new; + struct hists *iter, *new; while (*p != NULL) { parent = *p; - iter = rb_entry(parent, struct event_stat_id, rb_node); + iter = rb_entry(parent, struct hists, rb_node); if (iter->config == config) return iter; @@ -64,15 +65,15 @@ static struct event_stat_id *get_stats(struct perf_session *self, p = &(*p)->rb_left; } - new = malloc(sizeof(struct event_stat_id)); + new = malloc(sizeof(struct hists)); if (new == NULL) return NULL; - memset(new, 0, sizeof(struct event_stat_id)); + memset(new, 0, sizeof(struct hists)); new->event_stream = event_stream; new->config = config; new->type = type; rb_link_node(&new->rb_node, parent, p); - rb_insert_color(&new->rb_node, &self->stats_by_id); + rb_insert_color(&new->rb_node, &self->hists_tree); return new; } @@ -84,7 +85,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, struct symbol *parent = NULL; int err = -ENOMEM; struct hist_entry *he; - struct event_stat_id *stats; + struct hists *hists; struct perf_event_attr *attr; if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { @@ -96,13 +97,12 @@ static int perf_session__add_hist_entry(struct perf_session *self, attr = perf_header__find_attr(data->id, &self->header); if (attr) - stats = get_stats(self, data->id, attr->type, attr->config); + hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config); else - stats = get_stats(self, data->id, 0, 0); - if (stats == NULL) + hists = perf_session__hists_findnew(self, data->id, 0, 0); + if (hists == NULL) goto out_free_syms; - he = __perf_session__add_hist_entry(&stats->hists, al, parent, - data->period); + he = __hists__add_entry(hists, al, parent, data->period); if (he == NULL) goto out_free_syms; err = 0; @@ -117,18 +117,19 @@ static int add_event_total(struct perf_session *session, struct sample_data *data, struct perf_event_attr *attr) { - struct event_stat_id *stats; + struct hists *hists; if (attr) - stats = get_stats(session, data->id, attr->type, attr->config); + hists = perf_session__hists_findnew(session, data->id, + attr->type, attr->config); else - stats = get_stats(session, data->id, 0, 0); + hists = perf_session__hists_findnew(session, data->id, 0, 0); - if (!stats) + if (!hists) return -ENOMEM; - stats->stats.total += data->period; - session->events_stats.total += data->period; + hists->stats.total += data->period; + session->hists.stats.total += data->period; return 0; } @@ -292,35 +293,33 @@ static int __cmd_report(void) if (verbose > 2) perf_session__fprintf_dsos(session, stdout); - next = rb_first(&session->stats_by_id); + next = rb_first(&session->hists_tree); while (next) { - struct event_stat_id *stats; + struct hists *hists; u64 nr_hists; - stats = rb_entry(next, struct event_stat_id, rb_node); - perf_session__collapse_resort(&stats->hists); - nr_hists = perf_session__output_resort(&stats->hists, - stats->stats.total); + hists = rb_entry(next, struct hists, rb_node); + hists__collapse_resort(hists); + nr_hists = hists__output_resort(hists); if (use_browser) - perf_session__browse_hists(&stats->hists, nr_hists, - stats->stats.total, help, + perf_session__browse_hists(&hists->entries, nr_hists, + hists->stats.total, help, input_name); else { - if (rb_first(&session->stats_by_id) == - rb_last(&session->stats_by_id)) + if (rb_first(&session->hists.entries) == + rb_last(&session->hists.entries)) fprintf(stdout, "# Samples: %Ld\n#\n", - stats->stats.total); + hists->stats.total); else fprintf(stdout, "# Samples: %Ld %s\n#\n", - stats->stats.total, - __event_name(stats->type, stats->config)); + hists->stats.total, + __event_name(hists->type, hists->config)); - perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, - stats->stats.total); + hists__fprintf(hists, NULL, false, stdout); fprintf(stdout, "\n\n"); } - next = rb_next(&stats->rb_node); + next = rb_next(&hists->rb_node); } if (!use_browser && sort_order == default_sort_order && diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 9c483e92e8db..6e268ca761e9 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -107,7 +107,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) data.time, thread->comm); } - session->events_stats.total += data.period; + session->hists.stats.total += data.period; return 0; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index d2ea9dd9fdf1..cce006ec8f05 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -368,7 +368,7 @@ int event__process_comm(event_t *self, struct perf_session *session) int event__process_lost(event_t *self, struct perf_session *session) { dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); - session->events_stats.lost += self->lost.lost; + session->hists.stats.lost += self->lost.lost; return 0; } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 6cc1b1dced55..48c2cc9dae4f 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -131,20 +131,6 @@ typedef union event_union { struct build_id_event build_id; } event_t; -struct events_stats { - u64 total; - u64 lost; -}; - -struct event_stat_id { - struct rb_node rb_node; - struct rb_root hists; - struct events_stats stats; - u64 config; - u64 event_stream; - u32 type; -}; - void event__print_totals(void); struct perf_session; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 0f154a530dfd..410cf56c9662 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -8,21 +8,21 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -static void perf_session__add_cpumode_count(struct hist_entry *he, - unsigned int cpumode, u64 count) +static void hist_entry__add_cpumode_count(struct hist_entry *self, + unsigned int cpumode, u64 count) { switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - he->count_sys += count; + self->count_sys += count; break; case PERF_RECORD_MISC_USER: - he->count_us += count; + self->count_us += count; break; case PERF_RECORD_MISC_GUEST_KERNEL: - he->count_guest_sys += count; + self->count_guest_sys += count; break; case PERF_RECORD_MISC_GUEST_USER: - he->count_guest_us += count; + self->count_guest_us += count; break; default: break; @@ -47,12 +47,11 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return self; } -struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, - struct addr_location *al, - struct symbol *sym_parent, - u64 count) +struct hist_entry *__hists__add_entry(struct hists *self, + struct addr_location *al, + struct symbol *sym_parent, u64 count) { - struct rb_node **p = &hists->rb_node; + struct rb_node **p = &self->entries.rb_node; struct rb_node *parent = NULL; struct hist_entry *he; struct hist_entry entry = { @@ -89,9 +88,9 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, if (!he) return NULL; rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, hists); + rb_insert_color(&he->rb_node, &self->entries); out: - perf_session__add_cpumode_count(he, al->cpumode, count); + hist_entry__add_cpumode_count(he, al->cpumode, count); return he; } @@ -167,7 +166,7 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) rb_insert_color(&he->rb_node, root); } -void perf_session__collapse_resort(struct rb_root *hists) +void hists__collapse_resort(struct hists *self) { struct rb_root tmp; struct rb_node *next; @@ -177,28 +176,28 @@ void perf_session__collapse_resort(struct rb_root *hists) return; tmp = RB_ROOT; - next = rb_first(hists); + next = rb_first(&self->entries); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, hists); + rb_erase(&n->rb_node, &self->entries); collapse__insert_entry(&tmp, n); } - *hists = tmp; + self->entries = tmp; } /* * reverse the map, sort on count. */ -static void perf_session__insert_output_hist_entry(struct rb_root *root, - struct hist_entry *he, - u64 min_callchain_hits) +static void __hists__insert_output_entry(struct rb_root *entries, + struct hist_entry *he, + u64 min_callchain_hits) { - struct rb_node **p = &root->rb_node; + struct rb_node **p = &entries->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; @@ -217,10 +216,10 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, } rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, root); + rb_insert_color(&he->rb_node, entries); } -u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) +u64 hists__output_resort(struct hists *self) { struct rb_root tmp; struct rb_node *next; @@ -228,23 +227,21 @@ u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) u64 min_callchain_hits; u64 nr_hists = 0; - min_callchain_hits = - total_samples * (callchain_param.min_percent / 100); + min_callchain_hits = self->stats.total * (callchain_param.min_percent / 100); tmp = RB_ROOT; - next = rb_first(hists); + next = rb_first(&self->entries); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, hists); - perf_session__insert_output_hist_entry(&tmp, n, - min_callchain_hits); + rb_erase(&n->rb_node, &self->entries); + __hists__insert_output_entry(&tmp, n, min_callchain_hits); ++nr_hists; } - *hists = tmp; + self->entries = tmp; return nr_hists; } @@ -500,12 +497,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -int hist_entry__snprintf(struct hist_entry *self, - char *s, size_t size, - struct perf_session *pair_session, - bool show_displacement, - long displacement, bool color, - u64 session_total) +int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, + struct hists *pair_hists, bool show_displacement, + long displacement, bool color, u64 session_total) { struct sort_entry *se; u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; @@ -515,9 +509,9 @@ int hist_entry__snprintf(struct hist_entry *self, if (symbol_conf.exclude_other && !self->parent) return 0; - if (pair_session) { + if (pair_hists) { count = self->pair ? self->pair->count : 0; - total = pair_session->events_stats.total; + total = pair_hists->stats.total; count_sys = self->pair ? self->pair->count_sys : 0; count_us = self->pair ? self->pair->count_us : 0; count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; @@ -569,7 +563,7 @@ int hist_entry__snprintf(struct hist_entry *self, ret += snprintf(s + ret, size - ret, "%11lld", count); } - if (pair_session) { + if (pair_hists) { char bf[32]; double old_percent = 0, new_percent = 0, diff; @@ -615,14 +609,12 @@ int hist_entry__snprintf(struct hist_entry *self, return ret; } -int hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, +int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, + bool show_displacement, long displacement, FILE *fp, u64 session_total) { char bf[512]; - hist_entry__snprintf(self, bf, sizeof(bf), pair_session, + hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, show_displacement, displacement, true, session_total); return fprintf(fp, "%s\n", bf); @@ -644,10 +636,8 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, left_margin); } -size_t perf_session__fprintf_hists(struct rb_root *hists, - struct perf_session *pair, - bool show_displacement, FILE *fp, - u64 session_total) +size_t hists__fprintf(struct hists *self, struct hists *pair, + bool show_displacement, FILE *fp) { struct sort_entry *se; struct rb_node *nd; @@ -753,7 +743,7 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, fprintf(fp, "\n#\n"); print_entries: - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (show_displacement) { @@ -765,10 +755,10 @@ print_entries: ++position; } ret += hist_entry__fprintf(h, pair, show_displacement, - displacement, fp, session_total); + displacement, fp, self->stats.total); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, session_total); + ret += hist_entry__fprintf_callchain(h, fp, self->stats.total); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index b49013adb34b..bdde81eca69f 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -6,34 +6,40 @@ extern struct callchain_param callchain_param; -struct perf_session; struct hist_entry; struct addr_location; struct symbol; struct rb_root; -struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, - struct addr_location *al, - struct symbol *parent, - u64 count); +struct events_stats { + u64 total; + u64 lost; +}; + +struct hists { + struct rb_node rb_node; + struct rb_root entries; + struct events_stats stats; + u64 config; + u64 event_stream; + u32 type; +}; + +struct hist_entry *__hists__add_entry(struct hists *self, + struct addr_location *al, + struct symbol *parent, u64 count); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -int hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, - u64 session_total); -int hist_entry__snprintf(struct hist_entry *self, - char *bf, size_t size, - struct perf_session *pair_session, - bool show_displacement, long displacement, - bool color, u64 session_total); +int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, + bool show_displacement, long displacement, FILE *fp, + u64 total); +int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, + struct hists *pair_hists, bool show_displacement, + long displacement, bool color, u64 total); void hist_entry__free(struct hist_entry *); -u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); -void perf_session__collapse_resort(struct rb_root *hists); -size_t perf_session__fprintf_hists(struct rb_root *hists, - struct perf_session *pair, - bool show_displacement, FILE *fp, - u64 session_total); +u64 hists__output_resort(struct hists *self); +void hists__collapse_resort(struct hists *self); +size_t hists__fprintf(struct hists *self, struct hists *pair, + bool show_displacement, FILE *fp); #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4130036a0109..72a7f6ae0293 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -89,7 +89,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc memcpy(self->filename, filename, len); self->threads = RB_ROOT; - self->stats_by_id = RB_ROOT; + self->hists_tree = RB_ROOT; self->last_match = NULL; self->mmap_window = 32; self->cwd = NULL; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 242d528bfae2..46190f94b547 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -1,6 +1,7 @@ #ifndef __PERF_SESSION_H #define __PERF_SESSION_H +#include "hist.h" #include "event.h" #include "header.h" #include "symbol.h" @@ -28,11 +29,16 @@ struct perf_session { struct thread *last_match; struct machine host_machine; struct rb_root machines; - struct events_stats events_stats; - struct rb_root stats_by_id; + struct rb_root hists_tree; unsigned long event_total[PERF_RECORD_MAX]; unsigned long unknown_events; - struct rb_root hists; + /* + * FIXME: should point to the first entry in hists_tree and + * be a hists instance. Right now its only 'report' + * that is using ->hists_tree while all the rest use + * ->hists. + */ + struct hists hists; u64 sample_type; int fd; bool fd_pipe; -- cgit v1.2.3 From fefb0b94bbab858be0909a7eb5ef357e0f996a79 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 10 May 2010 13:57:51 -0300 Subject: perf hist: Calculate max_sym name len and nr_entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Better done when we are adding entries, be it initially of when we're re-sorting the histograms. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 6 +++--- tools/perf/util/hist.c | 27 ++++++++++++++++++++------- tools/perf/util/hist.h | 4 +++- tools/perf/util/symbol.c | 5 +++-- tools/perf/util/symbol.h | 1 + 5 files changed, 30 insertions(+), 13 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 53077fd973f0..d7c75291e788 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -296,13 +296,13 @@ static int __cmd_report(void) next = rb_first(&session->hists_tree); while (next) { struct hists *hists; - u64 nr_hists; hists = rb_entry(next, struct hists, rb_node); hists__collapse_resort(hists); - nr_hists = hists__output_resort(hists); + hists__output_resort(hists); if (use_browser) - perf_session__browse_hists(&hists->entries, nr_hists, + perf_session__browse_hists(&hists->entries, + hists->nr_entries, hists->stats.total, help, input_name); else { diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 410cf56c9662..e34fd248067d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -47,6 +47,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return self; } +static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) +{ + if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) + self->max_sym_namelen = entry->ms.sym->namelen; + ++self->nr_entries; +} + struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, struct symbol *sym_parent, u64 count) @@ -89,6 +96,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, return NULL; rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, &self->entries); + hists__inc_nr_entries(self, he); out: hist_entry__add_cpumode_count(he, al->cpumode, count); return he; @@ -137,7 +145,7 @@ void hist_entry__free(struct hist_entry *he) * collapse the histogram */ -static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) +static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -153,7 +161,7 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) if (!cmp) { iter->count += he->count; hist_entry__free(he); - return; + return false; } if (cmp < 0) @@ -164,6 +172,7 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root); + return true; } void hists__collapse_resort(struct hists *self) @@ -177,13 +186,16 @@ void hists__collapse_resort(struct hists *self) tmp = RB_ROOT; next = rb_first(&self->entries); + self->nr_entries = 0; + self->max_sym_namelen = 0; while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); rb_erase(&n->rb_node, &self->entries); - collapse__insert_entry(&tmp, n); + if (collapse__insert_entry(&tmp, n)) + hists__inc_nr_entries(self, n); } self->entries = tmp; @@ -219,30 +231,31 @@ static void __hists__insert_output_entry(struct rb_root *entries, rb_insert_color(&he->rb_node, entries); } -u64 hists__output_resort(struct hists *self) +void hists__output_resort(struct hists *self) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; - u64 nr_hists = 0; min_callchain_hits = self->stats.total * (callchain_param.min_percent / 100); tmp = RB_ROOT; next = rb_first(&self->entries); + self->nr_entries = 0; + self->max_sym_namelen = 0; + while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); rb_erase(&n->rb_node, &self->entries); __hists__insert_output_entry(&tmp, n, min_callchain_hits); - ++nr_hists; + hists__inc_nr_entries(self, n); } self->entries = tmp; - return nr_hists; } static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index bdde81eca69f..1b18d04195dc 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -19,10 +19,12 @@ struct events_stats { struct hists { struct rb_node rb_node; struct rb_root entries; + u64 nr_entries; struct events_stats stats; u64 config; u64 event_stream; u32 type; + u32 max_sym_namelen; }; struct hist_entry *__hists__add_entry(struct hists *self, @@ -38,7 +40,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, long displacement, bool color, u64 total); void hist_entry__free(struct hist_entry *); -u64 hists__output_resort(struct hists *self); +void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 994efdb531e4..ecccc8df128e 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -130,8 +130,9 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) if (symbol_conf.priv_size) self = ((void *)self) + symbol_conf.priv_size; - self->start = start; - self->end = len ? start + len - 1 : start; + self->start = start; + self->end = len ? start + len - 1 : start; + self->namelen = namelen - 1; pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index edff866d76b2..6389d1acaf81 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -54,6 +54,7 @@ struct symbol { struct rb_node rb_node; u64 start; u64 end; + u16 namelen; char name[0]; }; -- cgit v1.2.3 From b09e0190acf88c7fe3b05e3c331e1b2ef5310896 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 May 2010 11:10:15 -0300 Subject: perf hist: Adopt filter by dso and by thread methods from the newt browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those are really not specific to the newt code, can be used by other UI frontends. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 5 +-- tools/perf/util/hist.c | 59 +++++++++++++++++++++++++++++++++ tools/perf/util/hist.h | 15 +++++++++ tools/perf/util/newt.c | 80 ++++++++------------------------------------- tools/perf/util/session.h | 15 --------- 5 files changed, 89 insertions(+), 85 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d7c75291e788..3d67d6bf22cf 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -301,10 +301,7 @@ static int __cmd_report(void) hists__collapse_resort(hists); hists__output_resort(hists); if (use_browser) - perf_session__browse_hists(&hists->entries, - hists->nr_entries, - hists->stats.total, help, - input_name); + hists__browse(hists, help, input_name); else { if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e34fd248067d..baa55be64d9e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -784,3 +784,62 @@ print_entries: return ret; } + +enum hist_filter { + HIST_FILTER__DSO, + HIST_FILTER__THREAD, +}; + +void hists__filter_by_dso(struct hists *self, const struct dso *dso) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (symbol_conf.exclude_other && !h->parent) + continue; + + if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { + h->filtered |= (1 << HIST_FILTER__DSO); + continue; + } + + h->filtered &= ~(1 << HIST_FILTER__DSO); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total += h->count; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} + +void hists__filter_by_thread(struct hists *self, const struct thread *thread) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (thread != NULL && h->thread != thread) { + h->filtered |= (1 << HIST_FILTER__THREAD); + continue; + } + h->filtered &= ~(1 << HIST_FILTER__THREAD); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total += h->count; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1b18d04195dc..1c5f93ac5ab7 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -44,4 +44,19 @@ void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); + +void hists__filter_by_dso(struct hists *self, const struct dso *dso); +void hists__filter_by_thread(struct hists *self, const struct thread *thread); + +#ifdef NO_NEWT_SUPPORT +static inline int hists__browse(struct hists self __used, + const char *helpline __used, + const char *input_name __used) +{ + return 0; +} +#else +int hists__browse(struct hists *self, const char *helpline, + const char *input_name); +#endif #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e283a6e6b6e0..638b519e72b8 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -410,8 +410,8 @@ static void hist_browser__delete(struct hist_browser *self) free(self); } -static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, - u64 nr_hists, u64 session_total, const char *title) +static int hist_browser__populate(struct hist_browser *self, struct hists *hists, + const char *title) { int max_len = 0, idx, cols, rows; struct ui_progress *progress; @@ -426,7 +426,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his } snprintf(str, sizeof(str), "Samples: %Ld ", - session_total); + hists->stats.total); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); @@ -442,24 +442,25 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtComponentAddCallback(self->tree, hist_browser__selection, &self->selection); - progress = ui_progress__new("Adding entries to the browser...", nr_hists); + progress = ui_progress__new("Adding entries to the browser...", + hists->nr_entries); if (progress == NULL) return -1; idx = 0; - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); int len; if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, session_total); + len = hist_entry__append_browser(h, self->tree, hists->stats.total); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, self->tree, - session_total, idx++); + hists->stats.total, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); @@ -490,57 +491,6 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } -enum hist_filter { - HIST_FILTER__DSO, - HIST_FILTER__THREAD, -}; - -static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { - h->filtered |= (1 << HIST_FILTER__DSO); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__DSO); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (thread != NULL && h->thread != thread) { - h->filtered |= (1 << HIST_FILTER__THREAD); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__THREAD); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - static struct thread *hist_browser__selected_thread(struct hist_browser *self) { int *indexes; @@ -577,9 +527,7 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name, return printed ?: snprintf(bf, size, "Report: %s", input_name); } -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name) +int hists__browse(struct hists *self, const char *helpline, const char *input_name) { struct hist_browser *browser = hist_browser__new(); const struct thread *thread_filter = NULL; @@ -595,7 +543,7 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; while (1) { @@ -672,10 +620,10 @@ do_annotate: newtPushHelpLine(msg); dso_filter = dso; } - nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); + hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } else if (choice == zoom_thread) { if (thread_filter) { @@ -689,10 +637,10 @@ do_annotate: newtPushHelpLine(msg); thread_filter = thread; } - nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); + hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 46190f94b547..ce00fa6cdeda 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -102,21 +102,6 @@ int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); -#ifdef NO_NEWT_SUPPORT -static inline int perf_session__browse_hists(struct rb_root *hists __used, - u64 nr_hists __used, - u64 session_total __used, - const char *helpline __used, - const char *input_name __used) -{ - return 0; -} -#else -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name); -#endif - static inline struct machine *perf_session__find_host_machine(struct perf_session *self) { -- cgit v1.2.3 From ef7b93a11904c6ba10604233d318d9e8ec88cddc Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 May 2010 23:18:06 -0300 Subject: perf report: Librarize the annotation code and use it in the newt browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we don't anymore use popen to run 'perf annotate' for the selected symbol, instead we collect per address samplings when processing samples in 'perf report' if we're using the newt browser, then we use this data directly to do annotation. Done this way we can actually traverse the objdump_line objects directly, matching the addresses to the collected samples and colouring them appropriately using lower level slang routines. The new ui_browser class will be reused for the main, callchain aware, histogram browser, when it will be made generic and don't assume that the objects are always instances of the objdump_line class maintained using list_heads. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 5 + tools/perf/builtin-annotate.c | 198 +----------------------- tools/perf/builtin-report.c | 19 ++- tools/perf/util/hist.c | 184 ++++++++++++++++++++++ tools/perf/util/hist.h | 29 ++++ tools/perf/util/newt.c | 352 ++++++++++++++++++++++++++++++++++++------ tools/perf/util/sort.h | 6 - 7 files changed, 544 insertions(+), 249 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0797786aa72a..9c4dc30cdc13 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -560,6 +560,8 @@ ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtIni msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); BASIC_CFLAGS += -DNO_NEWT_SUPPORT else + # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h + BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt LIB_OBJS += $(OUTPUT)util/newt.o endif @@ -948,6 +950,9 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 3940964161b3..fd1b786c8f35 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -34,68 +34,8 @@ static bool full_paths; static bool print_line; -struct sym_hist { - u64 sum; - u64 ip[0]; -}; - -struct sym_ext { - struct rb_node node; - double percent; - char *path; -}; - -struct sym_priv { - struct sym_hist *hist; - struct sym_ext *ext; -}; - static const char *sym_hist_filter; -static int sym__alloc_hist(struct symbol *self) -{ - struct sym_priv *priv = symbol__priv(self); - const int size = (sizeof(*priv->hist) + - (self->end - self->start) * sizeof(u64)); - - priv->hist = zalloc(size); - return priv->hist == NULL ? -1 : 0; -} - -/* - * collect histogram counts - */ -static int annotate__hist_hit(struct hist_entry *he, u64 ip) -{ - unsigned int sym_size, offset; - struct symbol *sym = he->ms.sym; - struct sym_priv *priv; - struct sym_hist *h; - - if (!sym || !he->ms.map) - return 0; - - priv = symbol__priv(sym); - if (priv->hist == NULL && sym__alloc_hist(sym) < 0) - return -ENOMEM; - - sym_size = sym->end - sym->start; - offset = ip - sym->start; - - pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); - - if (offset >= sym_size) - return 0; - - h = priv->hist; - h->sum++; - h->ip[offset]++; - - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, - he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); - return 0; -} - static int hists__add_entry(struct hists *self, struct addr_location *al) { struct hist_entry *he; @@ -115,7 +55,7 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) if (he == NULL) return -ENOMEM; - return annotate__hist_hit(he, al->addr); + return hist_entry__inc_addr_samples(he, al->addr); } static int process_sample_event(event_t *event, struct perf_session *session) @@ -140,101 +80,6 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } -struct objdump_line { - struct list_head node; - s64 offset; - char *line; -}; - -static struct objdump_line *objdump_line__new(s64 offset, char *line) -{ - struct objdump_line *self = malloc(sizeof(*self)); - - if (self != NULL) { - self->offset = offset; - self->line = line; - } - - return self; -} - -static void objdump_line__free(struct objdump_line *self) -{ - free(self->line); - free(self); -} - -static void objdump__add_line(struct list_head *head, struct objdump_line *line) -{ - list_add_tail(&line->node, head); -} - -static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos) -{ - list_for_each_entry_continue(pos, head, node) - if (pos->offset >= 0) - return pos; - - return NULL; -} - -static int parse_line(FILE *file, struct hist_entry *he, - struct list_head *head) -{ - struct symbol *sym = he->ms.sym; - struct objdump_line *objdump_line; - char *line = NULL, *tmp, *tmp2; - size_t line_len; - s64 line_ip, offset = -1; - char *c; - - if (getline(&line, &line_len, file) < 0) - return -1; - - if (!line) - return -1; - - c = strchr(line, '\n'); - if (c) - *c = 0; - - line_ip = -1; - - /* - * Strip leading spaces: - */ - tmp = line; - while (*tmp) { - if (*tmp != ' ') - break; - tmp++; - } - - if (*tmp) { - /* - * Parse hexa addresses followed by ':' - */ - line_ip = strtoull(tmp, &tmp2, 16); - if (*tmp2 != ':') - line_ip = -1; - } - - if (line_ip != -1) { - u64 start = map__rip_2objdump(he->ms.map, sym->start); - offset = line_ip - start; - } - - objdump_line = objdump_line__new(offset, line); - if (objdump_line == NULL) { - free(line); - return -1; - } - objdump__add_line(head, objdump_line); - - return 0; -} - static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) @@ -439,27 +284,11 @@ static void annotate_sym(struct hist_entry *he) struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; - char command[PATH_MAX*2]; - FILE *file; LIST_HEAD(head); struct objdump_line *pos, *n; - if (!filename) - return; - - if (dso->origin == DSO__ORIG_KERNEL) { - if (dso->annotate_warned) - return; - dso->annotate_warned = 1; - pr_err("Can't annotate %s: No vmlinux file was found in the " - "path:\n", sym->name); - vmlinux_path__fprintf(stderr); + if (hist_entry__annotate(he, &head) < 0) return; - } - - pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, - filename, sym->name, map->unmap_ip(map, sym->start), - map->unmap_ip(map, sym->end)); if (full_paths) d_filename = filename; @@ -477,29 +306,6 @@ static void annotate_sym(struct hist_entry *he) printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); - if (verbose >= 2) - printf("annotating [%p] %30s : [%p] %30s\n", - dso, dso->long_name, sym, sym->name); - - sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", - map__rip_2objdump(map, sym->start), - map__rip_2objdump(map, sym->end), - filename, filename); - - if (verbose >= 3) - printf("doing: %s\n", command); - - file = popen(command, "r"); - if (!file) - return; - - while (!feof(file)) { - if (parse_line(file, he, &head) < 0) - break; - } - - pclose(file); - if (verbose) hist_entry__print_hits(he); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3d67d6bf22cf..04de3387de3f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -106,8 +106,18 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (he == NULL) goto out_free_syms; err = 0; - if (symbol_conf.use_callchain) + if (symbol_conf.use_callchain) { err = append_chain(he->callchain, data->callchain, syms); + if (err) + goto out_free_syms; + } + /* + * Only in the newt browser we are doing integrated annotation, + * so we don't allocated the extra space needed because the stdio + * code will not use it. + */ + if (use_browser) + err = hist_entry__inc_addr_samples(he, al->addr); out_free_syms: free(syms); return err; @@ -458,6 +468,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) if (strcmp(input_name, "-") != 0) setup_browser(); + /* + * Only in the newt browser we are doing integrated annotation, + * so don't allocate extra space that won't be used in the stdio + * implementation. + */ + if (use_browser) + symbol_conf.priv_size = sizeof(struct sym_priv); if (symbol__init() < 0) return -1; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index baa55be64d9e..451d2e45d843 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -843,3 +843,187 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) } } } + +static int symbol__alloc_hist(struct symbol *self) +{ + struct sym_priv *priv = symbol__priv(self); + const int size = (sizeof(*priv->hist) + + (self->end - self->start) * sizeof(u64)); + + priv->hist = zalloc(size); + return priv->hist == NULL ? -1 : 0; +} + +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) +{ + unsigned int sym_size, offset; + struct symbol *sym = self->ms.sym; + struct sym_priv *priv; + struct sym_hist *h; + + if (!sym || !self->ms.map) + return 0; + + priv = symbol__priv(sym); + if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) + return -ENOMEM; + + sym_size = sym->end - sym->start; + offset = ip - sym->start; + + pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); + + if (offset >= sym_size) + return 0; + + h = priv->hist; + h->sum++; + h->ip[offset]++; + + pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, + self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); + return 0; +} + +static struct objdump_line *objdump_line__new(s64 offset, char *line) +{ + struct objdump_line *self = malloc(sizeof(*self)); + + if (self != NULL) { + self->offset = offset; + self->line = line; + } + + return self; +} + +void objdump_line__free(struct objdump_line *self) +{ + free(self->line); + free(self); +} + +static void objdump__add_line(struct list_head *head, struct objdump_line *line) +{ + list_add_tail(&line->node, head); +} + +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos) +{ + list_for_each_entry_continue(pos, head, node) + if (pos->offset >= 0) + return pos; + + return NULL; +} + +static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, + struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct objdump_line *objdump_line; + char *line = NULL, *tmp, *tmp2, *c; + size_t line_len; + s64 line_ip, offset = -1; + + if (getline(&line, &line_len, file) < 0) + return -1; + + if (!line) + return -1; + + while (line_len != 0 && isspace(line[line_len - 1])) + line[--line_len] = '\0'; + + c = strchr(line, '\n'); + if (c) + *c = 0; + + line_ip = -1; + + /* + * Strip leading spaces: + */ + tmp = line; + while (*tmp) { + if (*tmp != ' ') + break; + tmp++; + } + + if (*tmp) { + /* + * Parse hexa addresses followed by ':' + */ + line_ip = strtoull(tmp, &tmp2, 16); + if (*tmp2 != ':') + line_ip = -1; + } + + if (line_ip != -1) { + u64 start = map__rip_2objdump(self->ms.map, sym->start); + offset = line_ip - start; + } + + objdump_line = objdump_line__new(offset, line); + if (objdump_line == NULL) { + free(line); + return -1; + } + objdump__add_line(head, objdump_line); + + return 0; +} + +int hist_entry__annotate(struct hist_entry *self, struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct map *map = self->ms.map; + struct dso *dso = map->dso; + const char *filename = dso->long_name; + char command[PATH_MAX * 2]; + FILE *file; + u64 len; + + if (!filename) + return -1; + + if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->annotate_warned) + return 0; + dso->annotate_warned = 1; + pr_err("Can't annotate %s: No vmlinux file was found in the " + "path:\n", sym->name); + vmlinux_path__fprintf(stderr); + return -1; + } + + pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, + filename, sym->name, map->unmap_ip(map, sym->start), + map->unmap_ip(map, sym->end)); + + len = sym->end - sym->start; + + pr_debug("annotating [%p] %30s : [%p] %30s\n", + dso, dso->long_name, sym, sym->name); + + snprintf(command, sizeof(command), + "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", + map__rip_2objdump(map, sym->start), + map__rip_2objdump(map, sym->end), + filename, filename); + + pr_debug("Executing: %s\n", command); + + file = popen(command, "r"); + if (!file) + return -1; + + while (!feof(file)) + if (hist_entry__parse_objdump_line(self, file, head) < 0) + break; + + pclose(file); + return 0; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1c5f93ac5ab7..ed9c06734965 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -11,6 +11,32 @@ struct addr_location; struct symbol; struct rb_root; +struct objdump_line { + struct list_head node; + s64 offset; + char *line; +}; + +void objdump_line__free(struct objdump_line *self); +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos); + +struct sym_hist { + u64 sum; + u64 ip[0]; +}; + +struct sym_ext { + struct rb_node node; + double percent; + char *path; +}; + +struct sym_priv { + struct sym_hist *hist; + struct sym_ext *ext; +}; + struct events_stats { u64 total; u64 lost; @@ -45,6 +71,9 @@ void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); +int hist_entry__annotate(struct hist_entry *self, struct list_head *head); + void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index daa86efffce6..ba6acd04c082 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -2,6 +2,7 @@ #include #undef _GNU_SOURCE +#include #include #include #include @@ -171,6 +172,254 @@ static bool dialog_yesno(const char *msg) return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } +#define HE_COLORSET_TOP 50 +#define HE_COLORSET_MEDIUM 51 +#define HE_COLORSET_NORMAL 52 +#define HE_COLORSET_SELECTED 53 +#define HE_COLORSET_CODE 54 + +static int ui_browser__percent_color(double percent, bool current) +{ + if (current) + return HE_COLORSET_SELECTED; + if (percent >= MIN_RED) + return HE_COLORSET_TOP; + if (percent >= MIN_GREEN) + return HE_COLORSET_MEDIUM; + return HE_COLORSET_NORMAL; +} + +struct ui_browser { + newtComponent form, sb; + u64 index, first_visible_entry_idx; + void *first_visible_entry, *entries; + u16 top, left, width, height; + void *priv; + u32 nr_entries; +}; + +static void ui_browser__refresh_dimensions(struct ui_browser *self) +{ + int cols, rows; + newtGetScreenSize(&cols, &rows); + + if (self->width > cols - 4) + self->width = cols - 4; + self->height = rows - 5; + if (self->height > self->nr_entries) + self->height = self->nr_entries; + self->top = (rows - self->height) / 2; + self->left = (cols - self->width) / 2; +} + +static void ui_browser__reset_index(struct ui_browser *self) +{ + self->index = self->first_visible_entry_idx = 0; + self->first_visible_entry = NULL; +} + +static int objdump_line__show(struct objdump_line *self, struct list_head *head, + int width, struct hist_entry *he, int len, + bool current_entry) +{ + if (self->offset != -1) { + struct symbol *sym = he->ms.sym; + unsigned int hits = 0; + double percent = 0.0; + int color; + struct sym_priv *priv = symbol__priv(sym); + struct sym_ext *sym_ext = priv->ext; + struct sym_hist *h = priv->hist; + s64 offset = self->offset; + struct objdump_line *next = objdump__get_next_ip_line(head, self); + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (sym_ext) { + percent += sym_ext[offset].percent; + } else + hits += h->ip[offset]; + + ++offset; + } + + if (sym_ext == NULL && h->sum) + percent = 100.0 * hits / h->sum; + + color = ui_browser__percent_color(percent, current_entry); + SLsmg_set_color(color); + SLsmg_printf(" %7.2f ", percent); + if (!current_entry) + SLsmg_set_color(HE_COLORSET_CODE); + } else { + int color = ui_browser__percent_color(0, current_entry); + SLsmg_set_color(color); + SLsmg_write_nstring(" ", 9); + } + + SLsmg_write_char(':'); + SLsmg_write_nstring(" ", 8); + if (!*self->line) + SLsmg_write_nstring(" ", width - 18); + else + SLsmg_write_nstring(self->line, width - 18); + + return 0; +} + +static int ui_browser__refresh_entries(struct ui_browser *self) +{ + struct objdump_line *pos; + struct list_head *head = self->entries; + struct hist_entry *he = self->priv; + int row = 0; + int len = he->ms.sym->end - he->ms.sym->start; + + if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = list_entry(self->first_visible_entry, struct objdump_line, node); + + list_for_each_entry_from(pos, head, node) { + bool current_entry = (self->first_visible_entry_idx + row) == self->index; + SLsmg_gotorc(self->top + row, self->left); + objdump_line__show(pos, head, self->width, + he, len, current_entry); + if (++row == self->height) + break; + } + + SLsmg_set_color(HE_COLORSET_NORMAL); + SLsmg_fill_region(self->top + row, self->left, + self->height - row, self->width, ' '); + + return 0; +} + +static int ui_browser__run(struct ui_browser *self, const char *title, + struct newtExitStruct *es) +{ + if (self->form) { + newtFormDestroy(self->form); + newtPopWindow(); + } + + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width + 2, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + + if (ui_browser__refresh_entries(self) < 0) + return -1; + newtFormAddComponent(self->form, self->sb); + + while (1) { + unsigned int offset; + + newtFormRun(self->form, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case NEWT_KEY_DOWN: + if (self->index == self->nr_entries - 1) + break; + ++self->index; + if (self->index == self->first_visible_entry_idx + self->height) { + struct list_head *pos = self->first_visible_entry; + ++self->first_visible_entry_idx; + self->first_visible_entry = pos->next; + } + break; + case NEWT_KEY_UP: + if (self->index == 0) + break; + --self->index; + if (self->index < self->first_visible_entry_idx) { + struct list_head *pos = self->first_visible_entry; + --self->first_visible_entry_idx; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_PGDN: + if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + break; + + offset = self->height; + if (self->index + offset > self->nr_entries - 1) + offset = self->nr_entries - 1 - self->index; + self->index += offset; + self->first_visible_entry_idx += offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->next; + } + + break; + case NEWT_KEY_PGUP: + if (self->first_visible_entry_idx == 0) + break; + + if (self->first_visible_entry_idx < self->height) + offset = self->first_visible_entry_idx; + else + offset = self->height; + + self->index -= offset; + self->first_visible_entry_idx -= offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_HOME: + ui_browser__reset_index(self); + break; + case NEWT_KEY_END: { + struct list_head *head = self->entries; + offset = self->height - 1; + + if (offset > self->nr_entries) + offset = self->nr_entries; + + self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; + self->first_visible_entry = head->prev; + while (offset-- != 0) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + } + break; + case NEWT_KEY_ESCAPE: + case CTRL('c'): + case 'Q': + case 'q': + return 0; + default: + continue; + } + if (ui_browser__refresh_entries(self) < 0) + return -1; + } + return 0; +} + /* * When debugging newt problems it was useful to be able to "unroll" * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate @@ -353,62 +602,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self, return ret; } -static void map_symbol__annotate_browser(const struct map_symbol *self, - const char *input_name) +static void hist_entry__annotate_browser(struct hist_entry *self) { - FILE *fp; - int cols, rows; - newtComponent form, tree; + struct ui_browser browser; struct newtExitStruct es; - char *str; - size_t line_len, max_line_len = 0; - size_t max_usable_width; - char *line = NULL; + struct objdump_line *pos, *n; + LIST_HEAD(head); - if (self->sym == NULL) + if (self->ms.sym == NULL) return; - if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", - input_name, self->map->dso->name, self->sym->name) < 0) + if (hist_entry__annotate(self, &head) < 0) return; - fp = popen(str, "r"); - if (fp == NULL) - goto out_free_str; - ui_helpline__push("Press ESC to exit"); - newtGetScreenSize(&cols, &rows); - tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); - - while (!feof(fp)) { - if (getline(&line, &line_len, fp) < 0 || !line_len) - break; - while (line_len != 0 && isspace(line[line_len - 1])) - line[--line_len] = '\0'; - if (line_len > max_line_len) - max_line_len = line_len; - newtListboxAppendEntry(tree, line, NULL); + memset(&browser, 0, sizeof(browser)); + browser.entries = &head; + browser.priv = self; + list_for_each_entry(pos, &head, node) { + size_t line_len = strlen(pos->line); + if (browser.width < line_len) + browser.width = line_len; + ++browser.nr_entries; } - fclose(fp); - free(line); - - max_usable_width = cols - 22; - if (max_line_len > max_usable_width) - max_line_len = max_usable_width; - - newtListboxSetWidth(tree, max_line_len); - newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); - form = newt_form__new(); - newtFormAddComponent(form, tree); - - newtFormRun(form, &es); - newtFormDestroy(form); + browser.width += 18; /* Percentage */ + ui_browser__run(&browser, self->ms.sym->name, &es); + newtFormDestroy(browser.form); newtPopWindow(); + list_for_each_entry_safe(pos, n, &head, node) { + list_del(&pos->node); + objdump_line__free(pos); + } ui_helpline__pop(); -out_free_str: - free(str); } static const void *newt__symbol_tree_get_current(newtComponent self) @@ -527,7 +754,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists return 0; } -static struct thread *hist_browser__selected_thread(struct hist_browser *self) +static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) { int *indexes; @@ -543,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) } return NULL; out: - return *(struct thread **)(self->selection + 1); + return container_of(self->selection, struct hist_entry, ms); +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + struct hist_entry *he = hist_browser__selected_entry(self); + return he ? he->thread : NULL; } static int hist_browser__title(char *bf, size_t size, const char *input_name, @@ -637,13 +870,20 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na continue; do_annotate: if (choice == annotate) { + struct hist_entry *he; + if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection, input_name); + + he = hist_browser__selected_entry(browser); + if (he == NULL) + continue; + + hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { if (dso_filter) { ui_helpline__pop(); @@ -681,8 +921,23 @@ out: return err; } +static struct newtPercentTreeColors { + const char *topColorFg, *topColorBg; + const char *mediumColorFg, *mediumColorBg; + const char *normalColorFg, *normalColorBg; + const char *selColorFg, *selColorBg; + const char *codeColorFg, *codeColorBg; +} defaultPercentTreeColors = { + "red", "lightgray", + "green", "lightgray", + "black", "lightgray", + "lightgray", "magenta", + "blue", "lightgray", +}; + void setup_browser(void) { + struct newtPercentTreeColors *c = &defaultPercentTreeColors; if (!isatty(1)) return; @@ -690,6 +945,11 @@ void setup_browser(void) newtInit(); newtCls(); ui_helpline__puts(" "); + SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); + SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); + SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); + SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); + SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); } void exit_browser(bool wait_for_ok) diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b7c54eeed9c9..af301acc461c 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -48,12 +48,6 @@ struct hist_entry { u64 count_us; u64 count_guest_sys; u64 count_guest_us; - - /* - * XXX WARNING! - * thread _has_ to come after ms, see - * hist_browser__selected_thread in util/newt.c - */ struct map_symbol ms; struct thread *thread; u64 ip; -- cgit v1.2.3 From 8a0ecfb8b47dc765fdf460913231876bbc95385e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 13 May 2010 19:47:16 +0200 Subject: perf hist: Fix missing getline declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hist.c needs to include util.h so that it gets stdio.h inclusion with __GNU_SOURCE defined. Fixes: util/hist.c: In function ‘hist_entry__parse_objdump_line’: util/hist.c:931: erreur: implicit declaration of function ‘getline’ util/hist.c:931: erreur: nested extern declaration of ‘getline’ Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras LKML-Reference: <1273772836-11533-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 451d2e45d843..5dc4f8429eda 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,3 +1,4 @@ +#include "util.h" #include "hist.h" #include "session.h" #include "sort.h" -- cgit v1.2.3 From c8446b9bdabcb0caa61bb341bd73c58f7104b503 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 10:36:42 -0300 Subject: perf hist: Make event__totals per hists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is one more thing that started global but are more useful per hist or per session. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-report.c | 8 +++++++- tools/perf/util/event.c | 17 +++++++++++++++++ tools/perf/util/event.h | 2 ++ tools/perf/util/hist.c | 21 +++++++++++++++++++++ tools/perf/util/hist.h | 6 ++++++ tools/perf/util/session.c | 36 ++---------------------------------- tools/perf/util/session.h | 8 ++++++-- 8 files changed, 62 insertions(+), 38 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index fd1b786c8f35..77bcc9b130f5 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -365,7 +365,7 @@ static int __cmd_annotate(void) goto out_delete; if (dump_trace) { - event__print_totals(); + perf_session__fprintf_nr_events(session, stdout); goto out_delete; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 04de3387de3f..f13cda1ef059 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -139,6 +139,12 @@ static int add_event_total(struct perf_session *session, return -ENOMEM; hists->stats.total += data->period; + /* + * FIXME: add_event_total should be moved from here to + * perf_session__process_event so that the proper hist is passed to + * the event_op methods. + */ + hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); session->hists.stats.total += data->period; return 0; } @@ -293,7 +299,7 @@ static int __cmd_report(void) goto out_delete; if (dump_trace) { - event__print_totals(); + perf_session__fprintf_nr_events(session, stdout); goto out_delete; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index cce006ec8f05..3e8fec173041 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -7,6 +7,23 @@ #include "strlist.h" #include "thread.h" +const char *event__name[] = { + [0] = "TOTAL", + [PERF_RECORD_MMAP] = "MMAP", + [PERF_RECORD_LOST] = "LOST", + [PERF_RECORD_COMM] = "COMM", + [PERF_RECORD_EXIT] = "EXIT", + [PERF_RECORD_THROTTLE] = "THROTTLE", + [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", + [PERF_RECORD_FORK] = "FORK", + [PERF_RECORD_READ] = "READ", + [PERF_RECORD_SAMPLE] = "SAMPLE", + [PERF_RECORD_HEADER_ATTR] = "ATTR", + [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", + [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", + [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", +}; + static pid_t event__synthesize_comm(pid_t pid, int full, event__handler_t process, struct perf_session *session) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 48c2cc9dae4f..8577085db067 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -160,4 +160,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, struct addr_location *al, symbol_filter_t filter); int event__parse_sample(event_t *event, u64 type, struct sample_data *data); +extern const char *event__name[]; + #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5dc4f8429eda..1614ad710046 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1028,3 +1028,24 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) pclose(file); return 0; } + +void hists__inc_nr_events(struct hists *self, u32 type) +{ + ++self->hists.stats.nr_events[0]; + ++self->hists.stats.nr_events[type]; +} + +size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) +{ + int i; + size_t ret = 0; + + for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { + if (!event__name[i]) + continue; + ret += fprintf(fp, "%10s events: %10d\n", + event__name[i], self->stats.nr_events[i]); + } + + return ret; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 0b4c8df914bd..97b8962ff69a 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -40,6 +40,8 @@ struct sym_priv { struct events_stats { u64 total; u64 lost; + u32 nr_events[PERF_RECORD_HEADER_MAX]; + u32 nr_unknown_events; }; struct hists { @@ -68,6 +70,10 @@ void hist_entry__free(struct hist_entry *); void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); + +void hists__inc_nr_events(struct hists *self, u32 type); +size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); + size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 72a7f6ae0293..7231f6b19fb4 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -94,7 +94,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->mmap_window = 32; self->cwd = NULL; self->cwdlen = 0; - self->unknown_events = 0; self->machines = RB_ROOT; self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples_head); @@ -241,36 +240,6 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) } } -static const char *event__name[] = { - [0] = "TOTAL", - [PERF_RECORD_MMAP] = "MMAP", - [PERF_RECORD_LOST] = "LOST", - [PERF_RECORD_COMM] = "COMM", - [PERF_RECORD_EXIT] = "EXIT", - [PERF_RECORD_THROTTLE] = "THROTTLE", - [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", - [PERF_RECORD_FORK] = "FORK", - [PERF_RECORD_READ] = "READ", - [PERF_RECORD_SAMPLE] = "SAMPLE", - [PERF_RECORD_HEADER_ATTR] = "ATTR", - [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", - [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", - [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", -}; - -unsigned long event__total[PERF_RECORD_HEADER_MAX]; - -void event__print_totals(void) -{ - int i; - for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { - if (!event__name[i]) - continue; - pr_info("%10s events: %10ld\n", - event__name[i], event__total[i]); - } -} - void mem_bswap_64(void *src, int byte_size) { u64 *m = src; @@ -580,8 +549,7 @@ static int perf_session__process_event(struct perf_session *self, dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); - ++event__total[0]; - ++event__total[event->header.type]; + hists__inc_nr_events(self, event->header.type); } if (self->header.needs_swap && event__swap_ops[event->header.type]) @@ -619,7 +587,7 @@ static int perf_session__process_event(struct perf_session *self, case PERF_RECORD_FINISHED_ROUND: return ops->finished_round(event, self, ops); default: - self->unknown_events++; + ++self->hists.stats.nr_unknown_events; return -1; } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index ce00fa6cdeda..e7fce486ebe2 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -30,8 +30,6 @@ struct perf_session { struct machine host_machine; struct rb_root machines; struct rb_root hists_tree; - unsigned long event_total[PERF_RECORD_MAX]; - unsigned long unknown_events; /* * FIXME: should point to the first entry in hists_tree and * be a hists instance. Right now its only 'report' @@ -140,4 +138,10 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, { return machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); } + +static inline +size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) +{ + return hists__fprintf_nr_events(&self->hists, fp); +} #endif /* __PERF_SESSION_H */ -- cgit v1.2.3 From cee75ac7ecc27084accdb9d9d6fde65a09f047ae Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 13:16:55 -0300 Subject: perf hist: Clarify events_stats fields usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The events_stats.total field is too generic, rename it to .total_period, and also add a comment explaining that it is the sum of all the .period fields in samples, that is needed because we use auto-freq to avoid sampling artifacts. Ditto for events_stats.lost, that is the sum of all lost_event.lost fields, i.e. the number of events the kernel dropped. Looking at the users, builtin-sched.c can make use of these fields and stop doing it again. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-diff.c | 2 +- tools/perf/builtin-report.c | 8 ++++---- tools/perf/builtin-sched.c | 17 ++++++----------- tools/perf/builtin-trace.c | 2 +- tools/perf/util/event.c | 2 +- tools/perf/util/hist.c | 20 ++++++++++---------- tools/perf/util/hist.h | 16 ++++++++++++++-- tools/perf/util/newt.c | 6 +++--- tools/perf/util/session.c | 2 +- 9 files changed, 41 insertions(+), 34 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3a95a0260a5b..6dd4bdae8a87 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -54,7 +54,7 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi return -1; } - session->hists.stats.total += data.period; + session->hists.stats.total_period += data.period; return 0; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f13cda1ef059..b8f47ded6287 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -138,14 +138,14 @@ static int add_event_total(struct perf_session *session, if (!hists) return -ENOMEM; - hists->stats.total += data->period; + hists->stats.total_period += data->period; /* * FIXME: add_event_total should be moved from here to * perf_session__process_event so that the proper hist is passed to * the event_op methods. */ hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); - session->hists.stats.total += data->period; + session->hists.stats.total_period += data->period; return 0; } @@ -322,10 +322,10 @@ static int __cmd_report(void) if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) fprintf(stdout, "# Samples: %Ld\n#\n", - hists->stats.total); + hists->stats.total_period); else fprintf(stdout, "# Samples: %Ld %s\n#\n", - hists->stats.total, + hists->stats.total_period, __event_name(hists->type, hists->config)); hists__fprintf(hists, NULL, false, stdout); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index aef6ed0e119c..be7bc9264710 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1641,19 +1641,10 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } -static int process_lost_event(event_t *event __used, - struct perf_session *session __used) -{ - nr_lost_chunks++; - nr_lost_events += event->lost.lost; - - return 0; -} - static struct perf_event_ops event_ops = { .sample = process_sample_event, .comm = event__process_comm, - .lost = process_lost_event, + .lost = event__process_lost, .ordered_samples = true, }; @@ -1664,8 +1655,12 @@ static int read_events(void) if (session == NULL) return -ENOMEM; - if (perf_session__has_traces(session, "record -R")) + if (perf_session__has_traces(session, "record -R")) { err = perf_session__process_events(session, &event_ops); + nr_events = session->hists.stats.nr_events[0]; + nr_lost_events = session->hists.stats.total_lost; + nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; + } perf_session__delete(session); return err; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 95fcb0517a9d..dddf3f01b5ab 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -109,7 +109,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) data.time, thread->comm); } - session->hists.stats.total += data.period; + session->hists.stats.total_period += data.period; return 0; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3e8fec173041..50771b5813ee 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -385,7 +385,7 @@ int event__process_comm(event_t *self, struct perf_session *session) int event__process_lost(event_t *self, struct perf_session *session) { dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); - session->hists.stats.lost += self->lost.lost; + session->hists.stats.total_lost += self->lost.lost; return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 1614ad710046..c59224518083 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -239,7 +239,7 @@ void hists__output_resort(struct hists *self) struct hist_entry *n; u64 min_callchain_hits; - min_callchain_hits = self->stats.total * (callchain_param.min_percent / 100); + min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); tmp = RB_ROOT; next = rb_first(&self->entries); @@ -525,7 +525,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, if (pair_hists) { count = self->pair ? self->pair->count : 0; - total = pair_hists->stats.total; + total = pair_hists->stats.total_period; count_sys = self->pair ? self->pair->count_sys : 0; count_us = self->pair ? self->pair->count_us : 0; count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; @@ -769,10 +769,10 @@ print_entries: ++position; } ret += hist_entry__fprintf(h, pair, show_displacement, - displacement, fp, self->stats.total); + displacement, fp, self->stats.total_period); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, self->stats.total); + ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, @@ -795,7 +795,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) { struct rb_node *nd; - self->nr_entries = self->stats.total = 0; + self->nr_entries = self->stats.total_period = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -812,7 +812,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) h->filtered &= ~(1 << HIST_FILTER__DSO); if (!h->filtered) { ++self->nr_entries; - self->stats.total += h->count; + self->stats.total_period += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -824,7 +824,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) { struct rb_node *nd; - self->nr_entries = self->stats.total = 0; + self->nr_entries = self->stats.total_period = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -837,7 +837,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) h->filtered &= ~(1 << HIST_FILTER__THREAD); if (!h->filtered) { ++self->nr_entries; - self->stats.total += h->count; + self->stats.total_period += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -1031,8 +1031,8 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) void hists__inc_nr_events(struct hists *self, u32 type) { - ++self->hists.stats.nr_events[0]; - ++self->hists.stats.nr_events[type]; + ++self->stats.nr_events[0]; + ++self->stats.nr_events[type]; } size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 97b8962ff69a..da6b84814a50 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -37,9 +37,21 @@ struct sym_priv { struct sym_ext *ext; }; +/* + * The kernel collects the number of events it couldn't send in a stretch and + * when possible sends this number in a PERF_RECORD_LOST event. The number of + * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while + * total_lost tells exactly how many events the kernel in fact lost, i.e. it is + * the sum of all struct lost_event.lost fields reported. + * + * The total_period is needed because by default auto-freq is used, so + * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get + * the total number of low level events, it is necessary to to sum all struct + * sample_event.period and stash the result in total_period. + */ struct events_stats { - u64 total; - u64 lost; + u64 total_period; + u64 total_lost; u32 nr_events[PERF_RECORD_HEADER_MAX]; u32 nr_unknown_events; }; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index ba6acd04c082..010bacf40163 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -689,7 +689,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists } snprintf(str, sizeof(str), "Samples: %Ld ", - hists->stats.total); + hists->stats.total_period); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); @@ -718,12 +718,12 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, hists->stats.total); + len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, self->tree, - hists->stats.total, idx++); + hists->stats.total_period, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 7231f6b19fb4..25bfca4f10f0 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -549,7 +549,7 @@ static int perf_session__process_event(struct perf_session *self, dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); - hists__inc_nr_events(self, event->header.type); + hists__inc_nr_events(&self->hists, event->header.type); } if (self->header.needs_swap && event__swap_ops[event->header.type]) -- cgit v1.2.3 From c82ee828aa20487d254a5225d256cd422acee459 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 14:19:35 -0300 Subject: perf report: Report number of events, not samples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Number of samples is meaningless after we switched to auto-freq, so report the number of events, i.e. not the sum of the different periods, but the number PERF_RECORD_SAMPLE emitted by the kernel. While doing this I noticed that naming "count" to the sum of all the event periods can be confusing, so rename it to .period, just like in struct sample.data, so that we become more consistent. This helps with the next step, that was to record in struct hist_entry the number of sample events for each instance, we need that because we use it to generate the number of events when applying filters to the tree of hist entries like it is being done in the TUI report browser. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-diff.c | 6 ++-- tools/perf/builtin-report.c | 32 +++++++++++------ tools/perf/util/hist.c | 88 ++++++++++++++++++++++++--------------------- tools/perf/util/hist.h | 2 +- tools/perf/util/newt.c | 8 +++-- tools/perf/util/sort.h | 11 +++--- tools/perf/util/util.c | 22 ++++++++++++ tools/perf/util/util.h | 1 + 8 files changed, 107 insertions(+), 63 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 6dd4bdae8a87..a6e2fdc7a04e 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -23,9 +23,9 @@ static bool force; static bool show_displacement; static int hists__add_entry(struct hists *self, - struct addr_location *al, u64 count) + struct addr_location *al, u64 period) { - if (__hists__add_entry(self, al, NULL, count) != NULL) + if (__hists__add_entry(self, al, NULL, period) != NULL) return 0; return -ENOMEM; } @@ -50,7 +50,7 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi event__parse_sample(event, session->sample_type, &data); if (hists__add_entry(&session->hists, &al, data.period)) { - pr_warning("problem incrementing symbol count, skipping event\n"); + pr_warning("problem incrementing symbol period, skipping event\n"); return -1; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b8f47ded6287..68265120ee07 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -188,14 +188,14 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; if (perf_session__add_hist_entry(session, &al, &data)) { - pr_debug("problem incrementing symbol count, skipping event\n"); + pr_debug("problem incrementing symbol period, skipping event\n"); return -1; } attr = perf_header__find_attr(data.id, &session->header); if (add_event_total(session, &data, attr)) { - pr_debug("problem adding event count\n"); + pr_debug("problem adding event period\n"); return -1; } @@ -269,11 +269,25 @@ static struct perf_event_ops event_ops = { extern volatile int session_done; -static void sig_handler(int sig __attribute__((__unused__))) +static void sig_handler(int sig __used) { session_done = 1; } +static size_t hists__fprintf_nr_sample_events(struct hists *self, + const char *evname, FILE *fp) +{ + size_t ret; + char unit; + unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; + + nr_events = convert_unit(nr_events, &unit); + ret = fprintf(fp, "# Events: %lu%c", nr_events, unit); + if (evname != NULL) + ret += fprintf(fp, " %s", evname); + return ret + fprintf(fp, "\n#\n"); +} + static int __cmd_report(void) { int ret = -EINVAL; @@ -319,14 +333,12 @@ static int __cmd_report(void) if (use_browser) hists__browse(hists, help, input_name); else { - if (rb_first(&session->hists.entries) == + const char *evname = NULL; + if (rb_first(&session->hists.entries) != rb_last(&session->hists.entries)) - fprintf(stdout, "# Samples: %Ld\n#\n", - hists->stats.total_period); - else - fprintf(stdout, "# Samples: %Ld %s\n#\n", - hists->stats.total_period, - __event_name(hists->type, hists->config)); + evname = __event_name(hists->type, hists->config); + + hists__fprintf_nr_sample_events(hists, evname, stdout); hists__fprintf(hists, NULL, false, stdout); fprintf(stdout, "\n\n"); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c59224518083..f75c5f62401c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -9,21 +9,21 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -static void hist_entry__add_cpumode_count(struct hist_entry *self, - unsigned int cpumode, u64 count) +static void hist_entry__add_cpumode_period(struct hist_entry *self, + unsigned int cpumode, u64 period) { switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - self->count_sys += count; + self->period_sys += period; break; case PERF_RECORD_MISC_USER: - self->count_us += count; + self->period_us += period; break; case PERF_RECORD_MISC_GUEST_KERNEL: - self->count_guest_sys += count; + self->period_guest_sys += period; break; case PERF_RECORD_MISC_GUEST_USER: - self->count_guest_us += count; + self->period_guest_us += period; break; default: break; @@ -31,7 +31,7 @@ static void hist_entry__add_cpumode_count(struct hist_entry *self, } /* - * histogram, sorted on item, collects counts + * histogram, sorted on item, collects periods */ static struct hist_entry *hist_entry__new(struct hist_entry *template) @@ -41,6 +41,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) if (self != NULL) { *self = *template; + self->nr_events = 1; if (symbol_conf.use_callchain) callchain_init(self->callchain); } @@ -57,7 +58,7 @@ static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *sym_parent, u64 count) + struct symbol *sym_parent, u64 period) { struct rb_node **p = &self->entries.rb_node; struct rb_node *parent = NULL; @@ -70,7 +71,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, }, .ip = al->addr, .level = al->level, - .count = count, + .period = period, .parent = sym_parent, }; int cmp; @@ -82,7 +83,8 @@ struct hist_entry *__hists__add_entry(struct hists *self, cmp = hist_entry__cmp(&entry, he); if (!cmp) { - he->count += count; + he->period += period; + ++he->nr_events; goto out; } @@ -99,7 +101,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, rb_insert_color(&he->rb_node, &self->entries); hists__inc_nr_entries(self, he); out: - hist_entry__add_cpumode_count(he, al->cpumode, count); + hist_entry__add_cpumode_period(he, al->cpumode, period); return he; } @@ -160,7 +162,7 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) cmp = hist_entry__collapse(iter, he); if (!cmp) { - iter->count += he->count; + iter->period += he->period; hist_entry__free(he); return false; } @@ -203,7 +205,7 @@ void hists__collapse_resort(struct hists *self) } /* - * reverse the map, sort on count. + * reverse the map, sort on period. */ static void __hists__insert_output_entry(struct rb_root *entries, @@ -222,7 +224,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); - if (he->count > iter->count) + if (he->period > iter->period) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -288,7 +290,7 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, } static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, - int depth, int depth_mask, int count, + int depth, int depth_mask, int period, u64 total_samples, int hits, int left_margin) { @@ -301,7 +303,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, ret += fprintf(fp, "|"); else ret += fprintf(fp, " "); - if (!count && i == depth - 1) { + if (!period && i == depth - 1) { double percent; percent = hits * 100.0 / total_samples; @@ -516,7 +518,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, long displacement, bool color, u64 session_total) { struct sort_entry *se; - u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; + u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; const char *sep = symbol_conf.field_sep; int ret; @@ -524,57 +526,57 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, return 0; if (pair_hists) { - count = self->pair ? self->pair->count : 0; + period = self->pair ? self->pair->period : 0; total = pair_hists->stats.total_period; - count_sys = self->pair ? self->pair->count_sys : 0; - count_us = self->pair ? self->pair->count_us : 0; - count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; - count_guest_us = self->pair ? self->pair->count_guest_us : 0; + period_sys = self->pair ? self->pair->period_sys : 0; + period_us = self->pair ? self->pair->period_us : 0; + period_guest_sys = self->pair ? self->pair->period_guest_sys : 0; + period_guest_us = self->pair ? self->pair->period_guest_us : 0; } else { - count = self->count; + period = self->period; total = session_total; - count_sys = self->count_sys; - count_us = self->count_us; - count_guest_sys = self->count_guest_sys; - count_guest_us = self->count_guest_us; + period_sys = self->period_sys; + period_us = self->period_us; + period_guest_sys = self->period_guest_sys; + period_guest_us = self->period_guest_us; } if (total) { if (color) ret = percent_color_snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); + (period * 100.0) / total); else ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); + (period * 100.0) / total); if (symbol_conf.show_cpu_utilization) { ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_sys * 100.0) / total); + (period_sys * 100.0) / total); ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_us * 100.0) / total); + (period_us * 100.0) / total); if (perf_guest) { ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_guest_sys * 100.0) / + (period_guest_sys * 100.0) / total); ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_guest_us * 100.0) / + (period_guest_us * 100.0) / total); } } } else - ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); + ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); if (symbol_conf.show_nr_samples) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); + ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); else - ret += snprintf(s + ret, size - ret, "%11lld", count); + ret += snprintf(s + ret, size - ret, "%11lld", period); } if (pair_hists) { @@ -582,9 +584,9 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, double old_percent = 0, new_percent = 0, diff; if (total > 0) - old_percent = (count * 100.0) / total; + old_percent = (period * 100.0) / total; if (session_total > 0) - new_percent = (self->count * 100.0) / session_total; + new_percent = (self->period * 100.0) / session_total; diff = new_percent - old_percent; @@ -796,6 +798,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) struct rb_node *nd; self->nr_entries = self->stats.total_period = 0; + self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -812,7 +815,8 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) h->filtered &= ~(1 << HIST_FILTER__DSO); if (!h->filtered) { ++self->nr_entries; - self->stats.total_period += h->count; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -825,6 +829,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) struct rb_node *nd; self->nr_entries = self->stats.total_period = 0; + self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -837,7 +842,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) h->filtered &= ~(1 << HIST_FILTER__THREAD); if (!h->filtered) { ++self->nr_entries; - self->stats.total_period += h->count; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -881,7 +887,7 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) h->sum++; h->ip[offset]++; - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, + pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); return 0; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index da6b84814a50..6f17dcd8412c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -69,7 +69,7 @@ struct hists { struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *parent, u64 count); + struct symbol *parent, u64 period); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 010bacf40163..3402453812b3 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -680,16 +680,18 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists struct ui_progress *progress; struct rb_node *nd; u64 curr_hist = 0; - char seq[] = "."; + char seq[] = ".", unit; char str[256]; + unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; if (self->form) { newtFormDestroy(self->form); newtPopWindow(); } - snprintf(str, sizeof(str), "Samples: %Ld ", - hists->stats.total_period); + nr_events = convert_unit(nr_events, &unit); + snprintf(str, sizeof(str), "Events: %lu%c ", + nr_events, unit); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index af301acc461c..eab2e0b3b74e 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -43,14 +43,15 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; - u64 count; - u64 count_sys; - u64 count_us; - u64 count_guest_sys; - u64 count_guest_us; + u64 period; + u64 period_sys; + u64 period_us; + u64 period_guest_sys; + u64 period_guest_us; struct map_symbol ms; struct thread *thread; u64 ip; + u32 nr_events; char level; u8 filtered; struct symbol *parent; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index f9b890fde681..214265674ddd 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -92,3 +92,25 @@ out_close_from: out: return err; } + +unsigned long convert_unit(unsigned long value, char *unit) +{ + *unit = ' '; + + if (value > 1000) { + value /= 1000; + *unit = 'K'; + } + + if (value > 1000) { + value /= 1000; + *unit = 'M'; + } + + if (value > 1000) { + value /= 1000; + *unit = 'G'; + } + + return value; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index fbf45d1b26f7..0795bf304b19 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -423,6 +423,7 @@ char **argv_split(const char *str, int *argcp); void argv_free(char **argv); bool strglobmatch(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat); +unsigned long convert_unit(unsigned long value, char *unit); #define _STR(x) #x #define STR(x) _STR(x) -- cgit v1.2.3 From edb7c60e27c1baff38d82440dc52eaffac9a45f4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 16:22:41 -0300 Subject: perf options: Type check all the remaining OPT_ variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OPT_SET_INT was renamed to OPT_SET_UINT since the only use in these tools is to set something that has an enum type, that is builtin compatible with unsigned int. Several string constifications were done to make OPT_STRING require a const char * type. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-bench.c | 4 ++-- tools/perf/builtin-help.c | 6 +++--- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-report.c | 4 ++-- tools/perf/builtin-sched.c | 2 +- tools/perf/builtin-top.c | 2 +- tools/perf/util/hist.c | 2 +- tools/perf/util/parse-options.c | 10 +++++----- tools/perf/util/parse-options.h | 16 ++++++++-------- tools/perf/util/sort.c | 8 ++++---- tools/perf/util/sort.h | 8 ++++---- tools/perf/util/symbol.h | 2 +- 12 files changed, 33 insertions(+), 33 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 46996774e559..fcb96269852a 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -95,7 +95,7 @@ static void dump_suites(int subsys_index) return; } -static char *bench_format_str; +static const char *bench_format_str; int bench_format = BENCH_FORMAT_DEFAULT; static const struct option bench_options[] = { @@ -126,7 +126,7 @@ static void print_usage(void) printf("\n"); } -static int bench_str2int(char *str) +static int bench_str2int(const char *str) { if (!str) return BENCH_FORMAT_DEFAULT; diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 81e3ecc40fc7..6d5a8a7faf48 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -33,10 +33,10 @@ static bool show_all = false; static enum help_format help_format = HELP_FORMAT_MAN; static struct option builtin_help_options[] = { OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), - OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), - OPT_SET_INT('w', "web", &help_format, "show manual in web browser", + OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), + OPT_SET_UINT('w', "web", &help_format, "show manual in web browser", HELP_FORMAT_WEB), - OPT_SET_INT('i', "info", &help_format, "show info page", + OPT_SET_UINT('i', "info", &help_format, "show info page", HELP_FORMAT_INFO), OPT_END(), }; diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index b1c6b38567f0..34d1e853829d 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -19,7 +19,7 @@ #include #include -static char *file_name; +static const char *file_name; static char name_buffer[256]; bool perf_host = 1; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 68265120ee07..1d3c1003b43a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -39,8 +39,8 @@ static bool dont_use_callchains; static bool show_threads; static struct perf_read_values show_threads_values; -static char default_pretty_printing_style[] = "normal"; -static char *pretty_printing_style = default_pretty_printing_style; +static const char default_pretty_printing_style[] = "normal"; +static const char *pretty_printing_style = default_pretty_printing_style; static char callchain_default_opt[] = "fractal,0.5"; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c80acdf927a7..f67bce2a83b4 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -22,7 +22,7 @@ static char const *input_name = "perf.data"; static char default_sort_order[] = "avg, max, switch, runtime"; -static char *sort_order = default_sort_order; +static const char *sort_order = default_sort_order; static int profile_cpu = -1; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 9f0cfa0108a6..397290a0a76e 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -96,7 +96,7 @@ struct source_line { struct source_line *next; }; -static char *sym_filter = NULL; +static const char *sym_filter = NULL; struct sym_entry *sym_filter_entry = NULL; struct sym_entry *sym_filter_entry_sched = NULL; static int sym_pcnt_filter = 5; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f75c5f62401c..9a71c94f057a 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -662,7 +662,7 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, long displacement = 0; unsigned int width; const char *sep = symbol_conf.field_sep; - char *col_width = symbol_conf.col_width_list_str; + const char *col_width = symbol_conf.col_width_list_str; init_rem_hits(); diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 36d955e49422..99d02aa57dbf 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -51,7 +51,7 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_BOOLEAN: case OPTION_INCR: case OPTION_BIT: - case OPTION_SET_INT: + case OPTION_SET_UINT: case OPTION_SET_PTR: return opterror(opt, "takes no value", flags); case OPTION_END: @@ -83,8 +83,8 @@ static int get_value(struct parse_opt_ctx_t *p, *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; return 0; - case OPTION_SET_INT: - *(int *)opt->value = unset ? 0 : opt->defval; + case OPTION_SET_UINT: + *(unsigned int *)opt->value = unset ? 0 : opt->defval; return 0; case OPTION_SET_PTR: @@ -515,13 +515,13 @@ int usage_with_options_internal(const char * const *usagestr, pos += fprintf(stderr, " ..."); } break; - default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ + default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ case OPTION_END: case OPTION_GROUP: case OPTION_BIT: case OPTION_BOOLEAN: case OPTION_INCR: - case OPTION_SET_INT: + case OPTION_SET_UINT: case OPTION_SET_PTR: break; } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 5838e2d8bbc1..c7d72dce54b2 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -13,7 +13,7 @@ enum parse_opt_type { OPTION_BIT, OPTION_BOOLEAN, OPTION_INCR, - OPTION_SET_INT, + OPTION_SET_UINT, OPTION_SET_PTR, /* options with arguments (usually) */ OPTION_STRING, @@ -79,7 +79,7 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * * `defval`:: * default value to fill (*->value) with for PARSE_OPT_OPTARG. - * OPTION_{BIT,SET_INT,SET_PTR} store the {mask,integer,pointer} to put in + * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in * the value when met. * CALLBACKS can use it like they want. */ @@ -101,16 +101,16 @@ struct option { #define OPT_END() { .type = OPTION_END } #define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } -#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) } +#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } -#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } +#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } +#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } #define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } -#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) } +#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } +#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } +#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } #define OPT_DATE(s, l, v, h) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } #define OPT_CALLBACK(s, l, v, a, h, f) \ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index da30b305fba0..2316cb5a4116 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,10 +1,10 @@ #include "sort.h" regex_t parent_regex; -char default_parent_pattern[] = "^sys_|^do_page_fault"; -char *parent_pattern = default_parent_pattern; -char default_sort_order[] = "comm,dso,symbol"; -char *sort_order = default_sort_order; +const char default_parent_pattern[] = "^sys_|^do_page_fault"; +const char *parent_pattern = default_parent_pattern; +const char default_sort_order[] = "comm,dso,symbol"; +const char *sort_order = default_sort_order; int sort__need_collapse = 0; int sort__has_parent = 0; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index eab2e0b3b74e..0d61c4082f43 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -25,10 +25,10 @@ #include "sort.h" extern regex_t parent_regex; -extern char *sort_order; -extern char default_parent_pattern[]; -extern char *parent_pattern; -extern char default_sort_order[]; +extern const char *sort_order; +extern const char default_parent_pattern[]; +extern const char *parent_pattern; +extern const char default_sort_order[]; extern int sort__need_collapse; extern int sort__has_parent; extern char *field_sep; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 6389d1acaf81..032469e41876 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -78,7 +78,7 @@ struct symbol_conf { *default_guest_kallsyms, *default_guest_modules; const char *guestmount; - char *dso_list_str, + const char *dso_list_str, *comm_list_str, *sym_list_str, *col_width_list_str; -- cgit v1.2.3