Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
7588bada
Commit
7588bada
authored
Oct 10, 2011
by
Ingo Molnar
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'perf/core' of
git://github.com/acmel/linux
into perf/core
parents
d48b0e17
64c6f0c7
Changes
35
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
2124 additions
and
804 deletions
+2124
-804
tools/perf/Documentation/perf-annotate.txt
tools/perf/Documentation/perf-annotate.txt
+1
-2
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-report.txt
+16
-1
tools/perf/Documentation/perf-script.txt
tools/perf/Documentation/perf-script.txt
+7
-0
tools/perf/Documentation/perf-top.txt
tools/perf/Documentation/perf-top.txt
+45
-3
tools/perf/Makefile
tools/perf/Makefile
+0
-4
tools/perf/arch/powerpc/Makefile
tools/perf/arch/powerpc/Makefile
+1
-0
tools/perf/arch/powerpc/util/header.c
tools/perf/arch/powerpc/util/header.c
+36
-0
tools/perf/arch/x86/Makefile
tools/perf/arch/x86/Makefile
+1
-0
tools/perf/arch/x86/util/header.c
tools/perf/arch/x86/util/header.c
+59
-0
tools/perf/builtin-annotate.c
tools/perf/builtin-annotate.c
+8
-5
tools/perf/builtin-diff.c
tools/perf/builtin-diff.c
+1
-1
tools/perf/builtin-record.c
tools/perf/builtin-record.c
+15
-0
tools/perf/builtin-report.c
tools/perf/builtin-report.c
+17
-4
tools/perf/builtin-script.c
tools/perf/builtin-script.c
+5
-1
tools/perf/builtin-top.c
tools/perf/builtin-top.c
+267
-170
tools/perf/builtin.h
tools/perf/builtin.h
+0
-1
tools/perf/perf.h
tools/perf/perf.h
+11
-0
tools/perf/util/annotate.h
tools/perf/util/annotate.h
+5
-2
tools/perf/util/evlist.c
tools/perf/util/evlist.c
+6
-0
tools/perf/util/evlist.h
tools/perf/util/evlist.h
+4
-0
tools/perf/util/evsel.c
tools/perf/util/evsel.c
+1
-0
tools/perf/util/header.c
tools/perf/util/header.c
+1114
-31
tools/perf/util/header.h
tools/perf/util/header.h
+27
-2
tools/perf/util/hist.c
tools/perf/util/hist.c
+241
-99
tools/perf/util/hist.h
tools/perf/util/hist.h
+26
-7
tools/perf/util/session.c
tools/perf/util/session.c
+19
-0
tools/perf/util/session.h
tools/perf/util/session.h
+1
-0
tools/perf/util/sort.h
tools/perf/util/sort.h
+1
-0
tools/perf/util/symbol.c
tools/perf/util/symbol.c
+0
-1
tools/perf/util/symbol.h
tools/perf/util/symbol.h
+1
-0
tools/perf/util/top.c
tools/perf/util/top.c
+2
-139
tools/perf/util/top.h
tools/perf/util/top.h
+2
-34
tools/perf/util/ui/browsers/annotate.c
tools/perf/util/ui/browsers/annotate.c
+84
-15
tools/perf/util/ui/browsers/hists.c
tools/perf/util/ui/browsers/hists.c
+100
-46
tools/perf/util/ui/browsers/top.c
tools/perf/util/ui/browsers/top.c
+0
-236
No files found.
tools/perf/Documentation/perf-annotate.txt
View file @
7588bada
...
...
@@ -73,8 +73,7 @@ OPTIONS
CPUs.
--asm-raw::
Show raw instruction encoding of assembly instructions. They
are displayed by default, disable with --no-asm-raw.
Show raw instruction encoding of assembly instructions.
--source::
Interleave source code with assembly code. Enabled by default,
...
...
tools/perf/Documentation/perf-report.txt
View file @
7588bada
...
...
@@ -137,6 +137,21 @@ OPTIONS
-M::
--disassembler-style=:: Set disassembler style for objdump.
--source::
Interleave source code with assembly code. Enabled by default,
disable with --no-source.
--asm-raw::
Show raw instruction encoding of assembly instructions.
--show-total-period:: Show a column with the sum of periods.
-I::
--show-info::
Display extended information about the perf.data file. This adds
information which may be very large and thus may clutter the display.
It currently includes: cpu and numa topology of the host system.
SEE ALSO
--------
linkperf:perf-stat[1]
linkperf:perf-stat[1]
, linkperf:perf-annotate[1]
tools/perf/Documentation/perf-script.txt
View file @
7588bada
...
...
@@ -188,6 +188,13 @@ OPTIONS
CPUs are specified with -: 0-2. Default is to report samples on all
CPUs.
-I::
--show-info::
Display extended information about the perf.data file. This adds
information which may be very large and thus may clutter the display.
It currently includes: cpu and numa topology of the host system.
It can only be used with the perf script report mode.
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-script-perl[1],
...
...
tools/perf/Documentation/perf-top.txt
View file @
7588bada
...
...
@@ -106,6 +106,51 @@ Default is to monitor all CPUS.
--zero::
Zero history across display updates.
-s::
--sort::
Sort by key(s): pid, comm, dso, symbol, parent
-n::
--show-nr-samples::
Show a column with the number of samples.
--show-total-period::
Show a column with the sum of periods.
--dsos::
Only consider symbols in these dsos.
--comms::
Only consider symbols in these comms.
--symbols::
Only consider these symbols.
-M::
--disassembler-style=:: Set disassembler style for objdump.
--source::
Interleave source code with assembly code. Enabled by default,
disable with --no-source.
--asm-raw::
Show raw instruction encoding of assembly instructions.
-G [type,min,order]::
--call-graph::
Display call chains using type, min percent threshold and order.
type can be either:
- flat: single column, linear exposure of call chains.
- graph: use a graph tree, displaying absolute overhead rates.
- fractal: like graph, but displays relative rates. Each branch of
the tree is considered as a new profiled object.
order can be either:
- callee: callee based call graph.
- caller: inverted caller based call graph.
Default: fractal,0.5,callee.
INTERACTIVE PROMPTING KEYS
--------------------------
...
...
@@ -130,9 +175,6 @@ INTERACTIVE PROMPTING KEYS
[S]::
Stop annotation, return to full profile display.
[w]::
Toggle between weighted sum and individual count[E]r profile.
[z]::
Toggle event count zeroing across display updates.
...
...
tools/perf/Makefile
View file @
7588bada
...
...
@@ -466,7 +466,6 @@ else
LIB_OBJS
+=
$(OUTPUT)
util/ui/browsers/annotate.o
LIB_OBJS
+=
$(OUTPUT)
util/ui/browsers/hists.o
LIB_OBJS
+=
$(OUTPUT)
util/ui/browsers/map.o
LIB_OBJS
+=
$(OUTPUT)
util/ui/browsers/top.o
LIB_OBJS
+=
$(OUTPUT)
util/ui/helpline.o
LIB_OBJS
+=
$(OUTPUT)
util/ui/progress.o
LIB_OBJS
+=
$(OUTPUT)
util/ui/util.o
...
...
@@ -729,9 +728,6 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
$(OUTPUT)util/ui/browsers/annotate.o
:
util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC)
-o
$@
-c
$(ALL_CFLAGS)
-DENABLE_SLFUTURE_CONST
$<
$(OUTPUT)util/ui/browsers/top.o
:
util/ui/browsers/top.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC)
-o
$@
-c
$(ALL_CFLAGS)
-DENABLE_SLFUTURE_CONST
$<
$(OUTPUT)util/ui/browsers/hists.o
:
util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC)
-o
$@
-c
$(ALL_CFLAGS)
-DENABLE_SLFUTURE_CONST
$<
...
...
tools/perf/arch/powerpc/Makefile
View file @
7588bada
...
...
@@ -2,3 +2,4 @@ ifndef NO_DWARF
PERF_HAVE_DWARF_REGS
:=
1
LIB_OBJS
+=
$(OUTPUT)
arch
/
$(ARCH)
/util/dwarf-regs.o
endif
LIB_OBJS
+=
$(OUTPUT)
arch
/
$(ARCH)
/util/header.o
tools/perf/arch/powerpc/util/header.c
0 → 100644
View file @
7588bada
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../../util/header.h"
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \
: "=r" (rval)); rval; })
#define SPRN_PVR 0x11F
/* Processor Version Register */
#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF)
/* Version field */
#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF)
/* Revison field */
int
get_cpuid
(
char
*
buffer
,
size_t
sz
)
{
unsigned
long
pvr
;
int
nb
;
pvr
=
mfspr
(
SPRN_PVR
);
nb
=
snprintf
(
buffer
,
sz
,
"%lu,%lu$"
,
PVR_VER
(
pvr
),
PVR_REV
(
pvr
));
/* look for end marker to ensure the entire data fit */
if
(
strchr
(
buffer
,
'$'
))
{
buffer
[
nb
-
1
]
=
'\0'
;
return
0
;
}
return
-
1
;
}
tools/perf/arch/x86/Makefile
View file @
7588bada
...
...
@@ -2,3 +2,4 @@ ifndef NO_DWARF
PERF_HAVE_DWARF_REGS
:=
1
LIB_OBJS
+=
$(OUTPUT)
arch
/
$(ARCH)
/util/dwarf-regs.o
endif
LIB_OBJS
+=
$(OUTPUT)
arch
/
$(ARCH)
/util/header.o
tools/perf/arch/x86/util/header.c
0 → 100644
View file @
7588bada
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../../util/header.h"
static
inline
void
cpuid
(
unsigned
int
op
,
unsigned
int
*
a
,
unsigned
int
*
b
,
unsigned
int
*
c
,
unsigned
int
*
d
)
{
__asm__
__volatile__
(
".byte 0x53
\n\t
cpuid
\n\t
"
"movl %%ebx, %%esi
\n\t
.byte 0x5b"
:
"=a"
(
*
a
),
"=S"
(
*
b
),
"=c"
(
*
c
),
"=d"
(
*
d
)
:
"a"
(
op
));
}
int
get_cpuid
(
char
*
buffer
,
size_t
sz
)
{
unsigned
int
a
,
b
,
c
,
d
,
lvl
;
int
family
=
-
1
,
model
=
-
1
,
step
=
-
1
;
int
nb
;
char
vendor
[
16
];
cpuid
(
0
,
&
lvl
,
&
b
,
&
c
,
&
d
);
strncpy
(
&
vendor
[
0
],
(
char
*
)(
&
b
),
4
);
strncpy
(
&
vendor
[
4
],
(
char
*
)(
&
d
),
4
);
strncpy
(
&
vendor
[
8
],
(
char
*
)(
&
c
),
4
);
vendor
[
12
]
=
'\0'
;
if
(
lvl
>=
1
)
{
cpuid
(
1
,
&
a
,
&
b
,
&
c
,
&
d
);
family
=
(
a
>>
8
)
&
0xf
;
/* bits 11 - 8 */
model
=
(
a
>>
4
)
&
0xf
;
/* Bits 7 - 4 */
step
=
a
&
0xf
;
/* extended family */
if
(
family
==
0xf
)
family
+=
(
a
>>
20
)
&
0xff
;
/* extended model */
if
(
family
>=
0x6
)
model
+=
((
a
>>
16
)
&
0xf
)
<<
4
;
}
nb
=
snprintf
(
buffer
,
sz
,
"%s,%u,%u,%u$"
,
vendor
,
family
,
model
,
step
);
/* look for end marker to ensure the entire data fit */
if
(
strchr
(
buffer
,
'$'
))
{
buffer
[
nb
-
1
]
=
'\0'
;
return
0
;
}
return
-
1
;
}
tools/perf/builtin-annotate.c
View file @
7588bada
...
...
@@ -114,7 +114,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he, int evidx)
print_line
,
full_paths
,
0
,
0
);
}
static
void
hists__find_annotations
(
struct
hists
*
self
,
int
evidx
)
static
void
hists__find_annotations
(
struct
hists
*
self
,
int
evidx
,
int
nr_events
)
{
struct
rb_node
*
nd
=
rb_first
(
&
self
->
entries
),
*
next
;
int
key
=
KEY_RIGHT
;
...
...
@@ -137,7 +138,8 @@ static void hists__find_annotations(struct hists *self, int evidx)
}
if
(
use_browser
>
0
)
{
key
=
hist_entry__tui_annotate
(
he
,
evidx
);
key
=
hist_entry__tui_annotate
(
he
,
evidx
,
nr_events
,
NULL
,
NULL
,
0
);
switch
(
key
)
{
case
KEY_RIGHT
:
next
=
rb_next
(
nd
);
...
...
@@ -215,7 +217,8 @@ static int __cmd_annotate(void)
total_nr_samples
+=
nr_samples
;
hists__collapse_resort
(
hists
);
hists__output_resort
(
hists
);
hists__find_annotations
(
hists
,
pos
->
idx
);
hists__find_annotations
(
hists
,
pos
->
idx
,
session
->
evlist
->
nr_entries
);
}
}
...
...
@@ -269,9 +272,9 @@ static const struct option options[] = {
OPT_STRING
(
'c'
,
"cpu"
,
&
cpu_list
,
"cpu"
,
"list of cpus to profile"
),
OPT_STRING
(
0
,
"symfs"
,
&
symbol_conf
.
symfs
,
"directory"
,
"Look for files with symbols relative to this directory"
),
OPT_BOOLEAN
(
'0'
,
"source"
,
&
symbol_conf
.
annotate_src
,
OPT_BOOLEAN
(
0
,
"source"
,
&
symbol_conf
.
annotate_src
,
"Interleave source code with assembly code (default)"
),
OPT_BOOLEAN
(
'0'
,
"asm-raw"
,
&
symbol_conf
.
annotate_asm_raw
,
OPT_BOOLEAN
(
0
,
"asm-raw"
,
&
symbol_conf
.
annotate_asm_raw
,
"Display raw encoding of assembly instructions (default)"
),
OPT_STRING
(
'M'
,
"disassembler-style"
,
&
disassembler_style
,
"disassembler style"
,
"Specify disassembler style (e.g. -M intel for intel syntax)"
),
...
...
tools/perf/builtin-diff.c
View file @
7588bada
...
...
@@ -162,7 +162,7 @@ static int __cmd_diff(void)
hists__match
(
&
session
[
0
]
->
hists
,
&
session
[
1
]
->
hists
);
hists__fprintf
(
&
session
[
1
]
->
hists
,
&
session
[
0
]
->
hists
,
show_displacement
,
stdout
);
show_displacement
,
true
,
0
,
0
,
stdout
);
out_delete:
for
(
i
=
0
;
i
<
2
;
++
i
)
perf_session__delete
(
session
[
i
]);
...
...
tools/perf/builtin-record.c
View file @
7588bada
...
...
@@ -529,6 +529,19 @@ static int __cmd_record(int argc, const char **argv)
if
(
have_tracepoints
(
&
evsel_list
->
entries
))
perf_header__set_feat
(
&
session
->
header
,
HEADER_TRACE_INFO
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_HOSTNAME
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_OSRELEASE
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_ARCH
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_CPUDESC
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_NRCPUS
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_EVENT_DESC
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_CMDLINE
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_VERSION
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_CPU_TOPOLOGY
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_TOTAL_MEM
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_NUMA_TOPOLOGY
);
perf_header__set_feat
(
&
session
->
header
,
HEADER_CPUID
);
/* 512 kiB: default amount of unprivileged mlocked memory */
if
(
mmap_pages
==
UINT_MAX
)
mmap_pages
=
(
512
*
1024
)
/
page_size
;
...
...
@@ -800,6 +813,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
int
err
=
-
ENOMEM
;
struct
perf_evsel
*
pos
;
perf_header__set_cmdline
(
argc
,
argv
);
evsel_list
=
perf_evlist__new
(
NULL
,
NULL
);
if
(
evsel_list
==
NULL
)
return
-
ENOMEM
;
...
...
tools/perf/builtin-report.c
View file @
7588bada
...
...
@@ -40,6 +40,7 @@ static char const *input_name = "perf.data";
static
bool
force
,
use_tui
,
use_stdio
;
static
bool
hide_unresolved
;
static
bool
dont_use_callchains
;
static
bool
show_full_info
;
static
bool
show_threads
;
static
struct
perf_read_values
show_threads_values
;
...
...
@@ -232,7 +233,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
const
char
*
evname
=
event_name
(
pos
);
hists__fprintf_nr_sample_events
(
hists
,
evname
,
stdout
);
hists__fprintf
(
hists
,
NULL
,
false
,
stdout
);
hists__fprintf
(
hists
,
NULL
,
false
,
true
,
0
,
0
,
stdout
);
fprintf
(
stdout
,
"
\n\n
"
);
}
...
...
@@ -273,6 +274,9 @@ static int __cmd_report(void)
goto
out_delete
;
}
if
(
use_browser
<=
0
)
perf_session__fprintf_info
(
session
,
stdout
,
show_full_info
);
if
(
show_threads
)
perf_read_values_init
(
&
show_threads_values
);
...
...
@@ -327,9 +331,10 @@ static int __cmd_report(void)
goto
out_delete
;
}
if
(
use_browser
>
0
)
perf_evlist__tui_browse_hists
(
session
->
evlist
,
help
);
else
if
(
use_browser
>
0
)
{
perf_evlist__tui_browse_hists
(
session
->
evlist
,
help
,
NULL
,
NULL
,
0
);
}
else
perf_evlist__tty_browse_hists
(
session
->
evlist
,
help
);
out_delete:
...
...
@@ -484,8 +489,16 @@ static const struct option options[] = {
OPT_STRING
(
0
,
"symfs"
,
&
symbol_conf
.
symfs
,
"directory"
,
"Look for files with symbols relative to this directory"
),
OPT_STRING
(
'c'
,
"cpu"
,
&
cpu_list
,
"cpu"
,
"list of cpus to profile"
),
OPT_BOOLEAN
(
'I'
,
"show-info"
,
&
show_full_info
,
"Display extended information about perf.data file"
),
OPT_BOOLEAN
(
0
,
"source"
,
&
symbol_conf
.
annotate_src
,
"Interleave source code with assembly code (default)"
),
OPT_BOOLEAN
(
0
,
"asm-raw"
,
&
symbol_conf
.
annotate_asm_raw
,
"Display raw encoding of assembly instructions (default)"
),
OPT_STRING
(
'M'
,
"disassembler-style"
,
&
disassembler_style
,
"disassembler style"
,
"Specify disassembler style (e.g. -M intel for intel syntax)"
),
OPT_BOOLEAN
(
0
,
"show-total-period"
,
&
symbol_conf
.
show_total_period
,
"Show a column with the sum of periods"
),
OPT_END
()
};
...
...
tools/perf/builtin-script.c
View file @
7588bada
...
...
@@ -22,6 +22,7 @@ static u64 last_timestamp;
static
u64
nr_unordered
;
extern
const
struct
option
record_options
[];
static
bool
no_callchain
;
static
bool
show_full_info
;
static
const
char
*
cpu_list
;
static
DECLARE_BITMAP
(
cpu_bitmap
,
MAX_NR_CPUS
);
...
...
@@ -1083,7 +1084,8 @@ static const struct option options[] = {
"comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr"
,
parse_output_fields
),
OPT_STRING
(
'c'
,
"cpu"
,
&
cpu_list
,
"cpu"
,
"list of cpus to profile"
),
OPT_BOOLEAN
(
'I'
,
"show-info"
,
&
show_full_info
,
"display extended information from perf.data file"
),
OPT_END
()
};
...
...
@@ -1268,6 +1270,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
return
-
1
;
}
perf_session__fprintf_info
(
session
,
stdout
,
show_full_info
);
if
(
!
no_callchain
)
symbol_conf
.
use_callchain
=
true
;
else
...
...
tools/perf/builtin-top.c
View file @
7588bada
...
...
@@ -5,6 +5,7 @@
* any workload, CPU or specific PID.
*
* Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com>
* 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Improvements and fixes by:
*
...
...
@@ -36,6 +37,7 @@
#include "util/parse-events.h"
#include "util/cpumap.h"
#include "util/xyarray.h"
#include "util/sort.h"
#include "util/debug.h"
...
...
@@ -65,12 +67,8 @@
static
struct
perf_top
top
=
{
.
count_filter
=
5
,
.
delay_secs
=
2
,
.
display_weighted
=
-
1
,
.
target_pid
=
-
1
,
.
target_tid
=
-
1
,
.
active_symbols
=
LIST_HEAD_INIT
(
top
.
active_symbols
),
.
active_symbols_lock
=
PTHREAD_MUTEX_INITIALIZER
,
.
active_symbols_cond
=
PTHREAD_COND_INITIALIZER
,
.
freq
=
1000
,
/* 1 KHz */
};
...
...
@@ -78,6 +76,12 @@ static bool system_wide = false;
static
bool
use_tui
,
use_stdio
;
static
bool
sort_has_symbols
;
static
bool
dont_use_callchains
;
static
char
callchain_default_opt
[]
=
"fractal,0.5,callee"
;
static
int
default_interval
=
0
;
static
bool
kptr_restrict_warned
;
...
...
@@ -85,7 +89,6 @@ static bool vmlinux_warned;
static
bool
inherit
=
false
;
static
int
realtime_prio
=
0
;
static
bool
group
=
false
;
static
unsigned
int
page_size
;
static
unsigned
int
mmap_pages
=
128
;
static
bool
dump_symtab
=
false
;
...
...
@@ -93,7 +96,6 @@ static bool dump_symtab = false;
static
struct
winsize
winsize
;
static
const
char
*
sym_filter
=
NULL
;
struct
sym_entry
*
sym_filter_entry_sched
=
NULL
;
static
int
sym_pcnt_filter
=
5
;
/*
...
...
@@ -136,18 +138,18 @@ static void sig_winch_handler(int sig __used)
update_print_entries
(
&
winsize
);
}
static
int
parse_source
(
struct
sym_entry
*
sym
e
)
static
int
parse_source
(
struct
hist_entry
*
h
e
)
{
struct
symbol
*
sym
;
struct
annotation
*
notes
;
struct
map
*
map
;
int
err
=
-
1
;
if
(
!
syme
)
if
(
!
he
||
!
he
->
ms
.
sym
)
return
-
1
;
sym
=
sym_entry__symbol
(
syme
)
;
map
=
syme
->
map
;
sym
=
he
->
ms
.
sym
;
map
=
he
->
ms
.
map
;
/*
* We can't annotate with just /proc/kallsyms
...
...
@@ -175,53 +177,62 @@ static int parse_source(struct sym_entry *syme)
return
err
;
}
err
=
symbol__annotate
(
sym
,
syme
->
map
,
0
);
err
=
symbol__annotate
(
sym
,
map
,
0
);
if
(
err
==
0
)
{
out_assign:
top
.
sym_filter_entry
=
sym
e
;
top
.
sym_filter_entry
=
h
e
;
}
pthread_mutex_unlock
(
&
notes
->
lock
);
return
err
;
}
static
void
__zero_source_counters
(
struct
sym_entry
*
sym
e
)
static
void
__zero_source_counters
(
struct
hist_entry
*
h
e
)
{
struct
symbol
*
sym
=
sym_entry__symbol
(
syme
)
;
struct
symbol
*
sym
=
he
->
ms
.
sym
;
symbol__annotate_zero_histograms
(
sym
);
}
static
void
record_precise_ip
(
struct
sym_entry
*
syme
,
struct
map
*
map
,
int
counter
,
u64
ip
)
static
void
record_precise_ip
(
struct
hist_entry
*
he
,
int
counter
,
u64
ip
)
{
struct
annotation
*
notes
;
struct
symbol
*
sym
;
if
(
syme
!=
top
.
sym_filter_entry
)
if
(
he
==
NULL
||
he
->
ms
.
sym
==
NULL
||
(
he
!=
top
.
sym_filter_entry
&&
use_browser
!=
1
))
return
;
sym
=
sym_entry__symbol
(
syme
)
;
sym
=
he
->
ms
.
sym
;
notes
=
symbol__annotation
(
sym
);
if
(
pthread_mutex_trylock
(
&
notes
->
lock
))
return
;
ip
=
map
->
map_ip
(
map
,
ip
);
symbol__inc_addr_samples
(
sym
,
map
,
counter
,
ip
);
if
(
notes
->
src
==
NULL
&&
symbol__alloc_hist
(
sym
,
top
.
evlist
->
nr_entries
)
<
0
)
{
pthread_mutex_unlock
(
&
notes
->
lock
);
pr_err
(
"Not enough memory for annotating '%s' symbol!
\n
"
,
sym
->
name
);
sleep
(
1
);
return
;
}
ip
=
he
->
ms
.
map
->
map_ip
(
he
->
ms
.
map
,
ip
);
symbol__inc_addr_samples
(
sym
,
he
->
ms
.
map
,
counter
,
ip
);
pthread_mutex_unlock
(
&
notes
->
lock
);
}
static
void
show_details
(
struct
sym_entry
*
sym
e
)
static
void
show_details
(
struct
hist_entry
*
h
e
)
{
struct
annotation
*
notes
;
struct
symbol
*
symbol
;
int
more
;
if
(
!
sym
e
)
if
(
!
h
e
)
return
;
symbol
=
sym_entry__symbol
(
syme
)
;
symbol
=
he
->
ms
.
sym
;
notes
=
symbol__annotation
(
symbol
);
pthread_mutex_lock
(
&
notes
->
lock
);
...
...
@@ -232,7 +243,7 @@ static void show_details(struct sym_entry *syme)
printf
(
"Showing %s for %s
\n
"
,
event_name
(
top
.
sym_evsel
),
symbol
->
name
);
printf
(
" Events Pcnt (>=%d%%)
\n
"
,
sym_pcnt_filter
);
more
=
symbol__annotate_printf
(
symbol
,
syme
->
map
,
top
.
sym_evsel
->
idx
,
more
=
symbol__annotate_printf
(
symbol
,
he
->
ms
.
map
,
top
.
sym_evsel
->
idx
,
0
,
sym_pcnt_filter
,
top
.
print_entries
,
4
);
if
(
top
.
zero
)
symbol__annotate_zero_histogram
(
symbol
,
top
.
sym_evsel
->
idx
);
...
...
@@ -246,21 +257,28 @@ static void show_details(struct sym_entry *syme)
static
const
char
CONSOLE_CLEAR
[]
=
"[H[2J"
;
static
void
__list_insert_active_sym
(
struct
sym_entry
*
syme
)
static
struct
hist_entry
*
perf_session__add_hist_entry
(
struct
perf_session
*
session
,
struct
addr_location
*
al
,
struct
perf_sample
*
sample
,
struct
perf_evsel
*
evsel
)
{
list_add
(
&
syme
->
node
,
&
top
.
active_symbols
);
struct
hist_entry
*
he
;
he
=
__hists__add_entry
(
&
evsel
->
hists
,
al
,
NULL
,
sample
->
period
);
if
(
he
==
NULL
)
return
NULL
;
session
->
hists
.
stats
.
total_period
+=
sample
->
period
;
hists__inc_nr_events
(
&
evsel
->
hists
,
PERF_RECORD_SAMPLE
);
return
he
;
}
static
void
print_sym_table
(
void
)
{
char
bf
[
160
];
int
printed
=
0
;
struct
rb_node
*
nd
;
struct
sym_entry
*
syme
;
struct
rb_root
tmp
=
RB_ROOT
;
const
int
win_width
=
winsize
.
ws_col
-
1
;
int
sym_width
,
dso_width
,
dso_short_width
;
float
sum_ksamples
=
perf_top__decay_samples
(
&
top
,
&
tmp
);
puts
(
CONSOLE_CLEAR
);
...
...
@@ -276,6 +294,7 @@ static void print_sym_table(void)
color_fprintf
(
stdout
,
PERF_COLOR_RED
,
"WARNING:"
);
printf
(
" LOST %"
PRIu64
" events, Check IO/CPU overload
\n
"
,
top
.
total_lost_warned
);
++
printed
;
}
if
(
top
.
sym_filter_entry
)
{
...
...
@@ -283,58 +302,13 @@ static void print_sym_table(void)
return
;
}
perf_top__find_widths
(
&
top
,
&
tmp
,
&
dso_width
,
&
dso_short_width
,
&
sym_width
);
if
(
sym_width
+
dso_width
>
winsize
.
ws_col
-
29
)
{
dso_width
=
dso_short_width
;
if
(
sym_width
+
dso_width
>
winsize
.
ws_col
-
29
)
sym_width
=
winsize
.
ws_col
-
dso_width
-
29
;
}
hists__collapse_resort_threaded
(
&
top
.
sym_evsel
->
hists
);
hists__output_resort_threaded
(
&
top
.
sym_evsel
->
hists
);
hists__decay_entries
(
&
top
.
sym_evsel
->
hists
);
hists__output_recalc_col_len
(
&
top
.
sym_evsel
->
hists
,
winsize
.
ws_row
-
3
);
putchar
(
'\n'
);
if
(
top
.
evlist
->
nr_entries
==
1
)
printf
(
" samples pcnt"
);
else
printf
(
" weight samples pcnt"
);
if
(
verbose
)
printf
(
" RIP "
);
printf
(
" %-*.*s DSO
\n
"
,
sym_width
,
sym_width
,
"function"
);
printf
(
" %s _______ _____"
,
top
.
evlist
->
nr_entries
==
1
?
" "
:
"______"
);
if
(
verbose
)
printf
(
" ________________"
);
printf
(
" %-*.*s"
,
sym_width
,
sym_width
,
graph_line
);
printf
(
" %-*.*s"
,
dso_width
,
dso_width
,
graph_line
);
puts
(
"
\n
"
);
for
(
nd
=
rb_first
(
&
tmp
);
nd
;
nd
=
rb_next
(
nd
))
{
struct
symbol
*
sym
;
double
pcnt
;
syme
=
rb_entry
(
nd
,
struct
sym_entry
,
rb_node
);
sym
=
sym_entry__symbol
(
syme
);
if
(
++
printed
>
top
.
print_entries
||
(
int
)
syme
->
snap_count
<
top
.
count_filter
)
continue
;
pcnt
=
100
.
0
-
(
100
.
0
*
((
sum_ksamples
-
syme
->
snap_count
)
/
sum_ksamples
));
if
(
top
.
evlist
->
nr_entries
==
1
||
!
top
.
display_weighted
)
printf
(
"%20.2f "
,
syme
->
weight
);
else
printf
(
"%9.1f %10ld "
,
syme
->
weight
,
syme
->
snap_count
);
percent_color_fprintf
(
stdout
,
"%4.1f%%"
,
pcnt
);
if
(
verbose
)
printf
(
" %016"
PRIx64
,
sym
->
start
);
printf
(
" %-*.*s"
,
sym_width
,
sym_width
,
sym
->
name
);
printf
(
" %-*.*s
\n
"
,
dso_width
,
dso_width
,
dso_width
>=
syme
->
map
->
dso
->
long_name_len
?
syme
->
map
->
dso
->
long_name
:
syme
->
map
->
dso
->
short_name
);
}
hists__fprintf
(
&
top
.
sym_evsel
->
hists
,
NULL
,
false
,
false
,
winsize
.
ws_row
-
4
-
printed
,
win_width
,
stdout
);
}
static
void
prompt_integer
(
int
*
target
,
const
char
*
msg
)
...
...
@@ -372,10 +346,11 @@ static void prompt_percent(int *target, const char *msg)
*
target
=
tmp
;
}
static
void
prompt_symbol
(
struct
sym
_entry
**
target
,
const
char
*
msg
)
static
void
prompt_symbol
(
struct
hist
_entry
**
target
,
const
char
*
msg
)
{
char
*
buf
=
malloc
(
0
),
*
p
;
struct
sym_entry
*
syme
=
*
target
,
*
n
,
*
found
=
NULL
;
struct
hist_entry
*
syme
=
*
target
,
*
n
,
*
found
=
NULL
;
struct
rb_node
*
next
;
size_t
dummy
=
0
;
/* zero counters of active symbol */
...
...
@@ -392,17 +367,14 @@ static void prompt_symbol(struct sym_entry **target, const char *msg)
if
(
p
)
*
p
=
0
;
pthread_mutex_lock
(
&
top
.
active_symbols_lock
);
syme
=
list_entry
(
top
.
active_symbols
.
next
,
struct
sym_entry
,
node
);
pthread_mutex_unlock
(
&
top
.
active_symbols_lock
);
list_for_each_entry_safe_from
(
syme
,
n
,
&
top
.
active_symbols
,
node
)
{
struct
symbol
*
sym
=
sym_entry__symbol
(
syme
);
if
(
!
strcmp
(
buf
,
sym
->
name
))
{
found
=
syme
;
next
=
rb_first
(
&
top
.
sym_evsel
->
hists
.
entries
);
while
(
next
)
{
n
=
rb_entry
(
next
,
struct
hist_entry
,
rb_node
);
if
(
n
->
ms
.
sym
&&
!
strcmp
(
buf
,
n
->
ms
.
sym
->
name
))
{
found
=
n
;
break
;
}
next
=
rb_next
(
&
n
->
rb_node
);
}
if
(
!
found
)
{
...
...
@@ -421,7 +393,7 @@ static void print_mapped_keys(void)
char
*
name
=
NULL
;
if
(
top
.
sym_filter_entry
)
{
struct
symbol
*
sym
=
sym_entry__symbol
(
top
.
sym_filter_entry
)
;
struct
symbol
*
sym
=
top
.
sym_filter_entry
->
ms
.
sym
;
name
=
sym
->
name
;
}
...
...
@@ -438,9 +410,6 @@ static void print_mapped_keys(void)
fprintf
(
stdout
,
"
\t
[s] annotate symbol.
\t
(%s)
\n
"
,
name
?:
"NULL"
);
fprintf
(
stdout
,
"
\t
[S] stop annotation.
\n
"
);
if
(
top
.
evlist
->
nr_entries
>
1
)
fprintf
(
stdout
,
"
\t
[w] toggle display weighted/count[E]r.
\t
(%d)
\n
"
,
top
.
display_weighted
?
1
:
0
);
fprintf
(
stdout
,
"
\t
[K] hide kernel_symbols symbols.
\t
(%s)
\n
"
,
top
.
hide_kernel_symbols
?
"yes"
:
"no"
);
...
...
@@ -467,8 +436,6 @@ static int key_mapped(int c)
case
'S'
:
return
1
;
case
'E'
:
case
'w'
:
return
top
.
evlist
->
nr_entries
>
1
?
1
:
0
;
default:
break
;
}
...
...
@@ -561,7 +528,7 @@ static void handle_keypress(int c)
if
(
!
top
.
sym_filter_entry
)
break
;
else
{
struct
sym
_entry
*
syme
=
top
.
sym_filter_entry
;
struct
hist
_entry
*
syme
=
top
.
sym_filter_entry
;
top
.
sym_filter_entry
=
NULL
;
__zero_source_counters
(
syme
);
...
...
@@ -570,9 +537,6 @@ static void handle_keypress(int c)
case
'U'
:
top
.
hide_user_symbols
=
!
top
.
hide_user_symbols
;
break
;
case
'w'
:
top
.
display_weighted
=
~
top
.
display_weighted
;
break
;
case
'z'
:
top
.
zero
=
!
top
.
zero
;
break
;
...
...
@@ -581,19 +545,29 @@ static void handle_keypress(int c)
}
}
static
void
perf_top__sort_new_samples
(
void
*
arg
)
{
struct
perf_top
*
t
=
arg
;
perf_top__reset_sample_counters
(
t
);
if
(
t
->
evlist
->
selected
!=
NULL
)
t
->
sym_evsel
=
t
->
evlist
->
selected
;
hists__collapse_resort_threaded
(
&
t
->
sym_evsel
->
hists
);
hists__output_resort_threaded
(
&
t
->
sym_evsel
->
hists
);
hists__decay_entries
(
&
t
->
sym_evsel
->
hists
);
hists__output_recalc_col_len
(
&
t
->
sym_evsel
->
hists
,
winsize
.
ws_row
-
3
);
}
static
void
*
display_thread_tui
(
void
*
arg
__used
)
{
int
err
=
0
;
pthread_mutex_lock
(
&
top
.
active_symbols_lock
);
while
(
list_empty
(
&
top
.
active_symbols
))
{
err
=
pthread_cond_wait
(
&
top
.
active_symbols_cond
,
&
top
.
active_symbols_lock
);
if
(
err
)
break
;
}
pthread_mutex_unlock
(
&
top
.
active_symbols_lock
);
if
(
!
err
)
perf_top__tui_browser
(
&
top
);
const
char
*
help
=
"For a higher level overview, try: perf top --sort comm,dso"
;
perf_top__sort_new_samples
(
&
top
);
perf_evlist__tui_browse_hists
(
top
.
evlist
,
help
,
perf_top__sort_new_samples
,
&
top
,
top
.
delay_secs
);
exit_browser
(
0
);
exit
(
0
);
return
NULL
;
...
...
@@ -645,9 +619,8 @@ static const char *skip_symbols[] = {
NULL
};
static
int
symbol_filter
(
struct
map
*
map
,
struct
symbol
*
sym
)
static
int
symbol_filter
(
struct
map
*
map
__used
,
struct
symbol
*
sym
)
{
struct
sym_entry
*
syme
;
const
char
*
name
=
sym
->
name
;
int
i
;
...
...
@@ -667,16 +640,6 @@ static int symbol_filter(struct map *map, struct symbol *sym)
strstr
(
name
,
"_text_end"
))
return
1
;
syme
=
symbol__priv
(
sym
);
syme
->
map
=
map
;
symbol__annotate_init
(
map
,
sym
);
if
(
!
top
.
sym_filter_entry
&&
sym_filter
&&
!
strcmp
(
name
,
sym_filter
))
{
/* schedule initial sym_filter_entry setup */
sym_filter_entry_sched
=
syme
;
sym_filter
=
NULL
;
}
for
(
i
=
0
;
skip_symbols
[
i
];
i
++
)
{
if
(
!
strcmp
(
skip_symbols
[
i
],
name
))
{
sym
->
ignore
=
true
;
...
...
@@ -691,10 +654,11 @@ static void perf_event__process_sample(const union perf_event *event,
struct
perf_sample
*
sample
,
struct
perf_session
*
session
)
{
struct
symbol
*
parent
=
NULL
;
u64
ip
=
event
->
ip
.
ip
;
struct
sym_entry
*
syme
;
struct
addr_location
al
;
struct
machine
*
machine
;
int
err
;
u8
origin
=
event
->
header
.
misc
&
PERF_RECORD_MISC_CPUMODE_MASK
;
++
top
.
samples
;
...
...
@@ -783,46 +747,41 @@ static void perf_event__process_sample(const union perf_event *event,
sleep
(
5
);
vmlinux_warned
=
true
;
}
return
;
}
/* let's see, whether we need to install initial sym_filter_entry */
if
(
sym_filter_entry_sched
)
{
top
.
sym_filter_entry
=
sym_filter_entry_sched
;
sym_filter_entry_sched
=
NULL
;
if
(
parse_source
(
top
.
sym_filter_entry
)
<
0
)
{
struct
symbol
*
sym
=
sym_entry__symbol
(
top
.
sym_filter_entry
);
pr_err
(
"Can't annotate %s"
,
sym
->
name
);
if
(
top
.
sym_filter_entry
->
map
->
dso
->
symtab_type
==
SYMTAB__KALLSYMS
)
{
pr_err
(
": No vmlinux file was found in the path:
\n
"
);
machine__fprintf_vmlinux_path
(
machine
,
stderr
);
}
else
pr_err
(
".
\n
"
);
exit
(
1
);
}
}
syme
=
symbol__priv
(
al
.
sym
);
if
(
!
al
.
sym
->
ignore
)
{
if
(
al
.
sym
==
NULL
||
!
al
.
sym
->
ignore
)
{
struct
perf_evsel
*
evsel
;
struct
hist_entry
*
he
;
evsel
=
perf_evlist__id2evsel
(
top
.
evlist
,
sample
->
id
);
assert
(
evsel
!=
NULL
);
syme
->
count
[
evsel
->
idx
]
++
;
record_precise_ip
(
syme
,
al
.
map
,
evsel
->
idx
,
ip
);
pthread_mutex_lock
(
&
top
.
active_symbols_lock
);
if
(
list_empty
(
&
syme
->
node
)
||
!
syme
->
node
.
next
)
{
static
bool
first
=
true
;
__list_insert_active_sym
(
syme
);
if
(
first
)
{
pthread_cond_broadcast
(
&
top
.
active_symbols_cond
);
first
=
false
;
}
if
((
sort__has_parent
||
symbol_conf
.
use_callchain
)
&&
sample
->
callchain
)
{
err
=
perf_session__resolve_callchain
(
session
,
al
.
thread
,
sample
->
callchain
,
&
parent
);
if
(
err
)
return
;
}
pthread_mutex_unlock
(
&
top
.
active_symbols_lock
);
he
=
perf_session__add_hist_entry
(
session
,
&
al
,
sample
,
evsel
);
if
(
he
==
NULL
)
{
pr_err
(
"Problem incrementing symbol period, skipping event
\n
"
);
return
;
}
if
(
symbol_conf
.
use_callchain
)
{
err
=
callchain_append
(
he
->
callchain
,
&
session
->
callchain_cursor
,
sample
->
period
);
if
(
err
)
return
;
}
if
(
sort_has_symbols
)
record_precise_ip
(
he
,
evsel
->
idx
,
ip
);
}
return
;
}
static
void
perf_session__mmap_read_idx
(
struct
perf_session
*
self
,
int
idx
)
...
...
@@ -873,7 +832,11 @@ static void start_counters(struct perf_evlist *evlist)
attr
->
read_format
|=
PERF_FORMAT_ID
;
}
if
(
symbol_conf
.
use_callchain
)
attr
->
sample_type
|=
PERF_SAMPLE_CALLCHAIN
;
attr
->
mmap
=
1
;
attr
->
comm
=
1
;
attr
->
inherit
=
inherit
;
try_again:
if
(
perf_evsel__open
(
counter
,
top
.
evlist
->
cpus
,
...
...
@@ -928,10 +891,27 @@ static void start_counters(struct perf_evlist *evlist)
exit
(
0
);
}
static
int
setup_sample_type
(
void
)
{
if
(
!
sort_has_symbols
)
{
if
(
symbol_conf
.
use_callchain
)
{
ui__warning
(
"Selected -g but
\"
sym
\"
not present in --sort/-s."
);
return
-
EINVAL
;
}
}
else
if
(
!
dont_use_callchains
&&
callchain_param
.
mode
!=
CHAIN_NONE
)
{
if
(
callchain_register_param
(
&
callchain_param
)
<
0
)
{
ui__warning
(
"Can't register callchain params.
\n
"
);
return
-
EINVAL
;
}
}
return
0
;
}
static
int
__cmd_top
(
void
)
{
pthread_t
thread
;
int
ret
__used
;
int
ret
;
/*
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
...
...
@@ -940,6 +920,10 @@ static int __cmd_top(void)
if
(
top
.
session
==
NULL
)
return
-
ENOMEM
;
ret
=
setup_sample_type
();
if
(
ret
)
goto
out_delete
;
if
(
top
.
target_tid
!=
-
1
)
perf_event__synthesize_thread_map
(
top
.
evlist
->
threads
,
perf_event__process
,
top
.
session
);
...
...
@@ -980,6 +964,90 @@ static int __cmd_top(void)
ret
=
poll
(
top
.
evlist
->
pollfd
,
top
.
evlist
->
nr_fds
,
100
);
}
out_delete:
perf_session__delete
(
top
.
session
);
top
.
session
=
NULL
;
return
0
;
}
static
int
parse_callchain_opt
(
const
struct
option
*
opt
__used
,
const
char
*
arg
,
int
unset
)
{
char
*
tok
,
*
tok2
;
char
*
endptr
;
/*
* --no-call-graph
*/
if
(
unset
)
{
dont_use_callchains
=
true
;
return
0
;
}
symbol_conf
.
use_callchain
=
true
;
if
(
!
arg
)
return
0
;
tok
=
strtok
((
char
*
)
arg
,
","
);
if
(
!
tok
)
return
-
1
;
/* get the output mode */
if
(
!
strncmp
(
tok
,
"graph"
,
strlen
(
arg
)))
callchain_param
.
mode
=
CHAIN_GRAPH_ABS
;
else
if
(
!
strncmp
(
tok
,
"flat"
,
strlen
(
arg
)))
callchain_param
.
mode
=
CHAIN_FLAT
;
else
if
(
!
strncmp
(
tok
,
"fractal"
,
strlen
(
arg
)))
callchain_param
.
mode
=
CHAIN_GRAPH_REL
;
else
if
(
!
strncmp
(
tok
,
"none"
,
strlen
(
arg
)))
{
callchain_param
.
mode
=
CHAIN_NONE
;
symbol_conf
.
use_callchain
=
false
;
return
0
;
}
else
return
-
1
;
/* get the min percentage */
tok
=
strtok
(
NULL
,
","
);
if
(
!
tok
)
goto
setup
;
callchain_param
.
min_percent
=
strtod
(
tok
,
&
endptr
);
if
(
tok
==
endptr
)
return
-
1
;
/* get the print limit */
tok2
=
strtok
(
NULL
,
","
);
if
(
!
tok2
)
goto
setup
;
if
(
tok2
[
0
]
!=
'c'
)
{
callchain_param
.
print_limit
=
strtod
(
tok2
,
&
endptr
);
tok2
=
strtok
(
NULL
,
","
);
if
(
!
tok2
)
goto
setup
;
}
/* get the call chain order */
if
(
!
strcmp
(
tok2
,
"caller"
))
callchain_param
.
order
=
ORDER_CALLER
;
else
if
(
!
strcmp
(
tok2
,
"callee"
))
callchain_param
.
order
=
ORDER_CALLEE
;
else
return
-
1
;
setup:
if
(
callchain_register_param
(
&
callchain_param
)
<
0
)
{
fprintf
(
stderr
,
"Can't register callchain params
\n
"
);
return
-
1
;
}
return
0
;
}
...
...
@@ -1019,7 +1087,7 @@ static const struct option options[] = {
"put the counters into a counter group"
),
OPT_BOOLEAN
(
'i'
,
"inherit"
,
&
inherit
,
"child tasks inherit counters"
),
OPT_STRING
(
's'
,
"sym-annotate"
,
&
sym_filter
,
"symbol name"
,
OPT_STRING
(
0
,
"sym-annotate"
,
&
sym_filter
,
"symbol name"
,
"symbol to annotate"
),
OPT_BOOLEAN
(
'z'
,
"zero"
,
&
top
.
zero
,
"zero history across updates"
),
...
...
@@ -1033,6 +1101,28 @@ static const struct option options[] = {
OPT_BOOLEAN
(
0
,
"stdio"
,
&
use_stdio
,
"Use the stdio interface"
),
OPT_INCR
(
'v'
,
"verbose"
,
&
verbose
,
"be more verbose (show counter open errors, etc)"
),
OPT_STRING
(
's'
,
"sort"
,
&
sort_order
,
"key[,key2...]"
,
"sort by key(s): pid, comm, dso, symbol, parent"
),
OPT_BOOLEAN
(
'n'
,
"show-nr-samples"
,
&
symbol_conf
.
show_nr_samples
,
"Show a column with the number of samples"
),
OPT_CALLBACK_DEFAULT
(
'G'
,
"call-graph"
,
NULL
,
"output_type,min_percent, call_order"
,
"Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. "
"Default: fractal,0.5,callee"
,
&
parse_callchain_opt
,
callchain_default_opt
),
OPT_BOOLEAN
(
0
,
"show-total-period"
,
&
symbol_conf
.
show_total_period
,
"Show a column with the sum of periods"
),
OPT_STRING
(
0
,
"dsos"
,
&
symbol_conf
.
dso_list_str
,
"dso[,dso...]"
,
"only consider symbols in these dsos"
),
OPT_STRING
(
0
,
"comms"
,
&
symbol_conf
.
comm_list_str
,
"comm[,comm...]"
,
"only consider symbols in these comms"
),
OPT_STRING
(
0
,
"symbols"
,
&
symbol_conf
.
sym_list_str
,
"symbol[,symbol...]"
,
"only consider these symbols"
),
OPT_BOOLEAN
(
0
,
"source"
,
&
symbol_conf
.
annotate_src
,
"Interleave source code with assembly code (default)"
),
OPT_BOOLEAN
(
0
,
"asm-raw"
,
&
symbol_conf
.
annotate_asm_raw
,
"Display raw encoding of assembly instructions (default)"
),
OPT_STRING
(
'M'
,
"disassembler-style"
,
&
disassembler_style
,
"disassembler style"
,
"Specify disassembler style (e.g. -M intel for intel syntax)"
),
OPT_END
()
};
...
...
@@ -1045,18 +1135,16 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
if
(
top
.
evlist
==
NULL
)
return
-
ENOMEM
;
page_size
=
sysconf
(
_SC_PAGE_SIZE
)
;
symbol_conf
.
exclude_other
=
false
;
argc
=
parse_options
(
argc
,
argv
,
options
,
top_usage
,
0
);
if
(
argc
)
usage_with_options
(
top_usage
,
options
);
/*
* XXX For now start disabled, only using TUI if explicitely asked for.
* Change that when handle_keys equivalent gets written, live annotation
* done, etc.
*/
use_browser
=
0
;
if
(
sort_order
==
default_sort_order
)
sort_order
=
"dso,symbol"
;
setup_sorting
(
top_usage
,
options
);
if
(
use_stdio
)
use_browser
=
0
;
...
...
@@ -1119,13 +1207,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
top
.
sym_evsel
=
list_entry
(
top
.
evlist
->
entries
.
next
,
struct
perf_evsel
,
node
);
symbol_conf
.
priv_size
=
(
sizeof
(
struct
sym_entry
)
+
sizeof
(
struct
annotation
)
+
(
top
.
evlist
->
nr_entries
+
1
)
*
sizeof
(
unsigned
long
));
symbol_conf
.
priv_size
=
sizeof
(
struct
annotation
);
symbol_conf
.
try_vmlinux_path
=
(
symbol_conf
.
vmlinux_name
==
NULL
);
if
(
symbol__init
()
<
0
)
return
-
1
;
sort_entry__setup_elide
(
&
sort_dso
,
symbol_conf
.
dso_list
,
"dso"
,
stdout
);
sort_entry__setup_elide
(
&
sort_comm
,
symbol_conf
.
comm_list
,
"comm"
,
stdout
);
sort_entry__setup_elide
(
&
sort_sym
,
symbol_conf
.
sym_list
,
"symbol"
,
stdout
);
/*
* Avoid annotation data structures overhead when symbols aren't on the
* sort list.
*/
sort_has_symbols
=
sort_sym
.
list
.
next
!=
NULL
;
get_term_dimensions
(
&
winsize
);
if
(
top
.
print_entries
==
0
)
{
update_print_entries
(
&
winsize
);
...
...
tools/perf/builtin.h
View file @
7588bada
...
...
@@ -4,7 +4,6 @@
#include "util/util.h"
#include "util/strbuf.h"
extern
const
char
perf_version_string
[];
extern
const
char
perf_usage_string
[];
extern
const
char
perf_more_info_string
[];
...
...
tools/perf/perf.h
View file @
7588bada
...
...
@@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
#include "../../arch/x86/include/asm/unistd.h"
#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
#define CPUINFO_PROC "model name"
#endif
#if defined(__x86_64__)
#include "../../arch/x86/include/asm/unistd.h"
#define rmb() asm volatile("lfence" ::: "memory")
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
#define CPUINFO_PROC "model name"
#endif
#ifdef __powerpc__
#include "../../arch/powerpc/include/asm/unistd.h"
#define rmb() asm volatile ("sync" ::: "memory")
#define cpu_relax() asm volatile ("" ::: "memory");
#define CPUINFO_PROC "cpu"
#endif
#ifdef __s390__
...
...
@@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
# define rmb() asm volatile("" ::: "memory")
#endif
#define cpu_relax() asm volatile("" ::: "memory")
#define CPUINFO_PROC "cpu type"
#endif
#ifdef __hppa__
#include "../../arch/parisc/include/asm/unistd.h"
#define rmb() asm volatile("" ::: "memory")
#define cpu_relax() asm volatile("" ::: "memory");
#define CPUINFO_PROC "cpu"
#endif
#ifdef __sparc__
#include "../../arch/sparc/include/asm/unistd.h"
#define rmb() asm volatile("":::"memory")
#define cpu_relax() asm volatile("":::"memory")
#define CPUINFO_PROC "cpu"
#endif
#ifdef __alpha__
#include "../../arch/alpha/include/asm/unistd.h"
#define rmb() asm volatile("mb" ::: "memory")
#define cpu_relax() asm volatile("" ::: "memory")
#define CPUINFO_PROC "cpu model"
#endif
#ifdef __ia64__
#include "../../arch/ia64/include/asm/unistd.h"
#define rmb() asm volatile ("mf" ::: "memory")
#define cpu_relax() asm volatile ("hint @pause" ::: "memory")
#define CPUINFO_PROC "model name"
#endif
#ifdef __arm__
...
...
@@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
*/
#define rmb() ((void(*)(void))0xffff0fa0)()
#define cpu_relax() asm volatile("":::"memory")
#define CPUINFO_PROC "Processor"
#endif
#ifdef __mips__
...
...
@@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
:
/* no input */
\
: "memory")
#define cpu_relax() asm volatile("" ::: "memory")
#define CPUINFO_PROC "cpu model"
#endif
#include <time.h>
...
...
@@ -171,5 +181,6 @@ struct ip_callchain {
};
extern
bool
perf_host
,
perf_guest
;
extern
const
char
perf_version_string
[];
#endif
tools/perf/util/annotate.h
View file @
7588bada
...
...
@@ -91,13 +91,16 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
#ifdef NO_NEWT_SUPPORT
static
inline
int
symbol__tui_annotate
(
struct
symbol
*
sym
__used
,
struct
map
*
map
__used
,
int
evidx
__used
,
int
refresh
__used
)
int
evidx
__used
,
void
(
*
timer
)(
void
*
arg
)
__used
,
void
*
arg
__used
,
int
delay_secs
__used
)
{
return
0
;
}
#else
int
symbol__tui_annotate
(
struct
symbol
*
sym
,
struct
map
*
map
,
int
evidx
,
int
refresh
);
int
nr_events
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
);
#endif
extern
const
char
*
disassembler_style
;
...
...
tools/perf/util/evlist.c
View file @
7588bada
...
...
@@ -533,3 +533,9 @@ bool perf_evlist__sample_id_all(const struct perf_evlist *evlist)
first
=
list_entry
(
evlist
->
entries
.
next
,
struct
perf_evsel
,
node
);
return
first
->
attr
.
sample_id_all
;
}
void
perf_evlist__set_selected
(
struct
perf_evlist
*
evlist
,
struct
perf_evsel
*
evsel
)
{
evlist
->
selected
=
evsel
;
}
tools/perf/util/evlist.h
View file @
7588bada
...
...
@@ -25,6 +25,7 @@ struct perf_evlist {
struct
pollfd
*
pollfd
;
struct
thread_map
*
threads
;
struct
cpu_map
*
cpus
;
struct
perf_evsel
*
selected
;
};
struct
perf_evsel
;
...
...
@@ -56,6 +57,9 @@ void perf_evlist__munmap(struct perf_evlist *evlist);
void
perf_evlist__disable
(
struct
perf_evlist
*
evlist
);
void
perf_evlist__enable
(
struct
perf_evlist
*
evlist
);
void
perf_evlist__set_selected
(
struct
perf_evlist
*
evlist
,
struct
perf_evsel
*
evsel
);
static
inline
void
perf_evlist__set_maps
(
struct
perf_evlist
*
evlist
,
struct
cpu_map
*
cpus
,
struct
thread_map
*
threads
)
...
...
tools/perf/util/evsel.c
View file @
7588bada
...
...
@@ -39,6 +39,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
evsel
->
idx
=
idx
;
evsel
->
attr
=
*
attr
;
INIT_LIST_HEAD
(
&
evsel
->
node
);
hists__init
(
&
evsel
->
hists
);
}
struct
perf_evsel
*
perf_evsel__new
(
struct
perf_event_attr
*
attr
,
int
idx
)
...
...
tools/perf/util/header.c
View file @
7588bada
...
...
@@ -7,6 +7,7 @@
#include <stdlib.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <sys/utsname.h>
#include "evlist.h"
#include "evsel.h"
...
...
@@ -17,12 +18,19 @@
#include "session.h"
#include "symbol.h"
#include "debug.h"
#include "cpumap.h"
static
bool
no_buildid_cache
=
false
;
static
int
event_count
;
static
struct
perf_trace_event_type
*
events
;
static
u32
header_argc
;
static
const
char
**
header_argv
;
static
int
dsos__write_buildid_table
(
struct
perf_header
*
header
,
int
fd
);
static
int
perf_session__cache_build_ids
(
struct
perf_session
*
session
);
int
perf_header__push_event
(
u64
id
,
const
char
*
name
)
{
if
(
strlen
(
name
)
>
MAX_EVENT_NAME
)
...
...
@@ -110,6 +118,1020 @@ static int write_padded(int fd, const void *bf, size_t count,
return
err
;
}
static
int
do_write_string
(
int
fd
,
const
char
*
str
)
{
u32
len
,
olen
;
int
ret
;
olen
=
strlen
(
str
)
+
1
;
len
=
ALIGN
(
olen
,
NAME_ALIGN
);
/* write len, incl. \0 */
ret
=
do_write
(
fd
,
&
len
,
sizeof
(
len
));
if
(
ret
<
0
)
return
ret
;
return
write_padded
(
fd
,
str
,
olen
,
len
);
}
static
char
*
do_read_string
(
int
fd
,
struct
perf_header
*
ph
)
{
ssize_t
sz
,
ret
;
u32
len
;
char
*
buf
;
sz
=
read
(
fd
,
&
len
,
sizeof
(
len
));
if
(
sz
<
(
ssize_t
)
sizeof
(
len
))
return
NULL
;
if
(
ph
->
needs_swap
)
len
=
bswap_32
(
len
);
buf
=
malloc
(
len
);
if
(
!
buf
)
return
NULL
;
ret
=
read
(
fd
,
buf
,
len
);
if
(
ret
==
(
ssize_t
)
len
)
{
/*
* strings are padded by zeroes
* thus the actual strlen of buf
* may be less than len
*/
return
buf
;
}
free
(
buf
);
return
NULL
;
}
int
perf_header__set_cmdline
(
int
argc
,
const
char
**
argv
)
{
int
i
;
header_argc
=
(
u32
)
argc
;
/* do not include NULL termination */
header_argv
=
calloc
(
argc
,
sizeof
(
char
*
));
if
(
!
header_argv
)
return
-
ENOMEM
;
/*
* must copy argv contents because it gets moved
* around during option parsing
*/
for
(
i
=
0
;
i
<
argc
;
i
++
)
header_argv
[
i
]
=
argv
[
i
];
return
0
;
}
static
int
write_trace_info
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
)
{
return
read_tracing_data
(
fd
,
&
evlist
->
entries
);
}
static
int
write_build_id
(
int
fd
,
struct
perf_header
*
h
,
struct
perf_evlist
*
evlist
__used
)
{
struct
perf_session
*
session
;
int
err
;
session
=
container_of
(
h
,
struct
perf_session
,
header
);
err
=
dsos__write_buildid_table
(
h
,
fd
);
if
(
err
<
0
)
{
pr_debug
(
"failed to write buildid table
\n
"
);
return
err
;
}
if
(
!
no_buildid_cache
)
perf_session__cache_build_ids
(
session
);
return
0
;
}
static
int
write_hostname
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
struct
utsname
uts
;
int
ret
;
ret
=
uname
(
&
uts
);
if
(
ret
<
0
)
return
-
1
;
return
do_write_string
(
fd
,
uts
.
nodename
);
}
static
int
write_osrelease
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
struct
utsname
uts
;
int
ret
;
ret
=
uname
(
&
uts
);
if
(
ret
<
0
)
return
-
1
;
return
do_write_string
(
fd
,
uts
.
release
);
}
static
int
write_arch
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
struct
utsname
uts
;
int
ret
;
ret
=
uname
(
&
uts
);
if
(
ret
<
0
)
return
-
1
;
return
do_write_string
(
fd
,
uts
.
machine
);
}
static
int
write_version
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
return
do_write_string
(
fd
,
perf_version_string
);
}
static
int
write_cpudesc
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
#ifndef CPUINFO_PROC
#define CPUINFO_PROC NULL
#endif
FILE
*
file
;
char
*
buf
=
NULL
;
char
*
s
,
*
p
;
const
char
*
search
=
CPUINFO_PROC
;
size_t
len
=
0
;
int
ret
=
-
1
;
if
(
!
search
)
return
-
1
;
file
=
fopen
(
"/proc/cpuinfo"
,
"r"
);
if
(
!
file
)
return
-
1
;
while
(
getline
(
&
buf
,
&
len
,
file
)
>
0
)
{
ret
=
strncmp
(
buf
,
search
,
strlen
(
search
));
if
(
!
ret
)
break
;
}
if
(
ret
)
goto
done
;
s
=
buf
;
p
=
strchr
(
buf
,
':'
);
if
(
p
&&
*
(
p
+
1
)
==
' '
&&
*
(
p
+
2
))
s
=
p
+
2
;
p
=
strchr
(
s
,
'\n'
);
if
(
p
)
*
p
=
'\0'
;
/* squash extra space characters (branding string) */
p
=
s
;
while
(
*
p
)
{
if
(
isspace
(
*
p
))
{
char
*
r
=
p
+
1
;
char
*
q
=
r
;
*
p
=
' '
;
while
(
*
q
&&
isspace
(
*
q
))
q
++
;
if
(
q
!=
(
p
+
1
))
while
((
*
r
++
=
*
q
++
));
}
p
++
;
}
ret
=
do_write_string
(
fd
,
s
);
done:
free
(
buf
);
fclose
(
file
);
return
ret
;
}
static
int
write_nrcpus
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
long
nr
;
u32
nrc
,
nra
;
int
ret
;
nr
=
sysconf
(
_SC_NPROCESSORS_CONF
);
if
(
nr
<
0
)
return
-
1
;
nrc
=
(
u32
)(
nr
&
UINT_MAX
);
nr
=
sysconf
(
_SC_NPROCESSORS_ONLN
);
if
(
nr
<
0
)
return
-
1
;
nra
=
(
u32
)(
nr
&
UINT_MAX
);
ret
=
do_write
(
fd
,
&
nrc
,
sizeof
(
nrc
));
if
(
ret
<
0
)
return
ret
;
return
do_write
(
fd
,
&
nra
,
sizeof
(
nra
));
}
static
int
write_event_desc
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
)
{
struct
perf_evsel
*
attr
;
u32
nre
=
0
,
nri
,
sz
;
int
ret
;
list_for_each_entry
(
attr
,
&
evlist
->
entries
,
node
)
nre
++
;
/*
* write number of events
*/
ret
=
do_write
(
fd
,
&
nre
,
sizeof
(
nre
));
if
(
ret
<
0
)
return
ret
;
/*
* size of perf_event_attr struct
*/
sz
=
(
u32
)
sizeof
(
attr
->
attr
);
ret
=
do_write
(
fd
,
&
sz
,
sizeof
(
sz
));
if
(
ret
<
0
)
return
ret
;
list_for_each_entry
(
attr
,
&
evlist
->
entries
,
node
)
{
ret
=
do_write
(
fd
,
&
attr
->
attr
,
sz
);
if
(
ret
<
0
)
return
ret
;
/*
* write number of unique id per event
* there is one id per instance of an event
*
* copy into an nri to be independent of the
* type of ids,
*/
nri
=
attr
->
ids
;
ret
=
do_write
(
fd
,
&
nri
,
sizeof
(
nri
));
if
(
ret
<
0
)
return
ret
;
/*
* write event string as passed on cmdline
*/
ret
=
do_write_string
(
fd
,
attr
->
name
);
if
(
ret
<
0
)
return
ret
;
/*
* write unique ids for this event
*/
ret
=
do_write
(
fd
,
attr
->
id
,
attr
->
ids
*
sizeof
(
u64
));
if
(
ret
<
0
)
return
ret
;
}
return
0
;
}
static
int
write_cmdline
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
char
buf
[
MAXPATHLEN
];
char
proc
[
32
];
u32
i
,
n
;
int
ret
;
/*
* actual atual path to perf binary
*/
sprintf
(
proc
,
"/proc/%d/exe"
,
getpid
());
ret
=
readlink
(
proc
,
buf
,
sizeof
(
buf
));
if
(
ret
<=
0
)
return
-
1
;
/* readlink() does not add null termination */
buf
[
ret
]
=
'\0'
;
/* account for binary path */
n
=
header_argc
+
1
;
ret
=
do_write
(
fd
,
&
n
,
sizeof
(
n
));
if
(
ret
<
0
)
return
ret
;
ret
=
do_write_string
(
fd
,
buf
);
if
(
ret
<
0
)
return
ret
;
for
(
i
=
0
;
i
<
header_argc
;
i
++
)
{
ret
=
do_write_string
(
fd
,
header_argv
[
i
]);
if
(
ret
<
0
)
return
ret
;
}
return
0
;
}
#define CORE_SIB_FMT \
"/sys/devices/system/cpu/cpu%d/topology/core_siblings_list"
#define THRD_SIB_FMT \
"/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list"
struct
cpu_topo
{
u32
core_sib
;
u32
thread_sib
;
char
**
core_siblings
;
char
**
thread_siblings
;
};
static
int
build_cpu_topo
(
struct
cpu_topo
*
tp
,
int
cpu
)
{
FILE
*
fp
;
char
filename
[
MAXPATHLEN
];
char
*
buf
=
NULL
,
*
p
;
size_t
len
=
0
;
u32
i
=
0
;
int
ret
=
-
1
;
sprintf
(
filename
,
CORE_SIB_FMT
,
cpu
);
fp
=
fopen
(
filename
,
"r"
);
if
(
!
fp
)
return
-
1
;
if
(
getline
(
&
buf
,
&
len
,
fp
)
<=
0
)
goto
done
;
fclose
(
fp
);
p
=
strchr
(
buf
,
'\n'
);
if
(
p
)
*
p
=
'\0'
;
for
(
i
=
0
;
i
<
tp
->
core_sib
;
i
++
)
{
if
(
!
strcmp
(
buf
,
tp
->
core_siblings
[
i
]))
break
;
}
if
(
i
==
tp
->
core_sib
)
{
tp
->
core_siblings
[
i
]
=
buf
;
tp
->
core_sib
++
;
buf
=
NULL
;
len
=
0
;
}
sprintf
(
filename
,
THRD_SIB_FMT
,
cpu
);
fp
=
fopen
(
filename
,
"r"
);
if
(
!
fp
)
goto
done
;
if
(
getline
(
&
buf
,
&
len
,
fp
)
<=
0
)
goto
done
;
p
=
strchr
(
buf
,
'\n'
);
if
(
p
)
*
p
=
'\0'
;
for
(
i
=
0
;
i
<
tp
->
thread_sib
;
i
++
)
{
if
(
!
strcmp
(
buf
,
tp
->
thread_siblings
[
i
]))
break
;
}
if
(
i
==
tp
->
thread_sib
)
{
tp
->
thread_siblings
[
i
]
=
buf
;
tp
->
thread_sib
++
;
buf
=
NULL
;
}
ret
=
0
;
done:
if
(
fp
)
fclose
(
fp
);
free
(
buf
);
return
ret
;
}
static
void
free_cpu_topo
(
struct
cpu_topo
*
tp
)
{
u32
i
;
if
(
!
tp
)
return
;
for
(
i
=
0
;
i
<
tp
->
core_sib
;
i
++
)
free
(
tp
->
core_siblings
[
i
]);
for
(
i
=
0
;
i
<
tp
->
thread_sib
;
i
++
)
free
(
tp
->
thread_siblings
[
i
]);
free
(
tp
);
}
static
struct
cpu_topo
*
build_cpu_topology
(
void
)
{
struct
cpu_topo
*
tp
;
void
*
addr
;
u32
nr
,
i
;
size_t
sz
;
long
ncpus
;
int
ret
=
-
1
;
ncpus
=
sysconf
(
_SC_NPROCESSORS_CONF
);
if
(
ncpus
<
0
)
return
NULL
;
nr
=
(
u32
)(
ncpus
&
UINT_MAX
);
sz
=
nr
*
sizeof
(
char
*
);
addr
=
calloc
(
1
,
sizeof
(
*
tp
)
+
2
*
sz
);
if
(
!
addr
)
return
NULL
;
tp
=
addr
;
addr
+=
sizeof
(
*
tp
);
tp
->
core_siblings
=
addr
;
addr
+=
sz
;
tp
->
thread_siblings
=
addr
;
for
(
i
=
0
;
i
<
nr
;
i
++
)
{
ret
=
build_cpu_topo
(
tp
,
i
);
if
(
ret
<
0
)
break
;
}
if
(
ret
)
{
free_cpu_topo
(
tp
);
tp
=
NULL
;
}
return
tp
;
}
static
int
write_cpu_topology
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
struct
cpu_topo
*
tp
;
u32
i
;
int
ret
;
tp
=
build_cpu_topology
();
if
(
!
tp
)
return
-
1
;
ret
=
do_write
(
fd
,
&
tp
->
core_sib
,
sizeof
(
tp
->
core_sib
));
if
(
ret
<
0
)
goto
done
;
for
(
i
=
0
;
i
<
tp
->
core_sib
;
i
++
)
{
ret
=
do_write_string
(
fd
,
tp
->
core_siblings
[
i
]);
if
(
ret
<
0
)
goto
done
;
}
ret
=
do_write
(
fd
,
&
tp
->
thread_sib
,
sizeof
(
tp
->
thread_sib
));
if
(
ret
<
0
)
goto
done
;
for
(
i
=
0
;
i
<
tp
->
thread_sib
;
i
++
)
{
ret
=
do_write_string
(
fd
,
tp
->
thread_siblings
[
i
]);
if
(
ret
<
0
)
break
;
}
done:
free_cpu_topo
(
tp
);
return
ret
;
}
static
int
write_total_mem
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
char
*
buf
=
NULL
;
FILE
*
fp
;
size_t
len
=
0
;
int
ret
=
-
1
,
n
;
uint64_t
mem
;
fp
=
fopen
(
"/proc/meminfo"
,
"r"
);
if
(
!
fp
)
return
-
1
;
while
(
getline
(
&
buf
,
&
len
,
fp
)
>
0
)
{
ret
=
strncmp
(
buf
,
"MemTotal:"
,
9
);
if
(
!
ret
)
break
;
}
if
(
!
ret
)
{
n
=
sscanf
(
buf
,
"%*s %"
PRIu64
,
&
mem
);
if
(
n
==
1
)
ret
=
do_write
(
fd
,
&
mem
,
sizeof
(
mem
));
}
free
(
buf
);
fclose
(
fp
);
return
ret
;
}
static
int
write_topo_node
(
int
fd
,
int
node
)
{
char
str
[
MAXPATHLEN
];
char
field
[
32
];
char
*
buf
=
NULL
,
*
p
;
size_t
len
=
0
;
FILE
*
fp
;
u64
mem_total
,
mem_free
,
mem
;
int
ret
=
-
1
;
sprintf
(
str
,
"/sys/devices/system/node/node%d/meminfo"
,
node
);
fp
=
fopen
(
str
,
"r"
);
if
(
!
fp
)
return
-
1
;
while
(
getline
(
&
buf
,
&
len
,
fp
)
>
0
)
{
/* skip over invalid lines */
if
(
!
strchr
(
buf
,
':'
))
continue
;
if
(
sscanf
(
buf
,
"%*s %*d %s %"
PRIu64
,
field
,
&
mem
)
!=
2
)
goto
done
;
if
(
!
strcmp
(
field
,
"MemTotal:"
))
mem_total
=
mem
;
if
(
!
strcmp
(
field
,
"MemFree:"
))
mem_free
=
mem
;
}
fclose
(
fp
);
ret
=
do_write
(
fd
,
&
mem_total
,
sizeof
(
u64
));
if
(
ret
)
goto
done
;
ret
=
do_write
(
fd
,
&
mem_free
,
sizeof
(
u64
));
if
(
ret
)
goto
done
;
ret
=
-
1
;
sprintf
(
str
,
"/sys/devices/system/node/node%d/cpulist"
,
node
);
fp
=
fopen
(
str
,
"r"
);
if
(
!
fp
)
goto
done
;
if
(
getline
(
&
buf
,
&
len
,
fp
)
<=
0
)
goto
done
;
p
=
strchr
(
buf
,
'\n'
);
if
(
p
)
*
p
=
'\0'
;
ret
=
do_write_string
(
fd
,
buf
);
done:
free
(
buf
);
fclose
(
fp
);
return
ret
;
}
static
int
write_numa_topology
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
char
*
buf
=
NULL
;
size_t
len
=
0
;
FILE
*
fp
;
struct
cpu_map
*
node_map
=
NULL
;
char
*
c
;
u32
nr
,
i
,
j
;
int
ret
=
-
1
;
fp
=
fopen
(
"/sys/devices/system/node/online"
,
"r"
);
if
(
!
fp
)
return
-
1
;
if
(
getline
(
&
buf
,
&
len
,
fp
)
<=
0
)
goto
done
;
c
=
strchr
(
buf
,
'\n'
);
if
(
c
)
*
c
=
'\0'
;
node_map
=
cpu_map__new
(
buf
);
if
(
!
node_map
)
goto
done
;
nr
=
(
u32
)
node_map
->
nr
;
ret
=
do_write
(
fd
,
&
nr
,
sizeof
(
nr
));
if
(
ret
<
0
)
goto
done
;
for
(
i
=
0
;
i
<
nr
;
i
++
)
{
j
=
(
u32
)
node_map
->
map
[
i
];
ret
=
do_write
(
fd
,
&
j
,
sizeof
(
j
));
if
(
ret
<
0
)
break
;
ret
=
write_topo_node
(
fd
,
i
);
if
(
ret
<
0
)
break
;
}
done:
free
(
buf
);
fclose
(
fp
);
free
(
node_map
);
return
ret
;
}
/*
* default get_cpuid(): nothing gets recorded
* actual implementation must be in arch/$(ARCH)/util/header.c
*/
int
__attribute__
((
weak
))
get_cpuid
(
char
*
buffer
__used
,
size_t
sz
__used
)
{
return
-
1
;
}
static
int
write_cpuid
(
int
fd
,
struct
perf_header
*
h
__used
,
struct
perf_evlist
*
evlist
__used
)
{
char
buffer
[
64
];
int
ret
;
ret
=
get_cpuid
(
buffer
,
sizeof
(
buffer
));
if
(
!
ret
)
goto
write_it
;
return
-
1
;
write_it:
return
do_write_string
(
fd
,
buffer
);
}
static
void
print_hostname
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
char
*
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# hostname : %s
\n
"
,
str
);
free
(
str
);
}
static
void
print_osrelease
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
char
*
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# os release : %s
\n
"
,
str
);
free
(
str
);
}
static
void
print_arch
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
char
*
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# arch : %s
\n
"
,
str
);
free
(
str
);
}
static
void
print_cpudesc
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
char
*
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# cpudesc : %s
\n
"
,
str
);
free
(
str
);
}
static
void
print_nrcpus
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
ssize_t
ret
;
u32
nr
;
ret
=
read
(
fd
,
&
nr
,
sizeof
(
nr
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
nr
))
nr
=
-
1
;
/* interpreted as error */
if
(
ph
->
needs_swap
)
nr
=
bswap_32
(
nr
);
fprintf
(
fp
,
"# nrcpus online : %u
\n
"
,
nr
);
ret
=
read
(
fd
,
&
nr
,
sizeof
(
nr
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
nr
))
nr
=
-
1
;
/* interpreted as error */
if
(
ph
->
needs_swap
)
nr
=
bswap_32
(
nr
);
fprintf
(
fp
,
"# nrcpus avail : %u
\n
"
,
nr
);
}
static
void
print_version
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
char
*
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# perf version : %s
\n
"
,
str
);
free
(
str
);
}
static
void
print_cmdline
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
ssize_t
ret
;
char
*
str
;
u32
nr
,
i
;
ret
=
read
(
fd
,
&
nr
,
sizeof
(
nr
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
nr
))
return
;
if
(
ph
->
needs_swap
)
nr
=
bswap_32
(
nr
);
fprintf
(
fp
,
"# cmdline : "
);
for
(
i
=
0
;
i
<
nr
;
i
++
)
{
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"%s "
,
str
);
free
(
str
);
}
fputc
(
'\n'
,
fp
);
}
static
void
print_cpu_topology
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
ssize_t
ret
;
u32
nr
,
i
;
char
*
str
;
ret
=
read
(
fd
,
&
nr
,
sizeof
(
nr
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
nr
))
return
;
if
(
ph
->
needs_swap
)
nr
=
bswap_32
(
nr
);
for
(
i
=
0
;
i
<
nr
;
i
++
)
{
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# sibling cores : %s
\n
"
,
str
);
free
(
str
);
}
ret
=
read
(
fd
,
&
nr
,
sizeof
(
nr
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
nr
))
return
;
if
(
ph
->
needs_swap
)
nr
=
bswap_32
(
nr
);
for
(
i
=
0
;
i
<
nr
;
i
++
)
{
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# sibling threads : %s
\n
"
,
str
);
free
(
str
);
}
}
static
void
print_event_desc
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
struct
perf_event_attr
attr
;
uint64_t
id
;
void
*
buf
=
NULL
;
char
*
str
;
u32
nre
,
sz
,
nr
,
i
,
j
,
msz
;
int
ret
;
/* number of events */
ret
=
read
(
fd
,
&
nre
,
sizeof
(
nre
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
nre
))
goto
error
;
if
(
ph
->
needs_swap
)
nre
=
bswap_32
(
nre
);
ret
=
read
(
fd
,
&
sz
,
sizeof
(
sz
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
sz
))
goto
error
;
if
(
ph
->
needs_swap
)
sz
=
bswap_32
(
sz
);
/*
* ensure it is at least to our ABI rev
*/
if
(
sz
<
(
u32
)
sizeof
(
attr
))
goto
error
;
memset
(
&
attr
,
0
,
sizeof
(
attr
));
/* read entire region to sync up to next field */
buf
=
malloc
(
sz
);
if
(
!
buf
)
goto
error
;
msz
=
sizeof
(
attr
);
if
(
sz
<
msz
)
msz
=
sz
;
for
(
i
=
0
;
i
<
nre
;
i
++
)
{
ret
=
read
(
fd
,
buf
,
sz
);
if
(
ret
!=
(
ssize_t
)
sz
)
goto
error
;
if
(
ph
->
needs_swap
)
perf_event__attr_swap
(
buf
);
memcpy
(
&
attr
,
buf
,
msz
);
ret
=
read
(
fd
,
&
nr
,
sizeof
(
nr
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
nr
))
goto
error
;
if
(
ph
->
needs_swap
)
nr
=
bswap_32
(
nr
);
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# event : name = %s, "
,
str
);
free
(
str
);
fprintf
(
fp
,
"type = %d, config = 0x%"
PRIx64
", config1 = 0x%"
PRIx64
", config2 = 0x%"
PRIx64
,
attr
.
type
,
(
u64
)
attr
.
config
,
(
u64
)
attr
.
config1
,
(
u64
)
attr
.
config2
);
fprintf
(
fp
,
", excl_usr = %d, excl_kern = %d"
,
attr
.
exclude_user
,
attr
.
exclude_kernel
);
if
(
nr
)
fprintf
(
fp
,
", id = {"
);
for
(
j
=
0
;
j
<
nr
;
j
++
)
{
ret
=
read
(
fd
,
&
id
,
sizeof
(
id
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
id
))
goto
error
;
if
(
ph
->
needs_swap
)
id
=
bswap_64
(
id
);
if
(
j
)
fputc
(
','
,
fp
);
fprintf
(
fp
,
" %"
PRIu64
,
id
);
}
if
(
nr
&&
j
==
nr
)
fprintf
(
fp
,
" }"
);
fputc
(
'\n'
,
fp
);
}
free
(
buf
);
return
;
error:
fprintf
(
fp
,
"# event desc: not available or unable to read
\n
"
);
}
static
void
print_total_mem
(
struct
perf_header
*
h
__used
,
int
fd
,
FILE
*
fp
)
{
uint64_t
mem
;
ssize_t
ret
;
ret
=
read
(
fd
,
&
mem
,
sizeof
(
mem
));
if
(
ret
!=
sizeof
(
mem
))
goto
error
;
if
(
h
->
needs_swap
)
mem
=
bswap_64
(
mem
);
fprintf
(
fp
,
"# total memory : %"
PRIu64
" kB
\n
"
,
mem
);
return
;
error:
fprintf
(
fp
,
"# total memory : unknown
\n
"
);
}
static
void
print_numa_topology
(
struct
perf_header
*
h
__used
,
int
fd
,
FILE
*
fp
)
{
ssize_t
ret
;
u32
nr
,
c
,
i
;
char
*
str
;
uint64_t
mem_total
,
mem_free
;
/* nr nodes */
ret
=
read
(
fd
,
&
nr
,
sizeof
(
nr
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
nr
))
goto
error
;
if
(
h
->
needs_swap
)
nr
=
bswap_32
(
nr
);
for
(
i
=
0
;
i
<
nr
;
i
++
)
{
/* node number */
ret
=
read
(
fd
,
&
c
,
sizeof
(
c
));
if
(
ret
!=
(
ssize_t
)
sizeof
(
c
))
goto
error
;
if
(
h
->
needs_swap
)
c
=
bswap_32
(
c
);
ret
=
read
(
fd
,
&
mem_total
,
sizeof
(
u64
));
if
(
ret
!=
sizeof
(
u64
))
goto
error
;
ret
=
read
(
fd
,
&
mem_free
,
sizeof
(
u64
));
if
(
ret
!=
sizeof
(
u64
))
goto
error
;
if
(
h
->
needs_swap
)
{
mem_total
=
bswap_64
(
mem_total
);
mem_free
=
bswap_64
(
mem_free
);
}
fprintf
(
fp
,
"# node%u meminfo : total = %"
PRIu64
" kB,"
" free = %"
PRIu64
" kB
\n
"
,
c
,
mem_total
,
mem_free
);
str
=
do_read_string
(
fd
,
h
);
fprintf
(
fp
,
"# node%u cpu list : %s
\n
"
,
c
,
str
);
free
(
str
);
}
return
;
error:
fprintf
(
fp
,
"# numa topology : not available
\n
"
);
}
static
void
print_cpuid
(
struct
perf_header
*
ph
,
int
fd
,
FILE
*
fp
)
{
char
*
str
=
do_read_string
(
fd
,
ph
);
fprintf
(
fp
,
"# cpuid : %s
\n
"
,
str
);
free
(
str
);
}
struct
feature_ops
{
int
(
*
write
)(
int
fd
,
struct
perf_header
*
h
,
struct
perf_evlist
*
evlist
);
void
(
*
print
)(
struct
perf_header
*
h
,
int
fd
,
FILE
*
fp
);
const
char
*
name
;
bool
full_only
;
};
#define FEAT_OPA(n, w, p) \
[n] = { .name = #n, .write = w, .print = p }
#define FEAT_OPF(n, w, p) \
[n] = { .name = #n, .write = w, .print = p, .full_only = true }
static
const
struct
feature_ops
feat_ops
[
HEADER_LAST_FEATURE
]
=
{
FEAT_OPA
(
HEADER_TRACE_INFO
,
write_trace_info
,
NULL
),
FEAT_OPA
(
HEADER_BUILD_ID
,
write_build_id
,
NULL
),
FEAT_OPA
(
HEADER_HOSTNAME
,
write_hostname
,
print_hostname
),
FEAT_OPA
(
HEADER_OSRELEASE
,
write_osrelease
,
print_osrelease
),
FEAT_OPA
(
HEADER_VERSION
,
write_version
,
print_version
),
FEAT_OPA
(
HEADER_ARCH
,
write_arch
,
print_arch
),
FEAT_OPA
(
HEADER_NRCPUS
,
write_nrcpus
,
print_nrcpus
),
FEAT_OPA
(
HEADER_CPUDESC
,
write_cpudesc
,
print_cpudesc
),
FEAT_OPA
(
HEADER_CPUID
,
write_cpuid
,
print_cpuid
),
FEAT_OPA
(
HEADER_TOTAL_MEM
,
write_total_mem
,
print_total_mem
),
FEAT_OPA
(
HEADER_EVENT_DESC
,
write_event_desc
,
print_event_desc
),
FEAT_OPA
(
HEADER_CMDLINE
,
write_cmdline
,
print_cmdline
),
FEAT_OPF
(
HEADER_CPU_TOPOLOGY
,
write_cpu_topology
,
print_cpu_topology
),
FEAT_OPF
(
HEADER_NUMA_TOPOLOGY
,
write_numa_topology
,
print_numa_topology
),
};
struct
header_print_data
{
FILE
*
fp
;
bool
full
;
/* extended list of headers */
};
static
int
perf_file_section__fprintf_info
(
struct
perf_file_section
*
section
,
struct
perf_header
*
ph
,
int
feat
,
int
fd
,
void
*
data
)
{
struct
header_print_data
*
hd
=
data
;
if
(
lseek
(
fd
,
section
->
offset
,
SEEK_SET
)
==
(
off_t
)
-
1
)
{
pr_debug
(
"Failed to lseek to %"
PRIu64
" offset for feature "
"%d, continuing...
\n
"
,
section
->
offset
,
feat
);
return
0
;
}
if
(
feat
<
HEADER_TRACE_INFO
||
feat
>=
HEADER_LAST_FEATURE
)
{
pr_warning
(
"unknown feature %d
\n
"
,
feat
);
return
-
1
;
}
if
(
!
feat_ops
[
feat
].
print
)
return
0
;
if
(
!
feat_ops
[
feat
].
full_only
||
hd
->
full
)
feat_ops
[
feat
].
print
(
ph
,
fd
,
hd
->
fp
);
else
fprintf
(
hd
->
fp
,
"# %s info available, use -I to display
\n
"
,
feat_ops
[
feat
].
name
);
return
0
;
}
int
perf_header__fprintf_info
(
struct
perf_session
*
session
,
FILE
*
fp
,
bool
full
)
{
struct
header_print_data
hd
;
struct
perf_header
*
header
=
&
session
->
header
;
int
fd
=
session
->
fd
;
hd
.
fp
=
fp
;
hd
.
full
=
full
;
perf_header__process_sections
(
header
,
fd
,
&
hd
,
perf_file_section__fprintf_info
);
return
0
;
}
#define dsos__for_each_with_build_id(pos, head) \
list_for_each_entry(pos, head, node) \
if (!pos->has_build_id) \
...
...
@@ -356,15 +1378,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
return
ret
;
}
static
int
do_write_feat
(
int
fd
,
struct
perf_header
*
h
,
int
type
,
struct
perf_file_section
**
p
,
struct
perf_evlist
*
evlist
)
{
int
err
;
int
ret
=
0
;
if
(
perf_header__has_feat
(
h
,
type
))
{
(
*
p
)
->
offset
=
lseek
(
fd
,
0
,
SEEK_CUR
);
err
=
feat_ops
[
type
].
write
(
fd
,
h
,
evlist
);
if
(
err
<
0
)
{
pr_debug
(
"failed to write feature %d
\n
"
,
type
);
/* undo anything written */
lseek
(
fd
,
(
*
p
)
->
offset
,
SEEK_SET
);
return
-
1
;
}
(
*
p
)
->
size
=
lseek
(
fd
,
0
,
SEEK_CUR
)
-
(
*
p
)
->
offset
;
(
*
p
)
++
;
}
return
ret
;
}
static
int
perf_header__adds_write
(
struct
perf_header
*
header
,
struct
perf_evlist
*
evlist
,
int
fd
)
{
int
nr_sections
;
struct
perf_session
*
session
;
struct
perf_file_section
*
feat_sec
;
struct
perf_file_section
*
feat_sec
,
*
p
;
int
sec_size
;
u64
sec_start
;
int
idx
=
0
,
err
;
int
err
;
session
=
container_of
(
header
,
struct
perf_session
,
header
);
...
...
@@ -376,7 +1424,7 @@ static int perf_header__adds_write(struct perf_header *header,
if
(
!
nr_sections
)
return
0
;
feat_sec
=
calloc
(
sizeof
(
*
feat_sec
),
nr_sections
);
feat_sec
=
p
=
calloc
(
sizeof
(
*
feat_sec
),
nr_sections
);
if
(
feat_sec
==
NULL
)
return
-
ENOMEM
;
...
...
@@ -385,36 +1433,69 @@ static int perf_header__adds_write(struct perf_header *header,
sec_start
=
header
->
data_offset
+
header
->
data_size
;
lseek
(
fd
,
sec_start
+
sec_size
,
SEEK_SET
);
if
(
perf_header__has_feat
(
header
,
HEADER_TRACE_INFO
))
{
struct
perf_file_section
*
trace_sec
;
trace_sec
=
&
feat_sec
[
idx
++
];
err
=
do_write_feat
(
fd
,
header
,
HEADER_TRACE_INFO
,
&
p
,
evlist
);
if
(
err
)
goto
out_free
;
/* Write trace info */
trace_sec
->
offset
=
lseek
(
fd
,
0
,
SEEK_CUR
);
read_tracing_data
(
fd
,
&
evlist
->
entries
);
trace_sec
->
size
=
lseek
(
fd
,
0
,
SEEK_CUR
)
-
trace_sec
->
offset
;
err
=
do_write_feat
(
fd
,
header
,
HEADER_BUILD_ID
,
&
p
,
evlist
);
if
(
err
)
{
perf_header__clear_feat
(
header
,
HEADER_BUILD_ID
);
goto
out_free
;
}
if
(
perf_header__has_feat
(
header
,
HEADER_BUILD_ID
))
{
struct
perf_file_section
*
buildid_sec
;
err
=
do_write_feat
(
fd
,
header
,
HEADER_HOSTNAME
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_HOSTNAME
);
buildid_sec
=
&
feat_sec
[
idx
++
];
err
=
do_write_feat
(
fd
,
header
,
HEADER_OSRELEASE
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_OSRELEASE
);
/* Write build-ids */
buildid_sec
->
offset
=
lseek
(
fd
,
0
,
SEEK_CUR
);
err
=
dsos__write_buildid_table
(
header
,
fd
);
if
(
err
<
0
)
{
pr_debug
(
"failed to write buildid table
\n
"
);
goto
out_free
;
}
buildid_sec
->
size
=
lseek
(
fd
,
0
,
SEEK_CUR
)
-
buildid_sec
->
offset
;
if
(
!
no_buildid_cache
)
perf_session__cache_build_ids
(
session
);
}
err
=
do_write_feat
(
fd
,
header
,
HEADER_VERSION
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_VERSION
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_ARCH
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_ARCH
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_NRCPUS
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_NRCPUS
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_CPUDESC
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_CPUDESC
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_CPUID
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_CPUID
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_TOTAL_MEM
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_TOTAL_MEM
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_CMDLINE
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_CMDLINE
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_EVENT_DESC
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_EVENT_DESC
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_CPU_TOPOLOGY
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_CPU_TOPOLOGY
);
err
=
do_write_feat
(
fd
,
header
,
HEADER_NUMA_TOPOLOGY
,
&
p
,
evlist
);
if
(
err
)
perf_header__clear_feat
(
header
,
HEADER_NUMA_TOPOLOGY
);
lseek
(
fd
,
sec_start
,
SEEK_SET
);
/*
* may write more than needed due to dropped feature, but
* this is okay, reader will skip the mising entries
*/
err
=
do_write
(
fd
,
feat_sec
,
sec_size
);
if
(
err
<
0
)
pr_debug
(
"failed to write feature section
\n
"
);
...
...
@@ -554,9 +1635,10 @@ static int perf_header__getbuffer64(struct perf_header *header,
}
int
perf_header__process_sections
(
struct
perf_header
*
header
,
int
fd
,
void
*
data
,
int
(
*
process
)(
struct
perf_file_section
*
section
,
struct
perf_header
*
ph
,
int
feat
,
int
fd
))
struct
perf_header
*
ph
,
int
feat
,
int
fd
,
void
*
data
))
{
struct
perf_file_section
*
feat_sec
;
int
nr_sections
;
...
...
@@ -584,7 +1666,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
if
(
perf_header__has_feat
(
header
,
feat
))
{
struct
perf_file_section
*
sec
=
&
feat_sec
[
idx
++
];
err
=
process
(
sec
,
header
,
feat
,
fd
);
err
=
process
(
sec
,
header
,
feat
,
fd
,
data
);
if
(
err
<
0
)
break
;
}
...
...
@@ -796,7 +1878,7 @@ static int perf_header__read_build_ids(struct perf_header *header,
static
int
perf_file_section__process
(
struct
perf_file_section
*
section
,
struct
perf_header
*
ph
,
int
feat
,
int
fd
)
int
feat
,
int
fd
,
void
*
data
__used
)
{
if
(
lseek
(
fd
,
section
->
offset
,
SEEK_SET
)
==
(
off_t
)
-
1
)
{
pr_debug
(
"Failed to lseek to %"
PRIu64
" offset for feature "
...
...
@@ -935,7 +2017,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
event_count
=
f_header
.
event_types
.
size
/
sizeof
(
struct
perf_trace_event_type
);
}
perf_header__process_sections
(
header
,
fd
,
perf_file_section__process
);
perf_header__process_sections
(
header
,
fd
,
NULL
,
perf_file_section__process
);
lseek
(
fd
,
header
->
data_offset
,
SEEK_SET
);
...
...
tools/perf/util/header.h
View file @
7588bada
...
...
@@ -12,6 +12,20 @@
enum
{
HEADER_TRACE_INFO
=
1
,
HEADER_BUILD_ID
,
HEADER_HOSTNAME
,
HEADER_OSRELEASE
,
HEADER_VERSION
,
HEADER_ARCH
,
HEADER_NRCPUS
,
HEADER_CPUDESC
,
HEADER_CPUID
,
HEADER_TOTAL_MEM
,
HEADER_CMDLINE
,
HEADER_EVENT_DESC
,
HEADER_CPU_TOPOLOGY
,
HEADER_NUMA_TOPOLOGY
,
HEADER_LAST_FEATURE
,
};
...
...
@@ -68,10 +82,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
void
perf_header__clear_feat
(
struct
perf_header
*
header
,
int
feat
);
bool
perf_header__has_feat
(
const
struct
perf_header
*
header
,
int
feat
);
int
perf_header__set_cmdline
(
int
argc
,
const
char
**
argv
);
int
perf_header__process_sections
(
struct
perf_header
*
header
,
int
fd
,
void
*
data
,
int
(
*
process
)(
struct
perf_file_section
*
section
,
struct
perf_header
*
ph
,
int
feat
,
int
fd
));
struct
perf_header
*
ph
,
int
feat
,
int
fd
,
void
*
data
));
int
perf_header__fprintf_info
(
struct
perf_session
*
s
,
FILE
*
fp
,
bool
full
);
int
build_id_cache__add_s
(
const
char
*
sbuild_id
,
const
char
*
debugdir
,
const
char
*
name
,
bool
is_kallsyms
);
...
...
@@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
struct
perf_session
*
session
);
int
perf_event__process_build_id
(
union
perf_event
*
event
,
struct
perf_session
*
session
);
/*
* arch specific callback
*/
int
get_cpuid
(
char
*
buffer
,
size_t
sz
);
#endif
/* __PERF_HEADER_H */
tools/perf/util/hist.c
View file @
7588bada
...
...
@@ -18,56 +18,56 @@ struct callchain_param callchain_param = {
.
order
=
ORDER_CALLEE
};
u16
hists__col_len
(
struct
hists
*
self
,
enum
hist_column
col
)
u16
hists__col_len
(
struct
hists
*
hists
,
enum
hist_column
col
)
{
return
self
->
col_len
[
col
];
return
hists
->
col_len
[
col
];
}
void
hists__set_col_len
(
struct
hists
*
self
,
enum
hist_column
col
,
u16
len
)
void
hists__set_col_len
(
struct
hists
*
hists
,
enum
hist_column
col
,
u16
len
)
{
self
->
col_len
[
col
]
=
len
;
hists
->
col_len
[
col
]
=
len
;
}
bool
hists__new_col_len
(
struct
hists
*
self
,
enum
hist_column
col
,
u16
len
)
bool
hists__new_col_len
(
struct
hists
*
hists
,
enum
hist_column
col
,
u16
len
)
{
if
(
len
>
hists__col_len
(
self
,
col
))
{
hists__set_col_len
(
self
,
col
,
len
);
if
(
len
>
hists__col_len
(
hists
,
col
))
{
hists__set_col_len
(
hists
,
col
,
len
);
return
true
;
}
return
false
;
}
static
void
hists__reset_col_len
(
struct
hists
*
self
)
static
void
hists__reset_col_len
(
struct
hists
*
hists
)
{
enum
hist_column
col
;
for
(
col
=
0
;
col
<
HISTC_NR_COLS
;
++
col
)
hists__set_col_len
(
self
,
col
,
0
);
hists__set_col_len
(
hists
,
col
,
0
);
}
static
void
hists__calc_col_len
(
struct
hists
*
self
,
struct
hist_entry
*
h
)
static
void
hists__calc_col_len
(
struct
hists
*
hists
,
struct
hist_entry
*
h
)
{
u16
len
;
if
(
h
->
ms
.
sym
)
hists__new_col_len
(
self
,
HISTC_SYMBOL
,
h
->
ms
.
sym
->
namelen
);
hists__new_col_len
(
hists
,
HISTC_SYMBOL
,
h
->
ms
.
sym
->
namelen
);
else
{
const
unsigned
int
unresolved_col_width
=
BITS_PER_LONG
/
4
;
if
(
hists__col_len
(
self
,
HISTC_DSO
)
<
unresolved_col_width
&&
if
(
hists__col_len
(
hists
,
HISTC_DSO
)
<
unresolved_col_width
&&
!
symbol_conf
.
col_width_list_str
&&
!
symbol_conf
.
field_sep
&&
!
symbol_conf
.
dso_list
)
hists__set_col_len
(
self
,
HISTC_DSO
,
hists__set_col_len
(
hists
,
HISTC_DSO
,
unresolved_col_width
);
}
len
=
thread__comm_len
(
h
->
thread
);
if
(
hists__new_col_len
(
self
,
HISTC_COMM
,
len
))
hists__set_col_len
(
self
,
HISTC_THREAD
,
len
+
6
);
if
(
hists__new_col_len
(
hists
,
HISTC_COMM
,
len
))
hists__set_col_len
(
hists
,
HISTC_THREAD
,
len
+
6
);
if
(
h
->
ms
.
map
)
{
len
=
dso__name_len
(
h
->
ms
.
map
->
dso
);
hists__new_col_len
(
self
,
HISTC_DSO
,
len
);
hists__new_col_len
(
hists
,
HISTC_DSO
,
len
);
}
}
...
...
@@ -92,6 +92,41 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self,
}
}
static
void
hist_entry__decay
(
struct
hist_entry
*
he
)
{
he
->
period
=
(
he
->
period
*
7
)
/
8
;
he
->
nr_events
=
(
he
->
nr_events
*
7
)
/
8
;
}
static
bool
hists__decay_entry
(
struct
hists
*
hists
,
struct
hist_entry
*
he
)
{
hists
->
stats
.
total_period
-=
he
->
period
;
hist_entry__decay
(
he
);
hists
->
stats
.
total_period
+=
he
->
period
;
return
he
->
period
==
0
;
}
void
hists__decay_entries
(
struct
hists
*
hists
)
{
struct
rb_node
*
next
=
rb_first
(
&
hists
->
entries
);
struct
hist_entry
*
n
;
while
(
next
)
{
n
=
rb_entry
(
next
,
struct
hist_entry
,
rb_node
);
next
=
rb_next
(
&
n
->
rb_node
);
if
(
hists__decay_entry
(
hists
,
n
))
{
rb_erase
(
&
n
->
rb_node
,
&
hists
->
entries
);
if
(
sort__need_collapse
)
rb_erase
(
&
n
->
rb_node_in
,
&
hists
->
entries_collapsed
);
hist_entry__free
(
n
);
--
hists
->
nr_entries
;
}
}
}
/*
* histogram, sorted on item, collects periods
*/
...
...
@@ -113,11 +148,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
return
self
;
}
static
void
hists__inc_nr_entries
(
struct
hists
*
self
,
struct
hist_entry
*
h
)
static
void
hists__inc_nr_entries
(
struct
hists
*
hists
,
struct
hist_entry
*
h
)
{
if
(
!
h
->
filtered
)
{
hists__calc_col_len
(
self
,
h
);
++
self
->
nr_entries
;
hists__calc_col_len
(
hists
,
h
);
++
hists
->
nr_entries
;
hists
->
stats
.
total_period
+=
h
->
period
;
}
}
...
...
@@ -128,11 +164,11 @@ static u8 symbol__parent_filter(const struct symbol *parent)
return
0
;
}
struct
hist_entry
*
__hists__add_entry
(
struct
hists
*
self
,
struct
hist_entry
*
__hists__add_entry
(
struct
hists
*
hists
,
struct
addr_location
*
al
,
struct
symbol
*
sym_parent
,
u64
period
)
{
struct
rb_node
**
p
=
&
self
->
entries
.
rb_node
;
struct
rb_node
**
p
;
struct
rb_node
*
parent
=
NULL
;
struct
hist_entry
*
he
;
struct
hist_entry
entry
=
{
...
...
@@ -150,9 +186,13 @@ struct hist_entry *__hists__add_entry(struct hists *self,
};
int
cmp
;
pthread_mutex_lock
(
&
hists
->
lock
);
p
=
&
hists
->
entries_in
->
rb_node
;
while
(
*
p
!=
NULL
)
{
parent
=
*
p
;
he
=
rb_entry
(
parent
,
struct
hist_entry
,
rb_node
);
he
=
rb_entry
(
parent
,
struct
hist_entry
,
rb_node
_in
);
cmp
=
hist_entry__cmp
(
&
entry
,
he
);
...
...
@@ -170,12 +210,14 @@ struct hist_entry *__hists__add_entry(struct hists *self,
he
=
hist_entry__new
(
&
entry
);
if
(
!
he
)
return
NULL
;
rb_link_node
(
&
he
->
rb_node
,
parent
,
p
);
rb_
insert_color
(
&
he
->
rb_node
,
&
self
->
entries
);
hists__inc_nr_entries
(
self
,
he
);
goto
out_unlock
;
rb_
link_node
(
&
he
->
rb_node_in
,
parent
,
p
);
rb_insert_color
(
&
he
->
rb_node_in
,
hists
->
entries_in
);
out:
hist_entry__add_cpumode_period
(
he
,
al
->
cpumode
,
period
);
out_unlock:
pthread_mutex_unlock
(
&
hists
->
lock
);
return
he
;
}
...
...
@@ -222,7 +264,7 @@ void hist_entry__free(struct hist_entry *he)
* collapse the histogram
*/
static
bool
hists__collapse_insert_entry
(
struct
hists
*
self
,
static
bool
hists__collapse_insert_entry
(
struct
hists
*
hists
,
struct
rb_root
*
root
,
struct
hist_entry
*
he
)
{
...
...
@@ -233,15 +275,16 @@ static bool hists__collapse_insert_entry(struct hists *self,
while
(
*
p
!=
NULL
)
{
parent
=
*
p
;
iter
=
rb_entry
(
parent
,
struct
hist_entry
,
rb_node
);
iter
=
rb_entry
(
parent
,
struct
hist_entry
,
rb_node
_in
);
cmp
=
hist_entry__collapse
(
iter
,
he
);
if
(
!
cmp
)
{
iter
->
period
+=
he
->
period
;
iter
->
nr_events
+=
he
->
nr_events
;
if
(
symbol_conf
.
use_callchain
)
{
callchain_cursor_reset
(
&
self
->
callchain_cursor
);
callchain_merge
(
&
self
->
callchain_cursor
,
iter
->
callchain
,
callchain_cursor_reset
(
&
hists
->
callchain_cursor
);
callchain_merge
(
&
hists
->
callchain_cursor
,
iter
->
callchain
,
he
->
callchain
);
}
hist_entry__free
(
he
);
...
...
@@ -254,35 +297,57 @@ static bool hists__collapse_insert_entry(struct hists *self,
p
=
&
(
*
p
)
->
rb_right
;
}
rb_link_node
(
&
he
->
rb_node
,
parent
,
p
);
rb_insert_color
(
&
he
->
rb_node
,
root
);
rb_link_node
(
&
he
->
rb_node
_in
,
parent
,
p
);
rb_insert_color
(
&
he
->
rb_node
_in
,
root
);
return
true
;
}
void
hists__collapse_resort
(
struct
hists
*
self
)
static
struct
rb_root
*
hists__get_rotate_entries_in
(
struct
hists
*
hists
)
{
struct
rb_root
*
root
;
pthread_mutex_lock
(
&
hists
->
lock
);
root
=
hists
->
entries_in
;
if
(
++
hists
->
entries_in
>
&
hists
->
entries_in_array
[
1
])
hists
->
entries_in
=
&
hists
->
entries_in_array
[
0
];
pthread_mutex_unlock
(
&
hists
->
lock
);
return
root
;
}
static
void
__hists__collapse_resort
(
struct
hists
*
hists
,
bool
threaded
)
{
struct
rb_root
tmp
;
struct
rb_root
*
root
;
struct
rb_node
*
next
;
struct
hist_entry
*
n
;
if
(
!
sort__need_collapse
)
if
(
!
sort__need_collapse
&&
!
threaded
)
return
;
tmp
=
RB_ROOT
;
next
=
rb_first
(
&
self
->
entries
);
self
->
nr_entries
=
0
;
hists__reset_col_len
(
self
);
root
=
hists__get_rotate_entries_in
(
hists
);
next
=
rb_first
(
root
);
hists
->
stats
.
total_period
=
0
;
while
(
next
)
{
n
=
rb_entry
(
next
,
struct
hist_entry
,
rb_node
);
next
=
rb_next
(
&
n
->
rb_node
);
n
=
rb_entry
(
next
,
struct
hist_entry
,
rb_node
_in
);
next
=
rb_next
(
&
n
->
rb_node
_in
);
rb_erase
(
&
n
->
rb_node
,
&
self
->
entries
);
if
(
hists__collapse_insert_entry
(
self
,
&
tmp
,
n
))
hists__inc_nr_entries
(
self
,
n
);
rb_erase
(
&
n
->
rb_node
_in
,
root
);
if
(
hists__collapse_insert_entry
(
hists
,
&
hists
->
entries_collapsed
,
n
))
hists__inc_nr_entries
(
hists
,
n
);
}
}
self
->
entries
=
tmp
;
void
hists__collapse_resort
(
struct
hists
*
hists
)
{
return
__hists__collapse_resort
(
hists
,
false
);
}
void
hists__collapse_resort_threaded
(
struct
hists
*
hists
)
{
return
__hists__collapse_resort
(
hists
,
true
);
}
/*
...
...
@@ -315,31 +380,43 @@ static void __hists__insert_output_entry(struct rb_root *entries,
rb_insert_color
(
&
he
->
rb_node
,
entries
);
}
void
hists__output_resort
(
struct
hists
*
self
)
static
void
__hists__output_resort
(
struct
hists
*
hists
,
bool
threaded
)
{
struct
rb_root
tmp
;
struct
rb_root
*
root
;
struct
rb_node
*
next
;
struct
hist_entry
*
n
;
u64
min_callchain_hits
;
min_callchain_hits
=
self
->
stats
.
total_period
*
(
callchain_param
.
min_percent
/
100
);
min_callchain_hits
=
hists
->
stats
.
total_period
*
(
callchain_param
.
min_percent
/
100
);
if
(
sort__need_collapse
||
threaded
)
root
=
&
hists
->
entries_collapsed
;
else
root
=
hists
->
entries_in
;
tmp
=
RB_ROOT
;
next
=
rb_first
(
&
self
->
entries
)
;
next
=
rb_first
(
root
)
;
hists
->
entries
=
RB_ROOT
;
self
->
nr_entries
=
0
;
hists__reset_col_len
(
self
);
hists
->
nr_entries
=
0
;
hists__reset_col_len
(
hists
);
while
(
next
)
{
n
=
rb_entry
(
next
,
struct
hist_entry
,
rb_node
);
next
=
rb_next
(
&
n
->
rb_node
);
n
=
rb_entry
(
next
,
struct
hist_entry
,
rb_node
_in
);
next
=
rb_next
(
&
n
->
rb_node
_in
);
rb_erase
(
&
n
->
rb_node
,
&
self
->
entries
);
__hists__insert_output_entry
(
&
tmp
,
n
,
min_callchain_hits
);
hists__inc_nr_entries
(
self
,
n
);
__hists__insert_output_entry
(
&
hists
->
entries
,
n
,
min_callchain_hits
);
hists__inc_nr_entries
(
hists
,
n
);
}
}
void
hists__output_resort
(
struct
hists
*
hists
)
{
return
__hists__output_resort
(
hists
,
false
);
}
self
->
entries
=
tmp
;
void
hists__output_resort_threaded
(
struct
hists
*
hists
)
{
return
__hists__output_resort
(
hists
,
true
);
}
static
size_t
callchain__fprintf_left_margin
(
FILE
*
fp
,
int
left_margin
)
...
...
@@ -594,6 +671,21 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
return
ret
;
}
void
hists__output_recalc_col_len
(
struct
hists
*
hists
,
int
max_rows
)
{
struct
rb_node
*
next
=
rb_first
(
&
hists
->
entries
);
struct
hist_entry
*
n
;
int
row
=
0
;
hists__reset_col_len
(
hists
);
while
(
next
&&
row
++
<
max_rows
)
{
n
=
rb_entry
(
next
,
struct
hist_entry
,
rb_node
);
hists__calc_col_len
(
hists
,
n
);
next
=
rb_next
(
&
n
->
rb_node
);
}
}
int
hist_entry__snprintf
(
struct
hist_entry
*
self
,
char
*
s
,
size_t
size
,
struct
hists
*
hists
,
struct
hists
*
pair_hists
,
bool
show_displacement
,
long
displacement
,
...
...
@@ -664,6 +756,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
ret
+=
snprintf
(
s
+
ret
,
size
-
ret
,
"%11"
PRIu64
,
nr_events
);
}
if
(
symbol_conf
.
show_total_period
)
{
if
(
sep
)
ret
+=
snprintf
(
s
+
ret
,
size
-
ret
,
"%c%"
PRIu64
,
*
sep
,
period
);
else
ret
+=
snprintf
(
s
+
ret
,
size
-
ret
,
" %12"
PRIu64
,
period
);
}
if
(
pair_hists
)
{
char
bf
[
32
];
double
old_percent
=
0
,
new_percent
=
0
,
diff
;
...
...
@@ -710,12 +809,16 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
return
ret
;
}
int
hist_entry__fprintf
(
struct
hist_entry
*
self
,
struct
hists
*
hists
,
int
hist_entry__fprintf
(
struct
hist_entry
*
he
,
size_t
size
,
struct
hists
*
hists
,
struct
hists
*
pair_hists
,
bool
show_displacement
,
long
displacement
,
FILE
*
fp
,
u64
session_total
)
{
char
bf
[
512
];
hist_entry__snprintf
(
self
,
bf
,
sizeof
(
bf
),
hists
,
pair_hists
,
if
(
size
==
0
||
size
>
sizeof
(
bf
))
size
=
sizeof
(
bf
);
hist_entry__snprintf
(
he
,
bf
,
size
,
hists
,
pair_hists
,
show_displacement
,
displacement
,
true
,
session_total
);
return
fprintf
(
fp
,
"%s
\n
"
,
bf
);
...
...
@@ -738,8 +841,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
left_margin
);
}
size_t
hists__fprintf
(
struct
hists
*
self
,
struct
hists
*
pair
,
bool
show_displacement
,
FILE
*
fp
)
size_t
hists__fprintf
(
struct
hists
*
hists
,
struct
hists
*
pair
,
bool
show_displacement
,
bool
show_header
,
int
max_rows
,
int
max_cols
,
FILE
*
fp
)
{
struct
sort_entry
*
se
;
struct
rb_node
*
nd
;
...
...
@@ -749,9 +853,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
unsigned
int
width
;
const
char
*
sep
=
symbol_conf
.
field_sep
;
const
char
*
col_width
=
symbol_conf
.
col_width_list_str
;
int
nr_rows
=
0
;
init_rem_hits
();
if
(
!
show_header
)
goto
print_entries
;
fprintf
(
fp
,
"# %s"
,
pair
?
"Baseline"
:
"Overhead"
);
if
(
symbol_conf
.
show_nr_samples
)
{
...
...
@@ -761,6 +869,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
fputs
(
" Samples "
,
fp
);
}
if
(
symbol_conf
.
show_total_period
)
{
if
(
sep
)
ret
+=
fprintf
(
fp
,
"%cPeriod"
,
*
sep
);
else
ret
+=
fprintf
(
fp
,
" Period "
);
}
if
(
symbol_conf
.
show_cpu_utilization
)
{
if
(
sep
)
{
ret
+=
fprintf
(
fp
,
"%csys"
,
*
sep
);
...
...
@@ -803,18 +918,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
width
=
strlen
(
se
->
se_header
);
if
(
symbol_conf
.
col_width_list_str
)
{
if
(
col_width
)
{
hists__set_col_len
(
self
,
se
->
se_width_idx
,
hists__set_col_len
(
hists
,
se
->
se_width_idx
,
atoi
(
col_width
));
col_width
=
strchr
(
col_width
,
','
);
if
(
col_width
)
++
col_width
;
}
}
if
(
!
hists__new_col_len
(
self
,
se
->
se_width_idx
,
width
))
width
=
hists__col_len
(
self
,
se
->
se_width_idx
);
if
(
!
hists__new_col_len
(
hists
,
se
->
se_width_idx
,
width
))
width
=
hists__col_len
(
hists
,
se
->
se_width_idx
);
fprintf
(
fp
,
" %*s"
,
width
,
se
->
se_header
);
}
fprintf
(
fp
,
"
\n
"
);
if
(
max_rows
&&
++
nr_rows
>=
max_rows
)
goto
out
;
if
(
sep
)
goto
print_entries
;
...
...
@@ -822,6 +940,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
fprintf
(
fp
,
"# ........"
);
if
(
symbol_conf
.
show_nr_samples
)
fprintf
(
fp
,
" .........."
);
if
(
symbol_conf
.
show_total_period
)
fprintf
(
fp
,
" ............"
);
if
(
pair
)
{
fprintf
(
fp
,
" .........."
);
if
(
show_displacement
)
...
...
@@ -834,17 +954,23 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
continue
;
fprintf
(
fp
,
" "
);
width
=
hists__col_len
(
self
,
se
->
se_width_idx
);
width
=
hists__col_len
(
hists
,
se
->
se_width_idx
);
if
(
width
==
0
)
width
=
strlen
(
se
->
se_header
);
for
(
i
=
0
;
i
<
width
;
i
++
)
fprintf
(
fp
,
"."
);
}
fprintf
(
fp
,
"
\n
#
\n
"
);
fprintf
(
fp
,
"
\n
"
);
if
(
max_rows
&&
++
nr_rows
>=
max_rows
)
goto
out
;
fprintf
(
fp
,
"#
\n
"
);
if
(
max_rows
&&
++
nr_rows
>=
max_rows
)
goto
out
;
print_entries:
for
(
nd
=
rb_first
(
&
self
->
entries
);
nd
;
nd
=
rb_next
(
nd
))
{
for
(
nd
=
rb_first
(
&
hists
->
entries
);
nd
;
nd
=
rb_next
(
nd
))
{
struct
hist_entry
*
h
=
rb_entry
(
nd
,
struct
hist_entry
,
rb_node
);
if
(
h
->
filtered
)
...
...
@@ -858,19 +984,22 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
displacement
=
0
;
++
position
;
}
ret
+=
hist_entry__fprintf
(
h
,
self
,
pair
,
show_displacement
,
displacement
,
fp
,
self
->
stats
.
total_period
);
ret
+=
hist_entry__fprintf
(
h
,
max_cols
,
hists
,
pair
,
show_displacement
,
displacement
,
fp
,
hists
->
stats
.
total_period
);
if
(
symbol_conf
.
use_callchain
)
ret
+=
hist_entry__fprintf_callchain
(
h
,
self
,
fp
,
self
->
stats
.
total_period
);
ret
+=
hist_entry__fprintf_callchain
(
h
,
hists
,
fp
,
hists
->
stats
.
total_period
);
if
(
max_rows
&&
++
nr_rows
>=
max_rows
)
goto
out
;
if
(
h
->
ms
.
map
==
NULL
&&
verbose
>
1
)
{
__map_groups__fprintf_maps
(
&
h
->
thread
->
mg
,
MAP__FUNCTION
,
verbose
,
fp
);
fprintf
(
fp
,
"%.10s end
\n
"
,
graph_dotted_line
);
}
}
out:
free
(
rem_sq_bracket
);
return
ret
;
...
...
@@ -879,7 +1008,7 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
/*
* See hists__fprintf to match the column widths
*/
unsigned
int
hists__sort_list_width
(
struct
hists
*
self
)
unsigned
int
hists__sort_list_width
(
struct
hists
*
hists
)
{
struct
sort_entry
*
se
;
int
ret
=
9
;
/* total % */
...
...
@@ -896,9 +1025,12 @@ unsigned int hists__sort_list_width(struct hists *self)
if
(
symbol_conf
.
show_nr_samples
)
ret
+=
11
;
if
(
symbol_conf
.
show_total_period
)
ret
+=
13
;
list_for_each_entry
(
se
,
&
hist_entry__sort_list
,
list
)
if
(
!
se
->
elide
)
ret
+=
2
+
hists__col_len
(
self
,
se
->
se_width_idx
);
ret
+=
2
+
hists__col_len
(
hists
,
se
->
se_width_idx
);
if
(
verbose
)
/* Addr + origin */
ret
+=
3
+
BITS_PER_LONG
/
4
;
...
...
@@ -906,32 +1038,32 @@ unsigned int hists__sort_list_width(struct hists *self)
return
ret
;
}
static
void
hists__remove_entry_filter
(
struct
hists
*
self
,
struct
hist_entry
*
h
,
static
void
hists__remove_entry_filter
(
struct
hists
*
hists
,
struct
hist_entry
*
h
,
enum
hist_filter
filter
)
{
h
->
filtered
&=
~
(
1
<<
filter
);
if
(
h
->
filtered
)
return
;
++
self
->
nr_entries
;
++
hists
->
nr_entries
;
if
(
h
->
ms
.
unfolded
)
self
->
nr_entries
+=
h
->
nr_rows
;
hists
->
nr_entries
+=
h
->
nr_rows
;
h
->
row_offset
=
0
;
self
->
stats
.
total_period
+=
h
->
period
;
self
->
stats
.
nr_events
[
PERF_RECORD_SAMPLE
]
+=
h
->
nr_events
;
hists
->
stats
.
total_period
+=
h
->
period
;
hists
->
stats
.
nr_events
[
PERF_RECORD_SAMPLE
]
+=
h
->
nr_events
;
hists__calc_col_len
(
self
,
h
);
hists__calc_col_len
(
hists
,
h
);
}
void
hists__filter_by_dso
(
struct
hists
*
self
,
const
struct
dso
*
dso
)
void
hists__filter_by_dso
(
struct
hists
*
hists
,
const
struct
dso
*
dso
)
{
struct
rb_node
*
nd
;
self
->
nr_entries
=
self
->
stats
.
total_period
=
0
;
self
->
stats
.
nr_events
[
PERF_RECORD_SAMPLE
]
=
0
;
hists__reset_col_len
(
self
);
hists
->
nr_entries
=
hists
->
stats
.
total_period
=
0
;
hists
->
stats
.
nr_events
[
PERF_RECORD_SAMPLE
]
=
0
;
hists__reset_col_len
(
hists
);
for
(
nd
=
rb_first
(
&
self
->
entries
);
nd
;
nd
=
rb_next
(
nd
))
{
for
(
nd
=
rb_first
(
&
hists
->
entries
);
nd
;
nd
=
rb_next
(
nd
))
{
struct
hist_entry
*
h
=
rb_entry
(
nd
,
struct
hist_entry
,
rb_node
);
if
(
symbol_conf
.
exclude_other
&&
!
h
->
parent
)
...
...
@@ -942,19 +1074,19 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso)
continue
;
}
hists__remove_entry_filter
(
self
,
h
,
HIST_FILTER__DSO
);
hists__remove_entry_filter
(
hists
,
h
,
HIST_FILTER__DSO
);
}
}
void
hists__filter_by_thread
(
struct
hists
*
self
,
const
struct
thread
*
thread
)
void
hists__filter_by_thread
(
struct
hists
*
hists
,
const
struct
thread
*
thread
)
{
struct
rb_node
*
nd
;
self
->
nr_entries
=
self
->
stats
.
total_period
=
0
;
self
->
stats
.
nr_events
[
PERF_RECORD_SAMPLE
]
=
0
;
hists__reset_col_len
(
self
);
hists
->
nr_entries
=
hists
->
stats
.
total_period
=
0
;
hists
->
stats
.
nr_events
[
PERF_RECORD_SAMPLE
]
=
0
;
hists__reset_col_len
(
hists
);
for
(
nd
=
rb_first
(
&
self
->
entries
);
nd
;
nd
=
rb_next
(
nd
))
{
for
(
nd
=
rb_first
(
&
hists
->
entries
);
nd
;
nd
=
rb_next
(
nd
))
{
struct
hist_entry
*
h
=
rb_entry
(
nd
,
struct
hist_entry
,
rb_node
);
if
(
thread
!=
NULL
&&
h
->
thread
!=
thread
)
{
...
...
@@ -962,7 +1094,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
continue
;
}
hists__remove_entry_filter
(
self
,
h
,
HIST_FILTER__THREAD
);
hists__remove_entry_filter
(
hists
,
h
,
HIST_FILTER__THREAD
);
}
}
...
...
@@ -976,13 +1108,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize)
return
symbol__annotate
(
he
->
ms
.
sym
,
he
->
ms
.
map
,
privsize
);
}
void
hists__inc_nr_events
(
struct
hists
*
self
,
u32
type
)
void
hists__inc_nr_events
(
struct
hists
*
hists
,
u32
type
)
{
++
self
->
stats
.
nr_events
[
0
];
++
self
->
stats
.
nr_events
[
type
];
++
hists
->
stats
.
nr_events
[
0
];
++
hists
->
stats
.
nr_events
[
type
];
}
size_t
hists__fprintf_nr_events
(
struct
hists
*
self
,
FILE
*
fp
)
size_t
hists__fprintf_nr_events
(
struct
hists
*
hists
,
FILE
*
fp
)
{
int
i
;
size_t
ret
=
0
;
...
...
@@ -990,7 +1122,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
for
(
i
=
0
;
i
<
PERF_RECORD_HEADER_MAX
;
++
i
)
{
const
char
*
name
;
if
(
self
->
stats
.
nr_events
[
i
]
==
0
)
if
(
hists
->
stats
.
nr_events
[
i
]
==
0
)
continue
;
name
=
perf_event__name
(
i
);
...
...
@@ -998,8 +1130,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
continue
;
ret
+=
fprintf
(
fp
,
"%16s events: %10d
\n
"
,
name
,
self
->
stats
.
nr_events
[
i
]);
hists
->
stats
.
nr_events
[
i
]);
}
return
ret
;
}
void
hists__init
(
struct
hists
*
hists
)
{
memset
(
hists
,
0
,
sizeof
(
*
hists
));
hists
->
entries_in_array
[
0
]
=
hists
->
entries_in_array
[
1
]
=
RB_ROOT
;
hists
->
entries_in
=
&
hists
->
entries_in_array
[
0
];
hists
->
entries_collapsed
=
RB_ROOT
;
hists
->
entries
=
RB_ROOT
;
pthread_mutex_init
(
&
hists
->
lock
,
NULL
);
}
tools/perf/util/hist.h
View file @
7588bada
...
...
@@ -2,6 +2,7 @@
#define __PERF_HIST_H
#include <linux/types.h>
#include <pthread.h>
#include "callchain.h"
extern
struct
callchain_param
callchain_param
;
...
...
@@ -43,8 +44,12 @@ enum hist_column {
};
struct
hists
{
struct
rb_root
entries_in_array
[
2
];
struct
rb_root
*
entries_in
;
struct
rb_root
entries
;
struct
rb_root
entries_collapsed
;
u64
nr_entries
;
pthread_mutex_t
lock
;
struct
events_stats
stats
;
u64
event_stream
;
u16
col_len
[
HISTC_NR_COLS
];
...
...
@@ -52,14 +57,16 @@ struct hists {
struct
callchain_cursor
callchain_cursor
;
};
void
hists__init
(
struct
hists
*
hists
);
struct
hist_entry
*
__hists__add_entry
(
struct
hists
*
self
,
struct
addr_location
*
al
,
struct
symbol
*
parent
,
u64
period
);
extern
int64_t
hist_entry__cmp
(
struct
hist_entry
*
,
struct
hist_entry
*
);
extern
int64_t
hist_entry__collapse
(
struct
hist_entry
*
,
struct
hist_entry
*
);
int
hist_entry__fprintf
(
struct
hist_entry
*
self
,
struct
hists
*
hists
,
int
hist_entry__fprintf
(
struct
hist_entry
*
he
,
size_t
size
,
struct
hists
*
hists
,
struct
hists
*
pair_hists
,
bool
show_displacement
,
long
displacement
,
FILE
*
fp
,
u64
total
);
long
displacement
,
FILE
*
fp
,
u64
session_
total
);
int
hist_entry__snprintf
(
struct
hist_entry
*
self
,
char
*
bf
,
size_t
size
,
struct
hists
*
hists
,
struct
hists
*
pair_hists
,
bool
show_displacement
,
long
displacement
,
...
...
@@ -67,13 +74,19 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
void
hist_entry__free
(
struct
hist_entry
*
);
void
hists__output_resort
(
struct
hists
*
self
);
void
hists__output_resort_threaded
(
struct
hists
*
hists
);
void
hists__collapse_resort
(
struct
hists
*
self
);
void
hists__collapse_resort_threaded
(
struct
hists
*
hists
);
void
hists__decay_entries
(
struct
hists
*
hists
);
void
hists__output_recalc_col_len
(
struct
hists
*
hists
,
int
max_rows
);
void
hists__inc_nr_events
(
struct
hists
*
self
,
u32
type
);
size_t
hists__fprintf_nr_events
(
struct
hists
*
self
,
FILE
*
fp
);
size_t
hists__fprintf
(
struct
hists
*
self
,
struct
hists
*
pair
,
bool
show_displacement
,
FILE
*
fp
);
bool
show_displacement
,
bool
show_header
,
int
max_rows
,
int
max_cols
,
FILE
*
fp
);
int
hist_entry__inc_addr_samples
(
struct
hist_entry
*
self
,
int
evidx
,
u64
addr
);
int
hist_entry__annotate
(
struct
hist_entry
*
self
,
size_t
privsize
);
...
...
@@ -90,13 +103,16 @@ struct perf_evlist;
#ifdef NO_NEWT_SUPPORT
static
inline
int
perf_evlist__tui_browse_hists
(
struct
perf_evlist
*
evlist
__used
,
const
char
*
help
__used
)
const
char
*
help
__used
,
void
(
*
timer
)(
void
*
arg
)
__used
,
void
*
arg
,
int
refresh
__used
)
{
return
0
;
}
static
inline
int
hist_entry__tui_annotate
(
struct
hist_entry
*
self
__used
,
int
evidx
__used
)
int
evidx
__used
,
int
nr_events
__used
,
void
(
*
timer
)(
void
*
arg
)
__used
,
void
*
arg
__used
,
int
delay_secs
__used
);
{
return
0
;
}
...
...
@@ -104,12 +120,15 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
#define KEY_RIGHT -2
#else
#include <newt.h>
int
hist_entry__tui_annotate
(
struct
hist_entry
*
self
,
int
evidx
);
int
hist_entry__tui_annotate
(
struct
hist_entry
*
he
,
int
evidx
,
int
nr_events
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
);
#define KEY_LEFT NEWT_KEY_LEFT
#define KEY_RIGHT NEWT_KEY_RIGHT
int
perf_evlist__tui_browse_hists
(
struct
perf_evlist
*
evlist
,
const
char
*
help
);
int
perf_evlist__tui_browse_hists
(
struct
perf_evlist
*
evlist
,
const
char
*
help
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
refresh
);
#endif
unsigned
int
hists__sort_list_width
(
struct
hists
*
self
);
...
...
tools/perf/util/session.c
View file @
7588bada
...
...
@@ -1326,3 +1326,22 @@ int perf_session__cpu_bitmap(struct perf_session *session,
return
0
;
}
void
perf_session__fprintf_info
(
struct
perf_session
*
session
,
FILE
*
fp
,
bool
full
)
{
struct
stat
st
;
int
ret
;
if
(
session
==
NULL
||
fp
==
NULL
)
return
;
ret
=
fstat
(
session
->
fd
,
&
st
);
if
(
ret
==
-
1
)
return
;
fprintf
(
fp
,
"# ========
\n
"
);
fprintf
(
fp
,
"# captured on: %s"
,
ctime
(
&
st
.
st_ctime
));
perf_header__fprintf_info
(
session
,
fp
,
full
);
fprintf
(
fp
,
"# ========
\n
#
\n
"
);
}
tools/perf/util/session.h
View file @
7588bada
...
...
@@ -177,4 +177,5 @@ void perf_session__print_ip(union perf_event *event,
int
perf_session__cpu_bitmap
(
struct
perf_session
*
session
,
const
char
*
cpu_list
,
unsigned
long
*
cpu_bitmap
);
void
perf_session__fprintf_info
(
struct
perf_session
*
s
,
FILE
*
fp
,
bool
full
);
#endif
/* __PERF_SESSION_H */
tools/perf/util/sort.h
View file @
7588bada
...
...
@@ -45,6 +45,7 @@ extern enum sort_type sort__first_dimension;
* @nr_rows - rows expanded in callchain, recalculated on folding/unfolding
*/
struct
hist_entry
{
struct
rb_node
rb_node_in
;
struct
rb_node
rb_node
;
u64
period
;
u64
period_sys
;
...
...
tools/perf/util/symbol.c
View file @
7588bada
...
...
@@ -46,7 +46,6 @@ struct symbol_conf symbol_conf = {
.
exclude_other
=
true
,
.
use_modules
=
true
,
.
try_vmlinux_path
=
true
,
.
annotate_asm_raw
=
true
,
.
annotate_src
=
true
,
.
symfs
=
""
,
};
...
...
tools/perf/util/symbol.h
View file @
7588bada
...
...
@@ -72,6 +72,7 @@ struct symbol_conf {
use_modules
,
sort_by_name
,
show_nr_samples
,
show_total_period
,
use_callchain
,
exclude_other
,
show_cpu_utilization
,
...
...
tools/perf/util/top.c
View file @
7588bada
...
...
@@ -15,52 +15,6 @@
#include "top.h"
#include <inttypes.h>
/*
* Ordering weight: count-1 * count-2 * ... / count-n
*/
static
double
sym_weight
(
const
struct
sym_entry
*
sym
,
struct
perf_top
*
top
)
{
double
weight
=
sym
->
snap_count
;
int
counter
;
if
(
!
top
->
display_weighted
)
return
weight
;
for
(
counter
=
1
;
counter
<
top
->
evlist
->
nr_entries
-
1
;
counter
++
)
weight
*=
sym
->
count
[
counter
];
weight
/=
(
sym
->
count
[
counter
]
+
1
);
return
weight
;
}
static
void
perf_top__remove_active_sym
(
struct
perf_top
*
top
,
struct
sym_entry
*
syme
)
{
pthread_mutex_lock
(
&
top
->
active_symbols_lock
);
list_del_init
(
&
syme
->
node
);
pthread_mutex_unlock
(
&
top
->
active_symbols_lock
);
}
static
void
rb_insert_active_sym
(
struct
rb_root
*
tree
,
struct
sym_entry
*
se
)
{
struct
rb_node
**
p
=
&
tree
->
rb_node
;
struct
rb_node
*
parent
=
NULL
;
struct
sym_entry
*
iter
;
while
(
*
p
!=
NULL
)
{
parent
=
*
p
;
iter
=
rb_entry
(
parent
,
struct
sym_entry
,
rb_node
);
if
(
se
->
weight
>
iter
->
weight
)
p
=
&
(
*
p
)
->
rb_left
;
else
p
=
&
(
*
p
)
->
rb_right
;
}
rb_link_node
(
&
se
->
rb_node
,
parent
,
p
);
rb_insert_color
(
&
se
->
rb_node
,
tree
);
}
#define SNPRINTF(buf, size, fmt, args...) \
({ \
size_t r = snprintf(buf, size, fmt, ## args); \
...
...
@@ -69,7 +23,6 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
size_t
perf_top__header_snprintf
(
struct
perf_top
*
top
,
char
*
bf
,
size_t
size
)
{
struct
perf_evsel
*
counter
;
float
samples_per_sec
=
top
->
samples
/
top
->
delay_secs
;
float
ksamples_per_sec
=
top
->
kernel_samples
/
top
->
delay_secs
;
float
esamples_percent
=
(
100
.
0
*
top
->
exact_samples
)
/
top
->
samples
;
...
...
@@ -104,7 +57,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
esamples_percent
);
}
if
(
top
->
evlist
->
nr_entries
==
1
||
!
top
->
display_weighted
)
{
if
(
top
->
evlist
->
nr_entries
==
1
)
{
struct
perf_evsel
*
first
;
first
=
list_entry
(
top
->
evlist
->
entries
.
next
,
struct
perf_evsel
,
node
);
ret
+=
SNPRINTF
(
bf
+
ret
,
size
-
ret
,
"%"
PRIu64
"%s "
,
...
...
@@ -112,27 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
top
->
freq
?
"Hz"
:
""
);
}
if
(
!
top
->
display_weighted
)
{
ret
+=
SNPRINTF
(
bf
+
ret
,
size
-
ret
,
"%s"
,
event_name
(
top
->
sym_evsel
));
}
else
{
/*
* Don't let events eat all the space. Leaving 30 bytes
* for the rest should be enough.
*/
size_t
last_pos
=
size
-
30
;
list_for_each_entry
(
counter
,
&
top
->
evlist
->
entries
,
node
)
{
ret
+=
SNPRINTF
(
bf
+
ret
,
size
-
ret
,
"%s%s"
,
counter
->
idx
?
"/"
:
""
,
event_name
(
counter
));
if
(
ret
>
last_pos
)
{
sprintf
(
bf
+
last_pos
-
3
,
".."
);
ret
=
last_pos
-
1
;
break
;
}
}
}
ret
+=
SNPRINTF
(
bf
+
ret
,
size
-
ret
,
"%s"
,
event_name
(
top
->
sym_evsel
));
ret
+=
SNPRINTF
(
bf
+
ret
,
size
-
ret
,
"], "
);
...
...
@@ -166,73 +99,3 @@ void perf_top__reset_sample_counters(struct perf_top *top)
top
->
exact_samples
=
top
->
guest_kernel_samples
=
top
->
guest_us_samples
=
0
;
}
float
perf_top__decay_samples
(
struct
perf_top
*
top
,
struct
rb_root
*
root
)
{
struct
sym_entry
*
syme
,
*
n
;
float
sum_ksamples
=
0
.
0
;
int
snap
=
!
top
->
display_weighted
?
top
->
sym_evsel
->
idx
:
0
,
j
;
/* Sort the active symbols */
pthread_mutex_lock
(
&
top
->
active_symbols_lock
);
syme
=
list_entry
(
top
->
active_symbols
.
next
,
struct
sym_entry
,
node
);
pthread_mutex_unlock
(
&
top
->
active_symbols_lock
);
top
->
rb_entries
=
0
;
list_for_each_entry_safe_from
(
syme
,
n
,
&
top
->
active_symbols
,
node
)
{
syme
->
snap_count
=
syme
->
count
[
snap
];
if
(
syme
->
snap_count
!=
0
)
{
if
((
top
->
hide_user_symbols
&&
syme
->
map
->
dso
->
kernel
==
DSO_TYPE_USER
)
||
(
top
->
hide_kernel_symbols
&&
syme
->
map
->
dso
->
kernel
==
DSO_TYPE_KERNEL
))
{
perf_top__remove_active_sym
(
top
,
syme
);
continue
;
}
syme
->
weight
=
sym_weight
(
syme
,
top
);
if
((
int
)
syme
->
snap_count
>=
top
->
count_filter
)
{
rb_insert_active_sym
(
root
,
syme
);
++
top
->
rb_entries
;
}
sum_ksamples
+=
syme
->
snap_count
;
for
(
j
=
0
;
j
<
top
->
evlist
->
nr_entries
;
j
++
)
syme
->
count
[
j
]
=
top
->
zero
?
0
:
syme
->
count
[
j
]
*
7
/
8
;
}
else
perf_top__remove_active_sym
(
top
,
syme
);
}
return
sum_ksamples
;
}
/*
* Find the longest symbol name that will be displayed
*/
void
perf_top__find_widths
(
struct
perf_top
*
top
,
struct
rb_root
*
root
,
int
*
dso_width
,
int
*
dso_short_width
,
int
*
sym_width
)
{
struct
rb_node
*
nd
;
int
printed
=
0
;
*
sym_width
=
*
dso_width
=
*
dso_short_width
=
0
;
for
(
nd
=
rb_first
(
root
);
nd
;
nd
=
rb_next
(
nd
))
{
struct
sym_entry
*
syme
=
rb_entry
(
nd
,
struct
sym_entry
,
rb_node
);
struct
symbol
*
sym
=
sym_entry__symbol
(
syme
);
if
(
++
printed
>
top
->
print_entries
||
(
int
)
syme
->
snap_count
<
top
->
count_filter
)
continue
;
if
(
syme
->
map
->
dso
->
long_name_len
>
*
dso_width
)
*
dso_width
=
syme
->
map
->
dso
->
long_name_len
;
if
(
syme
->
map
->
dso
->
short_name_len
>
*
dso_short_width
)
*
dso_short_width
=
syme
->
map
->
dso
->
short_name_len
;
if
(
sym
->
namelen
>
*
sym_width
)
*
sym_width
=
sym
->
namelen
;
}
}
tools/perf/util/top.h
View file @
7588bada
...
...
@@ -4,64 +4,32 @@
#include "types.h"
#include "../perf.h"
#include <stddef.h>
#include <pthread.h>
#include <linux/list.h>
#include <linux/rbtree.h>
struct
perf_evlist
;
struct
perf_evsel
;
struct
perf_session
;
struct
sym_entry
{
struct
rb_node
rb_node
;
struct
list_head
node
;
unsigned
long
snap_count
;
double
weight
;
struct
map
*
map
;
unsigned
long
count
[
0
];
};
static
inline
struct
symbol
*
sym_entry__symbol
(
struct
sym_entry
*
self
)
{
return
((
void
*
)
self
)
+
symbol_conf
.
priv_size
;
}
struct
perf_top
{
struct
perf_evlist
*
evlist
;
/*
* Symbols will be added here in perf_event__process_sample and will
* get out after decayed.
*/
struct
list_head
active_symbols
;
pthread_mutex_t
active_symbols_lock
;
pthread_cond_t
active_symbols_cond
;
u64
samples
;
u64
kernel_samples
,
us_samples
;
u64
exact_samples
;
u64
guest_us_samples
,
guest_kernel_samples
;
u64
total_lost_warned
;
int
print_entries
,
count_filter
,
delay_secs
;
int
display_weighted
,
freq
,
rb_entries
;
int
freq
;
pid_t
target_pid
,
target_tid
;
bool
hide_kernel_symbols
,
hide_user_symbols
,
zero
;
const
char
*
cpu_list
;
struct
sym_entry
*
sym_filter_entry
;
struct
hist_entry
*
sym_filter_entry
;
struct
perf_evsel
*
sym_evsel
;
struct
perf_session
*
session
;
};
size_t
perf_top__header_snprintf
(
struct
perf_top
*
top
,
char
*
bf
,
size_t
size
);
void
perf_top__reset_sample_counters
(
struct
perf_top
*
top
);
float
perf_top__decay_samples
(
struct
perf_top
*
top
,
struct
rb_root
*
root
);
void
perf_top__find_widths
(
struct
perf_top
*
top
,
struct
rb_root
*
root
,
int
*
dso_width
,
int
*
dso_short_width
,
int
*
sym_width
);
#ifdef NO_NEWT_SUPPORT
static
inline
int
perf_top__tui_browser
(
struct
perf_top
*
top
__used
)
{
return
0
;
}
#else
int
perf_top__tui_browser
(
struct
perf_top
*
top
);
#endif
#endif
/* __PERF_TOP_H */
tools/perf/util/ui/browsers/annotate.c
View file @
7588bada
...
...
@@ -20,6 +20,7 @@ struct annotate_browser {
struct
ui_browser
b
;
struct
rb_root
entries
;
struct
rb_node
*
curr_hot
;
struct
objdump_line
*
selection
;
};
struct
objdump_line_rb_node
{
...
...
@@ -36,6 +37,7 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
static
void
annotate_browser__write
(
struct
ui_browser
*
self
,
void
*
entry
,
int
row
)
{
struct
annotate_browser
*
ab
=
container_of
(
self
,
struct
annotate_browser
,
b
);
struct
objdump_line
*
ol
=
rb_entry
(
entry
,
struct
objdump_line
,
node
);
bool
current_entry
=
ui_browser__is_current_entry
(
self
,
row
);
int
width
=
self
->
width
;
...
...
@@ -58,6 +60,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
if
(
!
current_entry
)
ui_browser__set_color
(
self
,
HE_COLORSET_CODE
);
else
ab
->
selection
=
ol
;
}
static
double
objdump_line__calc_percent
(
struct
objdump_line
*
self
,
...
...
@@ -141,7 +145,8 @@ static void annotate_browser__set_top(struct annotate_browser *self,
static
void
annotate_browser__calc_percent
(
struct
annotate_browser
*
browser
,
int
evidx
)
{
struct
symbol
*
sym
=
browser
->
b
.
priv
;
struct
map_symbol
*
ms
=
browser
->
b
.
priv
;
struct
symbol
*
sym
=
ms
->
sym
;
struct
annotation
*
notes
=
symbol__annotation
(
sym
);
struct
objdump_line
*
pos
;
...
...
@@ -164,21 +169,23 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
}
static
int
annotate_browser__run
(
struct
annotate_browser
*
self
,
int
evidx
,
int
refresh
)
int
nr_events
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
)
{
struct
rb_node
*
nd
=
NULL
;
struct
symbol
*
sym
=
self
->
b
.
priv
;
struct
map_symbol
*
ms
=
self
->
b
.
priv
;
struct
symbol
*
sym
=
ms
->
sym
;
/*
* RIGHT To allow builtin-annotate to cycle thru multiple symbols by
* examining the exit key for this function.
*/
int
exit_keys
[]
=
{
'H'
,
NEWT_KEY_TAB
,
NEWT_KEY_UNTAB
,
NEWT_KEY_RIGHT
,
0
};
NEWT_KEY_RIGHT
,
NEWT_KEY_ENTER
,
0
};
int
key
;
if
(
ui_browser__show
(
&
self
->
b
,
sym
->
name
,
"<-
, ->
or ESC: exit, TAB/shift+TAB: "
"cycle hottest lines, H: Hottest"
)
<
0
)
"<- or ESC: exit, TAB/shift+TAB: "
"cycle hottest lines, H: Hottest
, -> Line action
"
)
<
0
)
return
-
1
;
ui_browser__add_exit_keys
(
&
self
->
b
,
exit_keys
);
...
...
@@ -189,13 +196,13 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
nd
=
self
->
curr_hot
;
if
(
refresh
!=
0
)
newtFormSetTimer
(
self
->
b
.
form
,
refresh
);
if
(
delay_secs
!=
0
)
newtFormSetTimer
(
self
->
b
.
form
,
delay_secs
*
1000
);
while
(
1
)
{
key
=
ui_browser__run
(
&
self
->
b
);
if
(
refresh
!=
0
)
{
if
(
delay_secs
!=
0
)
{
annotate_browser__calc_percent
(
self
,
evidx
);
/*
* Current line focus got out of the list of most active
...
...
@@ -212,7 +219,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
* FIXME we need to check if it was
* es.reason == NEWT_EXIT_TIMER
*/
if
(
refresh
!=
0
)
if
(
timer
!=
NULL
)
timer
(
arg
);
if
(
delay_secs
!=
0
)
symbol__annotate_decay_histogram
(
sym
,
evidx
);
continue
;
case
NEWT_KEY_TAB
:
...
...
@@ -234,6 +244,57 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
case
'H'
:
nd
=
self
->
curr_hot
;
break
;
case
NEWT_KEY_ENTER
:
case
NEWT_KEY_RIGHT
:
if
(
self
->
selection
==
NULL
)
{
ui_helpline__puts
(
"Huh? No selection. Report to linux-kernel@vger.kernel.org"
);
continue
;
}
if
(
self
->
selection
->
offset
==
-
1
)
{
ui_helpline__puts
(
"Actions are only available for assembly lines."
);
continue
;
}
else
{
char
*
s
=
strstr
(
self
->
selection
->
line
,
"callq "
);
struct
annotation
*
notes
;
struct
symbol
*
target
;
u64
ip
;
if
(
s
==
NULL
)
{
ui_helpline__puts
(
"Actions are only available for the 'callq' instruction."
);
continue
;
}
s
=
strchr
(
s
,
' '
);
if
(
s
++
==
NULL
)
{
ui_helpline__puts
(
"Invallid callq instruction."
);
continue
;
}
ip
=
strtoull
(
s
,
NULL
,
16
);
ip
=
ms
->
map
->
map_ip
(
ms
->
map
,
ip
);
target
=
map__find_symbol
(
ms
->
map
,
ip
,
NULL
);
if
(
target
==
NULL
)
{
ui_helpline__puts
(
"The called function was not found."
);
continue
;
}
notes
=
symbol__annotation
(
target
);
pthread_mutex_lock
(
&
notes
->
lock
);
if
(
notes
->
src
==
NULL
&&
symbol__alloc_hist
(
target
,
nr_events
)
<
0
)
{
pthread_mutex_unlock
(
&
notes
->
lock
);
ui__warning
(
"Not enough memory for annotating '%s' symbol!
\n
"
,
target
->
name
);
continue
;
}
pthread_mutex_unlock
(
&
notes
->
lock
);
symbol__tui_annotate
(
target
,
ms
->
map
,
evidx
,
nr_events
,
timer
,
arg
,
delay_secs
);
}
break
;
default:
goto
out
;
}
...
...
@@ -246,22 +307,29 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
return
key
;
}
int
hist_entry__tui_annotate
(
struct
hist_entry
*
he
,
int
evidx
)
int
hist_entry__tui_annotate
(
struct
hist_entry
*
he
,
int
evidx
,
int
nr_events
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
)
{
return
symbol__tui_annotate
(
he
->
ms
.
sym
,
he
->
ms
.
map
,
evidx
,
0
);
return
symbol__tui_annotate
(
he
->
ms
.
sym
,
he
->
ms
.
map
,
evidx
,
nr_events
,
timer
,
arg
,
delay_secs
);
}
int
symbol__tui_annotate
(
struct
symbol
*
sym
,
struct
map
*
map
,
int
evidx
,
int
refresh
)
int
nr_events
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
)
{
struct
objdump_line
*
pos
,
*
n
;
struct
annotation
*
notes
;
struct
map_symbol
ms
=
{
.
map
=
map
,
.
sym
=
sym
,
};
struct
annotate_browser
browser
=
{
.
b
=
{
.
refresh
=
ui_browser__list_head_refresh
,
.
seek
=
ui_browser__list_head_seek
,
.
write
=
annotate_browser__write
,
.
priv
=
sym
,
.
priv
=
&
ms
,
},
};
int
ret
;
...
...
@@ -293,7 +361,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
browser
.
b
.
entries
=
&
notes
->
src
->
source
,
browser
.
b
.
width
+=
18
;
/* Percentage */
ret
=
annotate_browser__run
(
&
browser
,
evidx
,
refresh
);
ret
=
annotate_browser__run
(
&
browser
,
evidx
,
nr_events
,
timer
,
arg
,
delay_secs
);
list_for_each_entry_safe
(
pos
,
n
,
&
notes
->
src
->
source
,
node
)
{
list_del
(
&
pos
->
node
);
objdump_line__free
(
pos
);
...
...
tools/perf/util/ui/browsers/hists.c
View file @
7588bada
...
...
@@ -24,8 +24,15 @@ struct hist_browser {
struct
hists
*
hists
;
struct
hist_entry
*
he_selection
;
struct
map_symbol
*
selection
;
const
struct
thread
*
thread_filter
;
const
struct
dso
*
dso_filter
;
bool
has_symbols
;
};
static
int
hists__browser_title
(
struct
hists
*
self
,
char
*
bf
,
size_t
size
,
const
char
*
ev_name
,
const
struct
dso
*
dso
,
const
struct
thread
*
thread
);
static
void
hist_browser__refresh_dimensions
(
struct
hist_browser
*
self
)
{
/* 3 == +/- toggle symbol before actual hist_entry rendering */
...
...
@@ -290,28 +297,53 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
ui_browser__reset_index
(
&
self
->
b
);
}
static
int
hist_browser__run
(
struct
hist_browser
*
self
,
const
char
*
title
)
static
int
hist_browser__run
(
struct
hist_browser
*
self
,
const
char
*
ev_name
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
)
{
int
key
;
int
exit_keys
[]
=
{
'a'
,
'?'
,
'h'
,
'C'
,
'd'
,
'D'
,
'E'
,
't'
,
NEWT_KEY_ENTER
,
NEWT_KEY_RIGHT
,
NEWT_KEY_LEFT
,
NEWT_KEY_TAB
,
NEWT_KEY_UNTAB
,
0
,
};
int
delay_msecs
=
delay_secs
*
1000
;
char
title
[
160
];
int
sym_exit_keys
[]
=
{
'a'
,
'h'
,
'C'
,
'd'
,
'E'
,
't'
,
0
,
};
int
exit_keys
[]
=
{
'?'
,
'h'
,
'D'
,
NEWT_KEY_LEFT
,
NEWT_KEY_RIGHT
,
NEWT_KEY_TAB
,
NEWT_KEY_UNTAB
,
NEWT_KEY_ENTER
,
0
,
};
self
->
b
.
entries
=
&
self
->
hists
->
entries
;
self
->
b
.
nr_entries
=
self
->
hists
->
nr_entries
;
hist_browser__refresh_dimensions
(
self
);
hists__browser_title
(
self
->
hists
,
title
,
sizeof
(
title
),
ev_name
,
self
->
dso_filter
,
self
->
thread_filter
);
if
(
ui_browser__show
(
&
self
->
b
,
title
,
"Press '?' for help on key bindings"
)
<
0
)
return
-
1
;
if
(
timer
!=
NULL
)
newtFormSetTimer
(
self
->
b
.
form
,
delay_msecs
);
ui_browser__add_exit_keys
(
&
self
->
b
,
exit_keys
);
if
(
self
->
has_symbols
)
ui_browser__add_exit_keys
(
&
self
->
b
,
sym_exit_keys
);
while
(
1
)
{
key
=
ui_browser__run
(
&
self
->
b
);
switch
(
key
)
{
case
-
1
:
/* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
timer
(
arg
);
/*
* The timer may have changed the number of entries.
* XXX: Find better way to keep this in synch, probably
* removing this timer function altogether and just sync
* using the hists->lock...
*/
self
->
b
.
nr_entries
=
self
->
hists
->
nr_entries
;
hists__browser_title
(
self
->
hists
,
title
,
sizeof
(
title
),
ev_name
,
self
->
dso_filter
,
self
->
thread_filter
);
ui_browser__show_title
(
&
self
->
b
,
title
);
continue
;
case
'D'
:
{
/* Debug */
static
int
seq
;
struct
hist_entry
*
h
=
rb_entry
(
self
->
b
.
top
,
...
...
@@ -761,6 +793,7 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
self
->
hists
=
hists
;
self
->
b
.
refresh
=
hist_browser__refresh
;
self
->
b
.
seek
=
ui_browser__hists_seek
;
self
->
has_symbols
=
sort_sym
.
list
.
next
!=
NULL
;
}
return
self
;
...
...
@@ -803,16 +836,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
return
printed
;
}
static
int
perf_evsel__hists_browse
(
struct
perf_evsel
*
evsel
,
static
int
perf_evsel__hists_browse
(
struct
perf_evsel
*
evsel
,
int
nr_events
,
const
char
*
helpline
,
const
char
*
ev_name
,
bool
left_exits
)
bool
left_exits
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
)
{
struct
hists
*
self
=
&
evsel
->
hists
;
struct
hist_browser
*
browser
=
hist_browser__new
(
self
);
struct
pstack
*
fstack
;
const
struct
thread
*
thread_filter
=
NULL
;
const
struct
dso
*
dso_filter
=
NULL
;
char
msg
[
160
];
int
key
=
-
1
;
if
(
browser
==
NULL
)
...
...
@@ -824,8 +856,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
ui_helpline__push
(
helpline
);
hists__browser_title
(
self
,
msg
,
sizeof
(
msg
),
ev_name
,
dso_filter
,
thread_filter
);
while
(
1
)
{
const
struct
thread
*
thread
=
NULL
;
const
struct
dso
*
dso
=
NULL
;
...
...
@@ -834,7 +864,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
annotate
=
-
2
,
zoom_dso
=
-
2
,
zoom_thread
=
-
2
,
browse_map
=
-
2
;
key
=
hist_browser__run
(
browser
,
msg
);
key
=
hist_browser__run
(
browser
,
ev_name
,
timer
,
arg
,
delay_secs
);
if
(
browser
->
he_selection
!=
NULL
)
{
thread
=
hist_browser__selected_thread
(
browser
);
...
...
@@ -862,16 +892,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
case
NEWT_KEY_F1
:
case
'h'
:
case
'?'
:
ui__help_window
(
"-> Zoom into DSO/Threads & Annotate current symbol
\n
"
ui__help_window
(
"h/?/F1 Show this window
\n
"
"TAB/UNTAB Switch events
\n
"
"q/CTRL+C Exit browser
\n\n
"
"For symbolic views (--sort has sym):
\n\n
"
"-> Zoom into DSO/Threads & Annotate current symbol
\n
"
"<- Zoom out
\n
"
"a Annotate current symbol
\n
"
"h/?/F1 Show this window
\n
"
"C Collapse all callchains
\n
"
"E Expand all callchains
\n
"
"d Zoom into current DSO
\n
"
"t Zoom into current Thread
\n
"
"TAB/UNTAB Switch events
\n
"
"q/CTRL+C Exit browser"
);
"t Zoom into current Thread
\n
"
);
continue
;
case
NEWT_KEY_ENTER
:
case
NEWT_KEY_RIGHT
:
...
...
@@ -889,9 +920,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
continue
;
}
top
=
pstack__pop
(
fstack
);
if
(
top
==
&
dso_filter
)
if
(
top
==
&
browser
->
dso_filter
)
goto
zoom_out_dso
;
if
(
top
==
&
thread_filter
)
if
(
top
==
&
browser
->
thread_filter
)
goto
zoom_out_thread
;
continue
;
}
...
...
@@ -904,6 +935,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
goto
out_free_stack
;
}
if
(
!
browser
->
has_symbols
)
goto
add_exit_option
;
if
(
browser
->
selection
!=
NULL
&&
browser
->
selection
->
sym
!=
NULL
&&
!
browser
->
selection
->
map
->
dso
->
annotate_warned
&&
...
...
@@ -913,14 +947,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
if
(
thread
!=
NULL
&&
asprintf
(
&
options
[
nr_options
],
"Zoom %s %s(%d) thread"
,
(
thread_filter
?
"out of"
:
"into"
),
(
browser
->
thread_filter
?
"out of"
:
"into"
),
(
thread
->
comm_set
?
thread
->
comm
:
""
),
thread
->
pid
)
>
0
)
zoom_thread
=
nr_options
++
;
if
(
dso
!=
NULL
&&
asprintf
(
&
options
[
nr_options
],
"Zoom %s %s DSO"
,
(
dso_filter
?
"out of"
:
"into"
),
(
browser
->
dso_filter
?
"out of"
:
"into"
),
(
dso
->
kernel
?
"the Kernel"
:
dso
->
short_name
))
>
0
)
zoom_dso
=
nr_options
++
;
...
...
@@ -928,7 +962,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
browser
->
selection
->
map
!=
NULL
&&
asprintf
(
&
options
[
nr_options
],
"Browse map details"
)
>
0
)
browse_map
=
nr_options
++
;
add_exit_option:
options
[
nr_options
++
]
=
(
char
*
)
"Exit"
;
choice
=
ui__popup_menu
(
nr_options
,
options
);
...
...
@@ -949,45 +983,42 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
if
(
he
==
NULL
)
continue
;
hist_entry__tui_annotate
(
he
,
evsel
->
idx
);
hist_entry__tui_annotate
(
he
,
evsel
->
idx
,
nr_events
,
timer
,
arg
,
delay_secs
);
}
else
if
(
choice
==
browse_map
)
map__browse
(
browser
->
selection
->
map
);
else
if
(
choice
==
zoom_dso
)
{
zoom_dso:
if
(
dso_filter
)
{
pstack__remove
(
fstack
,
&
dso_filter
);
if
(
browser
->
dso_filter
)
{
pstack__remove
(
fstack
,
&
browser
->
dso_filter
);
zoom_out_dso:
ui_helpline__pop
();
dso_filter
=
NULL
;
browser
->
dso_filter
=
NULL
;
}
else
{
if
(
dso
==
NULL
)
continue
;
ui_helpline__fpush
(
"To zoom out press <- or -> +
\"
Zoom out of %s DSO
\"
"
,
dso
->
kernel
?
"the Kernel"
:
dso
->
short_name
);
dso_filter
=
dso
;
pstack__push
(
fstack
,
&
dso_filter
);
browser
->
dso_filter
=
dso
;
pstack__push
(
fstack
,
&
browser
->
dso_filter
);
}
hists__filter_by_dso
(
self
,
dso_filter
);
hists__browser_title
(
self
,
msg
,
sizeof
(
msg
),
ev_name
,
dso_filter
,
thread_filter
);
hists__filter_by_dso
(
self
,
browser
->
dso_filter
);
hist_browser__reset
(
browser
);
}
else
if
(
choice
==
zoom_thread
)
{
zoom_thread:
if
(
thread_filter
)
{
pstack__remove
(
fstack
,
&
thread_filter
);
if
(
browser
->
thread_filter
)
{
pstack__remove
(
fstack
,
&
browser
->
thread_filter
);
zoom_out_thread:
ui_helpline__pop
();
thread_filter
=
NULL
;
browser
->
thread_filter
=
NULL
;
}
else
{
ui_helpline__fpush
(
"To zoom out press <- or -> +
\"
Zoom out of %s(%d) thread
\"
"
,
thread
->
comm_set
?
thread
->
comm
:
""
,
thread
->
pid
);
thread_filter
=
thread
;
pstack__push
(
fstack
,
&
thread_filter
);
browser
->
thread_filter
=
thread
;
pstack__push
(
fstack
,
&
browser
->
thread_filter
);
}
hists__filter_by_thread
(
self
,
thread_filter
);
hists__browser_title
(
self
,
msg
,
sizeof
(
msg
),
ev_name
,
dso_filter
,
thread_filter
);
hists__filter_by_thread
(
self
,
browser
->
thread_filter
);
hist_browser__reset
(
browser
);
}
}
...
...
@@ -1026,9 +1057,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
menu
->
selection
=
evsel
;
}
static
int
perf_evsel_menu__run
(
struct
perf_evsel_menu
*
menu
,
const
char
*
help
)
static
int
perf_evsel_menu__run
(
struct
perf_evsel_menu
*
menu
,
int
nr_events
,
const
char
*
help
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
)
{
int
exit_keys
[]
=
{
NEWT_KEY_ENTER
,
NEWT_KEY_RIGHT
,
0
,
};
int
delay_msecs
=
delay_secs
*
1000
;
struct
perf_evlist
*
evlist
=
menu
->
b
.
priv
;
struct
perf_evsel
*
pos
;
const
char
*
ev_name
,
*
title
=
"Available samples"
;
...
...
@@ -1038,20 +1072,30 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
"ESC: exit, ENTER|->: Browse histograms"
)
<
0
)
return
-
1
;
if
(
timer
!=
NULL
)
newtFormSetTimer
(
menu
->
b
.
form
,
delay_msecs
);
ui_browser__add_exit_keys
(
&
menu
->
b
,
exit_keys
);
while
(
1
)
{
key
=
ui_browser__run
(
&
menu
->
b
);
switch
(
key
)
{
case
-
1
:
/* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
timer
(
arg
);
continue
;
case
NEWT_KEY_RIGHT
:
case
NEWT_KEY_ENTER
:
if
(
!
menu
->
selection
)
continue
;
pos
=
menu
->
selection
;
perf_evlist__set_selected
(
evlist
,
pos
);
browse_hists:
ev_name
=
event_name
(
pos
);
key
=
perf_evsel__hists_browse
(
pos
,
help
,
ev_name
,
true
);
key
=
perf_evsel__hists_browse
(
pos
,
nr_events
,
help
,
ev_name
,
true
,
timer
,
arg
,
delay_secs
);
ui_browser__show_title
(
&
menu
->
b
,
title
);
break
;
case
NEWT_KEY_LEFT
:
...
...
@@ -1070,12 +1114,14 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
pos
=
list_entry
(
evlist
->
entries
.
next
,
struct
perf_evsel
,
node
);
else
pos
=
list_entry
(
pos
->
node
.
next
,
struct
perf_evsel
,
node
);
perf_evlist__set_selected
(
evlist
,
pos
);
goto
browse_hists
;
case
NEWT_KEY_UNTAB
:
if
(
pos
->
node
.
prev
==
&
evlist
->
entries
)
pos
=
list_entry
(
evlist
->
entries
.
prev
,
struct
perf_evsel
,
node
);
else
pos
=
list_entry
(
pos
->
node
.
prev
,
struct
perf_evsel
,
node
);
perf_evlist__set_selected
(
evlist
,
pos
);
goto
browse_hists
;
case
'q'
:
case
CTRL
(
'c'
):
...
...
@@ -1091,7 +1137,9 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
}
static
int
__perf_evlist__tui_browse_hists
(
struct
perf_evlist
*
evlist
,
const
char
*
help
)
const
char
*
help
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
)
{
struct
perf_evsel
*
pos
;
struct
perf_evsel_menu
menu
=
{
...
...
@@ -1121,18 +1169,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
pos
->
name
=
strdup
(
ev_name
);
}
return
perf_evsel_menu__run
(
&
menu
,
help
);
return
perf_evsel_menu__run
(
&
menu
,
evlist
->
nr_entries
,
help
,
timer
,
arg
,
delay_secs
);
}
int
perf_evlist__tui_browse_hists
(
struct
perf_evlist
*
evlist
,
const
char
*
help
)
int
perf_evlist__tui_browse_hists
(
struct
perf_evlist
*
evlist
,
const
char
*
help
,
void
(
*
timer
)(
void
*
arg
),
void
*
arg
,
int
delay_secs
)
{
if
(
evlist
->
nr_entries
==
1
)
{
struct
perf_evsel
*
first
=
list_entry
(
evlist
->
entries
.
next
,
struct
perf_evsel
,
node
);
const
char
*
ev_name
=
event_name
(
first
);
return
perf_evsel__hists_browse
(
first
,
help
,
ev_name
,
false
);
return
perf_evsel__hists_browse
(
first
,
evlist
->
nr_entries
,
help
,
ev_name
,
false
,
timer
,
arg
,
delay_secs
);
}
return
__perf_evlist__tui_browse_hists
(
evlist
,
help
);
return
__perf_evlist__tui_browse_hists
(
evlist
,
help
,
timer
,
arg
,
delay_secs
);
}
tools/perf/util/ui/browsers/top.c
deleted
100644 → 0
View file @
d48b0e17
/*
* Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Parts came from builtin-{top,stat,record}.c, see those files for further
* copyright notes.
*
* Released under the GPL v2. (and only v2, not any later version)
*/
#include "../browser.h"
#include "../../annotate.h"
#include "../helpline.h"
#include "../libslang.h"
#include "../util.h"
#include "../ui.h"
#include "../../evlist.h"
#include "../../hist.h"
#include "../../sort.h"
#include "../../symbol.h"
#include "../../session.h"
#include "../../top.h"
struct
perf_top_browser
{
struct
ui_browser
b
;
struct
rb_root
root
;
struct
sym_entry
*
selection
;
float
sum_ksamples
;
int
dso_width
;
int
dso_short_width
;
int
sym_width
;
};
static
void
perf_top_browser__write
(
struct
ui_browser
*
browser
,
void
*
entry
,
int
row
)
{
struct
perf_top_browser
*
top_browser
=
container_of
(
browser
,
struct
perf_top_browser
,
b
);
struct
sym_entry
*
syme
=
rb_entry
(
entry
,
struct
sym_entry
,
rb_node
);
bool
current_entry
=
ui_browser__is_current_entry
(
browser
,
row
);
struct
symbol
*
symbol
=
sym_entry__symbol
(
syme
);
struct
perf_top
*
top
=
browser
->
priv
;
int
width
=
browser
->
width
;
double
pcnt
;
pcnt
=
100
.
0
-
(
100
.
0
*
((
top_browser
->
sum_ksamples
-
syme
->
snap_count
)
/
top_browser
->
sum_ksamples
));
ui_browser__set_percent_color
(
browser
,
pcnt
,
current_entry
);
if
(
top
->
evlist
->
nr_entries
==
1
||
!
top
->
display_weighted
)
{
slsmg_printf
(
"%20.2f "
,
syme
->
weight
);
width
-=
21
;
}
else
{
slsmg_printf
(
"%9.1f %10ld "
,
syme
->
weight
,
syme
->
snap_count
);
width
-=
20
;
}
slsmg_printf
(
"%4.1f%%"
,
pcnt
);
width
-=
7
;
if
(
verbose
)
{
slsmg_printf
(
" %016"
PRIx64
,
symbol
->
start
);
width
-=
17
;
}
slsmg_printf
(
" %-*.*s "
,
top_browser
->
sym_width
,
top_browser
->
sym_width
,
symbol
->
name
);
width
-=
top_browser
->
sym_width
;
slsmg_write_nstring
(
width
>=
syme
->
map
->
dso
->
long_name_len
?
syme
->
map
->
dso
->
long_name
:
syme
->
map
->
dso
->
short_name
,
width
);
if
(
current_entry
)
top_browser
->
selection
=
syme
;
}
static
void
perf_top_browser__update_rb_tree
(
struct
perf_top_browser
*
browser
)
{
struct
perf_top
*
top
=
browser
->
b
.
priv
;
u64
top_idx
=
browser
->
b
.
top_idx
;
browser
->
root
=
RB_ROOT
;
browser
->
b
.
top
=
NULL
;
browser
->
sum_ksamples
=
perf_top__decay_samples
(
top
,
&
browser
->
root
);
/*
* No active symbols
*/
if
(
top
->
rb_entries
==
0
)
return
;
perf_top__find_widths
(
top
,
&
browser
->
root
,
&
browser
->
dso_width
,
&
browser
->
dso_short_width
,
&
browser
->
sym_width
);
if
(
browser
->
sym_width
+
browser
->
dso_width
>
browser
->
b
.
width
-
29
)
{
browser
->
dso_width
=
browser
->
dso_short_width
;
if
(
browser
->
sym_width
+
browser
->
dso_width
>
browser
->
b
.
width
-
29
)
browser
->
sym_width
=
browser
->
b
.
width
-
browser
->
dso_width
-
29
;
}
/*
* Adjust the ui_browser indexes since the entries in the browser->root
* rb_tree may have changed, then seek it from start, so that we get a
* possible new top of the screen.
*/
browser
->
b
.
nr_entries
=
top
->
rb_entries
;
if
(
top_idx
>=
browser
->
b
.
nr_entries
)
{
if
(
browser
->
b
.
height
>=
browser
->
b
.
nr_entries
)
top_idx
=
browser
->
b
.
nr_entries
-
browser
->
b
.
height
;
else
top_idx
=
0
;
}
if
(
browser
->
b
.
index
>=
top_idx
+
browser
->
b
.
height
)
browser
->
b
.
index
=
top_idx
+
browser
->
b
.
index
-
browser
->
b
.
top_idx
;
if
(
browser
->
b
.
index
>=
browser
->
b
.
nr_entries
)
browser
->
b
.
index
=
browser
->
b
.
nr_entries
-
1
;
browser
->
b
.
top_idx
=
top_idx
;
browser
->
b
.
seek
(
&
browser
->
b
,
top_idx
,
SEEK_SET
);
}
static
void
perf_top_browser__annotate
(
struct
perf_top_browser
*
browser
)
{
struct
sym_entry
*
syme
=
browser
->
selection
;
struct
symbol
*
sym
=
sym_entry__symbol
(
syme
);
struct
annotation
*
notes
=
symbol__annotation
(
sym
);
struct
perf_top
*
top
=
browser
->
b
.
priv
;
if
(
notes
->
src
!=
NULL
)
goto
do_annotation
;
pthread_mutex_lock
(
&
notes
->
lock
);
top
->
sym_filter_entry
=
NULL
;
if
(
symbol__alloc_hist
(
sym
,
top
->
evlist
->
nr_entries
)
<
0
)
{
pr_err
(
"Not enough memory for annotating '%s' symbol!
\n
"
,
sym
->
name
);
pthread_mutex_unlock
(
&
notes
->
lock
);
return
;
}
top
->
sym_filter_entry
=
syme
;
pthread_mutex_unlock
(
&
notes
->
lock
);
do_annotation:
symbol__tui_annotate
(
sym
,
syme
->
map
,
0
,
top
->
delay_secs
*
1000
);
}
static
void
perf_top_browser__warn_lost
(
struct
perf_top_browser
*
browser
)
{
struct
perf_top
*
top
=
browser
->
b
.
priv
;
char
msg
[
128
];
int
len
;
top
->
total_lost_warned
=
top
->
session
->
hists
.
stats
.
total_lost
;
pthread_mutex_lock
(
&
ui__lock
);
ui_browser__set_color
(
&
browser
->
b
,
HE_COLORSET_TOP
);
len
=
snprintf
(
msg
,
sizeof
(
msg
),
" WARNING: LOST %"
PRIu64
" events, Check IO/CPU overload"
,
top
->
total_lost_warned
);
if
(
len
>
browser
->
b
.
width
)
len
=
browser
->
b
.
width
;
SLsmg_gotorc
(
0
,
browser
->
b
.
width
-
len
);
slsmg_write_nstring
(
msg
,
len
);
pthread_mutex_unlock
(
&
ui__lock
);
}
static
int
perf_top_browser__run
(
struct
perf_top_browser
*
browser
)
{
int
key
;
char
title
[
160
];
struct
perf_top
*
top
=
browser
->
b
.
priv
;
int
delay_msecs
=
top
->
delay_secs
*
1000
;
int
exit_keys
[]
=
{
'a'
,
NEWT_KEY_ENTER
,
NEWT_KEY_RIGHT
,
0
,
};
perf_top_browser__update_rb_tree
(
browser
);
perf_top__header_snprintf
(
top
,
title
,
sizeof
(
title
));
perf_top__reset_sample_counters
(
top
);
if
(
ui_browser__show
(
&
browser
->
b
,
title
,
"ESC: exit, ENTER|->|a: Live Annotate"
)
<
0
)
return
-
1
;
newtFormSetTimer
(
browser
->
b
.
form
,
delay_msecs
);
ui_browser__add_exit_keys
(
&
browser
->
b
,
exit_keys
);
while
(
1
)
{
key
=
ui_browser__run
(
&
browser
->
b
);
switch
(
key
)
{
case
-
1
:
/* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
perf_top_browser__update_rb_tree
(
browser
);
perf_top__header_snprintf
(
top
,
title
,
sizeof
(
title
));
perf_top__reset_sample_counters
(
top
);
ui_browser__set_color
(
&
browser
->
b
,
NEWT_COLORSET_ROOT
);
SLsmg_gotorc
(
0
,
0
);
slsmg_write_nstring
(
title
,
browser
->
b
.
width
);
if
(
top
->
total_lost_warned
!=
top
->
session
->
hists
.
stats
.
total_lost
)
perf_top_browser__warn_lost
(
browser
);
break
;
case
'a'
:
case
NEWT_KEY_RIGHT
:
case
NEWT_KEY_ENTER
:
if
(
browser
->
selection
)
perf_top_browser__annotate
(
browser
);
break
;
case
NEWT_KEY_LEFT
:
continue
;
case
NEWT_KEY_ESCAPE
:
if
(
!
ui__dialog_yesno
(
"Do you really want to exit?"
))
continue
;
/* Fall thru */
default:
goto
out
;
}
}
out:
ui_browser__hide
(
&
browser
->
b
);
return
key
;
}
int
perf_top__tui_browser
(
struct
perf_top
*
top
)
{
struct
perf_top_browser
browser
=
{
.
b
=
{
.
entries
=
&
browser
.
root
,
.
refresh
=
ui_browser__rb_tree_refresh
,
.
seek
=
ui_browser__rb_tree_seek
,
.
write
=
perf_top_browser__write
,
.
priv
=
top
,
},
};
return
perf_top_browser__run
(
&
browser
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment