perf/core improvements and fixes:
User visible: - Add support for skipping itrace instructions, useful to fast forward processor trace (Intel PT, BTS) to right after initialization code at the start of a workload (Andi Kleen) - Add support for backtraces in perl 'perf script's (Dima Kogan) - Add -U/-K (--all-user/--all-kernel) options to 'perf mem' (Jiri Olsa) - Make -f/--force option documentation consistent across tools (Jiri Olsa) Infrastructure: - Add 'perf test' to check for event times (Jiri Olsa) - 'perf config' cleanups (Taeung Song) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJW++RsAAoJENZQFvNTUqpAxrgP/A8i+A+WqTok8jEvmeCnWiqQ b8ZIdiLOM0hrDHrZwSXvmOqyicix1mOaHfS1INm76i9C0Olwh2SRla4/MIYVI5vI MAdvXIABgm0ychNh+XtleE16gXRWuFfc/7NBZk3f36bYUXhErOGxFuAUFlK1iOZ3 0D4rZaJkvVNc2LG01W56uZxueD5R0oCae8ctuVH7cqiCtWH6geIkrOHW2xk6Fj30 IU48Oq5C0p4q1n2YHpNudpngUH+Sr9GSMKPtRYCtGkuD/AgDzKfIDrqppeIK33Yo SXKXkTCK2+cCajCbAFQgLf8xP+GLSgbk2fnQMsSXmEjnng7w0s0bu+Yew33qVjnx /Hiyl0kkFD3xuSdmqlQE15ltrB1s1RHtBqK10SxZVQ7MNFLg5lltpMddEf/d/U3Z U/jii+RYJKVIRnS/QwqRZdwnsRwTCeGB3KyZU55mM0P0PN3+U+EMTRasWvdyE7ou 7rSnhKdY9swybZyU0Z084LOWHSIEfYeMD/JofqDOrLpR/3ZKIFbmWAv5kcPp+tXL CMoNPY2QIvo7GQXy3Qju8cthi/Td5GO1vr2InYgmm2IqksOT7OgvTssxbfbYOB/M ii6wrkT/WILQNoMThoZXm+dFJP+6Lxsu0KbpEVBytnd0tmSgnHVPlFeGLB3Qfblv d2mZss5ibOsMpo0f22hm =NGjt -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-20160330' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes: User visible changes: - Add support for skipping itrace instructions, useful to fast forward processor trace (Intel PT, BTS) to right after initialization code at the start of a workload (Andi Kleen) - Add support for backtraces in perl 'perf script's (Dima Kogan) - Add -U/-K (--all-user/--all-kernel) options to 'perf mem' (Jiri Olsa) - Make -f/--force option documentation consistent across tools (Jiri Olsa) Infrastructure changes: - Add 'perf test' to check for event times (Jiri Olsa) - 'perf config' cleanups (Taeung Song) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Коммит
643cb15ba0
|
@ -672,6 +672,7 @@ The letters are:
|
|||
d create a debug log
|
||||
g synthesize a call chain (use with i or x)
|
||||
l synthesize last branch entries (use with i or x)
|
||||
s skip initial number of events
|
||||
|
||||
"Instructions" events look like they were recorded by "perf record -e
|
||||
instructions".
|
||||
|
@ -730,6 +731,12 @@ from one sample to the next.
|
|||
|
||||
To disable trace decoding entirely, use the option --no-itrace.
|
||||
|
||||
It is also possible to skip events generated (instructions, branches, transactions)
|
||||
at the beginning. This is useful to ignore initialization code.
|
||||
|
||||
--itrace=i0nss1000000
|
||||
|
||||
skips the first million instructions.
|
||||
|
||||
dump option
|
||||
-----------
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
d create a debug log
|
||||
g synthesize a call chain (use with i or x)
|
||||
l synthesize last branch entries (use with i or x)
|
||||
s skip initial number of events
|
||||
|
||||
The default is all events i.e. the same as --itrace=ibxe
|
||||
|
||||
|
@ -24,3 +25,10 @@
|
|||
|
||||
Also the number of last branch entries (default 64, max. 1024) for
|
||||
instructions or transactions events can be specified.
|
||||
|
||||
It is also possible to skip events generated (instructions, branches, transactions)
|
||||
at the beginning. This is useful to ignore initialization code.
|
||||
|
||||
--itrace=i0nss1000000
|
||||
|
||||
skips the first million instructions.
|
||||
|
|
|
@ -33,7 +33,7 @@ OPTIONS
|
|||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
Don't do ownership validation.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
|
|
|
@ -75,7 +75,7 @@ OPTIONS
|
|||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
Don't do ownership validation.
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
|
|
@ -48,6 +48,14 @@ OPTIONS
|
|||
option can be passed in record mode. It will be interpreted the same way as perf
|
||||
record.
|
||||
|
||||
-K::
|
||||
--all-kernel::
|
||||
Configure all used events to run in kernel space.
|
||||
|
||||
-U::
|
||||
--all-user::
|
||||
Configure all used events to run in user space.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
||||
|
|
|
@ -285,7 +285,7 @@ OPTIONS
|
|||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
Don't do ownership validation.
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
|
|
@ -262,6 +262,10 @@ include::itrace.txt[]
|
|||
--ns::
|
||||
Use 9 decimal places when displaying time (i.e. show the nanoseconds)
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Don't do ownership validation.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script-perl[1],
|
||||
|
|
|
@ -62,19 +62,22 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
|||
int rec_argc, i = 0, j;
|
||||
const char **rec_argv;
|
||||
int ret;
|
||||
bool all_user = false, all_kernel = false;
|
||||
struct option options[] = {
|
||||
OPT_CALLBACK('e', "event", &mem, "event",
|
||||
"event selector. use 'perf mem record -e list' to list available events",
|
||||
parse_record_events),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_BOOLEAN('U', "--all-user", &all_user, "collect only user level data"),
|
||||
OPT_BOOLEAN('K', "--all-kernel", &all_kernel, "collect only kernel level data"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, record_mem_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
rec_argc = argc + 7; /* max number of arguments */
|
||||
rec_argc = argc + 9; /* max number of arguments */
|
||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||
if (!rec_argv)
|
||||
return -1;
|
||||
|
@ -103,6 +106,12 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
|||
rec_argv[i++] = perf_mem_events__name(j);
|
||||
};
|
||||
|
||||
if (all_user)
|
||||
rec_argv[i++] = "--all-user";
|
||||
|
||||
if (all_kernel)
|
||||
rec_argv[i++] = "--all-kernel";
|
||||
|
||||
for (j = 0; j < argc; j++, i++)
|
||||
rec_argv[i] = argv[j];
|
||||
|
||||
|
|
|
@ -549,6 +549,7 @@ int main(int argc, const char **argv)
|
|||
srandom(time(NULL));
|
||||
|
||||
perf_config(perf_default_config, NULL);
|
||||
set_buildid_dir(NULL);
|
||||
|
||||
/* get debugfs/tracefs mount point from /proc/mounts */
|
||||
tracing_path_mount();
|
||||
|
@ -572,7 +573,6 @@ int main(int argc, const char **argv)
|
|||
}
|
||||
if (!prefixcmp(cmd, "trace")) {
|
||||
#ifdef HAVE_LIBAUDIT_SUPPORT
|
||||
set_buildid_dir(NULL);
|
||||
setup_path();
|
||||
argv[0] = "trace";
|
||||
return cmd_trace(argc, argv, NULL);
|
||||
|
@ -587,7 +587,6 @@ int main(int argc, const char **argv)
|
|||
argc--;
|
||||
handle_options(&argv, &argc, NULL);
|
||||
commit_pager_choice();
|
||||
set_buildid_dir(NULL);
|
||||
|
||||
if (argc > 0) {
|
||||
if (!prefixcmp(argv[0], "--"))
|
||||
|
|
|
@ -37,6 +37,7 @@ perf-y += topology.o
|
|||
perf-y += cpumap.o
|
||||
perf-y += stat.o
|
||||
perf-y += event_update.o
|
||||
perf-y += event-times.o
|
||||
|
||||
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
|
||||
$(call rule_mkdir)
|
||||
|
|
|
@ -203,6 +203,10 @@ static struct test generic_tests[] = {
|
|||
.desc = "Test attr update synthesize",
|
||||
.func = test__event_update,
|
||||
},
|
||||
{
|
||||
.desc = "Test events times",
|
||||
.func = test__event_times,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
#include <linux/compiler.h>
|
||||
#include <string.h>
|
||||
#include "tests.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "thread_map.h"
|
||||
#include "target.h"
|
||||
|
||||
static int attach__enable_on_exec(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct target target = {
|
||||
.uid = UINT_MAX,
|
||||
};
|
||||
const char *argv[] = { "true", NULL, };
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to spawned child, enable on exec\n");
|
||||
|
||||
err = perf_evlist__create_maps(evlist, &target);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create thread/cpu maps\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = perf_evlist__prepare_workload(evlist, &target, argv, false, NULL);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't run the workload!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
evsel->attr.enable_on_exec = 1;
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
return err;
|
||||
}
|
||||
|
||||
return perf_evlist__start_workload(evlist) == 1 ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
static int detach__enable_on_exec(struct perf_evlist *evlist)
|
||||
{
|
||||
waitpid(evlist->workload.pid, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int attach__current_disabled(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct thread_map *threads;
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to current thread as disabled\n");
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
evsel->attr.disabled = 1;
|
||||
|
||||
err = perf_evsel__open_per_thread(evsel, threads);
|
||||
if (err) {
|
||||
pr_debug("Failed to open event cpu-clock:u\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
thread_map__put(threads);
|
||||
return perf_evsel__enable(evsel) == 0 ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
static int attach__current_enabled(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct thread_map *threads;
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to current thread as enabled\n");
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("failed to call thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = perf_evsel__open_per_thread(evsel, threads);
|
||||
|
||||
thread_map__put(threads);
|
||||
return err == 0 ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
static int detach__disable(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
|
||||
return perf_evsel__enable(evsel);
|
||||
}
|
||||
|
||||
static int attach__cpu_disabled(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct cpu_map *cpus;
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to CPU 0 as enabled\n");
|
||||
|
||||
cpus = cpu_map__new("0");
|
||||
if (cpus == NULL) {
|
||||
pr_debug("failed to call cpu_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
evsel->attr.disabled = 1;
|
||||
|
||||
err = perf_evsel__open_per_cpu(evsel, cpus);
|
||||
if (err) {
|
||||
if (err == -EACCES)
|
||||
return TEST_SKIP;
|
||||
|
||||
pr_debug("Failed to open event cpu-clock:u\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
cpu_map__put(cpus);
|
||||
return perf_evsel__enable(evsel);
|
||||
}
|
||||
|
||||
static int attach__cpu_enabled(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct cpu_map *cpus;
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to CPU 0 as enabled\n");
|
||||
|
||||
cpus = cpu_map__new("0");
|
||||
if (cpus == NULL) {
|
||||
pr_debug("failed to call cpu_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = perf_evsel__open_per_cpu(evsel, cpus);
|
||||
if (err == -EACCES)
|
||||
return TEST_SKIP;
|
||||
|
||||
cpu_map__put(cpus);
|
||||
return err ? TEST_FAIL : TEST_OK;
|
||||
}
|
||||
|
||||
static int test_times(int (attach)(struct perf_evlist *),
|
||||
int (detach)(struct perf_evlist *))
|
||||
{
|
||||
struct perf_counts_values count;
|
||||
struct perf_evlist *evlist = NULL;
|
||||
struct perf_evsel *evsel;
|
||||
int err = -1, i;
|
||||
|
||||
evlist = perf_evlist__new();
|
||||
if (!evlist) {
|
||||
pr_debug("failed to create event list\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = parse_events(evlist, "cpu-clock:u", NULL);
|
||||
if (err) {
|
||||
pr_debug("failed to parse event cpu-clock:u\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
evsel = perf_evlist__last(evlist);
|
||||
evsel->attr.read_format |=
|
||||
PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING;
|
||||
|
||||
err = attach(evlist);
|
||||
if (err == TEST_SKIP) {
|
||||
pr_debug(" SKIP : not enough rights\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
TEST_ASSERT_VAL("failed to attach", !err);
|
||||
|
||||
for (i = 0; i < 100000000; i++) { }
|
||||
|
||||
TEST_ASSERT_VAL("failed to detach", !detach(evlist));
|
||||
|
||||
perf_evsel__read(evsel, 0, 0, &count);
|
||||
|
||||
err = !(count.ena == count.run);
|
||||
|
||||
pr_debug(" %s: ena %" PRIu64", run %" PRIu64"\n",
|
||||
!err ? "OK " : "FAILED",
|
||||
count.ena, count.run);
|
||||
|
||||
out_err:
|
||||
if (evlist)
|
||||
perf_evlist__delete(evlist);
|
||||
return !err ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This test creates software event 'cpu-clock'
|
||||
* attaches it in several ways (explained below)
|
||||
* and checks that enabled and running times
|
||||
* match.
|
||||
*/
|
||||
int test__event_times(int subtest __maybe_unused)
|
||||
{
|
||||
int err, ret = 0;
|
||||
|
||||
#define _T(attach, detach) \
|
||||
err = test_times(attach, detach); \
|
||||
if (err && (ret == TEST_OK || ret == TEST_SKIP)) \
|
||||
ret = err;
|
||||
|
||||
/* attach on newly spawned process after exec */
|
||||
_T(attach__enable_on_exec, detach__enable_on_exec)
|
||||
/* attach on current process as enabled */
|
||||
_T(attach__current_enabled, detach__disable)
|
||||
/* attach on current process as disabled */
|
||||
_T(attach__current_disabled, detach__disable)
|
||||
/* attach on cpu as disabled */
|
||||
_T(attach__cpu_disabled, detach__disable)
|
||||
/* attach on cpu as enabled */
|
||||
_T(attach__cpu_enabled, detach__disable)
|
||||
|
||||
#undef _T
|
||||
return ret;
|
||||
}
|
|
@ -85,6 +85,7 @@ int test__synthesize_stat_config(int subtest);
|
|||
int test__synthesize_stat(int subtest);
|
||||
int test__synthesize_stat_round(int subtest);
|
||||
int test__event_update(int subtest);
|
||||
int test__event_times(int subtest);
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
|
|
|
@ -940,6 +940,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts)
|
|||
synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
|
||||
synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
|
||||
synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ;
|
||||
synth_opts->initial_skip = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1064,6 +1065,12 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
|
|||
synth_opts->last_branch_sz = val;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
synth_opts->initial_skip = strtoul(p, &endptr, 10);
|
||||
if (p == endptr)
|
||||
goto out_err;
|
||||
p = endptr;
|
||||
break;
|
||||
case ' ':
|
||||
case ',':
|
||||
break;
|
||||
|
|
|
@ -68,6 +68,7 @@ enum itrace_period_type {
|
|||
* @last_branch_sz: branch context size
|
||||
* @period: 'instructions' events period
|
||||
* @period_type: 'instructions' events period type
|
||||
* @initial_skip: skip N events at the beginning.
|
||||
*/
|
||||
struct itrace_synth_opts {
|
||||
bool set;
|
||||
|
@ -86,6 +87,7 @@ struct itrace_synth_opts {
|
|||
unsigned int last_branch_sz;
|
||||
unsigned long long period;
|
||||
enum itrace_period_type period_type;
|
||||
unsigned long initial_skip;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -377,6 +377,21 @@ const char *perf_config_dirname(const char *name, const char *value)
|
|||
return value;
|
||||
}
|
||||
|
||||
static int perf_buildid_config(const char *var, const char *value)
|
||||
{
|
||||
/* same dir for all commands */
|
||||
if (!strcmp(var, "buildid.dir")) {
|
||||
const char *dirname = perf_config_dirname(var, value);
|
||||
|
||||
if (!dirname)
|
||||
return -1;
|
||||
strncpy(buildid_dir, dirname, MAXPATHLEN-1);
|
||||
buildid_dir[MAXPATHLEN-1] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_default_core_config(const char *var __maybe_unused,
|
||||
const char *value __maybe_unused)
|
||||
{
|
||||
|
@ -412,6 +427,9 @@ int perf_default_config(const char *var, const char *value,
|
|||
if (!prefixcmp(var, "llvm."))
|
||||
return perf_llvm_config(var, value);
|
||||
|
||||
if (!prefixcmp(var, "buildid."))
|
||||
return perf_buildid_config(var, value);
|
||||
|
||||
/* Add other config variables here. */
|
||||
return 0;
|
||||
}
|
||||
|
@ -515,49 +533,18 @@ int config_error_nonbool(const char *var)
|
|||
return error("Missing value for '%s'", var);
|
||||
}
|
||||
|
||||
struct buildid_dir_config {
|
||||
char *dir;
|
||||
};
|
||||
|
||||
static int buildid_dir_command_config(const char *var, const char *value,
|
||||
void *data)
|
||||
{
|
||||
struct buildid_dir_config *c = data;
|
||||
const char *v;
|
||||
|
||||
/* same dir for all commands */
|
||||
if (!strcmp(var, "buildid.dir")) {
|
||||
v = perf_config_dirname(var, value);
|
||||
if (!v)
|
||||
return -1;
|
||||
strncpy(c->dir, v, MAXPATHLEN-1);
|
||||
c->dir[MAXPATHLEN-1] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_buildid_dir_config(void)
|
||||
{
|
||||
struct buildid_dir_config c;
|
||||
c.dir = buildid_dir;
|
||||
perf_config(buildid_dir_command_config, &c);
|
||||
}
|
||||
|
||||
void set_buildid_dir(const char *dir)
|
||||
{
|
||||
if (dir)
|
||||
scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir);
|
||||
|
||||
/* try config file */
|
||||
if (buildid_dir[0] == '\0')
|
||||
check_buildid_dir_config();
|
||||
|
||||
/* default to $HOME/.debug */
|
||||
if (buildid_dir[0] == '\0') {
|
||||
char *v = getenv("HOME");
|
||||
if (v) {
|
||||
char *home = getenv("HOME");
|
||||
|
||||
if (home) {
|
||||
snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
|
||||
v, DEBUG_CACHE_DIR);
|
||||
home, DEBUG_CACHE_DIR);
|
||||
} else {
|
||||
strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
|
||||
}
|
||||
|
|
|
@ -1295,7 +1295,8 @@ static int hists__hierarchy_insert_entry(struct hists *hists,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root,
|
||||
static int hists__collapse_insert_entry(struct hists *hists,
|
||||
struct rb_root *root,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
|
|
|
@ -199,8 +199,6 @@ int hists__init(void);
|
|||
int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list);
|
||||
|
||||
struct rb_root *hists__get_rotate_entries_in(struct hists *hists);
|
||||
int hists__collapse_insert_entry(struct hists *hists,
|
||||
struct rb_root *root, struct hist_entry *he);
|
||||
|
||||
struct perf_hpp {
|
||||
char *buf;
|
||||
|
|
|
@ -66,6 +66,7 @@ struct intel_bts {
|
|||
u64 branches_id;
|
||||
size_t branches_event_size;
|
||||
bool synth_needs_swap;
|
||||
unsigned long num_events;
|
||||
};
|
||||
|
||||
struct intel_bts_queue {
|
||||
|
@ -275,6 +276,10 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq,
|
|||
union perf_event event;
|
||||
struct perf_sample sample = { .ip = 0, };
|
||||
|
||||
if (bts->synth_opts.initial_skip &&
|
||||
bts->num_events++ <= bts->synth_opts.initial_skip)
|
||||
return 0;
|
||||
|
||||
event.sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event.sample.header.misc = PERF_RECORD_MISC_USER;
|
||||
event.sample.header.size = sizeof(struct perf_event_header);
|
||||
|
|
|
@ -100,6 +100,8 @@ struct intel_pt {
|
|||
u64 cyc_bit;
|
||||
u64 noretcomp_bit;
|
||||
unsigned max_non_turbo_ratio;
|
||||
|
||||
unsigned long num_events;
|
||||
};
|
||||
|
||||
enum switch_state {
|
||||
|
@ -972,6 +974,10 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)
|
|||
if (pt->branches_filter && !(pt->branches_filter & ptq->flags))
|
||||
return 0;
|
||||
|
||||
if (pt->synth_opts.initial_skip &&
|
||||
pt->num_events++ < pt->synth_opts.initial_skip)
|
||||
return 0;
|
||||
|
||||
event->sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event->sample.header.misc = PERF_RECORD_MISC_USER;
|
||||
event->sample.header.size = sizeof(struct perf_event_header);
|
||||
|
@ -1029,6 +1035,10 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)
|
|||
union perf_event *event = ptq->event_buf;
|
||||
struct perf_sample sample = { .ip = 0, };
|
||||
|
||||
if (pt->synth_opts.initial_skip &&
|
||||
pt->num_events++ < pt->synth_opts.initial_skip)
|
||||
return 0;
|
||||
|
||||
event->sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event->sample.header.misc = PERF_RECORD_MISC_USER;
|
||||
event->sample.header.size = sizeof(struct perf_event_header);
|
||||
|
@ -1087,6 +1097,10 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq)
|
|||
union perf_event *event = ptq->event_buf;
|
||||
struct perf_sample sample = { .ip = 0, };
|
||||
|
||||
if (pt->synth_opts.initial_skip &&
|
||||
pt->num_events++ < pt->synth_opts.initial_skip)
|
||||
return 0;
|
||||
|
||||
event->sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event->sample.header.misc = PERF_RECORD_MISC_USER;
|
||||
event->sample.header.size = sizeof(struct perf_event_header);
|
||||
|
@ -1199,14 +1213,18 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
|
|||
ptq->have_sample = false;
|
||||
|
||||
if (pt->sample_instructions &&
|
||||
(state->type & INTEL_PT_INSTRUCTION)) {
|
||||
(state->type & INTEL_PT_INSTRUCTION) &&
|
||||
(!pt->synth_opts.initial_skip ||
|
||||
pt->num_events++ >= pt->synth_opts.initial_skip)) {
|
||||
err = intel_pt_synth_instruction_sample(ptq);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (pt->sample_transactions &&
|
||||
(state->type & INTEL_PT_TRANSACTION)) {
|
||||
(state->type & INTEL_PT_TRANSACTION) &&
|
||||
(!pt->synth_opts.initial_skip ||
|
||||
pt->num_events++ >= pt->synth_opts.initial_skip)) {
|
||||
err = intel_pt_synth_transaction_sample(ptq);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <perl.h>
|
||||
|
||||
#include "../../perf.h"
|
||||
#include "../callchain.h"
|
||||
#include "../machine.h"
|
||||
#include "../thread.h"
|
||||
#include "../event.h"
|
||||
#include "../trace-event.h"
|
||||
|
@ -248,10 +250,78 @@ static void define_event_symbols(struct event_format *event,
|
|||
define_event_symbols(event, ev_name, args->next);
|
||||
}
|
||||
|
||||
static SV *perl_process_callchain(struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct addr_location *al)
|
||||
{
|
||||
AV *list;
|
||||
|
||||
list = newAV();
|
||||
if (!list)
|
||||
goto exit;
|
||||
|
||||
if (!symbol_conf.use_callchain || !sample->callchain)
|
||||
goto exit;
|
||||
|
||||
if (thread__resolve_callchain(al->thread, evsel,
|
||||
sample, NULL, NULL,
|
||||
PERF_MAX_STACK_DEPTH) != 0) {
|
||||
pr_err("Failed to resolve callchain. Skipping\n");
|
||||
goto exit;
|
||||
}
|
||||
callchain_cursor_commit(&callchain_cursor);
|
||||
|
||||
|
||||
while (1) {
|
||||
HV *elem;
|
||||
struct callchain_cursor_node *node;
|
||||
node = callchain_cursor_current(&callchain_cursor);
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
elem = newHV();
|
||||
if (!elem)
|
||||
goto exit;
|
||||
|
||||
hv_stores(elem, "ip", newSVuv(node->ip));
|
||||
|
||||
if (node->sym) {
|
||||
HV *sym = newHV();
|
||||
if (!sym)
|
||||
goto exit;
|
||||
hv_stores(sym, "start", newSVuv(node->sym->start));
|
||||
hv_stores(sym, "end", newSVuv(node->sym->end));
|
||||
hv_stores(sym, "binding", newSVuv(node->sym->binding));
|
||||
hv_stores(sym, "name", newSVpvn(node->sym->name,
|
||||
node->sym->namelen));
|
||||
hv_stores(elem, "sym", newRV_noinc((SV*)sym));
|
||||
}
|
||||
|
||||
if (node->map) {
|
||||
struct map *map = node->map;
|
||||
const char *dsoname = "[unknown]";
|
||||
if (map && map->dso && (map->dso->name || map->dso->long_name)) {
|
||||
if (symbol_conf.show_kernel_path && map->dso->long_name)
|
||||
dsoname = map->dso->long_name;
|
||||
else if (map->dso->name)
|
||||
dsoname = map->dso->name;
|
||||
}
|
||||
hv_stores(elem, "dso", newSVpv(dsoname,0));
|
||||
}
|
||||
|
||||
callchain_cursor_advance(&callchain_cursor);
|
||||
av_push(list, newRV_noinc((SV*)elem));
|
||||
}
|
||||
|
||||
exit:
|
||||
return newRV_noinc((SV*)list);
|
||||
}
|
||||
|
||||
static void perl_process_tracepoint(struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct thread *thread)
|
||||
struct addr_location *al)
|
||||
{
|
||||
struct thread *thread = al->thread;
|
||||
struct event_format *event = evsel->tp_format;
|
||||
struct format_field *field;
|
||||
static char handler[256];
|
||||
|
@ -295,6 +365,7 @@ static void perl_process_tracepoint(struct perf_sample *sample,
|
|||
XPUSHs(sv_2mortal(newSVuv(ns)));
|
||||
XPUSHs(sv_2mortal(newSViv(pid)));
|
||||
XPUSHs(sv_2mortal(newSVpv(comm, 0)));
|
||||
XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al)));
|
||||
|
||||
/* common fields other than pid can be accessed via xsub fns */
|
||||
|
||||
|
@ -329,6 +400,7 @@ static void perl_process_tracepoint(struct perf_sample *sample,
|
|||
XPUSHs(sv_2mortal(newSVuv(nsecs)));
|
||||
XPUSHs(sv_2mortal(newSViv(pid)));
|
||||
XPUSHs(sv_2mortal(newSVpv(comm, 0)));
|
||||
XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al)));
|
||||
call_pv("main::trace_unhandled", G_SCALAR);
|
||||
}
|
||||
SPAGAIN;
|
||||
|
@ -366,7 +438,7 @@ static void perl_process_event(union perf_event *event,
|
|||
struct perf_evsel *evsel,
|
||||
struct addr_location *al)
|
||||
{
|
||||
perl_process_tracepoint(sample, evsel, al->thread);
|
||||
perl_process_tracepoint(sample, evsel, al);
|
||||
perl_process_event_generic(event, sample, evsel);
|
||||
}
|
||||
|
||||
|
@ -490,7 +562,27 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
|
|||
fprintf(ofp, "use Perf::Trace::Util;\n\n");
|
||||
|
||||
fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n");
|
||||
fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n");
|
||||
fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n");
|
||||
|
||||
|
||||
fprintf(ofp, "\n\
|
||||
sub print_backtrace\n\
|
||||
{\n\
|
||||
my $callchain = shift;\n\
|
||||
for my $node (@$callchain)\n\
|
||||
{\n\
|
||||
if(exists $node->{sym})\n\
|
||||
{\n\
|
||||
printf( \"\\t[\\%%x] \\%%s\\n\", $node->{ip}, $node->{sym}{name});\n\
|
||||
}\n\
|
||||
else\n\
|
||||
{\n\
|
||||
printf( \"\\t[\\%%x]\\n\", $node{ip});\n\
|
||||
}\n\
|
||||
}\n\
|
||||
}\n\n\
|
||||
");
|
||||
|
||||
|
||||
while ((event = trace_find_next_event(pevent, event))) {
|
||||
fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);
|
||||
|
@ -502,7 +594,8 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
|
|||
fprintf(ofp, "$common_secs, ");
|
||||
fprintf(ofp, "$common_nsecs,\n");
|
||||
fprintf(ofp, "\t $common_pid, ");
|
||||
fprintf(ofp, "$common_comm,\n\t ");
|
||||
fprintf(ofp, "$common_comm, ");
|
||||
fprintf(ofp, "$common_callchain,\n\t ");
|
||||
|
||||
not_first = 0;
|
||||
count = 0;
|
||||
|
@ -519,7 +612,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
|
|||
|
||||
fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
|
||||
"$common_secs, $common_nsecs,\n\t "
|
||||
"$common_pid, $common_comm);\n\n");
|
||||
"$common_pid, $common_comm, $common_callchain);\n\n");
|
||||
|
||||
fprintf(ofp, "\tprintf(\"");
|
||||
|
||||
|
@ -581,17 +674,22 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
|
|||
fprintf(ofp, "$%s", f->name);
|
||||
}
|
||||
|
||||
fprintf(ofp, ");\n");
|
||||
fprintf(ofp, ");\n\n");
|
||||
|
||||
fprintf(ofp, "\tprint_backtrace($common_callchain);\n");
|
||||
|
||||
fprintf(ofp, "}\n\n");
|
||||
}
|
||||
|
||||
fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, "
|
||||
"$common_cpu, $common_secs, $common_nsecs,\n\t "
|
||||
"$common_pid, $common_comm) = @_;\n\n");
|
||||
"$common_pid, $common_comm, $common_callchain) = @_;\n\n");
|
||||
|
||||
fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
|
||||
"$common_secs, $common_nsecs,\n\t $common_pid, "
|
||||
"$common_comm);\n}\n\n");
|
||||
"$common_comm, $common_callchain);\n");
|
||||
fprintf(ofp, "\tprint_backtrace($common_callchain);\n");
|
||||
fprintf(ofp, "}\n\n");
|
||||
|
||||
fprintf(ofp, "sub print_header\n{\n"
|
||||
"\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n"
|
||||
|
|
Загрузка…
Ссылка в новой задаче