mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 22:21:42 +00:00
perf/core improvements and fixes:
Hardware tracing: Adrian Hunter: - Handle calls optimized into jumps to a different symbol in the thread stack routines used to process hardware traces (Adrian Hunter) Intel PT: Adrian Hunter: - Fix overlap calculation for padding. - Fix CYC timestamp calculation after OVF. - Packet splitting can only happen in 32-bit. - Add timestamp to auxtrace errors. ARM CoreSight: Leo Yan: - Add last instruction information in packet - Set sample flags for instruction range, exception and return packets and for a trace discontinuity. - Add exception number in exception packet - Change tuple from traceID-CPU# to traceID-metadata - Add traceID in packet Mathieu Poirier: - Add "sinks" group to PMU directory - Use event attributes to send sink information to kernel - Remove set_drv_config() API, no longer used. perf annotate: Jiri Olsa: - Delay symbol annotation to the resort phase, speeding up 'perf report' startup. perf record: Alexey Budankov: - Allow binding userspace buffers to NUMA nodes. Symbols: Adrian Hunter: - Fix calculating of symbol sizes when splitting kallsyms into maps for kcore processing. Vendor events: William Cohen: - Intel: Fix Load_Miss_Real_Latency on CLX Misc: Arnaldo Carvalho de Melo: - Streamline headers, removing includes when all that is needed are just forward declarations, fixup the fallout for cases where headers should have been explicitely included but were instead obtained indirectly, by sheer luck. - Add fallback versions for CPU_{OR,EQUAL}(), so that code using it continue to build on older systems where those were not yet introduced or in systems using some other libc than the GNU one where those helpers aren't present. Documentation: Changbin Du: - Add documentation for BPF event selection. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQR2GiIUctdOfX2qHhGyPKLppCJ+JwUCXFsqugAKCRCyPKLppCJ+ JzpwAQDEh1mNZoxfdGZEi9d+8p2hnRlOs3GOUG4iGnqAYfae4QEAkMJ0V1wrmkdw NXgV+PgWfDcgbD4Cn90eWA8M6KEcbgA= =ogOF -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-5.1-20190206' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: Hardware tracing: Adrian Hunter: - Handle calls optimized into jumps to a different symbol in the thread stack routines used to process hardware traces (Adrian Hunter) Intel PT: Adrian Hunter: - Fix overlap calculation for padding. - Fix CYC timestamp calculation after OVF. - Packet splitting can only happen in 32-bit. - Add timestamp to auxtrace errors. ARM CoreSight: Leo Yan: - Add last instruction information in packet - Set sample flags for instruction range, exception and return packets and for a trace discontinuity. - Add exception number in exception packet - Change tuple from traceID-CPU# to traceID-metadata - Add traceID in packet Mathieu Poirier: - Add "sinks" group to PMU directory - Use event attributes to send sink information to kernel - Remove set_drv_config() API, no longer used. perf annotate: Jiri Olsa: - Delay symbol annotation to the resort phase, speeding up 'perf report' startup. perf record: Alexey Budankov: - Allow binding userspace buffers to NUMA nodes. Symbols: Adrian Hunter: - Fix calculating of symbol sizes when splitting kallsyms into maps for kcore processing. Vendor events: William Cohen: - Intel: Fix Load_Miss_Real_Latency on CLX Misc: Arnaldo Carvalho de Melo: - Streamline headers, removing includes when all that is needed are just forward declarations, fixup the fallout for cases where headers should have been explicitely included but were instead obtained indirectly, by sheer luck. - Add fallback versions for CPU_{OR,EQUAL}(), so that code using it continue to build on older systems where those were not yet introduced or in systems using some other libc than the GNU one where those helpers aren't present. Documentation: Changbin Du: - Add documentation for BPF event selection. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
6854daa07a
@ -1600,7 +1600,7 @@ static void aux_sdb_init(unsigned long sdb)
|
||||
|
||||
/*
|
||||
* aux_buffer_setup() - Setup AUX buffer for diagnostic mode sampling
|
||||
* @cpu: On which to allocate, -1 means current
|
||||
* @event: Event the buffer is setup for, event->cpu == -1 means current
|
||||
* @pages: Array of pointers to buffer pages passed from perf core
|
||||
* @nr_pages: Total pages
|
||||
* @snapshot: Flag for snapshot mode
|
||||
@ -1612,8 +1612,8 @@ static void aux_sdb_init(unsigned long sdb)
|
||||
*
|
||||
* Return the private AUX buffer structure if success or NULL if fails.
|
||||
*/
|
||||
static void *aux_buffer_setup(int cpu, void **pages, int nr_pages,
|
||||
bool snapshot)
|
||||
static void *aux_buffer_setup(struct perf_event *event, void **pages,
|
||||
int nr_pages, bool snapshot)
|
||||
{
|
||||
struct sf_buffer *sfb;
|
||||
struct aux_buffer *aux;
|
||||
|
@ -77,10 +77,12 @@ static size_t buf_size(struct page *page)
|
||||
}
|
||||
|
||||
static void *
|
||||
bts_buffer_setup_aux(int cpu, void **pages, int nr_pages, bool overwrite)
|
||||
bts_buffer_setup_aux(struct perf_event *event, void **pages,
|
||||
int nr_pages, bool overwrite)
|
||||
{
|
||||
struct bts_buffer *buf;
|
||||
struct page *page;
|
||||
int cpu = event->cpu;
|
||||
int node = (cpu == -1) ? cpu : cpu_to_node(cpu);
|
||||
unsigned long offset;
|
||||
size_t size = nr_pages << PAGE_SHIFT;
|
||||
|
@ -1114,10 +1114,11 @@ static int pt_buffer_init_topa(struct pt_buffer *buf, unsigned long nr_pages,
|
||||
* Return: Our private PT buffer structure.
|
||||
*/
|
||||
static void *
|
||||
pt_buffer_setup_aux(int cpu, void **pages, int nr_pages, bool snapshot)
|
||||
pt_buffer_setup_aux(struct perf_event *event, void **pages,
|
||||
int nr_pages, bool snapshot)
|
||||
{
|
||||
struct pt_buffer *buf;
|
||||
int node, ret;
|
||||
int node, ret, cpu = event->cpu;
|
||||
|
||||
if (!nr_pages)
|
||||
return NULL;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/percpu-defs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stringhash.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
@ -30,11 +31,14 @@ static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
|
||||
PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC));
|
||||
PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS));
|
||||
PMU_FORMAT_ATTR(retstack, "config:" __stringify(ETM_OPT_RETSTK));
|
||||
/* Sink ID - same for all ETMs */
|
||||
PMU_FORMAT_ATTR(sinkid, "config2:0-31");
|
||||
|
||||
static struct attribute *etm_config_formats_attr[] = {
|
||||
&format_attr_cycacc.attr,
|
||||
&format_attr_timestamp.attr,
|
||||
&format_attr_retstack.attr,
|
||||
&format_attr_sinkid.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -43,8 +47,18 @@ static const struct attribute_group etm_pmu_format_group = {
|
||||
.attrs = etm_config_formats_attr,
|
||||
};
|
||||
|
||||
static struct attribute *etm_config_sinks_attr[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group etm_pmu_sinks_group = {
|
||||
.name = "sinks",
|
||||
.attrs = etm_config_sinks_attr,
|
||||
};
|
||||
|
||||
static const struct attribute_group *etm_pmu_attr_groups[] = {
|
||||
&etm_pmu_format_group,
|
||||
&etm_pmu_sinks_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -177,31 +191,28 @@ static void etm_free_aux(void *data)
|
||||
schedule_work(&event_data->work);
|
||||
}
|
||||
|
||||
static void *etm_setup_aux(int event_cpu, void **pages,
|
||||
static void *etm_setup_aux(struct perf_event *event, void **pages,
|
||||
int nr_pages, bool overwrite)
|
||||
{
|
||||
int cpu;
|
||||
u32 id;
|
||||
int cpu = event->cpu;
|
||||
cpumask_t *mask;
|
||||
struct coresight_device *sink;
|
||||
struct etm_event_data *event_data = NULL;
|
||||
|
||||
event_data = alloc_event_data(event_cpu);
|
||||
event_data = alloc_event_data(cpu);
|
||||
if (!event_data)
|
||||
return NULL;
|
||||
INIT_WORK(&event_data->work, free_event_data);
|
||||
|
||||
/*
|
||||
* In theory nothing prevent tracers in a trace session from being
|
||||
* associated with different sinks, nor having a sink per tracer. But
|
||||
* until we have HW with this kind of topology we need to assume tracers
|
||||
* in a trace session are using the same sink. Therefore go through
|
||||
* the coresight bus and pick the first enabled sink.
|
||||
*
|
||||
* When operated from sysFS users are responsible to enable the sink
|
||||
* while from perf, the perf tools will do it based on the choice made
|
||||
* on the cmd line. As such the "enable_sink" flag in sysFS is reset.
|
||||
*/
|
||||
sink = coresight_get_enabled_sink(true);
|
||||
/* First get the selected sink from user space. */
|
||||
if (event->attr.config2) {
|
||||
id = (u32)event->attr.config2;
|
||||
sink = coresight_get_sink_by_id(id);
|
||||
} else {
|
||||
sink = coresight_get_enabled_sink(true);
|
||||
}
|
||||
|
||||
if (!sink || !sink_ops(sink)->alloc_buffer)
|
||||
goto err;
|
||||
|
||||
@ -479,6 +490,77 @@ int etm_perf_symlink(struct coresight_device *csdev, bool link)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t etm_perf_sink_name_show(struct device *dev,
|
||||
struct device_attribute *dattr,
|
||||
char *buf)
|
||||
{
|
||||
struct dev_ext_attribute *ea;
|
||||
|
||||
ea = container_of(dattr, struct dev_ext_attribute, attr);
|
||||
return scnprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)(ea->var));
|
||||
}
|
||||
|
||||
int etm_perf_add_symlink_sink(struct coresight_device *csdev)
|
||||
{
|
||||
int ret;
|
||||
unsigned long hash;
|
||||
const char *name;
|
||||
struct device *pmu_dev = etm_pmu.dev;
|
||||
struct device *pdev = csdev->dev.parent;
|
||||
struct dev_ext_attribute *ea;
|
||||
|
||||
if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
|
||||
csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
|
||||
return -EINVAL;
|
||||
|
||||
if (csdev->ea != NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!etm_perf_up)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ea = devm_kzalloc(pdev, sizeof(*ea), GFP_KERNEL);
|
||||
if (!ea)
|
||||
return -ENOMEM;
|
||||
|
||||
name = dev_name(pdev);
|
||||
/* See function coresight_get_sink_by_id() to know where this is used */
|
||||
hash = hashlen_hash(hashlen_string(NULL, name));
|
||||
|
||||
ea->attr.attr.name = devm_kstrdup(pdev, name, GFP_KERNEL);
|
||||
if (!ea->attr.attr.name)
|
||||
return -ENOMEM;
|
||||
|
||||
ea->attr.attr.mode = 0444;
|
||||
ea->attr.show = etm_perf_sink_name_show;
|
||||
ea->var = (unsigned long *)hash;
|
||||
|
||||
ret = sysfs_add_file_to_group(&pmu_dev->kobj,
|
||||
&ea->attr.attr, "sinks");
|
||||
|
||||
if (!ret)
|
||||
csdev->ea = ea;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void etm_perf_del_symlink_sink(struct coresight_device *csdev)
|
||||
{
|
||||
struct device *pmu_dev = etm_pmu.dev;
|
||||
struct dev_ext_attribute *ea = csdev->ea;
|
||||
|
||||
if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
|
||||
csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
|
||||
return;
|
||||
|
||||
if (!ea)
|
||||
return;
|
||||
|
||||
sysfs_remove_file_from_group(&pmu_dev->kobj,
|
||||
&ea->attr.attr, "sinks");
|
||||
csdev->ea = NULL;
|
||||
}
|
||||
|
||||
static int __init etm_perf_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -59,6 +59,8 @@ struct etm_event_data {
|
||||
|
||||
#ifdef CONFIG_CORESIGHT
|
||||
int etm_perf_symlink(struct coresight_device *csdev, bool link);
|
||||
int etm_perf_add_symlink_sink(struct coresight_device *csdev);
|
||||
void etm_perf_del_symlink_sink(struct coresight_device *csdev);
|
||||
static inline void *etm_perf_sink_config(struct perf_output_handle *handle)
|
||||
{
|
||||
struct etm_event_data *data = perf_get_aux(handle);
|
||||
@ -70,7 +72,9 @@ static inline void *etm_perf_sink_config(struct perf_output_handle *handle)
|
||||
#else
|
||||
static inline int etm_perf_symlink(struct coresight_device *csdev, bool link)
|
||||
{ return -EINVAL; }
|
||||
|
||||
int etm_perf_add_symlink_sink(struct coresight_device *csdev)
|
||||
{ return -EINVAL; }
|
||||
void etm_perf_del_symlink_sink(struct coresight_device *csdev) {}
|
||||
static inline void *etm_perf_sink_config(struct perf_output_handle *handle)
|
||||
{
|
||||
return NULL;
|
||||
|
@ -147,6 +147,7 @@ void coresight_disable_path(struct list_head *path);
|
||||
int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data);
|
||||
struct coresight_device *coresight_get_sink(struct list_head *path);
|
||||
struct coresight_device *coresight_get_enabled_sink(bool reset);
|
||||
struct coresight_device *coresight_get_sink_by_id(u32 id);
|
||||
struct list_head *coresight_build_path(struct coresight_device *csdev,
|
||||
struct coresight_device *sink);
|
||||
void coresight_release_path(struct list_head *path);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stringhash.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/coresight.h>
|
||||
@ -18,6 +19,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "coresight-etm-perf.h"
|
||||
#include "coresight-priv.h"
|
||||
|
||||
static DEFINE_MUTEX(coresight_mutex);
|
||||
@ -540,6 +542,47 @@ struct coresight_device *coresight_get_enabled_sink(bool deactivate)
|
||||
return dev ? to_coresight_device(dev) : NULL;
|
||||
}
|
||||
|
||||
static int coresight_sink_by_id(struct device *dev, void *data)
|
||||
{
|
||||
struct coresight_device *csdev = to_coresight_device(dev);
|
||||
unsigned long hash;
|
||||
|
||||
if (csdev->type == CORESIGHT_DEV_TYPE_SINK ||
|
||||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) {
|
||||
|
||||
if (!csdev->ea)
|
||||
return 0;
|
||||
/*
|
||||
* See function etm_perf_add_symlink_sink() to know where
|
||||
* this comes from.
|
||||
*/
|
||||
hash = (unsigned long)csdev->ea->var;
|
||||
|
||||
if ((u32)hash == *(u32 *)data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* coresight_get_sink_by_id - returns the sink that matches the id
|
||||
* @id: Id of the sink to match
|
||||
*
|
||||
* The name of a sink is unique, whether it is found on the AMBA bus or
|
||||
* otherwise. As such the hash of that name can easily be used to identify
|
||||
* a sink.
|
||||
*/
|
||||
struct coresight_device *coresight_get_sink_by_id(u32 id)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
|
||||
dev = bus_find_device(&coresight_bustype, NULL, &id,
|
||||
coresight_sink_by_id);
|
||||
|
||||
return dev ? to_coresight_device(dev) : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* coresight_grab_device - Power up this device and any of the helper
|
||||
* devices connected to it for trace operation. Since the helper devices
|
||||
@ -1167,6 +1210,22 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (csdev->type == CORESIGHT_DEV_TYPE_SINK ||
|
||||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) {
|
||||
ret = etm_perf_add_symlink_sink(csdev);
|
||||
|
||||
if (ret) {
|
||||
device_unregister(&csdev->dev);
|
||||
/*
|
||||
* As with the above, all resources are free'd
|
||||
* explicitly via coresight_device_release() triggered
|
||||
* from put_device(), which is in turn called from
|
||||
* function device_unregister().
|
||||
*/
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&coresight_mutex);
|
||||
|
||||
coresight_fixup_device_conns(csdev);
|
||||
@ -1185,6 +1244,7 @@ EXPORT_SYMBOL_GPL(coresight_register);
|
||||
|
||||
void coresight_unregister(struct coresight_device *csdev)
|
||||
{
|
||||
etm_perf_del_symlink_sink(csdev);
|
||||
/* Remove references of that device in the topology */
|
||||
coresight_remove_conns(csdev);
|
||||
device_unregister(&csdev->dev);
|
||||
|
@ -824,10 +824,10 @@ static void arm_spe_pmu_read(struct perf_event *event)
|
||||
{
|
||||
}
|
||||
|
||||
static void *arm_spe_pmu_setup_aux(int cpu, void **pages, int nr_pages,
|
||||
bool snapshot)
|
||||
static void *arm_spe_pmu_setup_aux(struct perf_event *event, void **pages,
|
||||
int nr_pages, bool snapshot)
|
||||
{
|
||||
int i;
|
||||
int i, cpu = event->cpu;
|
||||
struct page **pglist;
|
||||
struct arm_spe_pmu_buf *buf;
|
||||
|
||||
|
@ -154,8 +154,9 @@ struct coresight_connection {
|
||||
* @orphan: true if the component has connections that haven't been linked.
|
||||
* @enable: 'true' if component is currently part of an active path.
|
||||
* @activated: 'true' only if a _sink_ has been activated. A sink can be
|
||||
activated but not yet enabled. Enabling for a _sink_
|
||||
happens when a source has been selected for that it.
|
||||
* activated but not yet enabled. Enabling for a _sink_
|
||||
* appens when a source has been selected for that it.
|
||||
* @ea: Device attribute for sink representation under PMU directory.
|
||||
*/
|
||||
struct coresight_device {
|
||||
struct coresight_connection *conns;
|
||||
@ -168,7 +169,9 @@ struct coresight_device {
|
||||
atomic_t *refcnt;
|
||||
bool orphan;
|
||||
bool enable; /* true only if configured as part of a path */
|
||||
/* sink specific fields */
|
||||
bool activated; /* true only if a sink is part of a path */
|
||||
struct dev_ext_attribute *ea;
|
||||
};
|
||||
|
||||
#define to_coresight_device(d) container_of(d, struct coresight_device, dev)
|
||||
|
@ -410,7 +410,7 @@ struct pmu {
|
||||
/*
|
||||
* Set up pmu-private data structures for an AUX area
|
||||
*/
|
||||
void *(*setup_aux) (int cpu, void **pages,
|
||||
void *(*setup_aux) (struct perf_event *event, void **pages,
|
||||
int nr_pages, bool overwrite);
|
||||
/* optional */
|
||||
|
||||
|
@ -657,7 +657,7 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rb->aux_priv = event->pmu->setup_aux(event->cpu, rb->aux_pages, nr_pages,
|
||||
rb->aux_priv = event->pmu->setup_aux(event, rb->aux_pages, nr_pages,
|
||||
overwrite);
|
||||
if (!rb->aux_priv)
|
||||
goto out;
|
||||
|
@ -120,6 +120,10 @@ Given a $HOME/.perfconfig like this:
|
||||
children = true
|
||||
group = true
|
||||
|
||||
[llvm]
|
||||
dump-obj = true
|
||||
clang-opt = -g
|
||||
|
||||
You can hide source code of annotate feature setting the config to false with
|
||||
|
||||
% perf config annotate.hide_src_code=true
|
||||
@ -553,6 +557,33 @@ trace.*::
|
||||
trace.show_zeros::
|
||||
Do not suppress syscall arguments that are equal to zero.
|
||||
|
||||
llvm.*::
|
||||
llvm.clang-path::
|
||||
Path to clang. If omit, search it from $PATH.
|
||||
|
||||
llvm.clang-bpf-cmd-template::
|
||||
Cmdline template. Below lines show its default value. Environment
|
||||
variable is used to pass options.
|
||||
"$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
|
||||
-Wno-unused-value -Wno-pointer-sign -working-directory \
|
||||
$WORKING_DIR -c $CLANG_SOURCE -target bpf -O2 -o -"
|
||||
|
||||
llvm.clang-opt::
|
||||
Options passed to clang.
|
||||
|
||||
llvm.kbuild-dir::
|
||||
kbuild directory. If not set, use /lib/modules/`uname -r`/build.
|
||||
If set to "" deliberately, skip kernel header auto-detector.
|
||||
|
||||
llvm.kbuild-opts::
|
||||
Options passed to 'make' when detecting kernel header options.
|
||||
|
||||
llvm.dump-obj::
|
||||
Enable perf dump BPF object files compiled by LLVM.
|
||||
|
||||
llvm.opts::
|
||||
Options passed to llc.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf[1]
|
||||
|
@ -88,6 +88,20 @@ OPTIONS
|
||||
If you want to profile write accesses in [0x1000~1008), just set
|
||||
'mem:0x1000/8:w'.
|
||||
|
||||
- a BPF source file (ending in .c) or a precompiled object file (ending
|
||||
in .o) selects one or more BPF events.
|
||||
The BPF program can attach to various perf events based on the ELF section
|
||||
names.
|
||||
|
||||
When processing a '.c' file, perf searches an installed LLVM to compile it
|
||||
into an object file first. Optional clang options can be passed via the
|
||||
'--clang-opt' command line option, e.g.:
|
||||
|
||||
perf record --clang-opt "-DLINUX_VERSION_CODE=0x50000" \
|
||||
-e tests/bpf-script-example.c
|
||||
|
||||
Note: '--clang-opt' must be placed before '--event/-e'.
|
||||
|
||||
- a group of events surrounded by a pair of brace ("{event1,event2,...}").
|
||||
Each event is separated by commas and the group should be quoted to
|
||||
prevent the shell interpretation. You also need to use --group on
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "perf_regs.h"
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "map_groups.h"
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <api/fs/fs.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/coresight-pmu.h>
|
||||
@ -22,12 +23,10 @@
|
||||
#include "../../util/thread_map.h"
|
||||
#include "../../util/cs-etm.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define ENABLE_SINK_MAX 128
|
||||
#define CS_BUS_DEVICE_PATH "/bus/coresight/devices/"
|
||||
|
||||
struct cs_etm_recording {
|
||||
struct auxtrace_record itr;
|
||||
struct perf_pmu *cs_etm_pmu;
|
||||
@ -60,10 +59,48 @@ static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
char msg[BUFSIZ], path[PATH_MAX], *sink;
|
||||
struct perf_evsel_config_term *term;
|
||||
int ret = -EINVAL;
|
||||
u32 hash;
|
||||
|
||||
if (evsel->attr.config2 & GENMASK(31, 0))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(term, &evsel->config_terms, list) {
|
||||
if (term->type != PERF_EVSEL__CONFIG_TERM_DRV_CFG)
|
||||
continue;
|
||||
|
||||
sink = term->val.drv_cfg;
|
||||
snprintf(path, PATH_MAX, "sinks/%s", sink);
|
||||
|
||||
ret = perf_pmu__scan_file(pmu, path, "%x", &hash);
|
||||
if (ret != 1) {
|
||||
pr_err("failed to set sink \"%s\" on event %s with %d (%s)\n",
|
||||
sink, perf_evsel__name(evsel), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
evsel->attr.config2 |= hash;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* No sink was provided on the command line - for _now_ treat
|
||||
* this as an error.
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cs_etm_recording_options(struct auxtrace_record *itr,
|
||||
struct perf_evlist *evlist,
|
||||
struct record_opts *opts)
|
||||
{
|
||||
int ret;
|
||||
struct cs_etm_recording *ptr =
|
||||
container_of(itr, struct cs_etm_recording, itr);
|
||||
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
|
||||
@ -92,6 +129,10 @@ static int cs_etm_recording_options(struct auxtrace_record *itr,
|
||||
if (!cs_etm_evsel)
|
||||
return 0;
|
||||
|
||||
ret = cs_etm_set_sink_attr(cs_etm_pmu, cs_etm_evsel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (opts->use_clockid) {
|
||||
pr_err("Cannot use clockid (-k option) with %s\n",
|
||||
CORESIGHT_ETM_PMU_NAME);
|
||||
@ -598,54 +639,3 @@ struct auxtrace_record *cs_etm_record_init(int *err)
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static FILE *cs_device__open_file(const char *name)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
const char *sysfs;
|
||||
|
||||
sysfs = sysfs__mountpoint();
|
||||
if (!sysfs)
|
||||
return NULL;
|
||||
|
||||
snprintf(path, PATH_MAX,
|
||||
"%s" CS_BUS_DEVICE_PATH "%s", sysfs, name);
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return NULL;
|
||||
|
||||
return fopen(path, "w");
|
||||
|
||||
}
|
||||
|
||||
static int __printf(2, 3) cs_device__print_file(const char *name, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
FILE *file;
|
||||
int ret = -EINVAL;
|
||||
|
||||
va_start(args, fmt);
|
||||
file = cs_device__open_file(name);
|
||||
if (file) {
|
||||
ret = vfprintf(file, fmt, args);
|
||||
fclose(file);
|
||||
}
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cs_etm_set_drv_config(struct perf_evsel_config_term *term)
|
||||
{
|
||||
int ret;
|
||||
char enable_sink[ENABLE_SINK_MAX];
|
||||
|
||||
snprintf(enable_sink, ENABLE_SINK_MAX, "%s/%s",
|
||||
term->val.drv_cfg, "enable_sink");
|
||||
|
||||
ret = cs_device__print_file(enable_sink, "%d", 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -7,9 +7,6 @@
|
||||
#ifndef INCLUDE__PERF_CS_ETM_H__
|
||||
#define INCLUDE__PERF_CS_ETM_H__
|
||||
|
||||
#include "../../util/evsel.h"
|
||||
|
||||
struct auxtrace_record *cs_etm_record_init(int *err);
|
||||
int cs_etm_set_drv_config(struct perf_evsel_config_term *term);
|
||||
|
||||
#endif
|
||||
|
@ -7,8 +7,8 @@
|
||||
#include <string.h>
|
||||
#include <linux/coresight-pmu.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "cs-etm.h"
|
||||
#include "arm-spe.h"
|
||||
#include "../../util/pmu.h"
|
||||
|
||||
@ -19,7 +19,6 @@ struct perf_event_attr
|
||||
if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
|
||||
/* add ETM default config here */
|
||||
pmu->selectable = true;
|
||||
pmu->set_drv_config = cs_etm_set_drv_config;
|
||||
#if defined(__aarch64__)
|
||||
} else if (strstarts(pmu->name, ARM_SPE_PMU_NAME)) {
|
||||
return arm_spe_pmu_default_config(pmu);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "perf_regs.h"
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "map_groups.h"
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "perf_regs.h"
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "map_groups.h"
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "util/kvm-stat.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/evlist.h"
|
||||
|
||||
#include "book3s_hv_exits.h"
|
||||
#include "book3s_hcalls.h"
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "util/thread.h"
|
||||
#include "util/callchain.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/dso.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
|
||||
/*
|
||||
* When saving the callchain on Power, the kernel conservatively saves
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include "../../util/kvm-stat.h"
|
||||
#include "../../util/evsel.h"
|
||||
#include <asm/sie.h>
|
||||
|
||||
define_exit_reasons_table(sie_exit_reasons, sie_intercept_code);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "perf_regs.h"
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "map_groups.h"
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <errno.h>
|
||||
#include "../../util/kvm-stat.h"
|
||||
#include "../../util/evsel.h"
|
||||
#include <asm/svm.h>
|
||||
#include <asm/vmx.h>
|
||||
#include <asm/kvm.h>
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "util/thread.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/hist.h"
|
||||
#include "util/map.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/data.h"
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "ui/browsers/hists.h"
|
||||
#include "thread.h"
|
||||
#include "mem2node.h"
|
||||
#include "symbol.h"
|
||||
|
||||
struct c2c_hists {
|
||||
struct hists hists;
|
||||
@ -1969,7 +1970,7 @@ static void calc_width(struct c2c_hist_entry *c2c_he)
|
||||
set_nodestr(c2c_he);
|
||||
}
|
||||
|
||||
static int filter_cb(struct hist_entry *he)
|
||||
static int filter_cb(struct hist_entry *he, void *arg __maybe_unused)
|
||||
{
|
||||
struct c2c_hist_entry *c2c_he;
|
||||
|
||||
@ -1986,7 +1987,7 @@ static int filter_cb(struct hist_entry *he)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resort_cl_cb(struct hist_entry *he)
|
||||
static int resort_cl_cb(struct hist_entry *he, void *arg __maybe_unused)
|
||||
{
|
||||
struct c2c_hist_entry *c2c_he;
|
||||
struct c2c_hists *c2c_hists;
|
||||
@ -2073,7 +2074,7 @@ static int setup_nodes(struct perf_session *session)
|
||||
|
||||
#define HAS_HITMS(__h) ((__h)->stats.lcl_hitm || (__h)->stats.rmt_hitm)
|
||||
|
||||
static int resort_hitm_cb(struct hist_entry *he)
|
||||
static int resort_hitm_cb(struct hist_entry *he, void *arg __maybe_unused)
|
||||
{
|
||||
struct c2c_hist_entry *c2c_he;
|
||||
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||
@ -2095,7 +2096,7 @@ static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb)
|
||||
struct hist_entry *he;
|
||||
|
||||
he = rb_entry(next, struct hist_entry, rb_node);
|
||||
ret = cb(he);
|
||||
ret = cb(he, NULL);
|
||||
if (ret)
|
||||
break;
|
||||
next = rb_next(&he->rb_node);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "util/color.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/map.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/debug.h"
|
||||
@ -19,6 +20,7 @@
|
||||
#include "util/data.h"
|
||||
#include "util/auxtrace.h"
|
||||
#include "util/jit.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
|
||||
#include <subcmd/parse-options.h>
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <subcmd/parse-options.h>
|
||||
#include "debug.h"
|
||||
#include "machine.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
|
||||
static int __cmd_kallsyms(int argc, const char **argv)
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "util/evsel.h"
|
||||
#include "util/util.h"
|
||||
#include "util/config.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/header.h"
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "util/data.h"
|
||||
#include "util/mem-events.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
|
||||
#define MEM_OPERATION_LOAD 0x1
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/drv_configs.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/symbol.h"
|
||||
@ -39,6 +38,7 @@
|
||||
#include "util/bpf-loader.h"
|
||||
#include "util/trigger.h"
|
||||
#include "util/perf-hooks.h"
|
||||
#include "util/cpu-set-sched.h"
|
||||
#include "util/time-utils.h"
|
||||
#include "util/units.h"
|
||||
#include "util/bpf-event.h"
|
||||
@ -82,12 +82,17 @@ struct record {
|
||||
bool timestamp_boundary;
|
||||
struct switch_output switch_output;
|
||||
unsigned long long samples;
|
||||
cpu_set_t affinity_mask;
|
||||
};
|
||||
|
||||
static volatile int auxtrace_record__snapshot_started;
|
||||
static DEFINE_TRIGGER(auxtrace_snapshot_trigger);
|
||||
static DEFINE_TRIGGER(switch_output_trigger);
|
||||
|
||||
static const char *affinity_tags[PERF_AFFINITY_MAX] = {
|
||||
"SYS", "NODE", "CPU"
|
||||
};
|
||||
|
||||
static bool switch_output_signal(struct record *rec)
|
||||
{
|
||||
return rec->switch_output.signal &&
|
||||
@ -532,9 +537,13 @@ static int record__mmap_evlist(struct record *rec,
|
||||
struct record_opts *opts = &rec->opts;
|
||||
char msg[512];
|
||||
|
||||
if (opts->affinity != PERF_AFFINITY_SYS)
|
||||
cpu__setup_cpunode_map();
|
||||
|
||||
if (perf_evlist__mmap_ex(evlist, opts->mmap_pages,
|
||||
opts->auxtrace_mmap_pages,
|
||||
opts->auxtrace_snapshot_mode, opts->nr_cblocks) < 0) {
|
||||
opts->auxtrace_snapshot_mode,
|
||||
opts->nr_cblocks, opts->affinity) < 0) {
|
||||
if (errno == EPERM) {
|
||||
pr_err("Permission error mapping pages.\n"
|
||||
"Consider increasing "
|
||||
@ -567,7 +576,6 @@ static int record__open(struct record *rec)
|
||||
struct perf_evlist *evlist = rec->evlist;
|
||||
struct perf_session *session = rec->session;
|
||||
struct record_opts *opts = &rec->opts;
|
||||
struct perf_evsel_config_term *err_term;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
@ -620,14 +628,6 @@ try_again:
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (perf_evlist__apply_drv_configs(evlist, &pos, &err_term)) {
|
||||
pr_err("failed to set config \"%s\" on event %s with %d (%s)\n",
|
||||
err_term->val.drv_cfg, perf_evsel__name(pos), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = record__mmap(rec);
|
||||
if (rc)
|
||||
goto out;
|
||||
@ -723,6 +723,16 @@ static struct perf_event_header finished_round_event = {
|
||||
.type = PERF_RECORD_FINISHED_ROUND,
|
||||
};
|
||||
|
||||
static void record__adjust_affinity(struct record *rec, struct perf_mmap *map)
|
||||
{
|
||||
if (rec->opts.affinity != PERF_AFFINITY_SYS &&
|
||||
!CPU_EQUAL(&rec->affinity_mask, &map->affinity_mask)) {
|
||||
CPU_ZERO(&rec->affinity_mask);
|
||||
CPU_OR(&rec->affinity_mask, &rec->affinity_mask, &map->affinity_mask);
|
||||
sched_setaffinity(0, sizeof(rec->affinity_mask), &rec->affinity_mask);
|
||||
}
|
||||
}
|
||||
|
||||
static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evlist,
|
||||
bool overwrite)
|
||||
{
|
||||
@ -750,6 +760,7 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli
|
||||
struct perf_mmap *map = &maps[i];
|
||||
|
||||
if (map->base) {
|
||||
record__adjust_affinity(rec, map);
|
||||
if (!record__aio_enabled(rec)) {
|
||||
if (perf_mmap__push(map, rec, record__pushfn) != 0) {
|
||||
rc = -1;
|
||||
@ -1987,6 +1998,9 @@ int cmd_record(int argc, const char **argv)
|
||||
# undef REASON
|
||||
#endif
|
||||
|
||||
CPU_ZERO(&rec->affinity_mask);
|
||||
rec->opts.affinity = PERF_AFFINITY_SYS;
|
||||
|
||||
rec->evlist = perf_evlist__new();
|
||||
if (rec->evlist == NULL)
|
||||
return -ENOMEM;
|
||||
@ -2150,6 +2164,8 @@ int cmd_record(int argc, const char **argv)
|
||||
if (verbose > 0)
|
||||
pr_info("nr_cblocks: %d\n", rec->opts.nr_cblocks);
|
||||
|
||||
pr_debug("affinity: %s\n", affinity_tags[rec->opts.affinity]);
|
||||
|
||||
err = __cmd_record(&record, argc, argv);
|
||||
out:
|
||||
perf_evlist__delete(rec->evlist);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/err.h>
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/callchain.h"
|
||||
#include "util/values.h"
|
||||
@ -615,6 +616,21 @@ static int report__collapse_hists(struct report *rep)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hists__resort_cb(struct hist_entry *he, void *arg)
|
||||
{
|
||||
struct report *rep = arg;
|
||||
struct symbol *sym = he->ms.sym;
|
||||
|
||||
if (rep->symbol_ipc && sym && !sym->annotate2) {
|
||||
struct perf_evsel *evsel = hists_to_evsel(he->hists);
|
||||
|
||||
symbol__annotate2(sym, he->ms.map, evsel,
|
||||
&annotation__default_options, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void report__output_resort(struct report *rep)
|
||||
{
|
||||
struct ui_progress prog;
|
||||
@ -622,8 +638,10 @@ static void report__output_resort(struct report *rep)
|
||||
|
||||
ui_progress__init(&prog, rep->nr_entries, "Sorting events for output...");
|
||||
|
||||
evlist__for_each_entry(rep->session->evlist, pos)
|
||||
perf_evsel__output_resort(pos, &prog);
|
||||
evlist__for_each_entry(rep->session->evlist, pos) {
|
||||
perf_evsel__output_resort_cb(pos, &prog,
|
||||
hists__resort_cb, rep);
|
||||
}
|
||||
|
||||
ui_progress__finish();
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "util/perf_regs.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/trace-event.h"
|
||||
|
@ -52,7 +52,6 @@
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/drv_configs.h"
|
||||
#include "util/color.h"
|
||||
#include "util/stat.h"
|
||||
#include "util/header.h"
|
||||
@ -417,7 +416,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
||||
int status = 0;
|
||||
const bool forks = (argc > 0);
|
||||
bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false;
|
||||
struct perf_evsel_config_term *err_term;
|
||||
|
||||
if (interval) {
|
||||
ts.tv_sec = interval / USEC_PER_MSEC;
|
||||
@ -514,13 +512,6 @@ try_again:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_evlist__apply_drv_configs(evsel_list, &counter, &err_term)) {
|
||||
pr_err("failed to set config \"%s\" on event %s with %d (%s)\n",
|
||||
err_term->val.drv_cfg, perf_evsel__name(counter), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (STAT_RECORD) {
|
||||
int err, fd = perf_data__fd(&perf_stat.data);
|
||||
|
||||
|
@ -25,11 +25,11 @@
|
||||
#include "util/bpf-event.h"
|
||||
#include "util/config.h"
|
||||
#include "util/color.h"
|
||||
#include "util/drv_configs.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/event.h"
|
||||
#include "util/machine.h"
|
||||
#include "util/map.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
@ -1185,10 +1185,6 @@ static void init_process_thread(struct perf_top *top)
|
||||
|
||||
static int __cmd_top(struct perf_top *top)
|
||||
{
|
||||
char msg[512];
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evsel_config_term *err_term;
|
||||
struct perf_evlist *evlist = top->evlist;
|
||||
struct record_opts *opts = &top->record_opts;
|
||||
pthread_t thread, thread_process;
|
||||
int ret;
|
||||
@ -1239,14 +1235,6 @@ static int __cmd_top(struct perf_top *top)
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
ret = perf_evlist__apply_drv_configs(evlist, &pos, &err_term);
|
||||
if (ret) {
|
||||
pr_err("failed to set config \"%s\" on event %s with %d (%s)\n",
|
||||
err_term->val.drv_cfg, perf_evsel__name(pos), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
top->session->evlist = top->evlist;
|
||||
perf_session__set_id_hdr_size(top->session);
|
||||
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "util/evlist.h"
|
||||
#include <subcmd/exec-cmd.h>
|
||||
#include "util/machine.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/path.h"
|
||||
#include "util/session.h"
|
||||
#include "util/thread.h"
|
||||
|
@ -84,6 +84,14 @@ struct record_opts {
|
||||
clockid_t clockid;
|
||||
u64 clockid_res_ns;
|
||||
int nr_cblocks;
|
||||
int affinity;
|
||||
};
|
||||
|
||||
enum perf_affinity {
|
||||
PERF_AFFINITY_SYS = 0,
|
||||
PERF_AFFINITY_NODE,
|
||||
PERF_AFFINITY_CPU,
|
||||
PERF_AFFINITY_MAX
|
||||
};
|
||||
|
||||
struct option;
|
||||
|
@ -73,7 +73,7 @@
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Actual Average Latency for L1 data-cache miss demand loads",
|
||||
"MetricExpr": "L1D_PEND_MISS.PENDING / ( MEM_LOAD_RETIRED.L1_MISS_PS + MEM_LOAD_RETIRED.FB_HIT_PS )",
|
||||
"MetricExpr": "L1D_PEND_MISS.PENDING / ( MEM_LOAD_RETIRED.L1_MISS + MEM_LOAD_RETIRED.FB_HIT )",
|
||||
"MetricGroup": "Memory_Bound;Memory_Lat",
|
||||
"MetricName": "Load_Miss_Real_Latency"
|
||||
},
|
||||
|
@ -478,7 +478,7 @@ if perf_db_export_calls:
|
||||
'branch_count,'
|
||||
'call_id,'
|
||||
'return_id,'
|
||||
'CASE WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' ELSE \'\' END AS flags,'
|
||||
'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE flags END AS flags,'
|
||||
'parent_call_path_id'
|
||||
' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id')
|
||||
|
||||
|
@ -320,7 +320,7 @@ if perf_db_export_calls:
|
||||
'branch_count,'
|
||||
'call_id,'
|
||||
'return_id,'
|
||||
'CASE WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' ELSE \'\' END AS flags,'
|
||||
'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE flags END AS flags,'
|
||||
'parent_call_path_id'
|
||||
' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id')
|
||||
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "machine.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "event.h"
|
||||
#include "thread.h"
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "../util/unwind.h"
|
||||
#include "perf_regs.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include "callchain.h"
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <inttypes.h>
|
||||
#include "perf.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/evsel.h"
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "perf.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/event.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/evsel.h"
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "perf.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/evsel.h"
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "perf.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/event.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/evsel.h"
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "tests.h"
|
||||
#include "machine.h"
|
||||
#include "thread_map.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include "util.h"
|
||||
|
@ -4,7 +4,9 @@
|
||||
#include "util.h"
|
||||
#include "tests.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
/* Simulated format definitions. */
|
||||
static struct test_format {
|
||||
|
@ -1,9 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "branch.h"
|
||||
#include "util.h"
|
||||
#include "event.h"
|
||||
#include "evsel.h"
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <util/evlist.h>
|
||||
#include <util/symbol.h>
|
||||
#include <linux/filter.h>
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "../../util/annotate.h"
|
||||
#include "../../util/hist.h"
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/map.h"
|
||||
#include "../../util/symbol.h"
|
||||
#include "../../util/evsel.h"
|
||||
#include "../../util/evlist.h"
|
||||
|
@ -8,9 +8,12 @@
|
||||
#include <linux/rbtree.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
|
||||
#include "../../util/callchain.h"
|
||||
#include "../../util/evsel.h"
|
||||
#include "../../util/evlist.h"
|
||||
#include "../../util/hist.h"
|
||||
#include "../../util/map.h"
|
||||
#include "../../util/symbol.h"
|
||||
#include "../../util/pstack.h"
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/util.h"
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "util/debug.h"
|
||||
#include "util/annotate.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/map.h"
|
||||
#include "util/symbol.h"
|
||||
#include "ui/helpline.h"
|
||||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "../evlist.h"
|
||||
#include "../cache.h"
|
||||
#include "../callchain.h"
|
||||
#include "../evsel.h"
|
||||
#include "../sort.h"
|
||||
#include "../hist.h"
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <math.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#include "../util/callchain.h"
|
||||
#include "../util/hist.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/sort.h"
|
||||
|
@ -2,8 +2,12 @@
|
||||
#include <stdio.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "../../util/callchain.h"
|
||||
#include "../../util/util.h"
|
||||
#include "../../util/hist.h"
|
||||
#include "../../util/map.h"
|
||||
#include "../../util/map_groups.h"
|
||||
#include "../../util/symbol.h"
|
||||
#include "../../util/sort.h"
|
||||
#include "../../util/evsel.h"
|
||||
#include "../../util/srcline.h"
|
||||
|
@ -107,7 +107,6 @@ libperf-y += term.o
|
||||
libperf-y += help-unknown-cmd.o
|
||||
libperf-y += mem-events.o
|
||||
libperf-y += vsprintf.o
|
||||
libperf-y += drv_configs.o
|
||||
libperf-y += units.o
|
||||
libperf-y += time-utils.o
|
||||
libperf-y += expr-bison.o
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "color.h"
|
||||
#include "config.h"
|
||||
#include "cache.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "units.h"
|
||||
#include "debug.h"
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/time64.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <stdlib.h>
|
||||
@ -41,6 +42,7 @@
|
||||
#include "pmu.h"
|
||||
#include "evsel.h"
|
||||
#include "cpumap.h"
|
||||
#include "symbol.h"
|
||||
#include "thread_map.h"
|
||||
#include "asm/bug.h"
|
||||
#include "auxtrace.h"
|
||||
@ -857,7 +859,7 @@ void auxtrace_buffer__free(struct auxtrace_buffer *buffer)
|
||||
|
||||
void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type,
|
||||
int code, int cpu, pid_t pid, pid_t tid, u64 ip,
|
||||
const char *msg)
|
||||
const char *msg, u64 timestamp)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
@ -869,7 +871,9 @@ void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type,
|
||||
auxtrace_error->cpu = cpu;
|
||||
auxtrace_error->pid = pid;
|
||||
auxtrace_error->tid = tid;
|
||||
auxtrace_error->fmt = 1;
|
||||
auxtrace_error->ip = ip;
|
||||
auxtrace_error->time = timestamp;
|
||||
strlcpy(auxtrace_error->msg, msg, MAX_AUXTRACE_ERROR_MSG);
|
||||
|
||||
size = (void *)auxtrace_error->msg - (void *)auxtrace_error +
|
||||
@ -1159,12 +1163,27 @@ static const char *auxtrace_error_name(int type)
|
||||
size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp)
|
||||
{
|
||||
struct auxtrace_error_event *e = &event->auxtrace_error;
|
||||
unsigned long long nsecs = e->time;
|
||||
const char *msg = e->msg;
|
||||
int ret;
|
||||
|
||||
ret = fprintf(fp, " %s error type %u",
|
||||
auxtrace_error_name(e->type), e->type);
|
||||
|
||||
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;
|
||||
|
||||
ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRIx64" code %u: %s\n",
|
||||
e->cpu, e->pid, e->tid, e->ip, e->code, e->msg);
|
||||
e->cpu, e->pid, e->tid, e->ip, e->code, msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1278,9 +1297,9 @@ static int __auxtrace_mmap__read(struct perf_mmap *map,
|
||||
}
|
||||
|
||||
/* padding must be written by fn() e.g. record__process_auxtrace() */
|
||||
padding = size & 7;
|
||||
padding = size & (PERF_AUXTRACE_RECORD_ALIGNMENT - 1);
|
||||
if (padding)
|
||||
padding = 8 - padding;
|
||||
padding = PERF_AUXTRACE_RECORD_ALIGNMENT - padding;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.auxtrace.header.type = PERF_RECORD_AUXTRACE;
|
||||
|
@ -40,6 +40,9 @@ struct record_opts;
|
||||
struct auxtrace_info_event;
|
||||
struct events_stats;
|
||||
|
||||
/* Auxtrace records must have the same alignment as perf event records */
|
||||
#define PERF_AUXTRACE_RECORD_ALIGNMENT 8
|
||||
|
||||
enum auxtrace_type {
|
||||
PERF_AUXTRACE_UNKNOWN,
|
||||
PERF_AUXTRACE_INTEL_PT,
|
||||
@ -516,7 +519,7 @@ void auxtrace_index__free(struct list_head *head);
|
||||
|
||||
void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type,
|
||||
int code, int cpu, pid_t pid, pid_t tid, u64 ip,
|
||||
const char *msg);
|
||||
const char *msg, u64 timestamp);
|
||||
|
||||
int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
|
||||
struct perf_tool *tool,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <errno.h>
|
||||
#include "perf.h"
|
||||
#include "debug.h"
|
||||
#include "evlist.h"
|
||||
#include "bpf-loader.h"
|
||||
#include "bpf-prologue.h"
|
||||
#include "probe-event.h"
|
||||
|
@ -8,11 +8,7 @@
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/err.h>
|
||||
#include <string.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include "probe-event.h"
|
||||
#include "evlist.h"
|
||||
#include "debug.h"
|
||||
|
||||
enum bpf_loader_errno {
|
||||
__BPF_LOADER_ERRNO__START = __LIBBPF_ERRNO__START - 100,
|
||||
@ -44,6 +40,7 @@ enum bpf_loader_errno {
|
||||
};
|
||||
|
||||
struct perf_evsel;
|
||||
struct perf_evlist;
|
||||
struct bpf_object;
|
||||
struct parse_events_term;
|
||||
#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
|
||||
@ -87,6 +84,8 @@ struct perf_evsel *bpf__setup_output_event(struct perf_evlist *evlist, const cha
|
||||
int bpf__strerror_setup_output_event(struct perf_evlist *evlist, int err, char *buf, size_t size);
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "debug.h"
|
||||
|
||||
static inline struct bpf_object *
|
||||
bpf__prepare_load(const char *filename __maybe_unused,
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "build-id.h"
|
||||
#include "event.h"
|
||||
#include "namespaces.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include <linux/kernel.h>
|
||||
|
@ -23,8 +23,10 @@
|
||||
#include "util.h"
|
||||
#include "sort.h"
|
||||
#include "machine.h"
|
||||
#include "map.h"
|
||||
#include "callchain.h"
|
||||
#include "branch.h"
|
||||
#include "symbol.h"
|
||||
|
||||
#define CALLCHAIN_PARAM_DEFAULT \
|
||||
.mode = CHAIN_GRAPH_ABS, \
|
||||
@ -1577,3 +1579,18 @@ int callchain_cursor__copy(struct callchain_cursor *dst,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a cursor before adding entries inside, but keep
|
||||
* the previously allocated entries as a cache.
|
||||
*/
|
||||
void callchain_cursor_reset(struct callchain_cursor *cursor)
|
||||
{
|
||||
struct callchain_cursor_node *node;
|
||||
|
||||
cursor->nr = 0;
|
||||
cursor->last = &cursor->first;
|
||||
|
||||
for (node = cursor->first; node != NULL; node = node->next)
|
||||
map__zput(node->map);
|
||||
}
|
||||
|
@ -5,10 +5,11 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include "event.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "map_symbol.h"
|
||||
#include "branch.h"
|
||||
|
||||
struct map;
|
||||
|
||||
#define HELP_PAD "\t\t\t\t"
|
||||
|
||||
#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace):\n\n"
|
||||
@ -187,20 +188,7 @@ int callchain_append(struct callchain_root *root,
|
||||
int callchain_merge(struct callchain_cursor *cursor,
|
||||
struct callchain_root *dst, struct callchain_root *src);
|
||||
|
||||
/*
|
||||
* Initialize a cursor before adding entries inside, but keep
|
||||
* the previously allocated entries as a cache.
|
||||
*/
|
||||
static inline void callchain_cursor_reset(struct callchain_cursor *cursor)
|
||||
{
|
||||
struct callchain_cursor_node *node;
|
||||
|
||||
cursor->nr = 0;
|
||||
cursor->last = &cursor->first;
|
||||
|
||||
for (node = cursor->first; node != NULL; node = node->next)
|
||||
map__zput(node->map);
|
||||
}
|
||||
void callchain_cursor_reset(struct callchain_cursor *cursor);
|
||||
|
||||
int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip,
|
||||
struct map *map, struct symbol *sym,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <sys/param.h>
|
||||
#include "util.h"
|
||||
#include "cache.h"
|
||||
#include "callchain.h"
|
||||
#include <subcmd/exec-cmd.h>
|
||||
#include "util/event.h" /* proc_map_timeout */
|
||||
#include "util/hist.h" /* perf_hist_config */
|
||||
|
50
tools/perf/util/cpu-set-sched.h
Normal file
50
tools/perf/util/cpu-set-sched.h
Normal file
@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Definitions taken from glibc for use with older systems, same licensing.
|
||||
#ifndef _CPU_SET_SCHED_PERF_H
|
||||
#define _CPU_SET_SCHED_PERF_H
|
||||
|
||||
#include <features.h>
|
||||
#include <sched.h>
|
||||
|
||||
#ifndef CPU_EQUAL
|
||||
#ifndef __CPU_EQUAL_S
|
||||
#if __GNUC_PREREQ (2, 91)
|
||||
# define __CPU_EQUAL_S(setsize, cpusetp1, cpusetp2) \
|
||||
(__builtin_memcmp (cpusetp1, cpusetp2, setsize) == 0)
|
||||
#else
|
||||
# define __CPU_EQUAL_S(setsize, cpusetp1, cpusetp2) \
|
||||
(__extension__ \
|
||||
({ const __cpu_mask *__arr1 = (cpusetp1)->__bits; \
|
||||
const __cpu_mask *__arr2 = (cpusetp2)->__bits; \
|
||||
size_t __imax = (setsize) / sizeof (__cpu_mask); \
|
||||
size_t __i; \
|
||||
for (__i = 0; __i < __imax; ++__i) \
|
||||
if (__arr1[__i] != __arr2[__i]) \
|
||||
break; \
|
||||
__i == __imax; }))
|
||||
#endif
|
||||
#endif // __CPU_EQUAL_S
|
||||
|
||||
#define CPU_EQUAL(cpusetp1, cpusetp2) \
|
||||
__CPU_EQUAL_S (sizeof (cpu_set_t), cpusetp1, cpusetp2)
|
||||
#endif // CPU_EQUAL
|
||||
|
||||
#ifndef CPU_OR
|
||||
#ifndef __CPU_OP_S
|
||||
#define __CPU_OP_S(setsize, destset, srcset1, srcset2, op) \
|
||||
(__extension__ \
|
||||
({ cpu_set_t *__dest = (destset); \
|
||||
const __cpu_mask *__arr1 = (srcset1)->__bits; \
|
||||
const __cpu_mask *__arr2 = (srcset2)->__bits; \
|
||||
size_t __imax = (setsize) / sizeof (__cpu_mask); \
|
||||
size_t __i; \
|
||||
for (__i = 0; __i < __imax; ++__i) \
|
||||
((__cpu_mask *) __dest->__bits)[__i] = __arr1[__i] op __arr2[__i]; \
|
||||
__dest; }))
|
||||
#endif // __CPU_OP_S
|
||||
|
||||
#define CPU_OR(destset, srcset1, srcset2) \
|
||||
__CPU_OP_S (sizeof (cpu_set_t), destset, srcset1, srcset2, |)
|
||||
#endif // CPU_OR
|
||||
|
||||
#endif // _CPU_SET_SCHED_PERF_H
|
@ -730,3 +730,13 @@ size_t cpu_map__snprint_mask(struct cpu_map *map, char *buf, size_t size)
|
||||
buf[size - 1] = '\0';
|
||||
return ptr - buf;
|
||||
}
|
||||
|
||||
const struct cpu_map *cpu_map__online(void) /* thread unsafe */
|
||||
{
|
||||
static const struct cpu_map *online = NULL;
|
||||
|
||||
if (!online)
|
||||
online = cpu_map__new(NULL); /* from /sys/devices/system/cpu/online */
|
||||
|
||||
return online;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ int cpu_map__get_core_id(int cpu);
|
||||
int cpu_map__get_core(struct cpu_map *map, int idx, void *data);
|
||||
int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
|
||||
int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep);
|
||||
const struct cpu_map *cpu_map__online(void); /* thread unsafe */
|
||||
|
||||
struct cpu_map *cpu_map__get(struct cpu_map *map);
|
||||
void cpu_map__put(struct cpu_map *map);
|
||||
|
@ -290,6 +290,12 @@ static void cs_etm_decoder__clear_buffer(struct cs_etm_decoder *decoder)
|
||||
decoder->packet_buffer[i].instr_count = 0;
|
||||
decoder->packet_buffer[i].last_instr_taken_branch = false;
|
||||
decoder->packet_buffer[i].last_instr_size = 0;
|
||||
decoder->packet_buffer[i].last_instr_type = 0;
|
||||
decoder->packet_buffer[i].last_instr_subtype = 0;
|
||||
decoder->packet_buffer[i].last_instr_cond = 0;
|
||||
decoder->packet_buffer[i].flags = 0;
|
||||
decoder->packet_buffer[i].exception_number = UINT32_MAX;
|
||||
decoder->packet_buffer[i].trace_chan_id = UINT8_MAX;
|
||||
decoder->packet_buffer[i].cpu = INT_MIN;
|
||||
}
|
||||
}
|
||||
@ -300,14 +306,12 @@ cs_etm_decoder__buffer_packet(struct cs_etm_decoder *decoder,
|
||||
enum cs_etm_sample_type sample_type)
|
||||
{
|
||||
u32 et = 0;
|
||||
struct int_node *inode = NULL;
|
||||
int cpu;
|
||||
|
||||
if (decoder->packet_count >= MAX_BUFFER - 1)
|
||||
return OCSD_RESP_FATAL_SYS_ERR;
|
||||
|
||||
/* Search the RB tree for the cpu associated with this traceID */
|
||||
inode = intlist__find(traceid_list, trace_chan_id);
|
||||
if (!inode)
|
||||
if (cs_etm__get_cpu(trace_chan_id, &cpu) < 0)
|
||||
return OCSD_RESP_FATAL_SYS_ERR;
|
||||
|
||||
et = decoder->tail;
|
||||
@ -317,12 +321,18 @@ cs_etm_decoder__buffer_packet(struct cs_etm_decoder *decoder,
|
||||
|
||||
decoder->packet_buffer[et].sample_type = sample_type;
|
||||
decoder->packet_buffer[et].isa = CS_ETM_ISA_UNKNOWN;
|
||||
decoder->packet_buffer[et].cpu = *((int *)inode->priv);
|
||||
decoder->packet_buffer[et].cpu = cpu;
|
||||
decoder->packet_buffer[et].start_addr = CS_ETM_INVAL_ADDR;
|
||||
decoder->packet_buffer[et].end_addr = CS_ETM_INVAL_ADDR;
|
||||
decoder->packet_buffer[et].instr_count = 0;
|
||||
decoder->packet_buffer[et].last_instr_taken_branch = false;
|
||||
decoder->packet_buffer[et].last_instr_size = 0;
|
||||
decoder->packet_buffer[et].last_instr_type = 0;
|
||||
decoder->packet_buffer[et].last_instr_subtype = 0;
|
||||
decoder->packet_buffer[et].last_instr_cond = 0;
|
||||
decoder->packet_buffer[et].flags = 0;
|
||||
decoder->packet_buffer[et].exception_number = UINT32_MAX;
|
||||
decoder->packet_buffer[et].trace_chan_id = trace_chan_id;
|
||||
|
||||
if (decoder->packet_count == MAX_BUFFER - 1)
|
||||
return OCSD_RESP_WAIT;
|
||||
@ -366,6 +376,9 @@ cs_etm_decoder__buffer_range(struct cs_etm_decoder *decoder,
|
||||
packet->start_addr = elem->st_addr;
|
||||
packet->end_addr = elem->en_addr;
|
||||
packet->instr_count = elem->num_instr_range;
|
||||
packet->last_instr_type = elem->last_i_type;
|
||||
packet->last_instr_subtype = elem->last_i_subtype;
|
||||
packet->last_instr_cond = elem->last_instr_cond;
|
||||
|
||||
switch (elem->last_i_type) {
|
||||
case OCSD_INSTR_BR:
|
||||
@ -395,10 +408,20 @@ cs_etm_decoder__buffer_discontinuity(struct cs_etm_decoder *decoder,
|
||||
|
||||
static ocsd_datapath_resp_t
|
||||
cs_etm_decoder__buffer_exception(struct cs_etm_decoder *decoder,
|
||||
const ocsd_generic_trace_elem *elem,
|
||||
const uint8_t trace_chan_id)
|
||||
{
|
||||
return cs_etm_decoder__buffer_packet(decoder, trace_chan_id,
|
||||
CS_ETM_EXCEPTION);
|
||||
{ int ret = 0;
|
||||
struct cs_etm_packet *packet;
|
||||
|
||||
ret = cs_etm_decoder__buffer_packet(decoder, trace_chan_id,
|
||||
CS_ETM_EXCEPTION);
|
||||
if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT)
|
||||
return ret;
|
||||
|
||||
packet = &decoder->packet_buffer[decoder->tail];
|
||||
packet->exception_number = elem->exception_number;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ocsd_datapath_resp_t
|
||||
@ -432,7 +455,7 @@ static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
|
||||
trace_chan_id);
|
||||
break;
|
||||
case OCSD_GEN_TRC_ELEM_EXCEPTION:
|
||||
resp = cs_etm_decoder__buffer_exception(decoder,
|
||||
resp = cs_etm_decoder__buffer_exception(decoder, elem,
|
||||
trace_chan_id);
|
||||
break;
|
||||
case OCSD_GEN_TRC_ELEM_EXCEPTION_RET:
|
||||
|
@ -43,8 +43,14 @@ struct cs_etm_packet {
|
||||
u64 start_addr;
|
||||
u64 end_addr;
|
||||
u32 instr_count;
|
||||
u32 last_instr_type;
|
||||
u32 last_instr_subtype;
|
||||
u32 flags;
|
||||
u32 exception_number;
|
||||
u8 last_instr_cond;
|
||||
u8 last_instr_taken_branch;
|
||||
u8 last_instr_size;
|
||||
u8 trace_chan_id;
|
||||
int cpu;
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/log2.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <opencsd/ocsd_if_types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "auxtrace.h"
|
||||
@ -96,6 +97,34 @@ static u32 cs_etm__get_v7_protocol_version(u32 etmidr)
|
||||
return CS_ETM_PROTO_ETMV3;
|
||||
}
|
||||
|
||||
static int cs_etm__get_magic(u8 trace_chan_id, u64 *magic)
|
||||
{
|
||||
struct int_node *inode;
|
||||
u64 *metadata;
|
||||
|
||||
inode = intlist__find(traceid_list, trace_chan_id);
|
||||
if (!inode)
|
||||
return -EINVAL;
|
||||
|
||||
metadata = inode->priv;
|
||||
*magic = metadata[CS_ETM_MAGIC];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cs_etm__get_cpu(u8 trace_chan_id, int *cpu)
|
||||
{
|
||||
struct int_node *inode;
|
||||
u64 *metadata;
|
||||
|
||||
inode = intlist__find(traceid_list, trace_chan_id);
|
||||
if (!inode)
|
||||
return -EINVAL;
|
||||
|
||||
metadata = inode->priv;
|
||||
*cpu = (int)metadata[CS_ETM_CPU];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs_etm__packet_dump(const char *pkt_string)
|
||||
{
|
||||
const char *color = PERF_COLOR_BLUE;
|
||||
@ -251,7 +280,7 @@ static void cs_etm__free(struct perf_session *session)
|
||||
cs_etm__free_events(session);
|
||||
session->auxtrace = NULL;
|
||||
|
||||
/* First remove all traceID/CPU# nodes for the RB tree */
|
||||
/* First remove all traceID/metadata nodes for the RB tree */
|
||||
intlist__for_each_entry_safe(inode, tmp, traceid_list)
|
||||
intlist__remove(traceid_list, inode);
|
||||
/* Then the RB tree itself */
|
||||
@ -719,7 +748,7 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
|
||||
sample.stream_id = etmq->etm->instructions_id;
|
||||
sample.period = period;
|
||||
sample.cpu = etmq->packet->cpu;
|
||||
sample.flags = 0;
|
||||
sample.flags = etmq->prev_packet->flags;
|
||||
sample.insn_len = 1;
|
||||
sample.cpumode = event->sample.header.misc;
|
||||
|
||||
@ -778,7 +807,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq)
|
||||
sample.stream_id = etmq->etm->branches_id;
|
||||
sample.period = 1;
|
||||
sample.cpu = etmq->packet->cpu;
|
||||
sample.flags = 0;
|
||||
sample.flags = etmq->prev_packet->flags;
|
||||
sample.cpumode = event->sample.header.misc;
|
||||
|
||||
/*
|
||||
@ -1107,6 +1136,344 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
|
||||
struct cs_etm_packet *packet,
|
||||
u64 end_addr)
|
||||
{
|
||||
u16 instr16;
|
||||
u32 instr32;
|
||||
u64 addr;
|
||||
|
||||
switch (packet->isa) {
|
||||
case CS_ETM_ISA_T32:
|
||||
/*
|
||||
* The SVC of T32 is defined in ARM DDI 0487D.a, F5.1.247:
|
||||
*
|
||||
* b'15 b'8
|
||||
* +-----------------+--------+
|
||||
* | 1 1 0 1 1 1 1 1 | imm8 |
|
||||
* +-----------------+--------+
|
||||
*
|
||||
* According to the specifiction, it only defines SVC for T32
|
||||
* with 16 bits instruction and has no definition for 32bits;
|
||||
* so below only read 2 bytes as instruction size for T32.
|
||||
*/
|
||||
addr = end_addr - 2;
|
||||
cs_etm__mem_access(etmq, addr, sizeof(instr16), (u8 *)&instr16);
|
||||
if ((instr16 & 0xFF00) == 0xDF00)
|
||||
return true;
|
||||
|
||||
break;
|
||||
case CS_ETM_ISA_A32:
|
||||
/*
|
||||
* The SVC of A32 is defined in ARM DDI 0487D.a, F5.1.247:
|
||||
*
|
||||
* b'31 b'28 b'27 b'24
|
||||
* +---------+---------+-------------------------+
|
||||
* | !1111 | 1 1 1 1 | imm24 |
|
||||
* +---------+---------+-------------------------+
|
||||
*/
|
||||
addr = end_addr - 4;
|
||||
cs_etm__mem_access(etmq, addr, sizeof(instr32), (u8 *)&instr32);
|
||||
if ((instr32 & 0x0F000000) == 0x0F000000 &&
|
||||
(instr32 & 0xF0000000) != 0xF0000000)
|
||||
return true;
|
||||
|
||||
break;
|
||||
case CS_ETM_ISA_A64:
|
||||
/*
|
||||
* The SVC of A64 is defined in ARM DDI 0487D.a, C6.2.294:
|
||||
*
|
||||
* b'31 b'21 b'4 b'0
|
||||
* +-----------------------+---------+-----------+
|
||||
* | 1 1 0 1 0 1 0 0 0 0 0 | imm16 | 0 0 0 0 1 |
|
||||
* +-----------------------+---------+-----------+
|
||||
*/
|
||||
addr = end_addr - 4;
|
||||
cs_etm__mem_access(etmq, addr, sizeof(instr32), (u8 *)&instr32);
|
||||
if ((instr32 & 0xFFE0001F) == 0xd4000001)
|
||||
return true;
|
||||
|
||||
break;
|
||||
case CS_ETM_ISA_UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, u64 magic)
|
||||
{
|
||||
struct cs_etm_packet *packet = etmq->packet;
|
||||
struct cs_etm_packet *prev_packet = etmq->prev_packet;
|
||||
|
||||
if (magic == __perf_cs_etmv3_magic)
|
||||
if (packet->exception_number == CS_ETMV3_EXC_SVC)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* ETMv4 exception type CS_ETMV4_EXC_CALL covers SVC, SMC and
|
||||
* HVC cases; need to check if it's SVC instruction based on
|
||||
* packet address.
|
||||
*/
|
||||
if (magic == __perf_cs_etmv4_magic) {
|
||||
if (packet->exception_number == CS_ETMV4_EXC_CALL &&
|
||||
cs_etm__is_svc_instr(etmq, prev_packet,
|
||||
prev_packet->end_addr))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cs_etm__is_async_exception(struct cs_etm_queue *etmq, u64 magic)
|
||||
{
|
||||
struct cs_etm_packet *packet = etmq->packet;
|
||||
|
||||
if (magic == __perf_cs_etmv3_magic)
|
||||
if (packet->exception_number == CS_ETMV3_EXC_DEBUG_HALT ||
|
||||
packet->exception_number == CS_ETMV3_EXC_ASYNC_DATA_ABORT ||
|
||||
packet->exception_number == CS_ETMV3_EXC_PE_RESET ||
|
||||
packet->exception_number == CS_ETMV3_EXC_IRQ ||
|
||||
packet->exception_number == CS_ETMV3_EXC_FIQ)
|
||||
return true;
|
||||
|
||||
if (magic == __perf_cs_etmv4_magic)
|
||||
if (packet->exception_number == CS_ETMV4_EXC_RESET ||
|
||||
packet->exception_number == CS_ETMV4_EXC_DEBUG_HALT ||
|
||||
packet->exception_number == CS_ETMV4_EXC_SYSTEM_ERROR ||
|
||||
packet->exception_number == CS_ETMV4_EXC_INST_DEBUG ||
|
||||
packet->exception_number == CS_ETMV4_EXC_DATA_DEBUG ||
|
||||
packet->exception_number == CS_ETMV4_EXC_IRQ ||
|
||||
packet->exception_number == CS_ETMV4_EXC_FIQ)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, u64 magic)
|
||||
{
|
||||
struct cs_etm_packet *packet = etmq->packet;
|
||||
struct cs_etm_packet *prev_packet = etmq->prev_packet;
|
||||
|
||||
if (magic == __perf_cs_etmv3_magic)
|
||||
if (packet->exception_number == CS_ETMV3_EXC_SMC ||
|
||||
packet->exception_number == CS_ETMV3_EXC_HYP ||
|
||||
packet->exception_number == CS_ETMV3_EXC_JAZELLE_THUMBEE ||
|
||||
packet->exception_number == CS_ETMV3_EXC_UNDEFINED_INSTR ||
|
||||
packet->exception_number == CS_ETMV3_EXC_PREFETCH_ABORT ||
|
||||
packet->exception_number == CS_ETMV3_EXC_DATA_FAULT ||
|
||||
packet->exception_number == CS_ETMV3_EXC_GENERIC)
|
||||
return true;
|
||||
|
||||
if (magic == __perf_cs_etmv4_magic) {
|
||||
if (packet->exception_number == CS_ETMV4_EXC_TRAP ||
|
||||
packet->exception_number == CS_ETMV4_EXC_ALIGNMENT ||
|
||||
packet->exception_number == CS_ETMV4_EXC_INST_FAULT ||
|
||||
packet->exception_number == CS_ETMV4_EXC_DATA_FAULT)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* For CS_ETMV4_EXC_CALL, except SVC other instructions
|
||||
* (SMC, HVC) are taken as sync exceptions.
|
||||
*/
|
||||
if (packet->exception_number == CS_ETMV4_EXC_CALL &&
|
||||
!cs_etm__is_svc_instr(etmq, prev_packet,
|
||||
prev_packet->end_addr))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* ETMv4 has 5 bits for exception number; if the numbers
|
||||
* are in the range ( CS_ETMV4_EXC_FIQ, CS_ETMV4_EXC_END ]
|
||||
* they are implementation defined exceptions.
|
||||
*
|
||||
* For this case, simply take it as sync exception.
|
||||
*/
|
||||
if (packet->exception_number > CS_ETMV4_EXC_FIQ &&
|
||||
packet->exception_number <= CS_ETMV4_EXC_END)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq)
|
||||
{
|
||||
struct cs_etm_packet *packet = etmq->packet;
|
||||
struct cs_etm_packet *prev_packet = etmq->prev_packet;
|
||||
u64 magic;
|
||||
int ret;
|
||||
|
||||
switch (packet->sample_type) {
|
||||
case CS_ETM_RANGE:
|
||||
/*
|
||||
* Immediate branch instruction without neither link nor
|
||||
* return flag, it's normal branch instruction within
|
||||
* the function.
|
||||
*/
|
||||
if (packet->last_instr_type == OCSD_INSTR_BR &&
|
||||
packet->last_instr_subtype == OCSD_S_INSTR_NONE) {
|
||||
packet->flags = PERF_IP_FLAG_BRANCH;
|
||||
|
||||
if (packet->last_instr_cond)
|
||||
packet->flags |= PERF_IP_FLAG_CONDITIONAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Immediate branch instruction with link (e.g. BL), this is
|
||||
* branch instruction for function call.
|
||||
*/
|
||||
if (packet->last_instr_type == OCSD_INSTR_BR &&
|
||||
packet->last_instr_subtype == OCSD_S_INSTR_BR_LINK)
|
||||
packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_CALL;
|
||||
|
||||
/*
|
||||
* Indirect branch instruction with link (e.g. BLR), this is
|
||||
* branch instruction for function call.
|
||||
*/
|
||||
if (packet->last_instr_type == OCSD_INSTR_BR_INDIRECT &&
|
||||
packet->last_instr_subtype == OCSD_S_INSTR_BR_LINK)
|
||||
packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_CALL;
|
||||
|
||||
/*
|
||||
* Indirect branch instruction with subtype of
|
||||
* OCSD_S_INSTR_V7_IMPLIED_RET, this is explicit hint for
|
||||
* function return for A32/T32.
|
||||
*/
|
||||
if (packet->last_instr_type == OCSD_INSTR_BR_INDIRECT &&
|
||||
packet->last_instr_subtype == OCSD_S_INSTR_V7_IMPLIED_RET)
|
||||
packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_RETURN;
|
||||
|
||||
/*
|
||||
* Indirect branch instruction without link (e.g. BR), usually
|
||||
* this is used for function return, especially for functions
|
||||
* within dynamic link lib.
|
||||
*/
|
||||
if (packet->last_instr_type == OCSD_INSTR_BR_INDIRECT &&
|
||||
packet->last_instr_subtype == OCSD_S_INSTR_NONE)
|
||||
packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_RETURN;
|
||||
|
||||
/* Return instruction for function return. */
|
||||
if (packet->last_instr_type == OCSD_INSTR_BR_INDIRECT &&
|
||||
packet->last_instr_subtype == OCSD_S_INSTR_V8_RET)
|
||||
packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_RETURN;
|
||||
|
||||
/*
|
||||
* Decoder might insert a discontinuity in the middle of
|
||||
* instruction packets, fixup prev_packet with flag
|
||||
* PERF_IP_FLAG_TRACE_BEGIN to indicate restarting trace.
|
||||
*/
|
||||
if (prev_packet->sample_type == CS_ETM_DISCONTINUITY)
|
||||
prev_packet->flags |= PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_TRACE_BEGIN;
|
||||
|
||||
/*
|
||||
* If the previous packet is an exception return packet
|
||||
* and the return address just follows SVC instuction,
|
||||
* it needs to calibrate the previous packet sample flags
|
||||
* as PERF_IP_FLAG_SYSCALLRET.
|
||||
*/
|
||||
if (prev_packet->flags == (PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_RETURN |
|
||||
PERF_IP_FLAG_INTERRUPT) &&
|
||||
cs_etm__is_svc_instr(etmq, packet, packet->start_addr))
|
||||
prev_packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_RETURN |
|
||||
PERF_IP_FLAG_SYSCALLRET;
|
||||
break;
|
||||
case CS_ETM_DISCONTINUITY:
|
||||
/*
|
||||
* The trace is discontinuous, if the previous packet is
|
||||
* instruction packet, set flag PERF_IP_FLAG_TRACE_END
|
||||
* for previous packet.
|
||||
*/
|
||||
if (prev_packet->sample_type == CS_ETM_RANGE)
|
||||
prev_packet->flags |= PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_TRACE_END;
|
||||
break;
|
||||
case CS_ETM_EXCEPTION:
|
||||
ret = cs_etm__get_magic(packet->trace_chan_id, &magic);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* The exception is for system call. */
|
||||
if (cs_etm__is_syscall(etmq, magic))
|
||||
packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_CALL |
|
||||
PERF_IP_FLAG_SYSCALLRET;
|
||||
/*
|
||||
* The exceptions are triggered by external signals from bus,
|
||||
* interrupt controller, debug module, PE reset or halt.
|
||||
*/
|
||||
else if (cs_etm__is_async_exception(etmq, magic))
|
||||
packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_CALL |
|
||||
PERF_IP_FLAG_ASYNC |
|
||||
PERF_IP_FLAG_INTERRUPT;
|
||||
/*
|
||||
* Otherwise, exception is caused by trap, instruction &
|
||||
* data fault, or alignment errors.
|
||||
*/
|
||||
else if (cs_etm__is_sync_exception(etmq, magic))
|
||||
packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_CALL |
|
||||
PERF_IP_FLAG_INTERRUPT;
|
||||
|
||||
/*
|
||||
* When the exception packet is inserted, since exception
|
||||
* packet is not used standalone for generating samples
|
||||
* and it's affiliation to the previous instruction range
|
||||
* packet; so set previous range packet flags to tell perf
|
||||
* it is an exception taken branch.
|
||||
*/
|
||||
if (prev_packet->sample_type == CS_ETM_RANGE)
|
||||
prev_packet->flags = packet->flags;
|
||||
break;
|
||||
case CS_ETM_EXCEPTION_RET:
|
||||
/*
|
||||
* When the exception return packet is inserted, since
|
||||
* exception return packet is not used standalone for
|
||||
* generating samples and it's affiliation to the previous
|
||||
* instruction range packet; so set previous range packet
|
||||
* flags to tell perf it is an exception return branch.
|
||||
*
|
||||
* The exception return can be for either system call or
|
||||
* other exception types; unfortunately the packet doesn't
|
||||
* contain exception type related info so we cannot decide
|
||||
* the exception type purely based on exception return packet.
|
||||
* If we record the exception number from exception packet and
|
||||
* reuse it for excpetion return packet, this is not reliable
|
||||
* due the trace can be discontinuity or the interrupt can
|
||||
* be nested, thus the recorded exception number cannot be
|
||||
* used for exception return packet for these two cases.
|
||||
*
|
||||
* For exception return packet, we only need to distinguish the
|
||||
* packet is for system call or for other types. Thus the
|
||||
* decision can be deferred when receive the next packet which
|
||||
* contains the return address, based on the return address we
|
||||
* can read out the previous instruction and check if it's a
|
||||
* system call instruction and then calibrate the sample flag
|
||||
* as needed.
|
||||
*/
|
||||
if (prev_packet->sample_type == CS_ETM_RANGE)
|
||||
prev_packet->flags = PERF_IP_FLAG_BRANCH |
|
||||
PERF_IP_FLAG_RETURN |
|
||||
PERF_IP_FLAG_INTERRUPT;
|
||||
break;
|
||||
case CS_ETM_EMPTY:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
|
||||
{
|
||||
struct cs_etm_auxtrace *etm = etmq->etm;
|
||||
@ -1158,6 +1525,17 @@ static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
|
||||
*/
|
||||
break;
|
||||
|
||||
/*
|
||||
* Since packet addresses are swapped in packet
|
||||
* handling within below switch() statements,
|
||||
* thus setting sample flags must be called
|
||||
* prior to switch() statement to use address
|
||||
* information before packets swapping.
|
||||
*/
|
||||
err = cs_etm__set_sample_flags(etmq);
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
switch (etmq->packet->sample_type) {
|
||||
case CS_ETM_RANGE:
|
||||
/*
|
||||
@ -1414,9 +1792,9 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
|
||||
0xffffffff);
|
||||
|
||||
/*
|
||||
* Create an RB tree for traceID-CPU# tuple. Since the conversion has
|
||||
* to be made for each packet that gets decoded, optimizing access in
|
||||
* anything other than a sequential array is worth doing.
|
||||
* Create an RB tree for traceID-metadata tuple. Since the conversion
|
||||
* has to be made for each packet that gets decoded, optimizing access
|
||||
* in anything other than a sequential array is worth doing.
|
||||
*/
|
||||
traceid_list = intlist__new(NULL);
|
||||
if (!traceid_list) {
|
||||
@ -1482,8 +1860,8 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
|
||||
err = -EINVAL;
|
||||
goto err_free_metadata;
|
||||
}
|
||||
/* All good, associate the traceID with the CPU# */
|
||||
inode->priv = &metadata[j][CS_ETM_CPU];
|
||||
/* All good, associate the traceID with the metadata pointer */
|
||||
inode->priv = metadata[j];
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -53,7 +53,51 @@ enum {
|
||||
CS_ETMV4_PRIV_MAX,
|
||||
};
|
||||
|
||||
/* RB tree for quick conversion between traceID and CPUs */
|
||||
/*
|
||||
* ETMv3 exception encoding number:
|
||||
* See Embedded Trace Macrocell spcification (ARM IHI 0014Q)
|
||||
* table 7-12 Encoding of Exception[3:0] for non-ARMv7-M processors.
|
||||
*/
|
||||
enum {
|
||||
CS_ETMV3_EXC_NONE = 0,
|
||||
CS_ETMV3_EXC_DEBUG_HALT = 1,
|
||||
CS_ETMV3_EXC_SMC = 2,
|
||||
CS_ETMV3_EXC_HYP = 3,
|
||||
CS_ETMV3_EXC_ASYNC_DATA_ABORT = 4,
|
||||
CS_ETMV3_EXC_JAZELLE_THUMBEE = 5,
|
||||
CS_ETMV3_EXC_PE_RESET = 8,
|
||||
CS_ETMV3_EXC_UNDEFINED_INSTR = 9,
|
||||
CS_ETMV3_EXC_SVC = 10,
|
||||
CS_ETMV3_EXC_PREFETCH_ABORT = 11,
|
||||
CS_ETMV3_EXC_DATA_FAULT = 12,
|
||||
CS_ETMV3_EXC_GENERIC = 13,
|
||||
CS_ETMV3_EXC_IRQ = 14,
|
||||
CS_ETMV3_EXC_FIQ = 15,
|
||||
};
|
||||
|
||||
/*
|
||||
* ETMv4 exception encoding number:
|
||||
* See ARM Embedded Trace Macrocell Architecture Specification (ARM IHI 0064D)
|
||||
* table 6-12 Possible values for the TYPE field in an Exception instruction
|
||||
* trace packet, for ARMv7-A/R and ARMv8-A/R PEs.
|
||||
*/
|
||||
enum {
|
||||
CS_ETMV4_EXC_RESET = 0,
|
||||
CS_ETMV4_EXC_DEBUG_HALT = 1,
|
||||
CS_ETMV4_EXC_CALL = 2,
|
||||
CS_ETMV4_EXC_TRAP = 3,
|
||||
CS_ETMV4_EXC_SYSTEM_ERROR = 4,
|
||||
CS_ETMV4_EXC_INST_DEBUG = 6,
|
||||
CS_ETMV4_EXC_DATA_DEBUG = 7,
|
||||
CS_ETMV4_EXC_ALIGNMENT = 10,
|
||||
CS_ETMV4_EXC_INST_FAULT = 11,
|
||||
CS_ETMV4_EXC_DATA_FAULT = 12,
|
||||
CS_ETMV4_EXC_IRQ = 14,
|
||||
CS_ETMV4_EXC_FIQ = 15,
|
||||
CS_ETMV4_EXC_END = 31,
|
||||
};
|
||||
|
||||
/* RB tree for quick conversion between traceID and metadata pointers */
|
||||
struct intlist *traceid_list;
|
||||
|
||||
#define KiB(x) ((x) * 1024)
|
||||
@ -69,6 +113,7 @@ static const u64 __perf_cs_etmv4_magic = 0x4040404040404040ULL;
|
||||
#ifdef HAVE_CSTRACE_SUPPORT
|
||||
int cs_etm__process_auxtrace_info(union perf_event *event,
|
||||
struct perf_session *session);
|
||||
int cs_etm__get_cpu(u8 trace_chan_id, int *cpu);
|
||||
#else
|
||||
static inline int
|
||||
cs_etm__process_auxtrace_info(union perf_event *event __maybe_unused,
|
||||
@ -76,6 +121,12 @@ cs_etm__process_auxtrace_info(union perf_event *event __maybe_unused,
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int cs_etm__get_cpu(u8 trace_chan_id __maybe_unused,
|
||||
int *cpu __maybe_unused)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "thread.h"
|
||||
#include "comm.h"
|
||||
#include "symbol.h"
|
||||
#include "map.h"
|
||||
#include "event.h"
|
||||
#include "util.h"
|
||||
#include "thread-stack.h"
|
||||
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* drv_configs.h: Interface to apply PMU specific configuration
|
||||
* Copyright (c) 2016-2018, Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "drv_configs.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "pmu.h"
|
||||
#include <errno.h>
|
||||
|
||||
static int
|
||||
perf_evsel__apply_drv_configs(struct perf_evsel *evsel,
|
||||
struct perf_evsel_config_term **err_term)
|
||||
{
|
||||
bool found = false;
|
||||
int err = 0;
|
||||
struct perf_evsel_config_term *term;
|
||||
struct perf_pmu *pmu = NULL;
|
||||
|
||||
while ((pmu = perf_pmu__scan(pmu)) != NULL)
|
||||
if (pmu->type == evsel->attr.type) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
list_for_each_entry(term, &evsel->config_terms, list) {
|
||||
if (term->type != PERF_EVSEL__CONFIG_TERM_DRV_CFG)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We have a configuration term, report an error if we
|
||||
* can't find the PMU or if the PMU driver doesn't support
|
||||
* cmd line driver configuration.
|
||||
*/
|
||||
if (!found || !pmu->set_drv_config) {
|
||||
err = -EINVAL;
|
||||
*err_term = term;
|
||||
break;
|
||||
}
|
||||
|
||||
err = pmu->set_drv_config(term);
|
||||
if (err) {
|
||||
*err_term = term;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_evlist__apply_drv_configs(struct perf_evlist *evlist,
|
||||
struct perf_evsel **err_evsel,
|
||||
struct perf_evsel_config_term **err_term)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
int err = 0;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
err = perf_evsel__apply_drv_configs(evsel, err_term);
|
||||
if (err) {
|
||||
*err_evsel = evsel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* drv_configs.h: Interface to apply PMU specific configuration
|
||||
* Copyright (c) 2016-2018, Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __PERF_DRV_CONFIGS_H
|
||||
#define __PERF_DRV_CONFIGS_H
|
||||
|
||||
#include "drv_configs.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
|
||||
int perf_evlist__apply_drv_configs(struct perf_evlist *evlist,
|
||||
struct perf_evsel **err_evsel,
|
||||
struct perf_evsel_config_term **term);
|
||||
#endif
|
@ -12,6 +12,7 @@
|
||||
#include "compress.h"
|
||||
#include "namespaces.h"
|
||||
#include "path.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "srcline.h"
|
||||
#include "dso.h"
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include "thread.h"
|
||||
#include "thread_map.h"
|
||||
#include "sane_ctype.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "symbol/kallsyms.h"
|
||||
#include "asm/bug.h"
|
||||
#include "stat.h"
|
||||
|
@ -532,8 +532,9 @@ struct auxtrace_error_event {
|
||||
u32 cpu;
|
||||
u32 pid;
|
||||
u32 tid;
|
||||
u32 reserved__; /* For alignment */
|
||||
u32 fmt;
|
||||
u64 ip;
|
||||
u64 time;
|
||||
char msg[MAX_AUXTRACE_ERROR_MSG];
|
||||
};
|
||||
|
||||
|
@ -1022,7 +1022,7 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
|
||||
*/
|
||||
int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
|
||||
unsigned int auxtrace_pages,
|
||||
bool auxtrace_overwrite, int nr_cblocks)
|
||||
bool auxtrace_overwrite, int nr_cblocks, int affinity)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
const struct cpu_map *cpus = evlist->cpus;
|
||||
@ -1032,7 +1032,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
|
||||
* Its value is decided by evsel's write_backward.
|
||||
* So &mp should not be passed through const pointer.
|
||||
*/
|
||||
struct mmap_params mp = { .nr_cblocks = nr_cblocks };
|
||||
struct mmap_params mp = { .nr_cblocks = nr_cblocks, .affinity = affinity };
|
||||
|
||||
if (!evlist->mmap)
|
||||
evlist->mmap = perf_evlist__alloc_mmap(evlist, false);
|
||||
@ -1064,7 +1064,7 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
|
||||
|
||||
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages)
|
||||
{
|
||||
return perf_evlist__mmap_ex(evlist, pages, 0, false, 0);
|
||||
return perf_evlist__mmap_ex(evlist, pages, 0, false, 0, PERF_AFFINITY_SYS);
|
||||
}
|
||||
|
||||
int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
|
||||
|
@ -165,7 +165,7 @@ unsigned long perf_event_mlock_kb_in_pages(void);
|
||||
|
||||
int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
|
||||
unsigned int auxtrace_pages,
|
||||
bool auxtrace_overwrite, int nr_cblocks);
|
||||
bool auxtrace_overwrite, int nr_cblocks, int affinity);
|
||||
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages);
|
||||
void perf_evlist__munmap(struct perf_evlist *evlist);
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/types.h>
|
||||
#include "xyarray.h"
|
||||
#include "symbol.h"
|
||||
#include "symbol_conf.h"
|
||||
#include "cpumap.h"
|
||||
#include "counts.h"
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "callchain.h"
|
||||
#include "util.h"
|
||||
#include "build-id.h"
|
||||
#include "hist.h"
|
||||
@ -11,6 +12,7 @@
|
||||
#include "evsel.h"
|
||||
#include "annotate.h"
|
||||
#include "srcline.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include "ui/progress.h"
|
||||
#include <errno.h>
|
||||
@ -1719,7 +1721,8 @@ static void __hists__insert_output_entry(struct rb_root_cached *entries,
|
||||
}
|
||||
|
||||
static void output_resort(struct hists *hists, struct ui_progress *prog,
|
||||
bool use_callchain, hists__resort_cb_t cb)
|
||||
bool use_callchain, hists__resort_cb_t cb,
|
||||
void *cb_arg)
|
||||
{
|
||||
struct rb_root_cached *root;
|
||||
struct rb_node *next;
|
||||
@ -1758,7 +1761,7 @@ static void output_resort(struct hists *hists, struct ui_progress *prog,
|
||||
n = rb_entry(next, struct hist_entry, rb_node_in);
|
||||
next = rb_next(&n->rb_node_in);
|
||||
|
||||
if (cb && cb(n))
|
||||
if (cb && cb(n, cb_arg))
|
||||
continue;
|
||||
|
||||
__hists__insert_output_entry(&hists->entries, n, min_callchain_hits, use_callchain);
|
||||
@ -1772,7 +1775,8 @@ static void output_resort(struct hists *hists, struct ui_progress *prog,
|
||||
}
|
||||
}
|
||||
|
||||
void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog)
|
||||
void perf_evsel__output_resort_cb(struct perf_evsel *evsel, struct ui_progress *prog,
|
||||
hists__resort_cb_t cb, void *cb_arg)
|
||||
{
|
||||
bool use_callchain;
|
||||
|
||||
@ -1783,18 +1787,23 @@ void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *pro
|
||||
|
||||
use_callchain |= symbol_conf.show_branchflag_count;
|
||||
|
||||
output_resort(evsel__hists(evsel), prog, use_callchain, NULL);
|
||||
output_resort(evsel__hists(evsel), prog, use_callchain, cb, cb_arg);
|
||||
}
|
||||
|
||||
void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog)
|
||||
{
|
||||
return perf_evsel__output_resort_cb(evsel, prog, NULL, NULL);
|
||||
}
|
||||
|
||||
void hists__output_resort(struct hists *hists, struct ui_progress *prog)
|
||||
{
|
||||
output_resort(hists, prog, symbol_conf.use_callchain, NULL);
|
||||
output_resort(hists, prog, symbol_conf.use_callchain, NULL, NULL);
|
||||
}
|
||||
|
||||
void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog,
|
||||
hists__resort_cb_t cb)
|
||||
{
|
||||
output_resort(hists, prog, symbol_conf.use_callchain, cb);
|
||||
output_resort(hists, prog, symbol_conf.use_callchain, cb, NULL);
|
||||
}
|
||||
|
||||
static bool can_goto_child(struct hist_entry *he, enum hierarchy_move_dir hmd)
|
||||
|
@ -2,9 +2,9 @@
|
||||
#ifndef __PERF_HIST_H
|
||||
#define __PERF_HIST_H
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/types.h>
|
||||
#include <pthread.h>
|
||||
#include "callchain.h"
|
||||
#include "evsel.h"
|
||||
#include "header.h"
|
||||
#include "color.h"
|
||||
@ -13,6 +13,9 @@
|
||||
struct hist_entry;
|
||||
struct hist_entry_ops;
|
||||
struct addr_location;
|
||||
struct map_symbol;
|
||||
struct mem_info;
|
||||
struct branch_info;
|
||||
struct symbol;
|
||||
|
||||
enum hist_filter {
|
||||
@ -160,8 +163,10 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp,
|
||||
struct perf_hpp_fmt *fmt, int printed);
|
||||
void hist_entry__delete(struct hist_entry *he);
|
||||
|
||||
typedef int (*hists__resort_cb_t)(struct hist_entry *he);
|
||||
typedef int (*hists__resort_cb_t)(struct hist_entry *he, void *arg);
|
||||
|
||||
void perf_evsel__output_resort_cb(struct perf_evsel *evsel, struct ui_progress *prog,
|
||||
hists__resort_cb_t cb, void *cb_arg);
|
||||
void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog);
|
||||
void hists__output_resort(struct hists *hists, struct ui_progress *prog);
|
||||
void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog,
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "machine.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "session.h"
|
||||
#include "util.h"
|
||||
#include "thread.h"
|
||||
@ -142,7 +144,7 @@ static int intel_bts_lost(struct intel_bts *bts, struct perf_sample *sample)
|
||||
|
||||
auxtrace_synth_error(&event.auxtrace_error, PERF_AUXTRACE_ERROR_ITRACE,
|
||||
INTEL_BTS_ERR_LOST, sample->cpu, sample->pid,
|
||||
sample->tid, 0, "Lost trace data");
|
||||
sample->tid, 0, "Lost trace data", sample->time);
|
||||
|
||||
err = perf_session__deliver_synth_event(bts->session, &event, NULL);
|
||||
if (err)
|
||||
@ -372,7 +374,7 @@ static int intel_bts_synth_error(struct intel_bts *bts, int cpu, pid_t pid,
|
||||
|
||||
auxtrace_synth_error(&event.auxtrace_error, PERF_AUXTRACE_ERROR_ITRACE,
|
||||
INTEL_BTS_ERR_NOINSN, cpu, pid, tid, ip,
|
||||
"Failed to get instruction");
|
||||
"Failed to get instruction", 0);
|
||||
|
||||
err = perf_session__deliver_synth_event(bts->session, &event, NULL);
|
||||
if (err)
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "../cache.h"
|
||||
#include "../util.h"
|
||||
#include "../auxtrace.h"
|
||||
|
||||
#include "intel-pt-insn-decoder.h"
|
||||
#include "intel-pt-pkt-decoder.h"
|
||||
@ -867,7 +868,7 @@ static int intel_pt_get_next_packet(struct intel_pt_decoder *decoder)
|
||||
|
||||
ret = intel_pt_get_packet(decoder->buf, decoder->len,
|
||||
&decoder->packet);
|
||||
if (ret == INTEL_PT_NEED_MORE_BYTES &&
|
||||
if (ret == INTEL_PT_NEED_MORE_BYTES && BITS_PER_LONG == 32 &&
|
||||
decoder->len < INTEL_PT_PKT_MAX_SZ && !decoder->next_buf) {
|
||||
ret = intel_pt_get_split_packet(decoder);
|
||||
if (ret < 0)
|
||||
@ -1394,7 +1395,6 @@ static int intel_pt_overflow(struct intel_pt_decoder *decoder)
|
||||
{
|
||||
intel_pt_log("ERROR: Buffer overflow\n");
|
||||
intel_pt_clear_tx_flags(decoder);
|
||||
decoder->cbr = 0;
|
||||
decoder->timestamp_insn_cnt = 0;
|
||||
decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC;
|
||||
decoder->overflow = true;
|
||||
@ -2575,6 +2575,34 @@ static int intel_pt_tsc_cmp(uint64_t tsc1, uint64_t tsc2)
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_PADDING (PERF_AUXTRACE_RECORD_ALIGNMENT - 1)
|
||||
|
||||
/**
|
||||
* adj_for_padding - adjust overlap to account for padding.
|
||||
* @buf_b: second buffer
|
||||
* @buf_a: first buffer
|
||||
* @len_a: size of first buffer
|
||||
*
|
||||
* @buf_a might have up to 7 bytes of padding appended. Adjust the overlap
|
||||
* accordingly.
|
||||
*
|
||||
* Return: A pointer into @buf_b from where non-overlapped data starts
|
||||
*/
|
||||
static unsigned char *adj_for_padding(unsigned char *buf_b,
|
||||
unsigned char *buf_a, size_t len_a)
|
||||
{
|
||||
unsigned char *p = buf_b - MAX_PADDING;
|
||||
unsigned char *q = buf_a + len_a - MAX_PADDING;
|
||||
int i;
|
||||
|
||||
for (i = MAX_PADDING; i; i--, p++, q++) {
|
||||
if (*p != *q)
|
||||
break;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pt_find_overlap_tsc - determine start of non-overlapped trace data
|
||||
* using TSC.
|
||||
@ -2625,8 +2653,11 @@ static unsigned char *intel_pt_find_overlap_tsc(unsigned char *buf_a,
|
||||
|
||||
/* Same TSC, so buffers are consecutive */
|
||||
if (!cmp && rem_b >= rem_a) {
|
||||
unsigned char *start;
|
||||
|
||||
*consecutive = true;
|
||||
return buf_b + len_b - (rem_b - rem_a);
|
||||
start = buf_b + len_b - (rem_b - rem_a);
|
||||
return adj_for_padding(start, buf_a, len_a);
|
||||
}
|
||||
if (cmp < 0)
|
||||
return buf_b; /* tsc_a < tsc_b => no overlap */
|
||||
@ -2689,7 +2720,7 @@ unsigned char *intel_pt_find_overlap(unsigned char *buf_a, size_t len_a,
|
||||
found = memmem(buf_a, len_a, buf_b, len_a);
|
||||
if (found) {
|
||||
*consecutive = true;
|
||||
return buf_b + len_a;
|
||||
return adj_for_padding(buf_b + len_a, buf_a, len_a);
|
||||
}
|
||||
|
||||
/* Try again at next PSB in buffer 'a' */
|
||||
|
@ -1411,7 +1411,7 @@ static int intel_pt_synth_pwrx_sample(struct intel_pt_queue *ptq)
|
||||
}
|
||||
|
||||
static int intel_pt_synth_error(struct intel_pt *pt, int code, int cpu,
|
||||
pid_t pid, pid_t tid, u64 ip)
|
||||
pid_t pid, pid_t tid, u64 ip, u64 timestamp)
|
||||
{
|
||||
union perf_event event;
|
||||
char msg[MAX_AUXTRACE_ERROR_MSG];
|
||||
@ -1420,7 +1420,7 @@ static int intel_pt_synth_error(struct intel_pt *pt, int code, int cpu,
|
||||
intel_pt__strerror(code, msg, MAX_AUXTRACE_ERROR_MSG);
|
||||
|
||||
auxtrace_synth_error(&event.auxtrace_error, PERF_AUXTRACE_ERROR_ITRACE,
|
||||
code, cpu, pid, tid, ip, msg);
|
||||
code, cpu, pid, tid, ip, msg, timestamp);
|
||||
|
||||
err = perf_session__deliver_synth_event(pt->session, &event, NULL);
|
||||
if (err)
|
||||
@ -1430,6 +1430,18 @@ static int intel_pt_synth_error(struct intel_pt *pt, int code, int cpu,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int intel_ptq_synth_error(struct intel_pt_queue *ptq,
|
||||
const struct intel_pt_state *state)
|
||||
{
|
||||
struct intel_pt *pt = ptq->pt;
|
||||
u64 tm = ptq->timestamp;
|
||||
|
||||
tm = pt->timeless_decoding ? 0 : tsc_to_perf_time(tm, &pt->tc);
|
||||
|
||||
return intel_pt_synth_error(pt, state->err, ptq->cpu, ptq->pid,
|
||||
ptq->tid, state->from_ip, tm);
|
||||
}
|
||||
|
||||
static int intel_pt_next_tid(struct intel_pt *pt, struct intel_pt_queue *ptq)
|
||||
{
|
||||
struct auxtrace_queue *queue;
|
||||
@ -1676,10 +1688,7 @@ static int intel_pt_run_decoder(struct intel_pt_queue *ptq, u64 *timestamp)
|
||||
intel_pt_next_tid(pt, ptq);
|
||||
}
|
||||
if (pt->synth_opts.errors) {
|
||||
err = intel_pt_synth_error(pt, state->err,
|
||||
ptq->cpu, ptq->pid,
|
||||
ptq->tid,
|
||||
state->from_ip);
|
||||
err = intel_ptq_synth_error(ptq, state);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -1804,7 +1813,7 @@ static int intel_pt_process_timeless_queues(struct intel_pt *pt, pid_t tid,
|
||||
static int intel_pt_lost(struct intel_pt *pt, struct perf_sample *sample)
|
||||
{
|
||||
return intel_pt_synth_error(pt, INTEL_PT_ERR_LOST, sample->cpu,
|
||||
sample->pid, sample->tid, 0);
|
||||
sample->pid, sample->tid, 0, sample->time);
|
||||
}
|
||||
|
||||
static struct intel_pt_queue *intel_pt_cpu_to_ptq(struct intel_pt *pt, int cpu)
|
||||
|
@ -3,12 +3,13 @@
|
||||
#define __PERF_KVM_STAT_H
|
||||
|
||||
#include "../perf.h"
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "session.h"
|
||||
#include "tool.h"
|
||||
#include "stat.h"
|
||||
|
||||
struct perf_evsel;
|
||||
struct perf_evlist;
|
||||
struct perf_session;
|
||||
|
||||
struct event_key {
|
||||
#define INVALID_KEY (~0ULL)
|
||||
u64 key;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "hist.h"
|
||||
#include "machine.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "sort.h"
|
||||
#include "strlist.h"
|
||||
#include "thread.h"
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include "map.h"
|
||||
#include "map_groups.h"
|
||||
#include "dso.h"
|
||||
#include "event.h"
|
||||
#include "rwsem.h"
|
||||
|
@ -557,6 +557,12 @@ void map_groups__init(struct map_groups *mg, struct machine *machine)
|
||||
refcount_set(&mg->refcnt, 1);
|
||||
}
|
||||
|
||||
void map_groups__insert(struct map_groups *mg, struct map *map)
|
||||
{
|
||||
maps__insert(&mg->maps, map);
|
||||
map->groups = mg;
|
||||
}
|
||||
|
||||
static void __maps__purge(struct maps *maps)
|
||||
{
|
||||
struct rb_root *root = &maps->entries;
|
||||
|
@ -6,12 +6,10 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/types.h>
|
||||
#include "rwsem.h"
|
||||
|
||||
struct dso;
|
||||
struct ip_callchain;
|
||||
@ -48,38 +46,7 @@ struct map {
|
||||
refcount_t refcnt;
|
||||
};
|
||||
|
||||
#define KMAP_NAME_LEN 256
|
||||
|
||||
struct kmap {
|
||||
struct ref_reloc_sym *ref_reloc_sym;
|
||||
struct map_groups *kmaps;
|
||||
char name[KMAP_NAME_LEN];
|
||||
};
|
||||
|
||||
struct maps {
|
||||
struct rb_root entries;
|
||||
struct rb_root names;
|
||||
struct rw_semaphore lock;
|
||||
};
|
||||
|
||||
struct map_groups {
|
||||
struct maps maps;
|
||||
struct machine *machine;
|
||||
refcount_t refcnt;
|
||||
};
|
||||
|
||||
struct map_groups *map_groups__new(struct machine *machine);
|
||||
void map_groups__delete(struct map_groups *mg);
|
||||
bool map_groups__empty(struct map_groups *mg);
|
||||
|
||||
static inline struct map_groups *map_groups__get(struct map_groups *mg)
|
||||
{
|
||||
if (mg)
|
||||
refcount_inc(&mg->refcnt);
|
||||
return mg;
|
||||
}
|
||||
|
||||
void map_groups__put(struct map_groups *mg);
|
||||
struct kmap;
|
||||
|
||||
struct kmap *__map__kmap(struct map *map);
|
||||
struct kmap *map__kmap(struct map *map);
|
||||
@ -174,18 +141,7 @@ char *map__srcline(struct map *map, u64 addr, struct symbol *sym);
|
||||
int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
|
||||
FILE *fp);
|
||||
|
||||
struct srccode_state {
|
||||
char *srcfile;
|
||||
unsigned line;
|
||||
};
|
||||
|
||||
static inline void srccode_state_init(struct srccode_state *state)
|
||||
{
|
||||
state->srcfile = NULL;
|
||||
state->line = 0;
|
||||
}
|
||||
|
||||
void srccode_state_free(struct srccode_state *state);
|
||||
struct srccode_state;
|
||||
|
||||
int map__fprintf_srccode(struct map *map, u64 addr,
|
||||
FILE *fp, struct srccode_state *state);
|
||||
@ -198,61 +154,9 @@ void map__fixup_end(struct map *map);
|
||||
|
||||
void map__reloc_vmlinux(struct map *map);
|
||||
|
||||
void maps__insert(struct maps *maps, struct map *map);
|
||||
void maps__remove(struct maps *maps, struct map *map);
|
||||
struct map *maps__find(struct maps *maps, u64 addr);
|
||||
struct map *maps__first(struct maps *maps);
|
||||
struct map *map__next(struct map *map);
|
||||
struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name,
|
||||
struct map **mapp);
|
||||
void map_groups__init(struct map_groups *mg, struct machine *machine);
|
||||
void map_groups__exit(struct map_groups *mg);
|
||||
int map_groups__clone(struct thread *thread,
|
||||
struct map_groups *parent);
|
||||
size_t map_groups__fprintf(struct map_groups *mg, FILE *fp);
|
||||
|
||||
int map__set_kallsyms_ref_reloc_sym(struct map *map, const char *symbol_name,
|
||||
u64 addr);
|
||||
|
||||
static inline void map_groups__insert(struct map_groups *mg, struct map *map)
|
||||
{
|
||||
maps__insert(&mg->maps, map);
|
||||
map->groups = mg;
|
||||
}
|
||||
|
||||
static inline void map_groups__remove(struct map_groups *mg, struct map *map)
|
||||
{
|
||||
maps__remove(&mg->maps, map);
|
||||
}
|
||||
|
||||
static inline struct map *map_groups__find(struct map_groups *mg, u64 addr)
|
||||
{
|
||||
return maps__find(&mg->maps, addr);
|
||||
}
|
||||
|
||||
struct map *map_groups__first(struct map_groups *mg);
|
||||
|
||||
static inline struct map *map_groups__next(struct map *map)
|
||||
{
|
||||
return map__next(map);
|
||||
}
|
||||
|
||||
struct symbol *map_groups__find_symbol(struct map_groups *mg,
|
||||
u64 addr, struct map **mapp);
|
||||
|
||||
struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
|
||||
const char *name,
|
||||
struct map **mapp);
|
||||
|
||||
struct addr_map_symbol;
|
||||
|
||||
int map_groups__find_ams(struct addr_map_symbol *ams);
|
||||
|
||||
int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
|
||||
FILE *fp);
|
||||
|
||||
struct map *map_groups__find_by_name(struct map_groups *mg, const char *name);
|
||||
|
||||
bool __map__is_kernel(const struct map *map);
|
||||
bool __map__is_extra_kernel_map(const struct map *map);
|
||||
|
||||
|
91
tools/perf/util/map_groups.h
Normal file
91
tools/perf/util/map_groups.h
Normal file
@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __PERF_MAP_GROUPS_H
|
||||
#define __PERF_MAP_GROUPS_H
|
||||
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/types.h>
|
||||
#include "rwsem.h"
|
||||
|
||||
struct ref_reloc_sym;
|
||||
struct machine;
|
||||
struct map;
|
||||
struct thread;
|
||||
|
||||
struct maps {
|
||||
struct rb_root entries;
|
||||
struct rb_root names;
|
||||
struct rw_semaphore lock;
|
||||
};
|
||||
|
||||
void maps__insert(struct maps *maps, struct map *map);
|
||||
void maps__remove(struct maps *maps, struct map *map);
|
||||
struct map *maps__find(struct maps *maps, u64 addr);
|
||||
struct map *maps__first(struct maps *maps);
|
||||
struct map *map__next(struct map *map);
|
||||
struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp);
|
||||
|
||||
struct map_groups {
|
||||
struct maps maps;
|
||||
struct machine *machine;
|
||||
refcount_t refcnt;
|
||||
};
|
||||
|
||||
#define KMAP_NAME_LEN 256
|
||||
|
||||
struct kmap {
|
||||
struct ref_reloc_sym *ref_reloc_sym;
|
||||
struct map_groups *kmaps;
|
||||
char name[KMAP_NAME_LEN];
|
||||
};
|
||||
|
||||
struct map_groups *map_groups__new(struct machine *machine);
|
||||
void map_groups__delete(struct map_groups *mg);
|
||||
bool map_groups__empty(struct map_groups *mg);
|
||||
|
||||
static inline struct map_groups *map_groups__get(struct map_groups *mg)
|
||||
{
|
||||
if (mg)
|
||||
refcount_inc(&mg->refcnt);
|
||||
return mg;
|
||||
}
|
||||
|
||||
void map_groups__put(struct map_groups *mg);
|
||||
void map_groups__init(struct map_groups *mg, struct machine *machine);
|
||||
void map_groups__exit(struct map_groups *mg);
|
||||
int map_groups__clone(struct thread *thread, struct map_groups *parent);
|
||||
size_t map_groups__fprintf(struct map_groups *mg, FILE *fp);
|
||||
|
||||
void map_groups__insert(struct map_groups *mg, struct map *map);
|
||||
|
||||
static inline void map_groups__remove(struct map_groups *mg, struct map *map)
|
||||
{
|
||||
maps__remove(&mg->maps, map);
|
||||
}
|
||||
|
||||
static inline struct map *map_groups__find(struct map_groups *mg, u64 addr)
|
||||
{
|
||||
return maps__find(&mg->maps, addr);
|
||||
}
|
||||
|
||||
struct map *map_groups__first(struct map_groups *mg);
|
||||
|
||||
static inline struct map *map_groups__next(struct map *map)
|
||||
{
|
||||
return map__next(map);
|
||||
}
|
||||
|
||||
struct symbol *map_groups__find_symbol(struct map_groups *mg, u64 addr, struct map **mapp);
|
||||
struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, const char *name, struct map **mapp);
|
||||
|
||||
struct addr_map_symbol;
|
||||
|
||||
int map_groups__find_ams(struct addr_map_symbol *ams);
|
||||
|
||||
int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, FILE *fp);
|
||||
|
||||
struct map *map_groups__find_by_name(struct map_groups *mg, const char *name);
|
||||
|
||||
#endif // __PERF_MAP_GROUPS_H
|
22
tools/perf/util/map_symbol.h
Normal file
22
tools/perf/util/map_symbol.h
Normal file
@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifndef __PERF_MAP_SYMBOL
|
||||
#define __PERF_MAP_SYMBOL 1
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct map;
|
||||
struct symbol;
|
||||
|
||||
struct map_symbol {
|
||||
struct map *map;
|
||||
struct symbol *sym;
|
||||
};
|
||||
|
||||
struct addr_map_symbol {
|
||||
struct map *map;
|
||||
struct symbol *sym;
|
||||
u64 addr;
|
||||
u64 al_addr;
|
||||
u64 phys_addr;
|
||||
};
|
||||
#endif // __PERF_MAP_SYMBOL
|
@ -10,6 +10,9 @@
|
||||
#include <sys/mman.h>
|
||||
#include <inttypes.h>
|
||||
#include <asm/bug.h>
|
||||
#ifdef HAVE_LIBNUMA_SUPPORT
|
||||
#include <numaif.h>
|
||||
#endif
|
||||
#include "debug.h"
|
||||
#include "event.h"
|
||||
#include "mmap.h"
|
||||
@ -154,9 +157,72 @@ void __weak auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp __mayb
|
||||
}
|
||||
|
||||
#ifdef HAVE_AIO_SUPPORT
|
||||
|
||||
#ifdef HAVE_LIBNUMA_SUPPORT
|
||||
static int perf_mmap__aio_alloc(struct perf_mmap *map, int idx)
|
||||
{
|
||||
map->aio.data[idx] = mmap(NULL, perf_mmap__mmap_len(map), PROT_READ|PROT_WRITE,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
|
||||
if (map->aio.data[idx] == MAP_FAILED) {
|
||||
map->aio.data[idx] = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void perf_mmap__aio_free(struct perf_mmap *map, int idx)
|
||||
{
|
||||
if (map->aio.data[idx]) {
|
||||
munmap(map->aio.data[idx], perf_mmap__mmap_len(map));
|
||||
map->aio.data[idx] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int perf_mmap__aio_bind(struct perf_mmap *map, int idx, int cpu, int affinity)
|
||||
{
|
||||
void *data;
|
||||
size_t mmap_len;
|
||||
unsigned long node_mask;
|
||||
|
||||
if (affinity != PERF_AFFINITY_SYS && cpu__max_node() > 1) {
|
||||
data = map->aio.data[idx];
|
||||
mmap_len = perf_mmap__mmap_len(map);
|
||||
node_mask = 1UL << cpu__get_node(cpu);
|
||||
if (mbind(data, mmap_len, MPOL_BIND, &node_mask, 1, 0)) {
|
||||
pr_err("Failed to bind [%p-%p] AIO buffer to node %d: error %m\n",
|
||||
data, data + mmap_len, cpu__get_node(cpu));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int perf_mmap__aio_alloc(struct perf_mmap *map, int idx)
|
||||
{
|
||||
map->aio.data[idx] = malloc(perf_mmap__mmap_len(map));
|
||||
if (map->aio.data[idx] == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void perf_mmap__aio_free(struct perf_mmap *map, int idx)
|
||||
{
|
||||
zfree(&(map->aio.data[idx]));
|
||||
}
|
||||
|
||||
static int perf_mmap__aio_bind(struct perf_mmap *map __maybe_unused, int idx __maybe_unused,
|
||||
int cpu __maybe_unused, int affinity __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int perf_mmap__aio_mmap(struct perf_mmap *map, struct mmap_params *mp)
|
||||
{
|
||||
int delta_max, i, prio;
|
||||
int delta_max, i, prio, ret;
|
||||
|
||||
map->aio.nr_cblocks = mp->nr_cblocks;
|
||||
if (map->aio.nr_cblocks) {
|
||||
@ -177,11 +243,14 @@ static int perf_mmap__aio_mmap(struct perf_mmap *map, struct mmap_params *mp)
|
||||
}
|
||||
delta_max = sysconf(_SC_AIO_PRIO_DELTA_MAX);
|
||||
for (i = 0; i < map->aio.nr_cblocks; ++i) {
|
||||
map->aio.data[i] = malloc(perf_mmap__mmap_len(map));
|
||||
if (!map->aio.data[i]) {
|
||||
ret = perf_mmap__aio_alloc(map, i);
|
||||
if (ret == -1) {
|
||||
pr_debug2("failed to allocate data buffer area, error %m");
|
||||
return -1;
|
||||
}
|
||||
ret = perf_mmap__aio_bind(map, i, map->cpu, mp->affinity);
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
/*
|
||||
* Use cblock.aio_fildes value different from -1
|
||||
* to denote started aio write operation on the
|
||||
@ -210,7 +279,7 @@ static void perf_mmap__aio_munmap(struct perf_mmap *map)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < map->aio.nr_cblocks; ++i)
|
||||
zfree(&map->aio.data[i]);
|
||||
perf_mmap__aio_free(map, i);
|
||||
if (map->aio.data)
|
||||
zfree(&map->aio.data);
|
||||
zfree(&map->aio.cblocks);
|
||||
@ -314,6 +383,32 @@ void perf_mmap__munmap(struct perf_mmap *map)
|
||||
auxtrace_mmap__munmap(&map->auxtrace_mmap);
|
||||
}
|
||||
|
||||
static void build_node_mask(int node, cpu_set_t *mask)
|
||||
{
|
||||
int c, cpu, nr_cpus;
|
||||
const struct cpu_map *cpu_map = NULL;
|
||||
|
||||
cpu_map = cpu_map__online();
|
||||
if (!cpu_map)
|
||||
return;
|
||||
|
||||
nr_cpus = cpu_map__nr(cpu_map);
|
||||
for (c = 0; c < nr_cpus; c++) {
|
||||
cpu = cpu_map->map[c]; /* map c index to online cpu index */
|
||||
if (cpu__get_node(cpu) == node)
|
||||
CPU_SET(cpu, mask);
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_mmap__setup_affinity_mask(struct perf_mmap *map, struct mmap_params *mp)
|
||||
{
|
||||
CPU_ZERO(&map->affinity_mask);
|
||||
if (mp->affinity == PERF_AFFINITY_NODE && cpu__max_node() > 1)
|
||||
build_node_mask(cpu__get_node(map->cpu), &map->affinity_mask);
|
||||
else if (mp->affinity == PERF_AFFINITY_CPU)
|
||||
CPU_SET(map->cpu, &map->affinity_mask);
|
||||
}
|
||||
|
||||
int perf_mmap__mmap(struct perf_mmap *map, struct mmap_params *mp, int fd, int cpu)
|
||||
{
|
||||
/*
|
||||
@ -343,6 +438,8 @@ int perf_mmap__mmap(struct perf_mmap *map, struct mmap_params *mp, int fd, int c
|
||||
map->fd = fd;
|
||||
map->cpu = cpu;
|
||||
|
||||
perf_mmap__setup_affinity_mask(map, mp);
|
||||
|
||||
if (auxtrace_mmap__mmap(&map->auxtrace_mmap,
|
||||
&mp->auxtrace_mp, map->base, fd))
|
||||
return -1;
|
||||
|
@ -38,6 +38,7 @@ struct perf_mmap {
|
||||
int nr_cblocks;
|
||||
} aio;
|
||||
#endif
|
||||
cpu_set_t affinity_mask;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -69,7 +70,7 @@ enum bkw_mmap_state {
|
||||
};
|
||||
|
||||
struct mmap_params {
|
||||
int prot, mask, nr_cblocks;
|
||||
int prot, mask, nr_cblocks, affinity;
|
||||
struct auxtrace_mmap_params auxtrace_mp;
|
||||
};
|
||||
|
||||
|
@ -29,8 +29,6 @@ struct perf_pmu_format {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
|
||||
|
||||
int perf_pmu_parse(struct list_head *list, char *name);
|
||||
extern FILE *perf_pmu_in;
|
||||
|
||||
|
@ -6,9 +6,10 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <stdbool.h>
|
||||
#include "evsel.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
struct perf_evsel_config_term;
|
||||
|
||||
enum {
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG1,
|
||||
@ -16,6 +17,7 @@ enum {
|
||||
};
|
||||
|
||||
#define PERF_PMU_FORMAT_BITS 64
|
||||
#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
|
||||
|
||||
struct perf_event_attr;
|
||||
|
||||
@ -29,7 +31,6 @@ struct perf_pmu {
|
||||
struct list_head format; /* HEAD struct perf_pmu_format -> list */
|
||||
struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
|
||||
struct list_head list; /* ELEM */
|
||||
int (*set_drv_config) (struct perf_evsel_config_term *term);
|
||||
};
|
||||
|
||||
struct perf_pmu_info {
|
||||
|
@ -41,6 +41,8 @@
|
||||
#include "debug.h"
|
||||
#include "cache.h"
|
||||
#include "color.h"
|
||||
#include "map.h"
|
||||
#include "map_groups.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include <api/fs/fs.h>
|
||||
|
@ -819,7 +819,7 @@ static int s390_cpumsf_process_queues(struct s390_cpumsf *sf, u64 timestamp)
|
||||
}
|
||||
|
||||
static int s390_cpumsf_synth_error(struct s390_cpumsf *sf, int code, int cpu,
|
||||
pid_t pid, pid_t tid, u64 ip)
|
||||
pid_t pid, pid_t tid, u64 ip, u64 timestamp)
|
||||
{
|
||||
char msg[MAX_AUXTRACE_ERROR_MSG];
|
||||
union perf_event event;
|
||||
@ -827,7 +827,7 @@ static int s390_cpumsf_synth_error(struct s390_cpumsf *sf, int code, int cpu,
|
||||
|
||||
strncpy(msg, "Lost Auxiliary Trace Buffer", sizeof(msg) - 1);
|
||||
auxtrace_synth_error(&event.auxtrace_error, PERF_AUXTRACE_ERROR_ITRACE,
|
||||
code, cpu, pid, tid, ip, msg);
|
||||
code, cpu, pid, tid, ip, msg, timestamp);
|
||||
|
||||
err = perf_session__deliver_synth_event(sf->session, &event, NULL);
|
||||
if (err)
|
||||
@ -839,7 +839,8 @@ static int s390_cpumsf_synth_error(struct s390_cpumsf *sf, int code, int cpu,
|
||||
static int s390_cpumsf_lost(struct s390_cpumsf *sf, struct perf_sample *sample)
|
||||
{
|
||||
return s390_cpumsf_synth_error(sf, 1, sample->cpu,
|
||||
sample->pid, sample->tid, 0);
|
||||
sample->pid, sample->tid, 0,
|
||||
sample->time);
|
||||
}
|
||||
|
||||
static int
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user