2019-05-29 22:18:02 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2015-04-09 23:53:42 +08:00
|
|
|
/*
|
|
|
|
* auxtrace.c: AUX area trace support
|
|
|
|
* Copyright (c) 2013-2015, Intel Corporation.
|
|
|
|
*/
|
|
|
|
|
2017-04-18 02:23:08 +08:00
|
|
|
#include <inttypes.h>
|
2015-04-09 23:53:42 +08:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <stdbool.h>
|
2016-09-23 22:38:39 +08:00
|
|
|
#include <string.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <errno.h>
|
2015-04-09 23:53:42 +08:00
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/perf_event.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/log2.h>
|
2015-04-21 17:21:51 +08:00
|
|
|
#include <linux/string.h>
|
2019-02-06 18:39:47 +08:00
|
|
|
#include <linux/time64.h>
|
2015-04-09 23:53:42 +08:00
|
|
|
|
2015-04-21 17:21:51 +08:00
|
|
|
#include <sys/param.h>
|
2015-04-09 23:53:44 +08:00
|
|
|
#include <stdlib.h>
|
2015-04-09 23:53:50 +08:00
|
|
|
#include <stdio.h>
|
2015-04-21 17:21:51 +08:00
|
|
|
#include <linux/list.h>
|
2019-07-04 22:32:27 +08:00
|
|
|
#include <linux/zalloc.h>
|
2015-04-09 23:53:44 +08:00
|
|
|
|
2015-04-09 23:53:42 +08:00
|
|
|
#include "evlist.h"
|
2016-09-23 22:38:39 +08:00
|
|
|
#include "dso.h"
|
|
|
|
#include "map.h"
|
|
|
|
#include "pmu.h"
|
|
|
|
#include "evsel.h"
|
2019-11-15 20:42:17 +08:00
|
|
|
#include "evsel_config.h"
|
2019-01-27 21:10:31 +08:00
|
|
|
#include "symbol.h"
|
2020-05-05 22:49:08 +08:00
|
|
|
#include "util/perf_api_probe.h"
|
2019-09-18 22:36:13 +08:00
|
|
|
#include "util/synthetic-events.h"
|
2015-04-09 23:53:42 +08:00
|
|
|
#include "thread_map.h"
|
|
|
|
#include "asm/bug.h"
|
|
|
|
#include "auxtrace.h"
|
|
|
|
|
2015-04-09 23:53:54 +08:00
|
|
|
#include <linux/hash.h>
|
|
|
|
|
2015-04-09 23:53:44 +08:00
|
|
|
#include "event.h"
|
2019-08-23 02:40:29 +08:00
|
|
|
#include "record.h"
|
2015-04-09 23:53:50 +08:00
|
|
|
#include "session.h"
|
2015-04-09 23:53:44 +08:00
|
|
|
#include "debug.h"
|
2015-12-15 23:39:39 +08:00
|
|
|
#include <subcmd/parse-options.h>
|
2015-04-09 23:53:44 +08:00
|
|
|
|
2018-01-18 01:52:11 +08:00
|
|
|
#include "cs-etm.h"
|
2015-07-18 00:33:42 +08:00
|
|
|
#include "intel-pt.h"
|
perf tools: Add Intel BTS support
Intel BTS support fits within the new auxtrace infrastructure. Recording is
supporting by identifying the Intel BTS PMU, parsing options and setting up
events.
Decoding is supported by queuing up trace data by thread and then decoding
synchronously delivering synthesized event samples into the session processing
for tools to consume.
Committer note:
E.g:
[root@felicio ~]# perf record --per-thread -e intel_bts// ls
anaconda-ks.cfg apctest.output bin kernel-rt-3.10.0-298.rt56.171.el7.x86_64.rpm libexec lock_page.bpf.c perf.data perf.data.old
[ perf record: Woken up 3 times to write data ]
[ perf record: Captured and wrote 4.367 MB perf.data ]
[root@felicio ~]# perf evlist -v
intel_bts//: type: 6, size: 112, { sample_period, sample_freq }: 1, sample_type: IP|TID|IDENTIFIER, read_format: ID, disabled: 1, enable_on_exec: 1, sample_id_all: 1, exclude_guest: 1
dummy:u: type: 1, size: 112, config: 0x9, { sample_period, sample_freq }: 1, sample_type: IP|TID|IDENTIFIER, read_format: ID, disabled: 1, exclude_kernel: 1, exclude_hv: 1, mmap: 1, comm: 1, enable_on_exec: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1
[root@felicio ~]# perf script # the navigate in the pager to some interesting place:
ls 1843 1 branches: ffffffff810a60cb flush_signal_handlers ([kernel.kallsyms]) => ffffffff8121a522 setup_new_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8121a529 setup_new_exec ([kernel.kallsyms]) => ffffffff8122fa30 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa5d do_close_on_exec ([kernel.kallsyms]) => ffffffff81767ae0 _raw_spin_lock ([kernel.kallsyms])
ls 1843 1 branches: ffffffff81767af4 _raw_spin_lock ([kernel.kallsyms]) => ffffffff8122fa62 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fac9 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fad2 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fadd do_close_on_exec ([kernel.kallsyms]) => ffffffff8120fc80 filp_close ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8120fcaf filp_close ([kernel.kallsyms]) => ffffffff8120fcb6 filp_close ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8120fcc2 filp_close ([kernel.kallsyms]) => ffffffff812547f0 dnotify_flush ([kernel.kallsyms])
ls 1843 1 branches: ffffffff81254823 dnotify_flush ([kernel.kallsyms]) => ffffffff8120fcc7 filp_close ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8120fccd filp_close ([kernel.kallsyms]) => ffffffff81261790 locks_remove_posix ([kernel.kallsyms])
ls 1843 1 branches: ffffffff812617a3 locks_remove_posix ([kernel.kallsyms]) => ffffffff812617b9 locks_remove_posix ([kernel.kallsyms])
ls 1843 1 branches: ffffffff812617b9 locks_remove_posix ([kernel.kallsyms]) => ffffffff8120fcd2 filp_close ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8120fcd5 filp_close ([kernel.kallsyms]) => ffffffff812142c0 fput ([kernel.kallsyms])
ls 1843 1 branches: ffffffff812142d6 fput ([kernel.kallsyms]) => ffffffff812142df fput ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8121430c fput ([kernel.kallsyms]) => ffffffff810b6580 task_work_add ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810b65ad task_work_add ([kernel.kallsyms]) => ffffffff810b65b1 task_work_add ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810b65c1 task_work_add ([kernel.kallsyms]) => ffffffff810bc710 kick_process ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810bc725 kick_process ([kernel.kallsyms]) => ffffffff810bc742 kick_process ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810bc742 kick_process ([kernel.kallsyms]) => ffffffff810b65c6 task_work_add ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810b65c9 task_work_add ([kernel.kallsyms]) => ffffffff81214311 fput ([kernel.kallsyms])
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/r/1437150840-31811-9-git-send-email-adrian.hunter@intel.com
[ Merged sample->time fix for bug found after first round of testing on slightly older kernel ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-18 00:33:43 +08:00
|
|
|
#include "intel-bts.h"
|
2018-01-15 03:28:50 +08:00
|
|
|
#include "arm-spe.h"
|
perf auxtrace: Support for perf report -D for s390
Add initial support for s390 auxiliary traces using the CPU-Measurement
Sampling Facility.
Support and ignore PERF_REPORT_AUXTRACE_INFO records in the perf data
file. Later patches will show the contents of the auxiliary traces.
Setup the auxtrace queues and data structures for s390. A raw dump of
the perf.data file now does not show an error when an auxtrace event is
encountered.
Output before:
[root@s35lp76 perf]# ./perf report -D -i perf.data.auxtrace
0x128 [0x10]: failed to process type: 70
Error:
failed to process sample
0x128 [0x10]: event: 70
.
. ... raw event: size 16 bytes
. 0000: 00 00 00 46 00 00 00 10 00 00 00 00 00 00 00 00 ...F............
0x128 [0x10]: PERF_RECORD_AUXTRACE_INFO type: 0
[root@s35lp76 perf]#
Output after:
# ./perf report -D -i perf.data.auxtrace |fgrep PERF_RECORD_AUXTRACE
0 0 0x128 [0x10]: PERF_RECORD_AUXTRACE_INFO type: 5
0 0 0x25a66 [0x30]: PERF_RECORD_AUXTRACE size: 0x40000
offset: 0 ref: 0 idx: 4 tid: -1 cpu: 4
....
Additional notes about the underlying hardware and software
implementation, provided by Hendrik Brueckner (see Link: below).
=============================================================================
The CPU-Measurement Facility (CPU-MF) provides a set of functions to obtain
performance information on the mainframe. Basically, it was introduced
with System z10 years ago for the z/Architecture, that means, 64-bit.
For Linux, there are two facilities of interest, counter facility and sampling
facility. The counter facility provides hardware counters for instructions,
cycles, crypto-activities, and many more.
The sampling facility is a hardware sampler that when started will write
samples at a particular interval into a sampling buffer. At some point,
for example, if a sample block is full, it generates an interrupt to collect
samples (while the sampler continues to run).
Few years ago, I started to provide the a perf PMU to use the counter
and sampling facilities. Recently, the device driver was updated to also
"export" the sampling buffer into the AUX area. Thomas now completed the
related perf work to interpret and process these AUX data.
If people are more interested in the sampling facility, they can have a
look into:
- The Load-Program-Parameter and the CPU-Measurement Facilities, SA23-2260-05
http://www-01.ibm.com/support/docview.wss?uid=isg26fcd1cc32246f4c8852574ce0044734a
and to learn how-to use it for Linux on Z, have look at chapter 54,
"Using the CPU-measurement facilities" in the:
- Device Drivers, Features, and Commands, SC33-8411-34
http://public.dhe.ibm.com/software/dw/linux390/docu/l416dd34.pdf
=============================================================================
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Link: http://lkml.kernel.org/r/20180803100758.GA28475@linux.ibm.com
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Link: http://lkml.kernel.org/r/20180802074622.13641-2-tmricht@linux.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-08-02 15:46:20 +08:00
|
|
|
#include "s390-cpumsf.h"
|
2019-09-23 23:20:38 +08:00
|
|
|
#include "util/mmap.h"
|
2015-07-18 00:33:42 +08:00
|
|
|
|
tools perf: Move from sane_ctype.h obtained from git to the Linux's original
We got the sane_ctype.h headers from git and kept using it so far, but
since that code originally came from the kernel sources to the git
sources, perhaps its better to just use the one in the kernel, so that
we can leverage tools/perf/check_headers.sh to be notified when our copy
gets out of sync, i.e. when fixes or goodies are added to the code we've
copied.
This will help with things like tools/lib/string.c where we want to have
more things in common with the kernel, such as strim(), skip_spaces(),
etc so as to go on removing the things that we have in tools/perf/util/
and instead using the code in the kernel, indirectly and removing things
like EXPORT_SYMBOL(), etc, getting notified when fixes and improvements
are made to the original code.
Hopefully this also should help with reducing the difference of code
hosted in tools/ to the one in the kernel proper.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: https://lkml.kernel.org/n/tip-7k9868l713wqtgo01xxygn12@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-06-26 04:27:31 +08:00
|
|
|
#include <linux/ctype.h>
|
2017-04-18 03:10:49 +08:00
|
|
|
#include "symbol/kallsyms.h"
|
2019-09-03 21:56:06 +08:00
|
|
|
#include <internal/lib.h>
|
2017-04-18 03:10:49 +08:00
|
|
|
|
2019-11-15 20:42:17 +08:00
|
|
|
/*
|
|
|
|
* Make a group from 'leader' to 'last', requiring that the events were not
|
|
|
|
* already grouped to a different leader.
|
|
|
|
*/
|
2020-12-01 01:58:32 +08:00
|
|
|
static int evlist__regroup(struct evlist *evlist, struct evsel *leader, struct evsel *last)
|
2019-11-15 20:42:17 +08:00
|
|
|
{
|
|
|
|
struct evsel *evsel;
|
|
|
|
bool grp;
|
|
|
|
|
2020-04-30 21:51:16 +08:00
|
|
|
if (!evsel__is_group_leader(leader))
|
2019-11-15 20:42:17 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
grp = false;
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
|
|
|
if (grp) {
|
2021-07-06 23:17:00 +08:00
|
|
|
if (!(evsel__leader(evsel) == leader ||
|
|
|
|
(evsel__leader(evsel) == evsel &&
|
2019-11-15 20:42:17 +08:00
|
|
|
evsel->core.nr_members <= 1)))
|
|
|
|
return -EINVAL;
|
|
|
|
} else if (evsel == leader) {
|
|
|
|
grp = true;
|
|
|
|
}
|
|
|
|
if (evsel == last)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
grp = false;
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
|
|
|
if (grp) {
|
2021-07-06 23:17:00 +08:00
|
|
|
if (!evsel__has_leader(evsel, leader)) {
|
|
|
|
evsel__set_leader(evsel, leader);
|
2019-11-15 20:42:17 +08:00
|
|
|
if (leader->core.nr_members < 1)
|
|
|
|
leader->core.nr_members = 1;
|
|
|
|
leader->core.nr_members += 1;
|
|
|
|
}
|
|
|
|
} else if (evsel == leader) {
|
|
|
|
grp = true;
|
|
|
|
}
|
|
|
|
if (evsel == last)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-06 17:13:13 +08:00
|
|
|
static bool auxtrace__dont_decode(struct perf_session *session)
|
|
|
|
{
|
|
|
|
return !session->itrace_synth_opts ||
|
|
|
|
session->itrace_synth_opts->dont_decode;
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:42 +08:00
|
|
|
int auxtrace_mmap__mmap(struct auxtrace_mmap *mm,
|
|
|
|
struct auxtrace_mmap_params *mp,
|
|
|
|
void *userpg, int fd)
|
|
|
|
{
|
|
|
|
struct perf_event_mmap_page *pc = userpg;
|
|
|
|
|
|
|
|
WARN_ONCE(mm->base, "Uninitialized auxtrace_mmap\n");
|
|
|
|
|
|
|
|
mm->userpg = userpg;
|
|
|
|
mm->mask = mp->mask;
|
|
|
|
mm->len = mp->len;
|
|
|
|
mm->prev = 0;
|
|
|
|
mm->idx = mp->idx;
|
|
|
|
mm->tid = mp->tid;
|
|
|
|
mm->cpu = mp->cpu;
|
|
|
|
|
|
|
|
if (!mp->len) {
|
|
|
|
mm->base = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pc->aux_offset = mp->offset;
|
|
|
|
pc->aux_size = mp->len;
|
|
|
|
|
|
|
|
mm->base = mmap(NULL, mp->len, mp->prot, MAP_SHARED, fd, mp->offset);
|
|
|
|
if (mm->base == MAP_FAILED) {
|
|
|
|
pr_debug2("failed to mmap AUX area\n");
|
|
|
|
mm->base = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_mmap__munmap(struct auxtrace_mmap *mm)
|
|
|
|
{
|
|
|
|
if (mm->base) {
|
|
|
|
munmap(mm->base, mm->len);
|
|
|
|
mm->base = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp,
|
|
|
|
off_t auxtrace_offset,
|
|
|
|
unsigned int auxtrace_pages,
|
|
|
|
bool auxtrace_overwrite)
|
|
|
|
{
|
|
|
|
if (auxtrace_pages) {
|
|
|
|
mp->offset = auxtrace_offset;
|
|
|
|
mp->len = auxtrace_pages * (size_t)page_size;
|
|
|
|
mp->mask = is_power_of_2(mp->len) ? mp->len - 1 : 0;
|
|
|
|
mp->prot = PROT_READ | (auxtrace_overwrite ? 0 : PROT_WRITE);
|
|
|
|
pr_debug2("AUX area mmap length %zu\n", mp->len);
|
|
|
|
} else {
|
|
|
|
mp->len = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp,
|
2019-07-21 19:23:52 +08:00
|
|
|
struct evlist *evlist, int idx,
|
2015-04-09 23:53:42 +08:00
|
|
|
bool per_cpu)
|
|
|
|
{
|
|
|
|
mp->idx = idx;
|
|
|
|
|
|
|
|
if (per_cpu) {
|
2019-07-21 19:24:41 +08:00
|
|
|
mp->cpu = evlist->core.cpus->map[idx];
|
2019-07-21 19:24:42 +08:00
|
|
|
if (evlist->core.threads)
|
2019-08-22 19:11:41 +08:00
|
|
|
mp->tid = perf_thread_map__pid(evlist->core.threads, 0);
|
2015-04-09 23:53:42 +08:00
|
|
|
else
|
|
|
|
mp->tid = -1;
|
|
|
|
} else {
|
|
|
|
mp->cpu = -1;
|
2019-08-22 19:11:41 +08:00
|
|
|
mp->tid = perf_thread_map__pid(evlist->core.threads, idx);
|
2015-04-09 23:53:42 +08:00
|
|
|
}
|
|
|
|
}
|
2015-04-09 23:53:44 +08:00
|
|
|
|
2015-04-21 17:21:51 +08:00
|
|
|
#define AUXTRACE_INIT_NR_QUEUES 32
|
|
|
|
|
|
|
|
static struct auxtrace_queue *auxtrace_alloc_queue_array(unsigned int nr_queues)
|
|
|
|
{
|
|
|
|
struct auxtrace_queue *queue_array;
|
|
|
|
unsigned int max_nr_queues, i;
|
|
|
|
|
|
|
|
max_nr_queues = UINT_MAX / sizeof(struct auxtrace_queue);
|
|
|
|
if (nr_queues > max_nr_queues)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
queue_array = calloc(nr_queues, sizeof(struct auxtrace_queue));
|
|
|
|
if (!queue_array)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < nr_queues; i++) {
|
|
|
|
INIT_LIST_HEAD(&queue_array[i].head);
|
|
|
|
queue_array[i].priv = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return queue_array;
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_queues__init(struct auxtrace_queues *queues)
|
|
|
|
{
|
|
|
|
queues->nr_queues = AUXTRACE_INIT_NR_QUEUES;
|
|
|
|
queues->queue_array = auxtrace_alloc_queue_array(queues->nr_queues);
|
|
|
|
if (!queues->queue_array)
|
|
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int auxtrace_queues__grow(struct auxtrace_queues *queues,
|
|
|
|
unsigned int new_nr_queues)
|
|
|
|
{
|
|
|
|
unsigned int nr_queues = queues->nr_queues;
|
|
|
|
struct auxtrace_queue *queue_array;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (!nr_queues)
|
|
|
|
nr_queues = AUXTRACE_INIT_NR_QUEUES;
|
|
|
|
|
|
|
|
while (nr_queues && nr_queues < new_nr_queues)
|
|
|
|
nr_queues <<= 1;
|
|
|
|
|
|
|
|
if (nr_queues < queues->nr_queues || nr_queues < new_nr_queues)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
queue_array = auxtrace_alloc_queue_array(nr_queues);
|
|
|
|
if (!queue_array)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < queues->nr_queues; i++) {
|
|
|
|
list_splice_tail(&queues->queue_array[i].head,
|
|
|
|
&queue_array[i].head);
|
2018-08-14 16:46:08 +08:00
|
|
|
queue_array[i].tid = queues->queue_array[i].tid;
|
|
|
|
queue_array[i].cpu = queues->queue_array[i].cpu;
|
|
|
|
queue_array[i].set = queues->queue_array[i].set;
|
2015-04-21 17:21:51 +08:00
|
|
|
queue_array[i].priv = queues->queue_array[i].priv;
|
|
|
|
}
|
|
|
|
|
|
|
|
queues->nr_queues = nr_queues;
|
|
|
|
queues->queue_array = queue_array;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *auxtrace_copy_data(u64 size, struct perf_session *session)
|
|
|
|
{
|
2017-01-24 05:07:59 +08:00
|
|
|
int fd = perf_data__fd(session->data);
|
2015-04-21 17:21:51 +08:00
|
|
|
void *p;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
if (size > SSIZE_MAX)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
p = malloc(size);
|
|
|
|
if (!p)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret = readn(fd, p, size);
|
|
|
|
if (ret != (ssize_t)size) {
|
|
|
|
free(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2018-03-06 17:13:15 +08:00
|
|
|
static int auxtrace_queues__queue_buffer(struct auxtrace_queues *queues,
|
|
|
|
unsigned int idx,
|
|
|
|
struct auxtrace_buffer *buffer)
|
2015-04-21 17:21:51 +08:00
|
|
|
{
|
|
|
|
struct auxtrace_queue *queue;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (idx >= queues->nr_queues) {
|
|
|
|
err = auxtrace_queues__grow(queues, idx + 1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
queue = &queues->queue_array[idx];
|
|
|
|
|
|
|
|
if (!queue->set) {
|
|
|
|
queue->set = true;
|
|
|
|
queue->tid = buffer->tid;
|
|
|
|
queue->cpu = buffer->cpu;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer->buffer_nr = queues->next_buffer_nr++;
|
|
|
|
|
|
|
|
list_add_tail(&buffer->list, &queue->head);
|
|
|
|
|
|
|
|
queues->new_data = true;
|
|
|
|
queues->populated = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Limit buffers to 32MiB on 32-bit */
|
|
|
|
#define BUFFER_LIMIT_FOR_32_BIT (32 * 1024 * 1024)
|
|
|
|
|
|
|
|
static int auxtrace_queues__split_buffer(struct auxtrace_queues *queues,
|
|
|
|
unsigned int idx,
|
|
|
|
struct auxtrace_buffer *buffer)
|
|
|
|
{
|
|
|
|
u64 sz = buffer->size;
|
|
|
|
bool consecutive = false;
|
|
|
|
struct auxtrace_buffer *b;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
while (sz > BUFFER_LIMIT_FOR_32_BIT) {
|
|
|
|
b = memdup(buffer, sizeof(struct auxtrace_buffer));
|
|
|
|
if (!b)
|
|
|
|
return -ENOMEM;
|
|
|
|
b->size = BUFFER_LIMIT_FOR_32_BIT;
|
|
|
|
b->consecutive = consecutive;
|
2018-03-06 17:13:15 +08:00
|
|
|
err = auxtrace_queues__queue_buffer(queues, idx, b);
|
2015-04-21 17:21:51 +08:00
|
|
|
if (err) {
|
|
|
|
auxtrace_buffer__free(b);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
buffer->data_offset += BUFFER_LIMIT_FOR_32_BIT;
|
|
|
|
sz -= BUFFER_LIMIT_FOR_32_BIT;
|
|
|
|
consecutive = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer->size = sz;
|
|
|
|
buffer->consecutive = consecutive;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-06 17:13:18 +08:00
|
|
|
static bool filter_cpu(struct perf_session *session, int cpu)
|
|
|
|
{
|
|
|
|
unsigned long *cpu_bitmap = session->itrace_synth_opts->cpu_bitmap;
|
|
|
|
|
|
|
|
return cpu_bitmap && cpu != -1 && !test_bit(cpu, cpu_bitmap);
|
|
|
|
}
|
|
|
|
|
2018-03-06 17:13:15 +08:00
|
|
|
static int auxtrace_queues__add_buffer(struct auxtrace_queues *queues,
|
|
|
|
struct perf_session *session,
|
|
|
|
unsigned int idx,
|
2018-03-06 17:13:16 +08:00
|
|
|
struct auxtrace_buffer *buffer,
|
|
|
|
struct auxtrace_buffer **buffer_ptr)
|
2015-04-21 17:21:51 +08:00
|
|
|
{
|
2018-03-06 17:13:17 +08:00
|
|
|
int err = -ENOMEM;
|
|
|
|
|
2018-03-06 17:13:18 +08:00
|
|
|
if (filter_cpu(session, buffer->cpu))
|
|
|
|
return 0;
|
|
|
|
|
2018-03-06 17:13:17 +08:00
|
|
|
buffer = memdup(buffer, sizeof(*buffer));
|
|
|
|
if (!buffer)
|
|
|
|
return -ENOMEM;
|
2018-03-06 17:13:16 +08:00
|
|
|
|
2015-04-21 17:21:51 +08:00
|
|
|
if (session->one_mmap) {
|
|
|
|
buffer->data = buffer->data_offset - session->one_mmap_offset +
|
|
|
|
session->one_mmap_addr;
|
2017-01-24 05:07:59 +08:00
|
|
|
} else if (perf_data__is_pipe(session->data)) {
|
2015-04-21 17:21:51 +08:00
|
|
|
buffer->data = auxtrace_copy_data(buffer->size, session);
|
|
|
|
if (!buffer->data)
|
2018-03-06 17:13:17 +08:00
|
|
|
goto out_free;
|
2015-04-21 17:21:51 +08:00
|
|
|
buffer->data_needs_freeing = true;
|
|
|
|
} else if (BITS_PER_LONG == 32 &&
|
|
|
|
buffer->size > BUFFER_LIMIT_FOR_32_BIT) {
|
|
|
|
err = auxtrace_queues__split_buffer(queues, idx, buffer);
|
|
|
|
if (err)
|
2018-03-06 17:13:17 +08:00
|
|
|
goto out_free;
|
2015-04-21 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
2018-03-06 17:13:16 +08:00
|
|
|
err = auxtrace_queues__queue_buffer(queues, idx, buffer);
|
|
|
|
if (err)
|
2018-03-06 17:13:17 +08:00
|
|
|
goto out_free;
|
2018-03-06 17:13:16 +08:00
|
|
|
|
|
|
|
/* FIXME: Doesn't work for split buffer */
|
|
|
|
if (buffer_ptr)
|
|
|
|
*buffer_ptr = buffer;
|
|
|
|
|
|
|
|
return 0;
|
2018-03-06 17:13:17 +08:00
|
|
|
|
|
|
|
out_free:
|
|
|
|
auxtrace_buffer__free(buffer);
|
|
|
|
return err;
|
2015-04-21 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_queues__add_event(struct auxtrace_queues *queues,
|
|
|
|
struct perf_session *session,
|
|
|
|
union perf_event *event, off_t data_offset,
|
|
|
|
struct auxtrace_buffer **buffer_ptr)
|
|
|
|
{
|
2018-03-06 17:13:17 +08:00
|
|
|
struct auxtrace_buffer buffer = {
|
|
|
|
.pid = -1,
|
|
|
|
.tid = event->auxtrace.tid,
|
|
|
|
.cpu = event->auxtrace.cpu,
|
|
|
|
.data_offset = data_offset,
|
|
|
|
.offset = event->auxtrace.offset,
|
|
|
|
.reference = event->auxtrace.reference,
|
|
|
|
.size = event->auxtrace.size,
|
|
|
|
};
|
|
|
|
unsigned int idx = event->auxtrace.idx;
|
2015-04-21 17:21:51 +08:00
|
|
|
|
2018-03-06 17:13:17 +08:00
|
|
|
return auxtrace_queues__add_buffer(queues, session, idx, &buffer,
|
|
|
|
buffer_ptr);
|
2015-04-21 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
2015-04-30 22:37:25 +08:00
|
|
|
static int auxtrace_queues__add_indexed_event(struct auxtrace_queues *queues,
|
|
|
|
struct perf_session *session,
|
|
|
|
off_t file_offset, size_t sz)
|
|
|
|
{
|
|
|
|
union perf_event *event;
|
|
|
|
int err;
|
|
|
|
char buf[PERF_SAMPLE_MAX_SIZE];
|
|
|
|
|
|
|
|
err = perf_session__peek_event(session, file_offset, buf,
|
|
|
|
PERF_SAMPLE_MAX_SIZE, &event, NULL);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (event->header.type == PERF_RECORD_AUXTRACE) {
|
2019-08-28 21:57:16 +08:00
|
|
|
if (event->header.size < sizeof(struct perf_record_auxtrace) ||
|
2015-04-30 22:37:25 +08:00
|
|
|
event->header.size != sz) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
file_offset += event->header.size;
|
|
|
|
err = auxtrace_queues__add_event(queues, session, event,
|
|
|
|
file_offset, NULL);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-04-21 17:21:51 +08:00
|
|
|
void auxtrace_queues__free(struct auxtrace_queues *queues)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < queues->nr_queues; i++) {
|
|
|
|
while (!list_empty(&queues->queue_array[i].head)) {
|
|
|
|
struct auxtrace_buffer *buffer;
|
|
|
|
|
|
|
|
buffer = list_entry(queues->queue_array[i].head.next,
|
|
|
|
struct auxtrace_buffer, list);
|
2019-07-04 23:13:46 +08:00
|
|
|
list_del_init(&buffer->list);
|
2015-04-21 17:21:51 +08:00
|
|
|
auxtrace_buffer__free(buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
zfree(&queues->queue_array);
|
|
|
|
queues->nr_queues = 0;
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:52 +08:00
|
|
|
static void auxtrace_heapify(struct auxtrace_heap_item *heap_array,
|
|
|
|
unsigned int pos, unsigned int queue_nr,
|
|
|
|
u64 ordinal)
|
|
|
|
{
|
|
|
|
unsigned int parent;
|
|
|
|
|
|
|
|
while (pos) {
|
|
|
|
parent = (pos - 1) >> 1;
|
|
|
|
if (heap_array[parent].ordinal <= ordinal)
|
|
|
|
break;
|
|
|
|
heap_array[pos] = heap_array[parent];
|
|
|
|
pos = parent;
|
|
|
|
}
|
|
|
|
heap_array[pos].queue_nr = queue_nr;
|
|
|
|
heap_array[pos].ordinal = ordinal;
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_heap__add(struct auxtrace_heap *heap, unsigned int queue_nr,
|
|
|
|
u64 ordinal)
|
|
|
|
{
|
|
|
|
struct auxtrace_heap_item *heap_array;
|
|
|
|
|
|
|
|
if (queue_nr >= heap->heap_sz) {
|
|
|
|
unsigned int heap_sz = AUXTRACE_INIT_NR_QUEUES;
|
|
|
|
|
|
|
|
while (heap_sz <= queue_nr)
|
|
|
|
heap_sz <<= 1;
|
|
|
|
heap_array = realloc(heap->heap_array,
|
|
|
|
heap_sz * sizeof(struct auxtrace_heap_item));
|
|
|
|
if (!heap_array)
|
|
|
|
return -ENOMEM;
|
|
|
|
heap->heap_array = heap_array;
|
|
|
|
heap->heap_sz = heap_sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
auxtrace_heapify(heap->heap_array, heap->heap_cnt++, queue_nr, ordinal);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_heap__free(struct auxtrace_heap *heap)
|
|
|
|
{
|
|
|
|
zfree(&heap->heap_array);
|
|
|
|
heap->heap_cnt = 0;
|
|
|
|
heap->heap_sz = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_heap__pop(struct auxtrace_heap *heap)
|
|
|
|
{
|
|
|
|
unsigned int pos, last, heap_cnt = heap->heap_cnt;
|
|
|
|
struct auxtrace_heap_item *heap_array;
|
|
|
|
|
|
|
|
if (!heap_cnt)
|
|
|
|
return;
|
|
|
|
|
|
|
|
heap->heap_cnt -= 1;
|
|
|
|
|
|
|
|
heap_array = heap->heap_array;
|
|
|
|
|
|
|
|
pos = 0;
|
|
|
|
while (1) {
|
|
|
|
unsigned int left, right;
|
|
|
|
|
|
|
|
left = (pos << 1) + 1;
|
|
|
|
if (left >= heap_cnt)
|
|
|
|
break;
|
|
|
|
right = left + 1;
|
|
|
|
if (right >= heap_cnt) {
|
|
|
|
heap_array[pos] = heap_array[left];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (heap_array[left].ordinal < heap_array[right].ordinal) {
|
|
|
|
heap_array[pos] = heap_array[left];
|
|
|
|
pos = left;
|
|
|
|
} else {
|
|
|
|
heap_array[pos] = heap_array[right];
|
|
|
|
pos = right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
last = heap_cnt - 1;
|
|
|
|
auxtrace_heapify(heap_array, pos, heap_array[last].queue_nr,
|
|
|
|
heap_array[last].ordinal);
|
|
|
|
}
|
|
|
|
|
2016-01-15 05:46:15 +08:00
|
|
|
size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
|
2019-07-21 19:23:52 +08:00
|
|
|
struct evlist *evlist)
|
2015-04-09 23:53:44 +08:00
|
|
|
{
|
|
|
|
if (itr)
|
2016-01-15 05:46:15 +08:00
|
|
|
return itr->info_priv_size(itr, evlist);
|
2015-04-09 23:53:44 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int auxtrace_not_supported(void)
|
|
|
|
{
|
|
|
|
pr_err("AUX area tracing is not supported on this architecture\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_record__info_fill(struct auxtrace_record *itr,
|
|
|
|
struct perf_session *session,
|
2019-08-28 21:57:16 +08:00
|
|
|
struct perf_record_auxtrace_info *auxtrace_info,
|
2015-04-09 23:53:44 +08:00
|
|
|
size_t priv_size)
|
|
|
|
{
|
|
|
|
if (itr)
|
|
|
|
return itr->info_fill(itr, session, auxtrace_info, priv_size);
|
|
|
|
return auxtrace_not_supported();
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_record__free(struct auxtrace_record *itr)
|
|
|
|
{
|
|
|
|
if (itr)
|
|
|
|
itr->free(itr);
|
|
|
|
}
|
|
|
|
|
2015-04-30 22:37:31 +08:00
|
|
|
int auxtrace_record__snapshot_start(struct auxtrace_record *itr)
|
|
|
|
{
|
|
|
|
if (itr && itr->snapshot_start)
|
|
|
|
return itr->snapshot_start(itr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-06 22:41:01 +08:00
|
|
|
int auxtrace_record__snapshot_finish(struct auxtrace_record *itr, bool on_exit)
|
2015-04-30 22:37:31 +08:00
|
|
|
{
|
2019-08-06 22:41:01 +08:00
|
|
|
if (!on_exit && itr && itr->snapshot_finish)
|
2015-04-30 22:37:31 +08:00
|
|
|
return itr->snapshot_finish(itr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx,
|
|
|
|
struct auxtrace_mmap *mm,
|
|
|
|
unsigned char *data, u64 *head, u64 *old)
|
|
|
|
{
|
|
|
|
if (itr && itr->find_snapshot)
|
|
|
|
return itr->find_snapshot(itr, idx, mm, data, head, old);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:44 +08:00
|
|
|
int auxtrace_record__options(struct auxtrace_record *itr,
|
2019-07-21 19:23:52 +08:00
|
|
|
struct evlist *evlist,
|
2015-04-09 23:53:44 +08:00
|
|
|
struct record_opts *opts)
|
|
|
|
{
|
2020-02-17 16:23:00 +08:00
|
|
|
if (itr) {
|
|
|
|
itr->evlist = evlist;
|
2015-04-09 23:53:44 +08:00
|
|
|
return itr->recording_options(itr, evlist, opts);
|
2020-02-17 16:23:00 +08:00
|
|
|
}
|
2015-04-09 23:53:44 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 auxtrace_record__reference(struct auxtrace_record *itr)
|
|
|
|
{
|
|
|
|
if (itr)
|
|
|
|
return itr->reference(itr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-30 22:37:31 +08:00
|
|
|
int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
|
|
|
|
struct record_opts *opts, const char *str)
|
|
|
|
{
|
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
|
2019-08-06 22:41:01 +08:00
|
|
|
/* PMU-agnostic options */
|
|
|
|
switch (*str) {
|
|
|
|
case 'e':
|
|
|
|
opts->auxtrace_snapshot_on_exit = true;
|
|
|
|
str++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-04-20 23:15:53 +08:00
|
|
|
if (itr && itr->parse_snapshot_options)
|
2015-04-30 22:37:31 +08:00
|
|
|
return itr->parse_snapshot_options(itr, opts, str);
|
|
|
|
|
|
|
|
pr_err("No AUX area tracing to snapshot\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-02-17 16:23:00 +08:00
|
|
|
int auxtrace_record__read_finish(struct auxtrace_record *itr, int idx)
|
|
|
|
{
|
|
|
|
struct evsel *evsel;
|
|
|
|
|
|
|
|
if (!itr->evlist || !itr->pmu)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
evlist__for_each_entry(itr->evlist, evsel) {
|
|
|
|
if (evsel->core.attr.type == itr->pmu->type) {
|
|
|
|
if (evsel->disabled)
|
|
|
|
return 0;
|
2020-12-01 01:47:05 +08:00
|
|
|
return evlist__enable_event_idx(itr->evlist, evsel, idx);
|
2020-02-17 16:23:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-11-15 20:42:15 +08:00
|
|
|
/*
|
|
|
|
* Event record size is 16-bit which results in a maximum size of about 64KiB.
|
|
|
|
* Allow about 4KiB for the rest of the sample record, to give a maximum
|
|
|
|
* AUX area sample size of 60KiB.
|
|
|
|
*/
|
|
|
|
#define MAX_AUX_SAMPLE_SIZE (60 * 1024)
|
|
|
|
|
|
|
|
/* Arbitrary default size if no other default provided */
|
|
|
|
#define DEFAULT_AUX_SAMPLE_SIZE (4 * 1024)
|
|
|
|
|
|
|
|
static int auxtrace_validate_aux_sample_size(struct evlist *evlist,
|
|
|
|
struct record_opts *opts)
|
|
|
|
{
|
|
|
|
struct evsel *evsel;
|
|
|
|
bool has_aux_leader = false;
|
|
|
|
u32 sz;
|
|
|
|
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
|
|
|
sz = evsel->core.attr.aux_sample_size;
|
2020-04-30 21:51:16 +08:00
|
|
|
if (evsel__is_group_leader(evsel)) {
|
2020-04-30 02:51:38 +08:00
|
|
|
has_aux_leader = evsel__is_aux_event(evsel);
|
2019-11-15 20:42:15 +08:00
|
|
|
if (sz) {
|
|
|
|
if (has_aux_leader)
|
|
|
|
pr_err("Cannot add AUX area sampling to an AUX area event\n");
|
|
|
|
else
|
|
|
|
pr_err("Cannot add AUX area sampling to a group leader\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sz > MAX_AUX_SAMPLE_SIZE) {
|
|
|
|
pr_err("AUX area sample size %u too big, max. %d\n",
|
|
|
|
sz, MAX_AUX_SAMPLE_SIZE);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (sz) {
|
|
|
|
if (!has_aux_leader) {
|
|
|
|
pr_err("Cannot add AUX area sampling because group leader is not an AUX area event\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-04-30 03:12:15 +08:00
|
|
|
evsel__set_sample_bit(evsel, AUX);
|
2019-11-15 20:42:15 +08:00
|
|
|
opts->auxtrace_sample_mode = true;
|
|
|
|
} else {
|
2020-04-30 03:12:15 +08:00
|
|
|
evsel__reset_sample_bit(evsel, AUX);
|
2019-11-15 20:42:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!opts->auxtrace_sample_mode) {
|
|
|
|
pr_err("AUX area sampling requires an AUX area event group leader plus other events to which to add samples\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!perf_can_aux_sample()) {
|
|
|
|
pr_err("AUX area sampling is not supported by kernel\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_parse_sample_options(struct auxtrace_record *itr,
|
|
|
|
struct evlist *evlist,
|
|
|
|
struct record_opts *opts, const char *str)
|
|
|
|
{
|
2020-05-07 00:05:08 +08:00
|
|
|
struct evsel_config_term *term;
|
2019-11-15 20:42:17 +08:00
|
|
|
struct evsel *aux_evsel;
|
|
|
|
bool has_aux_sample_size = false;
|
2019-11-15 20:42:15 +08:00
|
|
|
bool has_aux_leader = false;
|
|
|
|
struct evsel *evsel;
|
|
|
|
char *endptr;
|
|
|
|
unsigned long sz;
|
|
|
|
|
|
|
|
if (!str)
|
2019-11-15 20:42:17 +08:00
|
|
|
goto no_opt;
|
2019-11-15 20:42:15 +08:00
|
|
|
|
|
|
|
if (!itr) {
|
|
|
|
pr_err("No AUX area event to sample\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sz = strtoul(str, &endptr, 0);
|
|
|
|
if (*endptr || sz > UINT_MAX) {
|
|
|
|
pr_err("Bad AUX area sampling option: '%s'\n", str);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sz)
|
|
|
|
sz = itr->default_aux_sample_size;
|
|
|
|
|
|
|
|
if (!sz)
|
|
|
|
sz = DEFAULT_AUX_SAMPLE_SIZE;
|
|
|
|
|
|
|
|
/* Set aux_sample_size based on --aux-sample option */
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
2020-04-30 21:51:16 +08:00
|
|
|
if (evsel__is_group_leader(evsel)) {
|
2020-04-30 02:51:38 +08:00
|
|
|
has_aux_leader = evsel__is_aux_event(evsel);
|
2019-11-15 20:42:15 +08:00
|
|
|
} else if (has_aux_leader) {
|
|
|
|
evsel->core.attr.aux_sample_size = sz;
|
|
|
|
}
|
|
|
|
}
|
2019-11-15 20:42:17 +08:00
|
|
|
no_opt:
|
|
|
|
aux_evsel = NULL;
|
|
|
|
/* Override with aux_sample_size from config term */
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
2020-04-30 02:51:38 +08:00
|
|
|
if (evsel__is_aux_event(evsel))
|
2019-11-15 20:42:17 +08:00
|
|
|
aux_evsel = evsel;
|
2020-05-07 00:05:08 +08:00
|
|
|
term = evsel__get_config_term(evsel, AUX_SAMPLE_SIZE);
|
2019-11-15 20:42:17 +08:00
|
|
|
if (term) {
|
|
|
|
has_aux_sample_size = true;
|
|
|
|
evsel->core.attr.aux_sample_size = term->val.aux_sample_size;
|
|
|
|
/* If possible, group with the AUX event */
|
|
|
|
if (aux_evsel && evsel->core.attr.aux_sample_size)
|
2020-12-01 01:58:32 +08:00
|
|
|
evlist__regroup(evlist, aux_evsel, evsel);
|
2019-11-15 20:42:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!str && !has_aux_sample_size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!itr) {
|
|
|
|
pr_err("No AUX area event to sample\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2019-11-15 20:42:15 +08:00
|
|
|
|
|
|
|
return auxtrace_validate_aux_sample_size(evlist, opts);
|
|
|
|
}
|
|
|
|
|
2021-01-21 22:04:18 +08:00
|
|
|
void auxtrace_regroup_aux_output(struct evlist *evlist)
|
|
|
|
{
|
|
|
|
struct evsel *evsel, *aux_evsel = NULL;
|
|
|
|
struct evsel_config_term *term;
|
|
|
|
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
|
|
|
if (evsel__is_aux_event(evsel))
|
|
|
|
aux_evsel = evsel;
|
|
|
|
term = evsel__get_config_term(evsel, AUX_OUTPUT);
|
|
|
|
/* If possible, group with the AUX event */
|
|
|
|
if (term && aux_evsel)
|
|
|
|
evlist__regroup(evlist, aux_evsel, evsel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:44 +08:00
|
|
|
struct auxtrace_record *__weak
|
2019-07-21 19:23:52 +08:00
|
|
|
auxtrace_record__init(struct evlist *evlist __maybe_unused, int *err)
|
2015-04-09 23:53:44 +08:00
|
|
|
{
|
|
|
|
*err = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-04-30 22:37:25 +08:00
|
|
|
static int auxtrace_index__alloc(struct list_head *head)
|
|
|
|
{
|
|
|
|
struct auxtrace_index *auxtrace_index;
|
|
|
|
|
|
|
|
auxtrace_index = malloc(sizeof(struct auxtrace_index));
|
|
|
|
if (!auxtrace_index)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
auxtrace_index->nr = 0;
|
|
|
|
INIT_LIST_HEAD(&auxtrace_index->list);
|
|
|
|
|
|
|
|
list_add_tail(&auxtrace_index->list, head);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_index__free(struct list_head *head)
|
|
|
|
{
|
|
|
|
struct auxtrace_index *auxtrace_index, *n;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(auxtrace_index, n, head, list) {
|
2019-07-04 23:13:46 +08:00
|
|
|
list_del_init(&auxtrace_index->list);
|
2015-04-30 22:37:25 +08:00
|
|
|
free(auxtrace_index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct auxtrace_index *auxtrace_index__last(struct list_head *head)
|
|
|
|
{
|
|
|
|
struct auxtrace_index *auxtrace_index;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (list_empty(head)) {
|
|
|
|
err = auxtrace_index__alloc(head);
|
|
|
|
if (err)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
auxtrace_index = list_entry(head->prev, struct auxtrace_index, list);
|
|
|
|
|
|
|
|
if (auxtrace_index->nr >= PERF_AUXTRACE_INDEX_ENTRY_COUNT) {
|
|
|
|
err = auxtrace_index__alloc(head);
|
|
|
|
if (err)
|
|
|
|
return NULL;
|
|
|
|
auxtrace_index = list_entry(head->prev, struct auxtrace_index,
|
|
|
|
list);
|
|
|
|
}
|
|
|
|
|
|
|
|
return auxtrace_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_index__auxtrace_event(struct list_head *head,
|
|
|
|
union perf_event *event, off_t file_offset)
|
|
|
|
{
|
|
|
|
struct auxtrace_index *auxtrace_index;
|
|
|
|
size_t nr;
|
|
|
|
|
|
|
|
auxtrace_index = auxtrace_index__last(head);
|
|
|
|
if (!auxtrace_index)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
nr = auxtrace_index->nr;
|
|
|
|
auxtrace_index->entries[nr].file_offset = file_offset;
|
|
|
|
auxtrace_index->entries[nr].sz = event->header.size;
|
|
|
|
auxtrace_index->nr += 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int auxtrace_index__do_write(int fd,
|
|
|
|
struct auxtrace_index *auxtrace_index)
|
|
|
|
{
|
|
|
|
struct auxtrace_index_entry ent;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < auxtrace_index->nr; i++) {
|
|
|
|
ent.file_offset = auxtrace_index->entries[i].file_offset;
|
|
|
|
ent.sz = auxtrace_index->entries[i].sz;
|
|
|
|
if (writen(fd, &ent, sizeof(ent)) != sizeof(ent))
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_index__write(int fd, struct list_head *head)
|
|
|
|
{
|
|
|
|
struct auxtrace_index *auxtrace_index;
|
|
|
|
u64 total = 0;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
list_for_each_entry(auxtrace_index, head, list)
|
|
|
|
total += auxtrace_index->nr;
|
|
|
|
|
|
|
|
if (writen(fd, &total, sizeof(total)) != sizeof(total))
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
list_for_each_entry(auxtrace_index, head, list) {
|
|
|
|
err = auxtrace_index__do_write(fd, auxtrace_index);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int auxtrace_index__process_entry(int fd, struct list_head *head,
|
|
|
|
bool needs_swap)
|
|
|
|
{
|
|
|
|
struct auxtrace_index *auxtrace_index;
|
|
|
|
struct auxtrace_index_entry ent;
|
|
|
|
size_t nr;
|
|
|
|
|
|
|
|
if (readn(fd, &ent, sizeof(ent)) != sizeof(ent))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
auxtrace_index = auxtrace_index__last(head);
|
|
|
|
if (!auxtrace_index)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
nr = auxtrace_index->nr;
|
|
|
|
if (needs_swap) {
|
|
|
|
auxtrace_index->entries[nr].file_offset =
|
|
|
|
bswap_64(ent.file_offset);
|
|
|
|
auxtrace_index->entries[nr].sz = bswap_64(ent.sz);
|
|
|
|
} else {
|
|
|
|
auxtrace_index->entries[nr].file_offset = ent.file_offset;
|
|
|
|
auxtrace_index->entries[nr].sz = ent.sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
auxtrace_index->nr = nr + 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_index__process(int fd, u64 size, struct perf_session *session,
|
|
|
|
bool needs_swap)
|
|
|
|
{
|
|
|
|
struct list_head *head = &session->auxtrace_index;
|
|
|
|
u64 nr;
|
|
|
|
|
|
|
|
if (readn(fd, &nr, sizeof(u64)) != sizeof(u64))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (needs_swap)
|
|
|
|
nr = bswap_64(nr);
|
|
|
|
|
|
|
|
if (sizeof(u64) + nr * sizeof(struct auxtrace_index_entry) > size)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (nr--) {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = auxtrace_index__process_entry(fd, head, needs_swap);
|
|
|
|
if (err)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int auxtrace_queues__process_index_entry(struct auxtrace_queues *queues,
|
|
|
|
struct perf_session *session,
|
|
|
|
struct auxtrace_index_entry *ent)
|
|
|
|
{
|
|
|
|
return auxtrace_queues__add_indexed_event(queues, session,
|
|
|
|
ent->file_offset, ent->sz);
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_queues__process_index(struct auxtrace_queues *queues,
|
|
|
|
struct perf_session *session)
|
|
|
|
{
|
|
|
|
struct auxtrace_index *auxtrace_index;
|
|
|
|
struct auxtrace_index_entry *ent;
|
|
|
|
size_t i;
|
|
|
|
int err;
|
|
|
|
|
2018-03-06 17:13:13 +08:00
|
|
|
if (auxtrace__dont_decode(session))
|
|
|
|
return 0;
|
|
|
|
|
2015-04-30 22:37:25 +08:00
|
|
|
list_for_each_entry(auxtrace_index, &session->auxtrace_index, list) {
|
|
|
|
for (i = 0; i < auxtrace_index->nr; i++) {
|
|
|
|
ent = &auxtrace_index->entries[i];
|
|
|
|
err = auxtrace_queues__process_index_entry(queues,
|
|
|
|
session,
|
|
|
|
ent);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-21 17:21:51 +08:00
|
|
|
struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue,
|
|
|
|
struct auxtrace_buffer *buffer)
|
|
|
|
{
|
|
|
|
if (buffer) {
|
|
|
|
if (list_is_last(&buffer->list, &queue->head))
|
|
|
|
return NULL;
|
|
|
|
return list_entry(buffer->list.next, struct auxtrace_buffer,
|
|
|
|
list);
|
|
|
|
} else {
|
|
|
|
if (list_empty(&queue->head))
|
|
|
|
return NULL;
|
|
|
|
return list_entry(queue->head.next, struct auxtrace_buffer,
|
|
|
|
list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-15 20:42:21 +08:00
|
|
|
struct auxtrace_queue *auxtrace_queues__sample_queue(struct auxtrace_queues *queues,
|
|
|
|
struct perf_sample *sample,
|
|
|
|
struct perf_session *session)
|
|
|
|
{
|
|
|
|
struct perf_sample_id *sid;
|
|
|
|
unsigned int idx;
|
|
|
|
u64 id;
|
|
|
|
|
|
|
|
id = sample->id;
|
|
|
|
if (!id)
|
|
|
|
return NULL;
|
|
|
|
|
2020-12-01 01:17:57 +08:00
|
|
|
sid = evlist__id2sid(session->evlist, id);
|
2019-11-15 20:42:21 +08:00
|
|
|
if (!sid)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
idx = sid->idx;
|
|
|
|
|
|
|
|
if (idx >= queues->nr_queues)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return &queues->queue_array[idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_queues__add_sample(struct auxtrace_queues *queues,
|
|
|
|
struct perf_session *session,
|
|
|
|
struct perf_sample *sample, u64 data_offset,
|
|
|
|
u64 reference)
|
|
|
|
{
|
|
|
|
struct auxtrace_buffer buffer = {
|
|
|
|
.pid = -1,
|
|
|
|
.data_offset = data_offset,
|
|
|
|
.reference = reference,
|
|
|
|
.size = sample->aux_sample.size,
|
|
|
|
};
|
|
|
|
struct perf_sample_id *sid;
|
|
|
|
u64 id = sample->id;
|
|
|
|
unsigned int idx;
|
|
|
|
|
|
|
|
if (!id)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2020-12-01 01:17:57 +08:00
|
|
|
sid = evlist__id2sid(session->evlist, id);
|
2019-11-15 20:42:21 +08:00
|
|
|
if (!sid)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
idx = sid->idx;
|
|
|
|
buffer.tid = sid->tid;
|
|
|
|
buffer.cpu = sid->cpu;
|
|
|
|
|
|
|
|
return auxtrace_queues__add_buffer(queues, session, idx, &buffer, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct queue_data {
|
|
|
|
bool samples;
|
|
|
|
bool events;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int auxtrace_queue_data_cb(struct perf_session *session,
|
|
|
|
union perf_event *event, u64 offset,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct queue_data *qd = data;
|
|
|
|
struct perf_sample sample;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (qd->events && event->header.type == PERF_RECORD_AUXTRACE) {
|
|
|
|
if (event->header.size < sizeof(struct perf_record_auxtrace))
|
|
|
|
return -EINVAL;
|
|
|
|
offset += event->header.size;
|
|
|
|
return session->auxtrace->queue_data(session, NULL, event,
|
|
|
|
offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!qd->samples || event->header.type != PERF_RECORD_SAMPLE)
|
|
|
|
return 0;
|
|
|
|
|
2020-11-30 20:43:07 +08:00
|
|
|
err = evlist__parse_sample(session->evlist, event, &sample);
|
2019-11-15 20:42:21 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (!sample.aux_sample.size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
offset += sample.aux_sample.data - (void *)event;
|
|
|
|
|
|
|
|
return session->auxtrace->queue_data(session, &sample, NULL, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_queue_data(struct perf_session *session, bool samples, bool events)
|
|
|
|
{
|
|
|
|
struct queue_data qd = {
|
|
|
|
.samples = samples,
|
|
|
|
.events = events,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (auxtrace__dont_decode(session))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!session->auxtrace || !session->auxtrace->queue_data)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return perf_session__peek_events(session, session->header.data_offset,
|
|
|
|
session->header.data_size,
|
|
|
|
auxtrace_queue_data_cb, &qd);
|
|
|
|
}
|
|
|
|
|
2021-04-30 15:03:03 +08:00
|
|
|
void *auxtrace_buffer__get_data_rw(struct auxtrace_buffer *buffer, int fd, bool rw)
|
2015-04-21 17:21:51 +08:00
|
|
|
{
|
2021-04-30 15:03:03 +08:00
|
|
|
int prot = rw ? PROT_READ | PROT_WRITE : PROT_READ;
|
2015-04-21 17:21:51 +08:00
|
|
|
size_t adj = buffer->data_offset & (page_size - 1);
|
|
|
|
size_t size = buffer->size + adj;
|
|
|
|
off_t file_offset = buffer->data_offset - adj;
|
|
|
|
void *addr;
|
|
|
|
|
|
|
|
if (buffer->data)
|
|
|
|
return buffer->data;
|
|
|
|
|
2021-04-30 15:03:03 +08:00
|
|
|
addr = mmap(NULL, size, prot, MAP_SHARED, fd, file_offset);
|
2015-04-21 17:21:51 +08:00
|
|
|
if (addr == MAP_FAILED)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
buffer->mmap_addr = addr;
|
|
|
|
buffer->mmap_size = size;
|
|
|
|
|
|
|
|
buffer->data = addr + adj;
|
|
|
|
|
|
|
|
return buffer->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_buffer__put_data(struct auxtrace_buffer *buffer)
|
|
|
|
{
|
|
|
|
if (!buffer->data || !buffer->mmap_addr)
|
|
|
|
return;
|
|
|
|
munmap(buffer->mmap_addr, buffer->mmap_size);
|
|
|
|
buffer->mmap_addr = NULL;
|
|
|
|
buffer->mmap_size = 0;
|
|
|
|
buffer->data = NULL;
|
|
|
|
buffer->use_data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_buffer__drop_data(struct auxtrace_buffer *buffer)
|
|
|
|
{
|
|
|
|
auxtrace_buffer__put_data(buffer);
|
|
|
|
if (buffer->data_needs_freeing) {
|
|
|
|
buffer->data_needs_freeing = false;
|
|
|
|
zfree(&buffer->data);
|
|
|
|
buffer->use_data = NULL;
|
|
|
|
buffer->size = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_buffer__free(struct auxtrace_buffer *buffer)
|
|
|
|
{
|
|
|
|
auxtrace_buffer__drop_data(buffer);
|
|
|
|
free(buffer);
|
|
|
|
}
|
|
|
|
|
2019-08-28 21:57:16 +08:00
|
|
|
void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int type,
|
2015-04-09 23:53:50 +08:00
|
|
|
int code, int cpu, pid_t pid, pid_t tid, u64 ip,
|
2019-02-06 18:39:47 +08:00
|
|
|
const char *msg, u64 timestamp)
|
2015-04-09 23:53:50 +08:00
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
|
2019-08-28 21:57:16 +08:00
|
|
|
memset(auxtrace_error, 0, sizeof(struct perf_record_auxtrace_error));
|
2015-04-09 23:53:50 +08:00
|
|
|
|
|
|
|
auxtrace_error->header.type = PERF_RECORD_AUXTRACE_ERROR;
|
|
|
|
auxtrace_error->type = type;
|
|
|
|
auxtrace_error->code = code;
|
|
|
|
auxtrace_error->cpu = cpu;
|
|
|
|
auxtrace_error->pid = pid;
|
|
|
|
auxtrace_error->tid = tid;
|
2019-02-06 18:39:47 +08:00
|
|
|
auxtrace_error->fmt = 1;
|
2015-04-09 23:53:50 +08:00
|
|
|
auxtrace_error->ip = ip;
|
2019-02-06 18:39:47 +08:00
|
|
|
auxtrace_error->time = timestamp;
|
2015-04-09 23:53:50 +08:00
|
|
|
strlcpy(auxtrace_error->msg, msg, MAX_AUXTRACE_ERROR_MSG);
|
|
|
|
|
|
|
|
size = (void *)auxtrace_error->msg - (void *)auxtrace_error +
|
|
|
|
strlen(auxtrace_error->msg) + 1;
|
|
|
|
auxtrace_error->header.size = PERF_ALIGN(size, sizeof(u64));
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:44 +08:00
|
|
|
int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
|
|
|
|
struct perf_tool *tool,
|
|
|
|
struct perf_session *session,
|
|
|
|
perf_event__handler_t process)
|
|
|
|
{
|
|
|
|
union perf_event *ev;
|
|
|
|
size_t priv_size;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
pr_debug2("Synthesizing auxtrace information\n");
|
2016-01-15 05:46:15 +08:00
|
|
|
priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
|
2019-08-28 21:57:16 +08:00
|
|
|
ev = zalloc(sizeof(struct perf_record_auxtrace_info) + priv_size);
|
2015-04-09 23:53:44 +08:00
|
|
|
if (!ev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ev->auxtrace_info.header.type = PERF_RECORD_AUXTRACE_INFO;
|
2019-08-28 21:57:16 +08:00
|
|
|
ev->auxtrace_info.header.size = sizeof(struct perf_record_auxtrace_info) +
|
2015-04-09 23:53:44 +08:00
|
|
|
priv_size;
|
|
|
|
err = auxtrace_record__info_fill(itr, session, &ev->auxtrace_info,
|
|
|
|
priv_size);
|
|
|
|
if (err)
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
err = process(tool, ev, NULL, NULL);
|
|
|
|
out_free:
|
|
|
|
free(ev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-04-01 18:16:04 +08:00
|
|
|
static void unleader_evsel(struct evlist *evlist, struct evsel *leader)
|
|
|
|
{
|
|
|
|
struct evsel *new_leader = NULL;
|
|
|
|
struct evsel *evsel;
|
|
|
|
|
|
|
|
/* Find new leader for the group */
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
2021-07-06 23:17:00 +08:00
|
|
|
if (!evsel__has_leader(evsel, leader) || evsel == leader)
|
2020-04-01 18:16:04 +08:00
|
|
|
continue;
|
|
|
|
if (!new_leader)
|
|
|
|
new_leader = evsel;
|
2021-07-06 23:17:00 +08:00
|
|
|
evsel__set_leader(evsel, new_leader);
|
2020-04-01 18:16:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update group information */
|
|
|
|
if (new_leader) {
|
|
|
|
zfree(&new_leader->group_name);
|
|
|
|
new_leader->group_name = leader->group_name;
|
|
|
|
leader->group_name = NULL;
|
|
|
|
|
|
|
|
new_leader->core.nr_members = leader->core.nr_members - 1;
|
|
|
|
leader->core.nr_members = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unleader_auxtrace(struct perf_session *session)
|
|
|
|
{
|
|
|
|
struct evsel *evsel;
|
|
|
|
|
|
|
|
evlist__for_each_entry(session->evlist, evsel) {
|
|
|
|
if (auxtrace__evsel_is_auxtrace(session, evsel) &&
|
2020-04-30 21:51:16 +08:00
|
|
|
evsel__is_group_leader(evsel)) {
|
2020-04-01 18:16:04 +08:00
|
|
|
unleader_evsel(session->evlist, evsel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-13 20:54:03 +08:00
|
|
|
int perf_event__process_auxtrace_info(struct perf_session *session,
|
|
|
|
union perf_event *event)
|
2015-04-09 23:53:53 +08:00
|
|
|
{
|
|
|
|
enum auxtrace_type type = event->auxtrace_info.type;
|
2020-04-01 18:16:04 +08:00
|
|
|
int err;
|
2015-04-09 23:53:53 +08:00
|
|
|
|
|
|
|
if (dump_trace)
|
|
|
|
fprintf(stdout, " type: %u\n", type);
|
|
|
|
|
|
|
|
switch (type) {
|
2015-07-18 00:33:36 +08:00
|
|
|
case PERF_AUXTRACE_INTEL_PT:
|
2020-04-01 18:16:04 +08:00
|
|
|
err = intel_pt_process_auxtrace_info(event, session);
|
|
|
|
break;
|
perf tools: Add Intel BTS support
Intel BTS support fits within the new auxtrace infrastructure. Recording is
supporting by identifying the Intel BTS PMU, parsing options and setting up
events.
Decoding is supported by queuing up trace data by thread and then decoding
synchronously delivering synthesized event samples into the session processing
for tools to consume.
Committer note:
E.g:
[root@felicio ~]# perf record --per-thread -e intel_bts// ls
anaconda-ks.cfg apctest.output bin kernel-rt-3.10.0-298.rt56.171.el7.x86_64.rpm libexec lock_page.bpf.c perf.data perf.data.old
[ perf record: Woken up 3 times to write data ]
[ perf record: Captured and wrote 4.367 MB perf.data ]
[root@felicio ~]# perf evlist -v
intel_bts//: type: 6, size: 112, { sample_period, sample_freq }: 1, sample_type: IP|TID|IDENTIFIER, read_format: ID, disabled: 1, enable_on_exec: 1, sample_id_all: 1, exclude_guest: 1
dummy:u: type: 1, size: 112, config: 0x9, { sample_period, sample_freq }: 1, sample_type: IP|TID|IDENTIFIER, read_format: ID, disabled: 1, exclude_kernel: 1, exclude_hv: 1, mmap: 1, comm: 1, enable_on_exec: 1, task: 1, sample_id_all: 1, mmap2: 1, comm_exec: 1
[root@felicio ~]# perf script # the navigate in the pager to some interesting place:
ls 1843 1 branches: ffffffff810a60cb flush_signal_handlers ([kernel.kallsyms]) => ffffffff8121a522 setup_new_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8121a529 setup_new_exec ([kernel.kallsyms]) => ffffffff8122fa30 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa5d do_close_on_exec ([kernel.kallsyms]) => ffffffff81767ae0 _raw_spin_lock ([kernel.kallsyms])
ls 1843 1 branches: ffffffff81767af4 _raw_spin_lock ([kernel.kallsyms]) => ffffffff8122fa62 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fa8e do_close_on_exec ([kernel.kallsyms]) => ffffffff8122faf0 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122faf7 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fa8b do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fac9 do_close_on_exec ([kernel.kallsyms]) => ffffffff8122fad2 do_close_on_exec ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8122fadd do_close_on_exec ([kernel.kallsyms]) => ffffffff8120fc80 filp_close ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8120fcaf filp_close ([kernel.kallsyms]) => ffffffff8120fcb6 filp_close ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8120fcc2 filp_close ([kernel.kallsyms]) => ffffffff812547f0 dnotify_flush ([kernel.kallsyms])
ls 1843 1 branches: ffffffff81254823 dnotify_flush ([kernel.kallsyms]) => ffffffff8120fcc7 filp_close ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8120fccd filp_close ([kernel.kallsyms]) => ffffffff81261790 locks_remove_posix ([kernel.kallsyms])
ls 1843 1 branches: ffffffff812617a3 locks_remove_posix ([kernel.kallsyms]) => ffffffff812617b9 locks_remove_posix ([kernel.kallsyms])
ls 1843 1 branches: ffffffff812617b9 locks_remove_posix ([kernel.kallsyms]) => ffffffff8120fcd2 filp_close ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8120fcd5 filp_close ([kernel.kallsyms]) => ffffffff812142c0 fput ([kernel.kallsyms])
ls 1843 1 branches: ffffffff812142d6 fput ([kernel.kallsyms]) => ffffffff812142df fput ([kernel.kallsyms])
ls 1843 1 branches: ffffffff8121430c fput ([kernel.kallsyms]) => ffffffff810b6580 task_work_add ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810b65ad task_work_add ([kernel.kallsyms]) => ffffffff810b65b1 task_work_add ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810b65c1 task_work_add ([kernel.kallsyms]) => ffffffff810bc710 kick_process ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810bc725 kick_process ([kernel.kallsyms]) => ffffffff810bc742 kick_process ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810bc742 kick_process ([kernel.kallsyms]) => ffffffff810b65c6 task_work_add ([kernel.kallsyms])
ls 1843 1 branches: ffffffff810b65c9 task_work_add ([kernel.kallsyms]) => ffffffff81214311 fput ([kernel.kallsyms])
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/r/1437150840-31811-9-git-send-email-adrian.hunter@intel.com
[ Merged sample->time fix for bug found after first round of testing on slightly older kernel ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-18 00:33:43 +08:00
|
|
|
case PERF_AUXTRACE_INTEL_BTS:
|
2020-04-01 18:16:04 +08:00
|
|
|
err = intel_bts_process_auxtrace_info(event, session);
|
|
|
|
break;
|
2018-01-15 03:28:50 +08:00
|
|
|
case PERF_AUXTRACE_ARM_SPE:
|
2020-04-01 18:16:04 +08:00
|
|
|
err = arm_spe_process_auxtrace_info(event, session);
|
|
|
|
break;
|
2016-09-16 23:50:00 +08:00
|
|
|
case PERF_AUXTRACE_CS_ETM:
|
2020-04-01 18:16:04 +08:00
|
|
|
err = cs_etm__process_auxtrace_info(event, session);
|
|
|
|
break;
|
perf auxtrace: Support for perf report -D for s390
Add initial support for s390 auxiliary traces using the CPU-Measurement
Sampling Facility.
Support and ignore PERF_REPORT_AUXTRACE_INFO records in the perf data
file. Later patches will show the contents of the auxiliary traces.
Setup the auxtrace queues and data structures for s390. A raw dump of
the perf.data file now does not show an error when an auxtrace event is
encountered.
Output before:
[root@s35lp76 perf]# ./perf report -D -i perf.data.auxtrace
0x128 [0x10]: failed to process type: 70
Error:
failed to process sample
0x128 [0x10]: event: 70
.
. ... raw event: size 16 bytes
. 0000: 00 00 00 46 00 00 00 10 00 00 00 00 00 00 00 00 ...F............
0x128 [0x10]: PERF_RECORD_AUXTRACE_INFO type: 0
[root@s35lp76 perf]#
Output after:
# ./perf report -D -i perf.data.auxtrace |fgrep PERF_RECORD_AUXTRACE
0 0 0x128 [0x10]: PERF_RECORD_AUXTRACE_INFO type: 5
0 0 0x25a66 [0x30]: PERF_RECORD_AUXTRACE size: 0x40000
offset: 0 ref: 0 idx: 4 tid: -1 cpu: 4
....
Additional notes about the underlying hardware and software
implementation, provided by Hendrik Brueckner (see Link: below).
=============================================================================
The CPU-Measurement Facility (CPU-MF) provides a set of functions to obtain
performance information on the mainframe. Basically, it was introduced
with System z10 years ago for the z/Architecture, that means, 64-bit.
For Linux, there are two facilities of interest, counter facility and sampling
facility. The counter facility provides hardware counters for instructions,
cycles, crypto-activities, and many more.
The sampling facility is a hardware sampler that when started will write
samples at a particular interval into a sampling buffer. At some point,
for example, if a sample block is full, it generates an interrupt to collect
samples (while the sampler continues to run).
Few years ago, I started to provide the a perf PMU to use the counter
and sampling facilities. Recently, the device driver was updated to also
"export" the sampling buffer into the AUX area. Thomas now completed the
related perf work to interpret and process these AUX data.
If people are more interested in the sampling facility, they can have a
look into:
- The Load-Program-Parameter and the CPU-Measurement Facilities, SA23-2260-05
http://www-01.ibm.com/support/docview.wss?uid=isg26fcd1cc32246f4c8852574ce0044734a
and to learn how-to use it for Linux on Z, have look at chapter 54,
"Using the CPU-measurement facilities" in the:
- Device Drivers, Features, and Commands, SC33-8411-34
http://public.dhe.ibm.com/software/dw/linux390/docu/l416dd34.pdf
=============================================================================
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Link: http://lkml.kernel.org/r/20180803100758.GA28475@linux.ibm.com
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Link: http://lkml.kernel.org/r/20180802074622.13641-2-tmricht@linux.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-08-02 15:46:20 +08:00
|
|
|
case PERF_AUXTRACE_S390_CPUMSF:
|
2020-04-01 18:16:04 +08:00
|
|
|
err = s390_cpumsf_process_auxtrace_info(event, session);
|
|
|
|
break;
|
2015-04-09 23:53:53 +08:00
|
|
|
case PERF_AUXTRACE_UNKNOWN:
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-04-01 18:16:04 +08:00
|
|
|
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
unleader_auxtrace(session);
|
|
|
|
|
|
|
|
return 0;
|
2015-04-09 23:53:53 +08:00
|
|
|
}
|
|
|
|
|
2018-09-13 20:54:04 +08:00
|
|
|
s64 perf_event__process_auxtrace(struct perf_session *session,
|
|
|
|
union perf_event *event)
|
2015-04-09 23:53:53 +08:00
|
|
|
{
|
|
|
|
s64 err;
|
|
|
|
|
|
|
|
if (dump_trace)
|
2019-08-28 21:57:03 +08:00
|
|
|
fprintf(stdout, " size: %#"PRI_lx64" offset: %#"PRI_lx64" ref: %#"PRI_lx64" idx: %u tid: %d cpu: %d\n",
|
2015-04-09 23:53:53 +08:00
|
|
|
event->auxtrace.size, event->auxtrace.offset,
|
|
|
|
event->auxtrace.reference, event->auxtrace.idx,
|
|
|
|
event->auxtrace.tid, event->auxtrace.cpu);
|
|
|
|
|
|
|
|
if (auxtrace__dont_decode(session))
|
|
|
|
return event->auxtrace.size;
|
|
|
|
|
|
|
|
if (!session->auxtrace || event->header.type != PERF_RECORD_AUXTRACE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-09-13 20:54:04 +08:00
|
|
|
err = session->auxtrace->process_auxtrace_event(session, event, session->tool);
|
2015-04-09 23:53:53 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return event->auxtrace.size;
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:49 +08:00
|
|
|
#define PERF_ITRACE_DEFAULT_PERIOD_TYPE PERF_ITRACE_PERIOD_NANOSECS
|
|
|
|
#define PERF_ITRACE_DEFAULT_PERIOD 100000
|
|
|
|
#define PERF_ITRACE_DEFAULT_CALLCHAIN_SZ 16
|
|
|
|
#define PERF_ITRACE_MAX_CALLCHAIN_SZ 1024
|
2015-09-25 21:15:39 +08:00
|
|
|
#define PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ 64
|
|
|
|
#define PERF_ITRACE_MAX_LAST_BRANCH_SZ 1024
|
2015-04-09 23:53:49 +08:00
|
|
|
|
2018-09-21 02:05:37 +08:00
|
|
|
void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts,
|
|
|
|
bool no_sample)
|
2015-04-09 23:53:49 +08:00
|
|
|
{
|
|
|
|
synth_opts->branches = true;
|
2015-04-30 22:37:28 +08:00
|
|
|
synth_opts->transactions = true;
|
2017-05-26 16:17:24 +08:00
|
|
|
synth_opts->ptwrites = true;
|
2017-05-26 16:17:25 +08:00
|
|
|
synth_opts->pwr_events = true;
|
2019-08-06 16:46:03 +08:00
|
|
|
synth_opts->other_events = true;
|
2015-04-09 23:53:49 +08:00
|
|
|
synth_opts->errors = true;
|
2020-05-30 20:24:41 +08:00
|
|
|
synth_opts->flc = true;
|
|
|
|
synth_opts->llc = true;
|
|
|
|
synth_opts->tlb = true;
|
2020-11-06 17:48:50 +08:00
|
|
|
synth_opts->mem = true;
|
2020-05-30 20:24:41 +08:00
|
|
|
synth_opts->remote_access = true;
|
|
|
|
|
2018-09-21 02:05:37 +08:00
|
|
|
if (no_sample) {
|
|
|
|
synth_opts->period_type = PERF_ITRACE_PERIOD_INSTRUCTIONS;
|
|
|
|
synth_opts->period = 1;
|
|
|
|
synth_opts->calls = true;
|
|
|
|
} else {
|
|
|
|
synth_opts->instructions = true;
|
|
|
|
synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE;
|
|
|
|
synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
|
|
|
|
}
|
2015-04-09 23:53:49 +08:00
|
|
|
synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
|
2015-09-25 21:15:39 +08:00
|
|
|
synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ;
|
2016-03-29 01:45:38 +08:00
|
|
|
synth_opts->initial_skip = 0;
|
2015-04-09 23:53:49 +08:00
|
|
|
}
|
|
|
|
|
2020-07-10 23:10:57 +08:00
|
|
|
static int get_flag(const char **ptr, unsigned int *flags)
|
|
|
|
{
|
|
|
|
while (1) {
|
|
|
|
char c = **ptr;
|
|
|
|
|
|
|
|
if (c >= 'a' && c <= 'z') {
|
|
|
|
*flags |= 1 << (c - 'a');
|
|
|
|
++*ptr;
|
|
|
|
return 0;
|
|
|
|
} else if (c == ' ') {
|
|
|
|
++*ptr;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_flags(const char **ptr, unsigned int *plus_flags, unsigned int *minus_flags)
|
|
|
|
{
|
|
|
|
while (1) {
|
|
|
|
switch (**ptr) {
|
|
|
|
case '+':
|
|
|
|
++*ptr;
|
|
|
|
if (get_flag(ptr, plus_flags))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case '-':
|
|
|
|
++*ptr;
|
|
|
|
if (get_flag(ptr, minus_flags))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
++*ptr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:49 +08:00
|
|
|
/*
|
|
|
|
* Please check tools/perf/Documentation/perf-script.txt for information
|
|
|
|
* about the options parsed here, which is introduced after this cset,
|
|
|
|
* when support in 'perf script' for these options is introduced.
|
|
|
|
*/
|
2021-05-31 03:23:03 +08:00
|
|
|
int itrace_do_parse_synth_opts(struct itrace_synth_opts *synth_opts,
|
|
|
|
const char *str, int unset)
|
2015-04-09 23:53:49 +08:00
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
char *endptr;
|
2015-07-18 00:33:46 +08:00
|
|
|
bool period_type_set = false;
|
2015-09-25 21:15:32 +08:00
|
|
|
bool period_set = false;
|
2015-04-09 23:53:49 +08:00
|
|
|
|
|
|
|
synth_opts->set = true;
|
|
|
|
|
|
|
|
if (unset) {
|
|
|
|
synth_opts->dont_decode = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!str) {
|
2019-05-20 19:37:08 +08:00
|
|
|
itrace_synth_opts__set_default(synth_opts,
|
|
|
|
synth_opts->default_no_sample);
|
2015-04-09 23:53:49 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (p = str; *p;) {
|
|
|
|
switch (*p++) {
|
|
|
|
case 'i':
|
|
|
|
synth_opts->instructions = true;
|
|
|
|
while (*p == ' ' || *p == ',')
|
|
|
|
p += 1;
|
|
|
|
if (isdigit(*p)) {
|
|
|
|
synth_opts->period = strtoull(p, &endptr, 10);
|
2015-09-25 21:15:32 +08:00
|
|
|
period_set = true;
|
2015-04-09 23:53:49 +08:00
|
|
|
p = endptr;
|
|
|
|
while (*p == ' ' || *p == ',')
|
|
|
|
p += 1;
|
|
|
|
switch (*p++) {
|
|
|
|
case 'i':
|
|
|
|
synth_opts->period_type =
|
|
|
|
PERF_ITRACE_PERIOD_INSTRUCTIONS;
|
2015-07-18 00:33:46 +08:00
|
|
|
period_type_set = true;
|
2015-04-09 23:53:49 +08:00
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
synth_opts->period_type =
|
|
|
|
PERF_ITRACE_PERIOD_TICKS;
|
2015-07-18 00:33:46 +08:00
|
|
|
period_type_set = true;
|
2015-04-09 23:53:49 +08:00
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
synth_opts->period *= 1000;
|
|
|
|
/* Fall through */
|
|
|
|
case 'u':
|
|
|
|
synth_opts->period *= 1000;
|
|
|
|
/* Fall through */
|
|
|
|
case 'n':
|
|
|
|
if (*p++ != 's')
|
|
|
|
goto out_err;
|
|
|
|
synth_opts->period_type =
|
|
|
|
PERF_ITRACE_PERIOD_NANOSECS;
|
2015-07-18 00:33:46 +08:00
|
|
|
period_type_set = true;
|
2015-04-09 23:53:49 +08:00
|
|
|
break;
|
|
|
|
case '\0':
|
|
|
|
goto out;
|
|
|
|
default:
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
synth_opts->branches = true;
|
|
|
|
break;
|
2015-04-30 22:37:28 +08:00
|
|
|
case 'x':
|
|
|
|
synth_opts->transactions = true;
|
|
|
|
break;
|
2017-05-26 16:17:24 +08:00
|
|
|
case 'w':
|
|
|
|
synth_opts->ptwrites = true;
|
|
|
|
break;
|
2017-05-26 16:17:25 +08:00
|
|
|
case 'p':
|
|
|
|
synth_opts->pwr_events = true;
|
|
|
|
break;
|
2019-08-06 16:46:03 +08:00
|
|
|
case 'o':
|
|
|
|
synth_opts->other_events = true;
|
|
|
|
break;
|
2015-04-09 23:53:49 +08:00
|
|
|
case 'e':
|
|
|
|
synth_opts->errors = true;
|
2020-07-10 23:10:57 +08:00
|
|
|
if (get_flags(&p, &synth_opts->error_plus_flags,
|
|
|
|
&synth_opts->error_minus_flags))
|
|
|
|
goto out_err;
|
2015-04-09 23:53:49 +08:00
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
synth_opts->log = true;
|
2020-07-10 23:10:59 +08:00
|
|
|
if (get_flags(&p, &synth_opts->log_plus_flags,
|
|
|
|
&synth_opts->log_minus_flags))
|
|
|
|
goto out_err;
|
2015-04-09 23:53:49 +08:00
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
synth_opts->branches = true;
|
|
|
|
synth_opts->calls = true;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
synth_opts->branches = true;
|
|
|
|
synth_opts->returns = true;
|
|
|
|
break;
|
2020-04-01 18:16:05 +08:00
|
|
|
case 'G':
|
2015-04-09 23:53:49 +08:00
|
|
|
case 'g':
|
2020-04-01 18:16:05 +08:00
|
|
|
if (p[-1] == 'G')
|
|
|
|
synth_opts->add_callchain = true;
|
|
|
|
else
|
|
|
|
synth_opts->callchain = true;
|
2015-04-09 23:53:49 +08:00
|
|
|
synth_opts->callchain_sz =
|
|
|
|
PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
|
|
|
|
while (*p == ' ' || *p == ',')
|
|
|
|
p += 1;
|
|
|
|
if (isdigit(*p)) {
|
|
|
|
unsigned int val;
|
|
|
|
|
|
|
|
val = strtoul(p, &endptr, 10);
|
|
|
|
p = endptr;
|
|
|
|
if (!val || val > PERF_ITRACE_MAX_CALLCHAIN_SZ)
|
|
|
|
goto out_err;
|
|
|
|
synth_opts->callchain_sz = val;
|
|
|
|
}
|
|
|
|
break;
|
2020-04-29 23:07:46 +08:00
|
|
|
case 'L':
|
2015-09-25 21:15:39 +08:00
|
|
|
case 'l':
|
2020-04-29 23:07:46 +08:00
|
|
|
if (p[-1] == 'L')
|
|
|
|
synth_opts->add_last_branch = true;
|
|
|
|
else
|
|
|
|
synth_opts->last_branch = true;
|
2015-09-25 21:15:39 +08:00
|
|
|
synth_opts->last_branch_sz =
|
|
|
|
PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ;
|
|
|
|
while (*p == ' ' || *p == ',')
|
|
|
|
p += 1;
|
|
|
|
if (isdigit(*p)) {
|
|
|
|
unsigned int val;
|
|
|
|
|
|
|
|
val = strtoul(p, &endptr, 10);
|
|
|
|
p = endptr;
|
|
|
|
if (!val ||
|
|
|
|
val > PERF_ITRACE_MAX_LAST_BRANCH_SZ)
|
|
|
|
goto out_err;
|
|
|
|
synth_opts->last_branch_sz = val;
|
|
|
|
}
|
|
|
|
break;
|
2016-03-29 01:45:38 +08:00
|
|
|
case 's':
|
|
|
|
synth_opts->initial_skip = strtoul(p, &endptr, 10);
|
|
|
|
if (p == endptr)
|
|
|
|
goto out_err;
|
|
|
|
p = endptr;
|
|
|
|
break;
|
2020-05-30 20:24:41 +08:00
|
|
|
case 'f':
|
|
|
|
synth_opts->flc = true;
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
synth_opts->llc = true;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
synth_opts->tlb = true;
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
synth_opts->remote_access = true;
|
|
|
|
break;
|
2020-11-06 17:48:50 +08:00
|
|
|
case 'M':
|
|
|
|
synth_opts->mem = true;
|
|
|
|
break;
|
2020-07-10 23:11:02 +08:00
|
|
|
case 'q':
|
|
|
|
synth_opts->quick += 1;
|
|
|
|
break;
|
2021-10-27 16:03:30 +08:00
|
|
|
case 'A':
|
|
|
|
synth_opts->approx_ipc = true;
|
|
|
|
break;
|
2021-04-30 15:02:58 +08:00
|
|
|
case 'Z':
|
|
|
|
synth_opts->timeless_decoding = true;
|
|
|
|
break;
|
2015-04-09 23:53:49 +08:00
|
|
|
case ' ':
|
|
|
|
case ',':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (synth_opts->instructions) {
|
2015-07-18 00:33:46 +08:00
|
|
|
if (!period_type_set)
|
2015-04-09 23:53:49 +08:00
|
|
|
synth_opts->period_type =
|
|
|
|
PERF_ITRACE_DEFAULT_PERIOD_TYPE;
|
2015-09-25 21:15:32 +08:00
|
|
|
if (!period_set)
|
2015-04-09 23:53:49 +08:00
|
|
|
synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_err:
|
|
|
|
pr_err("Bad Instruction Tracing options '%s'\n", str);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-05-31 03:23:03 +08:00
|
|
|
int itrace_parse_synth_opts(const struct option *opt, const char *str, int unset)
|
|
|
|
{
|
|
|
|
return itrace_do_parse_synth_opts(opt->value, str, unset);
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:50 +08:00
|
|
|
static const char * const auxtrace_error_type_name[] = {
|
|
|
|
[PERF_AUXTRACE_ERROR_ITRACE] = "instruction trace",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *auxtrace_error_name(int type)
|
|
|
|
{
|
|
|
|
const char *error_type_name = NULL;
|
|
|
|
|
|
|
|
if (type < PERF_AUXTRACE_ERROR_MAX)
|
|
|
|
error_type_name = auxtrace_error_type_name[type];
|
|
|
|
if (!error_type_name)
|
|
|
|
error_type_name = "unknown AUX";
|
|
|
|
return error_type_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp)
|
|
|
|
{
|
2019-08-28 21:57:16 +08:00
|
|
|
struct perf_record_auxtrace_error *e = &event->auxtrace_error;
|
2019-02-06 18:39:47 +08:00
|
|
|
unsigned long long nsecs = e->time;
|
|
|
|
const char *msg = e->msg;
|
2015-04-09 23:53:50 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = fprintf(fp, " %s error type %u",
|
|
|
|
auxtrace_error_name(e->type), e->type);
|
2019-02-06 18:39:47 +08:00
|
|
|
|
|
|
|
if (e->fmt && nsecs) {
|
|
|
|
unsigned long secs = nsecs / NSEC_PER_SEC;
|
|
|
|
|
|
|
|
nsecs -= secs * NSEC_PER_SEC;
|
|
|
|
ret += fprintf(fp, " time %lu.%09llu", secs, nsecs);
|
|
|
|
} else {
|
|
|
|
ret += fprintf(fp, " time 0");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!e->fmt)
|
|
|
|
msg = (const char *)&e->time;
|
|
|
|
|
2019-08-28 21:57:04 +08:00
|
|
|
ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRI_lx64" code %u: %s\n",
|
2019-02-06 18:39:47 +08:00
|
|
|
e->cpu, e->pid, e->tid, e->ip, e->code, msg);
|
2015-04-09 23:53:50 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void perf_session__auxtrace_error_inc(struct perf_session *session,
|
|
|
|
union perf_event *event)
|
|
|
|
{
|
2019-08-28 21:57:16 +08:00
|
|
|
struct perf_record_auxtrace_error *e = &event->auxtrace_error;
|
2015-04-09 23:53:50 +08:00
|
|
|
|
|
|
|
if (e->type < PERF_AUXTRACE_ERROR_MAX)
|
|
|
|
session->evlist->stats.nr_auxtrace_errors[e->type] += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void events_stats__auxtrace_error_warn(const struct events_stats *stats)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < PERF_AUXTRACE_ERROR_MAX; i++) {
|
|
|
|
if (!stats->nr_auxtrace_errors[i])
|
|
|
|
continue;
|
|
|
|
ui__warning("%u %s errors\n",
|
|
|
|
stats->nr_auxtrace_errors[i],
|
|
|
|
auxtrace_error_name(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-13 20:54:03 +08:00
|
|
|
int perf_event__process_auxtrace_error(struct perf_session *session,
|
|
|
|
union perf_event *event)
|
2015-04-09 23:53:50 +08:00
|
|
|
{
|
2015-04-09 23:53:53 +08:00
|
|
|
if (auxtrace__dont_decode(session))
|
|
|
|
return 0;
|
|
|
|
|
2015-04-09 23:53:50 +08:00
|
|
|
perf_event__fprintf_auxtrace_error(event, stdout);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
perf auxtrace: Add compat_auxtrace_mmap__{read_head|write_tail}
When perf runs in compat mode (kernel in 64-bit mode and the perf is in
32-bit mode), the 64-bit value atomicity in the user space cannot be
assured, E.g. on some architectures, the 64-bit value accessing is split
into two instructions, one is for the low 32-bit word accessing and
another is for the high 32-bit word.
This patch introduces weak functions compat_auxtrace_mmap__read_head()
and compat_auxtrace_mmap__write_tail(), as their naming indicates, when
perf tool works in compat mode, it uses these two functions to access
the AUX head and tail. These two functions can allow the perf tool to
work properly in certain conditions, e.g. when perf tool works in
snapshot mode with only using AUX head pointer, or perf tool uses the
AUX buffer and the incremented tail is not bigger than 4GB.
When perf tool cannot handle the case when the AUX tail is bigger than
4GB, the function compat_auxtrace_mmap__write_tail() returns -1 and
tells the caller to bail out for the error.
These two functions are declared as weak attribute, this allows to
implement arch specific functions if any arch can support the 64-bit
value atomicity in compat mode.
Suggested-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Russell King (oracle)" <linux@armlinux.org.uk>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lore.kernel.org/lkml/20210829102238.19693-2-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-08-29 18:22:37 +08:00
|
|
|
/*
|
|
|
|
* In the compat mode kernel runs in 64-bit and perf tool runs in 32-bit mode,
|
|
|
|
* 32-bit perf tool cannot access 64-bit value atomically, which might lead to
|
|
|
|
* the issues caused by the below sequence on multiple CPUs: when perf tool
|
|
|
|
* accesses either the load operation or the store operation for 64-bit value,
|
|
|
|
* on some architectures the operation is divided into two instructions, one
|
|
|
|
* is for accessing the low 32-bit value and another is for the high 32-bit;
|
|
|
|
* thus these two user operations can give the kernel chances to access the
|
|
|
|
* 64-bit value, and thus leads to the unexpected load values.
|
|
|
|
*
|
|
|
|
* kernel (64-bit) user (32-bit)
|
|
|
|
*
|
|
|
|
* if (LOAD ->aux_tail) { --, LOAD ->aux_head_lo
|
|
|
|
* STORE $aux_data | ,--->
|
|
|
|
* FLUSH $aux_data | | LOAD ->aux_head_hi
|
|
|
|
* STORE ->aux_head --|-------` smp_rmb()
|
|
|
|
* } | LOAD $data
|
|
|
|
* | smp_mb()
|
|
|
|
* | STORE ->aux_tail_lo
|
|
|
|
* `----------->
|
|
|
|
* STORE ->aux_tail_hi
|
|
|
|
*
|
|
|
|
* For this reason, it's impossible for the perf tool to work correctly when
|
|
|
|
* the AUX head or tail is bigger than 4GB (more than 32 bits length); and we
|
|
|
|
* can not simply limit the AUX ring buffer to less than 4GB, the reason is
|
|
|
|
* the pointers can be increased monotonically, whatever the buffer size it is,
|
|
|
|
* at the end the head and tail can be bigger than 4GB and carry out to the
|
|
|
|
* high 32-bit.
|
|
|
|
*
|
|
|
|
* To mitigate the issues and improve the user experience, we can allow the
|
|
|
|
* perf tool working in certain conditions and bail out with error if detect
|
|
|
|
* any overflow cannot be handled.
|
|
|
|
*
|
|
|
|
* For reading the AUX head, it reads out the values for three times, and
|
|
|
|
* compares the high 4 bytes of the values between the first time and the last
|
|
|
|
* time, if there has no change for high 4 bytes injected by the kernel during
|
|
|
|
* the user reading sequence, it's safe for use the second value.
|
|
|
|
*
|
|
|
|
* When compat_auxtrace_mmap__write_tail() detects any carrying in the high
|
|
|
|
* 32 bits, it means there have two store operations in user space and it cannot
|
|
|
|
* promise the atomicity for 64-bit write, so return '-1' in this case to tell
|
|
|
|
* the caller an overflow error has happened.
|
|
|
|
*/
|
|
|
|
u64 __weak compat_auxtrace_mmap__read_head(struct auxtrace_mmap *mm)
|
|
|
|
{
|
|
|
|
struct perf_event_mmap_page *pc = mm->userpg;
|
|
|
|
u64 first, second, last;
|
|
|
|
u64 mask = (u64)(UINT32_MAX) << 32;
|
|
|
|
|
|
|
|
do {
|
|
|
|
first = READ_ONCE(pc->aux_head);
|
|
|
|
/* Ensure all reads are done after we read the head */
|
|
|
|
smp_rmb();
|
|
|
|
second = READ_ONCE(pc->aux_head);
|
|
|
|
/* Ensure all reads are done after we read the head */
|
|
|
|
smp_rmb();
|
|
|
|
last = READ_ONCE(pc->aux_head);
|
|
|
|
} while ((first & mask) != (last & mask));
|
|
|
|
|
|
|
|
return second;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __weak compat_auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail)
|
|
|
|
{
|
|
|
|
struct perf_event_mmap_page *pc = mm->userpg;
|
|
|
|
u64 mask = (u64)(UINT32_MAX) << 32;
|
|
|
|
|
|
|
|
if (tail & mask)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Ensure all reads are done before we write the tail out */
|
|
|
|
smp_mb();
|
|
|
|
WRITE_ONCE(pc->aux_tail, tail);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-28 02:30:53 +08:00
|
|
|
static int __auxtrace_mmap__read(struct mmap *map,
|
2015-04-30 22:37:31 +08:00
|
|
|
struct auxtrace_record *itr,
|
|
|
|
struct perf_tool *tool, process_auxtrace_t fn,
|
|
|
|
bool snapshot, size_t snapshot_size)
|
2015-04-09 23:53:44 +08:00
|
|
|
{
|
2018-09-13 20:54:05 +08:00
|
|
|
struct auxtrace_mmap *mm = &map->auxtrace_mmap;
|
2015-04-30 22:37:31 +08:00
|
|
|
u64 head, old = mm->prev, offset, ref;
|
2015-04-09 23:53:44 +08:00
|
|
|
unsigned char *data = mm->base;
|
|
|
|
size_t size, head_off, old_off, len1, len2, padding;
|
|
|
|
union perf_event ev;
|
|
|
|
void *data1, *data2;
|
perf auxtrace: Add compat_auxtrace_mmap__{read_head|write_tail}
When perf runs in compat mode (kernel in 64-bit mode and the perf is in
32-bit mode), the 64-bit value atomicity in the user space cannot be
assured, E.g. on some architectures, the 64-bit value accessing is split
into two instructions, one is for the low 32-bit word accessing and
another is for the high 32-bit word.
This patch introduces weak functions compat_auxtrace_mmap__read_head()
and compat_auxtrace_mmap__write_tail(), as their naming indicates, when
perf tool works in compat mode, it uses these two functions to access
the AUX head and tail. These two functions can allow the perf tool to
work properly in certain conditions, e.g. when perf tool works in
snapshot mode with only using AUX head pointer, or perf tool uses the
AUX buffer and the incremented tail is not bigger than 4GB.
When perf tool cannot handle the case when the AUX tail is bigger than
4GB, the function compat_auxtrace_mmap__write_tail() returns -1 and
tells the caller to bail out for the error.
These two functions are declared as weak attribute, this allows to
implement arch specific functions if any arch can support the 64-bit
value atomicity in compat mode.
Suggested-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Russell King (oracle)" <linux@armlinux.org.uk>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lore.kernel.org/lkml/20210829102238.19693-2-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-08-29 18:22:37 +08:00
|
|
|
int kernel_is_64_bit = perf_env__kernel_is_64_bit(evsel__env(NULL));
|
2015-04-09 23:53:44 +08:00
|
|
|
|
perf auxtrace: Add compat_auxtrace_mmap__{read_head|write_tail}
When perf runs in compat mode (kernel in 64-bit mode and the perf is in
32-bit mode), the 64-bit value atomicity in the user space cannot be
assured, E.g. on some architectures, the 64-bit value accessing is split
into two instructions, one is for the low 32-bit word accessing and
another is for the high 32-bit word.
This patch introduces weak functions compat_auxtrace_mmap__read_head()
and compat_auxtrace_mmap__write_tail(), as their naming indicates, when
perf tool works in compat mode, it uses these two functions to access
the AUX head and tail. These two functions can allow the perf tool to
work properly in certain conditions, e.g. when perf tool works in
snapshot mode with only using AUX head pointer, or perf tool uses the
AUX buffer and the incremented tail is not bigger than 4GB.
When perf tool cannot handle the case when the AUX tail is bigger than
4GB, the function compat_auxtrace_mmap__write_tail() returns -1 and
tells the caller to bail out for the error.
These two functions are declared as weak attribute, this allows to
implement arch specific functions if any arch can support the 64-bit
value atomicity in compat mode.
Suggested-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Russell King (oracle)" <linux@armlinux.org.uk>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lore.kernel.org/lkml/20210829102238.19693-2-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-08-29 18:22:37 +08:00
|
|
|
head = auxtrace_mmap__read_head(mm, kernel_is_64_bit);
|
2021-08-09 19:14:05 +08:00
|
|
|
|
|
|
|
if (snapshot &&
|
|
|
|
auxtrace_record__find_snapshot(itr, mm->idx, mm, data, &head, &old))
|
|
|
|
return -1;
|
2015-04-30 22:37:31 +08:00
|
|
|
|
2015-04-09 23:53:44 +08:00
|
|
|
if (old == head)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pr_debug3("auxtrace idx %d old %#"PRIx64" head %#"PRIx64" diff %#"PRIx64"\n",
|
|
|
|
mm->idx, old, head, head - old);
|
|
|
|
|
|
|
|
if (mm->mask) {
|
|
|
|
head_off = head & mm->mask;
|
|
|
|
old_off = old & mm->mask;
|
|
|
|
} else {
|
|
|
|
head_off = head % mm->len;
|
|
|
|
old_off = old % mm->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (head_off > old_off)
|
|
|
|
size = head_off - old_off;
|
|
|
|
else
|
|
|
|
size = mm->len - (old_off - head_off);
|
|
|
|
|
2015-04-30 22:37:31 +08:00
|
|
|
if (snapshot && size > snapshot_size)
|
|
|
|
size = snapshot_size;
|
|
|
|
|
2015-04-09 23:53:44 +08:00
|
|
|
ref = auxtrace_record__reference(itr);
|
|
|
|
|
|
|
|
if (head > old || size <= head || mm->mask) {
|
|
|
|
offset = head - size;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* When the buffer size is not a power of 2, 'head' wraps at the
|
|
|
|
* highest multiple of the buffer size, so we have to subtract
|
|
|
|
* the remainder here.
|
|
|
|
*/
|
|
|
|
u64 rem = (0ULL - mm->len) % mm->len;
|
|
|
|
|
|
|
|
offset = head - size - rem;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size > head_off) {
|
|
|
|
len1 = size - head_off;
|
|
|
|
data1 = &data[mm->len - len1];
|
|
|
|
len2 = head_off;
|
|
|
|
data2 = &data[0];
|
|
|
|
} else {
|
|
|
|
len1 = size;
|
|
|
|
data1 = &data[head_off - len1];
|
|
|
|
len2 = 0;
|
|
|
|
data2 = NULL;
|
|
|
|
}
|
|
|
|
|
2015-05-29 21:33:38 +08:00
|
|
|
if (itr->alignment) {
|
|
|
|
unsigned int unwanted = len1 % itr->alignment;
|
|
|
|
|
|
|
|
len1 -= unwanted;
|
|
|
|
size -= unwanted;
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:44 +08:00
|
|
|
/* padding must be written by fn() e.g. record__process_auxtrace() */
|
2019-02-06 18:39:43 +08:00
|
|
|
padding = size & (PERF_AUXTRACE_RECORD_ALIGNMENT - 1);
|
2015-04-09 23:53:44 +08:00
|
|
|
if (padding)
|
2019-02-06 18:39:43 +08:00
|
|
|
padding = PERF_AUXTRACE_RECORD_ALIGNMENT - padding;
|
2015-04-09 23:53:44 +08:00
|
|
|
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
|
|
|
ev.auxtrace.header.type = PERF_RECORD_AUXTRACE;
|
|
|
|
ev.auxtrace.header.size = sizeof(ev.auxtrace);
|
|
|
|
ev.auxtrace.size = size + padding;
|
|
|
|
ev.auxtrace.offset = offset;
|
|
|
|
ev.auxtrace.reference = ref;
|
|
|
|
ev.auxtrace.idx = mm->idx;
|
|
|
|
ev.auxtrace.tid = mm->tid;
|
|
|
|
ev.auxtrace.cpu = mm->cpu;
|
|
|
|
|
2018-09-13 20:54:06 +08:00
|
|
|
if (fn(tool, map, &ev, data1, len1, data2, len2))
|
2015-04-09 23:53:44 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
mm->prev = head;
|
|
|
|
|
2015-04-30 22:37:31 +08:00
|
|
|
if (!snapshot) {
|
perf auxtrace: Add compat_auxtrace_mmap__{read_head|write_tail}
When perf runs in compat mode (kernel in 64-bit mode and the perf is in
32-bit mode), the 64-bit value atomicity in the user space cannot be
assured, E.g. on some architectures, the 64-bit value accessing is split
into two instructions, one is for the low 32-bit word accessing and
another is for the high 32-bit word.
This patch introduces weak functions compat_auxtrace_mmap__read_head()
and compat_auxtrace_mmap__write_tail(), as their naming indicates, when
perf tool works in compat mode, it uses these two functions to access
the AUX head and tail. These two functions can allow the perf tool to
work properly in certain conditions, e.g. when perf tool works in
snapshot mode with only using AUX head pointer, or perf tool uses the
AUX buffer and the incremented tail is not bigger than 4GB.
When perf tool cannot handle the case when the AUX tail is bigger than
4GB, the function compat_auxtrace_mmap__write_tail() returns -1 and
tells the caller to bail out for the error.
These two functions are declared as weak attribute, this allows to
implement arch specific functions if any arch can support the 64-bit
value atomicity in compat mode.
Suggested-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Russell King (oracle)" <linux@armlinux.org.uk>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lore.kernel.org/lkml/20210829102238.19693-2-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-08-29 18:22:37 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
err = auxtrace_mmap__write_tail(mm, head, kernel_is_64_bit);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2015-04-09 23:53:44 +08:00
|
|
|
|
perf auxtrace: Add compat_auxtrace_mmap__{read_head|write_tail}
When perf runs in compat mode (kernel in 64-bit mode and the perf is in
32-bit mode), the 64-bit value atomicity in the user space cannot be
assured, E.g. on some architectures, the 64-bit value accessing is split
into two instructions, one is for the low 32-bit word accessing and
another is for the high 32-bit word.
This patch introduces weak functions compat_auxtrace_mmap__read_head()
and compat_auxtrace_mmap__write_tail(), as their naming indicates, when
perf tool works in compat mode, it uses these two functions to access
the AUX head and tail. These two functions can allow the perf tool to
work properly in certain conditions, e.g. when perf tool works in
snapshot mode with only using AUX head pointer, or perf tool uses the
AUX buffer and the incremented tail is not bigger than 4GB.
When perf tool cannot handle the case when the AUX tail is bigger than
4GB, the function compat_auxtrace_mmap__write_tail() returns -1 and
tells the caller to bail out for the error.
These two functions are declared as weak attribute, this allows to
implement arch specific functions if any arch can support the 64-bit
value atomicity in compat mode.
Suggested-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Russell King (oracle)" <linux@armlinux.org.uk>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lore.kernel.org/lkml/20210829102238.19693-2-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-08-29 18:22:37 +08:00
|
|
|
if (itr->read_finish) {
|
2015-04-30 22:37:31 +08:00
|
|
|
err = itr->read_finish(itr, mm->idx);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
2015-04-09 23:53:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2015-04-09 23:53:54 +08:00
|
|
|
|
2019-07-28 02:30:53 +08:00
|
|
|
int auxtrace_mmap__read(struct mmap *map, struct auxtrace_record *itr,
|
2015-04-30 22:37:31 +08:00
|
|
|
struct perf_tool *tool, process_auxtrace_t fn)
|
|
|
|
{
|
2018-09-13 20:54:05 +08:00
|
|
|
return __auxtrace_mmap__read(map, itr, tool, fn, false, 0);
|
2015-04-30 22:37:31 +08:00
|
|
|
}
|
|
|
|
|
2019-07-28 02:30:53 +08:00
|
|
|
int auxtrace_mmap__read_snapshot(struct mmap *map,
|
2015-04-30 22:37:31 +08:00
|
|
|
struct auxtrace_record *itr,
|
|
|
|
struct perf_tool *tool, process_auxtrace_t fn,
|
|
|
|
size_t snapshot_size)
|
|
|
|
{
|
2018-09-13 20:54:05 +08:00
|
|
|
return __auxtrace_mmap__read(map, itr, tool, fn, true, snapshot_size);
|
2015-04-30 22:37:31 +08:00
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:54 +08:00
|
|
|
/**
|
|
|
|
* struct auxtrace_cache - hash table to implement a cache
|
|
|
|
* @hashtable: the hashtable
|
|
|
|
* @sz: hashtable size (number of hlists)
|
|
|
|
* @entry_size: size of an entry
|
|
|
|
* @limit: limit the number of entries to this maximum, when reached the cache
|
|
|
|
* is dropped and caching begins again with an empty cache
|
|
|
|
* @cnt: current number of entries
|
|
|
|
* @bits: hashtable size (@sz = 2^@bits)
|
|
|
|
*/
|
|
|
|
struct auxtrace_cache {
|
|
|
|
struct hlist_head *hashtable;
|
|
|
|
size_t sz;
|
|
|
|
size_t entry_size;
|
|
|
|
size_t limit;
|
|
|
|
size_t cnt;
|
|
|
|
unsigned int bits;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct auxtrace_cache *auxtrace_cache__new(unsigned int bits, size_t entry_size,
|
|
|
|
unsigned int limit_percent)
|
|
|
|
{
|
|
|
|
struct auxtrace_cache *c;
|
|
|
|
struct hlist_head *ht;
|
|
|
|
size_t sz, i;
|
|
|
|
|
|
|
|
c = zalloc(sizeof(struct auxtrace_cache));
|
|
|
|
if (!c)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sz = 1UL << bits;
|
|
|
|
|
|
|
|
ht = calloc(sz, sizeof(struct hlist_head));
|
|
|
|
if (!ht)
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
for (i = 0; i < sz; i++)
|
|
|
|
INIT_HLIST_HEAD(&ht[i]);
|
|
|
|
|
|
|
|
c->hashtable = ht;
|
|
|
|
c->sz = sz;
|
|
|
|
c->entry_size = entry_size;
|
|
|
|
c->limit = (c->sz * limit_percent) / 100;
|
|
|
|
c->bits = bits;
|
|
|
|
|
|
|
|
return c;
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
free(c);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void auxtrace_cache__drop(struct auxtrace_cache *c)
|
|
|
|
{
|
|
|
|
struct auxtrace_cache_entry *entry;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!c)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < c->sz; i++) {
|
|
|
|
hlist_for_each_entry_safe(entry, tmp, &c->hashtable[i], hash) {
|
|
|
|
hlist_del(&entry->hash);
|
|
|
|
auxtrace_cache__free_entry(c, entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c->cnt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_cache__free(struct auxtrace_cache *c)
|
|
|
|
{
|
|
|
|
if (!c)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auxtrace_cache__drop(c);
|
2019-07-04 23:06:20 +08:00
|
|
|
zfree(&c->hashtable);
|
2015-04-09 23:53:54 +08:00
|
|
|
free(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *auxtrace_cache__alloc_entry(struct auxtrace_cache *c)
|
|
|
|
{
|
|
|
|
return malloc(c->entry_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_cache__free_entry(struct auxtrace_cache *c __maybe_unused,
|
|
|
|
void *entry)
|
|
|
|
{
|
|
|
|
free(entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
int auxtrace_cache__add(struct auxtrace_cache *c, u32 key,
|
|
|
|
struct auxtrace_cache_entry *entry)
|
|
|
|
{
|
|
|
|
if (c->limit && ++c->cnt > c->limit)
|
|
|
|
auxtrace_cache__drop(c);
|
|
|
|
|
|
|
|
entry->key = key;
|
|
|
|
hlist_add_head(&entry->hash, &c->hashtable[hash_32(key, c->bits)]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-25 20:59:59 +08:00
|
|
|
static struct auxtrace_cache_entry *auxtrace_cache__rm(struct auxtrace_cache *c,
|
|
|
|
u32 key)
|
|
|
|
{
|
|
|
|
struct auxtrace_cache_entry *entry;
|
|
|
|
struct hlist_head *hlist;
|
|
|
|
struct hlist_node *n;
|
|
|
|
|
|
|
|
if (!c)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
hlist = &c->hashtable[hash_32(key, c->bits)];
|
|
|
|
hlist_for_each_entry_safe(entry, n, hlist, hash) {
|
|
|
|
if (entry->key == key) {
|
|
|
|
hlist_del(&entry->hash);
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace_cache__remove(struct auxtrace_cache *c, u32 key)
|
|
|
|
{
|
|
|
|
struct auxtrace_cache_entry *entry = auxtrace_cache__rm(c, key);
|
|
|
|
|
|
|
|
auxtrace_cache__free_entry(c, entry);
|
|
|
|
}
|
|
|
|
|
2015-04-09 23:53:54 +08:00
|
|
|
void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key)
|
|
|
|
{
|
|
|
|
struct auxtrace_cache_entry *entry;
|
|
|
|
struct hlist_head *hlist;
|
|
|
|
|
|
|
|
if (!c)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
hlist = &c->hashtable[hash_32(key, c->bits)];
|
|
|
|
hlist_for_each_entry(entry, hlist, hash) {
|
|
|
|
if (entry->key == key)
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-09-23 22:38:39 +08:00
|
|
|
|
|
|
|
static void addr_filter__free_str(struct addr_filter *filt)
|
|
|
|
{
|
2019-07-04 23:06:20 +08:00
|
|
|
zfree(&filt->str);
|
2016-09-23 22:38:39 +08:00
|
|
|
filt->action = NULL;
|
|
|
|
filt->sym_from = NULL;
|
|
|
|
filt->sym_to = NULL;
|
|
|
|
filt->filename = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct addr_filter *addr_filter__new(void)
|
|
|
|
{
|
|
|
|
struct addr_filter *filt = zalloc(sizeof(*filt));
|
|
|
|
|
|
|
|
if (filt)
|
|
|
|
INIT_LIST_HEAD(&filt->list);
|
|
|
|
|
|
|
|
return filt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addr_filter__free(struct addr_filter *filt)
|
|
|
|
{
|
|
|
|
if (filt)
|
|
|
|
addr_filter__free_str(filt);
|
|
|
|
free(filt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addr_filters__add(struct addr_filters *filts,
|
|
|
|
struct addr_filter *filt)
|
|
|
|
{
|
|
|
|
list_add_tail(&filt->list, &filts->head);
|
|
|
|
filts->cnt += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addr_filters__del(struct addr_filters *filts,
|
|
|
|
struct addr_filter *filt)
|
|
|
|
{
|
|
|
|
list_del_init(&filt->list);
|
|
|
|
filts->cnt -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addr_filters__init(struct addr_filters *filts)
|
|
|
|
{
|
|
|
|
INIT_LIST_HEAD(&filts->head);
|
|
|
|
filts->cnt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addr_filters__exit(struct addr_filters *filts)
|
|
|
|
{
|
|
|
|
struct addr_filter *filt, *n;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(filt, n, &filts->head, list) {
|
|
|
|
addr_filters__del(filts, filt);
|
|
|
|
addr_filter__free(filt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_num_or_str(char **inp, u64 *num, const char **str,
|
|
|
|
const char *str_delim)
|
|
|
|
{
|
|
|
|
*inp += strspn(*inp, " ");
|
|
|
|
|
|
|
|
if (isdigit(**inp)) {
|
|
|
|
char *endptr;
|
|
|
|
|
|
|
|
if (!num)
|
|
|
|
return -EINVAL;
|
|
|
|
errno = 0;
|
|
|
|
*num = strtoull(*inp, &endptr, 0);
|
|
|
|
if (errno)
|
|
|
|
return -errno;
|
|
|
|
if (endptr == *inp)
|
|
|
|
return -EINVAL;
|
|
|
|
*inp = endptr;
|
|
|
|
} else {
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return -EINVAL;
|
|
|
|
*inp += strspn(*inp, " ");
|
|
|
|
*str = *inp;
|
|
|
|
n = strcspn(*inp, str_delim);
|
|
|
|
if (!n)
|
|
|
|
return -EINVAL;
|
|
|
|
*inp += n;
|
|
|
|
if (**inp) {
|
|
|
|
**inp = '\0';
|
|
|
|
*inp += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_action(struct addr_filter *filt)
|
|
|
|
{
|
|
|
|
if (!strcmp(filt->action, "filter")) {
|
|
|
|
filt->start = true;
|
|
|
|
filt->range = true;
|
|
|
|
} else if (!strcmp(filt->action, "start")) {
|
|
|
|
filt->start = true;
|
|
|
|
} else if (!strcmp(filt->action, "stop")) {
|
|
|
|
filt->start = false;
|
|
|
|
} else if (!strcmp(filt->action, "tracestop")) {
|
|
|
|
filt->start = false;
|
|
|
|
filt->range = true;
|
|
|
|
filt->action += 5; /* Change 'tracestop' to 'stop' */
|
|
|
|
} else {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_sym_idx(char **inp, int *idx)
|
|
|
|
{
|
|
|
|
*idx = -1;
|
|
|
|
|
|
|
|
*inp += strspn(*inp, " ");
|
|
|
|
|
|
|
|
if (**inp != '#')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
*inp += 1;
|
|
|
|
|
|
|
|
if (**inp == 'g' || **inp == 'G') {
|
|
|
|
*inp += 1;
|
|
|
|
*idx = 0;
|
|
|
|
} else {
|
|
|
|
unsigned long num;
|
|
|
|
char *endptr;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
num = strtoul(*inp, &endptr, 0);
|
|
|
|
if (errno)
|
|
|
|
return -errno;
|
|
|
|
if (endptr == *inp || num > INT_MAX)
|
|
|
|
return -EINVAL;
|
|
|
|
*inp = endptr;
|
|
|
|
*idx = num;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_addr_size(char **inp, u64 *num, const char **str, int *idx)
|
|
|
|
{
|
|
|
|
int err = parse_num_or_str(inp, num, str, " ");
|
|
|
|
|
|
|
|
if (!err && *str)
|
|
|
|
err = parse_sym_idx(inp, idx);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_one_filter(struct addr_filter *filt, const char **filter_inp)
|
|
|
|
{
|
|
|
|
char *fstr;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
filt->str = fstr = strdup(*filter_inp);
|
|
|
|
if (!fstr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
err = parse_num_or_str(&fstr, NULL, &filt->action, " ");
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
err = parse_action(filt);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
err = parse_addr_size(&fstr, &filt->addr, &filt->sym_from,
|
|
|
|
&filt->sym_from_idx);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
fstr += strspn(fstr, " ");
|
|
|
|
|
|
|
|
if (*fstr == '/') {
|
|
|
|
fstr += 1;
|
|
|
|
err = parse_addr_size(&fstr, &filt->size, &filt->sym_to,
|
|
|
|
&filt->sym_to_idx);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
filt->range = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fstr += strspn(fstr, " ");
|
|
|
|
|
|
|
|
if (*fstr == '@') {
|
|
|
|
fstr += 1;
|
|
|
|
err = parse_num_or_str(&fstr, NULL, &filt->filename, " ,");
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
fstr += strspn(fstr, " ,");
|
|
|
|
|
|
|
|
*filter_inp += fstr - filt->str;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_err:
|
|
|
|
addr_filter__free_str(filt);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int addr_filters__parse_bare_filter(struct addr_filters *filts,
|
|
|
|
const char *filter)
|
|
|
|
{
|
|
|
|
struct addr_filter *filt;
|
|
|
|
const char *fstr = filter;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
while (*fstr) {
|
|
|
|
filt = addr_filter__new();
|
|
|
|
err = parse_one_filter(filt, &fstr);
|
|
|
|
if (err) {
|
|
|
|
addr_filter__free(filt);
|
|
|
|
addr_filters__exit(filts);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
addr_filters__add(filts, filt);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sym_args {
|
|
|
|
const char *name;
|
|
|
|
u64 start;
|
|
|
|
u64 size;
|
|
|
|
int idx;
|
|
|
|
int cnt;
|
|
|
|
bool started;
|
|
|
|
bool global;
|
|
|
|
bool selected;
|
|
|
|
bool duplicate;
|
|
|
|
bool near;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool kern_sym_match(struct sym_args *args, const char *name, char type)
|
|
|
|
{
|
|
|
|
/* A function with the same name, and global or the n'th found or any */
|
2018-04-26 04:16:31 +08:00
|
|
|
return kallsyms__is_function(type) &&
|
2016-09-23 22:38:39 +08:00
|
|
|
!strcmp(name, args->name) &&
|
|
|
|
((args->global && isupper(type)) ||
|
|
|
|
(args->selected && ++(args->cnt) == args->idx) ||
|
|
|
|
(!args->global && !args->selected));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int find_kern_sym_cb(void *arg, const char *name, char type, u64 start)
|
|
|
|
{
|
|
|
|
struct sym_args *args = arg;
|
|
|
|
|
|
|
|
if (args->started) {
|
|
|
|
if (!args->size)
|
|
|
|
args->size = start - args->start;
|
|
|
|
if (args->selected) {
|
|
|
|
if (args->size)
|
|
|
|
return 1;
|
|
|
|
} else if (kern_sym_match(args, name, type)) {
|
|
|
|
args->duplicate = true;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else if (kern_sym_match(args, name, type)) {
|
|
|
|
args->started = true;
|
|
|
|
args->start = start;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int print_kern_sym_cb(void *arg, const char *name, char type, u64 start)
|
|
|
|
{
|
|
|
|
struct sym_args *args = arg;
|
|
|
|
|
|
|
|
if (kern_sym_match(args, name, type)) {
|
|
|
|
pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
|
|
|
|
++args->cnt, start, type, name);
|
|
|
|
args->near = true;
|
|
|
|
} else if (args->near) {
|
|
|
|
args->near = false;
|
|
|
|
pr_err("\t\twhich is near\t\t%s\n", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sym_not_found_error(const char *sym_name, int idx)
|
|
|
|
{
|
|
|
|
if (idx > 0) {
|
|
|
|
pr_err("N'th occurrence (N=%d) of symbol '%s' not found.\n",
|
|
|
|
idx, sym_name);
|
|
|
|
} else if (!idx) {
|
|
|
|
pr_err("Global symbol '%s' not found.\n", sym_name);
|
|
|
|
} else {
|
|
|
|
pr_err("Symbol '%s' not found.\n", sym_name);
|
|
|
|
}
|
|
|
|
pr_err("Note that symbols must be functions.\n");
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int find_kern_sym(const char *sym_name, u64 *start, u64 *size, int idx)
|
|
|
|
{
|
|
|
|
struct sym_args args = {
|
|
|
|
.name = sym_name,
|
|
|
|
.idx = idx,
|
|
|
|
.global = !idx,
|
|
|
|
.selected = idx > 0,
|
|
|
|
};
|
|
|
|
int err;
|
|
|
|
|
|
|
|
*start = 0;
|
|
|
|
*size = 0;
|
|
|
|
|
|
|
|
err = kallsyms__parse("/proc/kallsyms", &args, find_kern_sym_cb);
|
|
|
|
if (err < 0) {
|
|
|
|
pr_err("Failed to parse /proc/kallsyms\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (args.duplicate) {
|
|
|
|
pr_err("Multiple kernel symbols with name '%s'\n", sym_name);
|
|
|
|
args.cnt = 0;
|
|
|
|
kallsyms__parse("/proc/kallsyms", &args, print_kern_sym_cb);
|
|
|
|
pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n",
|
|
|
|
sym_name);
|
|
|
|
pr_err("Or select a global symbol by inserting #0 or #g or #G\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!args.started) {
|
|
|
|
pr_err("Kernel symbol lookup: ");
|
|
|
|
return sym_not_found_error(sym_name, idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
*start = args.start;
|
|
|
|
*size = args.size;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int find_entire_kern_cb(void *arg, const char *name __maybe_unused,
|
|
|
|
char type, u64 start)
|
|
|
|
{
|
|
|
|
struct sym_args *args = arg;
|
|
|
|
|
2018-04-26 04:16:31 +08:00
|
|
|
if (!kallsyms__is_function(type))
|
2016-09-23 22:38:39 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!args->started) {
|
|
|
|
args->started = true;
|
|
|
|
args->start = start;
|
|
|
|
}
|
|
|
|
/* Don't know exactly where the kernel ends, so we add a page */
|
|
|
|
args->size = round_up(start, page_size) + page_size - args->start;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int addr_filter__entire_kernel(struct addr_filter *filt)
|
|
|
|
{
|
|
|
|
struct sym_args args = { .started = false };
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = kallsyms__parse("/proc/kallsyms", &args, find_entire_kern_cb);
|
|
|
|
if (err < 0 || !args.started) {
|
|
|
|
pr_err("Failed to parse /proc/kallsyms\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
filt->addr = args.start;
|
|
|
|
filt->size = args.size;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_end_after_start(struct addr_filter *filt, u64 start, u64 size)
|
|
|
|
{
|
|
|
|
if (start + size >= filt->addr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (filt->sym_from) {
|
|
|
|
pr_err("Symbol '%s' (0x%"PRIx64") comes before '%s' (0x%"PRIx64")\n",
|
|
|
|
filt->sym_to, start, filt->sym_from, filt->addr);
|
|
|
|
} else {
|
|
|
|
pr_err("Symbol '%s' (0x%"PRIx64") comes before address 0x%"PRIx64")\n",
|
|
|
|
filt->sym_to, start, filt->addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int addr_filter__resolve_kernel_syms(struct addr_filter *filt)
|
|
|
|
{
|
|
|
|
bool no_size = false;
|
|
|
|
u64 start, size;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (symbol_conf.kptr_restrict) {
|
|
|
|
pr_err("Kernel addresses are restricted. Unable to resolve kernel symbols.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filt->sym_from && !strcmp(filt->sym_from, "*"))
|
|
|
|
return addr_filter__entire_kernel(filt);
|
|
|
|
|
|
|
|
if (filt->sym_from) {
|
|
|
|
err = find_kern_sym(filt->sym_from, &start, &size,
|
|
|
|
filt->sym_from_idx);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
filt->addr = start;
|
|
|
|
if (filt->range && !filt->size && !filt->sym_to) {
|
|
|
|
filt->size = size;
|
2017-03-24 20:15:52 +08:00
|
|
|
no_size = !size;
|
2016-09-23 22:38:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filt->sym_to) {
|
|
|
|
err = find_kern_sym(filt->sym_to, &start, &size,
|
|
|
|
filt->sym_to_idx);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = check_end_after_start(filt, start, size);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
filt->size = start + size - filt->addr;
|
2017-03-24 20:15:52 +08:00
|
|
|
no_size = !size;
|
2016-09-23 22:38:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The very last symbol in kallsyms does not imply a particular size */
|
|
|
|
if (no_size) {
|
|
|
|
pr_err("Cannot determine size of symbol '%s'\n",
|
|
|
|
filt->sym_to ? filt->sym_to : filt->sym_from);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dso *load_dso(const char *name)
|
|
|
|
{
|
|
|
|
struct map *map;
|
|
|
|
struct dso *dso;
|
|
|
|
|
|
|
|
map = dso__new_map(name);
|
|
|
|
if (!map)
|
|
|
|
return NULL;
|
|
|
|
|
2019-03-01 20:29:02 +08:00
|
|
|
if (map__load(map) < 0)
|
|
|
|
pr_err("File '%s' not found or has no symbols.\n", name);
|
2016-09-23 22:38:39 +08:00
|
|
|
|
|
|
|
dso = dso__get(map->dso);
|
|
|
|
|
|
|
|
map__put(map);
|
|
|
|
|
|
|
|
return dso;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt,
|
|
|
|
int idx)
|
|
|
|
{
|
|
|
|
/* Same name, and global or the n'th found or any */
|
|
|
|
return !arch__compare_symbol_names(name, sym->name) &&
|
|
|
|
((!idx && sym->binding == STB_GLOBAL) ||
|
|
|
|
(idx > 0 && ++*cnt == idx) ||
|
|
|
|
idx < 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_duplicate_syms(struct dso *dso, const char *sym_name)
|
|
|
|
{
|
|
|
|
struct symbol *sym;
|
|
|
|
bool near = false;
|
|
|
|
int cnt = 0;
|
|
|
|
|
|
|
|
pr_err("Multiple symbols with name '%s'\n", sym_name);
|
|
|
|
|
2018-04-26 04:01:46 +08:00
|
|
|
sym = dso__first_symbol(dso);
|
2016-09-23 22:38:39 +08:00
|
|
|
while (sym) {
|
|
|
|
if (dso_sym_match(sym, sym_name, &cnt, -1)) {
|
|
|
|
pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
|
|
|
|
++cnt, sym->start,
|
|
|
|
sym->binding == STB_GLOBAL ? 'g' :
|
|
|
|
sym->binding == STB_LOCAL ? 'l' : 'w',
|
|
|
|
sym->name);
|
|
|
|
near = true;
|
|
|
|
} else if (near) {
|
|
|
|
near = false;
|
|
|
|
pr_err("\t\twhich is near\t\t%s\n", sym->name);
|
|
|
|
}
|
|
|
|
sym = dso__next_symbol(sym);
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n",
|
|
|
|
sym_name);
|
|
|
|
pr_err("Or select a global symbol by inserting #0 or #g or #G\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
|
|
|
|
u64 *size, int idx)
|
|
|
|
{
|
|
|
|
struct symbol *sym;
|
|
|
|
int cnt = 0;
|
|
|
|
|
|
|
|
*start = 0;
|
|
|
|
*size = 0;
|
|
|
|
|
2018-04-26 04:01:46 +08:00
|
|
|
sym = dso__first_symbol(dso);
|
2016-09-23 22:38:39 +08:00
|
|
|
while (sym) {
|
|
|
|
if (*start) {
|
|
|
|
if (!*size)
|
|
|
|
*size = sym->start - *start;
|
|
|
|
if (idx > 0) {
|
|
|
|
if (*size)
|
|
|
|
return 1;
|
|
|
|
} else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
|
|
|
|
print_duplicate_syms(dso, sym_name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
|
|
|
|
*start = sym->start;
|
|
|
|
*size = sym->end - sym->start;
|
|
|
|
}
|
|
|
|
sym = dso__next_symbol(sym);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*start)
|
|
|
|
return sym_not_found_error(sym_name, idx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int addr_filter__entire_dso(struct addr_filter *filt, struct dso *dso)
|
|
|
|
{
|
2018-11-27 16:46:34 +08:00
|
|
|
if (dso__data_file_size(dso, NULL)) {
|
|
|
|
pr_err("Failed to determine filter for %s\nCannot determine file size.\n",
|
2016-09-23 22:38:39 +08:00
|
|
|
filt->filename);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-11-27 16:46:34 +08:00
|
|
|
filt->addr = 0;
|
|
|
|
filt->size = dso->data.file_size;
|
2016-09-23 22:38:39 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int addr_filter__resolve_syms(struct addr_filter *filt)
|
|
|
|
{
|
|
|
|
u64 start, size;
|
|
|
|
struct dso *dso;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (!filt->sym_from && !filt->sym_to)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!filt->filename)
|
|
|
|
return addr_filter__resolve_kernel_syms(filt);
|
|
|
|
|
|
|
|
dso = load_dso(filt->filename);
|
|
|
|
if (!dso) {
|
|
|
|
pr_err("Failed to load symbols from: %s\n", filt->filename);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filt->sym_from && !strcmp(filt->sym_from, "*")) {
|
|
|
|
err = addr_filter__entire_dso(filt, dso);
|
|
|
|
goto put_dso;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filt->sym_from) {
|
|
|
|
err = find_dso_sym(dso, filt->sym_from, &start, &size,
|
|
|
|
filt->sym_from_idx);
|
|
|
|
if (err)
|
|
|
|
goto put_dso;
|
|
|
|
filt->addr = start;
|
|
|
|
if (filt->range && !filt->size && !filt->sym_to)
|
|
|
|
filt->size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filt->sym_to) {
|
|
|
|
err = find_dso_sym(dso, filt->sym_to, &start, &size,
|
|
|
|
filt->sym_to_idx);
|
|
|
|
if (err)
|
|
|
|
goto put_dso;
|
|
|
|
|
|
|
|
err = check_end_after_start(filt, start, size);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
filt->size = start + size - filt->addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
put_dso:
|
|
|
|
dso__put(dso);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *addr_filter__to_str(struct addr_filter *filt)
|
|
|
|
{
|
|
|
|
char filename_buf[PATH_MAX];
|
|
|
|
const char *at = "";
|
|
|
|
const char *fn = "";
|
|
|
|
char *filter;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (filt->filename) {
|
|
|
|
at = "@";
|
|
|
|
fn = realpath(filt->filename, filename_buf);
|
|
|
|
if (!fn)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filt->range) {
|
|
|
|
err = asprintf(&filter, "%s 0x%"PRIx64"/0x%"PRIx64"%s%s",
|
|
|
|
filt->action, filt->addr, filt->size, at, fn);
|
|
|
|
} else {
|
|
|
|
err = asprintf(&filter, "%s 0x%"PRIx64"%s%s",
|
|
|
|
filt->action, filt->addr, at, fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err < 0 ? NULL : filter;
|
|
|
|
}
|
|
|
|
|
2019-07-21 19:23:51 +08:00
|
|
|
static int parse_addr_filter(struct evsel *evsel, const char *filter,
|
2016-09-23 22:38:39 +08:00
|
|
|
int max_nr)
|
|
|
|
{
|
|
|
|
struct addr_filters filts;
|
|
|
|
struct addr_filter *filt;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
addr_filters__init(&filts);
|
|
|
|
|
|
|
|
err = addr_filters__parse_bare_filter(&filts, filter);
|
|
|
|
if (err)
|
|
|
|
goto out_exit;
|
|
|
|
|
|
|
|
if (filts.cnt > max_nr) {
|
|
|
|
pr_err("Error: number of address filters (%d) exceeds maximum (%d)\n",
|
|
|
|
filts.cnt, max_nr);
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(filt, &filts.head, list) {
|
|
|
|
char *new_filter;
|
|
|
|
|
|
|
|
err = addr_filter__resolve_syms(filt);
|
|
|
|
if (err)
|
|
|
|
goto out_exit;
|
|
|
|
|
|
|
|
new_filter = addr_filter__to_str(filt);
|
|
|
|
if (!new_filter) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out_exit;
|
|
|
|
}
|
|
|
|
|
2020-04-30 03:19:05 +08:00
|
|
|
if (evsel__append_addr_filter(evsel, new_filter)) {
|
2016-09-23 22:38:39 +08:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto out_exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out_exit:
|
|
|
|
addr_filters__exit(&filts);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
pr_err("Failed to parse address filter: '%s'\n", filter);
|
|
|
|
pr_err("Filter format is: filter|start|stop|tracestop <start symbol or address> [/ <end symbol or size>] [@<file name>]\n");
|
|
|
|
pr_err("Where multiple filters are separated by space or comma.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-04-30 03:19:05 +08:00
|
|
|
static int evsel__nr_addr_filter(struct evsel *evsel)
|
2016-09-23 22:38:39 +08:00
|
|
|
{
|
2020-04-30 02:50:10 +08:00
|
|
|
struct perf_pmu *pmu = evsel__find_pmu(evsel);
|
2016-09-23 22:38:39 +08:00
|
|
|
int nr_addr_filters = 0;
|
|
|
|
|
|
|
|
if (!pmu)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
perf_pmu__scan_file(pmu, "nr_addr_filters", "%d", &nr_addr_filters);
|
|
|
|
|
|
|
|
return nr_addr_filters;
|
|
|
|
}
|
|
|
|
|
2019-07-21 19:23:52 +08:00
|
|
|
int auxtrace_parse_filters(struct evlist *evlist)
|
2016-09-23 22:38:39 +08:00
|
|
|
{
|
2019-07-21 19:23:51 +08:00
|
|
|
struct evsel *evsel;
|
2016-09-23 22:38:39 +08:00
|
|
|
char *filter;
|
|
|
|
int err, max_nr;
|
|
|
|
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
|
|
|
filter = evsel->filter;
|
2020-04-30 03:19:05 +08:00
|
|
|
max_nr = evsel__nr_addr_filter(evsel);
|
2016-09-23 22:38:39 +08:00
|
|
|
if (!filter || !max_nr)
|
|
|
|
continue;
|
|
|
|
evsel->filter = NULL;
|
|
|
|
err = parse_addr_filter(evsel, filter, max_nr);
|
|
|
|
free(filter);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
pr_debug("Address filter: %s\n", evsel->filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-08-31 01:45:20 +08:00
|
|
|
|
|
|
|
int auxtrace__process_event(struct perf_session *session, union perf_event *event,
|
|
|
|
struct perf_sample *sample, struct perf_tool *tool)
|
|
|
|
{
|
|
|
|
if (!session->auxtrace)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return session->auxtrace->process_event(session, event, sample, tool);
|
|
|
|
}
|
|
|
|
|
2019-11-15 20:42:19 +08:00
|
|
|
void auxtrace__dump_auxtrace_sample(struct perf_session *session,
|
|
|
|
struct perf_sample *sample)
|
|
|
|
{
|
|
|
|
if (!session->auxtrace || !session->auxtrace->dump_auxtrace_sample ||
|
|
|
|
auxtrace__dont_decode(session))
|
|
|
|
return;
|
|
|
|
|
|
|
|
session->auxtrace->dump_auxtrace_sample(session, sample);
|
|
|
|
}
|
|
|
|
|
2019-08-31 01:45:20 +08:00
|
|
|
int auxtrace__flush_events(struct perf_session *session, struct perf_tool *tool)
|
|
|
|
{
|
|
|
|
if (!session->auxtrace)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return session->auxtrace->flush_events(session, tool);
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace__free_events(struct perf_session *session)
|
|
|
|
{
|
|
|
|
if (!session->auxtrace)
|
|
|
|
return;
|
|
|
|
|
|
|
|
return session->auxtrace->free_events(session);
|
|
|
|
}
|
|
|
|
|
|
|
|
void auxtrace__free(struct perf_session *session)
|
|
|
|
{
|
|
|
|
if (!session->auxtrace)
|
|
|
|
return;
|
|
|
|
|
|
|
|
return session->auxtrace->free(session);
|
|
|
|
}
|
2020-04-01 18:15:58 +08:00
|
|
|
|
|
|
|
bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
|
|
|
|
struct evsel *evsel)
|
|
|
|
{
|
|
|
|
if (!session->auxtrace || !session->auxtrace->evsel_is_auxtrace)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return session->auxtrace->evsel_is_auxtrace(session, evsel);
|
|
|
|
}
|