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:
Ingo Molnar 2016-03-31 08:33:43 +02:00
commit 643cb15ba0
21 changed files with 448 additions and 55 deletions

View file

@ -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
-----------

View file

@ -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.

View file

@ -33,7 +33,7 @@ OPTIONS
-f::
--force::
Don't complain, do it.
Don't do ownership validation.
-v::
--verbose::

View file

@ -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.

View file

@ -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]

View file

@ -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.

View file

@ -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],

View file

@ -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];

View file

@ -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], "--"))

View file

@ -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)

View file

@ -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,
},

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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;
};
/**

View file

@ -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);
}

View file

@ -1295,8 +1295,9 @@ static int hists__hierarchy_insert_entry(struct hists *hists,
return ret;
}
int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root,
struct hist_entry *he)
static int hists__collapse_insert_entry(struct hists *hists,
struct rb_root *root,
struct hist_entry *he)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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"