perf/core improvements and fixes:
New features: . Add --dry-run option to 'perf record' to check if command line options can be parsed, but not doing any recording (Wang Nan) . Allow dumping the object files generated by llvm when processing eBPF scriptlet events (Wang Nan) - Add stackcollapse.py script to help generating flame graphs (Paolo Bonzini) Documentation: . Fix 'perf script' documentation of '-f' when it should be '-F' (Adrian Hunter) Infrastructure: - Fix write_backwards fallback when using a new tool on older kernels without support for this feature (Arnaldo Carvalho de Melo) - Remove some leftovers from the initial codebase copying from git (Arnaldo Carvalho de Melo) - List libelf-devel as an alternative, as this is how the libelf development package is called on OpenSuSE (Jean Delvare) - Rename __hists__add_entry to hists__add_entry (Jiri Olsa) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXaWpiAAoJENZQFvNTUqpANWUQAKeRL3CDlyRfekNCOMNDzZo7 7/SeIcDZQwYR5/lHCk3/2Sy3QFtzAj0bEztYxWSL9L/NsNmW7mwaisYlh07UqSMJ idVehJ53bLXQjNirWmu0pfv0U/dVqH5YsLEgLqpwDgt8aN33m+F6/Z67vnxYEDbk mRYd+KcdShaY/gpwsS+S9ewb8A+CLuILOMQuqEU4BXD1zayg+v+H8ciNT8ai0HQm g9bpA9w6EICfx/aAylHnGNcaMrCl35cUm8cQaqkplqEf/DtUVewY7rt/ZvNj66mX T+CTPWh/Wt5bYjOYd1wF8YG7w1JqCIuEkkMzfqDdiVmv/PfTtNT9ihpF1gwo4Xj1 RZwkhPb1sNCfoLTdPIJTnP61+GWQ3Cqwl/jUnmvpDNAS9ehgLVotdw/dP6D6Loyq pQmQCtu/Ir4PeXwFS8feMoGk0bikAPpbv4HeY7k9JNOSkAQPO11inBHnupxbah9w 4FQE5yaxTuouoS8JNjAA28BqWjbpodKcvLXTl/hnVs+MWI95chaN1FlQJRU81Oaf 9Vf65gYU6iA0SLrLXBo1+4qBd3usFmNAoSyjFw0e+38O+8BNH6YIy7LlU2USFjtZ m8Xsoq2DlEKiO0iWXXLIKrZFYzFlZOl7cUQeqdC6Ho6okS+C+ougAaNxZrRU8fCE TmtWQrFrTPOQHNW8o35d =zbKX -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-20160621' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: New features: - Add --dry-run option to 'perf record' to check if command line options can be parsed, but not doing any recording (Wang Nan) - Allow dumping the object files generated by llvm when processing eBPF scriptlet events (Wang Nan) - Add stackcollapse.py script to help generating flame graphs (Paolo Bonzini) Documentation changes: - Fix 'perf script' documentation of '-f' when it should be '-F' (Adrian Hunter) Infrastructure changes: - Fix write_backwards fallback when using a new tool on older kernels without support for this feature (Arnaldo Carvalho de Melo) - Remove some leftovers from the initial codebase copying from git (Arnaldo Carvalho de Melo) - List libelf-devel as an alternative, as this is how the libelf development package is called on OpenSuSE (Jean Delvare) - Rename __hists__add_entry to hists__add_entry (Jiri Olsa) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
4330b439bb
2
Makefile
2
Makefile
@ -1038,7 +1038,7 @@ ifdef CONFIG_STACK_VALIDATION
|
|||||||
ifeq ($(has_libelf),1)
|
ifeq ($(has_libelf),1)
|
||||||
objtool_target := tools/objtool FORCE
|
objtool_target := tools/objtool FORCE
|
||||||
else
|
else
|
||||||
$(warning "Cannot use CONFIG_STACK_VALIDATION, please install libelf-dev or elfutils-libelf-devel")
|
$(warning "Cannot use CONFIG_STACK_VALIDATION, please install libelf-dev, libelf-devel or elfutils-libelf-devel")
|
||||||
SKIP_STACK_VALIDATION := 1
|
SKIP_STACK_VALIDATION := 1
|
||||||
export SKIP_STACK_VALIDATION
|
export SKIP_STACK_VALIDATION
|
||||||
endif
|
endif
|
||||||
|
@ -360,6 +360,13 @@ particular perf.data snapshot should be kept or not.
|
|||||||
|
|
||||||
Implies --timestamp-filename, --no-buildid and --no-buildid-cache.
|
Implies --timestamp-filename, --no-buildid and --no-buildid-cache.
|
||||||
|
|
||||||
|
--dry-run::
|
||||||
|
Parse options then exit. --dry-run can be used to detect errors in cmdline
|
||||||
|
options.
|
||||||
|
|
||||||
|
'perf record --dry-run -e' can act as a BPF script compiler if llvm.dump-obj
|
||||||
|
in config file is set to true.
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
linkperf:perf-stat[1], linkperf:perf-list[1]
|
linkperf:perf-stat[1], linkperf:perf-list[1]
|
||||||
|
@ -119,13 +119,13 @@ OPTIONS
|
|||||||
srcline, period, iregs, brstack, brstacksym, flags.
|
srcline, period, iregs, brstack, brstacksym, flags.
|
||||||
Field list can be prepended with the type, trace, sw or hw,
|
Field list can be prepended with the type, trace, sw or hw,
|
||||||
to indicate to which event type the field list applies.
|
to indicate to which event type the field list applies.
|
||||||
e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace
|
e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace
|
||||||
|
|
||||||
perf script -f <fields>
|
perf script -F <fields>
|
||||||
|
|
||||||
is equivalent to:
|
is equivalent to:
|
||||||
|
|
||||||
perf script -f trace:<fields> -f sw:<fields> -f hw:<fields>
|
perf script -F trace:<fields> -F sw:<fields> -F hw:<fields>
|
||||||
|
|
||||||
i.e., the specified fields apply to all event types if the type string
|
i.e., the specified fields apply to all event types if the type string
|
||||||
is not given.
|
is not given.
|
||||||
@ -133,9 +133,9 @@ OPTIONS
|
|||||||
The arguments are processed in the order received. A later usage can
|
The arguments are processed in the order received. A later usage can
|
||||||
reset a prior request. e.g.:
|
reset a prior request. e.g.:
|
||||||
|
|
||||||
-f trace: -f comm,tid,time,ip,sym
|
-F trace: -F comm,tid,time,ip,sym
|
||||||
|
|
||||||
The first -f suppresses trace events (field list is ""), but then the
|
The first -F suppresses trace events (field list is ""), but then the
|
||||||
second invocation sets the fields to comm,tid,time,ip,sym. In this case a
|
second invocation sets the fields to comm,tid,time,ip,sym. In this case a
|
||||||
warning is given to the user:
|
warning is given to the user:
|
||||||
|
|
||||||
@ -143,9 +143,9 @@ OPTIONS
|
|||||||
|
|
||||||
Alternatively, consider the order:
|
Alternatively, consider the order:
|
||||||
|
|
||||||
-f comm,tid,time,ip,sym -f trace:
|
-F comm,tid,time,ip,sym -F trace:
|
||||||
|
|
||||||
The first -f sets the fields for all events and the second -f
|
The first -F sets the fields for all events and the second -F
|
||||||
suppresses trace events. The user is given a warning message about
|
suppresses trace events. The user is given a warning message about
|
||||||
the override, and the result of the above is that only S/W and H/W
|
the override, and the result of the above is that only S/W and H/W
|
||||||
events are displayed with the given fields.
|
events are displayed with the given fields.
|
||||||
@ -154,14 +154,14 @@ OPTIONS
|
|||||||
event type, a message is displayed to the user that the option is
|
event type, a message is displayed to the user that the option is
|
||||||
ignored for that type. For example:
|
ignored for that type. For example:
|
||||||
|
|
||||||
$ perf script -f comm,tid,trace
|
$ perf script -F comm,tid,trace
|
||||||
'trace' not valid for hardware events. Ignoring.
|
'trace' not valid for hardware events. Ignoring.
|
||||||
'trace' not valid for software events. Ignoring.
|
'trace' not valid for software events. Ignoring.
|
||||||
|
|
||||||
Alternatively, if the type is given an invalid field is specified it
|
Alternatively, if the type is given an invalid field is specified it
|
||||||
is an error. For example:
|
is an error. For example:
|
||||||
|
|
||||||
perf script -v -f sw:comm,tid,trace
|
perf script -v -F sw:comm,tid,trace
|
||||||
'trace' not valid for software events.
|
'trace' not valid for software events.
|
||||||
|
|
||||||
At this point usage is displayed, and perf-script exits.
|
At this point usage is displayed, and perf-script exits.
|
||||||
@ -173,7 +173,7 @@ OPTIONS
|
|||||||
respectively.
|
respectively.
|
||||||
|
|
||||||
Finally, a user may not set fields to none for all event types.
|
Finally, a user may not set fields to none for all event types.
|
||||||
i.e., -f "" is not allowed.
|
i.e., -F "" is not allowed.
|
||||||
|
|
||||||
The brstack output includes branch related information with raw addresses using the
|
The brstack output includes branch related information with raw addresses using the
|
||||||
/v/v/v/v/ syntax in the following order:
|
/v/v/v/v/ syntax in the following order:
|
||||||
|
@ -75,7 +75,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
|
|||||||
sample->period = 1;
|
sample->period = 1;
|
||||||
sample->weight = 1;
|
sample->weight = 1;
|
||||||
|
|
||||||
he = __hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
|
he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -310,16 +310,6 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hists__add_entry(struct hists *hists,
|
|
||||||
struct addr_location *al,
|
|
||||||
struct perf_sample *sample)
|
|
||||||
{
|
|
||||||
if (__hists__add_entry(hists, al, NULL, NULL, NULL,
|
|
||||||
sample, true) != NULL)
|
|
||||||
return 0;
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
|
static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
@ -336,7 +326,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hists__add_entry(hists, &al, sample)) {
|
if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
|
||||||
pr_warning("problem incrementing symbol period, skipping event\n");
|
pr_warning("problem incrementing symbol period, skipping event\n");
|
||||||
goto out_put;
|
goto out_put;
|
||||||
}
|
}
|
||||||
|
@ -1274,6 +1274,8 @@ static struct record record = {
|
|||||||
const char record_callchain_help[] = CALLCHAIN_RECORD_HELP
|
const char record_callchain_help[] = CALLCHAIN_RECORD_HELP
|
||||||
"\n\t\t\t\tDefault: fp";
|
"\n\t\t\t\tDefault: fp";
|
||||||
|
|
||||||
|
static bool dry_run;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX Will stay a global variable till we fix builtin-script.c to stop messing
|
* XXX Will stay a global variable till we fix builtin-script.c to stop messing
|
||||||
* with it and switch to use the library functions in perf_evlist that came
|
* with it and switch to use the library functions in perf_evlist that came
|
||||||
@ -1393,6 +1395,8 @@ struct option __record_options[] = {
|
|||||||
"append timestamp to output filename"),
|
"append timestamp to output filename"),
|
||||||
OPT_BOOLEAN(0, "switch-output", &record.switch_output,
|
OPT_BOOLEAN(0, "switch-output", &record.switch_output,
|
||||||
"Switch output when receive SIGUSR2"),
|
"Switch output when receive SIGUSR2"),
|
||||||
|
OPT_BOOLEAN(0, "dry-run", &dry_run,
|
||||||
|
"Parse options then exit"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1462,6 +1466,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (dry_run)
|
||||||
|
return 0;
|
||||||
|
|
||||||
err = bpf__setup_stdout(rec->evlist);
|
err = bpf__setup_stdout(rec->evlist);
|
||||||
if (err) {
|
if (err) {
|
||||||
bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf));
|
bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf));
|
||||||
|
@ -257,7 +257,7 @@ else
|
|||||||
LIBC_SUPPORT := 1
|
LIBC_SUPPORT := 1
|
||||||
endif
|
endif
|
||||||
ifeq ($(LIBC_SUPPORT),1)
|
ifeq ($(LIBC_SUPPORT),1)
|
||||||
msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install elfutils-libelf-devel/libelf-dev);
|
msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install libelf-dev, libelf-devel or elfutils-libelf-devel);
|
||||||
|
|
||||||
NO_LIBELF := 1
|
NO_LIBELF := 1
|
||||||
NO_DWARF := 1
|
NO_DWARF := 1
|
||||||
|
@ -139,8 +139,6 @@ struct option options[] = {
|
|||||||
OPT_ARGUMENT("html-path", "html-path"),
|
OPT_ARGUMENT("html-path", "html-path"),
|
||||||
OPT_ARGUMENT("paginate", "paginate"),
|
OPT_ARGUMENT("paginate", "paginate"),
|
||||||
OPT_ARGUMENT("no-pager", "no-pager"),
|
OPT_ARGUMENT("no-pager", "no-pager"),
|
||||||
OPT_ARGUMENT("perf-dir", "perf-dir"),
|
|
||||||
OPT_ARGUMENT("work-tree", "work-tree"),
|
|
||||||
OPT_ARGUMENT("debugfs-dir", "debugfs-dir"),
|
OPT_ARGUMENT("debugfs-dir", "debugfs-dir"),
|
||||||
OPT_ARGUMENT("buildid-dir", "buildid-dir"),
|
OPT_ARGUMENT("buildid-dir", "buildid-dir"),
|
||||||
OPT_ARGUMENT("list-cmds", "list-cmds"),
|
OPT_ARGUMENT("list-cmds", "list-cmds"),
|
||||||
@ -200,35 +198,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
|||||||
use_pager = 0;
|
use_pager = 0;
|
||||||
if (envchanged)
|
if (envchanged)
|
||||||
*envchanged = 1;
|
*envchanged = 1;
|
||||||
} else if (!strcmp(cmd, "--perf-dir")) {
|
|
||||||
if (*argc < 2) {
|
|
||||||
fprintf(stderr, "No directory given for --perf-dir.\n");
|
|
||||||
usage(perf_usage_string);
|
|
||||||
}
|
|
||||||
setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1);
|
|
||||||
if (envchanged)
|
|
||||||
*envchanged = 1;
|
|
||||||
(*argv)++;
|
|
||||||
(*argc)--;
|
|
||||||
handled++;
|
|
||||||
} else if (!prefixcmp(cmd, CMD_PERF_DIR)) {
|
|
||||||
setenv(PERF_DIR_ENVIRONMENT, cmd + strlen(CMD_PERF_DIR), 1);
|
|
||||||
if (envchanged)
|
|
||||||
*envchanged = 1;
|
|
||||||
} else if (!strcmp(cmd, "--work-tree")) {
|
|
||||||
if (*argc < 2) {
|
|
||||||
fprintf(stderr, "No directory given for --work-tree.\n");
|
|
||||||
usage(perf_usage_string);
|
|
||||||
}
|
|
||||||
setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
|
|
||||||
if (envchanged)
|
|
||||||
*envchanged = 1;
|
|
||||||
(*argv)++;
|
|
||||||
(*argc)--;
|
|
||||||
} else if (!prefixcmp(cmd, CMD_WORK_TREE)) {
|
|
||||||
setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + strlen(CMD_WORK_TREE), 1);
|
|
||||||
if (envchanged)
|
|
||||||
*envchanged = 1;
|
|
||||||
} else if (!strcmp(cmd, "--debugfs-dir")) {
|
} else if (!strcmp(cmd, "--debugfs-dir")) {
|
||||||
if (*argc < 2) {
|
if (*argc < 2) {
|
||||||
fprintf(stderr, "No directory given for --debugfs-dir.\n");
|
fprintf(stderr, "No directory given for --debugfs-dir.\n");
|
||||||
@ -363,11 +332,6 @@ const char perf_version_string[] = PERF_VERSION;
|
|||||||
|
|
||||||
#define RUN_SETUP (1<<0)
|
#define RUN_SETUP (1<<0)
|
||||||
#define USE_PAGER (1<<1)
|
#define USE_PAGER (1<<1)
|
||||||
/*
|
|
||||||
* require working tree to be present -- anything uses this needs
|
|
||||||
* RUN_SETUP for reading from the configuration file.
|
|
||||||
*/
|
|
||||||
#define NEED_WORK_TREE (1<<2)
|
|
||||||
|
|
||||||
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||||
{
|
{
|
||||||
|
8
tools/perf/scripts/python/bin/stackcollapse-record
Executable file
8
tools/perf/scripts/python/bin/stackcollapse-record
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# stackcollapse.py can cover all type of perf samples including
|
||||||
|
# the tracepoints, so no special record requirements, just record what
|
||||||
|
# you want to analyze.
|
||||||
|
#
|
||||||
|
perf record "$@"
|
3
tools/perf/scripts/python/bin/stackcollapse-report
Executable file
3
tools/perf/scripts/python/bin/stackcollapse-report
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# description: produce callgraphs in short form for scripting use
|
||||||
|
perf script -s "$PERF_EXEC_PATH"/scripts/python/stackcollapse.py -- "$@"
|
127
tools/perf/scripts/python/stackcollapse.py
Executable file
127
tools/perf/scripts/python/stackcollapse.py
Executable file
@ -0,0 +1,127 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
#
|
||||||
|
# stackcollapse.py - format perf samples with one line per distinct call stack
|
||||||
|
#
|
||||||
|
# This script's output has two space-separated fields. The first is a semicolon
|
||||||
|
# separated stack including the program name (from the "comm" field) and the
|
||||||
|
# function names from the call stack. The second is a count:
|
||||||
|
#
|
||||||
|
# swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 2
|
||||||
|
#
|
||||||
|
# The file is sorted according to the first field.
|
||||||
|
#
|
||||||
|
# Input may be created and processed using:
|
||||||
|
#
|
||||||
|
# perf record -a -g -F 99 sleep 60
|
||||||
|
# perf script report stackcollapse > out.stacks-folded
|
||||||
|
#
|
||||||
|
# (perf script record stackcollapse works too).
|
||||||
|
#
|
||||||
|
# Written by Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
# Based on Brendan Gregg's stackcollapse-perf.pl script.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from optparse import OptionParser, make_option
|
||||||
|
|
||||||
|
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||||
|
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
|
||||||
|
|
||||||
|
from perf_trace_context import *
|
||||||
|
from Core import *
|
||||||
|
from EventClass import *
|
||||||
|
|
||||||
|
# command line parsing
|
||||||
|
|
||||||
|
option_list = [
|
||||||
|
# formatting options for the bottom entry of the stack
|
||||||
|
make_option("--include-tid", dest="include_tid",
|
||||||
|
action="store_true", default=False,
|
||||||
|
help="include thread id in stack"),
|
||||||
|
make_option("--include-pid", dest="include_pid",
|
||||||
|
action="store_true", default=False,
|
||||||
|
help="include process id in stack"),
|
||||||
|
make_option("--no-comm", dest="include_comm",
|
||||||
|
action="store_false", default=True,
|
||||||
|
help="do not separate stacks according to comm"),
|
||||||
|
make_option("--tidy-java", dest="tidy_java",
|
||||||
|
action="store_true", default=False,
|
||||||
|
help="beautify Java signatures"),
|
||||||
|
make_option("--kernel", dest="annotate_kernel",
|
||||||
|
action="store_true", default=False,
|
||||||
|
help="annotate kernel functions with _[k]")
|
||||||
|
]
|
||||||
|
|
||||||
|
parser = OptionParser(option_list=option_list)
|
||||||
|
(opts, args) = parser.parse_args()
|
||||||
|
|
||||||
|
if len(args) != 0:
|
||||||
|
parser.error("unexpected command line argument")
|
||||||
|
if opts.include_tid and not opts.include_comm:
|
||||||
|
parser.error("requesting tid but not comm is invalid")
|
||||||
|
if opts.include_pid and not opts.include_comm:
|
||||||
|
parser.error("requesting pid but not comm is invalid")
|
||||||
|
|
||||||
|
# event handlers
|
||||||
|
|
||||||
|
lines = defaultdict(lambda: 0)
|
||||||
|
|
||||||
|
def process_event(param_dict):
|
||||||
|
def tidy_function_name(sym, dso):
|
||||||
|
if sym is None:
|
||||||
|
sym = '[unknown]'
|
||||||
|
|
||||||
|
sym = sym.replace(';', ':')
|
||||||
|
if opts.tidy_java:
|
||||||
|
# the original stackcollapse-perf.pl script gives the
|
||||||
|
# example of converting this:
|
||||||
|
# Lorg/mozilla/javascript/MemberBox;.<init>(Ljava/lang/reflect/Method;)V
|
||||||
|
# to this:
|
||||||
|
# org/mozilla/javascript/MemberBox:.init
|
||||||
|
sym = sym.replace('<', '')
|
||||||
|
sym = sym.replace('>', '')
|
||||||
|
if sym[0] == 'L' and sym.find('/'):
|
||||||
|
sym = sym[1:]
|
||||||
|
try:
|
||||||
|
sym = sym[:sym.index('(')]
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if opts.annotate_kernel and dso == '[kernel.kallsyms]':
|
||||||
|
return sym + '_[k]'
|
||||||
|
else:
|
||||||
|
return sym
|
||||||
|
|
||||||
|
stack = list()
|
||||||
|
if 'callchain' in param_dict:
|
||||||
|
for entry in param_dict['callchain']:
|
||||||
|
entry.setdefault('sym', dict())
|
||||||
|
entry['sym'].setdefault('name', None)
|
||||||
|
entry.setdefault('dso', None)
|
||||||
|
stack.append(tidy_function_name(entry['sym']['name'],
|
||||||
|
entry['dso']))
|
||||||
|
else:
|
||||||
|
param_dict.setdefault('symbol', None)
|
||||||
|
param_dict.setdefault('dso', None)
|
||||||
|
stack.append(tidy_function_name(param_dict['symbol'],
|
||||||
|
param_dict['dso']))
|
||||||
|
|
||||||
|
if opts.include_comm:
|
||||||
|
comm = param_dict["comm"].replace(' ', '_')
|
||||||
|
sep = "-"
|
||||||
|
if opts.include_pid:
|
||||||
|
comm = comm + sep + str(param_dict['sample']['pid'])
|
||||||
|
sep = "/"
|
||||||
|
if opts.include_tid:
|
||||||
|
comm = comm + sep + str(param_dict['sample']['tid'])
|
||||||
|
stack.append(comm)
|
||||||
|
|
||||||
|
stack_string = ';'.join(reversed(stack))
|
||||||
|
lines[stack_string] = lines[stack_string] + 1
|
||||||
|
|
||||||
|
def trace_end():
|
||||||
|
list = lines.keys()
|
||||||
|
list.sort()
|
||||||
|
for stack in list:
|
||||||
|
print "%s %d" % (stack, lines[stack])
|
@ -84,7 +84,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
|||||||
if (machine__resolve(machine, &al, &sample) < 0)
|
if (machine__resolve(machine, &al, &sample) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
he = __hists__add_entry(hists, &al, NULL,
|
he = hists__add_entry(hists, &al, NULL,
|
||||||
NULL, NULL, &sample, true);
|
NULL, NULL, &sample, true);
|
||||||
if (he == NULL) {
|
if (he == NULL) {
|
||||||
addr_location__put(&al);
|
addr_location__put(&al);
|
||||||
@ -103,7 +103,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
|
|||||||
if (machine__resolve(machine, &al, &sample) < 0)
|
if (machine__resolve(machine, &al, &sample) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
he = __hists__add_entry(hists, &al, NULL,
|
he = hists__add_entry(hists, &al, NULL,
|
||||||
NULL, NULL, &sample, true);
|
NULL, NULL, &sample, true);
|
||||||
if (he == NULL) {
|
if (he == NULL) {
|
||||||
addr_location__put(&al);
|
addr_location__put(&al);
|
||||||
|
@ -11,14 +11,9 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
|
||||||
#define CMD_EXEC_PATH "--exec-path"
|
#define CMD_EXEC_PATH "--exec-path"
|
||||||
#define CMD_PERF_DIR "--perf-dir="
|
|
||||||
#define CMD_WORK_TREE "--work-tree="
|
|
||||||
#define CMD_DEBUGFS_DIR "--debugfs-dir="
|
#define CMD_DEBUGFS_DIR "--debugfs-dir="
|
||||||
|
|
||||||
#define PERF_DIR_ENVIRONMENT "PERF_DIR"
|
|
||||||
#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
|
|
||||||
#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
|
#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
|
||||||
#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
|
|
||||||
#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
|
#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
|
||||||
#define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR"
|
#define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR"
|
||||||
#define PERF_PAGER_ENVIRONMENT "PERF_PAGER"
|
#define PERF_PAGER_ENVIRONMENT "PERF_PAGER"
|
||||||
@ -32,7 +27,6 @@ int perf_config_int(const char *, const char *);
|
|||||||
u64 perf_config_u64(const char *, const char *);
|
u64 perf_config_u64(const char *, const char *);
|
||||||
int perf_config_bool(const char *, const char *);
|
int perf_config_bool(const char *, const char *);
|
||||||
int config_error_nonbool(const char *);
|
int config_error_nonbool(const char *);
|
||||||
const char *perf_config_dirname(const char *, const char *);
|
|
||||||
const char *perf_etc_perfconfig(void);
|
const char *perf_etc_perfconfig(void);
|
||||||
|
|
||||||
char *alias_lookup(const char *alias);
|
char *alias_lookup(const char *alias);
|
||||||
@ -45,9 +39,6 @@ static inline int is_absolute_path(const char *path)
|
|||||||
return path[0] == '/';
|
return path[0] == '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
char *strip_path_suffix(const char *path, const char *suffix);
|
|
||||||
|
|
||||||
char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
|
char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
|
||||||
char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
|
|
||||||
|
|
||||||
#endif /* __PERF_CACHE_H */
|
#endif /* __PERF_CACHE_H */
|
||||||
|
@ -372,7 +372,7 @@ int perf_config_bool(const char *name, const char *value)
|
|||||||
return !!perf_config_bool_or_int(name, value, &discard);
|
return !!perf_config_bool_or_int(name, value, &discard);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *perf_config_dirname(const char *name, const char *value)
|
static const char *perf_config_dirname(const char *name, const char *value)
|
||||||
{
|
{
|
||||||
if (!name)
|
if (!name)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1389,8 +1389,11 @@ fallback_missing_features:
|
|||||||
if (perf_missing_features.lbr_flags)
|
if (perf_missing_features.lbr_flags)
|
||||||
evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS |
|
evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS |
|
||||||
PERF_SAMPLE_BRANCH_NO_CYCLES);
|
PERF_SAMPLE_BRANCH_NO_CYCLES);
|
||||||
if (perf_missing_features.write_backward)
|
if (perf_missing_features.write_backward) {
|
||||||
|
if (evsel->overwrite)
|
||||||
|
return -EINVAL;
|
||||||
evsel->attr.write_backward = false;
|
evsel->attr.write_backward = false;
|
||||||
|
}
|
||||||
retry_sample_id:
|
retry_sample_id:
|
||||||
if (perf_missing_features.sample_id_all)
|
if (perf_missing_features.sample_id_all)
|
||||||
evsel->attr.sample_id_all = 0;
|
evsel->attr.sample_id_all = 0;
|
||||||
@ -1453,12 +1456,6 @@ retry_open:
|
|||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto out_close;
|
goto out_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evsel->overwrite &&
|
|
||||||
perf_missing_features.write_backward) {
|
|
||||||
err = -EINVAL;
|
|
||||||
goto out_close;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1496,7 +1493,10 @@ try_fallback:
|
|||||||
* Must probe features in the order they were added to the
|
* Must probe features in the order they were added to the
|
||||||
* perf_event_attr interface.
|
* perf_event_attr interface.
|
||||||
*/
|
*/
|
||||||
if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) {
|
if (!perf_missing_features.write_backward && evsel->attr.write_backward) {
|
||||||
|
perf_missing_features.write_backward = true;
|
||||||
|
goto fallback_missing_features;
|
||||||
|
} else if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) {
|
||||||
perf_missing_features.clockid_wrong = true;
|
perf_missing_features.clockid_wrong = true;
|
||||||
goto fallback_missing_features;
|
goto fallback_missing_features;
|
||||||
} else if (!perf_missing_features.clockid && evsel->attr.use_clockid) {
|
} else if (!perf_missing_features.clockid && evsel->attr.use_clockid) {
|
||||||
@ -1521,12 +1521,7 @@ try_fallback:
|
|||||||
PERF_SAMPLE_BRANCH_NO_FLAGS))) {
|
PERF_SAMPLE_BRANCH_NO_FLAGS))) {
|
||||||
perf_missing_features.lbr_flags = true;
|
perf_missing_features.lbr_flags = true;
|
||||||
goto fallback_missing_features;
|
goto fallback_missing_features;
|
||||||
} else if (!perf_missing_features.write_backward &&
|
|
||||||
evsel->attr.write_backward) {
|
|
||||||
perf_missing_features.write_backward = true;
|
|
||||||
goto fallback_missing_features;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out_close:
|
out_close:
|
||||||
do {
|
do {
|
||||||
while (--thread >= 0) {
|
while (--thread >= 0) {
|
||||||
@ -2409,6 +2404,8 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
|
|||||||
"We found oprofile daemon running, please stop it and try again.");
|
"We found oprofile daemon running, please stop it and try again.");
|
||||||
break;
|
break;
|
||||||
case EINVAL:
|
case EINVAL:
|
||||||
|
if (evsel->overwrite && perf_missing_features.write_backward)
|
||||||
|
return scnprintf(msg, size, "Reading from overwrite event is not supported by this kernel.");
|
||||||
if (perf_missing_features.clockid)
|
if (perf_missing_features.clockid)
|
||||||
return scnprintf(msg, size, "clockid feature not supported.");
|
return scnprintf(msg, size, "clockid feature not supported.");
|
||||||
if (perf_missing_features.clockid_wrong)
|
if (perf_missing_features.clockid_wrong)
|
||||||
|
@ -531,7 +531,7 @@ out:
|
|||||||
return he;
|
return he;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hist_entry *__hists__add_entry(struct hists *hists,
|
struct hist_entry *hists__add_entry(struct hists *hists,
|
||||||
struct addr_location *al,
|
struct addr_location *al,
|
||||||
struct symbol *sym_parent,
|
struct symbol *sym_parent,
|
||||||
struct branch_info *bi,
|
struct branch_info *bi,
|
||||||
@ -622,7 +622,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al
|
|||||||
*/
|
*/
|
||||||
sample->period = cost;
|
sample->period = cost;
|
||||||
|
|
||||||
he = __hists__add_entry(hists, al, iter->parent, NULL, mi,
|
he = hists__add_entry(hists, al, iter->parent, NULL, mi,
|
||||||
sample, true);
|
sample, true);
|
||||||
if (!he)
|
if (!he)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -727,7 +727,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a
|
|||||||
sample->period = 1;
|
sample->period = 1;
|
||||||
sample->weight = bi->flags.cycles ? bi->flags.cycles : 1;
|
sample->weight = bi->flags.cycles ? bi->flags.cycles : 1;
|
||||||
|
|
||||||
he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL,
|
he = hists__add_entry(hists, al, iter->parent, &bi[i], NULL,
|
||||||
sample, true);
|
sample, true);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -764,7 +764,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location
|
|||||||
struct perf_sample *sample = iter->sample;
|
struct perf_sample *sample = iter->sample;
|
||||||
struct hist_entry *he;
|
struct hist_entry *he;
|
||||||
|
|
||||||
he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
|
he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
|
||||||
sample, true);
|
sample, true);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -825,7 +825,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
|
|||||||
struct hist_entry *he;
|
struct hist_entry *he;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
he = __hists__add_entry(hists, al, iter->parent, NULL, NULL,
|
he = hists__add_entry(hists, al, iter->parent, NULL, NULL,
|
||||||
sample, true);
|
sample, true);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -900,7 +900,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
|
he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
|
||||||
sample, false);
|
sample, false);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -120,7 +120,7 @@ extern const struct hist_iter_ops hist_iter_branch;
|
|||||||
extern const struct hist_iter_ops hist_iter_mem;
|
extern const struct hist_iter_ops hist_iter_mem;
|
||||||
extern const struct hist_iter_ops hist_iter_cumulative;
|
extern const struct hist_iter_ops hist_iter_cumulative;
|
||||||
|
|
||||||
struct hist_entry *__hists__add_entry(struct hists *hists,
|
struct hist_entry *hists__add_entry(struct hists *hists,
|
||||||
struct addr_location *al,
|
struct addr_location *al,
|
||||||
struct symbol *parent,
|
struct symbol *parent,
|
||||||
struct branch_info *bi,
|
struct branch_info *bi,
|
||||||
|
@ -42,6 +42,8 @@ int perf_llvm_config(const char *var, const char *value)
|
|||||||
llvm_param.kbuild_dir = strdup(value);
|
llvm_param.kbuild_dir = strdup(value);
|
||||||
else if (!strcmp(var, "kbuild-opts"))
|
else if (!strcmp(var, "kbuild-opts"))
|
||||||
llvm_param.kbuild_opts = strdup(value);
|
llvm_param.kbuild_opts = strdup(value);
|
||||||
|
else if (!strcmp(var, "dump-obj"))
|
||||||
|
llvm_param.dump_obj = !!perf_config_bool(var, value);
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
llvm_param.user_set_param = true;
|
llvm_param.user_set_param = true;
|
||||||
@ -326,6 +328,42 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
|
|||||||
pr_debug("include option is set to %s\n", *kbuild_include_opts);
|
pr_debug("include option is set to %s\n", *kbuild_include_opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump_obj(const char *path, void *obj_buf, size_t size)
|
||||||
|
{
|
||||||
|
char *obj_path = strdup(path);
|
||||||
|
FILE *fp;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if (!obj_path) {
|
||||||
|
pr_warning("WARNING: No enough memory, skip object dumping\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = strrchr(obj_path, '.');
|
||||||
|
if (!p || (strcmp(p, ".c") != 0)) {
|
||||||
|
pr_warning("WARNING: invalid llvm source path: '%s', skip object dumping\n",
|
||||||
|
obj_path);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
p[1] = 'o';
|
||||||
|
fp = fopen(obj_path, "wb");
|
||||||
|
if (!fp) {
|
||||||
|
pr_warning("WARNING: failed to open '%s': %s, skip object dumping\n",
|
||||||
|
obj_path, strerror(errno));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("LLVM: dumping %s\n", obj_path);
|
||||||
|
if (fwrite(obj_buf, size, 1, fp) != 1)
|
||||||
|
pr_warning("WARNING: failed to write to file '%s': %s, skip object dumping\n",
|
||||||
|
obj_path, strerror(errno));
|
||||||
|
fclose(fp);
|
||||||
|
out:
|
||||||
|
free(obj_path);
|
||||||
|
}
|
||||||
|
|
||||||
int llvm__compile_bpf(const char *path, void **p_obj_buf,
|
int llvm__compile_bpf(const char *path, void **p_obj_buf,
|
||||||
size_t *p_obj_buf_sz)
|
size_t *p_obj_buf_sz)
|
||||||
{
|
{
|
||||||
@ -411,6 +449,10 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
|
|||||||
|
|
||||||
free(kbuild_dir);
|
free(kbuild_dir);
|
||||||
free(kbuild_include_opts);
|
free(kbuild_include_opts);
|
||||||
|
|
||||||
|
if (llvm_param.dump_obj)
|
||||||
|
dump_obj(path, obj_buf, obj_buf_sz);
|
||||||
|
|
||||||
if (!p_obj_buf)
|
if (!p_obj_buf)
|
||||||
free(obj_buf);
|
free(obj_buf);
|
||||||
else
|
else
|
||||||
|
@ -29,6 +29,11 @@ struct llvm_param {
|
|||||||
* compiling. Should not be used for dynamic compiling.
|
* compiling. Should not be used for dynamic compiling.
|
||||||
*/
|
*/
|
||||||
const char *kbuild_opts;
|
const char *kbuild_opts;
|
||||||
|
/*
|
||||||
|
* Default is false. If set to true, write compiling result
|
||||||
|
* to object file.
|
||||||
|
*/
|
||||||
|
bool dump_obj;
|
||||||
/*
|
/*
|
||||||
* Default is false. If one of the above fields is set by user
|
* Default is false. If one of the above fields is set by user
|
||||||
* explicitly then user_set_llvm is set to true. This is used
|
* explicitly then user_set_llvm is set to true. This is used
|
||||||
|
@ -14,14 +14,8 @@
|
|||||||
|
|
||||||
static char bad_path[] = "/bad-path/";
|
static char bad_path[] = "/bad-path/";
|
||||||
/*
|
/*
|
||||||
* Two hacks:
|
* One hack:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char *get_perf_dir(void)
|
|
||||||
{
|
|
||||||
return ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *get_pathname(void)
|
static char *get_pathname(void)
|
||||||
{
|
{
|
||||||
static char pathname_array[4][PATH_MAX];
|
static char pathname_array[4][PATH_MAX];
|
||||||
@ -54,60 +48,3 @@ char *mkpath(const char *fmt, ...)
|
|||||||
return bad_path;
|
return bad_path;
|
||||||
return cleanup_path(pathname);
|
return cleanup_path(pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *perf_path(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
const char *perf_dir = get_perf_dir();
|
|
||||||
char *pathname = get_pathname();
|
|
||||||
va_list args;
|
|
||||||
unsigned len;
|
|
||||||
|
|
||||||
len = strlen(perf_dir);
|
|
||||||
if (len > PATH_MAX-100)
|
|
||||||
return bad_path;
|
|
||||||
memcpy(pathname, perf_dir, len);
|
|
||||||
if (len && perf_dir[len-1] != '/')
|
|
||||||
pathname[len++] = '/';
|
|
||||||
va_start(args, fmt);
|
|
||||||
len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
if (len >= PATH_MAX)
|
|
||||||
return bad_path;
|
|
||||||
return cleanup_path(pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* strip arbitrary amount of directory separators at end of path */
|
|
||||||
static inline int chomp_trailing_dir_sep(const char *path, int len)
|
|
||||||
{
|
|
||||||
while (len && is_dir_sep(path[len - 1]))
|
|
||||||
len--;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If path ends with suffix (complete path components), returns the
|
|
||||||
* part before suffix (sans trailing directory separators).
|
|
||||||
* Otherwise returns NULL.
|
|
||||||
*/
|
|
||||||
char *strip_path_suffix(const char *path, const char *suffix)
|
|
||||||
{
|
|
||||||
int path_len = strlen(path), suffix_len = strlen(suffix);
|
|
||||||
|
|
||||||
while (suffix_len) {
|
|
||||||
if (!path_len)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (is_dir_sep(path[path_len - 1])) {
|
|
||||||
if (!is_dir_sep(suffix[suffix_len - 1]))
|
|
||||||
return NULL;
|
|
||||||
path_len = chomp_trailing_dir_sep(path, path_len);
|
|
||||||
suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
|
|
||||||
}
|
|
||||||
else if (path[--path_len] != suffix[--suffix_len])
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path_len && !is_dir_sep(path[path_len - 1]))
|
|
||||||
return NULL;
|
|
||||||
return strndup(path, chomp_trailing_dir_sep(path, path_len));
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user