perf tools: Support multiple keys in a single hierarchy level (v1)
Namhyung Kim
maintainercover perf tools: Support multiple keys in a single hierarchy level (v1)
Hello,
This implements what Arnaldo suggested in previous discussion of hierarchy patchset [1]. Originally each level in a hierarchy can have a single sort key in it, but with patches it's possible to have more than one sort keys. I added the struct perf_hpp_list_node and carry it to group output formats (hpp_fmt) in a single level.
I used ':' character instead of '+' as suggested since the '+' was also used to extend existing sort keys (like -s '+cpu').
Example below shows how 4 sort keys are used for 2 levels. As you can see, the first level shows pid and comm of previous (switched) task and the second level shows pid and comm of next task.
$ perf report --hierarchy -s prev_pid:prev_comm,next_pid:next_comm \
--percent-limit 1 -i perf.data.sched
...
# Overhead prev_pid+prev_comm / next_pid+next_comm
# ........... .......................................
#
22.36% 0 swapper/0
9.48% 17773 transmission-gt
5.25% 109 kworker/0:1H
1.53% 6524 Xephyr
21.39% 17773 transmission-gt
9.52% 0 swapper/0
9.04% 0 swapper/2
1.78% 0 swapper/3It's available on the 'perf/hierarchy-multi-v1' branch in my tree
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git
Any feedbacks are welcome
Thanks,
Namhyung
[1] https://lkml.org/lkml/2016/2/24/1041
Namhyung Kim (8):
perf tools: Add level field to struct perf_hpp_fmt perf tools: Introduce perf_hpp__setup_hists_formats() perf tools: Use own hpp_list for hierarchy mode perf tools: Support multiple sort keys in a hierarchy perf tools: Fix indent for multiple hierarchy sort key perf report: Use hierarchy hpp list on stdio perf hists browser: Use hierarchy hpp list perf report: Use hierarchy hpp list on gtk
tools/perf/ui/browsers/hists.c | 147 +++++++++++++++++++---------------- tools/perf/ui/gtk/hists.c | 73 +++++++++++------- tools/perf/ui/hist.c | 67 ++++++++++++++++ tools/perf/ui/stdio/hist.c | 171 +++++++++++++++++++++-------------------- tools/perf/util/hist.c | 81 +++++++++++++------ tools/perf/util/hist.h | 12 +++ tools/perf/util/sort.c | 123 ++++++++++++++++++++--------- tools/perf/util/sort.h | 1 + 8 files changed, 438 insertions(+), 237 deletions(-)
Signature · 1 line
2.7.1
Namhyung Kim
maintainerPATCH 1/8 perf tools: Add level field to struct perf_hpp_fmt
The level field is to distinguish levels in the hierarchy mode. Currently each column (perf_hpp_fmt) has a different level.
--- tools/perf/util/hist.h | 1 + tools/perf/util/sort.c | 74 ++++++++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 33 deletions(-)
tools/perf/util/hist.h +1 -0
@@ -233,6 +233,7 @@ struct perf_hpp_fmt {int len;int user_len;int idx; +int level; };struct perf_hpp_list {
tools/perf/util/sort.c +41 -33
272 lines not on this page · raw message
Signature · 1 line
2.7.1
Namhyung Kim
maintainerPATCH 2/8 perf tools: Introduce perf_hpp__setup_hists_formats()
In reply to Namhyung KimThe perf_hpp__setup_hists_formats() is to build hists-specific output formats (and sort keys). Currently it's only used in order to build the output format in a hierarchy with same sort keys, but it could be used with different sort keys in non-hierarchy mode later.
--- tools/perf/ui/hist.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/hist.c | 12 +++++++++ tools/perf/util/hist.h | 10 ++++++++ tools/perf/util/sort.c | 29 ++++++++++++++++++++++ 4 files changed, 117 insertions(+)
tools/perf/ui/hist.c +66 -0
@@ -5,6 +5,7 @@#include "../util/util.h" #include "../util/sort.h" #include "../util/evsel.h" +#include "../util/evlist.h" /* hist period print (hpp) functions */
@@ -715,3 +716,68 @@ void perf_hpp__set_user_width(const char *width_list_str)break ; } } + +static int add_hierarchy_fmt (struct hists *hists,struct perf_hpp_fmt *fmt) +{ +struct perf_hpp_list_node *node =NULL ; +struct perf_hpp_fmt *fmt_copy; +bool found = false; + +list_for_each_entry (node, &hists->hpp_formats, list) { +struct perf_hpp_fmt *pos; + + pos = list_first_entry(&node->hpp.fields, struct perf_hpp_fmt, list); +if (pos->level == fmt->level) { + found = true; +break ; + } + } + +if (!found) { + node =malloc (sizeof (*node)); +if (node ==NULL ) +return -1 ; + +INIT_LIST_HEAD (&node->hpp.fields); +INIT_LIST_HEAD (&node->hpp.sorts); + +list_add_tail (&node->list, &hists->hpp_formats); + } + + fmt_copy =perf_hpp_fmt__copy (fmt); +if (fmt_copy ==NULL ) +return -1 ; + +list_add_tail (&fmt_copy->list, &node->hpp.fields); +list_add_tail (&fmt_copy->sort_list, &node->hpp.sorts); + +return 0 ; +} + +int perf_hpp__setup_hists_formats (struct perf_hpp_list *list, +struct perf_evlist *evlist) +{ +struct perf_evsel *evsel; +struct perf_hpp_fmt *fmt; +struct hists *hists; +int ret; + +if (!symbol_conf.report_hierarchy) +return 0 ; + +evlist__for_each (evlist, evsel) { + hists =evsel__hists (evsel); + +perf_hpp_list__for_each_sort_list (list, fmt) { +if (perf_hpp__is_dynamic_entry (fmt) && + !perf_hpp__defined_dynamic_entry (fmt, hists)) +continue ; + + ret =add_hierarchy_fmt (hists, fmt); +if (ret <0 ) +return ret; + } + } + +return 0 ; +}
tools/perf/util/hist.c +12 -0
@@ -2105,6 +2105,7 @@ int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list)pthread_mutex_init (&hists->lock,NULL ); hists->socket_filter =-1 ; hists->hpp_list = hpp_list; +INIT_LIST_HEAD (&hists->hpp_formats);return 0 ; }
@@ -2133,8 +2134,19 @@ static void hists__delete_all_entries(struct hists *hists)static void hists_evsel__exit (struct perf_evsel *evsel) {struct hists *hists =evsel__hists (evsel); +struct perf_hpp_fmt *fmt, *pos; +struct perf_hpp_list_node *node;hists__delete_all_entries (hists); + +list_for_each_entry (node, &hists->hpp_formats, list) { +perf_hpp_list__for_each_format_safe (&node->hpp, fmt, pos) { +list_del (&fmt->list); +free (fmt); + } +list_del (&node->list); +free (node); + } }static int hists_evsel__init (struct perf_evsel *evsel)
tools/perf/util/hist.h +10 -0
@@ -78,6 +78,7 @@ struct hists {u16 col_len[HISTC_NR_COLS ];int socket_filter;struct perf_hpp_list *hpp_list; +struct list_head hpp_formats;int nr_sort_keys; };
@@ -244,6 +245,11 @@ struct perf_hpp_list {extern struct perf_hpp_list perf_hpp_list; +struct perf_hpp_list_node { +struct list_head list; +struct perf_hpp_list hpp; +}; +void perf_hpp_list__column_register (struct perf_hpp_list *list,struct perf_hpp_fmt *format); void perf_hpp_list__register_sort_field(struct perf_hpp_list *list,
@@ -299,6 +305,8 @@ void perf_hpp__cancel_cumulate(void);void perf_hpp__setup_output_field (struct perf_hpp_list *list);void perf_hpp__reset_output_field (struct perf_hpp_list *list);void perf_hpp__append_sort_keys (struct perf_hpp_list *list); +int perf_hpp__setup_hists_formats (struct perf_hpp_list *list, +struct perf_evlist *evlist);bool perf_hpp__is_sort_entry (struct perf_hpp_fmt *format);
@@ -308,6 +316,8 @@ bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt);bool perf_hpp__is_srcline_entry (struct perf_hpp_fmt *fmt);bool perf_hpp__is_srcfile_entry (struct perf_hpp_fmt *fmt); +struct perf_hpp_fmt *perf_hpp_fmt__copy (struct perf_hpp_fmt *fmt); +int hist_entry__filter (struct hist_entry *he,int type,const void *arg); static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format,
tools/perf/util/sort.c +29 -0
@@ -1908,6 +1908,31 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field,return hde; } +struct perf_hpp_fmt *perf_hpp_fmt__copy (struct perf_hpp_fmt *fmt) +{ +struct perf_hpp_fmt *new_fmt =NULL ; + +if (perf_hpp__is_sort_entry (fmt)) { +struct hpp_sort_entry *hse, *new_hse; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + new_hse =memdup (hse,sizeof (*hse)); +if (new_hse) + new_fmt = &new_hse->hpp; + }else if (perf_hpp__is_dynamic_entry (fmt)) { +struct hpp_dynamic_entry *hde, *new_hde; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + new_hde =memdup (hde,sizeof (*hde)); +if (new_hde) + new_fmt = &new_hde->hpp; + }else { + new_fmt =memdup (fmt,sizeof (*fmt)); + } + +return new_fmt; +} +static int parse_field_name (char *str,char **event,char **field,char **opt) {char *event_name, *field_name, *opt_name;
@@ -2700,6 +2725,10 @@ int setup_sorting(struct perf_evlist *evlist)/* and then copy output fields to sort keys */ perf_hpp__append_sort_keys (&perf_hpp_list); +/* setup hists-specific output fields */ +if (perf_hpp__setup_hists_formats (&perf_hpp_list, evlist) <0 ) +return -1 ; +return 0 ; }
Signature · 1 line
2.7.1
Re: [PATCH 2/8] perf tools: Introduce perf_hpp__setup_hists_formats()
On Thu, Mar 03, 2016 at 01:12:02AM +0900, Namhyung Kim wrote:
SNIP
Quoted 10 lines
tools/perf/util/hist.h +2
-0+struct perf_hpp_fmt *perf_hpp_fmt__copy (struct perf_hpp_fmt *fmt); +int hist_entry__filter (struct hist_entry *he,int type,const void *arg); static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format,tools/perf/util/sort.c +0
-0
tools/perf/util/sort.c __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field, · line 1911
Quoted 4 lines
tools/perf/util/sort.c +1
-0@@ -1908,6 +1908,31 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field,return hde; } +struct perf_hpp_fmt *perf_hpp_fmt__copy (struct perf_hpp_fmt *fmt)
perf_hpp_fmt__dup might be better
Quoted 10 lines
tools/perf/util/sort.c +10
-0+{ +struct perf_hpp_fmt *new_fmt =NULL ; + +if (perf_hpp__is_sort_entry (fmt)) { +struct hpp_sort_entry *hse, *new_hse; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + new_hse =memdup (hse,sizeof (*hse)); +if (new_hse) + new_fmt = &new_hse->hpp;
tools/perf/util/sort.c __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field, · line 1931
Quoted 10 lines
tools/perf/util/sort.c +10
-0+ }else if (perf_hpp__is_dynamic_entry (fmt)) { +struct hpp_dynamic_entry *hde, *new_hde; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + new_hde =memdup (hde,sizeof (*hde)); +if (new_hde) + new_fmt = &new_hde->hpp; + }else { + new_fmt =memdup (fmt,sizeof (*fmt)); + }
how about also initialize new_fmt's list and sort_list in here
could save some time in future if perf_hpp_fmt__copy is used in another place, which won't directly initialize them like you do in add_hierarchy_fmt
jirka
Namhyung Kim
maintainerRe: [PATCH 2/8] perf tools: Introduce perf_hpp__setup_hists_formats()
Hi Jiri,
Quoted 17 lines · Jiri Olsa
On Thu, Mar 03, 2016 at 01:12:02AM +0900, Namhyung Kim wrote:
SNIP
Quoted 13 lines
tools/perf/util/hist.h +2
-0+struct perf_hpp_fmt *perf_hpp_fmt__copy (struct perf_hpp_fmt *fmt); +int hist_entry__filter (struct hist_entry *he,int type,const void *arg); static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format,tools/perf/util/sort.c +0
-0@@ -1908,6 +1908,31 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field,return hde; }
Quoted 3 lines
tools/perf/util/sort.c __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field, · line 1911
Quoted 1 line
tools/perf/util/sort.c +1
-0+struct perf_hpp_fmt *perf_hpp_fmt__copy (struct perf_hpp_fmt *fmt)
perf_hpp_fmt__dupmight be better
OK
Quoted 24 lines
Quoted 11 lines
tools/perf/util/sort.c +10
-0+{ +struct perf_hpp_fmt *new_fmt =NULL ; + +if (perf_hpp__is_sort_entry (fmt)) { +struct hpp_sort_entry *hse, *new_hse; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + new_hse =memdup (hse,sizeof (*hse)); +if (new_hse) + new_fmt = &new_hse->hpp;tools/perf/util/sort.c __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field, · line 1931
Quoted 10 lines
tools/perf/util/sort.c +10
-0+ }else if (perf_hpp__is_dynamic_entry (fmt)) { +struct hpp_dynamic_entry *hde, *new_hde; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + new_hde =memdup (hde,sizeof (*hde)); +if (new_hde) + new_fmt = &new_hde->hpp; + }else { + new_fmt =memdup (fmt,sizeof (*fmt)); + }how about also initialize new_fmt's list and
sort_listin here
Quoted 3 lines
could save some time in future if
perf_hpp_fmt__copyis used in another place, which won't directly initialize them like you do inadd_hierarchy_fmt
Fair enough, will change.
Thanks,
Namhyung
Re: [PATCH 2/8] perf tools: Introduce perf_hpp__setup_hists_formats()
In reply to Namhyung KimQuoted 30 lines · Namhyung Kim
The
perf_hpp__setup_hists_formats()is to build hists-specific output formats (and sort keys). Currently it's only used in order to build the output format in a hierarchy with same sort keys, but it could be used with different sort keys in non-hierarchy mode later.Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/ui/hist.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/hist.c | 12 +++++++++ tools/perf/util/hist.h | 10 ++++++++ tools/perf/util/sort.c | 29 ++++++++++++++++++++++ 4 files changed, 117 insertions(+)tools/perf/ui/hist.c +2
-0@@ -5,6 +5,7 @@#include "../util/util.h" #include "../util/sort.h" #include "../util/evsel.h" +#include "../util/evlist.h" /* hist period print (hpp) functions */ @@ -715,3 +716,68 @@ void perf_hpp__set_user_width(const char *width_list_str)break ; } } +
tools/perf/ui/hist.c void perf_hpp__set_user_width(const char *width_list_str) · line 729
Quoted 10 lines
tools/perf/ui/hist.c +10
-0+static int add_hierarchy_fmt (struct hists *hists,struct perf_hpp_fmt *fmt) +{ +struct perf_hpp_list_node *node =NULL ; +struct perf_hpp_fmt *fmt_copy; +bool found = false; + +list_for_each_entry (node, &hists->hpp_formats, list) { +struct perf_hpp_fmt *pos; + + pos = list_first_entry(&node->hpp.fields, struct perf_hpp_fmt, list);
the struct perf_hpp_list_node could hold 'level' as well, so you wouldn't need to query first fmt
jirka
Namhyung Kim
maintainerRe: [PATCH 2/8] perf tools: Introduce perf_hpp__setup_hists_formats()
Quoted 36 lines · Jiri Olsa
Quoted 35 lines · Namhyung Kim
The
perf_hpp__setup_hists_formats()is to build hists-specific output formats (and sort keys). Currently it's only used in order to build the output format in a hierarchy with same sort keys, but it could be used with different sort keys in non-hierarchy mode later.Signed-off-by Namhyung Kim <[email protected]>--- tools/perf/ui/hist.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/hist.c | 12 +++++++++ tools/perf/util/hist.h | 10 ++++++++ tools/perf/util/sort.c | 29 ++++++++++++++++++++++ 4 files changed, 117 insertions(+)tools/perf/ui/hist.c +7
-0@@ -5,6 +5,7 @@#include "../util/util.h" #include "../util/sort.h" #include "../util/evsel.h" +#include "../util/evlist.h" /* hist period print (hpp) functions */ @@ -715,3 +716,68 @@ void perf_hpp__set_user_width(const char *width_list_str)break ; } } + +static int add_hierarchy_fmt (struct hists *hists,struct perf_hpp_fmt *fmt) +{ +struct perf_hpp_list_node *node =NULL ; +struct perf_hpp_fmt *fmt_copy; +bool found = false;
Quoted 8 lines
tools/perf/ui/hist.c void perf_hpp__set_user_width(const char *width_list_str) · line 729
Quoted 5 lines
tools/perf/ui/hist.c +5
-0+ +list_for_each_entry (node, &hists->hpp_formats, list) { +struct perf_hpp_fmt *pos; + + pos = list_first_entry(&node->hpp.fields, struct perf_hpp_fmt, list);the struct
perf_hpp_list_nodecould hold 'level' as well, so you wouldn't need to query first fmt
Will change.
Thanks,
Namhyung
Namhyung Kim
maintainerPATCH 3/8 perf tools: Use own hpp_list for hierarchy mode
In reply to Namhyung KimNow each hists has its own hpp lists in hierarchy. So instead of having a pointer to a single perf_hpp_fmt in a hist entry, make it point the hpp_list for its level. This will be used to support multiple sort keys in a single hierarchy level.
--- tools/perf/ui/browsers/hists.c | 45 +++++++++++++++------------ tools/perf/ui/gtk/hists.c | 20 ++++++++---- tools/perf/ui/stdio/hist.c | 44 +++++++++++++-------------- tools/perf/util/hist.c | 69 +++++++++++++++++++++++++++--------------- tools/perf/util/sort.h | 1 + 5 files changed, 107 insertions(+), 72 deletions(-)
tools/perf/ui/browsers/hists.c +25 -20
@@ -1388,25 +1388,26 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser, HE_COLORSET_NORMAL); } -ui_browser__write_nstring (&browser->b,"" ,2 ); - width -=2 ; +perf_hpp_list__for_each_format (entry->hpp_list, fmt) { +ui_browser__write_nstring (&browser->b,"" ,2 ); + width -=2 ; -/* -* No need to call hist_entry__snprintf_alignment() -* since this fmt is always the last column in the -* hierarchy mode. -*/ - fmt = entry->fmt; -if (fmt->color) { - width -= fmt->color (fmt, &hpp, entry); - }else { -int i =0 ; +/* +* No need to call hist_entry__snprintf_alignment() +* since this fmt is always the last column in the +* hierarchy mode. +*/ +if (fmt->color) { + width -= fmt->color (fmt, &hpp, entry); + }else { +int i =0 ; - width -= fmt->entry (fmt, &hpp, entry); -ui_browser__printf (&browser->b,"%s" ,ltrim (s)); + width -= fmt->entry (fmt, &hpp, entry); +ui_browser__printf (&browser->b,"%s" ,ltrim (s)); -while (isspace (s[i++])) - width++; +while (isspace (s[i++])) + width++; + } } }
@@ -1934,7 +1935,7 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,struct perf_hpp_fmt *fmt;bool first = true;int ret; -int hierarchy_indent = (nr_sort_keys +1 ) *HIERARCHY_INDENT ; +int hierarchy_indent = nr_sort_keys *HIERARCHY_INDENT ; printed =fprintf (fp,"%*s" , level *HIERARCHY_INDENT ,"" );
@@ -1962,9 +1963,13 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser, ret =scnprintf (hpp.buf, hpp.size,"%*s" , hierarchy_indent,"" );advance_hpp (&hpp, ret); - fmt = he->fmt; - ret = fmt->entry (fmt, &hpp, he); -advance_hpp (&hpp, ret); +perf_hpp_list__for_each_format (he->hpp_list, fmt) { + ret =scnprintf (hpp.buf, hpp.size," " ); +advance_hpp (&hpp, ret); + + ret = fmt->entry (fmt, &hpp, he); +advance_hpp (&hpp, ret); + } printed +=fprintf (fp,"%s\n" ,rtrim (s));
tools/perf/ui/gtk/hists.c +14 -6
@@ -412,6 +412,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,for (node =rb_first (root); node; node =rb_next (node)) {GtkTreeIter iter;float percent; +char *bf; he = rb_entry(node, struct hist_entry, rb_node);if (he->filtered)
@@ -437,13 +438,20 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,gtk_tree_store_set (store, &iter, col_idx++, hpp->buf,-1 ); } - fmt = he->fmt; -if (fmt->color) - fmt->color (fmt, hpp, he); -else - fmt->entry (fmt, hpp, he); + bf = hpp->buf; +perf_hpp_list__for_each_format (he->hpp_list, fmt) { +int ret; + +if (fmt->color) + ret = fmt->color (fmt, hpp, he); +else + ret = fmt->entry (fmt, hpp, he); + +snprintf (hpp->buf + ret, hpp->size - ret," " ); +advance_hpp (hpp, ret +2 ); + } -gtk_tree_store_set (store, &iter, col_idx,rtrim (hpp->buf),-1 ); +gtk_tree_store_set (store, &iter, col_idx,rtrim (bf),-1 );if (!he->leaf) { perf_gtk__add_hierarchy_entries(hists, &he->hroot_out,
tools/perf/ui/stdio/hist.c +22 -22
55 lines not on this page · raw message
tools/perf/util/hist.c +45 -24
121 lines not on this page · raw message
tools/perf/util/sort.h +1 -0
@@ -130,6 +130,7 @@ struct hist_entry {u32 raw_size;void *trace_output;struct perf_hpp_fmt *fmt; +struct perf_hpp_list *hpp_list;struct hist_entry *parent_he;union {/* this is for hierarchical entry structure */
Signature · 1 line
2.7.1
Namhyung Kim
maintainerPATCH 4/8 perf tools: Support multiple sort keys in a hierarchy
In reply to Namhyung KimThis implements having multiple sort keys in a single hierarchy level. Originally only single sort key is supported for each level, but now using the ':' separator, user can set more than one sort key in a level.
For example:
$ perf report --hierarchy -s comm:dso,sym
...
# Overhead Command / Shared Object / Symbol
# .............. ..........................................
#
48.67% swapper [kernel.vmlinux]
34.42% [k] intel_idle
1.30% [k] __tick_nohz_idle_enter
1.03% [k] cpuidle_reflect
8.87% firefox libpthread-2.22.so
6.60% [.] __GI___libc_recvmsg
1.18% [.] pthread_cond_signal@@GLIBC_2.3.2
1.09% [.] 0x000000000000ff4b
6.11% Xorg libc-2.22.so
5.27% [.] __memcpy_sse2_unalignedIn the above example, the command name and the shared object name are shown on the same line but the symbol name is on the different line. Since the first two are separated by ':', and the symbol is separated by ',' as usual.
--- tools/perf/util/sort.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-)
tools/perf/util/sort.c +18 -4
@@ -2307,10 +2307,22 @@ static int setup_sort_list(char *str, struct perf_evlist *evlist)char *tmp, *tok;int ret =0 ;int level =0 ; +int next_level; + + do { + tok = str; + tmp = strpbrk(str, ":, "); + if (tmp) { + if (*tmp == ':') + next_level = level; + else + next_level = level + 1; + + *tmp = '\0'; + str = tmp + 1; + } -for (tok =strtok_r (str,", " , &tmp); - tok; tok =strtok_r (NULL ,", " , &tmp)) { - ret =sort_dimension__add (tok, evlist, level++); + ret = sort_dimension__add(tok, evlist, level); if (ret == -EINVAL) { error("Invalid --sort key: `%s'", tok); break;
@@ -2318,7 +2330,9 @@ static int setup_sort_list(char *str, struct perf_evlist *evlist)error ("Unknown --sort key: `%s'" , tok);break ; } - } + + level = next_level; + }while (tmp);return ret; }
Signature · 1 line
2.7.1
Namhyung Kim
maintainerPATCH 5/8 perf tools: Fix indent for multiple hierarchy sort key
In reply to Namhyung KimWhen multiple sort keys are used in a single hierarchy, it should indent using number of hierarchy levels instead of number of sort keys.
--- tools/perf/ui/browsers/hists.c | 23 ++++++++++------------- tools/perf/ui/hist.c | 1 + tools/perf/ui/stdio/hist.c | 26 +++++++++++--------------- tools/perf/util/hist.h | 1 + 4 files changed, 23 insertions(+), 28 deletions(-)
tools/perf/ui/browsers/hists.c +10 -13
@@ -1280,7 +1280,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,static int hist_browser__show_hierarchy_entry (struct hist_browser *browser,struct hist_entry *entry,unsigned short row, -int level,int nr_sort_keys) +int level) {int printed =0 ;int width = browser->b.width;
@@ -1294,7 +1294,7 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser, .current_entry = current_entry, };int column =0 ; -int hierarchy_indent = (nr_sort_keys -1 ) *HIERARCHY_INDENT ; +int hierarchy_indent = (entry->hists->nr_hpp_node -2 ) *HIERARCHY_INDENT ;if (current_entry) { browser->he_selection = entry;
@@ -1436,8 +1436,7 @@ show_callchain: }static int hist_browser__show_no_entry (struct hist_browser *browser, -unsigned short row, -int level,int nr_sort_keys) +unsigned short row,int level) {int width = browser->b.width;bool current_entry =ui_browser__is_current_entry (&browser->b, row);
@@ -1445,6 +1444,7 @@ static int hist_browser__show_no_entry(struct hist_browser *browser,int column =0 ;int ret;struct perf_hpp_fmt *fmt; +int indent = browser->hists->nr_hpp_node -2 ;if (current_entry) { browser->he_selection =NULL ;
@@ -1485,8 +1485,8 @@ static int hist_browser__show_no_entry(struct hist_browser *browser, width -= ret; } -ui_browser__write_nstring (&browser->b,"" , nr_sort_keys *HIERARCHY_INDENT ); - width -= nr_sort_keys *HIERARCHY_INDENT ; +ui_browser__write_nstring (&browser->b,"" , indent *HIERARCHY_INDENT ); + width -= indent *HIERARCHY_INDENT ;if (column >= browser->b.horiz_scroll) {char buf[32 ];
@@ -1553,7 +1553,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browsstruct perf_hpp_fmt *fmt;size_t ret =0 ;int column =0 ; -int nr_sort_keys = hists->nr_sort_keys; +int indent = hists->nr_hpp_node -2 ;bool first = true; ret =scnprintf (buf, size," " );
@@ -1577,7 +1577,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows } ret =scnprintf (dummy_hpp.buf, dummy_hpp.size,"%*s" , - (nr_sort_keys -1 ) *HIERARCHY_INDENT ,"" ); + indent *HIERARCHY_INDENT ,"" );if (advance_hpp_check (&dummy_hpp, ret))return ret;
@@ -1645,7 +1645,6 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)u16 header_offset =0 ;struct rb_node *nd; struct hist_browser *hb = container_of(browser, struct hist_browser, b); -int nr_sort = hb->hists->nr_sort_keys;if (hb->show_headers) {hist_browser__show_headers (hb);
@@ -1672,14 +1671,12 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)if (symbol_conf.report_hierarchy) { row +=hist_browser__show_hierarchy_entry (hb, h, row, - h->depth, - nr_sort); + h->depth);if (row == browser->rows)break ;if (h->has_no_entry) { -hist_browser__show_no_entry (hb, row, h->depth, - nr_sort); +hist_browser__show_no_entry (hb, row, h->depth); row++; } }else {
tools/perf/ui/hist.c +1 -0
@@ -741,6 +741,7 @@ static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt)INIT_LIST_HEAD (&node->hpp.fields);INIT_LIST_HEAD (&node->hpp.sorts); + hists->nr_hpp_node++;list_add_tail (&node->list, &hists->hpp_formats); }
tools/perf/ui/stdio/hist.c +11 -15
@@ -412,7 +412,7 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)static int hist_entry__hierarchy_fprintf (struct hist_entry *he,struct perf_hpp *hpp, -int nr_sort_key,struct hists *hists, +struct hists *hists,FILE *fp) {const char *sep = symbol_conf.field_sep;
@@ -453,7 +453,7 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he,if (!sep) ret =scnprintf (hpp->buf, hpp->size,"%*s" , - (nr_sort_key -1 ) *HIERARCHY_INDENT ,"" ); + (hists->nr_hpp_node -2 ) *HIERARCHY_INDENT ,"" );advance_hpp (hpp, ret); printed +=fprintf (fp,"%s" , buf);
@@ -504,12 +504,8 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,if (size ==0 || size > bfsz) size = hpp.size = bfsz; -if (symbol_conf.report_hierarchy) { -int nr_sort = hists->nr_sort_keys; - -return hist_entry__hierarchy_fprintf (he, &hpp, nr_sort, - hists, fp); - } +if (symbol_conf.report_hierarchy) +return hist_entry__hierarchy_fprintf (he, &hpp, hists, fp);hist_entry__snprintf (he, &hpp);
@@ -521,29 +517,29 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,return ret; } -static int print_hierarchy_indent (const char *sep,int nr_sort, +static int print_hierarchy_indent (const char *sep,int indent,const char *line,FILE *fp) { -if (sep !=NULL || nr_sort <1 ) +if (sep !=NULL || indent <2 )return 0 ; -return fprintf (fp,"%-.*s" , (nr_sort -1 ) *HIERARCHY_INDENT , line); +return fprintf (fp,"%-.*s" , (indent -2 ) *HIERARCHY_INDENT , line); }static int print_hierarchy_header (struct hists *hists,struct perf_hpp *hpp,const char *sep,FILE *fp) {bool first = true; -int nr_sort; +int indent;int depth;unsigned width =0 ;unsigned header_width =0 ;struct perf_hpp_fmt *fmt; - nr_sort = hists->nr_sort_keys; + indent = hists->nr_hpp_node;/* preserve max indent depth for column headers */ -print_hierarchy_indent (sep, nr_sort, spaces, fp); +print_hierarchy_indent (sep, indent, spaces, fp);hists__for_each_format (hists, fmt) {if (perf_hpp__is_sort_entry (fmt) ||perf_hpp__is_dynamic_entry (fmt))
@@ -582,7 +578,7 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp,fprintf (fp,"\n# " );/* preserve max indent depth for initial dots */ -print_hierarchy_indent (sep, nr_sort, dots, fp); +print_hierarchy_indent (sep, indent, dots, fp); first = true;hists__for_each_format (hists, fmt) {
tools/perf/util/hist.h +1 -0
@@ -80,6 +80,7 @@ struct hists {struct perf_hpp_list *hpp_list;struct list_head hpp_formats;int nr_sort_keys; +int nr_hpp_node; };struct hist_entry_iter ;
Signature · 1 line
2.7.1
Namhyung Kim
maintainerPATCH 6/8 perf report: Use hierarchy hpp list on stdio
In reply to Namhyung KimNow hpp formats are linked using perf_hpp_list_node when hierarchy is enabled. Use this info to print entries with multiple sort keys in a single hierarchy properly.
For example, the below example shows using 4 sort keys with 2 levels.
$ perf report --hierarchy -s prev_pid:prev_comm,next_pid:next_comm \
--percent-limit 1 -i perf.data.sched
...
# Overhead prev_pid+prev_comm / next_pid+next_comm
# ........... .......................................
#
22.36% 0 swapper/0
9.48% 17773 transmission-gt
5.25% 109 kworker/0:1H
1.53% 6524 Xephyr
21.39% 17773 transmission-gt
9.52% 0 swapper/0
9.04% 0 swapper/2
1.78% 0 swapper/3--- tools/perf/ui/stdio/hist.c | 103 +++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 46 deletions(-)
tools/perf/ui/stdio/hist.c +57 -46
@@ -417,6 +417,7 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he, {const char *sep = symbol_conf.field_sep;struct perf_hpp_fmt *fmt; +struct perf_hpp_list_node *fmt_node;char *buf = hpp->buf;size_t size = hpp->size;int ret, printed =0 ;
@@ -428,10 +429,10 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he, ret =scnprintf (hpp->buf, hpp->size,"%*s" , he->depth *HIERARCHY_INDENT ,"" );advance_hpp (hpp, ret); -hists__for_each_format (he->hists, fmt) { -if (perf_hpp__is_sort_entry (fmt) ||perf_hpp__is_dynamic_entry (fmt)) -break ; - +/* the first hpp_list_node is for overhead columns */ + fmt_node =list_first_entry (&hists->hpp_formats, + struct perf_hpp_list_node, list); +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) {/* * If there's no field_sep, we still need * to display initial ' '.
@@ -529,50 +530,49 @@ static int print_hierarchy_indent(const char *sep, int indent,static int print_hierarchy_header (struct hists *hists,struct perf_hpp *hpp,const char *sep,FILE *fp) { -bool first = true; +bool first_node, first_col;int indent;int depth;unsigned width =0 ;unsigned header_width =0 ;struct perf_hpp_fmt *fmt; +struct perf_hpp_list_node *fmt_node; indent = hists->nr_hpp_node;/* preserve max indent depth for column headers */ print_hierarchy_indent (sep, indent, spaces, fp); -hists__for_each_format (hists, fmt) { -if (perf_hpp__is_sort_entry (fmt) ||perf_hpp__is_dynamic_entry (fmt)) -break ; - -if (!first) -fprintf (fp,"%s" , sep ?:" " ); -else - first = false; +/* the first hpp_list_node is for overhead columns */ + fmt_node =list_first_entry (&hists->hpp_formats, + struct perf_hpp_list_node, list); +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) { fmt->header (fmt, hpp,hists_to_evsel (hists)); -fprintf (fp,"%s" , hpp->buf); +fprintf (fp,"%s%s" , hpp->buf, sep ?:" " ); }/* combine sort headers with ' / ' */ - first = true; -hists__for_each_format (hists, fmt) { -if (!perf_hpp__is_sort_entry (fmt) && !perf_hpp__is_dynamic_entry (fmt)) -continue ; -if (perf_hpp__should_skip (fmt, hists)) -continue ; - -if (!first) + first_node = true; +list_for_each_entry_continue (fmt_node, &hists->hpp_formats, list) { +if (!first_node) header_width +=fprintf (fp," / " ); -else { -fprintf (fp,"%s" , sep ?:" " ); - first = false; - } + first_node = false; - fmt->header (fmt, hpp,hists_to_evsel (hists)); -rtrim (hpp->buf); + first_col = true; +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) { +if (perf_hpp__should_skip (fmt, hists)) +continue ; + +if (!first_col) + header_width +=fprintf (fp,"+" ); + first_col = false; + + fmt->header (fmt, hpp,hists_to_evsel (hists)); +rtrim (hpp->buf); - header_width +=fprintf (fp,"%s" ,ltrim (hpp->buf)); + header_width +=fprintf (fp,"%s" ,ltrim (hpp->buf)); + } }fprintf (fp,"\n# " );
@@ -580,29 +580,35 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp,/* preserve max indent depth for initial dots */ print_hierarchy_indent (sep, indent, dots, fp); - first = true; -hists__for_each_format (hists, fmt) { -if (perf_hpp__is_sort_entry (fmt) ||perf_hpp__is_dynamic_entry (fmt)) -break ; +/* the first hpp_list_node is for overhead columns */ + fmt_node =list_first_entry (&hists->hpp_formats, + struct perf_hpp_list_node, list); -if (!first) -fprintf (fp,"%s" , sep ?:" " ); -else - first = false; + first_col = true; +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) { +if (!first_col) +fprintf (fp,"%s" , sep ?:".." ); + first_col = false; width = fmt->width (fmt, hpp,hists_to_evsel (hists));fprintf (fp,"%.*s" , width, dots); } depth =0 ; -hists__for_each_format (hists, fmt) { -if (!perf_hpp__is_sort_entry (fmt) && !perf_hpp__is_dynamic_entry (fmt)) -continue ; -if (perf_hpp__should_skip (fmt, hists)) -continue ; +list_for_each_entry_continue (fmt_node, &hists->hpp_formats, list) { + first_col = true; + width = depth *HIERARCHY_INDENT ; - width = fmt->width (fmt, hpp,hists_to_evsel (hists)); - width += depth *HIERARCHY_INDENT ; +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) { +if (perf_hpp__should_skip (fmt, hists)) +continue ; + +if (!first_col) + width++;/* for '+' sign between column header */ + first_col = false; + + width += fmt->width (fmt, hpp,hists_to_evsel (hists)); + }if (width > header_width) header_width = width;
@@ -621,6 +627,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, float min_pcnt, FILE *fp) { struct perf_hpp_fmt *fmt; +struct perf_hpp_list_node *fmt_node;struct rb_node *nd;size_t ret =0 ;unsigned int width;
@@ -650,6 +657,10 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,fprintf (fp,"# " );if (symbol_conf.report_hierarchy) { +list_for_each_entry (fmt_node, &hists->hpp_formats, list) { +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) +perf_hpp__reset_width (fmt, hists); + } nr_rows +=print_hierarchy_header (hists, &dummy_hpp, sep, fp); goto print_entries; }
@@ -734,9 +745,9 @@ print_entries:* display "no entry >= x.xx%" message. */ if (!h->leaf && !hist_entry__has_hierarchy_children (h, min_pcnt)) { -int nr_sort = hists->nr_sort_keys; +int depth = hists->nr_hpp_node + h->depth +1 ; -print_hierarchy_indent (sep, nr_sort + h->depth +1 , spaces, fp); +print_hierarchy_indent (sep, depth, spaces, fp);fprintf (fp,"%*sno entry >= %.2f%%\n" , indent,"" , min_pcnt);if (max_rows && ++nr_rows >= max_rows)
Signature · 1 line
2.7.1
Namhyung Kim
maintainerPATCH 7/8 perf hists browser: Use hierarchy hpp list
In reply to Namhyung KimNow hpp formats are linked using perf_hpp_list_node when hierarchy is enabled. Like in stdio, use this info to print entries with multiple sort keys in a single hierarchy properly.
--- tools/perf/ui/browsers/hists.c | 81 +++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 36 deletions(-)
tools/perf/ui/browsers/hists.c +45 -36
@@ -1289,6 +1289,7 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,off_t row_offset = entry->row_offset;bool first = true;struct perf_hpp_fmt *fmt; +struct perf_hpp_list_node *fmt_node;struct hpp_arg arg = { .b = &browser->b, .current_entry = current_entry,
@@ -1320,7 +1321,10 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,ui_browser__write_nstring (&browser->b,"" , level *HIERARCHY_INDENT ); width -= level *HIERARCHY_INDENT ; -hists__for_each_format (entry->hists, fmt) { +/* the first hpp_list_node is for overhead columns */ + fmt_node =list_first_entry (&entry->hists->hpp_formats, + struct perf_hpp_list_node, list); +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) {char s[2048 ];struct perf_hpp hpp = { .buf = s,
@@ -1332,10 +1336,6 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser, column++ < browser->b.horiz_scroll)continue ; -if (perf_hpp__is_sort_entry (fmt) || -perf_hpp__is_dynamic_entry (fmt)) -break ; -if (current_entry && browser->b.navkeypressed) {ui_browser__set_color (&browser->b,HE_COLORSET_SELECTED );
@@ -1444,6 +1444,7 @@ static int hist_browser__show_no_entry(struct hist_browser *browser,int column =0 ;int ret;struct perf_hpp_fmt *fmt; +struct perf_hpp_list_node *fmt_node;int indent = browser->hists->nr_hpp_node -2 ;if (current_entry) {
@@ -1461,15 +1462,14 @@ static int hist_browser__show_no_entry(struct hist_browser *browser,ui_browser__write_nstring (&browser->b,"" , level *HIERARCHY_INDENT ); width -= level *HIERARCHY_INDENT ; -hists__for_each_format (browser->hists, fmt) { +/* the first hpp_list_node is for overhead columns */ + fmt_node =list_first_entry (&browser->hists->hpp_formats, + struct perf_hpp_list_node, list); +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) {if (perf_hpp__should_skip (fmt, browser->hists) || column++ < browser->b.horiz_scroll)continue ; -if (perf_hpp__is_sort_entry (fmt) || -perf_hpp__is_dynamic_entry (fmt)) -break ; - ret = fmt->width (fmt,NULL ,hists_to_evsel (browser->hists));if (first) {
@@ -1551,22 +1551,23 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows .size = size, };struct perf_hpp_fmt *fmt; +struct perf_hpp_list_node *fmt_node;size_t ret =0 ;int column =0 ;int indent = hists->nr_hpp_node -2 ; -bool first = true; +bool first_node, first_col; ret =scnprintf (buf, size," " );if (advance_hpp_check (&dummy_hpp, ret))return ret; -hists__for_each_format (hists, fmt) { +/* the first hpp_list_node is for overhead columns */ + fmt_node =list_first_entry (&hists->hpp_formats, + struct perf_hpp_list_node, list); +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) {if (column++ < browser->b.horiz_scroll)continue ; -if (perf_hpp__is_sort_entry (fmt) ||perf_hpp__is_dynamic_entry (fmt)) -break ; - ret = fmt->header (fmt, &dummy_hpp,hists_to_evsel (hists));if (advance_hpp_check (&dummy_hpp, ret))break ;
@@ -1581,34 +1582,42 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browsif (advance_hpp_check (&dummy_hpp, ret))return ret; -hists__for_each_format (hists, fmt) { -char *start; - -if (!perf_hpp__is_sort_entry (fmt) && !perf_hpp__is_dynamic_entry (fmt)) -continue ; -if (perf_hpp__should_skip (fmt, hists)) -continue ; - -if (first) { - first = false; - }else { + first_node = true; +list_for_each_entry_continue (fmt_node, &hists->hpp_formats, list) { +if (!first_node) { ret =scnprintf (dummy_hpp.buf, dummy_hpp.size," / " );if (advance_hpp_check (&dummy_hpp, ret))break ; } + first_node = false; - ret = fmt->header (fmt, &dummy_hpp,hists_to_evsel (hists)); - dummy_hpp.buf[ret] ='\0' ; -rtrim (dummy_hpp.buf); + first_col = true; +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) { +char *start; - start =ltrim (dummy_hpp.buf); - ret =strlen (start); +if (perf_hpp__should_skip (fmt, hists)) +continue ; -if (start != dummy_hpp.buf) -memmove (dummy_hpp.buf, start, ret +1 ); +if (!first_col) { + ret =scnprintf (dummy_hpp.buf, dummy_hpp.size,"+" ); +if (advance_hpp_check (&dummy_hpp, ret)) +break ; + } + first_col = false; -if (advance_hpp_check (&dummy_hpp, ret)) -break ; + ret = fmt->header (fmt, &dummy_hpp,hists_to_evsel (hists)); + dummy_hpp.buf[ret] ='\0' ; +rtrim (dummy_hpp.buf); + + start =ltrim (dummy_hpp.buf); + ret =strlen (start); + +if (start != dummy_hpp.buf) +memmove (dummy_hpp.buf, start, ret +1 ); + +if (advance_hpp_check (&dummy_hpp, ret)) +break ; + } }return ret;
@@ -1676,7 +1685,7 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)break ;if (h->has_no_entry) { -hist_browser__show_no_entry (hb, row, h->depth); +hist_browser__show_no_entry (hb, row, h->depth +1 ); row++; } }else {
Signature · 1 line
2.7.1
Namhyung Kim
maintainerPATCH 8/8 perf report: Use hierarchy hpp list on gtk
In reply to Namhyung KimNow hpp formats are linked using perf_hpp_list_node when hierarchy is enabled. Like in stdio, use this info to print entries with multiple sort keys in a single hierarchy properly.
--- tools/perf/ui/gtk/hists.c | 55 ++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 22 deletions(-)
tools/perf/ui/gtk/hists.c +33 -22
@@ -407,7 +407,9 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,struct rb_node *node;struct hist_entry *he;struct perf_hpp_fmt *fmt; +struct perf_hpp_list_node *fmt_node;u64 total =hists__total_period (hists); +int size;for (node =rb_first (root); node; node =rb_next (node)) {GtkTreeIter iter;
@@ -425,11 +427,11 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,gtk_tree_store_append (store, &iter, parent); col_idx =0 ; -hists__for_each_format (hists, fmt) { -if (perf_hpp__is_sort_entry (fmt) || -perf_hpp__is_dynamic_entry (fmt)) -break ; +/* the first hpp_list_node is for overhead columns */ + fmt_node =list_first_entry (&hists->hpp_formats, + struct perf_hpp_list_node, list); +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) {if (fmt->color) fmt->color (fmt, hpp, he);else
@@ -439,6 +441,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists, } bf = hpp->buf; + size = hpp->size;perf_hpp_list__for_each_format (he->hpp_list, fmt) {int ret;
@@ -451,9 +454,12 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,advance_hpp (hpp, ret +2 ); } -gtk_tree_store_set (store, &iter, col_idx,rtrim (bf),-1 ); +gtk_tree_store_set (store, &iter, col_idx,ltrim (rtrim (bf)),-1 );if (!he->leaf) { + hpp->buf = bf; + hpp->size = size; +perf_gtk__add_hierarchy_entries (hists, &he->hroot_out, store, &iter, hpp, min_pcnt);
@@ -486,6 +492,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists, float min_pcnt) {struct perf_hpp_fmt *fmt; +struct perf_hpp_list_node *fmt_node;GType col_types[MAX_COLUMNS ];GtkCellRenderer *renderer;GtkTreeStore *store;
@@ -494,7 +501,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,int nr_cols =0 ;char s[512 ];char buf[512 ]; -bool first = true; +bool first_node, first_col;struct perf_hpp hpp = { .buf = s, .size =sizeof (s),
@@ -514,11 +521,11 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists, renderer =gtk_cell_renderer_text_new (); col_idx =0 ; -hists__for_each_format (hists, fmt) { -if (perf_hpp__is_sort_entry (fmt) || -perf_hpp__is_dynamic_entry (fmt)) -break ; +/* the first hpp_list_node is for overhead columns */ + fmt_node =list_first_entry (&hists->hpp_formats, + struct perf_hpp_list_node, list); +perf_hpp_list__for_each_format (&fmt_node->hpp, fmt) { gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, fmt->name, renderer, "markup",
@@ -527,20 +534,24 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,/* construct merged column header since sort keys share single column */ buf[0 ] ='\0' ; -hists__for_each_format (hists ,fmt) { -if (!perf_hpp__is_sort_entry (fmt) && - !perf_hpp__is_dynamic_entry (fmt)) -continue ; -if (perf_hpp__should_skip (fmt, hists)) -continue ; - -if (first) - first = false; -else + first_node = true; +list_for_each_entry_continue (fmt_node, &hists->hpp_formats, list) { +if (!first_node)strcat (buf," / " ); + first_node = false; - fmt->header (fmt, &hpp,hists_to_evsel (hists)); -strcat (buf,rtrim (hpp.buf)); + first_col = true; +perf_hpp_list__for_each_format (&fmt_node->hpp ,fmt) { +if (perf_hpp__should_skip (fmt, hists)) +continue ; + +if (!first_col) +strcat (buf,"+" ); + first_col = false; + + fmt->header (fmt, &hpp,hists_to_evsel (hists)); +strcat (buf,ltrim (rtrim (hpp.buf))); + } } gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
Signature · 1 line
2.7.1
Arnaldo Carvalho de Melo
maintainerEm Thu, Mar 03, 2016 at 01:12:00AM +0900, Namhyung Kim escreveu:
Quoted 10 lines
Hello,
This implements what Arnaldo suggested in previous discussion of hierarchy patchset [1]. Originally each level in a hierarchy can have a single sort key in it, but with patches it's possible to have more than one sort keys. I added the struct
perf_hpp_list_nodeand carry it to group output formats (hpp_fmt) in a single level.I used ':' character instead of '+' as suggested since the '+' was also used to extend existing sort keys (like -s '+cpu').
Could we use ';' instead? That is associated with lists as well, i.e.:
perf report --hierarchy -s prev_pid;prev_comm,next_pid;next_comm
What do you think? Others?
But anyway, thanks for working on this, I'll try and test it,
- Arnaldo
Quoted 56 lines
Example below shows how 4 sort keys are used for 2 levels. As you can see, the first level shows pid and comm of previous (switched) task and the second level shows pid and comm of next task.
$ perf report --hierarchy -s prev_pid:prev_comm,next_pid:next_comm \ --percent-limit 1 -i perf.data.sched ... # Overhead prev_pid+prev_comm / next_pid+next_comm # ........... ....................................... # 22.36% 0 swapper/0 9.48% 17773 transmission-gt 5.25% 109 kworker/0:1H 1.53% 6524 Xephyr 21.39% 17773 transmission-gt 9.52% 0 swapper/0 9.04% 0 swapper/2 1.78% 0 swapper/3It's available on the 'perf/hierarchy-multi-v1' branch in my tree
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.gitAny feedbacks are welcome
Thanks,
Namhyung[1] https://lkml.org/lkml/2016/2/24/1041
Namhyung Kim (8):
perf tools: Add level field to structperf_hpp_fmtperf tools: Introduceperf_hpp__setup_hists_formats()perf tools: Use ownhpp_listfor hierarchy mode perf tools: Support multiple sort keys in a hierarchy perf tools: Fix indent for multiple hierarchy sort key perf report: Use hierarchy hpp list on stdio perf hists browser: Use hierarchy hpp list perf report: Use hierarchy hpp list on gtktools/perf/ui/browsers/hists.c | 147 +++++++++++++++++++---------------- tools/perf/ui/gtk/hists.c | 73 +++++++++++------- tools/perf/ui/hist.c | 67 ++++++++++++++++ tools/perf/ui/stdio/hist.c | 171 +++++++++++++++++++++-------------------- tools/perf/util/hist.c | 81 +++++++++++++------ tools/perf/util/hist.h | 12 +++ tools/perf/util/sort.c | 123 ++++++++++++++++++++--------- tools/perf/util/sort.h | 1 + 8 files changed, 438 insertions(+), 237 deletions(-)Signature · 1 line
2.7.1
Namhyung Kim
maintainerHi Arnaldo,
Quoted 9 lines · Arnaldo Carvalho de Melo
Em Thu, Mar 03, 2016 at 01:12:00AM +0900, Namhyung Kim escreveu:
Quoted 8 lines
Hello,
This implements what Arnaldo suggested in previous discussion of hierarchy patchset [1]. Originally each level in a hierarchy can have a single sort key in it, but with patches it's possible to have more than one sort keys. I added the struct
perf_hpp_list_nodeand carry it to group output formats (hpp_fmt) in a single level.
Quoted 8 lines
Quoted 2 lines
I used ':' character instead of '+' as suggested since the '+' was also used to extend existing sort keys (like -s '+cpu').
Could we use ';' instead? That is associated with lists as well, i.e.:
perf report --hierarchy -s prev_pid;prev_comm,next_pid;next_commWhat do you think? Others?
The ';' is interpreted by shell first, so it needs to be quoted.
$ perf report -s comm,dso;sym bash: sym: command not found
Thanks,
Namhyung
Quoted 61 lines
But anyway, thanks for working on this, I'll try and test it,
- Arnaldo
Quoted 56 lines
Example below shows how 4 sort keys are used for 2 levels. As you can see, the first level shows pid and comm of previous (switched) task and the second level shows pid and comm of next task.
$ perf report --hierarchy -s prev_pid:prev_comm,next_pid:next_comm \ --percent-limit 1 -i perf.data.sched ... # Overhead prev_pid+prev_comm / next_pid+next_comm # ........... ....................................... # 22.36% 0 swapper/0 9.48% 17773 transmission-gt 5.25% 109 kworker/0:1H 1.53% 6524 Xephyr 21.39% 17773 transmission-gt 9.52% 0 swapper/0 9.04% 0 swapper/2 1.78% 0 swapper/3It's available on the 'perf/hierarchy-multi-v1' branch in my tree
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.gitAny feedbacks are welcome
Thanks,
Namhyung[1] https://lkml.org/lkml/2016/2/24/1041
Namhyung Kim (8):
perf tools: Add level field to structperf_hpp_fmtperf tools: Introduceperf_hpp__setup_hists_formats()perf tools: Use ownhpp_listfor hierarchy mode perf tools: Support multiple sort keys in a hierarchy perf tools: Fix indent for multiple hierarchy sort key perf report: Use hierarchy hpp list on stdio perf hists browser: Use hierarchy hpp list perf report: Use hierarchy hpp list on gtktools/perf/ui/browsers/hists.c | 147 +++++++++++++++++++---------------- tools/perf/ui/gtk/hists.c | 73 +++++++++++------- tools/perf/ui/hist.c | 67 ++++++++++++++++ tools/perf/ui/stdio/hist.c | 171 +++++++++++++++++++++-------------------- tools/perf/util/hist.c | 81 +++++++++++++------ tools/perf/util/hist.h | 12 +++ tools/perf/util/sort.c | 123 ++++++++++++++++++++--------- tools/perf/util/sort.h | 1 + 8 files changed, 438 insertions(+), 237 deletions(-)Signature · 1 line
2.7.1
Arnaldo Carvalho de Melo
maintainerEm Thu, Mar 03, 2016 at 08:16:36AM +0900, Namhyung Kim escreveu:
Quoted 13 lines · Arnaldo Carvalho de Melo
Quoted 11 lines
Em Thu, Mar 03, 2016 at 01:12:00AM +0900, Namhyung Kim escreveu:
Quoted 8 lines
This implements what Arnaldo suggested in previous discussion of hierarchy patchset [1]. Originally each level in a hierarchy can have a single sort key in it, but with patches it's possible to have more than one sort keys. I added the struct
perf_hpp_list_nodeand carry it to group output formats (hpp_fmt) in a single level.I used ':' character instead of '+' as suggested since the '+' was also used to extend existing sort keys (like -s '+cpu').
Could we use ';' instead? That is associated with lists as well, i.e.:
Quoted 8 lines
Quoted 3 lines
perf report --hierarchy -s prev_pid;prev_comm,next_pid;next_commWhat do you think? Others?
The ';' is interpreted by shell first, so it needs to be quoted.
$ perf report -s comm,dso;sym bash: sym: command not found
Right, but this is not some really common operation, so asking for it to be quoted is not such an unreasonable demand, and I continue thinking that ':' should be left for other purposes, ',' and ';' are better suited for expressing lists.
We will/have stumble/d in this more times, like when we added { for groups:
# perf record -e cycles,instructions usleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.017 MB perf.data (4 samples) ]
# perf report --header-only | grep group
#
# perf record -e {cycles,instructions} usleep 1
Workload failed: No such file or directory
# perf record -e '{cycles,instructions}' usleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.017 MB perf.data (4 samples) ]
# perf report --header-only | grep group
# group: {cycles,instructions}
# We should try to use tokens that already have some strong meaning associated to them instead of using those that are more easily available, otherwise our mumbo jumbo will grow even more unwieldly 8-/
In fact I think that in this case we could even make it look more like natural languages and use:
perf report --hierarchy -s 'prev_pid,prev_comm;next_pid,next_comm'
To ask for:
prev_pid prev_comm
next_pid next_comm
- 1234 bash
5678 firefox
+ 8912 hexchat
I.e. if ';' is present, it is the separator for each hierarchy level, with ',' being used for stating the per-hierarchy level fields.
Ingo, what do you think?
- Arnaldo
Namhyung Kim
maintainerHi Arnaldo,
Quoted 55 lines · Arnaldo Carvalho de Melo
Em Thu, Mar 03, 2016 at 08:16:36AM +0900, Namhyung Kim escreveu:
Quoted 13 lines · Arnaldo Carvalho de Melo
Quoted 11 lines
Em Thu, Mar 03, 2016 at 01:12:00AM +0900, Namhyung Kim escreveu:
Quoted 8 lines
This implements what Arnaldo suggested in previous discussion of hierarchy patchset [1]. Originally each level in a hierarchy can have a single sort key in it, but with patches it's possible to have more than one sort keys. I added the struct
perf_hpp_list_nodeand carry it to group output formats (hpp_fmt) in a single level.I used ':' character instead of '+' as suggested since the '+' was also used to extend existing sort keys (like -s '+cpu').
Could we use ';' instead? That is associated with lists as well, i.e.:
Quoted 8 lines
Quoted 3 lines
perf report --hierarchy -s prev_pid;prev_comm,next_pid;next_commWhat do you think? Others?
The ';' is interpreted by shell first, so it needs to be quoted.
$ perf report -s comm,dso;sym bash: sym: command not foundRight, but this is not some really common operation, so asking for it to be quoted is not such an unreasonable demand, and I continue thinking that ':' should be left for other purposes, ',' and ';' are better suited for expressing lists.
We will/have stumble/d in this more times, like when we added { for groups:
# perf record -e cycles,instructions usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.017 MB perf.data (4 samples) ] # perf report --header-only | grep group # # perf record -e {cycles,instructions} usleep 1 Workload failed: No such file or directory # perf record -e '{cycles,instructions}' usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.017 MB perf.data (4 samples) ] # perf report --header-only | grep group # group: {cycles,instructions} #We should try to use tokens that already have some strong meaning associated to them instead of using those that are more easily available, otherwise our mumbo jumbo will grow even more unwieldly 8-/
In fact I think that in this case we could even make it look more like natural languages and use:
perf report --hierarchy -s 'prev_pid,prev_comm;next_pid,next_comm'To ask for:
Quoted 8 lines
prev_pidprev_comm
next_pidnext_comm
- 1234 bash
5678 firefox
+ 8912 hexchatI.e. if ';' is present, it is the separator for each hierarchy level, with ',' being used for stating the per-hierarchy level fields.
If it's not a common operation and we have used '{ }' for the event groups, why not using it here too?
$ perf report --hierarchy -s '{prev_pid,prev_comm},{next_pid,next_comm}'It's more verbose but more intuitive IMHO.
Thanks,
Namhyung
Arnaldo Carvalho de Melo
maintainerEm Fri, Mar 04, 2016 at 08:50:51AM +0900, Namhyung Kim escreveu:
Quoted 21 lines · Arnaldo Carvalho de Melo
Quoted 11 lines
We should try to use tokens that already have some strong meaning associated to them instead of using those that are more easily available, otherwise our mumbo jumbo will grow even more unwieldly 8-/
In fact I think that in this case we could even make it look more like natural languages and use:
perf report --hierarchy -s 'prev_pid,prev_comm;next_pid,next_comm'To ask for:
Quoted 8 lines
prev_pidprev_comm
next_pidnext_comm
- 1234 bash
5678 firefox
+ 8912 hexchatI.e. if ';' is present, it is the separator for each hierarchy level, with ',' being used for stating the per-hierarchy level fields.
Quoted 6 lines
If it's not a common operation and we have used '{ }' for the event groups, why not using it here too?
$ perf report --hierarchy -s '{prev_pid,prev_comm},{next_pid,next_comm}'It's more verbose but more intuitive IMHO.
Right, we would be using {} consistently as a way of grouping entities.
I'm fine with that.
- Arnaldo