perf/core improvements and fixes
Arnaldo Carvalho de Melo
maintainerHi Ingo,
Please consider pulling,
- Arnaldo
The following changes since commit c2b8d8c55c0235e21c563283f634bcfd2ba7bc1e:
Merge tag 'perf-core-for-mingo-2' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core (2016-02-24 08:20:30 +0100)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-core-for-mingo-20160224
for you to fetch changes up to c92fcfde3486fb4b9e476ee5ad5995a62e401cce:
perf top: Add --hierarchy option (2016-02-24 20:21:15 -0300)
----------------------------------------------------------------
perf/core improvements:
User visible:
- Hierarchy histogram mode for 'perf top' and 'perf report', showing multiple levels, one per --sort entry: (Namhyung Kim)
On a mostly idle system:
# perf top --hierarchy -s comm,dso
Then expand some levels and use 'P' to take a snapshot:
# cat perf.hist.0
- 92.32% perf
58.20% perf
22.29% libc-2.22.so
5.97% [kernel]
4.18% libelf-0.165.so
1.69% [unknown]
- 4.71% qemu-system-x86
3.10% [kernel]
1.60% qemu-system-x86_64 (deleted)
+ 2.97% swapper
#- Check availability of memory events in 'perf mem': (Jiri Olsa)
On a Intel Broadwell machine:
# perf mem record -e list ldlat-loads : available ldlat-stores: available #
- Decode data_src values (e.g. perf.data files generated by 'perf mem record') in 'perf script': (Jiri Olsa)
# perf script
perf 693 [1] 4.088652: 1 cpu/mem-loads,ldlat=30/P: ffff88007d0b0f40 68100142 L1 hit|SNP None|TLB L1 or L2 hit|LCK No <SNIP>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Print bpf-output events in 'perf script': (Wang Nan). # perf record -e bpf-output/no-inherit,name=evt/ -e ./test_bpf_output_3.c/map:channel.event=evt/ usleep 1000
# perf script
usleep 4882 21384.532523: evt: ffffffff810e97d1 sys_nanosleep ([kernel.kallsyms])
BPF output: 0000: 52 61 69 73 65 20 61 20 Raise a
0008: 42 50 46 20 65 76 65 6e BPF even
0010: 74 21 00 00 t!..
BPF string: "Raise a BPF event!"
#----------------------------------------------------------------
Jiri Olsa (11):
perf mem record: Check for memory events support
perf mem: Introduce perf_mem_events__name function
perf tools: Introduce perf_mem__tlb_scnprintf function
perf tools: Introduce perf_mem__lvl_scnprintf function
perf tools: Introduce perf_mem__snp_scnprintf function
perf tools: Introduce perf_mem__lck_scnprintf function
perf tools: Change perf_mem__tlb_scnprintf to return nb of displayed bytes perf tools: Change perf_mem__lvl_scnprintf to return nb of displayed bytes perf tools: Change perf_mem__snp_scnprintf to return nb of displayed bytes perf tools: Change perf_mem__lck_scnprintf to return nb of displayed bytes perf script: Display data_src values
Namhyung Kim (18):
perf tools: Add helper functions for some sort keys
perf hists: Basic support of hierarchical report view
perf hists: Resort hist entries with hierarchy
perf hists: Add helper functions for hierarchy mode
perf hists: Introduce hist_entry__filter()
perf hists: Support filtering in hierarchy mode
perf hists: Resort after filtering hierarchy
perf hists: Count number of sort keys
perf ui/stdio: Implement hierarchy output mode
perf ui/stdio: Align column header for hierarchy output
perf hists browser: Count number of hierarchy entries
perf hists browser: Support collapsing/expanding whole entries in hierarchy perf hists browser: Implement hierarchy output
perf hists browser: Align column header in hierarchy mode
perf ui/gtk: Implement hierarchy output mode
perf report: Add --hierarchy option
perf hists: Support decaying in hierarchy mode
perf top: Add --hierarchy option
Wang Nan (2):
perf tools: Make binary data printer code in trace_event public available perf script: Print bpf-output events in 'perf script'
tools/perf/Documentation/perf-report.txt | 3 + tools/perf/Documentation/perf-top.txt | 3 + tools/perf/Documentation/tips.txt | 1 + tools/perf/builtin-mem.c | 22 +- tools/perf/builtin-report.c | 17 ++ tools/perf/builtin-script.c | 113 ++++++- tools/perf/builtin-top.c | 15 + tools/perf/ui/browsers/hists.c | 504 ++++++++++++++++++++++++++++--- tools/perf/ui/gtk/hists.c | 163 +++++++++- tools/perf/ui/hist.c | 3 + tools/perf/ui/stdio/hist.c | 184 ++++++++++- tools/perf/util/ctype.c | 9 + tools/perf/util/debug.c | 75 +++-- tools/perf/util/hist.c | 461 ++++++++++++++++++++++++++-- tools/perf/util/hist.h | 24 ++ tools/perf/util/mem-events.c | 210 ++++++++++++- tools/perf/util/mem-events.h | 13 + tools/perf/util/sort.c | 285 +++++++++-------- tools/perf/util/sort.h | 14 +- tools/perf/util/symbol.h | 3 +- tools/perf/util/util.c | 37 +++ tools/perf/util/util.h | 22 ++ 22 files changed, 1940 insertions(+), 241 deletions(-)
Arnaldo Carvalho de Melo
maintainerPATCH 1/31 perf mem record: Check for memory events support
From: Jiri Olsa <[email protected]>
Check if current kernel support available memory events and display the status within -e list option:
$ perf mem record -e list ldlat-loads : available ldlat-stores : available
--- tools/perf/builtin-mem.c | 20 ++++++++++++++++---- tools/perf/util/mem-events.c | 35 ++++++++++++++++++++++++++++++++--- tools/perf/util/mem-events.h | 3 +++ 3 files changed, 51 insertions(+), 7 deletions(-)
tools/perf/builtin-mem.c +16 -4
@@ -40,10 +40,11 @@ static int parse_record_events(const struct option *opt,for (j =0 ; j <PERF_MEM_EVENTS__MAX ; j++) {struct perf_mem_event *e = &perf_mem_events[j]; -fprintf (stderr,"%-20s%s" , - e->tag, verbose ?"" :"\n" ); -if (verbose) -fprintf (stderr," [%s]\n" , e->name); +fprintf (stderr,"%-13s%-*s%s\n" , + e->tag, + verbose ?25 :0 , + verbose ? e->name :"" , + e->supported ?": available" :"" ); }exit (0 ); }
@@ -92,6 +93,12 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)if (!perf_mem_events[j].record)continue ; +if (!perf_mem_events[j].supported) { +pr_err ("failed: event '%s' not supported\n" , + perf_mem_events[j].name); +return -1 ; + } + rec_argv[i++] ="-e" ; rec_argv[i++] = perf_mem_events[j].name; };
@@ -355,6 +362,11 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) NULL }; +if (perf_mem_events__init ()) { +pr_err ("failed: memory events not supported\n" ); +return -1 ; + } + argc =parse_options_subcommand (argc, argv, mem_options, mem_subcommands, mem_usage,PARSE_OPT_STOP_AT_NON_OPTION );
tools/perf/util/mem-events.c +32 -3
@@ -2,15 +2,20 @@#include <stdlib.h> #include <string.h> #include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <api/fs/fs.h> #include "mem-events.h" #include "debug.h" -#define E (t, n) { .tag = t, .name = n } +#define E (t, n, s) { .tag = t, .name = n, .sysfs_name = s }struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX ] = { -E ("ldlat-loads" ,"cpu/mem-loads,ldlat=30/P" ), -E ("ldlat-stores" ,"cpu/mem-stores/P" ), +E ("ldlat-loads" ,"cpu/mem-loads,ldlat=30/P" ,"mem-loads" ), +E ("ldlat-stores" ,"cpu/mem-stores/P" ,"mem-stores" ), }; +#undef E#undef E
@@ -49,3 +54,27 @@ int perf_mem_events__parse(const char *str)pr_err ("failed: event '%s' not found, use '-e list' to get list of available events\n" , str);return -1 ; } + +int perf_mem_events__init (void ) +{ +const char *mnt =sysfs__mount (); +bool found = false; +int j; + +if (!mnt) +return -ENOENT ; + +for (j =0 ; j <PERF_MEM_EVENTS__MAX ; j++) { +char path[PATH_MAX ]; +struct perf_mem_event *e = &perf_mem_events[j]; +struct stat st; + +scnprintf (path,PATH_MAX ,"%s/devices/cpu/events/%s" , + mnt, e->sysfs_name); + +if (!stat (path, &st)) + e->supported = found = true; + } + +return found ?0 : -ENOENT ; +}
tools/perf/util/mem-events.h +3 -0
@@ -5,8 +5,10 @@struct perf_mem_event {bool record; +bool supported;const char *tag;const char *name; +const char *sysfs_name; };enum {
@@ -18,5 +20,6 @@ enum {extern struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX ];int perf_mem_events__parse (const char *str); +int perf_mem_events__init (void );#endif /* __PERF_MEM_EVENTS_H */
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 2/31 perf mem: Introduce perf_mem_events__name function
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Wrap perf_mem_events[].name into perf_mem_events__name() so we could alter the events name if needed.
This will be handy when changing latency settings for loads event in following patch.
--- tools/perf/builtin-mem.c | 6 +++--- tools/perf/util/mem-events.c | 5 +++++ tools/perf/util/mem-events.h | 1 + 3 files changed, 9 insertions(+), 3 deletions(-)
tools/perf/builtin-mem.c +3 -3
@@ -43,7 +43,7 @@ static int parse_record_events(const struct option *opt,fprintf (stderr,"%-13s%-*s%s\n" , e->tag, verbose ?25 :0 , - verbose ? e->name :"" , + verbose ?perf_mem_events__name (j) :"" , e->supported ?": available" :"" ); }exit (0 );
@@ -95,12 +95,12 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)if (!perf_mem_events[j].supported) {pr_err ("failed: event '%s' not supported\n" , - perf_mem_events[j].name); +perf_mem_events__name (j));return -1 ; } rec_argv[i++] ="-e" ; - rec_argv[i++] = perf_mem_events[j].name; + rec_argv[i++] =perf_mem_events__name (j); };for (j =0 ; j < argc; j++, i++)
tools/perf/util/mem-events.c +5 -0
@@ -19,6 +19,11 @@ struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {#undef E +char *perf_mem_events__name (int i) +{ +return (char *)perf_mem_events[i].name; +} +int perf_mem_events__parse (const char *str) {char *tok, *saveptr =NULL ;
tools/perf/util/mem-events.h +1 -0
@@ -22,4 +22,5 @@ extern struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX];int perf_mem_events__parse (const char *str);int perf_mem_events__init (void ); +char *perf_mem_events__name (int i);#endif /* __PERF_MEM_EVENTS_H */
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 3/31 perf tools: Introduce perf_mem__tlb_scnprintf function
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Move meminfo's tlb display function into mem-events.c object, so it could be reused later from script code.
--- tools/perf/util/mem-events.c | 47 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/mem-events.h | 3 +++ tools/perf/util/sort.c | 44 ++--------------------------------------- 3 files changed, 52 insertions(+), 42 deletions(-)
tools/perf/util/mem-events.c +47 -0
@@ -8,6 +8,7 @@#include <api/fs/fs.h> #include "mem-events.h" #include "debug.h" +#include "symbol.h" #define E (t, n, s) { .tag = t, .name = n, .sysfs_name = s }
@@ -83,3 +84,49 @@ int perf_mem_events__init(void)return found ?0 : -ENOENT ; } + +static const char *const tlb_access[] = { +"N/A" , +"HIT" , +"MISS" , +"L1" , +"L2" , +"Walker" , +"Fault" , +}; + +void perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info) +{ +size_t l =0 , i; +u64 m =PERF_MEM_TLB_NA ; +u64 hit, miss; + + sz -=1 ;/* -1 for null termination */ + out[0 ] ='\0' ; + +if (mem_info) + m = mem_info->data_src.mem_dtlb; + + hit = m &PERF_MEM_TLB_HIT ; + miss = m &PERF_MEM_TLB_MISS ; + +/* already taken care of */ + m &= ~(PERF_MEM_TLB_HIT |PERF_MEM_TLB_MISS ); + +for (i =0 ; m && i <ARRAY_SIZE (tlb_access); i++, m >>=1 ) { +if (!(m &0x1 )) +continue ; +if (l) { +strcat (out," or " ); + l +=4 ; + } +strncat (out, tlb_access[i], sz - l); + l +=strlen (tlb_access[i]); + } +if (*out =='\0' ) +strcpy (out,"N/A" ); +if (hit) +strncat (out," hit" , sz - l); +if (miss) +strncat (out," miss" , sz - l); +}
tools/perf/util/mem-events.h +3 -0
@@ -23,4 +23,7 @@ int perf_mem_events__parse(const char *str);int perf_mem_events__init (void );char *perf_mem_events__name (int i); + +struct mem_info ; +void perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info);#endif /* __PERF_MEM_EVENTS_H */
tools/perf/util/sort.c +2 -42
61 lines not on this page · raw message
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 4/31 perf tools: Introduce perf_mem__lvl_scnprintf function
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Move meminfo's lvl display function into mem-events.c object, so it could be reused later from script code.
--- tools/perf/util/mem-events.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/mem-events.h | 1 + tools/perf/util/sort.c | 50 +---------------------------------------- 3 files changed, 55 insertions(+), 49 deletions(-)
tools/perf/util/mem-events.c +53 -0
@@ -130,3 +130,56 @@ void perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)if (miss)strncat (out," miss" , sz - l); } + +static const char *const mem_lvl[] = { +"N/A" , +"HIT" , +"MISS" , +"L1" , +"LFB" , +"L2" , +"L3" , +"Local RAM" , +"Remote RAM (1 hop)" , +"Remote RAM (2 hops)" , +"Remote Cache (1 hop)" , +"Remote Cache (2 hops)" , +"I/O" , +"Uncached" , +}; + +void perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info) +{ +size_t i, l =0 ; +u64 m =PERF_MEM_LVL_NA ; +u64 hit, miss; + +if (mem_info) + m = mem_info->data_src.mem_lvl; + + sz -=1 ;/* -1 for null termination */ + out[0 ] ='\0' ; + + hit = m &PERF_MEM_LVL_HIT ; + miss = m &PERF_MEM_LVL_MISS ; + +/* already taken care of */ + m &= ~(PERF_MEM_LVL_HIT |PERF_MEM_LVL_MISS ); + +for (i =0 ; m && i <ARRAY_SIZE (mem_lvl); i++, m >>=1 ) { +if (!(m &0x1 )) +continue ; +if (l) { +strcat (out," or " ); + l +=4 ; + } +strncat (out, mem_lvl[i], sz - l); + l +=strlen (mem_lvl[i]); + } +if (*out =='\0' ) +strcpy (out,"N/A" ); +if (hit) +strncat (out," hit" , sz - l); +if (miss) +strncat (out," miss" , sz - l); +}
tools/perf/util/mem-events.h +1 -0
@@ -26,4 +26,5 @@ char *perf_mem_events__name(int i);struct mem_info ;void perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +void perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info);#endif /* __PERF_MEM_EVENTS_H */
tools/perf/util/sort.c +1 -49
61 lines not on this page · raw message
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 5/31 perf tools: Introduce perf_mem__snp_scnprintf function
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Move meminfo's snp display function into mem-events.c object, so it could be reused later from script code.
--- tools/perf/util/mem-events.c | 34 ++++++++++++++++++++++++++++++++++ tools/perf/util/mem-events.h | 1 + tools/perf/util/sort.c | 31 +------------------------------ 3 files changed, 36 insertions(+), 30 deletions(-)
tools/perf/util/mem-events.c +34 -0
@@ -183,3 +183,37 @@ void perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)if (miss)strncat (out," miss" , sz - l); } + +static const char *const snoop_access[] = { +"N/A" , +"None" , +"Miss" , +"Hit" , +"HitM" , +}; + +void perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info) +{ +size_t i, l =0 ; +u64 m =PERF_MEM_SNOOP_NA ; + + sz -=1 ;/* -1 for null termination */ + out[0 ] ='\0' ; + +if (mem_info) + m = mem_info->data_src.mem_snoop; + +for (i =0 ; m && i <ARRAY_SIZE (snoop_access); i++, m >>=1 ) { +if (!(m &0x1 )) +continue ; +if (l) { +strcat (out," or " ); + l +=4 ; + } +strncat (out, snoop_access[i], sz - l); + l +=strlen (snoop_access[i]); + } + +if (*out =='\0' ) +strcpy (out,"N/A" ); +}
tools/perf/util/mem-events.h +1 -0
@@ -27,4 +27,5 @@ char *perf_mem_events__name(int i);struct mem_info ;void perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +void perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info);#endif /* __PERF_MEM_EVENTS_H */
tools/perf/util/sort.c +1 -30
@@ -886,41 +886,12 @@ sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)return (int64_t )(data_src_r.mem_snoop - data_src_l.mem_snoop); } -static const char *const snoop_access[] = { -"N/A" , -"None" , -"Miss" , -"Hit" , -"HitM" , -}; -static int hist_entry__snoop_snprintf (struct hist_entry *he,char *bf,size_t size,unsigned int width) {char out[64 ]; -size_t sz =sizeof (out) -1 ;/* -1 for null termination */ -size_t i, l =0 ; -u64 m =PERF_MEM_SNOOP_NA ; - - out[0 ] ='\0' ; - -if (he->mem_info) - m = he->mem_info->data_src.mem_snoop; - -for (i =0 ; m && i <ARRAY_SIZE (snoop_access); i++, m >>=1 ) { -if (!(m &0x1 )) -continue ; -if (l) { -strcat (out," or " ); - l +=4 ; - } -strncat (out, snoop_access[i], sz - l); - l +=strlen (snoop_access[i]); - } - -if (*out =='\0' ) -strcpy (out,"N/A" ); +perf_mem__snp_scnprintf (out,sizeof (out), he->mem_info);return repsep_snprintf (bf, size,"%-*s" , width, out); }
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 6/31 perf tools: Introduce perf_mem__lck_scnprintf function
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Move meminfo's lck display function into mem-events.c object, so it could be reused later from script code.
--- tools/perf/util/mem-events.c | 16 ++++++++++++++++ tools/perf/util/mem-events.h | 2 ++ tools/perf/util/sort.c | 14 ++------------ 3 files changed, 20 insertions(+), 12 deletions(-)
tools/perf/util/mem-events.c +16 -0
@@ -217,3 +217,19 @@ void perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)if (*out =='\0' )strcpy (out,"N/A" ); } + +void perf_mem__lck_scnprintf (char *out,size_t sz__maybe_unused , +struct mem_info *mem_info) +{ +u64 mask =PERF_MEM_LOCK_NA ; + +if (mem_info) + mask = mem_info->data_src.mem_lock; + +if (mask &PERF_MEM_LOCK_NA ) +strncat (out,"N/A" ,3 ); +else if (mask &PERF_MEM_LOCK_LOCKED ) +strncat (out,"Yes" ,3 ); +else +strncat (out,"No" ,2 ); +}
tools/perf/util/mem-events.h +2 -0
@@ -28,4 +28,6 @@ struct mem_info;void perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +void perf_mem__lck_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +#endif /* __PERF_MEM_EVENTS_H */
tools/perf/util/sort.c +2 -12
@@ -795,19 +795,9 @@ sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)static int hist_entry__locked_snprintf (struct hist_entry *he,char *bf,size_t size,unsigned int width) { -const char *out; -u64 mask =PERF_MEM_LOCK_NA ; - -if (he->mem_info) - mask = he->mem_info->data_src.mem_lock; - -if (mask &PERF_MEM_LOCK_NA ) - out ="N/A" ; -else if (mask &PERF_MEM_LOCK_LOCKED ) - out ="Yes" ; -else - out ="No" ; +char out[10 ]; +perf_mem__lck_scnprintf (out,sizeof (out), he->mem_info);return repsep_snprintf (bf, size,"%.*s" , width, out); }
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 7/31 perf tools: Change perf_mem__tlb_scnprintf to return nb of displayed bytes
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Moving strncat/strcpy calls into scnprintf to easily track number of displayed bytes. It will be used in following patch.
--- tools/perf/util/mem-events.c | 13 +++++++------ tools/perf/util/mem-events.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-)
tools/perf/util/mem-events.c +7 -6
@@ -95,7 +95,7 @@ static const char * const tlb_access[] = { "Fault", }; -void perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info) +int perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info) {size_t l =0 , i;u64 m =PERF_MEM_TLB_NA ;
@@ -120,15 +120,16 @@ void perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)strcat (out," or " ); l +=4 ; } -strncat (out, tlb_access[i], sz - l); - l +=strlen (tlb_access[i]); + l +=scnprintf (out + l, sz - l, tlb_access[i]); }if (*out =='\0' ) -strcpy (out,"N/A" ); + l +=scnprintf (out, sz - l,"N/A" );if (hit) -strncat (out," hit" , sz - l); + l +=scnprintf (out + l, sz - l," hit" );if (miss) -strncat (out," miss" , sz - l); + l +=scnprintf (out + l, sz - l," miss" ); + +return l; }static const char *const mem_lvl[] = {
tools/perf/util/mem-events.h +1 -1
@@ -25,7 +25,7 @@ int perf_mem_events__init(void);char *perf_mem_events__name (int i);struct mem_info ; -void perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +int perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__lck_scnprintf (char *out,size_t sz,struct mem_info *mem_info);
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 8/31 perf tools: Change perf_mem__lvl_scnprintf to return nb of displayed bytes
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Moving strncat/strcpy calls into scnprintf to easily track number of displayed bytes. It will be used in following patch.
--- tools/perf/util/mem-events.c | 13 +++++++------ tools/perf/util/mem-events.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-)
tools/perf/util/mem-events.c +7 -6
@@ -149,7 +149,7 @@ static const char * const mem_lvl[] = { "Uncached", }; -void perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info) +int perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info) {size_t i, l =0 ;u64 m =PERF_MEM_LVL_NA ;
@@ -174,15 +174,16 @@ void perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)strcat (out," or " ); l +=4 ; } -strncat (out, mem_lvl[i], sz - l); - l +=strlen (mem_lvl[i]); + l +=scnprintf (out + l, sz - l, mem_lvl[i]); }if (*out =='\0' ) -strcpy (out,"N/A" ); + l +=scnprintf (out, sz - l,"N/A" );if (hit) -strncat (out," hit" , sz - l); + l +=scnprintf (out + l, sz - l," hit" );if (miss) -strncat (out," miss" , sz - l); + l +=scnprintf (out + l, sz - l," miss" ); + +return l; }static const char *const snoop_access[] = {
tools/perf/util/mem-events.h +1 -1
@@ -26,7 +26,7 @@ char *perf_mem_events__name(int i);struct mem_info ;int perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info); -void perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +int perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__lck_scnprintf (char *out,size_t sz,struct mem_info *mem_info);
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 9/31 perf tools: Change perf_mem__snp_scnprintf to return nb of displayed bytes
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Moving strncat/strcpy calls into scnprintf to easily track number of displayed bytes. It will be used in following patch.
--- tools/perf/util/mem-events.c | 9 +++++---- tools/perf/util/mem-events.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-)
tools/perf/util/mem-events.c +5 -4
@@ -194,7 +194,7 @@ static const char * const snoop_access[] = { "HitM", }; -void perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info) +int perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info) {size_t i, l =0 ;u64 m =PERF_MEM_SNOOP_NA ;
@@ -212,12 +212,13 @@ void perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)strcat (out," or " ); l +=4 ; } -strncat (out, snoop_access[i], sz - l); - l +=strlen (snoop_access[i]); + l +=scnprintf (out + l, sz - l, snoop_access[i]); }if (*out =='\0' ) -strcpy (out,"N/A" ); + l +=scnprintf (out, sz - l,"N/A" ); + +return l; } void perf_mem__lck_scnprintf(char *out, size_t sz __maybe_unused,
tools/perf/util/mem-events.h +1 -1
@@ -27,7 +27,7 @@ char *perf_mem_events__name(int i);struct mem_info ;int perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info);int perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info); -void perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +int perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info);void perf_mem__lck_scnprintf (char *out,size_t sz,struct mem_info *mem_info);#endif /* __PERF_MEM_EVENTS_H */
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 10/31 perf tools: Change perf_mem__lck_scnprintf to return nb of displayed bytes
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Moving strncat call into scnprintf to easily track number of displayed bytes. It will be used in following patch.
--- tools/perf/util/mem-events.c | 12 +++++++----- tools/perf/util/mem-events.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-)
tools/perf/util/mem-events.c +7 -5
@@ -221,18 +221,20 @@ int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)return l; } -void perf_mem__lck_scnprintf (char *out,size_t sz__maybe_unused , -struct mem_info *mem_info) +int perf_mem__lck_scnprintf (char *out,size_t sz,struct mem_info *mem_info) {u64 mask =PERF_MEM_LOCK_NA ; +int l;if (mem_info) mask = mem_info->data_src.mem_lock;if (mask &PERF_MEM_LOCK_NA ) -strncat (out,"N/A" ,3 ); + l =scnprintf (out, sz,"N/A" );else if (mask &PERF_MEM_LOCK_LOCKED ) -strncat (out,"Yes" ,3 ); + l =scnprintf (out, sz,"Yes" );else -strncat (out,"No" ,2 ); + l =scnprintf (out, sz,"No" ); + +return l; }
tools/perf/util/mem-events.h +1 -1
@@ -28,6 +28,6 @@ struct mem_info;int perf_mem__tlb_scnprintf (char *out,size_t sz,struct mem_info *mem_info);int perf_mem__lvl_scnprintf (char *out,size_t sz,struct mem_info *mem_info);int perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info); -void perf_mem__lck_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +int perf_mem__lck_scnprintf (char *out,size_t sz,struct mem_info *mem_info);#endif /* __PERF_MEM_EVENTS_H */
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 11/31 perf script: Display data_src values
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Adding support to display data_src values, for events with data_src data in sample.
Example:
$ perf script
...
rcuos/3 32 [002] ... 68501042 Local RAM hit|SNP None or Hit|TLB L1 or L2 hit|LCK No ...
rcuos/3 32 [002] ... 68100142 L1 hit|SNP None|TLB L1 or L2 hit|LCK No ...
swapper 0 [002] ... 68100242 LFB hit|SNP None|TLB L1 or L2 hit|LCK No ...
swapper 0 [000] ... 68100142 L1 hit|SNP None|TLB L1 or L2 hit|LCK No ...
swapper 0 [000] ... 50100142 L1 hit|SNP None|TLB L2 miss|LCK No ...
rcuos/3 32 [002] ... 68100142 L1 hit|SNP None|TLB L1 or L2 hit|LCK No ...
plugin-containe 16538 [000] ... 6a100142 L1 hit|SNP None|TLB L1 or L2 hit|LCK Yes ...
gkrellm 1736 [000] ... 68100242 LFB hit|SNP None|TLB L1 or L2 hit|LCK No ...
gkrellm 1736 [000] ... 6a100142 L1 hit|SNP None|TLB L1 or L2 hit|LCK Yes ... ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
data_src value data_src translation
--- tools/perf/builtin-script.c | 20 +++++++++++++++++++- tools/perf/util/mem-events.c | 15 +++++++++++++++ tools/perf/util/mem-events.h | 2 ++ 3 files changed, 36 insertions(+), 1 deletion(-)
tools/perf/builtin-script.c +19 -1
@@ -23,6 +23,7 @@#include "util/stat.h" #include <linux/bitmap.h> #include "asm/bug.h" +#include "util/mem-events.h" static char const *script_name;static char const *generate_script_lang;
@@ -649,6 +650,23 @@ static int perf_evlist__max_name_len(struct perf_evlist *evlist)return max; } +static size_t data_src__printf (u64 data_src) +{ +struct mem_info mi = { .data_src.val = data_src }; +char decode[100 ]; +char out[100 ]; +static int maxlen; +int len; + +perf_script__meminfo_scnprintf (decode,100 , &mi); + + len =scnprintf (out,100 ,"%16" PRIx64" %s" , data_src, decode); +if (maxlen < len) + maxlen = len; + +return printf ("%-*s" , maxlen, out); +} +static void process_event (struct perf_script *script,union perf_event *event,struct perf_sample *sample,struct perf_evsel *evsel,struct addr_location *al)
@@ -689,7 +707,7 @@ static void process_event(struct perf_script *script, union perf_event *event,print_sample_addr (event, sample, thread, attr);if (PRINT_FIELD (DATA_SRC )) -printf ("%16" PRIx64, sample->data_src); +data_src__printf (sample->data_src);if (PRINT_FIELD (WEIGHT ))printf ("%16" PRIu64, sample->weight);
tools/perf/util/mem-events.c +15 -0
@@ -238,3 +238,18 @@ int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)return l; } + +int perf_script__meminfo_scnprintf (char *out,size_t sz,struct mem_info *mem_info) +{ +int i =0 ; + + i +=perf_mem__lvl_scnprintf (out, sz, mem_info); + i +=scnprintf (out + i, sz - i,"|SNP " ); + i +=perf_mem__snp_scnprintf (out + i, sz - i, mem_info); + i +=scnprintf (out + i, sz - i,"|TLB " ); + i +=perf_mem__tlb_scnprintf (out + i, sz - i, mem_info); + i +=scnprintf (out + i, sz - i,"|LCK " ); + i +=perf_mem__lck_scnprintf (out + i, sz - i, mem_info); + +return i; +}
tools/perf/util/mem-events.h +2 -0
@@ -30,4 +30,6 @@ int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info);int perf_mem__snp_scnprintf (char *out,size_t sz,struct mem_info *mem_info);int perf_mem__lck_scnprintf (char *out,size_t sz,struct mem_info *mem_info); +int perf_script__meminfo_scnprintf (char *bf,size_t size,struct mem_info *mem_info); +#endif /* __PERF_MEM_EVENTS_H */
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 12/31 perf tools: Make binary data printer code in trace_event public available
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
Move code printing binray data from trace_event() to utils.c and allows passing different printer. Further commits will use this logic to print bpf output event.
--- tools/perf/util/debug.c | 75 +++++++++++++++++++++++++++++++------------------ tools/perf/util/util.c | 37 ++++++++++++++++++++++++ tools/perf/util/util.h | 20 +++++++++++++ 3 files changed, 105 insertions(+), 27 deletions(-)
tools/perf/util/debug.c +48 -27
@@ -106,40 +106,61 @@ int dump_printf(const char *fmt, ...)return ret; } +static void trace_event_printer (enum binary_printer_ops op, +unsigned int val,void *extra) +{ +const char *color =PERF_COLOR_BLUE ; +union perf_event *event = (union perf_event *)extra; +unsigned char ch = (unsigned char )val; + +switch (op) { +case BINARY_PRINT_DATA_BEGIN : +printf ("." ); +color_fprintf (stdout, color,"\n. ... raw event: size %d bytes\n" , + event->header.size); +break ; +case BINARY_PRINT_LINE_BEGIN : +printf ("." ); +break ; +case BINARY_PRINT_ADDR : +color_fprintf (stdout, color," %04x: " , val); +break ; +case BINARY_PRINT_NUM_DATA : +color_fprintf (stdout, color," %02x" , val); +break ; +case BINARY_PRINT_NUM_PAD : +color_fprintf (stdout, color," " ); +break ; +case BINARY_PRINT_SEP : +color_fprintf (stdout, color," " ); +break ; +case BINARY_PRINT_CHAR_DATA : +color_fprintf (stdout, color,"%c" , +isprint (ch) ? ch :'.' ); +break ; +case BINARY_PRINT_CHAR_PAD : +color_fprintf (stdout, color," " ); +break ; +case BINARY_PRINT_LINE_END : +color_fprintf (stdout, color,"\n" ); +break ; +case BINARY_PRINT_DATA_END : +printf ("\n" ); +break ; +default : +break ; + } +} +void trace_event (union perf_event *event) {unsigned char *raw_event = (void *)event; -const char *color =PERF_COLOR_BLUE ; -int i, j;if (!dump_trace)return ; -printf ("." ); -color_fprintf (stdout, color,"\n. ... raw event: size %d bytes\n" , - event->header.size); - -for (i =0 ; i < event->header.size; i++) { -if ((i &15 ) ==0 ) { -printf ("." ); -color_fprintf (stdout, color," %04x: " , i); - } - -color_fprintf (stdout, color," %02x" , raw_event[i]); - -if (((i &15 ) ==15 ) || i == event->header.size-1 ) { -color_fprintf (stdout, color," " ); -for (j =0 ; j <15 -(i &15 ); j++) -color_fprintf (stdout, color," " ); -for (j = i & ~15 ; j <= i; j++) { -color_fprintf (stdout, color,"%c" , -isprint (raw_event[j]) ? - raw_event[j] :'.' ); - } -color_fprintf (stdout, color,"\n" ); - } - } -printf (".\n" ); +print_binary (raw_event, event->header.size,16 , + trace_event_printer, event); } static struct debug_variable {
tools/perf/util/util.c +37 -0
@@ -14,6 +14,7 @@#include <limits.h> #include <byteswap.h> #include <linux/kernel.h> +#include <linux/log2.h> #include <unistd.h> #include "callchain.h" #include "strlist.h"
@@ -670,3 +671,39 @@ int fetch_current_timestamp(char *buf, size_t sz)return 0 ; } + +void print_binary (unsigned char *data,size_t len, +size_t bytes_per_line,print_binary_t printer, +void *extra) +{ +size_t i, j, mask; + +if (!printer) +return ; + + bytes_per_line =roundup_pow_of_two (bytes_per_line); + mask = bytes_per_line -1 ; + +printer (BINARY_PRINT_DATA_BEGIN ,0 , extra); +for (i =0 ; i < len; i++) { +if ((i & mask) ==0 ) { +printer (BINARY_PRINT_LINE_BEGIN ,-1 , extra); +printer (BINARY_PRINT_ADDR , i, extra); + } + +printer (BINARY_PRINT_NUM_DATA , data[i], extra); + +if (((i & mask) == mask) || i == len -1 ) { +for (j =0 ; j < mask-(i & mask); j++) +printer (BINARY_PRINT_NUM_PAD ,-1 , extra); + +printer (BINARY_PRINT_SEP , i, extra); +for (j = i & ~mask; j <= i; j++) +printer (BINARY_PRINT_CHAR_DATA , data[j], extra); +for (j =0 ; j < mask-(i & mask); j++) +printer (BINARY_PRINT_CHAR_PAD , i, extra); +printer (BINARY_PRINT_LINE_END ,-1 , extra); + } + } +printer (BINARY_PRINT_DATA_END ,-1 , extra); +}
tools/perf/util/util.h +20 -0
@@ -345,4 +345,24 @@ const char *perf_tip(const char *dirpath);bool is_regular_file (const char *file);int fetch_current_timestamp (char *buf,size_t sz); +enum binary_printer_ops { +BINARY_PRINT_DATA_BEGIN , +BINARY_PRINT_LINE_BEGIN , +BINARY_PRINT_ADDR , +BINARY_PRINT_NUM_DATA , +BINARY_PRINT_NUM_PAD , +BINARY_PRINT_SEP , +BINARY_PRINT_CHAR_DATA , +BINARY_PRINT_CHAR_PAD , +BINARY_PRINT_LINE_END , +BINARY_PRINT_DATA_END , +}; + +typedef void (*print_binary_t )(enum binary_printer_ops , +unsigned int val, +void *extra); + +void print_binary (unsigned char *data,size_t len, +size_t bytes_per_line,print_binary_t printer, +void *extra);#endif /* GIT_COMPAT_UTIL_H */
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 13/31 perf script: Print bpf-output events in 'perf script'
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch allows 'perf script' output messages from BPF program. For example, use test_bpf_output_3.c at the end of this commit message,
# ./perf record -e bpf-output/no-inherit,name=evt/ \
-e ./test_bpf_output_3.c/map:channel.event=evt/ \
usleep 100000 # ./perf script
usleep 4882 21384.532523: evt: ffffffff810e97d1 sys_nanosleep ([kernel.kallsyms])
BPF output: 0000: 52 61 69 73 65 20 61 20 Raise a
0008: 42 50 46 20 65 76 65 6e BPF even
0010: 74 21 00 00 t!..
BPF string: "Raise a BPF event!" usleep 4882 21384.632606: evt: ffffffff8105c609 kretprobe_trampoline_holder ([kernel.kallsyms
BPF output: 0000: 52 61 69 73 65 20 61 20 Raise a
0008: 42 50 46 20 65 76 65 6e BPF even
0010: 74 21 00 00 t!..
BPF string: "Raise a BPF event!"Two samples from BPF output are printed by both binary and string format.
If BPF program output something unprintable, string format is suppressed.
/************************ BEGIN **************************/
#include <uapi/linux/bpf.h>
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
#define SEC(NAME) __attribute__((section(NAME), used))
static u64 (*ktime_get_ns)(void) =
(void *)BPF_FUNC_ktime_get_ns;
static int (*trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)BPF_FUNC_trace_printk;
static int (*get_smp_processor_id)(void) =
(void *)BPF_FUNC_get_smp_processor_id;
static int (*perf_event_output)(void *, struct bpf_map_def *, int, void *, unsigned long) =
(void *)BPF_FUNC_perf_event_output;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(u32),
.max_entries = __NR_CPUS__,
}; static inline int __attribute__((always_inline)) func(void *ctx, int type)
{
char output_str[] = "Raise a BPF event!";
perf_event_output(ctx, &channel, get_smp_processor_id(),
&output_str, sizeof(output_str));
return 0; }
SEC("func_begin=sys_nanosleep")
int func_begin(void *ctx) {return func(ctx, 1);} SEC("func_end=sys_nanosleep%return")
int func_end(void *ctx) { return func(ctx, 2);} char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE; /************************* END ***************************/
--- tools/perf/builtin-script.c | 93 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 5 deletions(-)
tools/perf/builtin-script.c +88 -5
@@ -61,6 +61,7 @@ enum perf_output_field {PERF_OUTPUT_BRSTACKSYM =1U <<16 ,PERF_OUTPUT_DATA_SRC =1U <<17 ,PERF_OUTPUT_WEIGHT =1U <<18 , + PERF_OUTPUT_BPF_OUTPUT = 1U << 19, }; struct output_option {
@@ -86,6 +87,7 @@ struct output_option { {.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM}, {.str = "data_src", .field = PERF_OUTPUT_DATA_SRC}, {.str = "weight", .field = PERF_OUTPUT_WEIGHT}, + {.str = "bpf-output", .field = PERF_OUTPUT_BPF_OUTPUT}, };/* default set to maintain compatibility with current format */
@@ -106,7 +108,7 @@ static struct {PERF_OUTPUT_SYM |PERF_OUTPUT_DSO | PERF_OUTPUT_PERIOD, - .invalid_fields = PERF_OUTPUT_TRACE, + .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, }, [PERF_TYPE_SOFTWARE] = {
@@ -116,7 +118,7 @@ static struct {PERF_OUTPUT_CPU |PERF_OUTPUT_TIME |PERF_OUTPUT_EVNAME |PERF_OUTPUT_IP |PERF_OUTPUT_SYM |PERF_OUTPUT_DSO | - PERF_OUTPUT_PERIOD, + PERF_OUTPUT_PERIOD | PERF_OUTPUT_BPF_OUTPUT, .invalid_fields = PERF_OUTPUT_TRACE, },
@@ -126,7 +128,7 @@ static struct { .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |PERF_OUTPUT_CPU |PERF_OUTPUT_TIME | - PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, +PERF_OUTPUT_EVNAME |PERF_OUTPUT_TRACE }, [PERF_TYPE_RAW] = {
@@ -139,7 +141,7 @@ static struct {PERF_OUTPUT_PERIOD |PERF_OUTPUT_ADDR | PERF_OUTPUT_DATA_SRC | PERF_OUTPUT_WEIGHT, - .invalid_fields = PERF_OUTPUT_TRACE, + .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, }, [PERF_TYPE_BREAKPOINT] = {
@@ -151,7 +153,7 @@ static struct {PERF_OUTPUT_SYM |PERF_OUTPUT_DSO | PERF_OUTPUT_PERIOD, - .invalid_fields = PERF_OUTPUT_TRACE, + .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, }, };
@@ -624,6 +626,84 @@ static void print_sample_flags(u32 flags)printf (" %-4s " , str); } +struct printer_data { +int line_no; +bool hit_nul; +bool is_printable; +}; + +static void +print_sample_bpf_output_printer (enum binary_printer_ops op, +unsigned int val, +void *extra) +{ +unsigned char ch = (unsigned char )val; +struct printer_data *printer_data = extra; + +switch (op) { +case BINARY_PRINT_DATA_BEGIN : +printf ("\n" ); +break ; +case BINARY_PRINT_LINE_BEGIN : +printf ("%17s" , !printer_data->line_no ?"BPF output:" : +" " ); +break ; +case BINARY_PRINT_ADDR : +printf (" %04x:" , val); +break ; +case BINARY_PRINT_NUM_DATA : +printf (" %02x" , val); +break ; +case BINARY_PRINT_NUM_PAD : +printf (" " ); +break ; +case BINARY_PRINT_SEP : +printf (" " ); +break ; +case BINARY_PRINT_CHAR_DATA : +if (printer_data->hit_nul && ch) + printer_data->is_printable = false; + +if (!isprint (ch)) { +printf ("%c" ,'.' ); + +if (!printer_data->is_printable) +break ; + +if (ch =='\0' ) + printer_data->hit_nul = true; +else + printer_data->is_printable = false; + }else { +printf ("%c" , ch); + } +break ; +case BINARY_PRINT_CHAR_PAD : +printf (" " ); +break ; +case BINARY_PRINT_LINE_END : +printf ("\n" ); + printer_data->line_no++; +break ; +case BINARY_PRINT_DATA_END : +default : +break ; + } +} + +static void print_sample_bpf_output (struct perf_sample *sample) +{ +unsigned int nr_bytes = sample->raw_size; +struct printer_data printer_data = {0 , false, true}; + +print_binary (sample->raw_data, nr_bytes,8 , + print_sample_bpf_output_printer, &printer_data); + +if (printer_data.is_printable && printer_data.hit_nul) +printf ("%17s \"%s\"\n" ,"BPF string:" , + (char *)(sample->raw_data)); +} +struct perf_script {struct perf_tool tool;struct perf_session *session;
@@ -731,6 +811,9 @@ static void process_event(struct perf_script *script, union perf_event *event,else if (PRINT_FIELD (BRSTACKSYM ))print_sample_brstacksym (event, sample, thread, attr); +if (perf_evsel__is_bpf_output (evsel) &&PRINT_FIELD (BPF_OUTPUT )) +print_sample_bpf_output (sample); +printf ("\n" ); }
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 14/31 perf tools: Add helper functions for some sort keys
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The 'trace', 'srcline' and 'srcfile' sort keys updates hist entry's field later. With the hierarchy mode, those fields are passed to a matching entry so it needs to identify the sort keys.
--- tools/perf/util/hist.h | 3 +++ tools/perf/util/sort.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+)
tools/perf/util/hist.h +3 -0
@@ -301,6 +301,9 @@ void perf_hpp__append_sort_keys(struct perf_hpp_list *list);bool perf_hpp__is_sort_entry (struct perf_hpp_fmt *format);bool perf_hpp__is_dynamic_entry (struct perf_hpp_fmt *format);bool perf_hpp__defined_dynamic_entry (struct perf_hpp_fmt *fmt,struct hists *hists); +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);static inline bool perf_hpp__should_skip (struct perf_hpp_fmt *format,struct hists *hists)
tools/perf/util/sort.c +33 -0
@@ -1391,6 +1391,39 @@ bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)return format->header == __sort__hpp_header; } +bool perf_hpp__is_trace_entry (struct perf_hpp_fmt *fmt) +{ +struct hpp_sort_entry *hse; + +if (!perf_hpp__is_sort_entry (fmt)) +return false; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); +return hse->se == &sort_trace; +} + +bool perf_hpp__is_srcline_entry (struct perf_hpp_fmt *fmt) +{ +struct hpp_sort_entry *hse; + +if (!perf_hpp__is_sort_entry (fmt)) +return false; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); +return hse->se == &sort_srcline; +} + +bool perf_hpp__is_srcfile_entry (struct perf_hpp_fmt *fmt) +{ +struct hpp_sort_entry *hse; + +if (!perf_hpp__is_sort_entry (fmt)) +return false; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); +return hse->se == &sort_srcfile; +} +static bool __sort__hpp_equal (struct perf_hpp_fmt *a,struct perf_hpp_fmt *b) {struct hpp_sort_entry *hse_a;
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 15/31 perf hists: Basic support of hierarchical report view
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
In the hierarchical view, entries will be grouped and sorted on the first key, and then on the second key, and so on. Add the he->hroot_{in,out} fields to keep the lower level entries. Actually this can share space, in a union, with callchain's 'sorted_root' since the hroots are only used by non-leaf entries and callchain is only used by leaf entries.
It also adds the 'parent_he' and 'depth' fields which can be used by browsers.
This patch only implements collapsing part which creates internal entries for each sort key. These need to be sorted by output_sort stage and to be displayed properly in the later patch(es).
--- tools/perf/util/hist.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/sort.h | 13 +++++- tools/perf/util/symbol.h | 3 +- 3 files changed, 128 insertions(+), 2 deletions(-)
tools/perf/util/hist.c +114 -0
@@ -396,6 +396,9 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, }INIT_LIST_HEAD (&he->pairs.node);thread__get (he->thread); + +if (!symbol_conf.report_hierarchy) + he->leaf = true; }return he;
@@ -1049,6 +1052,114 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp,* collapse the histogram */ +static void hists__apply_filters (struct hists *hists,struct hist_entry *he); + +static struct hist_entry *hierarchy_insert_entry (struct hists *hists, +struct rb_root *root, +struct hist_entry *he, +struct perf_hpp_fmt *fmt) +{ +struct rb_node **p = &root->rb_node; +struct rb_node *parent =NULL ; +struct hist_entry *iter, *new; +int64_t cmp; + +while (*p !=NULL ) { + parent = *p; + iter = rb_entry(parent, struct hist_entry, rb_node_in); + + cmp = fmt->collapse (fmt, iter, he); +if (!cmp) { +he_stat__add_stat (&iter->stat, &he->stat); +return iter; + } + +if (cmp <0 ) + p = &parent->rb_left; +else + p = &parent->rb_right; + } + + new =hist_entry__new (he, true); +if (new ==NULL ) +return NULL ; + +hists__apply_filters (hists, new); + hists->nr_entries++; + +/* save related format for output */ + new->fmt = fmt; + +/* some fields are now passed to 'new' */ +if (perf_hpp__is_trace_entry (fmt)) + he->trace_output =NULL ; +else + new->trace_output =NULL ; + +if (perf_hpp__is_srcline_entry (fmt)) + he->srcline =NULL ; +else + new->srcline =NULL ; + +if (perf_hpp__is_srcfile_entry (fmt)) + he->srcfile =NULL ; +else + new->srcfile =NULL ; + +rb_link_node (&new->rb_node_in, parent, p); +rb_insert_color (&new->rb_node_in, root); +return new; +} + +static int hists__hierarchy_insert_entry (struct hists *hists, +struct rb_root *root, +struct hist_entry *he) +{ +struct perf_hpp_fmt *fmt; +struct hist_entry *new_he =NULL ; +struct hist_entry *parent =NULL ; +int depth =0 ; +int ret =0 ; + +hists__for_each_sort_list (hists, fmt) { +if (!perf_hpp__is_sort_entry (fmt) && + !perf_hpp__is_dynamic_entry (fmt)) +continue ; +if (perf_hpp__should_skip (fmt, hists)) +continue ; + +/* insert copy of 'he' for each fmt into the hierarchy */ + new_he =hierarchy_insert_entry (hists, root, he, fmt); +if (new_he ==NULL ) { + ret =-1 ; +break ; + } + + root = &new_he->hroot_in; + new_he->parent_he = parent; + new_he->depth = depth++; + parent = new_he; + } + +if (new_he) { + new_he->leaf = true; + +if (symbol_conf.use_callchain) { +callchain_cursor_reset (&callchain_cursor); +if (callchain_merge (&callchain_cursor, + new_he->callchain, + he->callchain) <0 ) + ret =-1 ; + } + } + +/* 'he' is no longer used */ +hist_entry__delete (he); + +/* return 0 (or -1) since it already applied filters */ +return ret; +} +int hists__collapse_insert_entry (struct hists *hists,struct rb_root *root,struct hist_entry *he) {
@@ -1057,6 +1168,9 @@ int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root,struct hist_entry *iter;int64_t cmp; +if (symbol_conf.report_hierarchy) +return hists__hierarchy_insert_entry (hists, root, he); +while (*p !=NULL ) { parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node_in);
tools/perf/util/sort.h +12 -1
@@ -96,9 +96,11 @@ struct hist_entry {s32 socket;s32 cpu;u8 cpumode; +u8 depth;/* We are added by hists__add_dummy_entry. */ bool dummy; +bool leaf;char level;u8 filtered;
@@ -120,13 +122,22 @@ struct hist_entry {char *srcline;char *srcfile;struct symbol *parent; -struct rb_root sorted_chain;struct branch_info *branch_info;struct hists *hists;struct mem_info *mem_info;void *raw_data;u32 raw_size;void *trace_output; +struct perf_hpp_fmt *fmt; +struct hist_entry *parent_he; +union { +/* this is for hierarchical entry structure */ +struct { +struct rb_root hroot_in; +struct rb_root hroot_out; + };/* non-leaf entries */ +struct rb_root sorted_chain;/* leaf entry has callchains */ + };struct callchain_root callchain[0 ];/* must be last member */ };
tools/perf/util/symbol.h +2 -1
@@ -110,7 +110,8 @@ struct symbol_conf { has_filter, show_ref_callgraph, hide_unresolved, - raw_trace; + raw_trace, + report_hierarchy;const char *vmlinux_name, *kallsyms_name, *source_prefix,
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 16/31 perf hists: Resort hist entries with hierarchy
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
For hierarchical output, each entry must be sorted in their rbtree (hroot) properly. Add hists__hierarchy_output_resort() to do the job. Note that those hierarchy entries share the period counts, it'd be important to update the hists->stats only once (for leaves).
--- tools/perf/util/hist.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-)
tools/perf/util/hist.c +91 -3
@@ -1318,6 +1318,86 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h) hists->stats.total_period += h->stat.period; } +static void hierarchy_insert_output_entry (struct rb_root *root, +struct hist_entry *he) +{ +struct rb_node **p = &root->rb_node; +struct rb_node *parent =NULL ; +struct hist_entry *iter; + +while (*p !=NULL ) { + parent = *p; + iter = rb_entry(parent, struct hist_entry, rb_node); + +if (hist_entry__sort (he, iter) >0 ) + p = &parent->rb_left; +else + p = &parent->rb_right; + } + +rb_link_node (&he->rb_node, parent, p); +rb_insert_color (&he->rb_node, root); +} + +static void hists__hierarchy_output_resort (struct hists *hists, +struct ui_progress *prog, +struct rb_root *root_in, +struct rb_root *root_out, +u64 min_callchain_hits, +bool use_callchain) +{ +struct rb_node *node; +struct hist_entry *he; + + *root_out =RB_ROOT ; + node =rb_first (root_in); + +while (node) { + he = rb_entry(node, struct hist_entry, rb_node_in); + node =rb_next (node); + +hierarchy_insert_output_entry (root_out, he); + +if (prog) +ui_progress__update (prog,1 ); + +if (!he->leaf) { +hists__hierarchy_output_resort (hists, prog, + &he->hroot_in, + &he->hroot_out, + min_callchain_hits, + use_callchain); + hists->nr_entries++; +if (!he->filtered) { + hists->nr_non_filtered_entries++; +hists__calc_col_len (hists, he); + } + +continue ; + } + +/* only update stat for leaf entries to avoid duplication */ +hists__inc_stats (hists, he); +if (!he->filtered) +hists__calc_col_len (hists, he); + +if (!use_callchain) +continue ; + +if (callchain_param.mode ==CHAIN_GRAPH_REL ) { +u64 total = he->stat.period; + +if (symbol_conf.cumulate_callchain) + total = he->stat_acc->period; + + min_callchain_hits = total * (callchain_param.min_percent /100 ); + } + + callchain_param.sort (&he->sorted_chain, he->callchain, + min_callchain_hits, &callchain_param); + } +} + static void __hists__insert_output_entry(struct rb_root *entries, struct hist_entry *he, u64 min_callchain_hits,
@@ -1369,6 +1449,17 @@ static void output_resort(struct hists *hists, struct ui_progress *prog, min_callchain_hits = callchain_total * (callchain_param.min_percent /100 ); +hists__reset_stats (hists); +hists__reset_col_len (hists); + +if (symbol_conf.report_hierarchy) { +return hists__hierarchy_output_resort (hists, prog, + &hists->entries_collapsed, + &hists->entries, + min_callchain_hits, + use_callchain); + } +if (sort__need_collapse) root = &hists->entries_collapsed;else
@@ -1377,9 +1468,6 @@ static void output_resort(struct hists *hists, struct ui_progress *prog, next =rb_first (root); hists->entries =RB_ROOT ; -hists__reset_stats (hists); -hists__reset_col_len (hists); -while (next) { n = rb_entry(next, struct hist_entry, rb_node_in); next =rb_next (&n->rb_node_in);
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 17/31 perf hists: Add helper functions for hierarchy mode
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The rb_hierarchy_{next,prev,last} functions are to traverse all hist entries in a hierarchy. They will be used by various function which supports hierarchy output.
As the rb_hierarchy_next() is used to traverse the whole hierarchy, it sometime needs to visit entries regardless of current folding state. So add enum hierarchy_move_dir and pass it to __rb_hierarchy_next() for those cases.
--- tools/perf/util/hist.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/hist.h | 16 +++++++++++++++ 2 files changed, 72 insertions(+)
tools/perf/util/hist.c +56 -0
@@ -1500,6 +1500,62 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog)output_resort (hists, prog, symbol_conf.use_callchain); } +static bool can_goto_child (struct hist_entry *he,enum hierarchy_move_dir hmd) +{ +if (he->leaf || hmd ==HMD_FORCE_SIBLING ) +return false; + +if (he->unfolded || hmd ==HMD_FORCE_CHILD ) +return true; + +return false; +} + +struct rb_node *rb_hierarchy_last (struct rb_node *node) +{ + struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); + +while (can_goto_child (he,HMD_NORMAL )) { + node =rb_last (&he->hroot_out); + he = rb_entry(node, struct hist_entry, rb_node); + } +return node; +} + +struct rb_node *__rb_hierarchy_next (struct rb_node *node,enum hierarchy_move_dir hmd) +{ + struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); + +if (can_goto_child (he, hmd)) + node =rb_first (&he->hroot_out); +else + node =rb_next (node); + +while (node ==NULL ) { + he = he->parent_he; +if (he ==NULL ) +break ; + + node =rb_next (&he->rb_node); + } +return node; +} + +struct rb_node *rb_hierarchy_prev (struct rb_node *node) +{ + struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); + + node =rb_prev (node); +if (node) +return rb_hierarchy_last (node); + + he = he->parent_he; +if (he ==NULL ) +return NULL ; + +return &he->rb_node; +} +static void hists__remove_entry_filter (struct hists *hists,struct hist_entry *h,enum hist_filter filter) {
tools/perf/util/hist.h +16 -0
@@ -418,4 +418,20 @@ int perf_hist_config(const char *var, const char *value);void perf_hpp_list__init (struct perf_hpp_list *list); +enum hierarchy_move_dir { +HMD_NORMAL , +HMD_FORCE_SIBLING , +HMD_FORCE_CHILD , +}; + +struct rb_node *rb_hierarchy_last (struct rb_node *node); +struct rb_node *__rb_hierarchy_next (struct rb_node *node, +enum hierarchy_move_dir hmd); +struct rb_node *rb_hierarchy_prev (struct rb_node *node); + +static inline struct rb_node *rb_hierarchy_next (struct rb_node *node) +{ +return __rb_hierarchy_next (node,HMD_NORMAL ); +} +#endif /* __PERF_HIST_H */
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 18/31 perf hists: Introduce hist_entry__filter()
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The hist_entry__filter() function is to filter hist entries using sort key related info. This is needed to support hierarchy mode since each hist entry will be associated with a hpp fmt which has a sort key. So each entry should compare to only matching type of filters.
To do that, add the ->se_filter callback field to struct sort_entry. This callback takes 'type' argument which determines whether it's matching sort key or not. It returns -1 for non-matching type, 0 for filtered entry and 1 for not filtered entries.
--- tools/perf/util/hist.h | 2 + tools/perf/util/sort.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/sort.h | 1 + 3 files changed, 116 insertions(+)
tools/perf/util/hist.h +2 -0
@@ -305,6 +305,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); +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,struct hists *hists) {
tools/perf/util/sort.c +113 -0
206 lines not on this page · raw message
tools/perf/util/sort.h +1 -0
@@ -245,6 +245,7 @@ struct sort_entry {int64_t (*se_sort)(struct hist_entry *,struct hist_entry *);int (*se_snprintf)(struct hist_entry *he,char *bf,size_t size,unsigned int width); +int (*se_filter)(struct hist_entry *he,int type,const void *arg);u8 se_width_idx; };
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 19/31 perf hists: Support filtering in hierarchy mode
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The hists__filter_hierarchy() function implements filtering in hierarchy mode. Now we have hist_entry__filter() so use it for entries in the hierarchy. It returns 3 kind of values.
A negative value means that it's not filtered by this type. It marks current entry as filtered tentatively so if a lower level entry removes the filter it also removes the all parent so that we can find the entry in the output.
Zero means it's filtered out by this type. A positive value means it's not filtered so it removes the filter and shows in the output. In these cases, it moves to next entry since lower level entry won't match by this type of filter anymore. Thus all children will be filtered or not together.
--- tools/perf/util/hist.c | 101 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 8 deletions(-)
tools/perf/util/hist.c +93 -8
@@ -1560,6 +1560,27 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h enum hist_filter filter) { h->filtered &= ~(1 << filter); + +if (symbol_conf.report_hierarchy) { +struct hist_entry *parent = h->parent_he; + +while (parent) { +he_stat__add_stat (&parent->stat, &h->stat); + + parent->filtered &= ~(1 << filter); + +if (parent->filtered) + goto next; + +/* force fold unfiltered entry for simplicity */ + parent->unfolded = false; + parent->row_offset =0 ; + parent->nr_rows =0 ; +next: + parent = parent->parent_he; + } + } +if (h->filtered)return ;
@@ -1645,28 +1666,92 @@ static void hists__filter_by_type(struct hists *hists, int type, filter_fn_t fil } } +static void hists__filter_hierarchy (struct hists *hists,int type,const void *arg) +{ +struct rb_node *nd; + + hists->stats.nr_non_filtered_samples =0 ; + +hists__reset_filter_stats (hists); +hists__reset_col_len (hists); + + nd =rb_first (&hists->entries); +while (nd) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); +int ret; + + ret =hist_entry__filter (h, type, arg); + +/* +* case 1. non-matching type +* zero out the period, set filter marker and move to child +*/ +if (ret <0 ) { +memset (&h->stat,0 ,sizeof (h->stat)); + h->filtered |= (1 << type); + + nd =__rb_hierarchy_next (&h->rb_node,HMD_FORCE_CHILD ); + } +/* +* case 2. matched type (filter out) +* set filter marker and move to next +*/ +else if (ret ==1 ) { + h->filtered |= (1 << type); + + nd =__rb_hierarchy_next (&h->rb_node,HMD_FORCE_SIBLING ); + } +/* +* case 3. ok (not filtered) +* add period to hists and parents, erase the filter marker +* and move to next sibling +*/ +else { +hists__remove_entry_filter (hists, h, type); + + nd =__rb_hierarchy_next (&h->rb_node,HMD_FORCE_SIBLING ); + } + } +} +void hists__filter_by_thread (struct hists *hists) { -hists__filter_by_type (hists,HIST_FILTER__THREAD , - hists__filter_entry_by_thread); +if (symbol_conf.report_hierarchy) +hists__filter_hierarchy (hists,HIST_FILTER__THREAD , + hists->thread_filter); +else +hists__filter_by_type (hists,HIST_FILTER__THREAD , + hists__filter_entry_by_thread); }void hists__filter_by_dso (struct hists *hists) { -hists__filter_by_type (hists,HIST_FILTER__DSO , - hists__filter_entry_by_dso); +if (symbol_conf.report_hierarchy) +hists__filter_hierarchy (hists,HIST_FILTER__DSO , + hists->dso_filter); +else +hists__filter_by_type (hists,HIST_FILTER__DSO , + hists__filter_entry_by_dso); }void hists__filter_by_symbol (struct hists *hists) { -hists__filter_by_type (hists,HIST_FILTER__SYMBOL , - hists__filter_entry_by_symbol); +if (symbol_conf.report_hierarchy) +hists__filter_hierarchy (hists,HIST_FILTER__SYMBOL , + hists->symbol_filter_str); +else +hists__filter_by_type (hists,HIST_FILTER__SYMBOL , + hists__filter_entry_by_symbol); }void hists__filter_by_socket (struct hists *hists) { -hists__filter_by_type (hists,HIST_FILTER__SOCKET , - hists__filter_entry_by_socket); +if (symbol_conf.report_hierarchy) +hists__filter_hierarchy (hists,HIST_FILTER__SOCKET , + &hists->socket_filter); +else +hists__filter_by_type (hists,HIST_FILTER__SOCKET , + hists__filter_entry_by_socket); }void events_stats__inc (struct events_stats *stats,u32 type)
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 20/31 perf hists: Resort after filtering hierarchy
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
In hierarchy mode, a filter can affect periods of entries in upper hierarchy. So it needs to resort the hists after filter.
For example, let's look at following example:
Overhead Command / Shared Object / Symbol
------------ --------------------------------
30.00% perf
20.00% perf
10.00% main
5.00% pr_debug
5.00% memcpy
10.00% [kernel.vmlinux]
8.00% memset
2.00% cpu_idleIf we apply simbol filter for 'mem' it should look like this
13.00% perf
8.00% [kernel.vmlinux]
8.00% memset
5.00% perf
5.00% memcpy--- tools/perf/util/hist.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
tools/perf/util/hist.c +54 -0
@@ -1666,9 +1666,47 @@ static void hists__filter_by_type(struct hists *hists, int type, filter_fn_t fil } } +static void resort_filtered_entry (struct rb_root *root,struct hist_entry *he) +{ +struct rb_node **p = &root->rb_node; +struct rb_node *parent =NULL ; +struct hist_entry *iter; +struct rb_root new_root =RB_ROOT ; +struct rb_node *nd; + +while (*p !=NULL ) { + parent = *p; + iter = rb_entry(parent, struct hist_entry, rb_node); + +if (hist_entry__sort (he, iter) >0 ) + p = &(*p)->rb_left; +else + p = &(*p)->rb_right; + } + +rb_link_node (&he->rb_node, parent, p); +rb_insert_color (&he->rb_node, root); + +if (he->leaf || he->filtered) +return ; + + nd =rb_first (&he->hroot_out); +while (nd) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + nd =rb_next (nd); +rb_erase (&h->rb_node, &he->hroot_out); + +resort_filtered_entry (&new_root, h); + } + + he->hroot_out = new_root; +} +static void hists__filter_hierarchy (struct hists *hists,int type,const void *arg) {struct rb_node *nd; +struct rb_root new_root =RB_ROOT ; hists->stats.nr_non_filtered_samples =0 ;
@@ -1712,6 +1750,22 @@ static void hists__filter_hierarchy(struct hists *hists, int type, const void *a nd =__rb_hierarchy_next (&h->rb_node,HMD_FORCE_SIBLING ); } } + +/* +* resort output after applying a new filter since filter in a lower +* hierarchy can change periods in a upper hierarchy. +*/ + nd =rb_first (&hists->entries); +while (nd) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + nd =rb_next (nd); +rb_erase (&h->rb_node, &hists->entries); + +resort_filtered_entry (&new_root, h); + } + + hists->entries = new_root; }void hists__filter_by_thread (struct hists *hists)
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 21/31 perf hists: Count number of sort keys
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
It'll be used for hierarchy output mode to indent entries properly.
--- tools/perf/ui/hist.c | 3 +++ tools/perf/util/hist.h | 1 + 2 files changed, 4 insertions(+)
tools/perf/ui/hist.c +3 -0
@@ -514,6 +514,9 @@ void perf_hpp_list__column_register(struct perf_hpp_list *list,void perf_hpp_list__register_sort_field (struct perf_hpp_list *list,struct perf_hpp_fmt *format) { +if (perf_hpp__is_sort_entry (format) ||perf_hpp__is_dynamic_entry (format)) + list->nr_sort_keys++; +list_add_tail (&format->sort_list, &list->sorts); }
tools/perf/util/hist.h +1 -0
@@ -237,6 +237,7 @@ struct perf_hpp_fmt {struct perf_hpp_list {struct list_head fields;struct list_head sorts; +int nr_sort_keys; };extern struct perf_hpp_list perf_hpp_list;
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 22/31 perf ui/stdio: Implement hierarchy output mode
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The hierarchy output mode is to group entries for each level so that user can see higher level picture more easily. It also helps to find out which component is most costly. The output will look like below:
15.11% swapper
14.97% [kernel.vmlinux]
0.09% [libahci]
0.05% [iwlwifi]
10.29% irq/33-iwlwifi
6.45% [kernel.vmlinux]
1.41% [mac80211]
1.15% [iwldvm]
1.14% [iwlwifi]
0.14% [cfg80211]
4.81% firefox
3.92% libxul.so
0.34% [kernel.vmlinux]--- tools/perf/ui/stdio/hist.c | 79 +++++++++++++++++++++++++++++++++++++++++++++- tools/perf/util/hist.h | 2 ++ 2 files changed, 80 insertions(+), 1 deletion(-)
tools/perf/ui/stdio/hist.c +78 -1
@@ -410,6 +410,76 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)return hpp->buf - start; } +static int hist_entry__hierarchy_fprintf (struct hist_entry *he, +struct perf_hpp *hpp, +int nr_sort_key,struct hists *hists, +FILE *fp) +{ +const char *sep = symbol_conf.field_sep; +struct perf_hpp_fmt *fmt; +char *buf = hpp->buf; +int ret, printed =0 ; +bool first = true; + +if (symbol_conf.exclude_other && !he->parent) +return 0 ; + + 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 ; + +/* +* If there's no field_sep, we still need +* to display initial ' '. +*/ +if (!sep || !first) { + ret =scnprintf (hpp->buf, hpp->size,"%s" , sep ?:" " ); +advance_hpp (hpp, ret); + }else + first = false; + +if (perf_hpp__use_color () && fmt->color) + ret = fmt->color (fmt, hpp, he); +else + ret = fmt->entry (fmt, hpp, he); + + ret =hist_entry__snprintf_alignment (he, hpp, fmt, ret); +advance_hpp (hpp, ret); + } + +if (sep) + ret =scnprintf (hpp->buf, hpp->size,"%s" , sep); +else + ret =scnprintf (hpp->buf, hpp->size,"%*s" , + (nr_sort_key -1 ) *HIERARCHY_INDENT +2 ,"" ); +advance_hpp (hpp, ret); + +/* +* No need to call hist_entry__snprintf_alignment() since this +* fmt is always the last column in the hierarchy mode. +*/ + fmt = he->fmt; +if (perf_hpp__use_color () && fmt->color) + fmt->color (fmt, hpp, he); +else + fmt->entry (fmt, hpp, he); + + printed +=fprintf (fp,"%s\n" , buf); + +if (symbol_conf.use_callchain && he->leaf) { +u64 total =hists__total_period (hists); + + printed +=hist_entry_callchain__fprintf (he, total,0 , fp); + goto out; + } + +out: +return printed; +} +static int hist_entry__fprintf (struct hist_entry *he,size_t size,struct hists *hists,char *bf,size_t bfsz,FILE *fp)
@@ -424,6 +494,13 @@ 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->hpp_list->nr_sort_keys; + +return hist_entry__hierarchy_fprintf (he, &hpp, nr_sort, + hists, fp); + } +hist_entry__snprintf (he, &hpp); ret =fprintf (fp,"%s\n" , bf);
@@ -522,7 +599,7 @@ print_entries: goto out; } -for (nd =rb_first (&hists->entries); nd; nd =rb_next (nd)) { +for (nd =rb_first (&hists->entries); nd; nd =__rb_hierarchy_next (nd,HMD_FORCE_CHILD )) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);float percent;
tools/perf/util/hist.h +2 -0
@@ -437,4 +437,6 @@ static inline struct rb_node *rb_hierarchy_next(struct rb_node *node) return __rb_hierarchy_next(node, HMD_NORMAL); } +#define HIERARCHY_INDENT 3 +#endif /* __PERF_HIST_H */
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 23/31 perf ui/stdio: Align column header for hierarchy output
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The hierarchy output mode is to group entries so the existing columns won't fit to the new output. Treat all sort keys as a single column and separate headers by "/".
# Overhead Command / Shared Object
# ........... ................................
#
15.11% swapper
14.97% [kernel.vmlinux]
0.09% [libahci]
0.05% [iwlwifi]
...--- tools/perf/ui/stdio/hist.c | 105 +++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/ctype.c | 9 ++++ tools/perf/util/util.h | 2 + 3 files changed, 116 insertions(+)
tools/perf/ui/stdio/hist.c +105 -0
@@ -511,6 +511,106 @@ 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, +const char *line,FILE *fp) +{ +if (sep !=NULL || nr_sort <1 ) +return 0 ; + +return fprintf (fp,"%-.*s" , (nr_sort -1 ) *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; +unsigned width =0 ; +unsigned header_width =0 ; +struct perf_hpp_fmt *fmt; + + nr_sort = hists->hpp_list->nr_sort_keys; + +/* preserve max indent depth for column headers */ +print_hierarchy_indent (sep, nr_sort, 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; + + fmt->header (fmt, hpp,hists_to_evsel (hists)); +fprintf (fp,"%s" , hpp->buf); + } + +/* 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) + header_width +=fprintf (fp," / " ); +else { + header_width +=fprintf (fp,"%s" , sep ?:" " ); + first = false; + } + + fmt->header (fmt, hpp,hists_to_evsel (hists)); +rtrim (hpp->buf); + + header_width +=fprintf (fp,"%s" , hpp->buf); + } + +/* preserve max indent depth for combined sort headers */ +print_hierarchy_indent (sep, nr_sort, spaces, fp); + +fprintf (fp,"\n# " ); + +/* preserve max indent depth for initial dots */ +print_hierarchy_indent (sep, nr_sort, dots, fp); + + first = true; +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; + + width = fmt->width (fmt, hpp,hists_to_evsel (hists)); +fprintf (fp,"%.*s" , width, dots); + } + +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 ; + + width = fmt->width (fmt, hpp,hists_to_evsel (hists)); +if (width > header_width) + header_width = width; + } + +fprintf (fp,"%s%-.*s" , sep ?:" " , header_width, dots); + +/* preserve max indent depth for dots under sort headers */ +print_hierarchy_indent (sep, nr_sort, dots, fp); + +fprintf (fp,"\n#\n" ); + +return 2 ; +} +size_t hists__fprintf (struct hists *hists,bool show_header,int max_rows,int max_cols,float min_pcnt,FILE *fp) {
@@ -542,6 +642,11 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,fprintf (fp,"# " ); +if (symbol_conf.report_hierarchy) { + nr_rows +=print_hierarchy_header (hists, &dummy_hpp, sep, fp); + goto print_entries; + } +hists__for_each_format (hists, fmt) {if (perf_hpp__should_skip (fmt, hists))continue ;
tools/perf/util/ctype.c +9 -0
@@ -32,8 +32,17 @@ unsigned char sane_ctype[256] = {const char *graph_line ="_____________________________________________________________________" +"_____________________________________________________________________" "_____________________________________________________________________" ;const char *graph_dotted_line ="---------------------------------------------------------------------" "---------------------------------------------------------------------" "---------------------------------------------------------------------" ; +const char *spaces = +" " +" " +" " ; +const char *dots = +"....................................................................." +"....................................................................." +"....................................................................." ;
tools/perf/util/util.h +2 -0
@@ -82,6 +82,8 @@extern const char *graph_line;extern const char *graph_dotted_line; +extern const char *spaces; +extern const char *dots;extern char buildid_dir[];/* On most systems <limits.h> would have given us this, but
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 24/31 perf hists browser: Count number of hierarchy entries
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
Add nr_hierarchy_entries field to keep current number of (unfolded) hist entries. And the hist_entry->nr_rows carries number of direct children. But in the hierarchy mode, entry can have grand children and callchains. So update the number properly using hierarchy_count_rows() when toggling the folded state (by pressing ENTER key).
--- tools/perf/ui/browsers/hists.c | 85 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 11 deletions(-)
tools/perf/ui/browsers/hists.c +74 -11
@@ -32,6 +32,7 @@ struct hist_browser {bool show_headers;float min_pcnt;u64 nr_non_filtered_entries; +u64 nr_hierarchy_entries;u64 nr_callchain_rows; };
@@ -58,11 +59,11 @@ static int hist_browser__get_folding(struct hist_browser *browser)for (nd =rb_first (&hists->entries); (nd =hists__filter_entries (nd, browser->min_pcnt)) !=NULL ; - nd =rb_next (nd)) { + nd =rb_hierarchy_next (nd)) {struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); -if (he->unfolded) +if (he->leaf && he->unfolded) unfolded_rows += he->nr_rows; }return unfolded_rows;
@@ -72,7 +73,9 @@ static u32 hist_browser__nr_entries(struct hist_browser *hb) {u32 nr_entries; -if (hist_browser__has_filter (hb)) +if (symbol_conf.report_hierarchy) + nr_entries = hb->nr_hierarchy_entries; +else if (hist_browser__has_filter (hb)) nr_entries = hb->nr_non_filtered_entries;else nr_entries = hb->hists->nr_entries;
@@ -247,6 +250,35 @@ static int callchain__count_rows(struct rb_root *chain)return n; } +static int hierarchy_count_rows (struct hist_browser *hb,struct hist_entry *he, +bool include_children) +{ +int count =0 ; +struct rb_node *node; +struct hist_entry *child; + +if (he->leaf) +return callchain__count_rows (&he->sorted_chain); + + node =rb_first (&he->hroot_out); +while (node) { +float percent; + + child = rb_entry(node, struct hist_entry, rb_node); + percent =hist_entry__get_percent_limit (child); + +if (!child->filtered && percent >= hb->min_pcnt) { + count++; + +if (include_children && child->unfolded) + count +=hierarchy_count_rows (hb, child, true); + } + + node =rb_next (node); + } +return count; +} +static bool hist_entry__toggle_fold (struct hist_entry *he) {if (!he)
@@ -326,11 +358,17 @@ static void callchain__init_have_children(struct rb_root *root)static void hist_entry__init_have_children (struct hist_entry *he) { -if (!he->init_have_children) { +if (he->init_have_children) +return ; + +if (he->leaf) { he->has_children = !RB_EMPTY_ROOT (&he->sorted_chain);callchain__init_have_children (&he->sorted_chain); - he->init_have_children = true; + }else { + he->has_children = !RB_EMPTY_ROOT (&he->hroot_out); } + + he->init_have_children = true; }static bool hist_browser__toggle_fold (struct hist_browser *browser)
@@ -349,17 +387,41 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser) has_children =callchain_list__toggle_fold (cl);if (has_children) { +int child_rows =0 ; +hist_entry__init_have_children (he); browser->b.nr_entries -= he->nr_rows; - browser->nr_callchain_rows -= he->nr_rows; -if (he->unfolded) - he->nr_rows =callchain__count_rows (&he->sorted_chain); +if (he->leaf) + browser->nr_callchain_rows -= he->nr_rows;else + browser->nr_hierarchy_entries -= he->nr_rows; + +if (symbol_conf.report_hierarchy) + child_rows =hierarchy_count_rows (browser, he, true); + +if (he->unfolded) { +if (he->leaf) + he->nr_rows =callchain__count_rows (&he->sorted_chain); +else + he->nr_rows =hierarchy_count_rows (browser, he, false); + +/* account grand children */ +if (symbol_conf.report_hierarchy) + browser->b.nr_entries += child_rows - he->nr_rows; + }else { +if (symbol_conf.report_hierarchy) + browser->b.nr_entries -= child_rows - he->nr_rows; + he->nr_rows =0 ; + } browser->b.nr_entries += he->nr_rows; - browser->nr_callchain_rows += he->nr_rows; + +if (he->leaf) + browser->nr_callchain_rows += he->nr_rows; +else + browser->nr_hierarchy_entries += he->nr_rows;return true; }
@@ -2025,17 +2087,18 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb)u64 nr_entries =0 ;struct rb_node *nd =rb_first (&hb->hists->entries); -if (hb->min_pcnt ==0 ) { +if (hb->min_pcnt ==0 && !symbol_conf.report_hierarchy) { hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;return ; }while ((nd =hists__filter_entries (nd, hb->min_pcnt)) !=NULL ) { nr_entries++; - nd =rb_next (nd); + nd =rb_hierarchy_next (nd); } hb->nr_non_filtered_entries = nr_entries; + hb->nr_hierarchy_entries = nr_entries; } static void hist_browser__update_percent_limit(struct hist_browser *hb,
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 25/31 perf hists browser: Support collapsing/expanding whole entries in hierarchy
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The 'C' and 'E' keys are to collapse/expand all hist entries. Update nr_hierarchy_entries properly in this case.
--- tools/perf/ui/browsers/hists.c | 58 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 10 deletions(-)
tools/perf/ui/browsers/hists.c +48 -10
@@ -484,13 +484,38 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold)return n; } -static void hist_entry__set_folding (struct hist_entry *he,bool unfold) +static int hierarchy_set_folding (struct hist_browser *hb,struct hist_entry *he, +bool unfold__maybe_unused ) +{ +float percent; +struct rb_node *nd; +struct hist_entry *child; +int n =0 ; + +for (nd =rb_first (&he->hroot_out); nd; nd =rb_next (nd)) { + child = rb_entry(nd, struct hist_entry, rb_node); + percent =hist_entry__get_percent_limit (child); +if (!child->filtered && percent >= hb->min_pcnt) + n++; + } + +return n; +} + +static void hist_entry__set_folding (struct hist_entry *he, +struct hist_browser *hb,bool unfold) {hist_entry__init_have_children (he); he->unfolded = unfold ? he->has_children : false;if (he->has_children) { -int n =callchain__set_folding (&he->sorted_chain, unfold); +int n; + +if (he->leaf) + n =callchain__set_folding (&he->sorted_chain, unfold); +else + n =hierarchy_set_folding (hb, he, unfold); + he->nr_rows = unfold ? n :0 ; }else he->nr_rows =0 ;
@@ -500,19 +525,32 @@ static void __hist_browser__set_folding(struct hist_browser *browser, bool unfold) {struct rb_node *nd; -struct hists *hists = browser->hists; +struct hist_entry *he; +double percent; -for (nd =rb_first (&hists->entries); - (nd =hists__filter_entries (nd, browser->min_pcnt)) !=NULL ; - nd =rb_next (nd)) { - struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); -hist_entry__set_folding (he, unfold); - browser->nr_callchain_rows += he->nr_rows; + nd =rb_first (&browser->hists->entries); +while (nd) { + he = rb_entry(nd, struct hist_entry, rb_node); + +/* set folding state even if it's currently folded */ + nd =__rb_hierarchy_next (nd,HMD_FORCE_CHILD ); + +hist_entry__set_folding (he, browser, unfold); + + percent =hist_entry__get_percent_limit (he); +if (he->filtered || percent < browser->min_pcnt) +continue ; + +if (!he->depth || unfold) + browser->nr_hierarchy_entries++; +if (he->leaf) + browser->nr_callchain_rows += he->nr_rows; } }static void hist_browser__set_folding (struct hist_browser *browser,bool unfold) { + browser->nr_hierarchy_entries =0 ; browser->nr_callchain_rows =0 ;__hist_browser__set_folding (browser, unfold);
@@ -2131,7 +2169,7 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb,/* force to re-evaluate folding state of callchains */ he->init_have_children = false; -hist_entry__set_folding (he, false); +hist_entry__set_folding (he, hb, false); nd =rb_next (nd); }
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 26/31 perf hists browser: Implement hierarchy output
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
Implement hierarchy mode in TUI. The output is look like stdio but it also supports to fold/unfold children dynamically.
--- tools/perf/ui/browsers/hists.c | 290 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 268 insertions(+), 22 deletions(-)
tools/perf/ui/browsers/hists.c +268 -22
@@ -1260,6 +1260,158 @@ static int hist_browser__show_entry(struct hist_browser *browser,return printed; } +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 printed =0 ; +int width = browser->b.width; +char folded_sign =' ' ; +bool current_entry =ui_browser__is_current_entry (&browser->b, row); +off_t row_offset = entry->row_offset; +bool first = true; +struct perf_hpp_fmt *fmt; +struct hpp_arg arg = { + .b = &browser->b, + .current_entry = current_entry, + }; +int column =0 ; +int hierarchy_indent = (nr_sort_keys -1 ) *HIERARCHY_INDENT ; + +if (current_entry) { + browser->he_selection = entry; + browser->selection = &entry->ms; + } + +hist_entry__init_have_children (entry); + folded_sign =hist_entry__folded (entry); + arg.folded_sign = folded_sign; + +if (entry->leaf && row_offset) { + row_offset--; + goto show_callchain; + } + +hist_browser__gotorc (browser, row,0 ); + +if (current_entry && browser->b.navkeypressed) +ui_browser__set_color (&browser->b,HE_COLORSET_SELECTED ); +else +ui_browser__set_color (&browser->b,HE_COLORSET_NORMAL ); + +ui_browser__write_nstring (&browser->b,"" , level *HIERARCHY_INDENT ); + width -= level *HIERARCHY_INDENT ; + +hists__for_each_format (entry->hists, fmt) { +char s[2048 ]; +struct perf_hpp hpp = { + .buf = s, + .size =sizeof (s), + .ptr = &arg, + }; + +if (perf_hpp__should_skip (fmt, entry->hists) || + 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 ); + }else { +ui_browser__set_color (&browser->b, +HE_COLORSET_NORMAL ); + } + +if (first) { +ui_browser__printf (&browser->b,"%c" , folded_sign); + width--; + first = false; + }else { +ui_browser__printf (&browser->b," " ); + width -=2 ; + } + +if (fmt->color) { +int ret = fmt->color (fmt, &hpp, entry); +hist_entry__snprintf_alignment (entry, &hpp, fmt, ret); +/* +* fmt->color() already used ui_browser to +* print the non alignment bits, skip it (+ret): +*/ +ui_browser__printf (&browser->b,"%s" , s + ret); + }else { +int ret = fmt->entry (fmt, &hpp, entry); +hist_entry__snprintf_alignment (entry, &hpp, fmt, ret); +ui_browser__printf (&browser->b,"%s" , s); + } + width -= hpp.buf - s; + } + +ui_browser__write_nstring (&browser->b,"" , hierarchy_indent); + width -= hierarchy_indent; + +if (column >= browser->b.horiz_scroll) { +char s[2048 ]; +struct perf_hpp hpp = { + .buf = s, + .size =sizeof (s), + .ptr = &arg, + }; + +if (current_entry && browser->b.navkeypressed) { +ui_browser__set_color (&browser->b, +HE_COLORSET_SELECTED ); + }else { +ui_browser__set_color (&browser->b, +HE_COLORSET_NORMAL ); + } + +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 { + width -= fmt->entry (fmt, &hpp, entry); +ui_browser__printf (&browser->b,"%s" , s); + } + } + +/* The scroll bar isn't being used */ +if (!browser->b.navkeypressed) + width +=1 ; + +ui_browser__write_nstring (&browser->b,"" , width); + + ++row; + ++printed; + +show_callchain: +if (entry->leaf && folded_sign =='-' && row != browser->b.rows) { +struct callchain_print_arg carg = { + .row_offset = row_offset, + }; + + printed +=hist_browser__show_callchain (browser, entry, + level +1 , row, + hist_browser__show_callchain_entry, &carg, + hist_browser__check_output_full); + } + +return printed; +} +static int advance_hpp_check (struct perf_hpp *hpp,int inc) {advance_hpp (hpp, inc);
@@ -1325,6 +1477,7 @@ 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->hpp_list->nr_sort_keys;if (hb->show_headers) {hist_browser__show_headers (hb);
@@ -1335,18 +1488,28 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) hb->he_selection =NULL ; hb->selection =NULL ; -for (nd = browser->top; nd; nd =rb_next (nd)) { +for (nd = browser->top; nd; nd =rb_hierarchy_next (nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);float percent; -if (h->filtered) +if (h->filtered) { +/* let it move to sibling */ + h->unfolded = false;continue ; + } percent =hist_entry__get_percent_limit (h);if (percent < hb->min_pcnt)continue ; - row +=hist_browser__show_entry (hb, h, row); +if (symbol_conf.report_hierarchy) { + row +=hist_browser__show_hierarchy_entry (hb, h, row, + h->depth, + nr_sort); + }else { + row +=hist_browser__show_entry (hb, h, row); + } +if (row == browser->rows)break ; }
@@ -1364,7 +1527,14 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,if (!h->filtered && percent >= min_pcnt)return nd; - nd =rb_next (nd); +/* +* If it's filtered, its all children also were filtered. +* So move to sibling node. +*/ +if (rb_next (nd)) + nd =rb_next (nd); +else + nd =rb_hierarchy_next (nd); }return NULL ;
@@ -1380,7 +1550,7 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,if (!h->filtered && percent >= min_pcnt)return nd; - nd =rb_prev (nd); + nd =rb_hierarchy_prev (nd); }return NULL ;
@@ -1410,8 +1580,8 @@ static void ui_browser__hists_seek(struct ui_browser *browser, nd = browser->top; goto do_offset;case SEEK_END : - nd =hists__filter_prev_entries (rb_last (browser->entries), - hb->min_pcnt); + nd =rb_hierarchy_last (rb_last (browser->entries)); + nd =hists__filter_prev_entries (nd, hb->min_pcnt); first = false;break ;default :
@@ -1445,7 +1615,7 @@ do_offset:if (offset >0 ) { do { h = rb_entry(nd, struct hist_entry, rb_node); - if (h->unfolded) { + if (h->unfolded && h->leaf) { u16 remaining = h->nr_rows - h->row_offset; if (offset > remaining) { offset -= remaining;
@@ -1457,7 +1627,8 @@ do_offset:break ; } } - nd =hists__filter_entries (rb_next (nd), hb->min_pcnt); + nd =hists__filter_entries (rb_hierarchy_next (nd), + hb->min_pcnt);if (nd ==NULL )break ; --offset;
@@ -1466,7 +1637,7 @@ do_offset: }else if (offset <0 ) {while (1 ) { h = rb_entry(nd, struct hist_entry, rb_node); -if (h->unfolded) { +if (h->unfolded && h->leaf) {if (first) {if (-offset > h->row_offset) { offset += h->row_offset;
@@ -1490,7 +1661,7 @@ do_offset: } } - nd =hists__filter_prev_entries (rb_prev (nd), + nd =hists__filter_prev_entries (rb_hierarchy_prev (nd), hb->min_pcnt);if (nd ==NULL )break ;
@@ -1503,7 +1674,7 @@ do_offset:* row_offset at its last entry. */ h = rb_entry(nd, struct hist_entry, rb_node); -if (h->unfolded) +if (h->unfolded && h->leaf) h->row_offset = h->nr_rows;break ; }
@@ -1517,13 +1688,14 @@ do_offset: }static int hist_browser__fprintf_callchain (struct hist_browser *browser, -struct hist_entry *he,FILE *fp) +struct hist_entry *he,FILE *fp, +int level) {struct callchain_print_arg arg = { .fp = fp, }; -hist_browser__show_callchain (browser, he,1 ,0 , +hist_browser__show_callchain (browser, he, level,0 , hist_browser__fprintf_callchain_entry, &arg, hist_browser__check_dump_full);return arg.printed;
@@ -1566,7 +1738,65 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, printed +=fprintf (fp,"%s\n" , s);if (folded_sign =='-' ) - printed +=hist_browser__fprintf_callchain (browser, he, fp); + printed +=hist_browser__fprintf_callchain (browser, he, fp,1 ); + +return printed; +} + + +static int hist_browser__fprintf_hierarchy_entry (struct hist_browser *browser, +struct hist_entry *he, +FILE *fp,int level, +int nr_sort_keys) +{ +char s[8192 ]; +int printed =0 ; +char folded_sign =' ' ; +struct perf_hpp hpp = { + .buf = s, + .size =sizeof (s), + }; +struct perf_hpp_fmt *fmt; +bool first = true; +int ret; +int hierarchy_indent = (nr_sort_keys +1 ) *HIERARCHY_INDENT ; + + printed =fprintf (fp,"%*s" , level *HIERARCHY_INDENT ,"" ); + + folded_sign =hist_entry__folded (he); + printed +=fprintf (fp,"%c" , folded_sign); + +hists__for_each_format (he->hists, fmt) { +if (perf_hpp__should_skip (fmt, he->hists)) +continue ; + +if (perf_hpp__is_sort_entry (fmt) || +perf_hpp__is_dynamic_entry (fmt)) +break ; + +if (!first) { + ret =scnprintf (hpp.buf, hpp.size," " ); +advance_hpp (&hpp, ret); + }else + first = false; + + ret = fmt->entry (fmt, &hpp, he); +advance_hpp (&hpp, ret); + } + + 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); + + printed +=fprintf (fp,"%s\n" ,rtrim (s)); + +if (he->leaf && folded_sign =='-' ) { + printed +=hist_browser__fprintf_callchain (browser, he, fp, + he->depth +1 ); + }return printed; }
@@ -1576,12 +1806,22 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)struct rb_node *nd =hists__filter_entries (rb_first (browser->b.entries), browser->min_pcnt);int printed =0 ; +int nr_sort = browser->hists->hpp_list->nr_sort_keys;while (nd) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - printed +=hist_browser__fprintf_entry (browser, h, fp); - nd =hists__filter_entries (rb_next (nd), browser->min_pcnt); +if (symbol_conf.report_hierarchy) { + printed +=hist_browser__fprintf_hierarchy_entry (browser, + h, fp, + h->depth, + nr_sort); + }else { + printed +=hist_browser__fprintf_entry (browser, h, fp); + } + + nd =hists__filter_entries (rb_hierarchy_next (nd), + browser->min_pcnt); }return printed;
@@ -2149,12 +2389,12 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb, hb->min_pcnt = callchain_param.min_percent = percent; -if (!symbol_conf.use_callchain) -return ; -while ((nd =hists__filter_entries (nd, hb->min_pcnt)) !=NULL ) { he = rb_entry(nd, struct hist_entry, rb_node); +if (!he->leaf || !symbol_conf.use_callchain) + goto next; +if (callchain_param.mode ==CHAIN_GRAPH_REL ) { total = he->stat.period;
@@ -2167,11 +2407,17 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb, callchain_param.sort (&he->sorted_chain, he->callchain, min_callchain_hits, &callchain_param); +next: +/* +* Tentatively set unfolded so that the rb_hierarchy_next() +* can toggle children of folded entries too. +*/ + he->unfolded = he->has_children; + nd =rb_hierarchy_next (nd); +/* force to re-evaluate folding state of callchains */ he->init_have_children = false;hist_entry__set_folding (he, hb, false); - - nd =rb_next (nd); } }
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 27/31 perf hists browser: Align column header in hierarchy mode
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
Like in stdio, fit column header to hierarchy output. Merge column headers with "/" as a separator.
Overhead Command / Shared Object / Symbol
...
+ 0.09% dwm + 0.06% emacs - 0.05% perf
- 0.05% [kernel.vmlinux]
+ 0.03% [k] memcpy_orig
+ 0.01% [k] unmap_single_vma
+ 0.01% [k] smp_call_function_single
+ 0.00% [k] native_irq_return_iret
+ 0.00% [k] arch_trigger_all_cpu_backtrace_handler
+ 0.00% [k] native_write_msr_safe
--- tools/perf/ui/browsers/hists.c | 71 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-)
tools/perf/ui/browsers/hists.c +70 -1
@@ -1451,11 +1451,80 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *return ret; } +static int hists_browser__scnprintf_hierarchy_headers (struct hist_browser *browser,char *buf,size_t size) +{ +struct hists *hists = browser->hists; +struct perf_hpp dummy_hpp = { + .buf = buf, + .size = size, + }; +struct perf_hpp_fmt *fmt; +size_t ret =0 ; +int column =0 ; +int nr_sort_keys = hists->hpp_list->nr_sort_keys; +bool first = true; + + ret =scnprintf (buf, size," " ); +if (advance_hpp_check (&dummy_hpp, ret)) +return ret; + +hists__for_each_format (hists, 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 ; + + ret =scnprintf (dummy_hpp.buf, dummy_hpp.size," " ); +if (advance_hpp_check (&dummy_hpp, ret)) +break ; + } + + ret =scnprintf (dummy_hpp.buf, dummy_hpp.size,"%*s" , + (nr_sort_keys -1 ) *HIERARCHY_INDENT ,"" ); +if (advance_hpp_check (&dummy_hpp, ret)) +return ret; + +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 { + ret =scnprintf (dummy_hpp.buf, dummy_hpp.size," / " ); +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); + + ret =strlen (dummy_hpp.buf); +if (advance_hpp_check (&dummy_hpp, ret)) +break ; + } + +return ret; +} +static void hist_browser__show_headers (struct hist_browser *browser) {char headers[1024 ]; -hists_browser__scnprintf_headers (browser, headers,sizeof (headers)); +if (symbol_conf.report_hierarchy) +hists_browser__scnprintf_hierarchy_headers (browser, headers, +sizeof (headers)); +else +hists_browser__scnprintf_headers (browser, headers, +sizeof (headers));ui_browser__gotorc (&browser->b,0 ,0 );ui_browser__set_color (&browser->b,HE_COLORSET_ROOT );ui_browser__write_nstring (&browser->b, headers, browser->b.width +1 );
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 28/31 perf ui/gtk: Implement hierarchy output mode
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The hierarchy output mode is to group entries for each level so that user can see higher level picture more easily.
--- tools/perf/ui/gtk/hists.c | 163 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-)
tools/perf/ui/gtk/hists.c +162 -1
@@ -396,6 +396,164 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,gtk_container_add (GTK_CONTAINER (window), view); } +static void perf_gtk__add_hierarchy_entries (struct hists *hists, +struct rb_root *root, +GtkTreeStore *store, +GtkTreeIter *parent, +struct perf_hpp *hpp, +float min_pcnt) +{ +int col_idx =0 ; +struct rb_node *node; +struct hist_entry *he; +struct perf_hpp_fmt *fmt; +u64 total =hists__total_period (hists); + +for (node =rb_first (root); node; node =rb_next (node)) { +GtkTreeIter iter; +float percent; + + he = rb_entry(node, struct hist_entry, rb_node); +if (he->filtered) +continue ; + + percent =hist_entry__get_percent_limit (he); +if (percent < min_pcnt) +continue ; + +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 ; + +if (fmt->color) + fmt->color (fmt, hpp, he); +else + fmt->entry (fmt, hpp, he); + +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); + +gtk_tree_store_set (store, &iter, col_idx,rtrim (hpp->buf),-1 ); + +if (!he->leaf) { +perf_gtk__add_hierarchy_entries (hists, &he->hroot_out, + store, &iter, hpp, + min_pcnt); + } + +if (symbol_conf.use_callchain && he->leaf) { +if (callchain_param.mode ==CHAIN_GRAPH_REL ) + total = symbol_conf.cumulate_callchain ? + he->stat_acc->period : he->stat.period; + +perf_gtk__add_callchain (&he->sorted_chain, store, &iter, + col_idx, total); + } + } + +} + +static void perf_gtk__show_hierarchy (GtkWidget *window,struct hists *hists, +float min_pcnt) +{ +struct perf_hpp_fmt *fmt; +GType col_types[MAX_COLUMNS ]; +GtkCellRenderer *renderer; +GtkTreeStore *store; +GtkWidget *view; +int col_idx; +int nr_cols =0 ; +char s[512 ]; +char buf[512 ]; +bool first = true; +struct perf_hpp hpp = { + .buf = s, + .size =sizeof (s), + }; + +hists__for_each_format (hists, fmt) { +if (perf_hpp__is_sort_entry (fmt) || +perf_hpp__is_dynamic_entry (fmt)) +break ; + + col_types[nr_cols++] =G_TYPE_STRING ; + } + col_types[nr_cols++] =G_TYPE_STRING ; + + store =gtk_tree_store_newv (nr_cols, col_types); + view =gtk_tree_view_new (); + 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 ; + +gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), +-1 , fmt->name, + renderer,"markup" , + col_idx++,NULL ); + } + +/* 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 +strcat (buf," / " ); + + fmt->header (fmt, &hpp,hists_to_evsel (hists)); +strcat (buf,rtrim (hpp.buf)); + } + +gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), +-1 , buf, + renderer,"markup" , + col_idx++,NULL ); + +for (col_idx =0 ; col_idx < nr_cols; col_idx++) { +GtkTreeViewColumn *column; + + column =gtk_tree_view_get_column (GTK_TREE_VIEW (view), col_idx); +gtk_tree_view_column_set_resizable (column, TRUE); + +if (col_idx ==0 ) { +gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view), + column); + } + } + +gtk_tree_view_set_model (GTK_TREE_VIEW (view),GTK_TREE_MODEL (store)); +g_object_unref (GTK_TREE_MODEL (store)); + +perf_gtk__add_hierarchy_entries (hists, &hists->entries, store, +NULL , &hpp, min_pcnt); + +gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE); + +g_signal_connect (view,"row-activated" , +G_CALLBACK (on_row_activated),NULL ); +gtk_container_add (GTK_CONTAINER (window), view); +} + int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, struct hist_browser_timer *hbt __maybe_unused,
@@ -463,7 +621,10 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,GTK_POLICY_AUTOMATIC , GTK_POLICY_AUTOMATIC); -perf_gtk__show_hists (scrolled_window, hists, min_pcnt); +if (symbol_conf.report_hierarchy) +perf_gtk__show_hierarchy (scrolled_window, hists, min_pcnt); +else +perf_gtk__show_hists (scrolled_window, hists, min_pcnt); tab_label =gtk_label_new (evname);
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 29/31 perf report: Add --hierarchy option
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The --hierarchy option is to show output in hierarchy mode. It extends folding/unfolding in the TUI and GTK browsers to support sort items as well as callchains. Users can toggle the items to see the performance result at wanted level.
$ perf report --hierarchy --tui
Overhead Command / Shared Object / Symbol
--------------------------------------------------
+ 32.96% gnome-shell
- 15.11% swapper
- 14.97% [kernel.vmlinux]
6.82% [k] intel_idle
0.66% [k] menu_select
0.43% [k] __hrtimer_start_range_ns
...--- tools/perf/Documentation/perf-report.txt | 3 +++ tools/perf/Documentation/tips.txt | 1 + tools/perf/builtin-report.c | 17 +++++++++++++++++ 3 files changed, 21 insertions(+)
tools/perf/Documentation/perf-report.txt +3 -0
@@ -401,6 +401,9 @@ include::itrace.txt[] --raw-trace:: When displaying traceevent output, do not use print fmt or plugins. +--hierarchy:: + Enable hierarchical output. + include::callchain-overhead-calculation.txt[] SEE ALSO
tools/perf/Documentation/tips.txt +1 -0
@@ -27,3 +27,4 @@ Skip collecing build-id when recording: perf record -B To change sampling frequency to 100 Hz: perf record -F 100 See assembly instructions with percentage: perf annotate <symbol> If you prefer Intel style assembly, try: perf annotate -M intel +For hierarchical output, try: perf report --hierarchy
tools/perf/builtin-report.c +17 -0
@@ -811,6 +811,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "only show processor socket that match with this filter"),OPT_BOOLEAN (0 ,"raw-trace" , &symbol_conf.raw_trace,"Show raw trace event output (do not use print fmt or plugins)" ), +OPT_BOOLEAN (0 ,"hierarchy" , &symbol_conf.report_hierarchy, +"Show entries in a hierarchy" ),OPT_END () }; struct perf_data_file file = {
@@ -920,6 +922,21 @@ repeat: symbol_conf.cumulate_callchain = false; } +if (symbol_conf.report_hierarchy) { +/* disable incompatible options */ + symbol_conf.event_group = false; + symbol_conf.cumulate_callchain = false; + +if (field_order) { +pr_err ("Error: --hierarchy and --fields options cannot be used together\n" ); +parse_options_usage (report_usage, options,"F" ,1 ); +parse_options_usage (NULL , options,"hierarchy" ,0 ); + goto error; + } + + sort__need_collapse = true; + } +/* Force tty output for header output and per-thread stat. */ if (report.header || report.header_only || report.show_threads) use_browser =0 ;
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 30/31 perf hists: Support decaying in hierarchy mode
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
In the hierarchy mode, hist entries should decay their children too. Also update hists__delete_entry() to be able to free child entries.
--- tools/perf/util/hist.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-)
tools/perf/util/hist.c +34 -8
@@ -248,6 +248,8 @@ static void he_stat__decay(struct he_stat *he_stat)/* XXX need decay for weight too? */ } +static void hists__delete_entry (struct hists *hists,struct hist_entry *he); +static bool hists__decay_entry (struct hists *hists,struct hist_entry *he) {u64 prev_period = he->stat.period;
@@ -263,21 +265,45 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) diff = prev_period - he->stat.period; - hists->stats.total_period -= diff; -if (!he->filtered) - hists->stats.total_non_filtered_period -= diff; +if (!he->depth) { + hists->stats.total_period -= diff; +if (!he->filtered) + hists->stats.total_non_filtered_period -= diff; + } + +if (!he->leaf) { +struct hist_entry *child; +struct rb_node *node =rb_first (&he->hroot_out); +while (node) { + child = rb_entry(node, struct hist_entry, rb_node); + node =rb_next (node); + +if (hists__decay_entry (hists, child)) +hists__delete_entry (hists, child); + } + }return he->stat.period ==0 ; }static void hists__delete_entry (struct hists *hists,struct hist_entry *he) { -rb_erase (&he->rb_node, &hists->entries); +struct rb_root *root_in; +struct rb_root *root_out; -if (sort__need_collapse) -rb_erase (&he->rb_node_in, &hists->entries_collapsed); -else -rb_erase (&he->rb_node_in, hists->entries_in); +if (he->parent_he) { + root_in = &he->parent_he->hroot_in; + root_out = &he->parent_he->hroot_out; + }else { +if (sort__need_collapse) + root_in = &hists->entries_collapsed; +else + root_in = hists->entries_in; + root_out = &hists->entries; + } + +rb_erase (&he->rb_node_in, root_in); +rb_erase (&he->rb_node, root_out); --hists->nr_entries;if (!he->filtered)
Signature · 1 line
2.5.0
Arnaldo Carvalho de Melo
maintainerPATCH 31/31 perf top: Add --hierarchy option
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
Support hierarchy output for perf-top using --hierarchy option.
--- tools/perf/Documentation/perf-top.txt | 3 +++ tools/perf/builtin-top.c | 15 +++++++++++++++ 2 files changed, 18 insertions(+)
tools/perf/Documentation/perf-top.txt +3 -0
@@ -233,6 +233,9 @@ Default is to monitor all CPUS. --raw-trace:: When displaying traceevent output, do not use print fmt or plugins. +--hierarchy:: + Enable hierarchy output. + INTERACTIVE PROMPTING KEYS --------------------------
tools/perf/builtin-top.c +15 -0
@@ -1214,6 +1214,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) parse_branch_stack),OPT_BOOLEAN (0 ,"raw-trace" , &symbol_conf.raw_trace,"Show raw trace event output (do not use print fmt or plugins)" ), +OPT_BOOLEAN (0 ,"hierarchy" , &symbol_conf.report_hierarchy, +"Show entries in a hierarchy" ),OPT_END () }; const char * const top_usage[] = {
@@ -1241,6 +1243,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) goto out_delete_evlist; } +if (symbol_conf.report_hierarchy) { +/* disable incompatible options */ + symbol_conf.event_group = false; + symbol_conf.cumulate_callchain = false; + +if (field_order) { +pr_err ("Error: --hierarchy and --fields options cannot be used together\n" ); +parse_options_usage (top_usage, options,"fields" ,0 ); +parse_options_usage (NULL , options,"hierarchy" ,0 ); + goto out_delete_evlist; + } + } + sort__mode =SORT_MODE__TOP ;/* display thread wants entries to be collapsed in a different tree */ sort__need_collapse =1 ;
Signature · 1 line
2.5.0
Quoted 126 lines · Arnaldo Carvalho de Melo
Hi Ingo,
Please consider pulling,
- Arnaldo
The following changes since commit
c2b8d8c55c0235e21c563283f634bcfd2ba7bc1e:Merge tag 'perf-core-for-mingo-2' of
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linuxinto perf/core (2016-02-24 08:20:30 +0100)are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.gittags/perf-core-for-mingo-20160224for you to fetch changes up to
c92fcfde3486fb4b9e476ee5ad5995a62e401cce:perf top: Add --hierarchy option (2016-02-24 20:21:15 -0300)----------------------------------------------------------------
perf/core improvements:User visible:
- Hierarchy histogram mode for 'perf top' and 'perf report', showing multiple levels, one per --sort entry: (Namhyung Kim)
On a mostly idle system:
# perf top --hierarchy -s comm,dsoThen expand some levels and use 'P' to take a snapshot:
# cat perf.hist.0 - 92.32% perf 58.20% perf 22.29% libc-2.22.so 5.97% [kernel] 4.18% libelf-0.165.so 1.69% [unknown] - 4.71% qemu-system-x86 3.10% [kernel] 1.60% qemu-system-x86_64 (deleted) + 2.97% swapper #- Check availability of memory events in 'perf mem': (Jiri Olsa)
On a Intel Broadwell machine:
# perf mem record -e list ldlat-loads : available ldlat-stores: available #- Decode
data_srcvalues (e.g. perf.data files generated by 'perf mem record') in 'perf script': (Jiri Olsa)# perf script perf 693 [1] 4.088652: 1 cpu/mem-loads,ldlat=30/P: ffff88007d0b0f40 68100142 L1 hit|SNP None|TLB L1 or L2 hit|LCK No <SNIP> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Print bpf-output events in 'perf script': (Wang Nan).# perf record -e bpf-output/no-inherit,name=evt/ -e ./test_bpf_output_3.c/map:channel.event=evt/ usleep 1000 # perf script usleep 4882 21384.532523: evt: ffffffff810e97d1 sys_nanosleep ([kernel.kallsyms]) BPF output: 0000: 52 61 69 73 65 20 61 20 Raise a 0008: 42 50 46 20 65 76 65 6e BPF even 0010: 74 21 00 00 t!.. BPF string: "Raise a BPF event!" #Signed-off-by Arnaldo Carvalho de Melo <[email protected]>----------------------------------------------------------------
Jiri Olsa (11):
perf mem record: Check for memory events support
perf mem: Introduceperf_mem_events__namefunction
perf tools: Introduceperf_mem__tlb_scnprintffunction
perf tools: Introduceperf_mem__lvl_scnprintffunction
perf tools: Introduceperf_mem__snp_scnprintffunction
perf tools: Introduceperf_mem__lck_scnprintffunction
perf tools: Changeperf_mem__tlb_scnprintfto return nb of displayed bytes perf tools: Changeperf_mem__lvl_scnprintfto return nb of displayed bytes perf tools: Changeperf_mem__snp_scnprintfto return nb of displayed bytes perf tools: Changeperf_mem__lck_scnprintfto return nb of displayed bytes perf script: Displaydata_srcvaluesNamhyung Kim (18):
perf tools: Add helper functions for some sort keys
perf hists: Basic support of hierarchical report view
perf hists: Resort hist entries with hierarchy
perf hists: Add helper functions for hierarchy mode
perf hists: Introducehist_entry__filter()
perf hists: Support filtering in hierarchy mode
perf hists: Resort after filtering hierarchy
perf hists: Count number of sort keys
perf ui/stdio: Implement hierarchy output mode
perf ui/stdio: Align column header for hierarchy output
perf hists browser: Count number of hierarchy entries
perf hists browser: Support collapsing/expanding whole entries in hierarchy perf hists browser: Implement hierarchy output
perf hists browser: Align column header in hierarchy mode
perf ui/gtk: Implement hierarchy output mode
perf report: Add --hierarchy option
perf hists: Support decaying in hierarchy mode
perf top: Add --hierarchy optionWang Nan (2):
perf tools: Make binary data printer code intrace_eventpublic available perf script: Print bpf-output events in 'perf script'tools/perf/Documentation/perf-report.txt | 3 + tools/perf/Documentation/perf-top.txt | 3 + tools/perf/Documentation/tips.txt | 1 + tools/perf/builtin-mem.c | 22 +- tools/perf/builtin-report.c | 17 ++ tools/perf/builtin-script.c | 113 ++++++- tools/perf/builtin-top.c | 15 + tools/perf/ui/browsers/hists.c | 504 ++++++++++++++++++++++++++++--- tools/perf/ui/gtk/hists.c | 163 +++++++++- tools/perf/ui/hist.c | 3 + tools/perf/ui/stdio/hist.c | 184 ++++++++++- tools/perf/util/ctype.c | 9 + tools/perf/util/debug.c | 75 +++-- tools/perf/util/hist.c | 461 ++++++++++++++++++++++++++-- tools/perf/util/hist.h | 24 ++
Quoted 8 lines
tools/perf/util/mem-events.c | 210 ++++++++++++- tools/perf/util/mem-events.h | 13 + tools/perf/util/sort.c | 285 +++++++++-------- tools/perf/util/sort.h | 14 +- tools/perf/util/symbol.h | 3 +- tools/perf/util/util.c | 37 +++ tools/perf/util/util.h | 22 ++ 22 files changed, 1940 insertions(+), 241 deletions(-)
Pulled, thanks a lot Arnaldo!
Ingo