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
cf230918
Commit
cf230918
authored
Jun 14, 2014
by
Ingo Molnar
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'perf/core' into perf/urgent, to pick up the latest fixes
Signed-off-by:
Ingo Molnar
<
mingo@kernel.org
>
parents
4cdf77a8
4ba96195
Changes
39
Hide whitespace changes
Inline
Side-by-side
Showing
39 changed files
with
1242 additions
and
123 deletions
+1242
-123
include/uapi/linux/perf_event.h
include/uapi/linux/perf_event.h
+1
-0
kernel/events/core.c
kernel/events/core.c
+33
-4
tools/lib/traceevent/event-parse.c
tools/lib/traceevent/event-parse.c
+113
-0
tools/lib/traceevent/event-parse.h
tools/lib/traceevent/event-parse.h
+22
-3
tools/lib/traceevent/event-plugin.c
tools/lib/traceevent/event-plugin.c
+202
-1
tools/lib/traceevent/plugin_function.c
tools/lib/traceevent/plugin_function.c
+37
-6
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-report.txt
+23
-0
tools/perf/Documentation/perf-timechart.txt
tools/perf/Documentation/perf-timechart.txt
+20
-21
tools/perf/Makefile.perf
tools/perf/Makefile.perf
+3
-3
tools/perf/builtin-inject.c
tools/perf/builtin-inject.c
+1
-1
tools/perf/builtin-probe.c
tools/perf/builtin-probe.c
+14
-9
tools/perf/config/Makefile
tools/perf/config/Makefile
+5
-1
tools/perf/perf.c
tools/perf/perf.c
+1
-0
tools/perf/tests/builtin-test.c
tools/perf/tests/builtin-test.c
+40
-2
tools/perf/tests/dso-data.c
tools/perf/tests/dso-data.c
+210
-4
tools/perf/tests/dwarf-unwind.c
tools/perf/tests/dwarf-unwind.c
+1
-1
tools/perf/tests/make
tools/perf/tests/make
+2
-5
tools/perf/tests/tests.h
tools/perf/tests/tests.h
+2
-0
tools/perf/util/dso.c
tools/perf/util/dso.c
+254
-25
tools/perf/util/dso.h
tools/perf/util/dso.h
+49
-1
tools/perf/util/event.c
tools/perf/util/event.c
+41
-16
tools/perf/util/event.h
tools/perf/util/event.h
+7
-0
tools/perf/util/evsel.c
tools/perf/util/evsel.c
+3
-2
tools/perf/util/hist.c
tools/perf/util/hist.c
+6
-3
tools/perf/util/hist.h
tools/perf/util/hist.h
+1
-0
tools/perf/util/machine.c
tools/perf/util/machine.c
+3
-1
tools/perf/util/map.c
tools/perf/util/map.c
+3
-1
tools/perf/util/map.h
tools/perf/util/map.h
+3
-1
tools/perf/util/perf_regs.c
tools/perf/util/perf_regs.c
+9
-1
tools/perf/util/perf_regs.h
tools/perf/util/perf_regs.h
+3
-1
tools/perf/util/probe-event.c
tools/perf/util/probe-event.c
+9
-4
tools/perf/util/probe-finder.c
tools/perf/util/probe-finder.c
+7
-4
tools/perf/util/scripting-engines/trace-event-perl.c
tools/perf/util/scripting-engines/trace-event-perl.c
+1
-0
tools/perf/util/scripting-engines/trace-event-python.c
tools/perf/util/scripting-engines/trace-event-python.c
+2
-0
tools/perf/util/sort.c
tools/perf/util/sort.c
+107
-0
tools/perf/util/sort.h
tools/perf/util/sort.h
+2
-0
tools/perf/util/unwind-libunwind.c
tools/perf/util/unwind-libunwind.c
+0
-2
tools/perf/util/util.c
tools/perf/util/util.c
+1
-0
tools/perf/util/util.h
tools/perf/util/util.h
+1
-0
No files found.
include/uapi/linux/perf_event.h
View file @
cf230918
...
...
@@ -705,6 +705,7 @@ enum perf_event_type {
* u32 min;
* u64 ino;
* u64 ino_generation;
* u32 prot, flags;
* char filename[];
* struct sample_id sample_id;
* };
...
...
kernel/events/core.c
View file @
cf230918
...
...
@@ -40,6 +40,7 @@
#include <linux/mm_types.h>
#include <linux/cgroup.h>
#include <linux/module.h>
#include <linux/mman.h>
#include "internal.h"
...
...
@@ -5128,6 +5129,7 @@ struct perf_mmap_event {
int
maj
,
min
;
u64
ino
;
u64
ino_generation
;
u32
prot
,
flags
;
struct
{
struct
perf_event_header
header
;
...
...
@@ -5169,6 +5171,8 @@ static void perf_event_mmap_output(struct perf_event *event,
mmap_event
->
event_id
.
header
.
size
+=
sizeof
(
mmap_event
->
min
);
mmap_event
->
event_id
.
header
.
size
+=
sizeof
(
mmap_event
->
ino
);
mmap_event
->
event_id
.
header
.
size
+=
sizeof
(
mmap_event
->
ino_generation
);
mmap_event
->
event_id
.
header
.
size
+=
sizeof
(
mmap_event
->
prot
);
mmap_event
->
event_id
.
header
.
size
+=
sizeof
(
mmap_event
->
flags
);
}
perf_event_header__init_id
(
&
mmap_event
->
event_id
.
header
,
&
sample
,
event
);
...
...
@@ -5187,6 +5191,8 @@ static void perf_event_mmap_output(struct perf_event *event,
perf_output_put
(
&
handle
,
mmap_event
->
min
);
perf_output_put
(
&
handle
,
mmap_event
->
ino
);
perf_output_put
(
&
handle
,
mmap_event
->
ino_generation
);
perf_output_put
(
&
handle
,
mmap_event
->
prot
);
perf_output_put
(
&
handle
,
mmap_event
->
flags
);
}
__output_copy
(
&
handle
,
mmap_event
->
file_name
,
...
...
@@ -5205,6 +5211,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
struct
file
*
file
=
vma
->
vm_file
;
int
maj
=
0
,
min
=
0
;
u64
ino
=
0
,
gen
=
0
;
u32
prot
=
0
,
flags
=
0
;
unsigned
int
size
;
char
tmp
[
16
];
char
*
buf
=
NULL
;
...
...
@@ -5235,6 +5242,28 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
gen
=
inode
->
i_generation
;
maj
=
MAJOR
(
dev
);
min
=
MINOR
(
dev
);
if
(
vma
->
vm_flags
&
VM_READ
)
prot
|=
PROT_READ
;
if
(
vma
->
vm_flags
&
VM_WRITE
)
prot
|=
PROT_WRITE
;
if
(
vma
->
vm_flags
&
VM_EXEC
)
prot
|=
PROT_EXEC
;
if
(
vma
->
vm_flags
&
VM_MAYSHARE
)
flags
=
MAP_SHARED
;
else
flags
=
MAP_PRIVATE
;
if
(
vma
->
vm_flags
&
VM_DENYWRITE
)
flags
|=
MAP_DENYWRITE
;
if
(
vma
->
vm_flags
&
VM_MAYEXEC
)
flags
|=
MAP_EXECUTABLE
;
if
(
vma
->
vm_flags
&
VM_LOCKED
)
flags
|=
MAP_LOCKED
;
if
(
vma
->
vm_flags
&
VM_HUGETLB
)
flags
|=
MAP_HUGETLB
;
goto
got_name
;
}
else
{
name
=
(
char
*
)
arch_vma_name
(
vma
);
...
...
@@ -5275,6 +5304,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
mmap_event
->
min
=
min
;
mmap_event
->
ino
=
ino
;
mmap_event
->
ino_generation
=
gen
;
mmap_event
->
prot
=
prot
;
mmap_event
->
flags
=
flags
;
if
(
!
(
vma
->
vm_flags
&
VM_EXEC
))
mmap_event
->
event_id
.
header
.
misc
|=
PERF_RECORD_MISC_MMAP_DATA
;
...
...
@@ -5315,6 +5346,8 @@ void perf_event_mmap(struct vm_area_struct *vma)
/* .min (attr_mmap2 only) */
/* .ino (attr_mmap2 only) */
/* .ino_generation (attr_mmap2 only) */
/* .prot (attr_mmap2 only) */
/* .flags (attr_mmap2 only) */
};
perf_event_mmap_event
(
&
mmap_event
);
...
...
@@ -6897,10 +6930,6 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
if
(
ret
)
return
-
EFAULT
;
/* disabled for now */
if
(
attr
->
mmap2
)
return
-
EINVAL
;
if
(
attr
->
__reserved_1
)
return
-
EINVAL
;
...
...
tools/lib/traceevent/event-parse.c
View file @
cf230918
...
...
@@ -765,6 +765,9 @@ static void free_arg(struct print_arg *arg)
case
PRINT_BSTRING
:
free
(
arg
->
string
.
string
);
break
;
case
PRINT_BITMASK
:
free
(
arg
->
bitmask
.
bitmask
);
break
;
case
PRINT_DYNAMIC_ARRAY
:
free
(
arg
->
dynarray
.
index
);
break
;
...
...
@@ -2268,6 +2271,7 @@ static int arg_num_eval(struct print_arg *arg, long long *val)
case
PRINT_FIELD
...
PRINT_SYMBOL
:
case
PRINT_STRING
:
case
PRINT_BSTRING
:
case
PRINT_BITMASK
:
default:
do_warning
(
"invalid eval type %d"
,
arg
->
type
);
ret
=
0
;
...
...
@@ -2296,6 +2300,7 @@ static char *arg_eval (struct print_arg *arg)
case
PRINT_FIELD
...
PRINT_SYMBOL
:
case
PRINT_STRING
:
case
PRINT_BSTRING
:
case
PRINT_BITMASK
:
default:
do_warning
(
"invalid eval type %d"
,
arg
->
type
);
break
;
...
...
@@ -2683,6 +2688,35 @@ process_str(struct event_format *event __maybe_unused, struct print_arg *arg,
return
EVENT_ERROR
;
}
static
enum
event_type
process_bitmask
(
struct
event_format
*
event
__maybe_unused
,
struct
print_arg
*
arg
,
char
**
tok
)
{
enum
event_type
type
;
char
*
token
;
if
(
read_expect_type
(
EVENT_ITEM
,
&
token
)
<
0
)
goto
out_free
;
arg
->
type
=
PRINT_BITMASK
;
arg
->
bitmask
.
bitmask
=
token
;
arg
->
bitmask
.
offset
=
-
1
;
if
(
read_expected
(
EVENT_DELIM
,
")"
)
<
0
)
goto
out_err
;
type
=
read_token
(
&
token
);
*
tok
=
token
;
return
type
;
out_free:
free_token
(
token
);
out_err:
*
tok
=
NULL
;
return
EVENT_ERROR
;
}
static
struct
pevent_function_handler
*
find_func_handler
(
struct
pevent
*
pevent
,
char
*
func_name
)
{
...
...
@@ -2797,6 +2831,10 @@ process_function(struct event_format *event, struct print_arg *arg,
free_token
(
token
);
return
process_str
(
event
,
arg
,
tok
);
}
if
(
strcmp
(
token
,
"__get_bitmask"
)
==
0
)
{
free_token
(
token
);
return
process_bitmask
(
event
,
arg
,
tok
);
}
if
(
strcmp
(
token
,
"__get_dynamic_array"
)
==
0
)
{
free_token
(
token
);
return
process_dynamic_array
(
event
,
arg
,
tok
);
...
...
@@ -3324,6 +3362,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
return
eval_type
(
val
,
arg
,
0
);
case
PRINT_STRING
:
case
PRINT_BSTRING
:
case
PRINT_BITMASK
:
return
0
;
case
PRINT_FUNC
:
{
struct
trace_seq
s
;
...
...
@@ -3556,6 +3595,60 @@ static void print_str_to_seq(struct trace_seq *s, const char *format,
trace_seq_printf
(
s
,
format
,
str
);
}
static
void
print_bitmask_to_seq
(
struct
pevent
*
pevent
,
struct
trace_seq
*
s
,
const
char
*
format
,
int
len_arg
,
const
void
*
data
,
int
size
)
{
int
nr_bits
=
size
*
8
;
int
str_size
=
(
nr_bits
+
3
)
/
4
;
int
len
=
0
;
char
buf
[
3
];
char
*
str
;
int
index
;
int
i
;
/*
* The kernel likes to put in commas every 32 bits, we
* can do the same.
*/
str_size
+=
(
nr_bits
-
1
)
/
32
;
str
=
malloc
(
str_size
+
1
);
if
(
!
str
)
{
do_warning
(
"%s: not enough memory!"
,
__func__
);
return
;
}
str
[
str_size
]
=
0
;
/* Start out with -2 for the two chars per byte */
for
(
i
=
str_size
-
2
;
i
>=
0
;
i
-=
2
)
{
/*
* data points to a bit mask of size bytes.
* In the kernel, this is an array of long words, thus
* endianess is very important.
*/
if
(
pevent
->
file_bigendian
)
index
=
size
-
(
len
+
1
);
else
index
=
len
;
snprintf
(
buf
,
3
,
"%02x"
,
*
((
unsigned
char
*
)
data
+
index
));
memcpy
(
str
+
i
,
buf
,
2
);
len
++
;
if
(
!
(
len
&
3
)
&&
i
>
0
)
{
i
--
;
str
[
i
]
=
','
;
}
}
if
(
len_arg
>=
0
)
trace_seq_printf
(
s
,
format
,
len_arg
,
str
);
else
trace_seq_printf
(
s
,
format
,
str
);
free
(
str
);
}
static
void
print_str_arg
(
struct
trace_seq
*
s
,
void
*
data
,
int
size
,
struct
event_format
*
event
,
const
char
*
format
,
int
len_arg
,
struct
print_arg
*
arg
)
...
...
@@ -3691,6 +3784,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
case
PRINT_BSTRING
:
print_str_to_seq
(
s
,
format
,
len_arg
,
arg
->
string
.
string
);
break
;
case
PRINT_BITMASK
:
{
int
bitmask_offset
;
int
bitmask_size
;
if
(
arg
->
bitmask
.
offset
==
-
1
)
{
struct
format_field
*
f
;
f
=
pevent_find_any_field
(
event
,
arg
->
bitmask
.
bitmask
);
arg
->
bitmask
.
offset
=
f
->
offset
;
}
bitmask_offset
=
data2host4
(
pevent
,
data
+
arg
->
bitmask
.
offset
);
bitmask_size
=
bitmask_offset
>>
16
;
bitmask_offset
&=
0xffff
;
print_bitmask_to_seq
(
pevent
,
s
,
format
,
len_arg
,
data
+
bitmask_offset
,
bitmask_size
);
break
;
}
case
PRINT_OP
:
/*
* The only op for string should be ? :
...
...
@@ -4822,6 +4932,9 @@ static void print_args(struct print_arg *args)
case
PRINT_BSTRING
:
printf
(
"__get_str(%s)"
,
args
->
string
.
string
);
break
;
case
PRINT_BITMASK
:
printf
(
"__get_bitmask(%s)"
,
args
->
bitmask
.
bitmask
);
break
;
case
PRINT_TYPE
:
printf
(
"(%s)"
,
args
->
typecast
.
type
);
print_args
(
args
->
typecast
.
item
);
...
...
tools/lib/traceevent/event-parse.h
View file @
cf230918
...
...
@@ -107,8 +107,8 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s,
typedef
int
(
*
pevent_plugin_load_func
)(
struct
pevent
*
pevent
);
typedef
int
(
*
pevent_plugin_unload_func
)(
struct
pevent
*
pevent
);
struct
plugin_option
{
struct
p
lugin_option
*
next
;
struct
p
event_p
lugin_option
{
struct
p
event_plugin_option
*
next
;
void
*
handle
;
char
*
file
;
char
*
name
;
...
...
@@ -135,7 +135,7 @@ struct plugin_option {
* PEVENT_PLUGIN_OPTIONS: (optional)
* Plugin options that can be set before loading
*
* struct plugin_option PEVENT_PLUGIN_OPTIONS[] = {
* struct p
event_p
lugin_option PEVENT_PLUGIN_OPTIONS[] = {
* {
* .name = "option-name",
* .plugin_alias = "overide-file-name", (optional)
...
...
@@ -208,6 +208,11 @@ struct print_arg_string {
int
offset
;
};
struct
print_arg_bitmask
{
char
*
bitmask
;
int
offset
;
};
struct
print_arg_field
{
char
*
name
;
struct
format_field
*
field
;
...
...
@@ -274,6 +279,7 @@ enum print_arg_type {
PRINT_DYNAMIC_ARRAY
,
PRINT_OP
,
PRINT_FUNC
,
PRINT_BITMASK
,
};
struct
print_arg
{
...
...
@@ -288,6 +294,7 @@ struct print_arg {
struct
print_arg_hex
hex
;
struct
print_arg_func
func
;
struct
print_arg_string
string
;
struct
print_arg_bitmask
bitmask
;
struct
print_arg_op
op
;
struct
print_arg_dynarray
dynarray
;
};
...
...
@@ -354,6 +361,8 @@ enum pevent_func_arg_type {
enum
pevent_flag
{
PEVENT_NSEC_OUTPUT
=
1
,
/* output in NSECS */
PEVENT_DISABLE_SYS_PLUGINS
=
1
<<
1
,
PEVENT_DISABLE_PLUGINS
=
1
<<
2
,
};
#define PEVENT_ERRORS \
...
...
@@ -410,9 +419,19 @@ enum pevent_errno {
struct
plugin_list
;
#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1))
struct
plugin_list
*
traceevent_load_plugins
(
struct
pevent
*
pevent
);
void
traceevent_unload_plugins
(
struct
plugin_list
*
plugin_list
,
struct
pevent
*
pevent
);
char
**
traceevent_plugin_list_options
(
void
);
void
traceevent_plugin_free_options_list
(
char
**
list
);
int
traceevent_plugin_add_options
(
const
char
*
name
,
struct
pevent_plugin_option
*
options
);
void
traceevent_plugin_remove_options
(
struct
pevent_plugin_option
*
options
);
void
traceevent_print_plugins
(
struct
trace_seq
*
s
,
const
char
*
prefix
,
const
char
*
suffix
,
const
struct
plugin_list
*
list
);
struct
cmdline
;
struct
cmdline_list
;
...
...
tools/lib/traceevent/event-plugin.c
View file @
cf230918
...
...
@@ -18,6 +18,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
...
...
@@ -30,12 +31,207 @@
#define LOCAL_PLUGIN_DIR ".traceevent/plugins"
static
struct
registered_plugin_options
{
struct
registered_plugin_options
*
next
;
struct
pevent_plugin_option
*
options
;
}
*
registered_options
;
static
struct
trace_plugin_options
{
struct
trace_plugin_options
*
next
;
char
*
plugin
;
char
*
option
;
char
*
value
;
}
*
trace_plugin_options
;
struct
plugin_list
{
struct
plugin_list
*
next
;
char
*
name
;
void
*
handle
;
};
/**
* traceevent_plugin_list_options - get list of plugin options
*
* Returns an array of char strings that list the currently registered
* plugin options in the format of <plugin>:<option>. This list can be
* used by toggling the option.
*
* Returns NULL if there's no options registered. On error it returns
* INVALID_PLUGIN_LIST_OPTION
*
* Must be freed with traceevent_plugin_free_options_list().
*/
char
**
traceevent_plugin_list_options
(
void
)
{
struct
registered_plugin_options
*
reg
;
struct
pevent_plugin_option
*
op
;
char
**
list
=
NULL
;
char
*
name
;
int
count
=
0
;
for
(
reg
=
registered_options
;
reg
;
reg
=
reg
->
next
)
{
for
(
op
=
reg
->
options
;
op
->
name
;
op
++
)
{
char
*
alias
=
op
->
plugin_alias
?
op
->
plugin_alias
:
op
->
file
;
char
**
temp
=
list
;
name
=
malloc
(
strlen
(
op
->
name
)
+
strlen
(
alias
)
+
2
);
if
(
!
name
)
goto
err
;
sprintf
(
name
,
"%s:%s"
,
alias
,
op
->
name
);
list
=
realloc
(
list
,
count
+
2
);
if
(
!
list
)
{
list
=
temp
;
free
(
name
);
goto
err
;
}
list
[
count
++
]
=
name
;
list
[
count
]
=
NULL
;
}
}
return
list
;
err:
while
(
--
count
>=
0
)
free
(
list
[
count
]);
free
(
list
);
return
INVALID_PLUGIN_LIST_OPTION
;
}
void
traceevent_plugin_free_options_list
(
char
**
list
)
{
int
i
;
if
(
!
list
)
return
;
if
(
list
==
INVALID_PLUGIN_LIST_OPTION
)
return
;
for
(
i
=
0
;
list
[
i
];
i
++
)
free
(
list
[
i
]);
free
(
list
);
}
static
int
update_option
(
const
char
*
file
,
struct
pevent_plugin_option
*
option
)
{
struct
trace_plugin_options
*
op
;
char
*
plugin
;
if
(
option
->
plugin_alias
)
{
plugin
=
strdup
(
option
->
plugin_alias
);
if
(
!
plugin
)
return
-
1
;
}
else
{
char
*
p
;
plugin
=
strdup
(
file
);
if
(
!
plugin
)
return
-
1
;
p
=
strstr
(
plugin
,
"."
);
if
(
p
)
*
p
=
'\0'
;
}
/* first look for named options */
for
(
op
=
trace_plugin_options
;
op
;
op
=
op
->
next
)
{
if
(
!
op
->
plugin
)
continue
;
if
(
strcmp
(
op
->
plugin
,
plugin
)
!=
0
)
continue
;
if
(
strcmp
(
op
->
option
,
option
->
name
)
!=
0
)
continue
;
option
->
value
=
op
->
value
;
option
->
set
^=
1
;
goto
out
;
}
/* first look for unnamed options */
for
(
op
=
trace_plugin_options
;
op
;
op
=
op
->
next
)
{
if
(
op
->
plugin
)
continue
;
if
(
strcmp
(
op
->
option
,
option
->
name
)
!=
0
)
continue
;
option
->
value
=
op
->
value
;
option
->
set
^=
1
;
break
;
}
out:
free
(
plugin
);
return
0
;
}
/**
* traceevent_plugin_add_options - Add a set of options by a plugin
* @name: The name of the plugin adding the options
* @options: The set of options being loaded
*
* Sets the options with the values that have been added by user.
*/
int
traceevent_plugin_add_options
(
const
char
*
name
,
struct
pevent_plugin_option
*
options
)
{
struct
registered_plugin_options
*
reg
;
reg
=
malloc
(
sizeof
(
*
reg
));
if
(
!
reg
)
return
-
1
;
reg
->
next
=
registered_options
;
reg
->
options
=
options
;
registered_options
=
reg
;
while
(
options
->
name
)
{
update_option
(
name
,
options
);
options
++
;
}
return
0
;
}
/**
* traceevent_plugin_remove_options - remove plugin options that were registered
* @options: Options to removed that were registered with traceevent_plugin_add_options
*/
void
traceevent_plugin_remove_options
(
struct
pevent_plugin_option
*
options
)
{
struct
registered_plugin_options
**
last
;
struct
registered_plugin_options
*
reg
;
for
(
last
=
&
registered_options
;
*
last
;
last
=
&
(
*
last
)
->
next
)
{
if
((
*
last
)
->
options
==
options
)
{
reg
=
*
last
;
*
last
=
reg
->
next
;
free
(
reg
);
return
;
}
}
}
/**
* traceevent_print_plugins - print out the list of plugins loaded
* @s: the trace_seq descripter to write to
* @prefix: The prefix string to add before listing the option name
* @suffix: The suffix string ot append after the option name
* @list: The list of plugins (usually returned by traceevent_load_plugins()
*
* Writes to the trace_seq @s the list of plugins (files) that is
* returned by traceevent_load_plugins(). Use @prefix and @suffix for formating:
* @prefix = " ", @suffix = "\n".
*/
void
traceevent_print_plugins
(
struct
trace_seq
*
s
,
const
char
*
prefix
,
const
char
*
suffix
,
const
struct
plugin_list
*
list
)
{
while
(
list
)
{
trace_seq_printf
(
s
,
"%s%s%s"
,
prefix
,
list
->
name
,
suffix
);
list
=
list
->
next
;
}
}
static
void
load_plugin
(
struct
pevent
*
pevent
,
const
char
*
path
,
const
char
*
file
,
void
*
data
)
...
...
@@ -148,12 +344,17 @@ load_plugins(struct pevent *pevent, const char *suffix,
char
*
path
;
char
*
envdir
;
if
(
pevent
->
flags
&
PEVENT_DISABLE_PLUGINS
)
return
;
/*
* If a system plugin directory was defined,
* check that first.
*/
#ifdef PLUGIN_DIR
load_plugins_dir
(
pevent
,
suffix
,
PLUGIN_DIR
,
load_plugin
,
data
);
if
(
!
(
pevent
->
flags
&
PEVENT_DISABLE_SYS_PLUGINS
))
load_plugins_dir
(
pevent
,
suffix
,
PLUGIN_DIR
,
load_plugin
,
data
);
#endif
/*
...
...
tools/lib/traceevent/plugin_function.c
View file @
cf230918
...
...
@@ -33,6 +33,29 @@ static int cpus = -1;
#define STK_BLK 10
struct
pevent_plugin_option
plugin_options
[]
=
{
{
.
name
=
"parent"
,
.
plugin_alias
=
"ftrace"
,
.
description
=
"Print parent of functions for function events"
,
},
{
.
name
=
"indent"
,
.
plugin_alias
=
"ftrace"
,
.
description
=
"Try to show function call indents, based on parents"
,
.
set
=
1
,
},
{
.
name
=
NULL
,
}
};
static
struct
pevent_plugin_option
*
ftrace_parent
=
&
plugin_options
[
0
];
static
struct
pevent_plugin_option
*
ftrace_indent
=
&
plugin_options
[
1
];
static
void
add_child
(
struct
func_stack
*
stack
,
const
char
*
child
,
int
pos
)
{
int
i
;
...
...
@@ -119,7 +142,8 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record,
parent
=
pevent_find_function
(
pevent
,
pfunction
);
index
=
add_and_get_index
(
parent
,
func
,
record
->
cpu
);
if
(
parent
&&
ftrace_indent
->
set
)
index
=
add_and_get_index
(
parent
,
func
,
record
->
cpu
);
trace_seq_printf
(
s
,
"%*s"
,
index
*
3
,
""
);
...
...
@@ -128,11 +152,13 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record,
else
trace_seq_printf
(
s
,
"0x%llx"
,
function
);
trace_seq_printf
(
s
,
" <-- "
);
if
(
parent
)
trace_seq_printf
(
s
,
"%s"
,
parent
);
else
trace_seq_printf
(
s
,
"0x%llx"
,
pfunction
);
if
(
ftrace_parent
->
set
)
{
trace_seq_printf
(
s
,
" <-- "
);
if
(
parent
)
trace_seq_printf
(
s
,
"%s"
,
parent
);
else
trace_seq_printf
(
s
,
"0x%llx"
,
pfunction
);
}
return
0
;
}
...
...
@@ -141,6 +167,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_event_handler
(
pevent
,
-
1
,
"ftrace"
,
"function"
,
function_handler
,
NULL
);
traceevent_plugin_add_options
(
"ftrace"
,
plugin_options
);
return
0
;
}
...
...
@@ -157,6 +186,8 @@ void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
free
(
fstack
[
i
].
stack
);
}
traceevent_plugin_remove_options
(
plugin_options
);
free
(
fstack
);
fstack
=
NULL
;
cpus
=
-
1
;
...
...
tools/perf/Documentation/perf-report.txt
View file @
cf230918
...
...
@@ -117,6 +117,22 @@ OPTIONS
By default, every sort keys not specified in -F will be appended
automatically.
If --mem-mode option is used, following sort keys are also available
(incompatible with --branch-stack):
symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline.
- symbol_daddr: name of data symbol being executed on at the time of sample
- dso_daddr: name of library or module containing the data being executed
on at the time of sample
- locked: whether the bus was locked at the time of sample
- tlb: type of tlb access for the data at the time of sample
- mem: type of memory access for the data at the time of sample
- snoop: type of snoop (if any) for the data at the time of sample
- dcacheline: the cacheline the data address is on at the time of sample
And default sort keys are changed to local_weight, mem, sym, dso,
symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'.
-p::
--parent=<regex>::
A regex filter to identify parent. The parent is a caller of this
...
...
@@ -260,6 +276,13 @@ OPTIONS
Demangle symbol names to human readable form. It's enabled by default,
disable with --no-demangle.
--mem-mode::
Use the data addresses of samples in addition to instruction addresses
to build the histograms. To generate meaningful output, the perf.data
file must have been obtained using perf record -d -W and using a
special event -e cpu/mem-loads/ or -e cpu/mem-stores/. See
'perf mem' for simpler access.
--percent-limit::
Do not show entries which have an overhead under that percent.
(Default: 0).
...
...
tools/perf/Documentation/perf-timechart.txt
View file @
cf230918
...
...
@@ -43,27 +43,6 @@ TIMECHART OPTIONS
--symfs=<directory>::
Look for files with symbols relative to this directory.
EXAMPLES
--------
$ perf timechart record git pull
[ perf record: Woken up 13 times to write data ]
[ perf record: Captured and wrote 4.253 MB perf.data (~185801 samples) ]
$ perf timechart
Written 10.2 seconds of trace to output.svg.
Record system-wide timechart:
$ perf timechart record
then generate timechart and highlight 'gcc' tasks:
$ perf timechart --highlight gcc
-n::
--proc-num::
Print task info for at least given number of tasks.
...
...
@@ -88,6 +67,26 @@ RECORD OPTIONS
--callchain::
Do call-graph (stack chain/backtrace) recording
EXAMPLES
--------
$ perf timechart record git pull
[ perf record: Woken up 13 times to write data ]
[ perf record: Captured and wrote 4.253 MB perf.data (~185801 samples) ]
$ perf timechart
Written 10.2 seconds of trace to output.svg.
Record system-wide timechart:
$ perf timechart record
then generate timechart and highlight 'gcc' tasks:
$ perf timechart --highlight gcc
SEE ALSO
--------
linkperf:perf-record[1]
tools/perf/Makefile.perf
View file @
cf230918
...
...
@@ -819,15 +819,15 @@ TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
TAG_FILES
=
../../include/uapi/linux/perf_event.h
TAGS
:
$(
RM)
TAGS
$(
QUIET_GEN)$(RM)
TAGS
;
\
$(FIND)
$(TAG_FOLDERS)
-name
'*.[hcS]'
-print
| xargs etags
-a
$(TAG_FILES)
tags
:
$(
RM)
tags
$(
QUIET_GEN)$(RM)
tags
;
\
$(FIND)
$(TAG_FOLDERS)
-name
'*.[hcS]'
-print
| xargs ctags
-a
$(TAG_FILES)
cscope
:
$(
RM)
cscope
*
$(
QUIET_GEN)$(RM)
cscope
*
;
\
$(FIND)
$(TAG_FOLDERS)
-name
'*.[hcS]'
-print
| xargs cscope
-b
$(TAG_FILES)
### Detect prefix changes
...
...
tools/perf/builtin-inject.c
View file @
cf230918
...
...
@@ -72,7 +72,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool,
if
(
ret
)
return
ret
;
if
(
&
inject
->
output
.
is_pipe
)
if
(
!
inject
->
output
.
is_pipe
)
return
0
;
return
perf_event__repipe_synth
(
tool
,
event
);
...
...
tools/perf/builtin-probe.c
View file @
cf230918
...
...
@@ -288,6 +288,13 @@ static void cleanup_params(void)
memset
(
&
params
,
0
,
sizeof
(
params
));
}
static
void
pr_err_with_code
(
const
char
*
msg
,
int
err
)
{
pr_err
(
"%s"
,
msg
);
pr_debug
(
" Reason: %s (Code: %d)"
,
strerror
(
-
err
),
err
);
pr_err
(
"
\n
"
);
}
static
int
__cmd_probe
(
int
argc
,
const
char
**
argv
,
const
char
*
prefix
__maybe_unused
)
{
...
...
@@ -379,7 +386,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
}
ret
=
parse_probe_event_argv
(
argc
,
argv
);
if
(
ret
<
0
)
{
pr_err
(
" Error: Parse Error. (%d)
\n
"
,
ret
);
pr_err
_with_code
(
" Error: Command Parse Error.
"
,
ret
);
return
ret
;
}
}
...
...
@@ -419,8 +426,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
}
ret
=
show_perf_probe_events
();
if
(
ret
<
0
)
pr_err
(
" Error: Failed to show event list. (%d)
\n
"
,
ret
);
pr_err_with_code
(
" Error: Failed to show event list."
,
ret
);
return
ret
;
}
if
(
params
.
show_funcs
)
{
...
...
@@ -445,8 +451,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
strfilter__delete
(
params
.
filter
);
params
.
filter
=
NULL
;
if
(
ret
<
0
)
pr_err
(
" Error: Failed to show functions."
" (%d)
\n
"
,
ret
);
pr_err_with_code
(
" Error: Failed to show functions."
,
ret
);
return
ret
;
}
...
...
@@ -464,7 +469,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
ret
=
show_line_range
(
&
params
.
line_range
,
params
.
target
);
if
(
ret
<
0
)
pr_err
(
" Error: Failed to show lines. (%d)
\n
"
,
ret
);
pr_err
_with_code
(
" Error: Failed to show lines.
"
,
ret
);
return
ret
;
}
if
(
params
.
show_vars
)
{
...
...
@@ -485,7 +490,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
strfilter__delete
(
params
.
filter
);
params
.
filter
=
NULL
;
if
(
ret
<
0
)
pr_err
(
" Error: Failed to show vars. (%d)
\n
"
,
ret
);
pr_err
_with_code
(
" Error: Failed to show vars.
"
,
ret
);
return
ret
;
}
#endif
...
...
@@ -493,7 +498,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
if
(
params
.
dellist
)
{
ret
=
del_perf_probe_events
(
params
.
dellist
);
if
(
ret
<
0
)
{
pr_err
(
" Error: Failed to delete events. (%d)
\n
"
,
ret
);
pr_err
_with_code
(
" Error: Failed to delete events.
"
,
ret
);
return
ret
;
}
}
...
...
@@ -504,7 +509,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
params
.
target
,
params
.
force_add
);
if
(
ret
<
0
)
{
pr_err
(
" Error: Failed to add events. (%d)
\n
"
,
ret
);
pr_err
_with_code
(
" Error: Failed to add events.
"
,
ret
);
return
ret
;
}
}
...
...
tools/perf/config/Makefile
View file @
cf230918
...
...
@@ -299,7 +299,11 @@ else
NO_LIBUNWIND
:=
1
NO_LIBDW_DWARF_UNWIND
:=
1
else
msg
:=
$(
error
No gnu/libc-version.h found, please
install
glibc-dev[el]/glibc-static
)
;
ifneq
($(filter s% -static%,$(LDFLAGS),),)
msg
:=
$(
error
No static glibc found, please
install
glibc-static
)
;
else
msg
:=
$(
error
No gnu/libc-version.h found, please
install
glibc-dev[el]
)
;
endif
endif
else
ifndef
NO_LIBDW_DWARF_UNWIND
...
...
tools/perf/perf.c
View file @
cf230918
...
...
@@ -458,6 +458,7 @@ int main(int argc, const char **argv)
/* The page_size is placed in util object. */
page_size
=
sysconf
(
_SC_PAGE_SIZE
);
cacheline_size
=
sysconf
(
_SC_LEVEL1_DCACHE_LINESIZE
);
cmd
=
perf_extract_argv0_path
(
argv
[
0
]);
if
(
!
cmd
)
...
...
tools/perf/tests/builtin-test.c
View file @
cf230918
...
...
@@ -3,6 +3,8 @@
*
* Builtin regression testing command: ever growing number of sanity tests
*/
#include <unistd.h>
#include <string.h>
#include "builtin.h"
#include "intlist.h"
#include "tests.h"
...
...
@@ -50,9 +52,17 @@ static struct test {
.
func
=
test__pmu
,
},
{
.
desc
=
"Test dso data
interface
"
,
.
desc
=
"Test dso data
read
"
,
.
func
=
test__dso_data
,
},
{
.
desc
=
"Test dso data cache"
,
.
func
=
test__dso_data_cache
,
},
{
.
desc
=
"Test dso data reopen"
,
.
func
=
test__dso_data_reopen
,
},
{
.
desc
=
"roundtrip evsel->name check"
,
.
func
=
test__perf_evsel__roundtrip_name_test
,
...
...
@@ -172,6 +182,34 @@ static bool perf_test__matches(int curr, int argc, const char *argv[])
return
false
;
}
static
int
run_test
(
struct
test
*
test
)
{
int
status
,
err
=
-
1
,
child
=
fork
();
if
(
child
<
0
)
{
pr_err
(
"failed to fork test: %s
\n
"
,
strerror
(
errno
));
return
-
1
;
}
if
(
!
child
)
{
pr_debug
(
"test child forked, pid %d
\n
"
,
getpid
());
err
=
test
->
func
();
exit
(
err
);
}
wait
(
&
status
);
if
(
WIFEXITED
(
status
))
{
err
=
WEXITSTATUS
(
status
);
pr_debug
(
"test child finished with %d
\n
"
,
err
);
}
else
if
(
WIFSIGNALED
(
status
))
{
err
=
-
1
;
pr_debug
(
"test child interrupted
\n
"
);
}
return
err
;
}
static
int
__cmd_test
(
int
argc
,
const
char
*
argv
[],
struct
intlist
*
skiplist
)
{
int
i
=
0
;
...
...
@@ -200,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
}
pr_debug
(
"
\n
--- start ---
\n
"
);
err
=
tests
[
curr
].
func
(
);
err
=
run_test
(
&
tests
[
curr
]
);
pr_debug
(
"---- end ----
\n
%s:"
,
tests
[
curr
].
desc
);
switch
(
err
)
{
...
...
tools/perf/tests/dso-data.c
View file @
cf230918
#include "util.h"
#include <stdlib.h>
#include <linux/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <api/fs/fs.h>
#include "util.h"
#include "machine.h"
#include "symbol.h"
#include "tests.h"
static
char
*
test_file
(
int
size
)
{
static
char
buf_templ
[]
=
"/tmp/test-XXXXXX"
;
#define TEMPL "/tmp/perf-test-XXXXXX"
static
char
buf_templ
[
sizeof
(
TEMPL
)];
char
*
templ
=
buf_templ
;
int
fd
,
i
;
unsigned
char
*
buf
;
strcpy
(
buf_templ
,
TEMPL
);
#undef TEMPL
fd
=
mkstemp
(
templ
);
if
(
fd
<
0
)
{
perror
(
"mkstemp failed"
);
...
...
@@ -150,3 +155,204 @@ int test__dso_data(void)
unlink
(
file
);
return
0
;
}
static
long
open_files_cnt
(
void
)
{
char
path
[
PATH_MAX
];
struct
dirent
*
dent
;
DIR
*
dir
;
long
nr
=
0
;
scnprintf
(
path
,
PATH_MAX
,
"%s/self/fd"
,
procfs__mountpoint
());
pr_debug
(
"fd path: %s
\n
"
,
path
);
dir
=
opendir
(
path
);
TEST_ASSERT_VAL
(
"failed to open fd directory"
,
dir
);
while
((
dent
=
readdir
(
dir
))
!=
NULL
)
{
if
(
!
strcmp
(
dent
->
d_name
,
"."
)
||
!
strcmp
(
dent
->
d_name
,
".."
))
continue
;
nr
++
;
}
closedir
(
dir
);
return
nr
-
1
;
}
static
struct
dso
**
dsos
;
static
int
dsos__create
(
int
cnt
,
int
size
)
{
int
i
;
dsos
=
malloc
(
sizeof
(
dsos
)
*
cnt
);
TEST_ASSERT_VAL
(
"failed to alloc dsos array"
,
dsos
);
for
(
i
=
0
;
i
<
cnt
;
i
++
)
{
char
*
file
;
file
=
test_file
(
size
);
TEST_ASSERT_VAL
(
"failed to get dso file"
,
file
);
dsos
[
i
]
=
dso__new
(
file
);
TEST_ASSERT_VAL
(
"failed to get dso"
,
dsos
[
i
]);
}
return
0
;
}
static
void
dsos__delete
(
int
cnt
)
{
int
i
;
for
(
i
=
0
;
i
<
cnt
;
i
++
)
{
struct
dso
*
dso
=
dsos
[
i
];
unlink
(
dso
->
name
);
dso__delete
(
dso
);
}
free
(
dsos
);
}
static
int
set_fd_limit
(
int
n
)
{
struct
rlimit
rlim
;
if
(
getrlimit
(
RLIMIT_NOFILE
,
&
rlim
))
return
-
1
;
pr_debug
(
"file limit %ld, new %d
\n
"
,
(
long
)
rlim
.
rlim_cur
,
n
);
rlim
.
rlim_cur
=
n
;
return
setrlimit
(
RLIMIT_NOFILE
,
&
rlim
);
}
int
test__dso_data_cache
(
void
)
{
struct
machine
machine
;
long
nr_end
,
nr
=
open_files_cnt
();
int
dso_cnt
,
limit
,
i
,
fd
;
memset
(
&
machine
,
0
,
sizeof
(
machine
));
/* set as system limit */
limit
=
nr
*
4
;
TEST_ASSERT_VAL
(
"failed to set file limit"
,
!
set_fd_limit
(
limit
));
/* and this is now our dso open FDs limit + 1 extra */
dso_cnt
=
limit
/
2
+
1
;
TEST_ASSERT_VAL
(
"failed to create dsos
\n
"
,
!
dsos__create
(
dso_cnt
,
TEST_FILE_SIZE
));
for
(
i
=
0
;
i
<
(
dso_cnt
-
1
);
i
++
)
{
struct
dso
*
dso
=
dsos
[
i
];
/*
* Open dsos via dso__data_fd or dso__data_read_offset.
* Both opens the data file and keep it open.
*/
if
(
i
%
2
)
{
fd
=
dso__data_fd
(
dso
,
&
machine
);
TEST_ASSERT_VAL
(
"failed to get fd"
,
fd
>
0
);
}
else
{
#define BUFSIZE 10
u8
buf
[
BUFSIZE
];
ssize_t
n
;
n
=
dso__data_read_offset
(
dso
,
&
machine
,
0
,
buf
,
BUFSIZE
);
TEST_ASSERT_VAL
(
"failed to read dso"
,
n
==
BUFSIZE
);
}
}
/* open +1 dso over the allowed limit */
fd
=
dso__data_fd
(
dsos
[
i
],
&
machine
);
TEST_ASSERT_VAL
(
"failed to get fd"
,
fd
>
0
);
/* should force the first one to be closed */
TEST_ASSERT_VAL
(
"failed to close dsos[0]"
,
dsos
[
0
]
->
data
.
fd
==
-
1
);
/* cleanup everything */
dsos__delete
(
dso_cnt
);
/* Make sure we did not leak any file descriptor. */
nr_end
=
open_files_cnt
();
pr_debug
(
"nr start %ld, nr stop %ld
\n
"
,
nr
,
nr_end
);
TEST_ASSERT_VAL
(
"failed leadking files"
,
nr
==
nr_end
);
return
0
;
}
int
test__dso_data_reopen
(
void
)
{
struct
machine
machine
;
long
nr_end
,
nr
=
open_files_cnt
();
int
fd
,
fd_extra
;
#define dso_0 (dsos[0])
#define dso_1 (dsos[1])
#define dso_2 (dsos[2])
memset
(
&
machine
,
0
,
sizeof
(
machine
));
/*
* Test scenario:
* - create 3 dso objects
* - set process file descriptor limit to current
* files count + 3
* - test that the first dso gets closed when we
* reach the files count limit
*/
/* Make sure we are able to open 3 fds anyway */
TEST_ASSERT_VAL
(
"failed to set file limit"
,
!
set_fd_limit
((
nr
+
3
)));
TEST_ASSERT_VAL
(
"failed to create dsos
\n
"
,
!
dsos__create
(
3
,
TEST_FILE_SIZE
));
/* open dso_0 */
fd
=
dso__data_fd
(
dso_0
,
&
machine
);
TEST_ASSERT_VAL
(
"failed to get fd"
,
fd
>
0
);
/* open dso_1 */
fd
=
dso__data_fd
(
dso_1
,
&
machine
);
TEST_ASSERT_VAL
(
"failed to get fd"
,
fd
>
0
);
/*
* open extra file descriptor and we just
* reached the files count limit
*/
fd_extra
=
open
(
"/dev/null"
,
O_RDONLY
);
TEST_ASSERT_VAL
(
"failed to open extra fd"
,
fd_extra
>
0
);
/* open dso_2 */
fd
=
dso__data_fd
(
dso_2
,
&
machine
);
TEST_ASSERT_VAL
(
"failed to get fd"
,
fd
>
0
);
/*
* dso_0 should get closed, because we reached
* the file descriptor limit
*/
TEST_ASSERT_VAL
(
"failed to close dso_0"
,
dso_0
->
data
.
fd
==
-
1
);
/* open dso_0 */
fd
=
dso__data_fd
(
dso_0
,
&
machine
);
TEST_ASSERT_VAL
(
"failed to get fd"
,
fd
>
0
);
/*
* dso_1 should get closed, because we reached
* the file descriptor limit
*/
TEST_ASSERT_VAL
(
"failed to close dso_1"
,
dso_1
->
data
.
fd
==
-
1
);
/* cleanup everything */
close
(
fd_extra
);
dsos__delete
(
3
);
/* Make sure we did not leak any file descriptor. */
nr_end
=
open_files_cnt
();
pr_debug
(
"nr start %ld, nr stop %ld
\n
"
,
nr
,
nr_end
);
TEST_ASSERT_VAL
(
"failed leadking files"
,
nr
==
nr_end
);
return
0
;
}
tools/perf/tests/dwarf-unwind.c
View file @
cf230918
...
...
@@ -15,7 +15,7 @@ static int mmap_handler(struct perf_tool *tool __maybe_unused,
struct
perf_sample
*
sample
__maybe_unused
,
struct
machine
*
machine
)
{
return
machine__process_mmap_event
(
machine
,
event
,
NULL
);
return
machine__process_mmap
2
_event
(
machine
,
event
,
NULL
);
}
static
int
init_live_machine
(
struct
machine
*
machine
)
...
...
tools/perf/tests/make
View file @
cf230918
...
...
@@ -205,8 +205,7 @@ $(run):
( eval $$cmd ) >> $@ 2>&1; \
echo " test: $(call test,$@)" >> $@ 2>&1; \
$(call test,$@) && \
rm -f $@ \
rm -rf $$TMP_DEST
rm -rf $@ $$TMP_DEST || (cat $@ ; false)
$(run_O):
$(call clean)
...
...
@@ -217,9 +216,7 @@ $(run_O):
( eval $$cmd ) >> $@ 2>&1 && \
echo " test: $(call test_O,$@)" >> $@ 2>&1; \
$(call test_O,$@) && \
rm -f $@ && \
rm -rf $$TMP_O \
rm -rf $$TMP_DEST
rm -rf $@ $$TMP_O $$TMP_DEST || (cat $@ ; false)
tarpkg:
@cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \
...
...
tools/perf/tests/tests.h
View file @
cf230918
...
...
@@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void);
int
test__pmu
(
void
);
int
test__attr
(
void
);
int
test__dso_data
(
void
);
int
test__dso_data_cache
(
void
);
int
test__dso_data_reopen
(
void
);
int
test__parse_events
(
void
);
int
test__hists_link
(
void
);
int
test__python_use
(
void
);
...
...
tools/perf/util/dso.c
View file @
cf230918
#include <asm/bug.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "symbol.h"
#include "dso.h"
#include "machine.h"
...
...
@@ -136,7 +139,48 @@ int dso__read_binary_type_filename(const struct dso *dso,
return
ret
;
}
static
int
open_dso
(
struct
dso
*
dso
,
struct
machine
*
machine
)
/*
* Global list of open DSOs and the counter.
*/
static
LIST_HEAD
(
dso__data_open
);
static
long
dso__data_open_cnt
;
static
void
dso__list_add
(
struct
dso
*
dso
)
{
list_add_tail
(
&
dso
->
data
.
open_entry
,
&
dso__data_open
);
dso__data_open_cnt
++
;
}
static
void
dso__list_del
(
struct
dso
*
dso
)
{
list_del
(
&
dso
->
data
.
open_entry
);
WARN_ONCE
(
dso__data_open_cnt
<=
0
,
"DSO data fd counter out of bounds."
);
dso__data_open_cnt
--
;
}
static
void
close_first_dso
(
void
);
static
int
do_open
(
char
*
name
)
{
int
fd
;
do
{
fd
=
open
(
name
,
O_RDONLY
);
if
(
fd
>=
0
)
return
fd
;
pr_debug
(
"dso open failed, mmap: %s
\n
"
,
strerror
(
errno
));
if
(
!
dso__data_open_cnt
||
errno
!=
EMFILE
)
break
;
close_first_dso
();
}
while
(
1
);
return
-
1
;
}
static
int
__open_dso
(
struct
dso
*
dso
,
struct
machine
*
machine
)
{
int
fd
;
char
*
root_dir
=
(
char
*
)
""
;
...
...
@@ -154,11 +198,130 @@ static int open_dso(struct dso *dso, struct machine *machine)
return
-
EINVAL
;
}
fd
=
open
(
name
,
O_RDONLY
);
fd
=
do_open
(
name
);
free
(
name
);
return
fd
;
}
static
void
check_data_close
(
void
);
/**
* dso_close - Open DSO data file
* @dso: dso object
*
* Open @dso's data file descriptor and updates
* list/count of open DSO objects.
*/
static
int
open_dso
(
struct
dso
*
dso
,
struct
machine
*
machine
)
{
int
fd
=
__open_dso
(
dso
,
machine
);
if
(
fd
>
0
)
{
dso__list_add
(
dso
);
/*
* Check if we crossed the allowed number
* of opened DSOs and close one if needed.
*/
check_data_close
();
}
return
fd
;
}
static
void
close_data_fd
(
struct
dso
*
dso
)
{
if
(
dso
->
data
.
fd
>=
0
)
{
close
(
dso
->
data
.
fd
);
dso
->
data
.
fd
=
-
1
;
dso
->
data
.
file_size
=
0
;
dso__list_del
(
dso
);
}
}
/**
* dso_close - Close DSO data file
* @dso: dso object
*
* Close @dso's data file descriptor and updates
* list/count of open DSO objects.
*/
static
void
close_dso
(
struct
dso
*
dso
)
{
close_data_fd
(
dso
);
}
static
void
close_first_dso
(
void
)
{
struct
dso
*
dso
;
dso
=
list_first_entry
(
&
dso__data_open
,
struct
dso
,
data
.
open_entry
);
close_dso
(
dso
);
}
static
rlim_t
get_fd_limit
(
void
)
{
struct
rlimit
l
;
rlim_t
limit
=
0
;
/* Allow half of the current open fd limit. */
if
(
getrlimit
(
RLIMIT_NOFILE
,
&
l
)
==
0
)
{
if
(
l
.
rlim_cur
==
RLIM_INFINITY
)
limit
=
l
.
rlim_cur
;
else
limit
=
l
.
rlim_cur
/
2
;
}
else
{
pr_err
(
"failed to get fd limit
\n
"
);
limit
=
1
;
}
return
limit
;
}
static
bool
may_cache_fd
(
void
)
{
static
rlim_t
limit
;
if
(
!
limit
)
limit
=
get_fd_limit
();
if
(
limit
==
RLIM_INFINITY
)
return
true
;
return
limit
>
(
rlim_t
)
dso__data_open_cnt
;
}
/*
* Check and close LRU dso if we crossed allowed limit
* for opened dso file descriptors. The limit is half
* of the RLIMIT_NOFILE files opened.
*/
static
void
check_data_close
(
void
)
{
bool
cache_fd
=
may_cache_fd
();
if
(
!
cache_fd
)
close_first_dso
();
}
/**
* dso__data_close - Close DSO data file
* @dso: dso object
*
* External interface to close @dso's data file descriptor.
*/
void
dso__data_close
(
struct
dso
*
dso
)
{
close_dso
(
dso
);
}
/**
* dso__data_fd - Get dso's data file descriptor
* @dso: dso object
* @machine: machine object
*
* External interface to find dso's file, open it and
* returns file descriptor.
*/
int
dso__data_fd
(
struct
dso
*
dso
,
struct
machine
*
machine
)
{
enum
dso_binary_type
binary_type_data
[]
=
{
...
...
@@ -168,8 +331,13 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
};
int
i
=
0
;
if
(
dso
->
binary_type
!=
DSO_BINARY_TYPE__NOT_FOUND
)
return
open_dso
(
dso
,
machine
);
if
(
dso
->
data
.
fd
>=
0
)
return
dso
->
data
.
fd
;
if
(
dso
->
binary_type
!=
DSO_BINARY_TYPE__NOT_FOUND
)
{
dso
->
data
.
fd
=
open_dso
(
dso
,
machine
);
return
dso
->
data
.
fd
;
}
do
{
int
fd
;
...
...
@@ -178,7 +346,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
fd
=
open_dso
(
dso
,
machine
);
if
(
fd
>=
0
)
return
fd
;
return
dso
->
data
.
fd
=
fd
;
}
while
(
dso
->
binary_type
!=
DSO_BINARY_TYPE__NOT_FOUND
);
...
...
@@ -260,16 +428,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset,
}
static
ssize_t
dso_cache__read
(
struct
dso
*
dso
,
struct
machine
*
machine
,
u64
offset
,
u8
*
data
,
ssize_t
size
)
dso_cache__read
(
struct
dso
*
dso
,
u64
offset
,
u8
*
data
,
ssize_t
size
)
{
struct
dso_cache
*
cache
;
ssize_t
ret
;
int
fd
;
fd
=
dso__data_fd
(
dso
,
machine
);
if
(
fd
<
0
)
return
-
1
;
do
{
u64
cache_offset
;
...
...
@@ -283,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine,
cache_offset
=
offset
&
DSO__DATA_CACHE_MASK
;
ret
=
-
EINVAL
;
if
(
-
1
==
lseek
(
fd
,
cache_offset
,
SEEK_SET
))
if
(
-
1
==
lseek
(
dso
->
data
.
fd
,
cache_offset
,
SEEK_SET
))
break
;
ret
=
read
(
fd
,
cache
->
data
,
DSO__DATA_CACHE_SIZE
);
ret
=
read
(
dso
->
data
.
fd
,
cache
->
data
,
DSO__DATA_CACHE_SIZE
);
if
(
ret
<=
0
)
break
;
cache
->
offset
=
cache_offset
;
cache
->
size
=
ret
;
dso_cache__insert
(
&
dso
->
cache
,
cache
);
dso_cache__insert
(
&
dso
->
data
.
cache
,
cache
);
ret
=
dso_cache__memcpy
(
cache
,
offset
,
data
,
size
);
...
...
@@ -301,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine,
if
(
ret
<=
0
)
free
(
cache
);
close
(
fd
);
return
ret
;
}
static
ssize_t
dso_cache_read
(
struct
dso
*
dso
,
struct
machine
*
machine
,
u
64
offset
,
u
8
*
data
,
ssize_t
size
)
static
ssize_t
dso_cache_read
(
struct
dso
*
dso
,
u64
offset
,
u8
*
data
,
ssize_t
size
)
{
struct
dso_cache
*
cache
;
cache
=
dso_cache__find
(
&
dso
->
cache
,
offset
);
cache
=
dso_cache__find
(
&
dso
->
data
.
cache
,
offset
);
if
(
cache
)
return
dso_cache__memcpy
(
cache
,
offset
,
data
,
size
);
else
return
dso_cache__read
(
dso
,
machine
,
offset
,
data
,
size
);
return
dso_cache__read
(
dso
,
offset
,
data
,
size
);
}
ssize_t
dso__data_read_offset
(
struct
dso
*
dso
,
struct
machine
*
machine
,
u64
offset
,
u8
*
data
,
ssize_t
size
)
/*
* Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks
* in the rb_tree. Any read to already cached data is served
* by cached data.
*/
static
ssize_t
cached_read
(
struct
dso
*
dso
,
u64
offset
,
u8
*
data
,
ssize_t
size
)
{
ssize_t
r
=
0
;
u8
*
p
=
data
;
...
...
@@ -326,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
do
{
ssize_t
ret
;
ret
=
dso_cache_read
(
dso
,
machine
,
offset
,
p
,
size
);
ret
=
dso_cache_read
(
dso
,
offset
,
p
,
size
);
if
(
ret
<
0
)
return
ret
;
...
...
@@ -346,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
return
r
;
}
static
int
data_file_size
(
struct
dso
*
dso
)
{
struct
stat
st
;
if
(
!
dso
->
data
.
file_size
)
{
if
(
fstat
(
dso
->
data
.
fd
,
&
st
))
{
pr_err
(
"dso mmap failed, fstat: %s
\n
"
,
strerror
(
errno
));
return
-
1
;
}
dso
->
data
.
file_size
=
st
.
st_size
;
}
return
0
;
}
static
ssize_t
data_read_offset
(
struct
dso
*
dso
,
u64
offset
,
u8
*
data
,
ssize_t
size
)
{
if
(
data_file_size
(
dso
))
return
-
1
;
/* Check the offset sanity. */
if
(
offset
>
dso
->
data
.
file_size
)
return
-
1
;
if
(
offset
+
size
<
offset
)
return
-
1
;
return
cached_read
(
dso
,
offset
,
data
,
size
);
}
/**
* dso__data_read_offset - Read data from dso file offset
* @dso: dso object
* @machine: machine object
* @offset: file offset
* @data: buffer to store data
* @size: size of the @data buffer
*
* External interface to read data from dso file offset. Open
* dso data file and use cached_read to get the data.
*/
ssize_t
dso__data_read_offset
(
struct
dso
*
dso
,
struct
machine
*
machine
,
u64
offset
,
u8
*
data
,
ssize_t
size
)
{
if
(
dso__data_fd
(
dso
,
machine
)
<
0
)
return
-
1
;
return
data_read_offset
(
dso
,
offset
,
data
,
size
);
}
/**
* dso__data_read_addr - Read data from dso address
* @dso: dso object
* @machine: machine object
* @add: virtual memory address
* @data: buffer to store data
* @size: size of the @data buffer
*
* External interface to read data from dso address.
*/
ssize_t
dso__data_read_addr
(
struct
dso
*
dso
,
struct
map
*
map
,
struct
machine
*
machine
,
u64
addr
,
u8
*
data
,
ssize_t
size
)
...
...
@@ -473,7 +699,8 @@ struct dso *dso__new(const char *name)
dso__set_short_name
(
dso
,
dso
->
name
,
false
);
for
(
i
=
0
;
i
<
MAP__NR_TYPES
;
++
i
)
dso
->
symbols
[
i
]
=
dso
->
symbol_names
[
i
]
=
RB_ROOT
;
dso
->
cache
=
RB_ROOT
;
dso
->
data
.
cache
=
RB_ROOT
;
dso
->
data
.
fd
=
-
1
;
dso
->
symtab_type
=
DSO_BINARY_TYPE__NOT_FOUND
;
dso
->
binary_type
=
DSO_BINARY_TYPE__NOT_FOUND
;
dso
->
loaded
=
0
;
...
...
@@ -485,6 +712,7 @@ struct dso *dso__new(const char *name)
dso
->
kernel
=
DSO_TYPE_USER
;
dso
->
needs_swap
=
DSO_SWAP__UNSET
;
INIT_LIST_HEAD
(
&
dso
->
node
);
INIT_LIST_HEAD
(
&
dso
->
data
.
open_entry
);
}
return
dso
;
...
...
@@ -506,7 +734,8 @@ void dso__delete(struct dso *dso)
dso
->
long_name_allocated
=
false
;
}
dso_cache__free
(
&
dso
->
cache
);
dso__data_close
(
dso
);
dso_cache__free
(
&
dso
->
data
.
cache
);
dso__free_a2l
(
dso
);
zfree
(
&
dso
->
symsrc_filename
);
free
(
dso
);
...
...
tools/perf/util/dso.h
View file @
cf230918
...
...
@@ -76,7 +76,6 @@ struct dso {
struct
list_head
node
;
struct
rb_root
symbols
[
MAP__NR_TYPES
];
struct
rb_root
symbol_names
[
MAP__NR_TYPES
];
struct
rb_root
cache
;
void
*
a2l
;
char
*
symsrc_filename
;
unsigned
int
a2l_fails
;
...
...
@@ -99,6 +98,15 @@ struct dso {
const
char
*
long_name
;
u16
long_name_len
;
u16
short_name_len
;
/* dso data file */
struct
{
struct
rb_root
cache
;
int
fd
;
size_t
file_size
;
struct
list_head
open_entry
;
}
data
;
char
name
[
0
];
};
...
...
@@ -141,7 +149,47 @@ char dso__symtab_origin(const struct dso *dso);
int
dso__read_binary_type_filename
(
const
struct
dso
*
dso
,
enum
dso_binary_type
type
,
char
*
root_dir
,
char
*
filename
,
size_t
size
);
/*
* The dso__data_* external interface provides following functions:
* dso__data_fd
* dso__data_close
* dso__data_read_offset
* dso__data_read_addr
*
* Please refer to the dso.c object code for each function and
* arguments documentation. Following text tries to explain the
* dso file descriptor caching.
*
* The dso__data* interface allows caching of opened file descriptors
* to speed up the dso data accesses. The idea is to leave the file
* descriptor opened ideally for the whole life of the dso object.
*
* The current usage of the dso__data_* interface is as follows:
*
* Get DSO's fd:
* int fd = dso__data_fd(dso, machine);
* USE 'fd' SOMEHOW
*
* Read DSO's data:
* n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE);
* n = dso__data_read_addr(dso_0, &machine, 0, buf, BUFSIZE);
*
* Eventually close DSO's fd:
* dso__data_close(dso);
*
* It is not necessary to close the DSO object data file. Each time new
* DSO data file is opened, the limit (RLIMIT_NOFILE/2) is checked. Once
* it is crossed, the oldest opened DSO object is closed.
*
* The dso__delete function calls close_dso function to ensure the
* data file descriptor gets closed/unmapped before the dso object
* is freed.
*
* TODO
*/
int
dso__data_fd
(
struct
dso
*
dso
,
struct
machine
*
machine
);
void
dso__data_close
(
struct
dso
*
dso
);
ssize_t
dso__data_read_offset
(
struct
dso
*
dso
,
struct
machine
*
machine
,
u64
offset
,
u8
*
data
,
ssize_t
size
);
ssize_t
dso__data_read_addr
(
struct
dso
*
dso
,
struct
map
*
map
,
...
...
tools/perf/util/event.c
View file @
cf230918
#include <linux/types.h>
#include <sys/mman.h>
#include "event.h"
#include "debug.h"
#include "hist.h"
...
...
@@ -178,13 +179,14 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
return
-
1
;
}
event
->
header
.
type
=
PERF_RECORD_MMAP
;
event
->
header
.
type
=
PERF_RECORD_MMAP
2
;
while
(
1
)
{
char
bf
[
BUFSIZ
];
char
prot
[
5
];
char
execname
[
PATH_MAX
];
char
anonstr
[]
=
"//anon"
;
unsigned
int
ino
;
size_t
size
;
ssize_t
n
;
...
...
@@ -195,15 +197,20 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy
(
execname
,
""
);
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
n
=
sscanf
(
bf
,
"%"
PRIx64
"-%"
PRIx64
" %s %"
PRIx64
" %*x:%*x %*u %s
\n
"
,
&
event
->
mmap
.
start
,
&
event
->
mmap
.
len
,
prot
,
&
event
->
mmap
.
pgoff
,
execname
);
n
=
sscanf
(
bf
,
"%"
PRIx64
"-%"
PRIx64
" %s %"
PRIx64
" %x:%x %u %s
\n
"
,
&
event
->
mmap2
.
start
,
&
event
->
mmap2
.
len
,
prot
,
&
event
->
mmap2
.
pgoff
,
&
event
->
mmap2
.
maj
,
&
event
->
mmap2
.
min
,
&
ino
,
execname
);
/*
* Anon maps don't have the execname.
*/
if
(
n
<
4
)
if
(
n
<
7
)
continue
;
event
->
mmap2
.
ino
=
(
u64
)
ino
;
/*
* Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
*/
...
...
@@ -212,6 +219,21 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
else
event
->
header
.
misc
=
PERF_RECORD_MISC_GUEST_USER
;
/* map protection and flags bits */
event
->
mmap2
.
prot
=
0
;
event
->
mmap2
.
flags
=
0
;
if
(
prot
[
0
]
==
'r'
)
event
->
mmap2
.
prot
|=
PROT_READ
;
if
(
prot
[
1
]
==
'w'
)
event
->
mmap2
.
prot
|=
PROT_WRITE
;
if
(
prot
[
2
]
==
'x'
)
event
->
mmap2
.
prot
|=
PROT_EXEC
;
if
(
prot
[
3
]
==
's'
)
event
->
mmap2
.
flags
|=
MAP_SHARED
;
else
event
->
mmap2
.
flags
|=
MAP_PRIVATE
;
if
(
prot
[
2
]
!=
'x'
)
{
if
(
!
mmap_data
||
prot
[
0
]
!=
'r'
)
continue
;
...
...
@@ -223,15 +245,15 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy
(
execname
,
anonstr
);
size
=
strlen
(
execname
)
+
1
;
memcpy
(
event
->
mmap
.
filename
,
execname
,
size
);
memcpy
(
event
->
mmap
2
.
filename
,
execname
,
size
);
size
=
PERF_ALIGN
(
size
,
sizeof
(
u64
));
event
->
mmap
.
len
-=
event
->
mmap
.
start
;
event
->
mmap
.
header
.
size
=
(
sizeof
(
event
->
mmap
)
-
(
sizeof
(
event
->
mmap
.
filename
)
-
size
));
memset
(
event
->
mmap
.
filename
+
size
,
0
,
machine
->
id_hdr_size
);
event
->
mmap
.
header
.
size
+=
machine
->
id_hdr_size
;
event
->
mmap
.
pid
=
tgid
;
event
->
mmap
.
tid
=
pid
;
event
->
mmap
2
.
len
-=
event
->
mmap
.
start
;
event
->
mmap
2
.
header
.
size
=
(
sizeof
(
event
->
mmap2
)
-
(
sizeof
(
event
->
mmap
2
.
filename
)
-
size
));
memset
(
event
->
mmap
2
.
filename
+
size
,
0
,
machine
->
id_hdr_size
);
event
->
mmap
2
.
header
.
size
+=
machine
->
id_hdr_size
;
event
->
mmap
2
.
pid
=
tgid
;
event
->
mmap
2
.
tid
=
pid
;
if
(
process
(
tool
,
event
,
&
synth_sample
,
machine
)
!=
0
)
{
rc
=
-
1
;
...
...
@@ -612,12 +634,15 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
size_t
perf_event__fprintf_mmap2
(
union
perf_event
*
event
,
FILE
*
fp
)
{
return
fprintf
(
fp
,
" %d/%d: [%#"
PRIx64
"(%#"
PRIx64
") @ %#"
PRIx64
" %02x:%02x %"
PRIu64
" %"
PRIu64
"]: %c %s
\n
"
,
" %02x:%02x %"
PRIu64
" %"
PRIu64
"]: %c
%c%c%c
%s
\n
"
,
event
->
mmap2
.
pid
,
event
->
mmap2
.
tid
,
event
->
mmap2
.
start
,
event
->
mmap2
.
len
,
event
->
mmap2
.
pgoff
,
event
->
mmap2
.
maj
,
event
->
mmap2
.
min
,
event
->
mmap2
.
ino
,
event
->
mmap2
.
ino_generation
,
(
event
->
header
.
misc
&
PERF_RECORD_MISC_MMAP_DATA
)
?
'r'
:
'x'
,
(
event
->
mmap2
.
prot
&
PROT_READ
)
?
'r'
:
'-'
,
(
event
->
mmap2
.
prot
&
PROT_WRITE
)
?
'w'
:
'-'
,
(
event
->
mmap2
.
prot
&
PROT_EXEC
)
?
'x'
:
'-'
,
(
event
->
mmap2
.
flags
&
MAP_SHARED
)
?
's'
:
'p'
,
event
->
mmap2
.
filename
);
}
...
...
tools/perf/util/event.h
View file @
cf230918
...
...
@@ -7,6 +7,7 @@
#include "../perf.h"
#include "map.h"
#include "build-id.h"
#include "perf_regs.h"
struct
mmap_event
{
struct
perf_event_header
header
;
...
...
@@ -27,6 +28,8 @@ struct mmap2_event {
u32
min
;
u64
ino
;
u64
ino_generation
;
u32
prot
;
u32
flags
;
char
filename
[
PATH_MAX
];
};
...
...
@@ -87,6 +90,10 @@ struct regs_dump {
u64
abi
;
u64
mask
;
u64
*
regs
;
/* Cached values/mask filled by first register access. */
u64
cache_regs
[
PERF_REGS_MAX
];
u64
cache_mask
;
};
struct
stack_dump
{
...
...
tools/perf/util/evsel.c
View file @
cf230918
...
...
@@ -589,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
}
/*
* We default some events to
a 1
default interval. But keep
* We default some events to
have a
default interval. But keep
* it a weak assumption overridable by the user.
*/
if
(
!
attr
->
sample_period
||
(
opts
->
user_freq
!=
UINT_MAX
&&
if
(
!
attr
->
sample_period
||
(
opts
->
user_freq
!=
UINT_MAX
||
opts
->
user_interval
!=
ULLONG_MAX
))
{
if
(
opts
->
freq
)
{
perf_evsel__set_sample_bit
(
evsel
,
PERIOD
);
...
...
@@ -659,6 +659,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
perf_evsel__set_sample_bit
(
evsel
,
WEIGHT
);
attr
->
mmap
=
track
;
attr
->
mmap2
=
track
&&
!
perf_missing_features
.
mmap2
;
attr
->
comm
=
track
;
if
(
opts
->
sample_transaction
)
...
...
tools/perf/util/hist.c
View file @
cf230918
...
...
@@ -128,6 +128,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
+
unresolved_col_width
+
2
;
hists__new_col_len
(
hists
,
HISTC_MEM_DADDR_SYMBOL
,
symlen
);
hists__new_col_len
(
hists
,
HISTC_MEM_DCACHELINE
,
symlen
+
1
);
}
else
{
symlen
=
unresolved_col_width
+
4
+
2
;
hists__new_col_len
(
hists
,
HISTC_MEM_DADDR_SYMBOL
,
...
...
@@ -439,9 +441,10 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
.
map
=
al
->
map
,
.
sym
=
al
->
sym
,
},
.
cpu
=
al
->
cpu
,
.
ip
=
al
->
addr
,
.
level
=
al
->
level
,
.
cpu
=
al
->
cpu
,
.
cpumode
=
al
->
cpumode
,
.
ip
=
al
->
addr
,
.
level
=
al
->
level
,
.
stat
=
{
.
nr_events
=
1
,
.
period
=
period
,
...
...
tools/perf/util/hist.h
View file @
cf230918
...
...
@@ -72,6 +72,7 @@ enum hist_column {
HISTC_MEM_TLB
,
HISTC_MEM_LVL
,
HISTC_MEM_SNOOP
,
HISTC_MEM_DCACHELINE
,
HISTC_TRANSACTION
,
HISTC_NR_COLS
,
/* Last entry */
};
...
...
tools/perf/util/machine.c
View file @
cf230918
...
...
@@ -1060,6 +1060,8 @@ int machine__process_mmap2_event(struct machine *machine,
event
->
mmap2
.
pid
,
event
->
mmap2
.
maj
,
event
->
mmap2
.
min
,
event
->
mmap2
.
ino
,
event
->
mmap2
.
ino_generation
,
event
->
mmap2
.
prot
,
event
->
mmap2
.
flags
,
event
->
mmap2
.
filename
,
type
);
if
(
map
==
NULL
)
...
...
@@ -1105,7 +1107,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
map
=
map__new
(
&
machine
->
user_dsos
,
event
->
mmap
.
start
,
event
->
mmap
.
len
,
event
->
mmap
.
pgoff
,
event
->
mmap
.
pid
,
0
,
0
,
0
,
0
,
event
->
mmap
.
pid
,
0
,
0
,
0
,
0
,
0
,
0
,
event
->
mmap
.
filename
,
type
);
...
...
tools/perf/util/map.c
View file @
cf230918
...
...
@@ -138,7 +138,7 @@ void map__init(struct map *map, enum map_type type,
struct
map
*
map__new
(
struct
list_head
*
dsos__list
,
u64
start
,
u64
len
,
u64
pgoff
,
u32
pid
,
u32
d_maj
,
u32
d_min
,
u64
ino
,
u64
ino_gen
,
char
*
filename
,
u64
ino_gen
,
u32
prot
,
u32
flags
,
char
*
filename
,
enum
map_type
type
)
{
struct
map
*
map
=
malloc
(
sizeof
(
*
map
));
...
...
@@ -157,6 +157,8 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
map
->
min
=
d_min
;
map
->
ino
=
ino
;
map
->
ino_generation
=
ino_gen
;
map
->
prot
=
prot
;
map
->
flags
=
flags
;
if
((
anon
||
no_dso
)
&&
type
==
MAP__FUNCTION
)
{
snprintf
(
newfilename
,
sizeof
(
newfilename
),
"/tmp/perf-%d.map"
,
pid
);
...
...
tools/perf/util/map.h
View file @
cf230918
...
...
@@ -35,6 +35,8 @@ struct map {
bool
referenced
;
bool
erange_warned
;
u32
priv
;
u32
prot
;
u32
flags
;
u64
pgoff
;
u64
reloc
;
u32
maj
,
min
;
/* only valid for MMAP2 record */
...
...
@@ -118,7 +120,7 @@ void map__init(struct map *map, enum map_type type,
u64
start
,
u64
end
,
u64
pgoff
,
struct
dso
*
dso
);
struct
map
*
map__new
(
struct
list_head
*
dsos__list
,
u64
start
,
u64
len
,
u64
pgoff
,
u32
pid
,
u32
d_maj
,
u32
d_min
,
u64
ino
,
u64
ino_gen
,
u64
ino_gen
,
u32
prot
,
u32
flags
,
char
*
filename
,
enum
map_type
type
);
struct
map
*
map__new2
(
u64
start
,
struct
dso
*
dso
,
enum
map_type
type
);
void
map__delete
(
struct
map
*
map
);
...
...
tools/perf/util/perf_regs.c
View file @
cf230918
#include <errno.h>
#include "perf_regs.h"
#include "event.h"
int
perf_reg_value
(
u64
*
valp
,
struct
regs_dump
*
regs
,
int
id
)
{
int
i
,
idx
=
0
;
u64
mask
=
regs
->
mask
;
if
(
regs
->
cache_mask
&
(
1
<<
id
))
goto
out
;
if
(
!
(
mask
&
(
1
<<
id
)))
return
-
EINVAL
;
...
...
@@ -14,6 +18,10 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
idx
++
;
}
*
valp
=
regs
->
regs
[
idx
];
regs
->
cache_mask
|=
(
1
<<
id
);
regs
->
cache_regs
[
id
]
=
regs
->
regs
[
idx
];
out:
*
valp
=
regs
->
cache_regs
[
id
];
return
0
;
}
tools/perf/util/perf_regs.h
View file @
cf230918
...
...
@@ -2,7 +2,8 @@
#define __PERF_REGS_H
#include <linux/types.h>
#include "event.h"
struct
regs_dump
;
#ifdef HAVE_PERF_REGS_SUPPORT
#include <perf_regs.h>
...
...
@@ -11,6 +12,7 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
#else
#define PERF_REGS_MASK 0
#define PERF_REGS_MAX 0
static
inline
const
char
*
perf_reg_name
(
int
id
__maybe_unused
)
{
...
...
tools/perf/util/probe-event.c
View file @
cf230918
...
...
@@ -628,11 +628,11 @@ static int __show_line_range(struct line_range *lr, const char *module)
ret
=
debuginfo__find_line_range
(
dinfo
,
lr
);
debuginfo__delete
(
dinfo
);
if
(
ret
==
0
)
{
if
(
ret
==
0
||
ret
==
-
ENOENT
)
{
pr_warning
(
"Specified source line is not found.
\n
"
);
return
-
ENOENT
;
}
else
if
(
ret
<
0
)
{
pr_warning
(
"Debuginfo analysis failed.
(%d)
\n
"
,
ret
);
pr_warning
(
"Debuginfo analysis failed.
\n
"
);
return
ret
;
}
...
...
@@ -641,7 +641,7 @@ static int __show_line_range(struct line_range *lr, const char *module)
ret
=
get_real_path
(
tmp
,
lr
->
comp_dir
,
&
lr
->
path
);
free
(
tmp
);
/* Free old path */
if
(
ret
<
0
)
{
pr_warning
(
"Failed to find source file
. (%d)
\n
"
,
ret
);
pr_warning
(
"Failed to find source file
path.
\n
"
);
return
ret
;
}
...
...
@@ -721,9 +721,14 @@ static int show_available_vars_at(struct debuginfo *dinfo,
ret
=
debuginfo__find_available_vars_at
(
dinfo
,
pev
,
&
vls
,
max_vls
,
externs
);
if
(
ret
<=
0
)
{
pr_err
(
"Failed to find variables at %s (%d)
\n
"
,
buf
,
ret
);
if
(
ret
==
0
||
ret
==
-
ENOENT
)
{
pr_err
(
"Failed to find the address of %s
\n
"
,
buf
);
ret
=
-
ENOENT
;
}
else
pr_warning
(
"Debuginfo analysis failed.
\n
"
);
goto
end
;
}
/* Some variables are found */
fprintf
(
stdout
,
"Available variables at %s
\n
"
,
buf
);
for
(
i
=
0
;
i
<
ret
;
i
++
)
{
...
...
tools/perf/util/probe-finder.c
View file @
cf230918
...
...
@@ -573,14 +573,13 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
if
(
!
die_find_variable_at
(
sc_die
,
pf
->
pvar
->
var
,
pf
->
addr
,
&
vr_die
))
{
/* Search again in global variables */
if
(
!
die_find_variable_at
(
&
pf
->
cu_die
,
pf
->
pvar
->
var
,
0
,
&
vr_die
))
pr_warning
(
"Failed to find '%s' in this function.
\n
"
,
pf
->
pvar
->
var
);
ret
=
-
ENOENT
;
}
if
(
ret
>=
0
)
ret
=
convert_variable
(
&
vr_die
,
pf
);
if
(
ret
<
0
)
pr_warning
(
"Failed to find '%s' in this function.
\n
"
,
pf
->
pvar
->
var
);
return
ret
;
}
...
...
@@ -1281,7 +1280,11 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
return
ret
;
}
/* Find available variables at given probe point */
/*
* Find available variables at given probe point
* Return the number of found probe points. Return 0 if there is no
* matched probe point. Return <0 if an error occurs.
*/
int
debuginfo__find_available_vars_at
(
struct
debuginfo
*
dbg
,
struct
perf_probe_event
*
pev
,
struct
variable_list
**
vls
,
...
...
tools/perf/util/scripting-engines/trace-event-perl.c
View file @
cf230918
...
...
@@ -215,6 +215,7 @@ static void define_event_symbols(struct event_format *event,
case
PRINT_BSTRING
:
case
PRINT_DYNAMIC_ARRAY
:
case
PRINT_STRING
:
case
PRINT_BITMASK
:
break
;
case
PRINT_TYPE
:
define_event_symbols
(
event
,
ev_name
,
args
->
typecast
.
item
);
...
...
tools/perf/util/scripting-engines/trace-event-python.c
View file @
cf230918
...
...
@@ -197,6 +197,7 @@ static void define_event_symbols(struct event_format *event,
case
PRINT_BSTRING
:
case
PRINT_DYNAMIC_ARRAY
:
case
PRINT_FUNC
:
case
PRINT_BITMASK
:
/* we should warn... */
return
;
}
...
...
@@ -622,6 +623,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)
fprintf
(
ofp
,
"%s="
,
f
->
name
);
if
(
f
->
flags
&
FIELD_IS_STRING
||
f
->
flags
&
FIELD_IS_FLAG
||
f
->
flags
&
FIELD_IS_ARRAY
||
f
->
flags
&
FIELD_IS_SYMBOLIC
)
fprintf
(
ofp
,
"%%s"
);
else
if
(
f
->
flags
&
FIELD_IS_SIGNED
)
...
...
tools/perf/util/sort.c
View file @
cf230918
#include <sys/mman.h>
#include "sort.h"
#include "hist.h"
#include "comm.h"
...
...
@@ -784,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
return
repsep_snprintf
(
bf
,
size
,
"%-*s"
,
width
,
out
);
}
static
inline
u64
cl_address
(
u64
address
)
{
/* return the cacheline of the address */
return
(
address
&
~
(
cacheline_size
-
1
));
}
static
int64_t
sort__dcacheline_cmp
(
struct
hist_entry
*
left
,
struct
hist_entry
*
right
)
{
u64
l
,
r
;
struct
map
*
l_map
,
*
r_map
;
if
(
!
left
->
mem_info
)
return
-
1
;
if
(
!
right
->
mem_info
)
return
1
;
/* group event types together */
if
(
left
->
cpumode
>
right
->
cpumode
)
return
-
1
;
if
(
left
->
cpumode
<
right
->
cpumode
)
return
1
;
l_map
=
left
->
mem_info
->
daddr
.
map
;
r_map
=
right
->
mem_info
->
daddr
.
map
;
/* if both are NULL, jump to sort on al_addr instead */
if
(
!
l_map
&&
!
r_map
)
goto
addr
;
if
(
!
l_map
)
return
-
1
;
if
(
!
r_map
)
return
1
;
if
(
l_map
->
maj
>
r_map
->
maj
)
return
-
1
;
if
(
l_map
->
maj
<
r_map
->
maj
)
return
1
;
if
(
l_map
->
min
>
r_map
->
min
)
return
-
1
;
if
(
l_map
->
min
<
r_map
->
min
)
return
1
;
if
(
l_map
->
ino
>
r_map
->
ino
)
return
-
1
;
if
(
l_map
->
ino
<
r_map
->
ino
)
return
1
;
if
(
l_map
->
ino_generation
>
r_map
->
ino_generation
)
return
-
1
;
if
(
l_map
->
ino_generation
<
r_map
->
ino_generation
)
return
1
;
/*
* Addresses with no major/minor numbers are assumed to be
* anonymous in userspace. Sort those on pid then address.
*
* The kernel and non-zero major/minor mapped areas are
* assumed to be unity mapped. Sort those on address.
*/
if
((
left
->
cpumode
!=
PERF_RECORD_MISC_KERNEL
)
&&
(
!
(
l_map
->
flags
&
MAP_SHARED
))
&&
!
l_map
->
maj
&&
!
l_map
->
min
&&
!
l_map
->
ino
&&
!
l_map
->
ino_generation
)
{
/* userspace anonymous */
if
(
left
->
thread
->
pid_
>
right
->
thread
->
pid_
)
return
-
1
;
if
(
left
->
thread
->
pid_
<
right
->
thread
->
pid_
)
return
1
;
}
addr:
/* al_addr does all the right addr - start + offset calculations */
l
=
cl_address
(
left
->
mem_info
->
daddr
.
al_addr
);
r
=
cl_address
(
right
->
mem_info
->
daddr
.
al_addr
);
if
(
l
>
r
)
return
-
1
;
if
(
l
<
r
)
return
1
;
return
0
;
}
static
int
hist_entry__dcacheline_snprintf
(
struct
hist_entry
*
he
,
char
*
bf
,
size_t
size
,
unsigned
int
width
)
{
uint64_t
addr
=
0
;
struct
map
*
map
=
NULL
;
struct
symbol
*
sym
=
NULL
;
char
level
=
he
->
level
;
if
(
he
->
mem_info
)
{
addr
=
cl_address
(
he
->
mem_info
->
daddr
.
al_addr
);
map
=
he
->
mem_info
->
daddr
.
map
;
sym
=
he
->
mem_info
->
daddr
.
sym
;
/* print [s] for shared data mmaps */
if
((
he
->
cpumode
!=
PERF_RECORD_MISC_KERNEL
)
&&
map
&&
(
map
->
type
==
MAP__VARIABLE
)
&&
(
map
->
flags
&
MAP_SHARED
)
&&
(
map
->
maj
||
map
->
min
||
map
->
ino
||
map
->
ino_generation
))
level
=
's'
;
else
if
(
!
map
)
level
=
'X'
;
}
return
_hist_entry__sym_snprintf
(
map
,
sym
,
addr
,
level
,
bf
,
size
,
width
);
}
struct
sort_entry
sort_mispredict
=
{
.
se_header
=
"Branch Mispredicted"
,
.
se_cmp
=
sort__mispredict_cmp
,
...
...
@@ -876,6 +975,13 @@ struct sort_entry sort_mem_snoop = {
.
se_width_idx
=
HISTC_MEM_SNOOP
,
};
struct
sort_entry
sort_mem_dcacheline
=
{
.
se_header
=
"Data Cacheline"
,
.
se_cmp
=
sort__dcacheline_cmp
,
.
se_snprintf
=
hist_entry__dcacheline_snprintf
,
.
se_width_idx
=
HISTC_MEM_DCACHELINE
,
};
static
int64_t
sort__abort_cmp
(
struct
hist_entry
*
left
,
struct
hist_entry
*
right
)
{
...
...
@@ -1043,6 +1149,7 @@ static struct sort_dimension memory_sort_dimensions[] = {
DIM
(
SORT_MEM_TLB
,
"tlb"
,
sort_mem_tlb
),
DIM
(
SORT_MEM_LVL
,
"mem"
,
sort_mem_lvl
),
DIM
(
SORT_MEM_SNOOP
,
"snoop"
,
sort_mem_snoop
),
DIM
(
SORT_MEM_DCACHELINE
,
"dcacheline"
,
sort_mem_dcacheline
),
};
#undef DIM
...
...
tools/perf/util/sort.h
View file @
cf230918
...
...
@@ -89,6 +89,7 @@ struct hist_entry {
u64
ip
;
u64
transaction
;
s32
cpu
;
u8
cpumode
;
struct
hist_entry_diff
diff
;
...
...
@@ -185,6 +186,7 @@ enum sort_type {
SORT_MEM_TLB
,
SORT_MEM_LVL
,
SORT_MEM_SNOOP
,
SORT_MEM_DCACHELINE
,
};
/*
...
...
tools/perf/util/unwind-libunwind.c
View file @
cf230918
...
...
@@ -250,7 +250,6 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
/* Check the .eh_frame section for unwinding info */
offset
=
elf_section_offset
(
fd
,
".eh_frame_hdr"
);
close
(
fd
);
if
(
offset
)
ret
=
unwind_spec_ehframe
(
dso
,
machine
,
offset
,
...
...
@@ -271,7 +270,6 @@ static int read_unwind_spec_debug_frame(struct dso *dso,
/* Check the .debug_frame section for unwinding info */
*
offset
=
elf_section_offset
(
fd
,
".debug_frame"
);
close
(
fd
);
if
(
*
offset
)
return
0
;
...
...
tools/perf/util/util.c
View file @
cf230918
...
...
@@ -17,6 +17,7 @@
* XXX We need to find a better place for these things...
*/
unsigned
int
page_size
;
int
cacheline_size
;
bool
test_attr__enabled
;
...
...
tools/perf/util/util.h
View file @
cf230918
...
...
@@ -304,6 +304,7 @@ char *rtrim(char *s);
void
dump_stack
(
void
);
extern
unsigned
int
page_size
;
extern
int
cacheline_size
;
void
get_term_dimensions
(
struct
winsize
*
ws
);
...
...
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