Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "The main kernel side changes were: - Modernize the kprobe and uprobe creation/destruction tooling ABIs: The existing text based APIs (kprobe_events and uprobe_events in tracefs), are naive, limited ABIs in that they require user-space to clean up after themselves, which is both difficult and fragile if the tool is buggy or exits unexpectedly. In other words they are not really suited for modern, robust tooling. So introduce a modern, file descriptor based ABI that does not have these limitations: introduce the 'perf_kprobe' and 'perf_uprobe' PMUs and extend the perf_event_open() syscall to create events with a kprobe/uprobe attached to them. These [k,u]probe are associated with this file descriptor, so they are not available in tracefs. (Song Liu) - Intel Cannon Lake CPU support (Harry Pan) - Intel PT cleanups (Alexander Shishkin) - Improve the performance of pinned/flexible event groups by using RB trees (Alexey Budankov) - Add PERF_EVENT_IOC_MODIFY_ATTRIBUTES which allows the modification of hardware breakpoints, which new ABI variant massively speeds up existing tooling that uses hardware breakpoints to instrument (and debug) memory usage. (Milind Chabbi, Jiri Olsa) - Various Intel PEBS handling fixes and improvements, and other Intel PMU improvements (Kan Liang) - Various perf core improvements and optimizations (Peter Zijlstra) - ... misc cleanups, fixes and updates. There's over 200 tooling commits, here's an (imperfect) list of highlights: - 'perf annotate' improvements: * Recognize and handle jumps to other functions as calls, which improves the navigation along jumps and back. (Arnaldo Carvalho de Melo) * Add the 'P' hotkey in TUI annotation to dump annotation output into a file, to ease e-mail reporting of annotation details. (Arnaldo Carvalho de Melo) * Add an IPC/cycles column to the TUI (Jin Yao) * Improve s390 assembly annotation (Thomas Richter) * Refactor the output formatting logic to better separate it into interactive and non-interactive features and add the --stdio2 output variant to demonstrate this. (Arnaldo Carvalho de Melo) - 'perf script' improvements: * Add Python 3 support (Jaroslav Škarvada) * Add --show-round-event (Jiri Olsa) - 'perf c2c' improvements: * Add NUMA analysis support (Jiri Olsa) - 'perf trace' improvements: * Improve PowerPC support (Ravi Bangoria) - 'perf inject' improvements: * Integrate ARM CoreSight traces (Robert Walker) - 'perf stat' improvements: * Add the --interval-count option (yuzhoujian) * Add the --timeout option (yuzhoujian) - 'perf sched' improvements (Changbin Du) - Vendor events improvements : * Add IBM s390 vendor events (Thomas Richter) * Add and improve arm64 vendor events (John Garry, Ganapatrao Kulkarni) * Update POWER9 vendor events (Sukadev Bhattiprolu) - Intel PT tooling improvements (Adrian Hunter) - PMU handling improvements (Agustin Vega-Frias) - Record machine topology in perf.data (Jiri Olsa) - Various overwrite related cleanups (Kan Liang) - Add arm64 dwarf post unwind support (Kim Phillips, Jean Pihet) - ... and lots of other changes, cleanups and fixes, see the shortlog and Git history for details" * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (262 commits) perf/x86/intel: Enable C-state residency events for Cannon Lake perf/x86/intel: Add Cannon Lake support for RAPL profiling perf/x86/pt, coresight: Clean up address filter structure perf vendor events s390: Add JSON files for IBM z14 perf vendor events s390: Add JSON files for IBM z13 perf vendor events s390: Add JSON files for IBM zEC12 zBC12 perf vendor events s390: Add JSON files for IBM z196 perf vendor events s390: Add JSON files for IBM z10EC z10BC perf mmap: Be consistent when checking for an unmaped ring buffer perf mmap: Fix accessing unmapped mmap in perf_mmap__read_done() perf build: Fix check-headers.sh opts assignment perf/x86: Update rdpmc_always_available static key to the modern API perf annotate: Use absolute addresses to calculate jump target offsets perf annotate: Defer searching for comma in raw line till it is needed perf annotate: Support jumping from one function to another perf annotate: Add "_local" to jump/offset validation routines perf python: Reference Py_None before returning it perf annotate: Mark jumps to outher functions with the call arrow perf annotate: Pass function descriptor to its instruction parsing routines perf annotate: No need to calculate notes->start twice ...
This commit is contained in:
commit
486adcea4a
|
@ -330,3 +330,54 @@ Details on how to use the generic STM API can be found here [2].
|
||||||
|
|
||||||
[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm
|
[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm
|
||||||
[2]. Documentation/trace/stm.txt
|
[2]. Documentation/trace/stm.txt
|
||||||
|
|
||||||
|
|
||||||
|
Using perf tools
|
||||||
|
----------------
|
||||||
|
|
||||||
|
perf can be used to record and analyze trace of programs.
|
||||||
|
|
||||||
|
Execution can be recorded using 'perf record' with the cs_etm event,
|
||||||
|
specifying the name of the sink to record to, e.g:
|
||||||
|
|
||||||
|
perf record -e cs_etm/@20070000.etr/u --per-thread
|
||||||
|
|
||||||
|
The 'perf report' and 'perf script' commands can be used to analyze execution,
|
||||||
|
synthesizing instruction and branch events from the instruction trace.
|
||||||
|
'perf inject' can be used to replace the trace data with the synthesized events.
|
||||||
|
The --itrace option controls the type and frequency of synthesized events
|
||||||
|
(see perf documentation).
|
||||||
|
|
||||||
|
Note that only 64-bit programs are currently supported - further work is
|
||||||
|
required to support instruction decode of 32-bit Arm programs.
|
||||||
|
|
||||||
|
|
||||||
|
Generating coverage files for Feedback Directed Optimization: AutoFDO
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
'perf inject' accepts the --itrace option in which case tracing data is
|
||||||
|
removed and replaced with the synthesized events. e.g.
|
||||||
|
|
||||||
|
perf inject --itrace --strip -i perf.data -o perf.data.new
|
||||||
|
|
||||||
|
Below is an example of using ARM ETM for autoFDO. It requires autofdo
|
||||||
|
(https://github.com/google/autofdo) and gcc version 5. The bubble
|
||||||
|
sort example is from the AutoFDO tutorial (https://gcc.gnu.org/wiki/AutoFDO/Tutorial).
|
||||||
|
|
||||||
|
$ gcc-5 -O3 sort.c -o sort
|
||||||
|
$ taskset -c 2 ./sort
|
||||||
|
Bubble sorting array of 30000 elements
|
||||||
|
5910 ms
|
||||||
|
|
||||||
|
$ perf record -e cs_etm/@20070000.etr/u --per-thread taskset -c 2 ./sort
|
||||||
|
Bubble sorting array of 30000 elements
|
||||||
|
12543 ms
|
||||||
|
[ perf record: Woken up 35 times to write data ]
|
||||||
|
[ perf record: Captured and wrote 69.640 MB perf.data ]
|
||||||
|
|
||||||
|
$ perf inject -i perf.data -o inj.data --itrace=il64 --strip
|
||||||
|
$ create_gcov --binary=./sort --profile=inj.data --gcov=sort.gcov -gcov_version=1
|
||||||
|
$ gcc-5 -O3 -fauto-profile=sort.gcov sort.c -o sort_autofdo
|
||||||
|
$ taskset -c 2 ./sort_autofdo
|
||||||
|
Bubble sorting array of 30000 elements
|
||||||
|
5806 ms
|
||||||
|
|
|
@ -351,7 +351,7 @@ static int collect_events(struct perf_event *group, int max_count,
|
||||||
evtype[n] = group->hw.event_base;
|
evtype[n] = group->hw.event_base;
|
||||||
current_idx[n++] = PMC_NO_INDEX;
|
current_idx[n++] = PMC_NO_INDEX;
|
||||||
}
|
}
|
||||||
list_for_each_entry(pe, &group->sibling_list, group_entry) {
|
for_each_sibling_event(pe, group) {
|
||||||
if (!is_software_event(pe) && pe->state != PERF_EVENT_STATE_OFF) {
|
if (!is_software_event(pe) && pe->state != PERF_EVENT_STATE_OFF) {
|
||||||
if (n >= max_count)
|
if (n >= max_count)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -269,7 +269,7 @@ static bool mmdc_pmu_group_is_valid(struct perf_event *event)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(sibling, leader) {
|
||||||
if (!mmdc_pmu_group_event_is_valid(sibling, pmu, &counter_mask))
|
if (!mmdc_pmu_group_event_is_valid(sibling, pmu, &counter_mask))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,7 +293,7 @@ static bool l2x0_pmu_group_is_valid(struct perf_event *event)
|
||||||
else if (!is_software_event(leader))
|
else if (!is_software_event(leader))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(sibling, leader) {
|
||||||
if (sibling->pmu == pmu)
|
if (sibling->pmu == pmu)
|
||||||
num_hw++;
|
num_hw++;
|
||||||
else if (!is_software_event(sibling))
|
else if (!is_software_event(sibling))
|
||||||
|
|
|
@ -711,7 +711,7 @@ static int validate_group(struct perf_event *event)
|
||||||
if (mipsxx_pmu_alloc_counter(&fake_cpuc, &leader->hw) < 0)
|
if (mipsxx_pmu_alloc_counter(&fake_cpuc, &leader->hw) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(sibling, leader) {
|
||||||
if (mipsxx_pmu_alloc_counter(&fake_cpuc, &sibling->hw) < 0)
|
if (mipsxx_pmu_alloc_counter(&fake_cpuc, &sibling->hw) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1426,7 +1426,7 @@ static int collect_events(struct perf_event *group, int max_count,
|
||||||
flags[n] = group->hw.event_base;
|
flags[n] = group->hw.event_base;
|
||||||
events[n++] = group->hw.config;
|
events[n++] = group->hw.config;
|
||||||
}
|
}
|
||||||
list_for_each_entry(event, &group->sibling_list, group_entry) {
|
for_each_sibling_event(event, group) {
|
||||||
if (event->pmu->task_ctx_nr == perf_hw_context &&
|
if (event->pmu->task_ctx_nr == perf_hw_context &&
|
||||||
event->state != PERF_EVENT_STATE_OFF) {
|
event->state != PERF_EVENT_STATE_OFF) {
|
||||||
if (n >= max_count)
|
if (n >= max_count)
|
||||||
|
|
|
@ -277,7 +277,7 @@ static int collect_events(struct perf_event *group, int max_count,
|
||||||
ctrs[n] = group;
|
ctrs[n] = group;
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
list_for_each_entry(event, &group->sibling_list, group_entry) {
|
for_each_sibling_event(event, group) {
|
||||||
if (!is_software_event(event) &&
|
if (!is_software_event(event) &&
|
||||||
event->state != PERF_EVENT_STATE_OFF) {
|
event->state != PERF_EVENT_STATE_OFF) {
|
||||||
if (n >= max_count)
|
if (n >= max_count)
|
||||||
|
|
|
@ -1342,7 +1342,7 @@ static int collect_events(struct perf_event *group, int max_count,
|
||||||
events[n] = group->hw.event_base;
|
events[n] = group->hw.event_base;
|
||||||
current_idx[n++] = PIC_NO_INDEX;
|
current_idx[n++] = PIC_NO_INDEX;
|
||||||
}
|
}
|
||||||
list_for_each_entry(event, &group->sibling_list, group_entry) {
|
for_each_sibling_event(event, group) {
|
||||||
if (!is_software_event(event) &&
|
if (!is_software_event(event) &&
|
||||||
event->state != PERF_EVENT_STATE_OFF) {
|
event->state != PERF_EVENT_STATE_OFF) {
|
||||||
if (n >= max_count)
|
if (n >= max_count)
|
||||||
|
|
|
@ -48,7 +48,7 @@ DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
|
||||||
.enabled = 1,
|
.enabled = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct static_key rdpmc_always_available = STATIC_KEY_INIT_FALSE;
|
DEFINE_STATIC_KEY_FALSE(rdpmc_always_available_key);
|
||||||
|
|
||||||
u64 __read_mostly hw_cache_event_ids
|
u64 __read_mostly hw_cache_event_ids
|
||||||
[PERF_COUNT_HW_CACHE_MAX]
|
[PERF_COUNT_HW_CACHE_MAX]
|
||||||
|
@ -990,7 +990,7 @@ static int collect_events(struct cpu_hw_events *cpuc, struct perf_event *leader,
|
||||||
if (!dogrp)
|
if (!dogrp)
|
||||||
return n;
|
return n;
|
||||||
|
|
||||||
list_for_each_entry(event, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(event, leader) {
|
||||||
if (!is_x86_event(event) ||
|
if (!is_x86_event(event) ||
|
||||||
event->state <= PERF_EVENT_STATE_OFF)
|
event->state <= PERF_EVENT_STATE_OFF)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1156,16 +1156,13 @@ int x86_perf_event_set_period(struct perf_event *event)
|
||||||
|
|
||||||
per_cpu(pmc_prev_left[idx], smp_processor_id()) = left;
|
per_cpu(pmc_prev_left[idx], smp_processor_id()) = left;
|
||||||
|
|
||||||
if (!(hwc->flags & PERF_X86_EVENT_AUTO_RELOAD) ||
|
/*
|
||||||
local64_read(&hwc->prev_count) != (u64)-left) {
|
* The hw event starts counting from this event offset,
|
||||||
/*
|
* mark it to be able to extra future deltas:
|
||||||
* The hw event starts counting from this event offset,
|
*/
|
||||||
* mark it to be able to extra future deltas:
|
local64_set(&hwc->prev_count, (u64)-left);
|
||||||
*/
|
|
||||||
local64_set(&hwc->prev_count, (u64)-left);
|
|
||||||
|
|
||||||
wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
|
wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Due to erratum on certan cpu we need
|
* Due to erratum on certan cpu we need
|
||||||
|
@ -1884,6 +1881,8 @@ early_initcall(init_hw_perf_events);
|
||||||
|
|
||||||
static inline void x86_pmu_read(struct perf_event *event)
|
static inline void x86_pmu_read(struct perf_event *event)
|
||||||
{
|
{
|
||||||
|
if (x86_pmu.read)
|
||||||
|
return x86_pmu.read(event);
|
||||||
x86_perf_event_update(event);
|
x86_perf_event_update(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2207,9 +2206,9 @@ static ssize_t set_attr_rdpmc(struct device *cdev,
|
||||||
* but only root can trigger it, so it's okay.
|
* but only root can trigger it, so it's okay.
|
||||||
*/
|
*/
|
||||||
if (val == 2)
|
if (val == 2)
|
||||||
static_key_slow_inc(&rdpmc_always_available);
|
static_branch_inc(&rdpmc_always_available_key);
|
||||||
else
|
else
|
||||||
static_key_slow_dec(&rdpmc_always_available);
|
static_branch_dec(&rdpmc_always_available_key);
|
||||||
on_each_cpu(refresh_pce, NULL, 1);
|
on_each_cpu(refresh_pce, NULL, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2060,6 +2060,14 @@ static void intel_pmu_del_event(struct perf_event *event)
|
||||||
intel_pmu_pebs_del(event);
|
intel_pmu_pebs_del(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void intel_pmu_read_event(struct perf_event *event)
|
||||||
|
{
|
||||||
|
if (event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD)
|
||||||
|
intel_pmu_auto_reload_read(event);
|
||||||
|
else
|
||||||
|
x86_perf_event_update(event);
|
||||||
|
}
|
||||||
|
|
||||||
static void intel_pmu_enable_fixed(struct hw_perf_event *hwc)
|
static void intel_pmu_enable_fixed(struct hw_perf_event *hwc)
|
||||||
{
|
{
|
||||||
int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
|
int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
|
||||||
|
@ -2201,9 +2209,15 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
|
||||||
int bit, loops;
|
int bit, loops;
|
||||||
u64 status;
|
u64 status;
|
||||||
int handled;
|
int handled;
|
||||||
|
int pmu_enabled;
|
||||||
|
|
||||||
cpuc = this_cpu_ptr(&cpu_hw_events);
|
cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the PMU state.
|
||||||
|
* It needs to be restored when leaving the handler.
|
||||||
|
*/
|
||||||
|
pmu_enabled = cpuc->enabled;
|
||||||
/*
|
/*
|
||||||
* No known reason to not always do late ACK,
|
* No known reason to not always do late ACK,
|
||||||
* but just in case do it opt-in.
|
* but just in case do it opt-in.
|
||||||
|
@ -2211,6 +2225,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
|
||||||
if (!x86_pmu.late_ack)
|
if (!x86_pmu.late_ack)
|
||||||
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
||||||
intel_bts_disable_local();
|
intel_bts_disable_local();
|
||||||
|
cpuc->enabled = 0;
|
||||||
__intel_pmu_disable_all();
|
__intel_pmu_disable_all();
|
||||||
handled = intel_pmu_drain_bts_buffer();
|
handled = intel_pmu_drain_bts_buffer();
|
||||||
handled += intel_bts_interrupt();
|
handled += intel_bts_interrupt();
|
||||||
|
@ -2320,7 +2335,8 @@ again:
|
||||||
|
|
||||||
done:
|
done:
|
||||||
/* Only restore PMU state when it's active. See x86_pmu_disable(). */
|
/* Only restore PMU state when it's active. See x86_pmu_disable(). */
|
||||||
if (cpuc->enabled)
|
cpuc->enabled = pmu_enabled;
|
||||||
|
if (pmu_enabled)
|
||||||
__intel_pmu_enable_all(0, true);
|
__intel_pmu_enable_all(0, true);
|
||||||
intel_bts_enable_local();
|
intel_bts_enable_local();
|
||||||
|
|
||||||
|
@ -3188,7 +3204,7 @@ glp_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
|
||||||
* Therefore the effective (average) period matches the requested period,
|
* Therefore the effective (average) period matches the requested period,
|
||||||
* despite coarser hardware granularity.
|
* despite coarser hardware granularity.
|
||||||
*/
|
*/
|
||||||
static unsigned bdw_limit_period(struct perf_event *event, unsigned left)
|
static u64 bdw_limit_period(struct perf_event *event, u64 left)
|
||||||
{
|
{
|
||||||
if ((event->hw.config & INTEL_ARCH_EVENT_MASK) ==
|
if ((event->hw.config & INTEL_ARCH_EVENT_MASK) ==
|
||||||
X86_CONFIG(.event=0xc0, .umask=0x01)) {
|
X86_CONFIG(.event=0xc0, .umask=0x01)) {
|
||||||
|
@ -3495,6 +3511,7 @@ static __initconst const struct x86_pmu intel_pmu = {
|
||||||
.disable = intel_pmu_disable_event,
|
.disable = intel_pmu_disable_event,
|
||||||
.add = intel_pmu_add_event,
|
.add = intel_pmu_add_event,
|
||||||
.del = intel_pmu_del_event,
|
.del = intel_pmu_del_event,
|
||||||
|
.read = intel_pmu_read_event,
|
||||||
.hw_config = intel_pmu_hw_config,
|
.hw_config = intel_pmu_hw_config,
|
||||||
.schedule_events = x86_schedule_events,
|
.schedule_events = x86_schedule_events,
|
||||||
.eventsel = MSR_ARCH_PERFMON_EVENTSEL0,
|
.eventsel = MSR_ARCH_PERFMON_EVENTSEL0,
|
||||||
|
|
|
@ -40,50 +40,51 @@
|
||||||
* Model specific counters:
|
* Model specific counters:
|
||||||
* MSR_CORE_C1_RES: CORE C1 Residency Counter
|
* MSR_CORE_C1_RES: CORE C1 Residency Counter
|
||||||
* perf code: 0x00
|
* perf code: 0x00
|
||||||
* Available model: SLM,AMT,GLM
|
* Available model: SLM,AMT,GLM,CNL
|
||||||
* Scope: Core (each processor core has a MSR)
|
* Scope: Core (each processor core has a MSR)
|
||||||
* MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter
|
* MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter
|
||||||
* perf code: 0x01
|
* perf code: 0x01
|
||||||
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,GLM
|
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,GLM,
|
||||||
|
CNL
|
||||||
* Scope: Core
|
* Scope: Core
|
||||||
* MSR_CORE_C6_RESIDENCY: CORE C6 Residency Counter
|
* MSR_CORE_C6_RESIDENCY: CORE C6 Residency Counter
|
||||||
* perf code: 0x02
|
* perf code: 0x02
|
||||||
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW
|
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW,
|
||||||
* SKL,KNL,GLM
|
* SKL,KNL,GLM,CNL
|
||||||
* Scope: Core
|
* Scope: Core
|
||||||
* MSR_CORE_C7_RESIDENCY: CORE C7 Residency Counter
|
* MSR_CORE_C7_RESIDENCY: CORE C7 Residency Counter
|
||||||
* perf code: 0x03
|
* perf code: 0x03
|
||||||
* Available model: SNB,IVB,HSW,BDW,SKL
|
* Available model: SNB,IVB,HSW,BDW,SKL,CNL
|
||||||
* Scope: Core
|
* Scope: Core
|
||||||
* MSR_PKG_C2_RESIDENCY: Package C2 Residency Counter.
|
* MSR_PKG_C2_RESIDENCY: Package C2 Residency Counter.
|
||||||
* perf code: 0x00
|
* perf code: 0x00
|
||||||
* Available model: SNB,IVB,HSW,BDW,SKL,KNL,GLM
|
* Available model: SNB,IVB,HSW,BDW,SKL,KNL,GLM,CNL
|
||||||
* Scope: Package (physical package)
|
* Scope: Package (physical package)
|
||||||
* MSR_PKG_C3_RESIDENCY: Package C3 Residency Counter.
|
* MSR_PKG_C3_RESIDENCY: Package C3 Residency Counter.
|
||||||
* perf code: 0x01
|
* perf code: 0x01
|
||||||
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,KNL
|
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,KNL,
|
||||||
* GLM
|
* GLM,CNL
|
||||||
* Scope: Package (physical package)
|
* Scope: Package (physical package)
|
||||||
* MSR_PKG_C6_RESIDENCY: Package C6 Residency Counter.
|
* MSR_PKG_C6_RESIDENCY: Package C6 Residency Counter.
|
||||||
* perf code: 0x02
|
* perf code: 0x02
|
||||||
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW
|
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW
|
||||||
* SKL,KNL,GLM
|
* SKL,KNL,GLM,CNL
|
||||||
* Scope: Package (physical package)
|
* Scope: Package (physical package)
|
||||||
* MSR_PKG_C7_RESIDENCY: Package C7 Residency Counter.
|
* MSR_PKG_C7_RESIDENCY: Package C7 Residency Counter.
|
||||||
* perf code: 0x03
|
* perf code: 0x03
|
||||||
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL
|
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,CNL
|
||||||
* Scope: Package (physical package)
|
* Scope: Package (physical package)
|
||||||
* MSR_PKG_C8_RESIDENCY: Package C8 Residency Counter.
|
* MSR_PKG_C8_RESIDENCY: Package C8 Residency Counter.
|
||||||
* perf code: 0x04
|
* perf code: 0x04
|
||||||
* Available model: HSW ULT only
|
* Available model: HSW ULT,CNL
|
||||||
* Scope: Package (physical package)
|
* Scope: Package (physical package)
|
||||||
* MSR_PKG_C9_RESIDENCY: Package C9 Residency Counter.
|
* MSR_PKG_C9_RESIDENCY: Package C9 Residency Counter.
|
||||||
* perf code: 0x05
|
* perf code: 0x05
|
||||||
* Available model: HSW ULT only
|
* Available model: HSW ULT,CNL
|
||||||
* Scope: Package (physical package)
|
* Scope: Package (physical package)
|
||||||
* MSR_PKG_C10_RESIDENCY: Package C10 Residency Counter.
|
* MSR_PKG_C10_RESIDENCY: Package C10 Residency Counter.
|
||||||
* perf code: 0x06
|
* perf code: 0x06
|
||||||
* Available model: HSW ULT, GLM
|
* Available model: HSW ULT,GLM,CNL
|
||||||
* Scope: Package (physical package)
|
* Scope: Package (physical package)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -486,6 +487,21 @@ static const struct cstate_model hswult_cstates __initconst = {
|
||||||
BIT(PERF_CSTATE_PKG_C10_RES),
|
BIT(PERF_CSTATE_PKG_C10_RES),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct cstate_model cnl_cstates __initconst = {
|
||||||
|
.core_events = BIT(PERF_CSTATE_CORE_C1_RES) |
|
||||||
|
BIT(PERF_CSTATE_CORE_C3_RES) |
|
||||||
|
BIT(PERF_CSTATE_CORE_C6_RES) |
|
||||||
|
BIT(PERF_CSTATE_CORE_C7_RES),
|
||||||
|
|
||||||
|
.pkg_events = BIT(PERF_CSTATE_PKG_C2_RES) |
|
||||||
|
BIT(PERF_CSTATE_PKG_C3_RES) |
|
||||||
|
BIT(PERF_CSTATE_PKG_C6_RES) |
|
||||||
|
BIT(PERF_CSTATE_PKG_C7_RES) |
|
||||||
|
BIT(PERF_CSTATE_PKG_C8_RES) |
|
||||||
|
BIT(PERF_CSTATE_PKG_C9_RES) |
|
||||||
|
BIT(PERF_CSTATE_PKG_C10_RES),
|
||||||
|
};
|
||||||
|
|
||||||
static const struct cstate_model slm_cstates __initconst = {
|
static const struct cstate_model slm_cstates __initconst = {
|
||||||
.core_events = BIT(PERF_CSTATE_CORE_C1_RES) |
|
.core_events = BIT(PERF_CSTATE_CORE_C1_RES) |
|
||||||
BIT(PERF_CSTATE_CORE_C6_RES),
|
BIT(PERF_CSTATE_CORE_C6_RES),
|
||||||
|
@ -557,6 +573,8 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = {
|
||||||
X86_CSTATES_MODEL(INTEL_FAM6_KABYLAKE_MOBILE, snb_cstates),
|
X86_CSTATES_MODEL(INTEL_FAM6_KABYLAKE_MOBILE, snb_cstates),
|
||||||
X86_CSTATES_MODEL(INTEL_FAM6_KABYLAKE_DESKTOP, snb_cstates),
|
X86_CSTATES_MODEL(INTEL_FAM6_KABYLAKE_DESKTOP, snb_cstates),
|
||||||
|
|
||||||
|
X86_CSTATES_MODEL(INTEL_FAM6_CANNONLAKE_MOBILE, cnl_cstates),
|
||||||
|
|
||||||
X86_CSTATES_MODEL(INTEL_FAM6_XEON_PHI_KNL, knl_cstates),
|
X86_CSTATES_MODEL(INTEL_FAM6_XEON_PHI_KNL, knl_cstates),
|
||||||
X86_CSTATES_MODEL(INTEL_FAM6_XEON_PHI_KNM, knl_cstates),
|
X86_CSTATES_MODEL(INTEL_FAM6_XEON_PHI_KNM, knl_cstates),
|
||||||
|
|
||||||
|
|
|
@ -1315,17 +1315,93 @@ get_next_pebs_record_by_bit(void *base, void *top, int bit)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void intel_pmu_auto_reload_read(struct perf_event *event)
|
||||||
|
{
|
||||||
|
WARN_ON(!(event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD));
|
||||||
|
|
||||||
|
perf_pmu_disable(event->pmu);
|
||||||
|
intel_pmu_drain_pebs_buffer();
|
||||||
|
perf_pmu_enable(event->pmu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special variant of intel_pmu_save_and_restart() for auto-reload.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
intel_pmu_save_and_restart_reload(struct perf_event *event, int count)
|
||||||
|
{
|
||||||
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
int shift = 64 - x86_pmu.cntval_bits;
|
||||||
|
u64 period = hwc->sample_period;
|
||||||
|
u64 prev_raw_count, new_raw_count;
|
||||||
|
s64 new, old;
|
||||||
|
|
||||||
|
WARN_ON(!period);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* drain_pebs() only happens when the PMU is disabled.
|
||||||
|
*/
|
||||||
|
WARN_ON(this_cpu_read(cpu_hw_events.enabled));
|
||||||
|
|
||||||
|
prev_raw_count = local64_read(&hwc->prev_count);
|
||||||
|
rdpmcl(hwc->event_base_rdpmc, new_raw_count);
|
||||||
|
local64_set(&hwc->prev_count, new_raw_count);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since the counter increments a negative counter value and
|
||||||
|
* overflows on the sign switch, giving the interval:
|
||||||
|
*
|
||||||
|
* [-period, 0]
|
||||||
|
*
|
||||||
|
* the difference between two consequtive reads is:
|
||||||
|
*
|
||||||
|
* A) value2 - value1;
|
||||||
|
* when no overflows have happened in between,
|
||||||
|
*
|
||||||
|
* B) (0 - value1) + (value2 - (-period));
|
||||||
|
* when one overflow happened in between,
|
||||||
|
*
|
||||||
|
* C) (0 - value1) + (n - 1) * (period) + (value2 - (-period));
|
||||||
|
* when @n overflows happened in between.
|
||||||
|
*
|
||||||
|
* Here A) is the obvious difference, B) is the extension to the
|
||||||
|
* discrete interval, where the first term is to the top of the
|
||||||
|
* interval and the second term is from the bottom of the next
|
||||||
|
* interval and C) the extension to multiple intervals, where the
|
||||||
|
* middle term is the whole intervals covered.
|
||||||
|
*
|
||||||
|
* An equivalent of C, by reduction, is:
|
||||||
|
*
|
||||||
|
* value2 - value1 + n * period
|
||||||
|
*/
|
||||||
|
new = ((s64)(new_raw_count << shift) >> shift);
|
||||||
|
old = ((s64)(prev_raw_count << shift) >> shift);
|
||||||
|
local64_add(new - old + count * period, &event->count);
|
||||||
|
|
||||||
|
perf_event_update_userpage(event);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void __intel_pmu_pebs_event(struct perf_event *event,
|
static void __intel_pmu_pebs_event(struct perf_event *event,
|
||||||
struct pt_regs *iregs,
|
struct pt_regs *iregs,
|
||||||
void *base, void *top,
|
void *base, void *top,
|
||||||
int bit, int count)
|
int bit, int count)
|
||||||
{
|
{
|
||||||
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
struct perf_sample_data data;
|
struct perf_sample_data data;
|
||||||
struct pt_regs regs;
|
struct pt_regs regs;
|
||||||
void *at = get_next_pebs_record_by_bit(base, top, bit);
|
void *at = get_next_pebs_record_by_bit(base, top, bit);
|
||||||
|
|
||||||
if (!intel_pmu_save_and_restart(event) &&
|
if (hwc->flags & PERF_X86_EVENT_AUTO_RELOAD) {
|
||||||
!(event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD))
|
/*
|
||||||
|
* Now, auto-reload is only enabled in fixed period mode.
|
||||||
|
* The reload value is always hwc->sample_period.
|
||||||
|
* May need to change it, if auto-reload is enabled in
|
||||||
|
* freq mode later.
|
||||||
|
*/
|
||||||
|
intel_pmu_save_and_restart_reload(event, count);
|
||||||
|
} else if (!intel_pmu_save_and_restart(event))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while (count > 1) {
|
while (count > 1) {
|
||||||
|
@ -1377,8 +1453,11 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
n = top - at;
|
n = top - at;
|
||||||
if (n <= 0)
|
if (n <= 0) {
|
||||||
|
if (event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD)
|
||||||
|
intel_pmu_save_and_restart_reload(event, 0);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
__intel_pmu_pebs_event(event, iregs, at, top, 0, n);
|
__intel_pmu_pebs_event(event, iregs, at, top, 0, n);
|
||||||
}
|
}
|
||||||
|
@ -1401,8 +1480,22 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
|
||||||
|
|
||||||
ds->pebs_index = ds->pebs_buffer_base;
|
ds->pebs_index = ds->pebs_buffer_base;
|
||||||
|
|
||||||
if (unlikely(base >= top))
|
if (unlikely(base >= top)) {
|
||||||
|
/*
|
||||||
|
* The drain_pebs() could be called twice in a short period
|
||||||
|
* for auto-reload event in pmu::read(). There are no
|
||||||
|
* overflows have happened in between.
|
||||||
|
* It needs to call intel_pmu_save_and_restart_reload() to
|
||||||
|
* update the event->count for this case.
|
||||||
|
*/
|
||||||
|
for_each_set_bit(bit, (unsigned long *)&cpuc->pebs_enabled,
|
||||||
|
x86_pmu.max_pebs_events) {
|
||||||
|
event = cpuc->events[bit];
|
||||||
|
if (event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD)
|
||||||
|
intel_pmu_save_and_restart_reload(event, 0);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (at = base; at < top; at += x86_pmu.pebs_record_size) {
|
for (at = base; at < top; at += x86_pmu.pebs_record_size) {
|
||||||
struct pebs_record_nhm *p = at;
|
struct pebs_record_nhm *p = at;
|
||||||
|
|
|
@ -1186,8 +1186,12 @@ static int pt_event_addr_filters_validate(struct list_head *filters)
|
||||||
int range = 0;
|
int range = 0;
|
||||||
|
|
||||||
list_for_each_entry(filter, filters, entry) {
|
list_for_each_entry(filter, filters, entry) {
|
||||||
/* PT doesn't support single address triggers */
|
/*
|
||||||
if (!filter->range || !filter->size)
|
* PT doesn't support single address triggers and
|
||||||
|
* 'start' filters.
|
||||||
|
*/
|
||||||
|
if (!filter->size ||
|
||||||
|
filter->action == PERF_ADDR_FILTER_ACTION_START)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!filter->inode) {
|
if (!filter->inode) {
|
||||||
|
@ -1227,7 +1231,10 @@ static void pt_event_addr_filters_sync(struct perf_event *event)
|
||||||
|
|
||||||
filters->filter[range].msr_a = msr_a;
|
filters->filter[range].msr_a = msr_a;
|
||||||
filters->filter[range].msr_b = msr_b;
|
filters->filter[range].msr_b = msr_b;
|
||||||
filters->filter[range].config = filter->filter ? 1 : 2;
|
if (filter->action == PERF_ADDR_FILTER_ACTION_FILTER)
|
||||||
|
filters->filter[range].config = 1;
|
||||||
|
else
|
||||||
|
filters->filter[range].config = 2;
|
||||||
range++;
|
range++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -774,6 +774,8 @@ static const struct x86_cpu_id rapl_cpu_match[] __initconst = {
|
||||||
X86_RAPL_MODEL_MATCH(INTEL_FAM6_KABYLAKE_MOBILE, skl_rapl_init),
|
X86_RAPL_MODEL_MATCH(INTEL_FAM6_KABYLAKE_MOBILE, skl_rapl_init),
|
||||||
X86_RAPL_MODEL_MATCH(INTEL_FAM6_KABYLAKE_DESKTOP, skl_rapl_init),
|
X86_RAPL_MODEL_MATCH(INTEL_FAM6_KABYLAKE_DESKTOP, skl_rapl_init),
|
||||||
|
|
||||||
|
X86_RAPL_MODEL_MATCH(INTEL_FAM6_CANNONLAKE_MOBILE, skl_rapl_init),
|
||||||
|
|
||||||
X86_RAPL_MODEL_MATCH(INTEL_FAM6_ATOM_GOLDMONT, hsw_rapl_init),
|
X86_RAPL_MODEL_MATCH(INTEL_FAM6_ATOM_GOLDMONT, hsw_rapl_init),
|
||||||
X86_RAPL_MODEL_MATCH(INTEL_FAM6_ATOM_DENVERTON, hsw_rapl_init),
|
X86_RAPL_MODEL_MATCH(INTEL_FAM6_ATOM_DENVERTON, hsw_rapl_init),
|
||||||
|
|
||||||
|
|
|
@ -354,7 +354,7 @@ uncore_collect_events(struct intel_uncore_box *box, struct perf_event *leader,
|
||||||
if (!dogrp)
|
if (!dogrp)
|
||||||
return n;
|
return n;
|
||||||
|
|
||||||
list_for_each_entry(event, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(event, leader) {
|
||||||
if (!is_box_event(box, event) ||
|
if (!is_box_event(box, event) ||
|
||||||
event->state <= PERF_EVENT_STATE_OFF)
|
event->state <= PERF_EVENT_STATE_OFF)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -520,6 +520,7 @@ struct x86_pmu {
|
||||||
void (*disable)(struct perf_event *);
|
void (*disable)(struct perf_event *);
|
||||||
void (*add)(struct perf_event *);
|
void (*add)(struct perf_event *);
|
||||||
void (*del)(struct perf_event *);
|
void (*del)(struct perf_event *);
|
||||||
|
void (*read)(struct perf_event *event);
|
||||||
int (*hw_config)(struct perf_event *event);
|
int (*hw_config)(struct perf_event *event);
|
||||||
int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign);
|
int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign);
|
||||||
unsigned eventsel;
|
unsigned eventsel;
|
||||||
|
@ -557,7 +558,7 @@ struct x86_pmu {
|
||||||
struct x86_pmu_quirk *quirks;
|
struct x86_pmu_quirk *quirks;
|
||||||
int perfctr_second_write;
|
int perfctr_second_write;
|
||||||
bool late_ack;
|
bool late_ack;
|
||||||
unsigned (*limit_period)(struct perf_event *event, unsigned l);
|
u64 (*limit_period)(struct perf_event *event, u64 l);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sysfs attrs
|
* sysfs attrs
|
||||||
|
@ -923,6 +924,8 @@ void intel_pmu_pebs_disable_all(void);
|
||||||
|
|
||||||
void intel_pmu_pebs_sched_task(struct perf_event_context *ctx, bool sched_in);
|
void intel_pmu_pebs_sched_task(struct perf_event_context *ctx, bool sched_in);
|
||||||
|
|
||||||
|
void intel_pmu_auto_reload_read(struct perf_event *event);
|
||||||
|
|
||||||
void intel_ds_init(void);
|
void intel_ds_init(void);
|
||||||
|
|
||||||
void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in);
|
void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in);
|
||||||
|
|
|
@ -24,11 +24,12 @@ static inline void paravirt_activate_mm(struct mm_struct *prev,
|
||||||
#endif /* !CONFIG_PARAVIRT */
|
#endif /* !CONFIG_PARAVIRT */
|
||||||
|
|
||||||
#ifdef CONFIG_PERF_EVENTS
|
#ifdef CONFIG_PERF_EVENTS
|
||||||
extern struct static_key rdpmc_always_available;
|
|
||||||
|
DECLARE_STATIC_KEY_FALSE(rdpmc_always_available_key);
|
||||||
|
|
||||||
static inline void load_mm_cr4(struct mm_struct *mm)
|
static inline void load_mm_cr4(struct mm_struct *mm)
|
||||||
{
|
{
|
||||||
if (static_key_false(&rdpmc_always_available) ||
|
if (static_branch_unlikely(&rdpmc_always_available_key) ||
|
||||||
atomic_read(&mm->context.perf_rdpmc_allowed))
|
atomic_read(&mm->context.perf_rdpmc_allowed))
|
||||||
cr4_set_bits(X86_CR4_PCE);
|
cr4_set_bits(X86_CR4_PCE);
|
||||||
else
|
else
|
||||||
|
|
|
@ -1311,7 +1311,7 @@ validate_group(struct perf_event *event)
|
||||||
if (!validate_event(event->pmu, &fake_pmu, leader))
|
if (!validate_event(event->pmu, &fake_pmu, leader))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(sibling, leader) {
|
||||||
if (!validate_event(event->pmu, &fake_pmu, sibling))
|
if (!validate_event(event->pmu, &fake_pmu, sibling))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -846,11 +846,11 @@ static int arm_ccn_pmu_event_init(struct perf_event *event)
|
||||||
!is_software_event(event->group_leader))
|
!is_software_event(event->group_leader))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
list_for_each_entry(sibling, &event->group_leader->sibling_list,
|
for_each_sibling_event(sibling, event->group_leader) {
|
||||||
group_entry)
|
|
||||||
if (sibling->pmu != event->pmu &&
|
if (sibling->pmu != event->pmu &&
|
||||||
!is_software_event(sibling))
|
!is_software_event(sibling))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,35 +393,26 @@ static int etm_addr_filters_validate(struct list_head *filters)
|
||||||
if (++index > ETM_ADDR_CMP_MAX)
|
if (++index > ETM_ADDR_CMP_MAX)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* filter::size==0 means single address trigger */
|
||||||
|
if (filter->size) {
|
||||||
|
/*
|
||||||
|
* The existing code relies on START/STOP filters
|
||||||
|
* being address filters.
|
||||||
|
*/
|
||||||
|
if (filter->action == PERF_ADDR_FILTER_ACTION_START ||
|
||||||
|
filter->action == PERF_ADDR_FILTER_ACTION_STOP)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
range = true;
|
||||||
|
} else
|
||||||
|
address = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As taken from the struct perf_addr_filter documentation:
|
|
||||||
* @range: 1: range, 0: address
|
|
||||||
*
|
|
||||||
* At this time we don't allow range and start/stop filtering
|
* At this time we don't allow range and start/stop filtering
|
||||||
* to cohabitate, they have to be mutually exclusive.
|
* to cohabitate, they have to be mutually exclusive.
|
||||||
*/
|
*/
|
||||||
if ((filter->range == 1) && address)
|
if (range && address)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if ((filter->range == 0) && range)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For range filtering, the second address in the address
|
|
||||||
* range comparator needs to be higher than the first.
|
|
||||||
* Invalid otherwise.
|
|
||||||
*/
|
|
||||||
if (filter->range && filter->size == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Everything checks out with this filter, record what we've
|
|
||||||
* received before moving on to the next one.
|
|
||||||
*/
|
|
||||||
if (filter->range)
|
|
||||||
range = true;
|
|
||||||
else
|
|
||||||
address = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -441,18 +432,20 @@ static void etm_addr_filters_sync(struct perf_event *event)
|
||||||
stop = start + filter->size;
|
stop = start + filter->size;
|
||||||
etm_filter = &filters->etm_filter[i];
|
etm_filter = &filters->etm_filter[i];
|
||||||
|
|
||||||
if (filter->range == 1) {
|
switch (filter->action) {
|
||||||
|
case PERF_ADDR_FILTER_ACTION_FILTER:
|
||||||
etm_filter->start_addr = start;
|
etm_filter->start_addr = start;
|
||||||
etm_filter->stop_addr = stop;
|
etm_filter->stop_addr = stop;
|
||||||
etm_filter->type = ETM_ADDR_TYPE_RANGE;
|
etm_filter->type = ETM_ADDR_TYPE_RANGE;
|
||||||
} else {
|
break;
|
||||||
if (filter->filter == 1) {
|
case PERF_ADDR_FILTER_ACTION_START:
|
||||||
etm_filter->start_addr = start;
|
etm_filter->start_addr = start;
|
||||||
etm_filter->type = ETM_ADDR_TYPE_START;
|
etm_filter->type = ETM_ADDR_TYPE_START;
|
||||||
} else {
|
break;
|
||||||
etm_filter->stop_addr = stop;
|
case PERF_ADDR_FILTER_ACTION_STOP:
|
||||||
etm_filter->type = ETM_ADDR_TYPE_STOP;
|
etm_filter->stop_addr = stop;
|
||||||
}
|
etm_filter->type = ETM_ADDR_TYPE_STOP;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -536,7 +536,7 @@ static bool dsu_pmu_validate_group(struct perf_event *event)
|
||||||
memset(fake_hw.used_mask, 0, sizeof(fake_hw.used_mask));
|
memset(fake_hw.used_mask, 0, sizeof(fake_hw.used_mask));
|
||||||
if (!dsu_pmu_validate_event(event->pmu, &fake_hw, leader))
|
if (!dsu_pmu_validate_event(event->pmu, &fake_hw, leader))
|
||||||
return false;
|
return false;
|
||||||
list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(sibling, leader) {
|
||||||
if (!dsu_pmu_validate_event(event->pmu, &fake_hw, sibling))
|
if (!dsu_pmu_validate_event(event->pmu, &fake_hw, sibling))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,7 +311,7 @@ validate_group(struct perf_event *event)
|
||||||
if (!validate_event(event->pmu, &fake_pmu, leader))
|
if (!validate_event(event->pmu, &fake_pmu, leader))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(sibling, leader) {
|
||||||
if (!validate_event(event->pmu, &fake_pmu, sibling))
|
if (!validate_event(event->pmu, &fake_pmu, sibling))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,8 +82,7 @@ static bool hisi_validate_event_group(struct perf_event *event)
|
||||||
counters++;
|
counters++;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(sibling, &event->group_leader->sibling_list,
|
for_each_sibling_event(sibling, event->group_leader) {
|
||||||
group_entry) {
|
|
||||||
if (is_software_event(sibling))
|
if (is_software_event(sibling))
|
||||||
continue;
|
continue;
|
||||||
if (sibling->pmu != event->pmu)
|
if (sibling->pmu != event->pmu)
|
||||||
|
|
|
@ -534,14 +534,14 @@ static int l2_cache_event_init(struct perf_event *event)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(sibling, &event->group_leader->sibling_list,
|
for_each_sibling_event(sibling, event->group_leader) {
|
||||||
group_entry)
|
|
||||||
if (sibling->pmu != event->pmu &&
|
if (sibling->pmu != event->pmu &&
|
||||||
!is_software_event(sibling)) {
|
!is_software_event(sibling)) {
|
||||||
dev_dbg_ratelimited(&l2cache_pmu->pdev->dev,
|
dev_dbg_ratelimited(&l2cache_pmu->pdev->dev,
|
||||||
"Can't create mixed PMU group\n");
|
"Can't create mixed PMU group\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cluster = get_cluster_pmu(l2cache_pmu, event->cpu);
|
cluster = get_cluster_pmu(l2cache_pmu, event->cpu);
|
||||||
if (!cluster) {
|
if (!cluster) {
|
||||||
|
@ -571,8 +571,7 @@ static int l2_cache_event_init(struct perf_event *event)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(sibling, &event->group_leader->sibling_list,
|
for_each_sibling_event(sibling, event->group_leader) {
|
||||||
group_entry) {
|
|
||||||
if ((sibling != event) &&
|
if ((sibling != event) &&
|
||||||
!is_software_event(sibling) &&
|
!is_software_event(sibling) &&
|
||||||
(L2_EVT_GROUP(sibling->attr.config) ==
|
(L2_EVT_GROUP(sibling->attr.config) ==
|
||||||
|
|
|
@ -468,7 +468,7 @@ static bool qcom_l3_cache__validate_event_group(struct perf_event *event)
|
||||||
counters = event_num_counters(event);
|
counters = event_num_counters(event);
|
||||||
counters += event_num_counters(leader);
|
counters += event_num_counters(leader);
|
||||||
|
|
||||||
list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
|
for_each_sibling_event(sibling, leader) {
|
||||||
if (is_software_event(sibling))
|
if (is_software_event(sibling))
|
||||||
continue;
|
continue;
|
||||||
if (sibling->pmu != event->pmu)
|
if (sibling->pmu != event->pmu)
|
||||||
|
|
|
@ -949,11 +949,11 @@ static int xgene_perf_event_init(struct perf_event *event)
|
||||||
!is_software_event(event->group_leader))
|
!is_software_event(event->group_leader))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
list_for_each_entry(sibling, &event->group_leader->sibling_list,
|
for_each_sibling_event(sibling, event->group_leader) {
|
||||||
group_entry)
|
|
||||||
if (sibling->pmu != event->pmu &&
|
if (sibling->pmu != event->pmu &&
|
||||||
!is_software_event(sibling))
|
!is_software_event(sibling))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,9 @@ register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||||
/* FIXME: only change from the attr, and don't unregister */
|
/* FIXME: only change from the attr, and don't unregister */
|
||||||
extern int
|
extern int
|
||||||
modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr);
|
modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr);
|
||||||
|
extern int
|
||||||
|
modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *attr,
|
||||||
|
bool check);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Kernel breakpoints are not associated with any particular thread.
|
* Kernel breakpoints are not associated with any particular thread.
|
||||||
|
@ -97,6 +100,10 @@ register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||||
static inline int
|
static inline int
|
||||||
modify_user_hw_breakpoint(struct perf_event *bp,
|
modify_user_hw_breakpoint(struct perf_event *bp,
|
||||||
struct perf_event_attr *attr) { return -ENOSYS; }
|
struct perf_event_attr *attr) { return -ENOSYS; }
|
||||||
|
static inline int
|
||||||
|
modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *attr,
|
||||||
|
bool check) { return -ENOSYS; }
|
||||||
|
|
||||||
static inline struct perf_event *
|
static inline struct perf_event *
|
||||||
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
|
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
|
||||||
perf_overflow_handler_t triggered,
|
perf_overflow_handler_t triggered,
|
||||||
|
|
|
@ -449,14 +449,19 @@ struct pmu {
|
||||||
int (*filter_match) (struct perf_event *event); /* optional */
|
int (*filter_match) (struct perf_event *event); /* optional */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum perf_addr_filter_action_t {
|
||||||
|
PERF_ADDR_FILTER_ACTION_STOP = 0,
|
||||||
|
PERF_ADDR_FILTER_ACTION_START,
|
||||||
|
PERF_ADDR_FILTER_ACTION_FILTER,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct perf_addr_filter - address range filter definition
|
* struct perf_addr_filter - address range filter definition
|
||||||
* @entry: event's filter list linkage
|
* @entry: event's filter list linkage
|
||||||
* @inode: object file's inode for file-based filters
|
* @inode: object file's inode for file-based filters
|
||||||
* @offset: filter range offset
|
* @offset: filter range offset
|
||||||
* @size: filter range size
|
* @size: filter range size (size==0 means single address trigger)
|
||||||
* @range: 1: range, 0: address
|
* @action: filter/start/stop
|
||||||
* @filter: 1: filter/start, 0: stop
|
|
||||||
*
|
*
|
||||||
* This is a hardware-agnostic filter configuration as specified by the user.
|
* This is a hardware-agnostic filter configuration as specified by the user.
|
||||||
*/
|
*/
|
||||||
|
@ -465,8 +470,7 @@ struct perf_addr_filter {
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
unsigned int range : 1,
|
enum perf_addr_filter_action_t action;
|
||||||
filter : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -536,6 +540,10 @@ struct pmu_event_list {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define for_each_sibling_event(sibling, event) \
|
||||||
|
if ((event)->group_leader == (event)) \
|
||||||
|
list_for_each_entry((sibling), &(event)->sibling_list, sibling_list)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct perf_event - performance event kernel representation:
|
* struct perf_event - performance event kernel representation:
|
||||||
*/
|
*/
|
||||||
|
@ -549,16 +557,16 @@ struct perf_event {
|
||||||
struct list_head event_entry;
|
struct list_head event_entry;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: group_entry and sibling_list should be mutually exclusive;
|
|
||||||
* either you're a sibling on a group, or you're the group leader.
|
|
||||||
* Rework the code to always use the same list element.
|
|
||||||
*
|
|
||||||
* Locked for modification by both ctx->mutex and ctx->lock; holding
|
* Locked for modification by both ctx->mutex and ctx->lock; holding
|
||||||
* either sufficies for read.
|
* either sufficies for read.
|
||||||
*/
|
*/
|
||||||
struct list_head group_entry;
|
|
||||||
struct list_head sibling_list;
|
struct list_head sibling_list;
|
||||||
|
struct list_head active_list;
|
||||||
|
/*
|
||||||
|
* Node on the pinned or flexible tree located at the event context;
|
||||||
|
*/
|
||||||
|
struct rb_node group_node;
|
||||||
|
u64 group_index;
|
||||||
/*
|
/*
|
||||||
* We need storage to track the entries in perf_pmu_migrate_context; we
|
* We need storage to track the entries in perf_pmu_migrate_context; we
|
||||||
* cannot use the event_entry because of RCU and we want to keep the
|
* cannot use the event_entry because of RCU and we want to keep the
|
||||||
|
@ -690,6 +698,12 @@ struct perf_event {
|
||||||
#endif /* CONFIG_PERF_EVENTS */
|
#endif /* CONFIG_PERF_EVENTS */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct perf_event_groups {
|
||||||
|
struct rb_root tree;
|
||||||
|
u64 index;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct perf_event_context - event context structure
|
* struct perf_event_context - event context structure
|
||||||
*
|
*
|
||||||
|
@ -710,9 +724,13 @@ struct perf_event_context {
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
|
|
||||||
struct list_head active_ctx_list;
|
struct list_head active_ctx_list;
|
||||||
struct list_head pinned_groups;
|
struct perf_event_groups pinned_groups;
|
||||||
struct list_head flexible_groups;
|
struct perf_event_groups flexible_groups;
|
||||||
struct list_head event_list;
|
struct list_head event_list;
|
||||||
|
|
||||||
|
struct list_head pinned_active;
|
||||||
|
struct list_head flexible_active;
|
||||||
|
|
||||||
int nr_events;
|
int nr_events;
|
||||||
int nr_active;
|
int nr_active;
|
||||||
int is_active;
|
int is_active;
|
||||||
|
|
|
@ -540,6 +540,14 @@ extern int perf_trace_init(struct perf_event *event);
|
||||||
extern void perf_trace_destroy(struct perf_event *event);
|
extern void perf_trace_destroy(struct perf_event *event);
|
||||||
extern int perf_trace_add(struct perf_event *event, int flags);
|
extern int perf_trace_add(struct perf_event *event, int flags);
|
||||||
extern void perf_trace_del(struct perf_event *event, int flags);
|
extern void perf_trace_del(struct perf_event *event, int flags);
|
||||||
|
#ifdef CONFIG_KPROBE_EVENTS
|
||||||
|
extern int perf_kprobe_init(struct perf_event *event, bool is_retprobe);
|
||||||
|
extern void perf_kprobe_destroy(struct perf_event *event);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_UPROBE_EVENTS
|
||||||
|
extern int perf_uprobe_init(struct perf_event *event, bool is_retprobe);
|
||||||
|
extern void perf_uprobe_destroy(struct perf_event *event);
|
||||||
|
#endif
|
||||||
extern int ftrace_profile_set_filter(struct perf_event *event, int event_id,
|
extern int ftrace_profile_set_filter(struct perf_event *event, int event_id,
|
||||||
char *filter_str);
|
char *filter_str);
|
||||||
extern void ftrace_profile_free_filter(struct perf_event *event);
|
extern void ftrace_profile_free_filter(struct perf_event *event);
|
||||||
|
|
|
@ -380,10 +380,14 @@ struct perf_event_attr {
|
||||||
__u32 bp_type;
|
__u32 bp_type;
|
||||||
union {
|
union {
|
||||||
__u64 bp_addr;
|
__u64 bp_addr;
|
||||||
|
__u64 kprobe_func; /* for perf_kprobe */
|
||||||
|
__u64 uprobe_path; /* for perf_uprobe */
|
||||||
__u64 config1; /* extension of config */
|
__u64 config1; /* extension of config */
|
||||||
};
|
};
|
||||||
union {
|
union {
|
||||||
__u64 bp_len;
|
__u64 bp_len;
|
||||||
|
__u64 kprobe_addr; /* when kprobe_func == NULL */
|
||||||
|
__u64 probe_offset; /* for perf_[k,u]probe */
|
||||||
__u64 config2; /* extension of config1 */
|
__u64 config2; /* extension of config1 */
|
||||||
};
|
};
|
||||||
__u64 branch_sample_type; /* enum perf_branch_sample_type */
|
__u64 branch_sample_type; /* enum perf_branch_sample_type */
|
||||||
|
@ -444,17 +448,18 @@ struct perf_event_query_bpf {
|
||||||
/*
|
/*
|
||||||
* Ioctls that can be done on a perf event fd:
|
* Ioctls that can be done on a perf event fd:
|
||||||
*/
|
*/
|
||||||
#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
|
#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
|
||||||
#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
|
#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
|
||||||
#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
|
#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
|
||||||
#define PERF_EVENT_IOC_RESET _IO ('$', 3)
|
#define PERF_EVENT_IOC_RESET _IO ('$', 3)
|
||||||
#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
|
#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
|
||||||
#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
|
#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
|
||||||
#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
|
#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
|
||||||
#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
|
#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
|
||||||
#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
|
#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
|
||||||
#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32)
|
#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32)
|
||||||
#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *)
|
#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *)
|
||||||
|
#define PERF_EVENT_IOC_MODIFY_ATTRIBUTES _IOW('$', 11, struct perf_event_attr *)
|
||||||
|
|
||||||
enum perf_event_ioc_flags {
|
enum perf_event_ioc_flags {
|
||||||
PERF_IOC_FLAG_GROUP = 1U << 0,
|
PERF_IOC_FLAG_GROUP = 1U << 0,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -44,6 +44,7 @@
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
|
#include <linux/bug.h>
|
||||||
|
|
||||||
#include <linux/hw_breakpoint.h>
|
#include <linux/hw_breakpoint.h>
|
||||||
/*
|
/*
|
||||||
|
@ -85,9 +86,9 @@ __weak int hw_breakpoint_weight(struct perf_event *bp)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline enum bp_type_idx find_slot_idx(struct perf_event *bp)
|
static inline enum bp_type_idx find_slot_idx(u64 bp_type)
|
||||||
{
|
{
|
||||||
if (bp->attr.bp_type & HW_BREAKPOINT_RW)
|
if (bp_type & HW_BREAKPOINT_RW)
|
||||||
return TYPE_DATA;
|
return TYPE_DATA;
|
||||||
|
|
||||||
return TYPE_INST;
|
return TYPE_INST;
|
||||||
|
@ -122,7 +123,7 @@ static int task_bp_pinned(int cpu, struct perf_event *bp, enum bp_type_idx type)
|
||||||
|
|
||||||
list_for_each_entry(iter, &bp_task_head, hw.bp_list) {
|
list_for_each_entry(iter, &bp_task_head, hw.bp_list) {
|
||||||
if (iter->hw.target == tsk &&
|
if (iter->hw.target == tsk &&
|
||||||
find_slot_idx(iter) == type &&
|
find_slot_idx(iter->attr.bp_type) == type &&
|
||||||
(iter->cpu < 0 || cpu == iter->cpu))
|
(iter->cpu < 0 || cpu == iter->cpu))
|
||||||
count += hw_breakpoint_weight(iter);
|
count += hw_breakpoint_weight(iter);
|
||||||
}
|
}
|
||||||
|
@ -277,7 +278,7 @@ __weak void arch_unregister_hw_breakpoint(struct perf_event *bp)
|
||||||
* ((per_cpu(info->flexible, *) > 1) + max(per_cpu(info->cpu_pinned, *))
|
* ((per_cpu(info->flexible, *) > 1) + max(per_cpu(info->cpu_pinned, *))
|
||||||
* + max(per_cpu(info->tsk_pinned, *))) < HBP_NUM
|
* + max(per_cpu(info->tsk_pinned, *))) < HBP_NUM
|
||||||
*/
|
*/
|
||||||
static int __reserve_bp_slot(struct perf_event *bp)
|
static int __reserve_bp_slot(struct perf_event *bp, u64 bp_type)
|
||||||
{
|
{
|
||||||
struct bp_busy_slots slots = {0};
|
struct bp_busy_slots slots = {0};
|
||||||
enum bp_type_idx type;
|
enum bp_type_idx type;
|
||||||
|
@ -288,11 +289,11 @@ static int __reserve_bp_slot(struct perf_event *bp)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Basic checks */
|
/* Basic checks */
|
||||||
if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY ||
|
if (bp_type == HW_BREAKPOINT_EMPTY ||
|
||||||
bp->attr.bp_type == HW_BREAKPOINT_INVALID)
|
bp_type == HW_BREAKPOINT_INVALID)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
type = find_slot_idx(bp);
|
type = find_slot_idx(bp_type);
|
||||||
weight = hw_breakpoint_weight(bp);
|
weight = hw_breakpoint_weight(bp);
|
||||||
|
|
||||||
fetch_bp_busy_slots(&slots, bp, type);
|
fetch_bp_busy_slots(&slots, bp, type);
|
||||||
|
@ -317,19 +318,19 @@ int reserve_bp_slot(struct perf_event *bp)
|
||||||
|
|
||||||
mutex_lock(&nr_bp_mutex);
|
mutex_lock(&nr_bp_mutex);
|
||||||
|
|
||||||
ret = __reserve_bp_slot(bp);
|
ret = __reserve_bp_slot(bp, bp->attr.bp_type);
|
||||||
|
|
||||||
mutex_unlock(&nr_bp_mutex);
|
mutex_unlock(&nr_bp_mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __release_bp_slot(struct perf_event *bp)
|
static void __release_bp_slot(struct perf_event *bp, u64 bp_type)
|
||||||
{
|
{
|
||||||
enum bp_type_idx type;
|
enum bp_type_idx type;
|
||||||
int weight;
|
int weight;
|
||||||
|
|
||||||
type = find_slot_idx(bp);
|
type = find_slot_idx(bp_type);
|
||||||
weight = hw_breakpoint_weight(bp);
|
weight = hw_breakpoint_weight(bp);
|
||||||
toggle_bp_slot(bp, false, type, weight);
|
toggle_bp_slot(bp, false, type, weight);
|
||||||
}
|
}
|
||||||
|
@ -339,11 +340,43 @@ void release_bp_slot(struct perf_event *bp)
|
||||||
mutex_lock(&nr_bp_mutex);
|
mutex_lock(&nr_bp_mutex);
|
||||||
|
|
||||||
arch_unregister_hw_breakpoint(bp);
|
arch_unregister_hw_breakpoint(bp);
|
||||||
__release_bp_slot(bp);
|
__release_bp_slot(bp, bp->attr.bp_type);
|
||||||
|
|
||||||
mutex_unlock(&nr_bp_mutex);
|
mutex_unlock(&nr_bp_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __modify_bp_slot(struct perf_event *bp, u64 old_type)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
__release_bp_slot(bp, old_type);
|
||||||
|
|
||||||
|
err = __reserve_bp_slot(bp, bp->attr.bp_type);
|
||||||
|
if (err) {
|
||||||
|
/*
|
||||||
|
* Reserve the old_type slot back in case
|
||||||
|
* there's no space for the new type.
|
||||||
|
*
|
||||||
|
* This must succeed, because we just released
|
||||||
|
* the old_type slot in the __release_bp_slot
|
||||||
|
* call above. If not, something is broken.
|
||||||
|
*/
|
||||||
|
WARN_ON(__reserve_bp_slot(bp, old_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int modify_bp_slot(struct perf_event *bp, u64 old_type)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&nr_bp_mutex);
|
||||||
|
ret = __modify_bp_slot(bp, old_type);
|
||||||
|
mutex_unlock(&nr_bp_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allow the kernel debugger to reserve breakpoint slots without
|
* Allow the kernel debugger to reserve breakpoint slots without
|
||||||
* taking a lock using the dbg_* variant of for the reserve and
|
* taking a lock using the dbg_* variant of for the reserve and
|
||||||
|
@ -354,7 +387,7 @@ int dbg_reserve_bp_slot(struct perf_event *bp)
|
||||||
if (mutex_is_locked(&nr_bp_mutex))
|
if (mutex_is_locked(&nr_bp_mutex))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return __reserve_bp_slot(bp);
|
return __reserve_bp_slot(bp, bp->attr.bp_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dbg_release_bp_slot(struct perf_event *bp)
|
int dbg_release_bp_slot(struct perf_event *bp)
|
||||||
|
@ -362,7 +395,7 @@ int dbg_release_bp_slot(struct perf_event *bp)
|
||||||
if (mutex_is_locked(&nr_bp_mutex))
|
if (mutex_is_locked(&nr_bp_mutex))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
__release_bp_slot(bp);
|
__release_bp_slot(bp, bp->attr.bp_type);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -423,6 +456,38 @@ register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
|
EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
|
||||||
|
|
||||||
|
int
|
||||||
|
modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *attr,
|
||||||
|
bool check)
|
||||||
|
{
|
||||||
|
u64 old_addr = bp->attr.bp_addr;
|
||||||
|
u64 old_len = bp->attr.bp_len;
|
||||||
|
int old_type = bp->attr.bp_type;
|
||||||
|
bool modify = attr->bp_type != old_type;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
bp->attr.bp_addr = attr->bp_addr;
|
||||||
|
bp->attr.bp_type = attr->bp_type;
|
||||||
|
bp->attr.bp_len = attr->bp_len;
|
||||||
|
|
||||||
|
if (check && memcmp(&bp->attr, attr, sizeof(*attr)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
err = validate_hw_breakpoint(bp);
|
||||||
|
if (!err && modify)
|
||||||
|
err = modify_bp_slot(bp, old_type);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
bp->attr.bp_addr = old_addr;
|
||||||
|
bp->attr.bp_type = old_type;
|
||||||
|
bp->attr.bp_len = old_len;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bp->attr.disabled = attr->disabled;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* modify_user_hw_breakpoint - modify a user-space hardware breakpoint
|
* modify_user_hw_breakpoint - modify a user-space hardware breakpoint
|
||||||
* @bp: the breakpoint structure to modify
|
* @bp: the breakpoint structure to modify
|
||||||
|
@ -441,21 +506,14 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
|
||||||
else
|
else
|
||||||
perf_event_disable(bp);
|
perf_event_disable(bp);
|
||||||
|
|
||||||
bp->attr.bp_addr = attr->bp_addr;
|
|
||||||
bp->attr.bp_type = attr->bp_type;
|
|
||||||
bp->attr.bp_len = attr->bp_len;
|
|
||||||
bp->attr.disabled = 1;
|
|
||||||
|
|
||||||
if (!attr->disabled) {
|
if (!attr->disabled) {
|
||||||
int err = validate_hw_breakpoint(bp);
|
int err = modify_user_hw_breakpoint_check(bp, attr, false);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
perf_event_enable(bp);
|
perf_event_enable(bp);
|
||||||
bp->attr.disabled = 0;
|
bp->attr.disabled = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
|
EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kprobes.h>
|
#include <linux/kprobes.h>
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "trace_probe.h"
|
||||||
|
|
||||||
static char __percpu *perf_trace_buf[PERF_NR_CONTEXTS];
|
static char __percpu *perf_trace_buf[PERF_NR_CONTEXTS];
|
||||||
|
|
||||||
|
@ -237,6 +238,107 @@ void perf_trace_destroy(struct perf_event *p_event)
|
||||||
mutex_unlock(&event_mutex);
|
mutex_unlock(&event_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KPROBE_EVENTS
|
||||||
|
int perf_kprobe_init(struct perf_event *p_event, bool is_retprobe)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char *func = NULL;
|
||||||
|
struct trace_event_call *tp_event;
|
||||||
|
|
||||||
|
if (p_event->attr.kprobe_func) {
|
||||||
|
func = kzalloc(KSYM_NAME_LEN, GFP_KERNEL);
|
||||||
|
if (!func)
|
||||||
|
return -ENOMEM;
|
||||||
|
ret = strncpy_from_user(
|
||||||
|
func, u64_to_user_ptr(p_event->attr.kprobe_func),
|
||||||
|
KSYM_NAME_LEN);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (func[0] == '\0') {
|
||||||
|
kfree(func);
|
||||||
|
func = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tp_event = create_local_trace_kprobe(
|
||||||
|
func, (void *)(unsigned long)(p_event->attr.kprobe_addr),
|
||||||
|
p_event->attr.probe_offset, is_retprobe);
|
||||||
|
if (IS_ERR(tp_event)) {
|
||||||
|
ret = PTR_ERR(tp_event);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = perf_trace_event_init(tp_event, p_event);
|
||||||
|
if (ret)
|
||||||
|
destroy_local_trace_kprobe(tp_event);
|
||||||
|
out:
|
||||||
|
kfree(func);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_kprobe_destroy(struct perf_event *p_event)
|
||||||
|
{
|
||||||
|
perf_trace_event_close(p_event);
|
||||||
|
perf_trace_event_unreg(p_event);
|
||||||
|
|
||||||
|
destroy_local_trace_kprobe(p_event->tp_event);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KPROBE_EVENTS */
|
||||||
|
|
||||||
|
#ifdef CONFIG_UPROBE_EVENTS
|
||||||
|
int perf_uprobe_init(struct perf_event *p_event, bool is_retprobe)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char *path = NULL;
|
||||||
|
struct trace_event_call *tp_event;
|
||||||
|
|
||||||
|
if (!p_event->attr.uprobe_path)
|
||||||
|
return -EINVAL;
|
||||||
|
path = kzalloc(PATH_MAX, GFP_KERNEL);
|
||||||
|
if (!path)
|
||||||
|
return -ENOMEM;
|
||||||
|
ret = strncpy_from_user(
|
||||||
|
path, u64_to_user_ptr(p_event->attr.uprobe_path), PATH_MAX);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
if (path[0] == '\0') {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
tp_event = create_local_trace_uprobe(
|
||||||
|
path, p_event->attr.probe_offset, is_retprobe);
|
||||||
|
if (IS_ERR(tp_event)) {
|
||||||
|
ret = PTR_ERR(tp_event);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* local trace_uprobe need to hold event_mutex to call
|
||||||
|
* uprobe_buffer_enable() and uprobe_buffer_disable().
|
||||||
|
* event_mutex is not required for local trace_kprobes.
|
||||||
|
*/
|
||||||
|
mutex_lock(&event_mutex);
|
||||||
|
ret = perf_trace_event_init(tp_event, p_event);
|
||||||
|
if (ret)
|
||||||
|
destroy_local_trace_uprobe(tp_event);
|
||||||
|
mutex_unlock(&event_mutex);
|
||||||
|
out:
|
||||||
|
kfree(path);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_uprobe_destroy(struct perf_event *p_event)
|
||||||
|
{
|
||||||
|
mutex_lock(&event_mutex);
|
||||||
|
perf_trace_event_close(p_event);
|
||||||
|
perf_trace_event_unreg(p_event);
|
||||||
|
mutex_unlock(&event_mutex);
|
||||||
|
destroy_local_trace_uprobe(p_event->tp_event);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_UPROBE_EVENTS */
|
||||||
|
|
||||||
int perf_trace_add(struct perf_event *p_event, int flags)
|
int perf_trace_add(struct perf_event *p_event, int flags)
|
||||||
{
|
{
|
||||||
struct trace_event_call *tp_event = p_event->tp_event;
|
struct trace_event_call *tp_event = p_event->tp_event;
|
||||||
|
|
|
@ -462,6 +462,14 @@ disable_trace_kprobe(struct trace_kprobe *tk, struct trace_event_file *file)
|
||||||
disable_kprobe(&tk->rp.kp);
|
disable_kprobe(&tk->rp.kp);
|
||||||
wait = 1;
|
wait = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if tk is not added to any list, it must be a local trace_kprobe
|
||||||
|
* created with perf_event_open. We don't need to wait for these
|
||||||
|
* trace_kprobes
|
||||||
|
*/
|
||||||
|
if (list_empty(&tk->list))
|
||||||
|
wait = 0;
|
||||||
out:
|
out:
|
||||||
if (wait) {
|
if (wait) {
|
||||||
/*
|
/*
|
||||||
|
@ -1358,12 +1366,9 @@ static struct trace_event_functions kprobe_funcs = {
|
||||||
.trace = print_kprobe_event
|
.trace = print_kprobe_event
|
||||||
};
|
};
|
||||||
|
|
||||||
static int register_kprobe_event(struct trace_kprobe *tk)
|
static inline void init_trace_event_call(struct trace_kprobe *tk,
|
||||||
|
struct trace_event_call *call)
|
||||||
{
|
{
|
||||||
struct trace_event_call *call = &tk->tp.call;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Initialize trace_event_call */
|
|
||||||
INIT_LIST_HEAD(&call->class->fields);
|
INIT_LIST_HEAD(&call->class->fields);
|
||||||
if (trace_kprobe_is_return(tk)) {
|
if (trace_kprobe_is_return(tk)) {
|
||||||
call->event.funcs = &kretprobe_funcs;
|
call->event.funcs = &kretprobe_funcs;
|
||||||
|
@ -1372,6 +1377,19 @@ static int register_kprobe_event(struct trace_kprobe *tk)
|
||||||
call->event.funcs = &kprobe_funcs;
|
call->event.funcs = &kprobe_funcs;
|
||||||
call->class->define_fields = kprobe_event_define_fields;
|
call->class->define_fields = kprobe_event_define_fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
call->flags = TRACE_EVENT_FL_KPROBE;
|
||||||
|
call->class->reg = kprobe_register;
|
||||||
|
call->data = tk;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int register_kprobe_event(struct trace_kprobe *tk)
|
||||||
|
{
|
||||||
|
struct trace_event_call *call = &tk->tp.call;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
init_trace_event_call(tk, call);
|
||||||
|
|
||||||
if (set_print_fmt(&tk->tp, trace_kprobe_is_return(tk)) < 0)
|
if (set_print_fmt(&tk->tp, trace_kprobe_is_return(tk)) < 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
ret = register_trace_event(&call->event);
|
ret = register_trace_event(&call->event);
|
||||||
|
@ -1379,9 +1397,6 @@ static int register_kprobe_event(struct trace_kprobe *tk)
|
||||||
kfree(call->print_fmt);
|
kfree(call->print_fmt);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
call->flags = TRACE_EVENT_FL_KPROBE;
|
|
||||||
call->class->reg = kprobe_register;
|
|
||||||
call->data = tk;
|
|
||||||
ret = trace_add_event_call(call);
|
ret = trace_add_event_call(call);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_info("Failed to register kprobe event: %s\n",
|
pr_info("Failed to register kprobe event: %s\n",
|
||||||
|
@ -1403,6 +1418,66 @@ static int unregister_kprobe_event(struct trace_kprobe *tk)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PERF_EVENTS
|
||||||
|
/* create a trace_kprobe, but don't add it to global lists */
|
||||||
|
struct trace_event_call *
|
||||||
|
create_local_trace_kprobe(char *func, void *addr, unsigned long offs,
|
||||||
|
bool is_return)
|
||||||
|
{
|
||||||
|
struct trace_kprobe *tk;
|
||||||
|
int ret;
|
||||||
|
char *event;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* local trace_kprobes are not added to probe_list, so they are never
|
||||||
|
* searched in find_trace_kprobe(). Therefore, there is no concern of
|
||||||
|
* duplicated name here.
|
||||||
|
*/
|
||||||
|
event = func ? func : "DUMMY_EVENT";
|
||||||
|
|
||||||
|
tk = alloc_trace_kprobe(KPROBE_EVENT_SYSTEM, event, (void *)addr, func,
|
||||||
|
offs, 0 /* maxactive */, 0 /* nargs */,
|
||||||
|
is_return);
|
||||||
|
|
||||||
|
if (IS_ERR(tk)) {
|
||||||
|
pr_info("Failed to allocate trace_probe.(%d)\n",
|
||||||
|
(int)PTR_ERR(tk));
|
||||||
|
return ERR_CAST(tk);
|
||||||
|
}
|
||||||
|
|
||||||
|
init_trace_event_call(tk, &tk->tp.call);
|
||||||
|
|
||||||
|
if (set_print_fmt(&tk->tp, trace_kprobe_is_return(tk)) < 0) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = __register_trace_kprobe(tk);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return &tk->tp.call;
|
||||||
|
error:
|
||||||
|
free_trace_kprobe(tk);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_local_trace_kprobe(struct trace_event_call *event_call)
|
||||||
|
{
|
||||||
|
struct trace_kprobe *tk;
|
||||||
|
|
||||||
|
tk = container_of(event_call, struct trace_kprobe, tp.call);
|
||||||
|
|
||||||
|
if (trace_probe_is_enabled(&tk->tp)) {
|
||||||
|
WARN_ON(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__unregister_trace_kprobe(tk);
|
||||||
|
free_trace_kprobe(tk);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PERF_EVENTS */
|
||||||
|
|
||||||
/* Make a tracefs interface for controlling probe points */
|
/* Make a tracefs interface for controlling probe points */
|
||||||
static __init int init_kprobe_trace(void)
|
static __init int init_kprobe_trace(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -416,3 +416,14 @@ store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs,
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int set_print_fmt(struct trace_probe *tp, bool is_return);
|
extern int set_print_fmt(struct trace_probe *tp, bool is_return);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PERF_EVENTS
|
||||||
|
extern struct trace_event_call *
|
||||||
|
create_local_trace_kprobe(char *func, void *addr, unsigned long offs,
|
||||||
|
bool is_return);
|
||||||
|
extern void destroy_local_trace_kprobe(struct trace_event_call *event_call);
|
||||||
|
|
||||||
|
extern struct trace_event_call *
|
||||||
|
create_local_trace_uprobe(char *name, unsigned long offs, bool is_return);
|
||||||
|
extern void destroy_local_trace_uprobe(struct trace_event_call *event_call);
|
||||||
|
#endif
|
||||||
|
|
|
@ -1292,16 +1292,25 @@ static struct trace_event_functions uprobe_funcs = {
|
||||||
.trace = print_uprobe_event
|
.trace = print_uprobe_event
|
||||||
};
|
};
|
||||||
|
|
||||||
static int register_uprobe_event(struct trace_uprobe *tu)
|
static inline void init_trace_event_call(struct trace_uprobe *tu,
|
||||||
|
struct trace_event_call *call)
|
||||||
{
|
{
|
||||||
struct trace_event_call *call = &tu->tp.call;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Initialize trace_event_call */
|
|
||||||
INIT_LIST_HEAD(&call->class->fields);
|
INIT_LIST_HEAD(&call->class->fields);
|
||||||
call->event.funcs = &uprobe_funcs;
|
call->event.funcs = &uprobe_funcs;
|
||||||
call->class->define_fields = uprobe_event_define_fields;
|
call->class->define_fields = uprobe_event_define_fields;
|
||||||
|
|
||||||
|
call->flags = TRACE_EVENT_FL_UPROBE;
|
||||||
|
call->class->reg = trace_uprobe_register;
|
||||||
|
call->data = tu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int register_uprobe_event(struct trace_uprobe *tu)
|
||||||
|
{
|
||||||
|
struct trace_event_call *call = &tu->tp.call;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
init_trace_event_call(tu, call);
|
||||||
|
|
||||||
if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0)
|
if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -1311,9 +1320,6 @@ static int register_uprobe_event(struct trace_uprobe *tu)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
call->flags = TRACE_EVENT_FL_UPROBE;
|
|
||||||
call->class->reg = trace_uprobe_register;
|
|
||||||
call->data = tu;
|
|
||||||
ret = trace_add_event_call(call);
|
ret = trace_add_event_call(call);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -1339,6 +1345,70 @@ static int unregister_uprobe_event(struct trace_uprobe *tu)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PERF_EVENTS
|
||||||
|
struct trace_event_call *
|
||||||
|
create_local_trace_uprobe(char *name, unsigned long offs, bool is_return)
|
||||||
|
{
|
||||||
|
struct trace_uprobe *tu;
|
||||||
|
struct inode *inode;
|
||||||
|
struct path path;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kern_path(name, LOOKUP_FOLLOW, &path);
|
||||||
|
if (ret)
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
|
||||||
|
inode = igrab(d_inode(path.dentry));
|
||||||
|
path_put(&path);
|
||||||
|
|
||||||
|
if (!inode || !S_ISREG(inode->i_mode)) {
|
||||||
|
iput(inode);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* local trace_kprobes are not added to probe_list, so they are never
|
||||||
|
* searched in find_trace_kprobe(). Therefore, there is no concern of
|
||||||
|
* duplicated name "DUMMY_EVENT" here.
|
||||||
|
*/
|
||||||
|
tu = alloc_trace_uprobe(UPROBE_EVENT_SYSTEM, "DUMMY_EVENT", 0,
|
||||||
|
is_return);
|
||||||
|
|
||||||
|
if (IS_ERR(tu)) {
|
||||||
|
pr_info("Failed to allocate trace_uprobe.(%d)\n",
|
||||||
|
(int)PTR_ERR(tu));
|
||||||
|
return ERR_CAST(tu);
|
||||||
|
}
|
||||||
|
|
||||||
|
tu->offset = offs;
|
||||||
|
tu->inode = inode;
|
||||||
|
tu->filename = kstrdup(name, GFP_KERNEL);
|
||||||
|
init_trace_event_call(tu, &tu->tp.call);
|
||||||
|
|
||||||
|
if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tu->tp.call;
|
||||||
|
error:
|
||||||
|
free_trace_uprobe(tu);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_local_trace_uprobe(struct trace_event_call *event_call)
|
||||||
|
{
|
||||||
|
struct trace_uprobe *tu;
|
||||||
|
|
||||||
|
tu = container_of(event_call, struct trace_uprobe, tp.call);
|
||||||
|
|
||||||
|
kfree(tu->tp.call.print_fmt);
|
||||||
|
tu->tp.call.print_fmt = NULL;
|
||||||
|
|
||||||
|
free_trace_uprobe(tu);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PERF_EVENTS */
|
||||||
|
|
||||||
/* Make a trace interface for controling probe points */
|
/* Make a trace interface for controling probe points */
|
||||||
static __init int init_uprobe_trace(void)
|
static __init int init_uprobe_trace(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,402 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||||
|
/*
|
||||||
|
* This file contains the system call numbers.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version
|
||||||
|
* 2 of the License, or (at your option) any later version.
|
||||||
|
*/
|
||||||
|
#ifndef _UAPI_ASM_POWERPC_UNISTD_H_
|
||||||
|
#define _UAPI_ASM_POWERPC_UNISTD_H_
|
||||||
|
|
||||||
|
|
||||||
|
#define __NR_restart_syscall 0
|
||||||
|
#define __NR_exit 1
|
||||||
|
#define __NR_fork 2
|
||||||
|
#define __NR_read 3
|
||||||
|
#define __NR_write 4
|
||||||
|
#define __NR_open 5
|
||||||
|
#define __NR_close 6
|
||||||
|
#define __NR_waitpid 7
|
||||||
|
#define __NR_creat 8
|
||||||
|
#define __NR_link 9
|
||||||
|
#define __NR_unlink 10
|
||||||
|
#define __NR_execve 11
|
||||||
|
#define __NR_chdir 12
|
||||||
|
#define __NR_time 13
|
||||||
|
#define __NR_mknod 14
|
||||||
|
#define __NR_chmod 15
|
||||||
|
#define __NR_lchown 16
|
||||||
|
#define __NR_break 17
|
||||||
|
#define __NR_oldstat 18
|
||||||
|
#define __NR_lseek 19
|
||||||
|
#define __NR_getpid 20
|
||||||
|
#define __NR_mount 21
|
||||||
|
#define __NR_umount 22
|
||||||
|
#define __NR_setuid 23
|
||||||
|
#define __NR_getuid 24
|
||||||
|
#define __NR_stime 25
|
||||||
|
#define __NR_ptrace 26
|
||||||
|
#define __NR_alarm 27
|
||||||
|
#define __NR_oldfstat 28
|
||||||
|
#define __NR_pause 29
|
||||||
|
#define __NR_utime 30
|
||||||
|
#define __NR_stty 31
|
||||||
|
#define __NR_gtty 32
|
||||||
|
#define __NR_access 33
|
||||||
|
#define __NR_nice 34
|
||||||
|
#define __NR_ftime 35
|
||||||
|
#define __NR_sync 36
|
||||||
|
#define __NR_kill 37
|
||||||
|
#define __NR_rename 38
|
||||||
|
#define __NR_mkdir 39
|
||||||
|
#define __NR_rmdir 40
|
||||||
|
#define __NR_dup 41
|
||||||
|
#define __NR_pipe 42
|
||||||
|
#define __NR_times 43
|
||||||
|
#define __NR_prof 44
|
||||||
|
#define __NR_brk 45
|
||||||
|
#define __NR_setgid 46
|
||||||
|
#define __NR_getgid 47
|
||||||
|
#define __NR_signal 48
|
||||||
|
#define __NR_geteuid 49
|
||||||
|
#define __NR_getegid 50
|
||||||
|
#define __NR_acct 51
|
||||||
|
#define __NR_umount2 52
|
||||||
|
#define __NR_lock 53
|
||||||
|
#define __NR_ioctl 54
|
||||||
|
#define __NR_fcntl 55
|
||||||
|
#define __NR_mpx 56
|
||||||
|
#define __NR_setpgid 57
|
||||||
|
#define __NR_ulimit 58
|
||||||
|
#define __NR_oldolduname 59
|
||||||
|
#define __NR_umask 60
|
||||||
|
#define __NR_chroot 61
|
||||||
|
#define __NR_ustat 62
|
||||||
|
#define __NR_dup2 63
|
||||||
|
#define __NR_getppid 64
|
||||||
|
#define __NR_getpgrp 65
|
||||||
|
#define __NR_setsid 66
|
||||||
|
#define __NR_sigaction 67
|
||||||
|
#define __NR_sgetmask 68
|
||||||
|
#define __NR_ssetmask 69
|
||||||
|
#define __NR_setreuid 70
|
||||||
|
#define __NR_setregid 71
|
||||||
|
#define __NR_sigsuspend 72
|
||||||
|
#define __NR_sigpending 73
|
||||||
|
#define __NR_sethostname 74
|
||||||
|
#define __NR_setrlimit 75
|
||||||
|
#define __NR_getrlimit 76
|
||||||
|
#define __NR_getrusage 77
|
||||||
|
#define __NR_gettimeofday 78
|
||||||
|
#define __NR_settimeofday 79
|
||||||
|
#define __NR_getgroups 80
|
||||||
|
#define __NR_setgroups 81
|
||||||
|
#define __NR_select 82
|
||||||
|
#define __NR_symlink 83
|
||||||
|
#define __NR_oldlstat 84
|
||||||
|
#define __NR_readlink 85
|
||||||
|
#define __NR_uselib 86
|
||||||
|
#define __NR_swapon 87
|
||||||
|
#define __NR_reboot 88
|
||||||
|
#define __NR_readdir 89
|
||||||
|
#define __NR_mmap 90
|
||||||
|
#define __NR_munmap 91
|
||||||
|
#define __NR_truncate 92
|
||||||
|
#define __NR_ftruncate 93
|
||||||
|
#define __NR_fchmod 94
|
||||||
|
#define __NR_fchown 95
|
||||||
|
#define __NR_getpriority 96
|
||||||
|
#define __NR_setpriority 97
|
||||||
|
#define __NR_profil 98
|
||||||
|
#define __NR_statfs 99
|
||||||
|
#define __NR_fstatfs 100
|
||||||
|
#define __NR_ioperm 101
|
||||||
|
#define __NR_socketcall 102
|
||||||
|
#define __NR_syslog 103
|
||||||
|
#define __NR_setitimer 104
|
||||||
|
#define __NR_getitimer 105
|
||||||
|
#define __NR_stat 106
|
||||||
|
#define __NR_lstat 107
|
||||||
|
#define __NR_fstat 108
|
||||||
|
#define __NR_olduname 109
|
||||||
|
#define __NR_iopl 110
|
||||||
|
#define __NR_vhangup 111
|
||||||
|
#define __NR_idle 112
|
||||||
|
#define __NR_vm86 113
|
||||||
|
#define __NR_wait4 114
|
||||||
|
#define __NR_swapoff 115
|
||||||
|
#define __NR_sysinfo 116
|
||||||
|
#define __NR_ipc 117
|
||||||
|
#define __NR_fsync 118
|
||||||
|
#define __NR_sigreturn 119
|
||||||
|
#define __NR_clone 120
|
||||||
|
#define __NR_setdomainname 121
|
||||||
|
#define __NR_uname 122
|
||||||
|
#define __NR_modify_ldt 123
|
||||||
|
#define __NR_adjtimex 124
|
||||||
|
#define __NR_mprotect 125
|
||||||
|
#define __NR_sigprocmask 126
|
||||||
|
#define __NR_create_module 127
|
||||||
|
#define __NR_init_module 128
|
||||||
|
#define __NR_delete_module 129
|
||||||
|
#define __NR_get_kernel_syms 130
|
||||||
|
#define __NR_quotactl 131
|
||||||
|
#define __NR_getpgid 132
|
||||||
|
#define __NR_fchdir 133
|
||||||
|
#define __NR_bdflush 134
|
||||||
|
#define __NR_sysfs 135
|
||||||
|
#define __NR_personality 136
|
||||||
|
#define __NR_afs_syscall 137 /* Syscall for Andrew File System */
|
||||||
|
#define __NR_setfsuid 138
|
||||||
|
#define __NR_setfsgid 139
|
||||||
|
#define __NR__llseek 140
|
||||||
|
#define __NR_getdents 141
|
||||||
|
#define __NR__newselect 142
|
||||||
|
#define __NR_flock 143
|
||||||
|
#define __NR_msync 144
|
||||||
|
#define __NR_readv 145
|
||||||
|
#define __NR_writev 146
|
||||||
|
#define __NR_getsid 147
|
||||||
|
#define __NR_fdatasync 148
|
||||||
|
#define __NR__sysctl 149
|
||||||
|
#define __NR_mlock 150
|
||||||
|
#define __NR_munlock 151
|
||||||
|
#define __NR_mlockall 152
|
||||||
|
#define __NR_munlockall 153
|
||||||
|
#define __NR_sched_setparam 154
|
||||||
|
#define __NR_sched_getparam 155
|
||||||
|
#define __NR_sched_setscheduler 156
|
||||||
|
#define __NR_sched_getscheduler 157
|
||||||
|
#define __NR_sched_yield 158
|
||||||
|
#define __NR_sched_get_priority_max 159
|
||||||
|
#define __NR_sched_get_priority_min 160
|
||||||
|
#define __NR_sched_rr_get_interval 161
|
||||||
|
#define __NR_nanosleep 162
|
||||||
|
#define __NR_mremap 163
|
||||||
|
#define __NR_setresuid 164
|
||||||
|
#define __NR_getresuid 165
|
||||||
|
#define __NR_query_module 166
|
||||||
|
#define __NR_poll 167
|
||||||
|
#define __NR_nfsservctl 168
|
||||||
|
#define __NR_setresgid 169
|
||||||
|
#define __NR_getresgid 170
|
||||||
|
#define __NR_prctl 171
|
||||||
|
#define __NR_rt_sigreturn 172
|
||||||
|
#define __NR_rt_sigaction 173
|
||||||
|
#define __NR_rt_sigprocmask 174
|
||||||
|
#define __NR_rt_sigpending 175
|
||||||
|
#define __NR_rt_sigtimedwait 176
|
||||||
|
#define __NR_rt_sigqueueinfo 177
|
||||||
|
#define __NR_rt_sigsuspend 178
|
||||||
|
#define __NR_pread64 179
|
||||||
|
#define __NR_pwrite64 180
|
||||||
|
#define __NR_chown 181
|
||||||
|
#define __NR_getcwd 182
|
||||||
|
#define __NR_capget 183
|
||||||
|
#define __NR_capset 184
|
||||||
|
#define __NR_sigaltstack 185
|
||||||
|
#define __NR_sendfile 186
|
||||||
|
#define __NR_getpmsg 187 /* some people actually want streams */
|
||||||
|
#define __NR_putpmsg 188 /* some people actually want streams */
|
||||||
|
#define __NR_vfork 189
|
||||||
|
#define __NR_ugetrlimit 190 /* SuS compliant getrlimit */
|
||||||
|
#define __NR_readahead 191
|
||||||
|
#ifndef __powerpc64__ /* these are 32-bit only */
|
||||||
|
#define __NR_mmap2 192
|
||||||
|
#define __NR_truncate64 193
|
||||||
|
#define __NR_ftruncate64 194
|
||||||
|
#define __NR_stat64 195
|
||||||
|
#define __NR_lstat64 196
|
||||||
|
#define __NR_fstat64 197
|
||||||
|
#endif
|
||||||
|
#define __NR_pciconfig_read 198
|
||||||
|
#define __NR_pciconfig_write 199
|
||||||
|
#define __NR_pciconfig_iobase 200
|
||||||
|
#define __NR_multiplexer 201
|
||||||
|
#define __NR_getdents64 202
|
||||||
|
#define __NR_pivot_root 203
|
||||||
|
#ifndef __powerpc64__
|
||||||
|
#define __NR_fcntl64 204
|
||||||
|
#endif
|
||||||
|
#define __NR_madvise 205
|
||||||
|
#define __NR_mincore 206
|
||||||
|
#define __NR_gettid 207
|
||||||
|
#define __NR_tkill 208
|
||||||
|
#define __NR_setxattr 209
|
||||||
|
#define __NR_lsetxattr 210
|
||||||
|
#define __NR_fsetxattr 211
|
||||||
|
#define __NR_getxattr 212
|
||||||
|
#define __NR_lgetxattr 213
|
||||||
|
#define __NR_fgetxattr 214
|
||||||
|
#define __NR_listxattr 215
|
||||||
|
#define __NR_llistxattr 216
|
||||||
|
#define __NR_flistxattr 217
|
||||||
|
#define __NR_removexattr 218
|
||||||
|
#define __NR_lremovexattr 219
|
||||||
|
#define __NR_fremovexattr 220
|
||||||
|
#define __NR_futex 221
|
||||||
|
#define __NR_sched_setaffinity 222
|
||||||
|
#define __NR_sched_getaffinity 223
|
||||||
|
/* 224 currently unused */
|
||||||
|
#define __NR_tuxcall 225
|
||||||
|
#ifndef __powerpc64__
|
||||||
|
#define __NR_sendfile64 226
|
||||||
|
#endif
|
||||||
|
#define __NR_io_setup 227
|
||||||
|
#define __NR_io_destroy 228
|
||||||
|
#define __NR_io_getevents 229
|
||||||
|
#define __NR_io_submit 230
|
||||||
|
#define __NR_io_cancel 231
|
||||||
|
#define __NR_set_tid_address 232
|
||||||
|
#define __NR_fadvise64 233
|
||||||
|
#define __NR_exit_group 234
|
||||||
|
#define __NR_lookup_dcookie 235
|
||||||
|
#define __NR_epoll_create 236
|
||||||
|
#define __NR_epoll_ctl 237
|
||||||
|
#define __NR_epoll_wait 238
|
||||||
|
#define __NR_remap_file_pages 239
|
||||||
|
#define __NR_timer_create 240
|
||||||
|
#define __NR_timer_settime 241
|
||||||
|
#define __NR_timer_gettime 242
|
||||||
|
#define __NR_timer_getoverrun 243
|
||||||
|
#define __NR_timer_delete 244
|
||||||
|
#define __NR_clock_settime 245
|
||||||
|
#define __NR_clock_gettime 246
|
||||||
|
#define __NR_clock_getres 247
|
||||||
|
#define __NR_clock_nanosleep 248
|
||||||
|
#define __NR_swapcontext 249
|
||||||
|
#define __NR_tgkill 250
|
||||||
|
#define __NR_utimes 251
|
||||||
|
#define __NR_statfs64 252
|
||||||
|
#define __NR_fstatfs64 253
|
||||||
|
#ifndef __powerpc64__
|
||||||
|
#define __NR_fadvise64_64 254
|
||||||
|
#endif
|
||||||
|
#define __NR_rtas 255
|
||||||
|
#define __NR_sys_debug_setcontext 256
|
||||||
|
/* Number 257 is reserved for vserver */
|
||||||
|
#define __NR_migrate_pages 258
|
||||||
|
#define __NR_mbind 259
|
||||||
|
#define __NR_get_mempolicy 260
|
||||||
|
#define __NR_set_mempolicy 261
|
||||||
|
#define __NR_mq_open 262
|
||||||
|
#define __NR_mq_unlink 263
|
||||||
|
#define __NR_mq_timedsend 264
|
||||||
|
#define __NR_mq_timedreceive 265
|
||||||
|
#define __NR_mq_notify 266
|
||||||
|
#define __NR_mq_getsetattr 267
|
||||||
|
#define __NR_kexec_load 268
|
||||||
|
#define __NR_add_key 269
|
||||||
|
#define __NR_request_key 270
|
||||||
|
#define __NR_keyctl 271
|
||||||
|
#define __NR_waitid 272
|
||||||
|
#define __NR_ioprio_set 273
|
||||||
|
#define __NR_ioprio_get 274
|
||||||
|
#define __NR_inotify_init 275
|
||||||
|
#define __NR_inotify_add_watch 276
|
||||||
|
#define __NR_inotify_rm_watch 277
|
||||||
|
#define __NR_spu_run 278
|
||||||
|
#define __NR_spu_create 279
|
||||||
|
#define __NR_pselect6 280
|
||||||
|
#define __NR_ppoll 281
|
||||||
|
#define __NR_unshare 282
|
||||||
|
#define __NR_splice 283
|
||||||
|
#define __NR_tee 284
|
||||||
|
#define __NR_vmsplice 285
|
||||||
|
#define __NR_openat 286
|
||||||
|
#define __NR_mkdirat 287
|
||||||
|
#define __NR_mknodat 288
|
||||||
|
#define __NR_fchownat 289
|
||||||
|
#define __NR_futimesat 290
|
||||||
|
#ifdef __powerpc64__
|
||||||
|
#define __NR_newfstatat 291
|
||||||
|
#else
|
||||||
|
#define __NR_fstatat64 291
|
||||||
|
#endif
|
||||||
|
#define __NR_unlinkat 292
|
||||||
|
#define __NR_renameat 293
|
||||||
|
#define __NR_linkat 294
|
||||||
|
#define __NR_symlinkat 295
|
||||||
|
#define __NR_readlinkat 296
|
||||||
|
#define __NR_fchmodat 297
|
||||||
|
#define __NR_faccessat 298
|
||||||
|
#define __NR_get_robust_list 299
|
||||||
|
#define __NR_set_robust_list 300
|
||||||
|
#define __NR_move_pages 301
|
||||||
|
#define __NR_getcpu 302
|
||||||
|
#define __NR_epoll_pwait 303
|
||||||
|
#define __NR_utimensat 304
|
||||||
|
#define __NR_signalfd 305
|
||||||
|
#define __NR_timerfd_create 306
|
||||||
|
#define __NR_eventfd 307
|
||||||
|
#define __NR_sync_file_range2 308
|
||||||
|
#define __NR_fallocate 309
|
||||||
|
#define __NR_subpage_prot 310
|
||||||
|
#define __NR_timerfd_settime 311
|
||||||
|
#define __NR_timerfd_gettime 312
|
||||||
|
#define __NR_signalfd4 313
|
||||||
|
#define __NR_eventfd2 314
|
||||||
|
#define __NR_epoll_create1 315
|
||||||
|
#define __NR_dup3 316
|
||||||
|
#define __NR_pipe2 317
|
||||||
|
#define __NR_inotify_init1 318
|
||||||
|
#define __NR_perf_event_open 319
|
||||||
|
#define __NR_preadv 320
|
||||||
|
#define __NR_pwritev 321
|
||||||
|
#define __NR_rt_tgsigqueueinfo 322
|
||||||
|
#define __NR_fanotify_init 323
|
||||||
|
#define __NR_fanotify_mark 324
|
||||||
|
#define __NR_prlimit64 325
|
||||||
|
#define __NR_socket 326
|
||||||
|
#define __NR_bind 327
|
||||||
|
#define __NR_connect 328
|
||||||
|
#define __NR_listen 329
|
||||||
|
#define __NR_accept 330
|
||||||
|
#define __NR_getsockname 331
|
||||||
|
#define __NR_getpeername 332
|
||||||
|
#define __NR_socketpair 333
|
||||||
|
#define __NR_send 334
|
||||||
|
#define __NR_sendto 335
|
||||||
|
#define __NR_recv 336
|
||||||
|
#define __NR_recvfrom 337
|
||||||
|
#define __NR_shutdown 338
|
||||||
|
#define __NR_setsockopt 339
|
||||||
|
#define __NR_getsockopt 340
|
||||||
|
#define __NR_sendmsg 341
|
||||||
|
#define __NR_recvmsg 342
|
||||||
|
#define __NR_recvmmsg 343
|
||||||
|
#define __NR_accept4 344
|
||||||
|
#define __NR_name_to_handle_at 345
|
||||||
|
#define __NR_open_by_handle_at 346
|
||||||
|
#define __NR_clock_adjtime 347
|
||||||
|
#define __NR_syncfs 348
|
||||||
|
#define __NR_sendmmsg 349
|
||||||
|
#define __NR_setns 350
|
||||||
|
#define __NR_process_vm_readv 351
|
||||||
|
#define __NR_process_vm_writev 352
|
||||||
|
#define __NR_finit_module 353
|
||||||
|
#define __NR_kcmp 354
|
||||||
|
#define __NR_sched_setattr 355
|
||||||
|
#define __NR_sched_getattr 356
|
||||||
|
#define __NR_renameat2 357
|
||||||
|
#define __NR_seccomp 358
|
||||||
|
#define __NR_getrandom 359
|
||||||
|
#define __NR_memfd_create 360
|
||||||
|
#define __NR_bpf 361
|
||||||
|
#define __NR_execveat 362
|
||||||
|
#define __NR_switch_endian 363
|
||||||
|
#define __NR_userfaultfd 364
|
||||||
|
#define __NR_membarrier 365
|
||||||
|
#define __NR_mlock2 378
|
||||||
|
#define __NR_copy_file_range 379
|
||||||
|
#define __NR_preadv2 380
|
||||||
|
#define __NR_pwritev2 381
|
||||||
|
#define __NR_kexec_file_load 382
|
||||||
|
#define __NR_statx 383
|
||||||
|
#define __NR_pkey_alloc 384
|
||||||
|
#define __NR_pkey_free 385
|
||||||
|
#define __NR_pkey_mprotect 386
|
||||||
|
|
||||||
|
#endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */
|
|
@ -82,7 +82,11 @@ FEATURE_TESTS_EXTRA := \
|
||||||
liberty-z \
|
liberty-z \
|
||||||
libunwind-debug-frame \
|
libunwind-debug-frame \
|
||||||
libunwind-debug-frame-arm \
|
libunwind-debug-frame-arm \
|
||||||
libunwind-debug-frame-aarch64
|
libunwind-debug-frame-aarch64 \
|
||||||
|
cxx \
|
||||||
|
llvm \
|
||||||
|
llvm-version \
|
||||||
|
clang
|
||||||
|
|
||||||
FEATURE_TESTS ?= $(FEATURE_TESTS_BASIC)
|
FEATURE_TESTS ?= $(FEATURE_TESTS_BASIC)
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,10 @@ FILES= \
|
||||||
test-jvmti.bin \
|
test-jvmti.bin \
|
||||||
test-sched_getcpu.bin \
|
test-sched_getcpu.bin \
|
||||||
test-setns.bin \
|
test-setns.bin \
|
||||||
test-libopencsd.bin
|
test-libopencsd.bin \
|
||||||
|
test-clang.bin \
|
||||||
|
test-llvm.bin \
|
||||||
|
test-llvm-version.bin
|
||||||
|
|
||||||
FILES := $(addprefix $(OUTPUT),$(FILES))
|
FILES := $(addprefix $(OUTPUT),$(FILES))
|
||||||
|
|
||||||
|
@ -257,11 +260,13 @@ $(OUTPUT)test-llvm.bin:
|
||||||
-I$(shell $(LLVM_CONFIG) --includedir) \
|
-I$(shell $(LLVM_CONFIG) --includedir) \
|
||||||
-L$(shell $(LLVM_CONFIG) --libdir) \
|
-L$(shell $(LLVM_CONFIG) --libdir) \
|
||||||
$(shell $(LLVM_CONFIG) --libs Core BPF) \
|
$(shell $(LLVM_CONFIG) --libs Core BPF) \
|
||||||
$(shell $(LLVM_CONFIG) --system-libs)
|
$(shell $(LLVM_CONFIG) --system-libs) \
|
||||||
|
> $(@:.bin=.make.output) 2>&1
|
||||||
|
|
||||||
$(OUTPUT)test-llvm-version.bin:
|
$(OUTPUT)test-llvm-version.bin:
|
||||||
$(BUILDXX) -std=gnu++11 \
|
$(BUILDXX) -std=gnu++11 \
|
||||||
-I$(shell $(LLVM_CONFIG) --includedir)
|
-I$(shell $(LLVM_CONFIG) --includedir) \
|
||||||
|
> $(@:.bin=.make.output) 2>&1
|
||||||
|
|
||||||
$(OUTPUT)test-clang.bin:
|
$(OUTPUT)test-clang.bin:
|
||||||
$(BUILDXX) -std=gnu++11 \
|
$(BUILDXX) -std=gnu++11 \
|
||||||
|
@ -271,7 +276,8 @@ $(OUTPUT)test-clang.bin:
|
||||||
-lclangFrontend -lclangEdit -lclangLex \
|
-lclangFrontend -lclangEdit -lclangLex \
|
||||||
-lclangAST -Wl,--end-group \
|
-lclangAST -Wl,--end-group \
|
||||||
$(shell $(LLVM_CONFIG) --libs Core option) \
|
$(shell $(LLVM_CONFIG) --libs Core option) \
|
||||||
$(shell $(LLVM_CONFIG) --system-libs)
|
$(shell $(LLVM_CONFIG) --system-libs) \
|
||||||
|
> $(@:.bin=.make.output) 2>&1
|
||||||
|
|
||||||
-include $(OUTPUT)*.d
|
-include $(OUTPUT)*.d
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ static inline int test_and_set_bit(int nr, unsigned long *addr)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bitmap_alloc - Allocate bitmap
|
* bitmap_alloc - Allocate bitmap
|
||||||
* @nr: Bit to set
|
* @nbits: Number of bits
|
||||||
*/
|
*/
|
||||||
static inline unsigned long *bitmap_alloc(int nbits)
|
static inline unsigned long *bitmap_alloc(int nbits)
|
||||||
{
|
{
|
||||||
|
|
|
@ -380,10 +380,14 @@ struct perf_event_attr {
|
||||||
__u32 bp_type;
|
__u32 bp_type;
|
||||||
union {
|
union {
|
||||||
__u64 bp_addr;
|
__u64 bp_addr;
|
||||||
|
__u64 kprobe_func; /* for perf_kprobe */
|
||||||
|
__u64 uprobe_path; /* for perf_uprobe */
|
||||||
__u64 config1; /* extension of config */
|
__u64 config1; /* extension of config */
|
||||||
};
|
};
|
||||||
union {
|
union {
|
||||||
__u64 bp_len;
|
__u64 bp_len;
|
||||||
|
__u64 kprobe_addr; /* when kprobe_func == NULL */
|
||||||
|
__u64 probe_offset; /* for perf_[k,u]probe */
|
||||||
__u64 config2; /* extension of config1 */
|
__u64 config2; /* extension of config1 */
|
||||||
};
|
};
|
||||||
__u64 branch_sample_type; /* enum perf_branch_sample_type */
|
__u64 branch_sample_type; /* enum perf_branch_sample_type */
|
||||||
|
@ -444,17 +448,18 @@ struct perf_event_query_bpf {
|
||||||
/*
|
/*
|
||||||
* Ioctls that can be done on a perf event fd:
|
* Ioctls that can be done on a perf event fd:
|
||||||
*/
|
*/
|
||||||
#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
|
#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
|
||||||
#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
|
#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
|
||||||
#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
|
#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
|
||||||
#define PERF_EVENT_IOC_RESET _IO ('$', 3)
|
#define PERF_EVENT_IOC_RESET _IO ('$', 3)
|
||||||
#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
|
#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
|
||||||
#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
|
#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
|
||||||
#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
|
#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
|
||||||
#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
|
#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
|
||||||
#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
|
#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
|
||||||
#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32)
|
#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32)
|
||||||
#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *)
|
#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *)
|
||||||
|
#define PERF_EVENT_IOC_MODIFY_ATTRIBUTES _IOW('$', 11, struct perf_event_attr *)
|
||||||
|
|
||||||
enum perf_event_ioc_flags {
|
enum perf_event_ioc_flags {
|
||||||
PERF_IOC_FLAG_GROUP = 1U << 0,
|
PERF_IOC_FLAG_GROUP = 1U << 0,
|
||||||
|
|
|
@ -315,12 +315,8 @@ int filename__read_int(const char *filename, int *value)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int filename__read_ull_base(const char *filename,
|
||||||
* Parses @value out of @filename with strtoull.
|
unsigned long long *value, int base)
|
||||||
* By using 0 for base, the strtoull detects the
|
|
||||||
* base automatically (see man strtoull).
|
|
||||||
*/
|
|
||||||
int filename__read_ull(const char *filename, unsigned long long *value)
|
|
||||||
{
|
{
|
||||||
char line[64];
|
char line[64];
|
||||||
int fd = open(filename, O_RDONLY), err = -1;
|
int fd = open(filename, O_RDONLY), err = -1;
|
||||||
|
@ -329,7 +325,7 @@ int filename__read_ull(const char *filename, unsigned long long *value)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (read(fd, line, sizeof(line)) > 0) {
|
if (read(fd, line, sizeof(line)) > 0) {
|
||||||
*value = strtoull(line, NULL, 0);
|
*value = strtoull(line, NULL, base);
|
||||||
if (*value != ULLONG_MAX)
|
if (*value != ULLONG_MAX)
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
@ -338,6 +334,25 @@ int filename__read_ull(const char *filename, unsigned long long *value)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses @value out of @filename with strtoull.
|
||||||
|
* By using 16 for base to treat the number as hex.
|
||||||
|
*/
|
||||||
|
int filename__read_xll(const char *filename, unsigned long long *value)
|
||||||
|
{
|
||||||
|
return filename__read_ull_base(filename, value, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses @value out of @filename with strtoull.
|
||||||
|
* By using 0 for base, the strtoull detects the
|
||||||
|
* base automatically (see man strtoull).
|
||||||
|
*/
|
||||||
|
int filename__read_ull(const char *filename, unsigned long long *value)
|
||||||
|
{
|
||||||
|
return filename__read_ull_base(filename, value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */
|
#define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */
|
||||||
|
|
||||||
int filename__read_str(const char *filename, char **buf, size_t *sizep)
|
int filename__read_str(const char *filename, char **buf, size_t *sizep)
|
||||||
|
@ -417,7 +432,8 @@ int procfs__read_str(const char *entry, char **buf, size_t *sizep)
|
||||||
return filename__read_str(path, buf, sizep);
|
return filename__read_str(path, buf, sizep);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sysfs__read_ull(const char *entry, unsigned long long *value)
|
static int sysfs__read_ull_base(const char *entry,
|
||||||
|
unsigned long long *value, int base)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
const char *sysfs = sysfs__mountpoint();
|
const char *sysfs = sysfs__mountpoint();
|
||||||
|
@ -427,7 +443,17 @@ int sysfs__read_ull(const char *entry, unsigned long long *value)
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
|
snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
|
||||||
|
|
||||||
return filename__read_ull(path, value);
|
return filename__read_ull_base(path, value, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sysfs__read_xll(const char *entry, unsigned long long *value)
|
||||||
|
{
|
||||||
|
return sysfs__read_ull_base(entry, value, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sysfs__read_ull(const char *entry, unsigned long long *value)
|
||||||
|
{
|
||||||
|
return sysfs__read_ull_base(entry, value, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sysfs__read_int(const char *entry, int *value)
|
int sysfs__read_int(const char *entry, int *value)
|
||||||
|
|
|
@ -30,6 +30,7 @@ FS(bpf_fs)
|
||||||
|
|
||||||
int filename__read_int(const char *filename, int *value);
|
int filename__read_int(const char *filename, int *value);
|
||||||
int filename__read_ull(const char *filename, unsigned long long *value);
|
int filename__read_ull(const char *filename, unsigned long long *value);
|
||||||
|
int filename__read_xll(const char *filename, unsigned long long *value);
|
||||||
int filename__read_str(const char *filename, char **buf, size_t *sizep);
|
int filename__read_str(const char *filename, char **buf, size_t *sizep);
|
||||||
|
|
||||||
int filename__write_int(const char *filename, int value);
|
int filename__write_int(const char *filename, int value);
|
||||||
|
@ -39,6 +40,7 @@ int procfs__read_str(const char *entry, char **buf, size_t *sizep);
|
||||||
int sysctl__read_int(const char *sysctl, int *value);
|
int sysctl__read_int(const char *sysctl, int *value);
|
||||||
int sysfs__read_int(const char *entry, int *value);
|
int sysfs__read_int(const char *entry, int *value);
|
||||||
int sysfs__read_ull(const char *entry, unsigned long long *value);
|
int sysfs__read_ull(const char *entry, unsigned long long *value);
|
||||||
|
int sysfs__read_xll(const char *entry, unsigned long long *value);
|
||||||
int sysfs__read_str(const char *entry, char **buf, size_t *sizep);
|
int sysfs__read_str(const char *entry, char **buf, size_t *sizep);
|
||||||
int sysfs__read_bool(const char *entry, bool *value);
|
int sysfs__read_bool(const char *entry, bool *value);
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,6 @@ char *str_error_r(int errnum, char *buf, size_t buflen)
|
||||||
{
|
{
|
||||||
int err = strerror_r(errnum, buf, buflen);
|
int err = strerror_r(errnum, buf, buflen);
|
||||||
if (err)
|
if (err)
|
||||||
snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, %p, %zd)=%d", errnum, buf, buflen, err);
|
snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, [buf], %zd)=%d", errnum, buflen, err);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,10 @@ int kallsyms__parse(const char *filename, void *arg,
|
||||||
|
|
||||||
len = hex2u64(line, &start);
|
len = hex2u64(line, &start);
|
||||||
|
|
||||||
|
/* Skip the line if we failed to parse the address. */
|
||||||
|
if (!len)
|
||||||
|
continue;
|
||||||
|
|
||||||
len++;
|
len++;
|
||||||
if (len + 2 >= line_len)
|
if (len + 2 >= line_len)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -21,7 +21,7 @@ If there is no debug info in the object, then annotated assembly is displayed.
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
-i::
|
-i::
|
||||||
--input=::
|
--input=<file>::
|
||||||
Input file name. (default: perf.data unless stdin is a fifo)
|
Input file name. (default: perf.data unless stdin is a fifo)
|
||||||
|
|
||||||
-d::
|
-d::
|
||||||
|
@ -55,6 +55,9 @@ OPTIONS
|
||||||
--vmlinux=<file>::
|
--vmlinux=<file>::
|
||||||
vmlinux pathname.
|
vmlinux pathname.
|
||||||
|
|
||||||
|
--ignore-vmlinux::
|
||||||
|
Ignore vmlinux files.
|
||||||
|
|
||||||
-m::
|
-m::
|
||||||
--modules::
|
--modules::
|
||||||
Load module symbols. WARNING: use only with -k and LIVE kernel.
|
Load module symbols. WARNING: use only with -k and LIVE kernel.
|
||||||
|
@ -69,7 +72,9 @@ OPTIONS
|
||||||
|
|
||||||
--stdio:: Use the stdio interface.
|
--stdio:: Use the stdio interface.
|
||||||
|
|
||||||
--stdio-color::
|
--stdio2:: Use the stdio2 interface, non-interactive, uses the TUI formatting.
|
||||||
|
|
||||||
|
--stdio-color=<mode>::
|
||||||
'always', 'never' or 'auto', allowing configuring color output
|
'always', 'never' or 'auto', allowing configuring color output
|
||||||
via the command line, in addition to via "color.ui" .perfconfig.
|
via the command line, in addition to via "color.ui" .perfconfig.
|
||||||
Use '--stdio-color always' to generate color even when redirecting
|
Use '--stdio-color always' to generate color even when redirecting
|
||||||
|
@ -84,7 +89,7 @@ OPTIONS
|
||||||
--gtk:: Use the GTK interface.
|
--gtk:: Use the GTK interface.
|
||||||
|
|
||||||
-C::
|
-C::
|
||||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
--cpu=<cpu>:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||||
CPUs are specified with -: 0-2. Default is to report samples on all
|
CPUs are specified with -: 0-2. Default is to report samples on all
|
||||||
CPUs.
|
CPUs.
|
||||||
|
|
|
@ -116,7 +116,7 @@ and calls standard perf record command.
|
||||||
Following perf record options are configured by default:
|
Following perf record options are configured by default:
|
||||||
(check perf record man page for details)
|
(check perf record man page for details)
|
||||||
|
|
||||||
-W,-d,--sample-cpu
|
-W,-d,--phys-data,--sample-cpu
|
||||||
|
|
||||||
Unless specified otherwise with '-e' option, following events are monitored by
|
Unless specified otherwise with '-e' option, following events are monitored by
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
perf-data(1)
|
perf-data(1)
|
||||||
==============
|
============
|
||||||
|
|
||||||
NAME
|
NAME
|
||||||
----
|
----
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
perf-ftrace(1)
|
perf-ftrace(1)
|
||||||
=============
|
==============
|
||||||
|
|
||||||
NAME
|
NAME
|
||||||
----
|
----
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
perf-kallsyms(1)
|
perf-kallsyms(1)
|
||||||
==============
|
================
|
||||||
|
|
||||||
NAME
|
NAME
|
||||||
----
|
----
|
||||||
|
|
|
@ -25,6 +25,10 @@ OPTIONS
|
||||||
--input=<file>::
|
--input=<file>::
|
||||||
Select the input file (default: perf.data unless stdin is a fifo)
|
Select the input file (default: perf.data unless stdin is a fifo)
|
||||||
|
|
||||||
|
-f::
|
||||||
|
--force::
|
||||||
|
Don't do ownership validation
|
||||||
|
|
||||||
-v::
|
-v::
|
||||||
--verbose::
|
--verbose::
|
||||||
Be more verbose. (show symbol address, etc)
|
Be more verbose. (show symbol address, etc)
|
||||||
|
@ -61,7 +65,7 @@ OPTIONS
|
||||||
default, but this option shows live (currently allocated) pages
|
default, but this option shows live (currently allocated) pages
|
||||||
instead. (This option works with --page option only)
|
instead. (This option works with --page option only)
|
||||||
|
|
||||||
--time::
|
--time=<start>,<stop>::
|
||||||
Only analyze samples within given time window: <start>,<stop>. Times
|
Only analyze samples within given time window: <start>,<stop>. Times
|
||||||
have the format seconds.microseconds. If start is not given (i.e., time
|
have the format seconds.microseconds. If start is not given (i.e., time
|
||||||
string is ',x.y') then analysis starts at the beginning of the file. If
|
string is ',x.y') then analysis starts at the beginning of the file. If
|
||||||
|
|
|
@ -141,7 +141,13 @@ on the first memory controller on socket 0 of a Intel Xeon system
|
||||||
|
|
||||||
Each memory controller has its own PMU. Measuring the complete system
|
Each memory controller has its own PMU. Measuring the complete system
|
||||||
bandwidth would require specifying all imc PMUs (see perf list output),
|
bandwidth would require specifying all imc PMUs (see perf list output),
|
||||||
and adding the values together.
|
and adding the values together. To simplify creation of multiple events,
|
||||||
|
prefix and glob matching is supported in the PMU name, and the prefix
|
||||||
|
'uncore_' is also ignored when performing the match. So the command above
|
||||||
|
can be expanded to all memory controllers by using the syntaxes:
|
||||||
|
|
||||||
|
perf stat -C 0 -a imc/cas_count_read/,imc/cas_count_write/ -I 1000 ...
|
||||||
|
perf stat -C 0 -a *imc*/cas_count_read/,*imc*/cas_count_write/ -I 1000 ...
|
||||||
|
|
||||||
This example measures the combined core power every second
|
This example measures the combined core power every second
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,10 @@ OPTIONS
|
||||||
<command>...::
|
<command>...::
|
||||||
Any command you can specify in a shell.
|
Any command you can specify in a shell.
|
||||||
|
|
||||||
|
-f::
|
||||||
|
--force::
|
||||||
|
Don't do ownership validation
|
||||||
|
|
||||||
-t::
|
-t::
|
||||||
--type=::
|
--type=::
|
||||||
Select the memory operation type: load or store (default: load,store)
|
Select the memory operation type: load or store (default: load,store)
|
||||||
|
|
|
@ -191,9 +191,16 @@ OPTIONS
|
||||||
-i::
|
-i::
|
||||||
--no-inherit::
|
--no-inherit::
|
||||||
Child tasks do not inherit counters.
|
Child tasks do not inherit counters.
|
||||||
|
|
||||||
-F::
|
-F::
|
||||||
--freq=::
|
--freq=::
|
||||||
Profile at this frequency.
|
Profile at this frequency. Use 'max' to use the currently maximum
|
||||||
|
allowed frequency, i.e. the value in the kernel.perf_event_max_sample_rate
|
||||||
|
sysctl. Will throttle down to the currently maximum allowed frequency.
|
||||||
|
See --strict-freq.
|
||||||
|
|
||||||
|
--strict-freq::
|
||||||
|
Fail if the specified frequency can't be used.
|
||||||
|
|
||||||
-m::
|
-m::
|
||||||
--mmap-pages=::
|
--mmap-pages=::
|
||||||
|
@ -308,7 +315,11 @@ can be provided. Each cgroup is applied to the corresponding event, i.e., first
|
||||||
to first event, second cgroup to second event and so on. It is possible to provide
|
to first event, second cgroup to second event and so on. It is possible to provide
|
||||||
an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have
|
an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have
|
||||||
corresponding events, i.e., they always refer to events defined earlier on the command
|
corresponding events, i.e., they always refer to events defined earlier on the command
|
||||||
line.
|
line. If the user wants to track multiple events for a specific cgroup, the user can
|
||||||
|
use '-e e1 -e e2 -G foo,foo' or just use '-e e1 -e e2 -G foo'.
|
||||||
|
|
||||||
|
If wanting to monitor, say, 'cycles' for a cgroup and also for system wide, this
|
||||||
|
command line can be used: 'perf stat -e cycles -G cgroup_name -a -e cycles'.
|
||||||
|
|
||||||
-b::
|
-b::
|
||||||
--branch-any::
|
--branch-any::
|
||||||
|
|
|
@ -296,6 +296,9 @@ OPTIONS
|
||||||
--vmlinux=<file>::
|
--vmlinux=<file>::
|
||||||
vmlinux pathname
|
vmlinux pathname
|
||||||
|
|
||||||
|
--ignore-vmlinux::
|
||||||
|
Ignore vmlinux files.
|
||||||
|
|
||||||
--kallsyms=<file>::
|
--kallsyms=<file>::
|
||||||
kallsyms pathname
|
kallsyms pathname
|
||||||
|
|
||||||
|
@ -354,7 +357,8 @@ OPTIONS
|
||||||
Path to objdump binary.
|
Path to objdump binary.
|
||||||
|
|
||||||
--group::
|
--group::
|
||||||
Show event group information together.
|
Show event group information together. It forces group output also
|
||||||
|
if there are no groups defined in data file.
|
||||||
|
|
||||||
--demangle::
|
--demangle::
|
||||||
Demangle symbol names to human readable form. It's enabled by default,
|
Demangle symbol names to human readable form. It's enabled by default,
|
||||||
|
@ -367,7 +371,7 @@ OPTIONS
|
||||||
Use the data addresses of samples in addition to instruction addresses
|
Use the data addresses of samples in addition to instruction addresses
|
||||||
to build the histograms. To generate meaningful output, the perf.data
|
to build the histograms. To generate meaningful output, the perf.data
|
||||||
file must have been obtained using perf record -d -W and using a
|
file must have been obtained using perf record -d -W and using a
|
||||||
special event -e cpu/mem-loads/ or -e cpu/mem-stores/. See
|
special event -e cpu/mem-loads/p or -e cpu/mem-stores/p. See
|
||||||
'perf mem' for simpler access.
|
'perf mem' for simpler access.
|
||||||
|
|
||||||
--percent-limit::
|
--percent-limit::
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
perf-sched(1)
|
perf-sched(1)
|
||||||
==============
|
=============
|
||||||
|
|
||||||
NAME
|
NAME
|
||||||
----
|
----
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
perf-script-perl(1)
|
perf-script-perl(1)
|
||||||
==================
|
===================
|
||||||
|
|
||||||
NAME
|
NAME
|
||||||
----
|
----
|
||||||
|
|
|
@ -303,6 +303,9 @@ OPTIONS
|
||||||
--show-lost-events
|
--show-lost-events
|
||||||
Display lost events i.e. events of type PERF_RECORD_LOST.
|
Display lost events i.e. events of type PERF_RECORD_LOST.
|
||||||
|
|
||||||
|
--show-round-events
|
||||||
|
Display finished round events i.e. events of type PERF_RECORD_FINISHED_ROUND.
|
||||||
|
|
||||||
--demangle::
|
--demangle::
|
||||||
Demangle symbol names to human readable form. It's enabled by default,
|
Demangle symbol names to human readable form. It's enabled by default,
|
||||||
disable with --no-demangle.
|
disable with --no-demangle.
|
||||||
|
|
|
@ -49,6 +49,13 @@ report::
|
||||||
parameters are defined by corresponding entries in
|
parameters are defined by corresponding entries in
|
||||||
/sys/bus/event_source/devices/<pmu>/format/*
|
/sys/bus/event_source/devices/<pmu>/format/*
|
||||||
|
|
||||||
|
Note that the last two syntaxes support prefix and glob matching in
|
||||||
|
the PMU name to simplify creation of events accross multiple instances
|
||||||
|
of the same type of PMU in large systems (e.g. memory controller PMUs).
|
||||||
|
Multiple PMU instances are typical for uncore PMUs, so the prefix
|
||||||
|
'uncore_' is also ignored when performing this match.
|
||||||
|
|
||||||
|
|
||||||
-i::
|
-i::
|
||||||
--no-inherit::
|
--no-inherit::
|
||||||
child tasks do not inherit counters
|
child tasks do not inherit counters
|
||||||
|
@ -118,7 +125,11 @@ can be provided. Each cgroup is applied to the corresponding event, i.e., first
|
||||||
to first event, second cgroup to second event and so on. It is possible to provide
|
to first event, second cgroup to second event and so on. It is possible to provide
|
||||||
an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have
|
an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have
|
||||||
corresponding events, i.e., they always refer to events defined earlier on the command
|
corresponding events, i.e., they always refer to events defined earlier on the command
|
||||||
line.
|
line. If the user wants to track multiple events for a specific cgroup, the user can
|
||||||
|
use '-e e1 -e e2 -G foo,foo' or just use '-e e1 -e e2 -G foo'.
|
||||||
|
|
||||||
|
If wanting to monitor, say, 'cycles' for a cgroup and also for system wide, this
|
||||||
|
command line can be used: 'perf stat -e cycles -G cgroup_name -a -e cycles'.
|
||||||
|
|
||||||
-o file::
|
-o file::
|
||||||
--output file::
|
--output file::
|
||||||
|
@ -146,6 +157,16 @@ Print count deltas every N milliseconds (minimum: 10ms)
|
||||||
The overhead percentage could be high in some cases, for instance with small, sub 100ms intervals. Use with caution.
|
The overhead percentage could be high in some cases, for instance with small, sub 100ms intervals. Use with caution.
|
||||||
example: 'perf stat -I 1000 -e cycles -a sleep 5'
|
example: 'perf stat -I 1000 -e cycles -a sleep 5'
|
||||||
|
|
||||||
|
--interval-count times::
|
||||||
|
Print count deltas for fixed number of times.
|
||||||
|
This option should be used together with "-I" option.
|
||||||
|
example: 'perf stat -I 1000 --interval-count 2 -e cycles -a'
|
||||||
|
|
||||||
|
--timeout msecs::
|
||||||
|
Stop the 'perf stat' session and print count deltas after N milliseconds (minimum: 10 ms).
|
||||||
|
This option is not supported with the "-I" option.
|
||||||
|
example: 'perf stat --time 2000 -e cycles -a'
|
||||||
|
|
||||||
--metric-only::
|
--metric-only::
|
||||||
Only print computed metrics. Print them in a single line.
|
Only print computed metrics. Print them in a single line.
|
||||||
Don't show any raw values. Not supported with --per-thread.
|
Don't show any raw values. Not supported with --per-thread.
|
||||||
|
@ -246,6 +267,16 @@ taskset.
|
||||||
--no-merge::
|
--no-merge::
|
||||||
Do not merge results from same PMUs.
|
Do not merge results from same PMUs.
|
||||||
|
|
||||||
|
When multiple events are created from a single event specification,
|
||||||
|
stat will, by default, aggregate the event counts and show the result
|
||||||
|
in a single row. This option disables that behavior and shows
|
||||||
|
the individual events and counts.
|
||||||
|
|
||||||
|
Multiple events are created from a single event specification when:
|
||||||
|
1. Prefix or glob matching is used for the PMU name.
|
||||||
|
2. Aliases, which are listed immediately after the Kernel PMU events
|
||||||
|
by perf list, are used.
|
||||||
|
|
||||||
--smi-cost::
|
--smi-cost::
|
||||||
Measure SMI cost if msr/aperf/ and msr/smi/ events are supported.
|
Measure SMI cost if msr/aperf/ and msr/smi/ events are supported.
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,9 @@ Default is to monitor all CPUS.
|
||||||
|
|
||||||
-F <freq>::
|
-F <freq>::
|
||||||
--freq=<freq>::
|
--freq=<freq>::
|
||||||
Profile at this frequency.
|
Profile at this frequency. Use 'max' to use the currently maximum
|
||||||
|
allowed frequency, i.e. the value in the kernel.perf_event_max_sample_rate
|
||||||
|
sysctl.
|
||||||
|
|
||||||
-i::
|
-i::
|
||||||
--inherit::
|
--inherit::
|
||||||
|
@ -65,6 +67,9 @@ Default is to monitor all CPUS.
|
||||||
--vmlinux=<path>::
|
--vmlinux=<path>::
|
||||||
Path to vmlinux. Required for annotation functionality.
|
Path to vmlinux. Required for annotation functionality.
|
||||||
|
|
||||||
|
--ignore-vmlinux::
|
||||||
|
Ignore vmlinux files.
|
||||||
|
|
||||||
-m <pages>::
|
-m <pages>::
|
||||||
--mmap-pages=<pages>::
|
--mmap-pages=<pages>::
|
||||||
Number of mmap data pages (must be a power of two) or size
|
Number of mmap data pages (must be a power of two) or size
|
||||||
|
|
|
@ -63,6 +63,31 @@ filter out the startup phase of the program, which is often very different.
|
||||||
--uid=::
|
--uid=::
|
||||||
Record events in threads owned by uid. Name or number.
|
Record events in threads owned by uid. Name or number.
|
||||||
|
|
||||||
|
-G::
|
||||||
|
--cgroup::
|
||||||
|
Record events in threads in a cgroup.
|
||||||
|
|
||||||
|
Look for cgroups to set at the /sys/fs/cgroup/perf_event directory, then
|
||||||
|
remove the /sys/fs/cgroup/perf_event/ part and try:
|
||||||
|
|
||||||
|
perf trace -G A -e sched:*switch
|
||||||
|
|
||||||
|
Will set all raw_syscalls:sys_{enter,exit}, pgfault, vfs_getname, etc
|
||||||
|
_and_ sched:sched_switch to the 'A' cgroup, while:
|
||||||
|
|
||||||
|
perf trace -e sched:*switch -G A
|
||||||
|
|
||||||
|
will only set the sched:sched_switch event to the 'A' cgroup, all the
|
||||||
|
other events (raw_syscalls:sys_{enter,exit}, etc are left "without"
|
||||||
|
a cgroup (on the root cgroup, sys wide, etc).
|
||||||
|
|
||||||
|
Multiple cgroups:
|
||||||
|
|
||||||
|
perf trace -G A -e sched:*switch -G B
|
||||||
|
|
||||||
|
the syscall ones go to the 'A' cgroup, the sched:sched_switch goes
|
||||||
|
to the 'B' cgroup.
|
||||||
|
|
||||||
--filter-pids=::
|
--filter-pids=::
|
||||||
Filter out events for these pids and for 'trace' itself (comma separated list).
|
Filter out events for these pids and for 'trace' itself (comma separated list).
|
||||||
|
|
||||||
|
|
|
@ -485,10 +485,5 @@ in pmu-tools parser. This allows to read perf.data from python and dump it.
|
||||||
quipper
|
quipper
|
||||||
|
|
||||||
The quipper C++ parser is available at
|
The quipper C++ parser is available at
|
||||||
https://chromium.googlesource.com/chromiumos/platform2
|
http://github.com/google/perf_data_converter/tree/master/src/quipper
|
||||||
|
|
||||||
It is under the chromiumos-wide-profiling/ subdirectory. This library can
|
|
||||||
convert a perf data file to a protobuf and vice versa.
|
|
||||||
|
|
||||||
Unfortunately this parser tends to be many versions behind and may not be able
|
|
||||||
to parse data files generated by recent perf.
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ NO_SYSCALL_TABLE := 1
|
||||||
# Additional ARCH settings for ppc
|
# Additional ARCH settings for ppc
|
||||||
ifeq ($(SRCARCH),powerpc)
|
ifeq ($(SRCARCH),powerpc)
|
||||||
NO_PERF_REGS := 0
|
NO_PERF_REGS := 0
|
||||||
|
NO_SYSCALL_TABLE := 0
|
||||||
|
CFLAGS += -I$(OUTPUT)arch/powerpc/include/generated
|
||||||
LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
|
LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -73,7 +75,7 @@ endif
|
||||||
# Disable it on all other architectures in case libdw unwind
|
# Disable it on all other architectures in case libdw unwind
|
||||||
# support is detected in system. Add supported architectures
|
# support is detected in system. Add supported architectures
|
||||||
# to the check.
|
# to the check.
|
||||||
ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm powerpc s390))
|
ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390))
|
||||||
NO_LIBDW_DWARF_UNWIND := 1
|
NO_LIBDW_DWARF_UNWIND := 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -666,25 +668,10 @@ else
|
||||||
ifneq ($(feature-libpython), 1)
|
ifneq ($(feature-libpython), 1)
|
||||||
$(call disable-python,No 'Python.h' (for Python 2.x support) was found: disables Python support - please install python-devel/python-dev)
|
$(call disable-python,No 'Python.h' (for Python 2.x support) was found: disables Python support - please install python-devel/python-dev)
|
||||||
else
|
else
|
||||||
ifneq ($(feature-libpython-version), 1)
|
LDFLAGS += $(PYTHON_EMBED_LDFLAGS)
|
||||||
$(warning Python 3 is not yet supported; please set)
|
EXTLIBS += $(PYTHON_EMBED_LIBADD)
|
||||||
$(warning PYTHON and/or PYTHON_CONFIG appropriately.)
|
LANG_BINDINGS += $(obj-perf)python/perf.so
|
||||||
$(warning If you also have Python 2 installed, then)
|
$(call detected,CONFIG_LIBPYTHON)
|
||||||
$(warning try something like:)
|
|
||||||
$(warning $(and ,))
|
|
||||||
$(warning $(and ,) make PYTHON=python2)
|
|
||||||
$(warning $(and ,))
|
|
||||||
$(warning Otherwise, disable Python support entirely:)
|
|
||||||
$(warning $(and ,))
|
|
||||||
$(warning $(and ,) make NO_LIBPYTHON=1)
|
|
||||||
$(warning $(and ,))
|
|
||||||
$(error $(and ,))
|
|
||||||
else
|
|
||||||
LDFLAGS += $(PYTHON_EMBED_LDFLAGS)
|
|
||||||
EXTLIBS += $(PYTHON_EMBED_LIBADD)
|
|
||||||
LANG_BINDINGS += $(obj-perf)python/perf.so
|
|
||||||
$(call detected,CONFIG_LIBPYTHON)
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -296,7 +296,7 @@ PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
|
||||||
PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/
|
PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/
|
||||||
export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP
|
export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP
|
||||||
|
|
||||||
python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so
|
python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT)python/perf*.so
|
||||||
|
|
||||||
PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
|
PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
|
||||||
PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBAPI)
|
PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBAPI)
|
||||||
|
@ -473,7 +473,7 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(LIBTRACEEVENT_D
|
||||||
$(PYTHON_WORD) util/setup.py \
|
$(PYTHON_WORD) util/setup.py \
|
||||||
--quiet build_ext; \
|
--quiet build_ext; \
|
||||||
mkdir -p $(OUTPUT)python && \
|
mkdir -p $(OUTPUT)python && \
|
||||||
cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/
|
cp $(PYTHON_EXTBUILD_LIB)perf*.so $(OUTPUT)python/
|
||||||
|
|
||||||
please_set_SHELL_PATH_to_a_more_modern_shell:
|
please_set_SHELL_PATH_to_a_more_modern_shell:
|
||||||
$(Q)$$(:)
|
$(Q)$$(:)
|
||||||
|
@ -708,15 +708,15 @@ TAG_FILES= ../../include/uapi/linux/perf_event.h
|
||||||
|
|
||||||
TAGS:
|
TAGS:
|
||||||
$(QUIET_GEN)$(RM) TAGS; \
|
$(QUIET_GEN)$(RM) TAGS; \
|
||||||
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES)
|
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print -o -name '*.cpp' -print | xargs etags -a $(TAG_FILES)
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
$(QUIET_GEN)$(RM) tags; \
|
$(QUIET_GEN)$(RM) tags; \
|
||||||
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES)
|
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print -o -name '*.cpp' -print | xargs ctags -a $(TAG_FILES)
|
||||||
|
|
||||||
cscope:
|
cscope:
|
||||||
$(QUIET_GEN)$(RM) cscope*; \
|
$(QUIET_GEN)$(RM) cscope*; \
|
||||||
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES)
|
$(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print -o -name '*.cpp' -print | xargs cscope -b $(TAG_FILES)
|
||||||
|
|
||||||
### Testing rules
|
### Testing rules
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ struct auxtrace_record
|
||||||
bool found_spe = false;
|
bool found_spe = false;
|
||||||
static struct perf_pmu **arm_spe_pmus = NULL;
|
static struct perf_pmu **arm_spe_pmus = NULL;
|
||||||
static int nr_spes = 0;
|
static int nr_spes = 0;
|
||||||
int i;
|
int i = 0;
|
||||||
|
|
||||||
if (!evlist)
|
if (!evlist)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -298,12 +298,17 @@ cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int etmv3 = 0, etmv4 = 0;
|
int etmv3 = 0, etmv4 = 0;
|
||||||
const struct cpu_map *cpus = evlist->cpus;
|
struct cpu_map *event_cpus = evlist->cpus;
|
||||||
|
struct cpu_map *online_cpus = cpu_map__new(NULL);
|
||||||
|
|
||||||
/* cpu map is not empty, we have specific CPUs to work with */
|
/* cpu map is not empty, we have specific CPUs to work with */
|
||||||
if (!cpu_map__empty(cpus)) {
|
if (!cpu_map__empty(event_cpus)) {
|
||||||
for (i = 0; i < cpu_map__nr(cpus); i++) {
|
for (i = 0; i < cpu__max_cpu(); i++) {
|
||||||
if (cs_etm_is_etmv4(itr, cpus->map[i]))
|
if (!cpu_map__has(event_cpus, i) ||
|
||||||
|
!cpu_map__has(online_cpus, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cs_etm_is_etmv4(itr, i))
|
||||||
etmv4++;
|
etmv4++;
|
||||||
else
|
else
|
||||||
etmv3++;
|
etmv3++;
|
||||||
|
@ -311,6 +316,9 @@ cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
|
||||||
} else {
|
} else {
|
||||||
/* get configuration for all CPUs in the system */
|
/* get configuration for all CPUs in the system */
|
||||||
for (i = 0; i < cpu__max_cpu(); i++) {
|
for (i = 0; i < cpu__max_cpu(); i++) {
|
||||||
|
if (!cpu_map__has(online_cpus, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (cs_etm_is_etmv4(itr, i))
|
if (cs_etm_is_etmv4(itr, i))
|
||||||
etmv4++;
|
etmv4++;
|
||||||
else
|
else
|
||||||
|
@ -318,6 +326,8 @@ cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpu_map__put(online_cpus);
|
||||||
|
|
||||||
return (CS_ETM_HEADER_SIZE +
|
return (CS_ETM_HEADER_SIZE +
|
||||||
(etmv4 * CS_ETMV4_PRIV_SIZE) +
|
(etmv4 * CS_ETMV4_PRIV_SIZE) +
|
||||||
(etmv3 * CS_ETMV3_PRIV_SIZE));
|
(etmv3 * CS_ETMV3_PRIV_SIZE));
|
||||||
|
@ -447,7 +457,9 @@ static int cs_etm_info_fill(struct auxtrace_record *itr,
|
||||||
int i;
|
int i;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u64 nr_cpu, type;
|
u64 nr_cpu, type;
|
||||||
const struct cpu_map *cpus = session->evlist->cpus;
|
struct cpu_map *cpu_map;
|
||||||
|
struct cpu_map *event_cpus = session->evlist->cpus;
|
||||||
|
struct cpu_map *online_cpus = cpu_map__new(NULL);
|
||||||
struct cs_etm_recording *ptr =
|
struct cs_etm_recording *ptr =
|
||||||
container_of(itr, struct cs_etm_recording, itr);
|
container_of(itr, struct cs_etm_recording, itr);
|
||||||
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
|
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
|
||||||
|
@ -458,8 +470,21 @@ static int cs_etm_info_fill(struct auxtrace_record *itr,
|
||||||
if (!session->evlist->nr_mmaps)
|
if (!session->evlist->nr_mmaps)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* If the cpu_map is empty all CPUs are involved */
|
/* If the cpu_map is empty all online CPUs are involved */
|
||||||
nr_cpu = cpu_map__empty(cpus) ? cpu__max_cpu() : cpu_map__nr(cpus);
|
if (cpu_map__empty(event_cpus)) {
|
||||||
|
cpu_map = online_cpus;
|
||||||
|
} else {
|
||||||
|
/* Make sure all specified CPUs are online */
|
||||||
|
for (i = 0; i < cpu_map__nr(event_cpus); i++) {
|
||||||
|
if (cpu_map__has(event_cpus, i) &&
|
||||||
|
!cpu_map__has(online_cpus, i))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_map = event_cpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
nr_cpu = cpu_map__nr(cpu_map);
|
||||||
/* Get PMU type as dynamically assigned by the core */
|
/* Get PMU type as dynamically assigned by the core */
|
||||||
type = cs_etm_pmu->type;
|
type = cs_etm_pmu->type;
|
||||||
|
|
||||||
|
@ -472,15 +497,11 @@ static int cs_etm_info_fill(struct auxtrace_record *itr,
|
||||||
|
|
||||||
offset = CS_ETM_SNAPSHOT + 1;
|
offset = CS_ETM_SNAPSHOT + 1;
|
||||||
|
|
||||||
/* cpu map is not empty, we have specific CPUs to work with */
|
for (i = 0; i < cpu__max_cpu() && offset < priv_size; i++)
|
||||||
if (!cpu_map__empty(cpus)) {
|
if (cpu_map__has(cpu_map, i))
|
||||||
for (i = 0; i < cpu_map__nr(cpus) && offset < priv_size; i++)
|
|
||||||
cs_etm_get_metadata(cpus->map[i], &offset, itr, info);
|
|
||||||
} else {
|
|
||||||
/* get configuration for all CPUs in the system */
|
|
||||||
for (i = 0; i < cpu__max_cpu(); i++)
|
|
||||||
cs_etm_get_metadata(i, &offset, itr, info);
|
cs_etm_get_metadata(i, &offset, itr, info);
|
||||||
}
|
|
||||||
|
cpu_map__put(online_cpus);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef ARCH_TESTS_H
|
||||||
|
#define ARCH_TESTS_H
|
||||||
|
|
||||||
|
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||||
|
struct thread;
|
||||||
|
struct perf_sample;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern struct test arch_tests[];
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,2 +1,4 @@
|
||||||
libperf-y += regs_load.o
|
libperf-y += regs_load.o
|
||||||
libperf-y += dwarf-unwind.o
|
libperf-y += dwarf-unwind.o
|
||||||
|
|
||||||
|
libperf-y += arch-tests.o
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include <string.h>
|
||||||
|
#include "tests/tests.h"
|
||||||
|
#include "arch-tests.h"
|
||||||
|
|
||||||
|
struct test arch_tests[] = {
|
||||||
|
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||||
|
{
|
||||||
|
.desc = "DWARF unwind",
|
||||||
|
.func = test__dwarf_unwind,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
.func = NULL,
|
||||||
|
},
|
||||||
|
};
|
|
@ -2,6 +2,7 @@ libperf-y += header.o
|
||||||
libperf-y += sym-handling.o
|
libperf-y += sym-handling.o
|
||||||
libperf-$(CONFIG_DWARF) += dwarf-regs.o
|
libperf-$(CONFIG_DWARF) += dwarf-regs.o
|
||||||
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
libperf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
|
||||||
|
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||||
|
|
||||||
libperf-$(CONFIG_AUXTRACE) += ../../arm/util/pmu.o \
|
libperf-$(CONFIG_AUXTRACE) += ../../arm/util/pmu.o \
|
||||||
../../arm/util/auxtrace.o \
|
../../arm/util/auxtrace.o \
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include <elfutils/libdwfl.h>
|
||||||
|
#include "../../util/unwind-libdw.h"
|
||||||
|
#include "../../util/perf_regs.h"
|
||||||
|
#include "../../util/event.h"
|
||||||
|
|
||||||
|
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||||
|
{
|
||||||
|
struct unwind_info *ui = arg;
|
||||||
|
struct regs_dump *user_regs = &ui->sample->user_regs;
|
||||||
|
Dwarf_Word dwarf_regs[PERF_REG_ARM64_MAX], dwarf_pc;
|
||||||
|
|
||||||
|
#define REG(r) ({ \
|
||||||
|
Dwarf_Word val = 0; \
|
||||||
|
perf_reg_value(&val, user_regs, PERF_REG_ARM64_##r); \
|
||||||
|
val; \
|
||||||
|
})
|
||||||
|
|
||||||
|
dwarf_regs[0] = REG(X0);
|
||||||
|
dwarf_regs[1] = REG(X1);
|
||||||
|
dwarf_regs[2] = REG(X2);
|
||||||
|
dwarf_regs[3] = REG(X3);
|
||||||
|
dwarf_regs[4] = REG(X4);
|
||||||
|
dwarf_regs[5] = REG(X5);
|
||||||
|
dwarf_regs[6] = REG(X6);
|
||||||
|
dwarf_regs[7] = REG(X7);
|
||||||
|
dwarf_regs[8] = REG(X8);
|
||||||
|
dwarf_regs[9] = REG(X9);
|
||||||
|
dwarf_regs[10] = REG(X10);
|
||||||
|
dwarf_regs[11] = REG(X11);
|
||||||
|
dwarf_regs[12] = REG(X12);
|
||||||
|
dwarf_regs[13] = REG(X13);
|
||||||
|
dwarf_regs[14] = REG(X14);
|
||||||
|
dwarf_regs[15] = REG(X15);
|
||||||
|
dwarf_regs[16] = REG(X16);
|
||||||
|
dwarf_regs[17] = REG(X17);
|
||||||
|
dwarf_regs[18] = REG(X18);
|
||||||
|
dwarf_regs[19] = REG(X19);
|
||||||
|
dwarf_regs[20] = REG(X20);
|
||||||
|
dwarf_regs[21] = REG(X21);
|
||||||
|
dwarf_regs[22] = REG(X22);
|
||||||
|
dwarf_regs[23] = REG(X23);
|
||||||
|
dwarf_regs[24] = REG(X24);
|
||||||
|
dwarf_regs[25] = REG(X25);
|
||||||
|
dwarf_regs[26] = REG(X26);
|
||||||
|
dwarf_regs[27] = REG(X27);
|
||||||
|
dwarf_regs[28] = REG(X28);
|
||||||
|
dwarf_regs[29] = REG(X29);
|
||||||
|
dwarf_regs[30] = REG(LR);
|
||||||
|
dwarf_regs[31] = REG(SP);
|
||||||
|
|
||||||
|
if (!dwfl_thread_state_registers(thread, 0, PERF_REG_ARM64_MAX,
|
||||||
|
dwarf_regs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dwarf_pc = REG(PC);
|
||||||
|
dwfl_thread_state_register_pc(thread, dwarf_pc);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -6,3 +6,28 @@ endif
|
||||||
HAVE_KVM_STAT_SUPPORT := 1
|
HAVE_KVM_STAT_SUPPORT := 1
|
||||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||||
PERF_HAVE_JITDUMP := 1
|
PERF_HAVE_JITDUMP := 1
|
||||||
|
|
||||||
|
#
|
||||||
|
# Syscall table generation for perf
|
||||||
|
#
|
||||||
|
|
||||||
|
out := $(OUTPUT)arch/powerpc/include/generated/asm
|
||||||
|
header32 := $(out)/syscalls_32.c
|
||||||
|
header64 := $(out)/syscalls_64.c
|
||||||
|
sysdef := $(srctree)/tools/arch/powerpc/include/uapi/asm/unistd.h
|
||||||
|
sysprf := $(srctree)/tools/perf/arch/powerpc/entry/syscalls/
|
||||||
|
systbl := $(sysprf)/mksyscalltbl
|
||||||
|
|
||||||
|
# Create output directory if not already present
|
||||||
|
_dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)')
|
||||||
|
|
||||||
|
$(header64): $(sysdef) $(systbl)
|
||||||
|
$(Q)$(SHELL) '$(systbl)' '64' '$(CC)' $(sysdef) > $@
|
||||||
|
|
||||||
|
$(header32): $(sysdef) $(systbl)
|
||||||
|
$(Q)$(SHELL) '$(systbl)' '32' '$(CC)' $(sysdef) > $@
|
||||||
|
|
||||||
|
clean::
|
||||||
|
$(call QUIET_CLEAN, powerpc) $(RM) $(header32) $(header64)
|
||||||
|
|
||||||
|
archheaders: $(header32) $(header64)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
# Generate system call table for perf. Derived from
|
||||||
|
# s390 script.
|
||||||
|
#
|
||||||
|
# Copyright IBM Corp. 2017
|
||||||
|
# Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
|
||||||
|
# Changed by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
|
||||||
|
|
||||||
|
wordsize=$1
|
||||||
|
gcc=$2
|
||||||
|
input=$3
|
||||||
|
|
||||||
|
if ! test -r $input; then
|
||||||
|
echo "Could not read input file" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
create_table()
|
||||||
|
{
|
||||||
|
local wordsize=$1
|
||||||
|
local max_nr
|
||||||
|
|
||||||
|
echo "static const char *syscalltbl_powerpc_${wordsize}[] = {"
|
||||||
|
while read sc nr; do
|
||||||
|
printf '\t[%d] = "%s",\n' $nr $sc
|
||||||
|
max_nr=$nr
|
||||||
|
done
|
||||||
|
echo '};'
|
||||||
|
echo "#define SYSCALLTBL_POWERPC_${wordsize}_MAX_ID $max_nr"
|
||||||
|
}
|
||||||
|
|
||||||
|
$gcc -m${wordsize} -E -dM -x c $input \
|
||||||
|
|sed -ne 's/^#define __NR_//p' \
|
||||||
|
|sort -t' ' -k2 -nu \
|
||||||
|
|create_table ${wordsize}
|
|
@ -1,6 +1,113 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
|
|
||||||
|
static int s390_call__parse(struct arch *arch, struct ins_operands *ops,
|
||||||
|
struct map_symbol *ms)
|
||||||
|
{
|
||||||
|
char *endptr, *tok, *name;
|
||||||
|
struct map *map = ms->map;
|
||||||
|
struct addr_map_symbol target = {
|
||||||
|
.map = map,
|
||||||
|
};
|
||||||
|
|
||||||
|
tok = strchr(ops->raw, ',');
|
||||||
|
if (!tok)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ops->target.addr = strtoull(tok + 1, &endptr, 16);
|
||||||
|
|
||||||
|
name = strchr(endptr, '<');
|
||||||
|
if (name == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
name++;
|
||||||
|
|
||||||
|
if (arch->objdump.skip_functions_char &&
|
||||||
|
strchr(name, arch->objdump.skip_functions_char))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
tok = strchr(name, '>');
|
||||||
|
if (tok == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*tok = '\0';
|
||||||
|
ops->target.name = strdup(name);
|
||||||
|
*tok = '>';
|
||||||
|
|
||||||
|
if (ops->target.name == NULL)
|
||||||
|
return -1;
|
||||||
|
target.addr = map__objdump_2mem(map, ops->target.addr);
|
||||||
|
|
||||||
|
if (map_groups__find_ams(&target) == 0 &&
|
||||||
|
map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr)
|
||||||
|
ops->target.sym = target.sym;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int call__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||||
|
struct ins_operands *ops);
|
||||||
|
|
||||||
|
static struct ins_ops s390_call_ops = {
|
||||||
|
.parse = s390_call__parse,
|
||||||
|
.scnprintf = call__scnprintf,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int s390_mov__parse(struct arch *arch __maybe_unused,
|
||||||
|
struct ins_operands *ops,
|
||||||
|
struct map_symbol *ms __maybe_unused)
|
||||||
|
{
|
||||||
|
char *s = strchr(ops->raw, ','), *target, *endptr;
|
||||||
|
|
||||||
|
if (s == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*s = '\0';
|
||||||
|
ops->source.raw = strdup(ops->raw);
|
||||||
|
*s = ',';
|
||||||
|
|
||||||
|
if (ops->source.raw == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
target = ++s;
|
||||||
|
ops->target.raw = strdup(target);
|
||||||
|
if (ops->target.raw == NULL)
|
||||||
|
goto out_free_source;
|
||||||
|
|
||||||
|
ops->target.addr = strtoull(target, &endptr, 16);
|
||||||
|
if (endptr == target)
|
||||||
|
goto out_free_target;
|
||||||
|
|
||||||
|
s = strchr(endptr, '<');
|
||||||
|
if (s == NULL)
|
||||||
|
goto out_free_target;
|
||||||
|
endptr = strchr(s + 1, '>');
|
||||||
|
if (endptr == NULL)
|
||||||
|
goto out_free_target;
|
||||||
|
|
||||||
|
*endptr = '\0';
|
||||||
|
ops->target.name = strdup(s + 1);
|
||||||
|
*endptr = '>';
|
||||||
|
if (ops->target.name == NULL)
|
||||||
|
goto out_free_target;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_free_target:
|
||||||
|
zfree(&ops->target.raw);
|
||||||
|
out_free_source:
|
||||||
|
zfree(&ops->source.raw);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mov__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||||
|
struct ins_operands *ops);
|
||||||
|
|
||||||
|
static struct ins_ops s390_mov_ops = {
|
||||||
|
.parse = s390_mov__parse,
|
||||||
|
.scnprintf = mov__scnprintf,
|
||||||
|
};
|
||||||
|
|
||||||
static struct ins_ops *s390__associate_ins_ops(struct arch *arch, const char *name)
|
static struct ins_ops *s390__associate_ins_ops(struct arch *arch, const char *name)
|
||||||
{
|
{
|
||||||
struct ins_ops *ops = NULL;
|
struct ins_ops *ops = NULL;
|
||||||
|
@ -14,21 +121,54 @@ static struct ins_ops *s390__associate_ins_ops(struct arch *arch, const char *na
|
||||||
if (!strcmp(name, "bras") ||
|
if (!strcmp(name, "bras") ||
|
||||||
!strcmp(name, "brasl") ||
|
!strcmp(name, "brasl") ||
|
||||||
!strcmp(name, "basr"))
|
!strcmp(name, "basr"))
|
||||||
ops = &call_ops;
|
ops = &s390_call_ops;
|
||||||
if (!strcmp(name, "br"))
|
if (!strcmp(name, "br"))
|
||||||
ops = &ret_ops;
|
ops = &ret_ops;
|
||||||
|
/* override load/store relative to PC */
|
||||||
|
if (!strcmp(name, "lrl") ||
|
||||||
|
!strcmp(name, "lgrl") ||
|
||||||
|
!strcmp(name, "lgfrl") ||
|
||||||
|
!strcmp(name, "llgfrl") ||
|
||||||
|
!strcmp(name, "strl") ||
|
||||||
|
!strcmp(name, "stgrl"))
|
||||||
|
ops = &s390_mov_ops;
|
||||||
|
|
||||||
if (ops)
|
if (ops)
|
||||||
arch__associate_ins_ops(arch, name, ops);
|
arch__associate_ins_ops(arch, name, ops);
|
||||||
return ops;
|
return ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int s390__cpuid_parse(struct arch *arch, char *cpuid)
|
||||||
|
{
|
||||||
|
unsigned int family;
|
||||||
|
char model[16], model_c[16], cpumf_v[16], cpumf_a[16];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cpuid string format:
|
||||||
|
* "IBM,family,model-capacity,model[,cpum_cf-version,cpum_cf-authorization]"
|
||||||
|
*/
|
||||||
|
ret = sscanf(cpuid, "%*[^,],%u,%[^,],%[^,],%[^,],%s", &family, model_c,
|
||||||
|
model, cpumf_v, cpumf_a);
|
||||||
|
if (ret >= 2) {
|
||||||
|
arch->family = family;
|
||||||
|
arch->model = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int s390__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
static int s390__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||||
{
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
if (!arch->initialized) {
|
if (!arch->initialized) {
|
||||||
arch->initialized = true;
|
arch->initialized = true;
|
||||||
arch->associate_instruction_ops = s390__associate_ins_ops;
|
arch->associate_instruction_ops = s390__associate_ins_ops;
|
||||||
|
if (cpuid)
|
||||||
|
err = s390__cpuid_parse(arch, cpuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* Implementation of get_cpuid().
|
* Implementation of get_cpuid().
|
||||||
*
|
*
|
||||||
* Copyright 2014 IBM Corp.
|
* Copyright IBM Corp. 2014, 2018
|
||||||
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
|
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
|
||||||
|
* Thomas Richter <tmricht@linux.vnet.ibm.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License (version 2 only)
|
* it under the terms of the GNU General Public License (version 2 only)
|
||||||
|
@ -13,16 +14,153 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "../../util/header.h"
|
#include "../../util/header.h"
|
||||||
|
#include "../../util/util.h"
|
||||||
|
|
||||||
|
#define SYSINFO_MANU "Manufacturer:"
|
||||||
|
#define SYSINFO_TYPE "Type:"
|
||||||
|
#define SYSINFO_MODEL "Model:"
|
||||||
|
#define SRVLVL_CPUMF "CPU-MF:"
|
||||||
|
#define SRVLVL_VERSION "version="
|
||||||
|
#define SRVLVL_AUTHORIZATION "authorization="
|
||||||
|
#define SYSINFO "/proc/sysinfo"
|
||||||
|
#define SRVLVL "/proc/service_levels"
|
||||||
|
|
||||||
int get_cpuid(char *buffer, size_t sz)
|
int get_cpuid(char *buffer, size_t sz)
|
||||||
{
|
{
|
||||||
const char *cpuid = "IBM/S390";
|
char *cp, *line = NULL, *line2;
|
||||||
|
char type[8], model[33], version[8], manufacturer[32], authorization[8];
|
||||||
|
int tpsize = 0, mdsize = 0, vssize = 0, mfsize = 0, atsize = 0;
|
||||||
|
int read;
|
||||||
|
unsigned long line_sz;
|
||||||
|
size_t nbytes;
|
||||||
|
FILE *sysinfo;
|
||||||
|
|
||||||
if (strlen(cpuid) + 1 > sz)
|
/*
|
||||||
|
* Scan /proc/sysinfo line by line and read out values for
|
||||||
|
* Manufacturer:, Type: and Model:, for example:
|
||||||
|
* Manufacturer: IBM
|
||||||
|
* Type: 2964
|
||||||
|
* Model: 702 N96
|
||||||
|
* The first word is the Model Capacity and the second word is
|
||||||
|
* Model (can be omitted). Both words have a maximum size of 16
|
||||||
|
* bytes.
|
||||||
|
*/
|
||||||
|
memset(manufacturer, 0, sizeof(manufacturer));
|
||||||
|
memset(type, 0, sizeof(type));
|
||||||
|
memset(model, 0, sizeof(model));
|
||||||
|
memset(version, 0, sizeof(version));
|
||||||
|
memset(authorization, 0, sizeof(authorization));
|
||||||
|
|
||||||
|
sysinfo = fopen(SYSINFO, "r");
|
||||||
|
if (sysinfo == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
strcpy(buffer, cpuid);
|
while ((read = getline(&line, &line_sz, sysinfo)) != -1) {
|
||||||
return 0;
|
if (!strncmp(line, SYSINFO_MANU, strlen(SYSINFO_MANU))) {
|
||||||
|
line2 = line + strlen(SYSINFO_MANU);
|
||||||
|
|
||||||
|
while ((cp = strtok_r(line2, "\n ", &line2))) {
|
||||||
|
mfsize += scnprintf(manufacturer + mfsize,
|
||||||
|
sizeof(manufacturer) - mfsize, "%s", cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp(line, SYSINFO_TYPE, strlen(SYSINFO_TYPE))) {
|
||||||
|
line2 = line + strlen(SYSINFO_TYPE);
|
||||||
|
|
||||||
|
while ((cp = strtok_r(line2, "\n ", &line2))) {
|
||||||
|
tpsize += scnprintf(type + tpsize,
|
||||||
|
sizeof(type) - tpsize, "%s", cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp(line, SYSINFO_MODEL, strlen(SYSINFO_MODEL))) {
|
||||||
|
line2 = line + strlen(SYSINFO_MODEL);
|
||||||
|
|
||||||
|
while ((cp = strtok_r(line2, "\n ", &line2))) {
|
||||||
|
mdsize += scnprintf(model + mdsize, sizeof(model) - mdsize,
|
||||||
|
"%s%s", model[0] ? "," : "", cp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(sysinfo);
|
||||||
|
|
||||||
|
/* Missing manufacturer, type or model information should not happen */
|
||||||
|
if (!manufacturer[0] || !type[0] || !model[0])
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan /proc/service_levels and return the CPU-MF counter facility
|
||||||
|
* version number and authorization level.
|
||||||
|
* Optional, does not exist on z/VM guests.
|
||||||
|
*/
|
||||||
|
sysinfo = fopen(SRVLVL, "r");
|
||||||
|
if (sysinfo == NULL)
|
||||||
|
goto skip_sysinfo;
|
||||||
|
while ((read = getline(&line, &line_sz, sysinfo)) != -1) {
|
||||||
|
if (strncmp(line, SRVLVL_CPUMF, strlen(SRVLVL_CPUMF)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
line2 = line + strlen(SRVLVL_CPUMF);
|
||||||
|
while ((cp = strtok_r(line2, "\n ", &line2))) {
|
||||||
|
if (!strncmp(cp, SRVLVL_VERSION,
|
||||||
|
strlen(SRVLVL_VERSION))) {
|
||||||
|
char *sep = strchr(cp, '=');
|
||||||
|
|
||||||
|
vssize += scnprintf(version + vssize,
|
||||||
|
sizeof(version) - vssize, "%s", sep + 1);
|
||||||
|
}
|
||||||
|
if (!strncmp(cp, SRVLVL_AUTHORIZATION,
|
||||||
|
strlen(SRVLVL_AUTHORIZATION))) {
|
||||||
|
char *sep = strchr(cp, '=');
|
||||||
|
|
||||||
|
atsize += scnprintf(authorization + atsize,
|
||||||
|
sizeof(authorization) - atsize, "%s", sep + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(sysinfo);
|
||||||
|
|
||||||
|
skip_sysinfo:
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
if (version[0] && authorization[0] )
|
||||||
|
nbytes = snprintf(buffer, sz, "%s,%s,%s,%s,%s",
|
||||||
|
manufacturer, type, model, version,
|
||||||
|
authorization);
|
||||||
|
else
|
||||||
|
nbytes = snprintf(buffer, sz, "%s,%s,%s", manufacturer, type,
|
||||||
|
model);
|
||||||
|
return (nbytes >= sz) ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
|
||||||
|
{
|
||||||
|
char *buf = malloc(128);
|
||||||
|
|
||||||
|
if (buf && get_cpuid(buf, 128) < 0)
|
||||||
|
zfree(&buf);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare the cpuid string returned by get_cpuid() function
|
||||||
|
* with the name generated by the jevents file read from
|
||||||
|
* pmu-events/arch/s390/mapfile.csv.
|
||||||
|
*
|
||||||
|
* Parameter mapcpuid is the cpuid as stored in the
|
||||||
|
* pmu-events/arch/s390/mapfile.csv. This is just the type number.
|
||||||
|
* Parameter cpuid is the cpuid returned by function get_cpuid().
|
||||||
|
*/
|
||||||
|
int strcmp_cpuid_str(const char *mapcpuid, const char *cpuid)
|
||||||
|
{
|
||||||
|
char *cp = strchr(cpuid, ',');
|
||||||
|
|
||||||
|
if (cp == NULL)
|
||||||
|
return -1;
|
||||||
|
return strncmp(cp + 1, mapcpuid, strlen(mapcpuid));
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
|
||||||
union perf_event *event;
|
union perf_event *event;
|
||||||
u64 test_tsc, comm1_tsc, comm2_tsc;
|
u64 test_tsc, comm1_tsc, comm2_tsc;
|
||||||
u64 test_time, comm1_time = 0, comm2_time = 0;
|
u64 test_time, comm1_time = 0, comm2_time = 0;
|
||||||
|
struct perf_mmap *md;
|
||||||
|
|
||||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||||
CHECK_NOT_NULL__(threads);
|
CHECK_NOT_NULL__(threads);
|
||||||
|
@ -109,7 +110,11 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
|
||||||
perf_evlist__disable(evlist);
|
perf_evlist__disable(evlist);
|
||||||
|
|
||||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
md = &evlist->mmap[i];
|
||||||
|
if (perf_mmap__read_init(md) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while ((event = perf_mmap__read_event(md)) != NULL) {
|
||||||
struct perf_sample sample;
|
struct perf_sample sample;
|
||||||
|
|
||||||
if (event->header.type != PERF_RECORD_COMM ||
|
if (event->header.type != PERF_RECORD_COMM ||
|
||||||
|
@ -128,8 +133,9 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
|
||||||
comm2_time = sample.time;
|
comm2_time = sample.time;
|
||||||
}
|
}
|
||||||
next_event:
|
next_event:
|
||||||
perf_evlist__mmap_consume(evlist, i);
|
perf_mmap__consume(md);
|
||||||
}
|
}
|
||||||
|
perf_mmap__read_done(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!comm1_time || !comm2_time)
|
if (!comm1_time || !comm2_time)
|
||||||
|
|
|
@ -37,15 +37,11 @@ struct auxtrace_record *auxtrace_record__init_intel(struct perf_evlist *evlist,
|
||||||
intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME);
|
intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME);
|
||||||
intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME);
|
intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME);
|
||||||
|
|
||||||
if (evlist) {
|
evlist__for_each_entry(evlist, evsel) {
|
||||||
evlist__for_each_entry(evlist, evsel) {
|
if (intel_pt_pmu && evsel->attr.type == intel_pt_pmu->type)
|
||||||
if (intel_pt_pmu &&
|
found_pt = true;
|
||||||
evsel->attr.type == intel_pt_pmu->type)
|
if (intel_bts_pmu && evsel->attr.type == intel_bts_pmu->type)
|
||||||
found_pt = true;
|
found_bts = true;
|
||||||
if (intel_bts_pmu &&
|
|
||||||
evsel->attr.type == intel_bts_pmu->type)
|
|
||||||
found_bts = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found_pt && found_bts) {
|
if (found_pt && found_bts) {
|
||||||
|
|
|
@ -40,10 +40,11 @@
|
||||||
struct perf_annotate {
|
struct perf_annotate {
|
||||||
struct perf_tool tool;
|
struct perf_tool tool;
|
||||||
struct perf_session *session;
|
struct perf_session *session;
|
||||||
bool use_tui, use_stdio, use_gtk;
|
bool use_tui, use_stdio, use_stdio2, use_gtk;
|
||||||
bool full_paths;
|
bool full_paths;
|
||||||
bool print_line;
|
bool print_line;
|
||||||
bool skip_missing;
|
bool skip_missing;
|
||||||
|
bool has_br_stack;
|
||||||
const char *sym_hist_filter;
|
const char *sym_hist_filter;
|
||||||
const char *cpu_list;
|
const char *cpu_list;
|
||||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||||
|
@ -146,16 +147,78 @@ static void process_branch_stack(struct branch_stack *bs, struct addr_location *
|
||||||
free(bi);
|
free(bi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hist_iter__branch_callback(struct hist_entry_iter *iter,
|
||||||
|
struct addr_location *al __maybe_unused,
|
||||||
|
bool single __maybe_unused,
|
||||||
|
void *arg __maybe_unused)
|
||||||
|
{
|
||||||
|
struct hist_entry *he = iter->he;
|
||||||
|
struct branch_info *bi;
|
||||||
|
struct perf_sample *sample = iter->sample;
|
||||||
|
struct perf_evsel *evsel = iter->evsel;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
hist__account_cycles(sample->branch_stack, al, sample, false);
|
||||||
|
|
||||||
|
bi = he->branch_info;
|
||||||
|
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel->idx);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = addr_map_symbol__inc_samples(&bi->to, sample, evsel->idx);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_branch_callback(struct perf_evsel *evsel,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct addr_location *al __maybe_unused,
|
||||||
|
struct perf_annotate *ann,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
struct hist_entry_iter iter = {
|
||||||
|
.evsel = evsel,
|
||||||
|
.sample = sample,
|
||||||
|
.add_entry_cb = hist_iter__branch_callback,
|
||||||
|
.hide_unresolved = symbol_conf.hide_unresolved,
|
||||||
|
.ops = &hist_iter_branch,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct addr_location a;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (machine__resolve(machine, &a, sample) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (a.sym == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (a.map != NULL)
|
||||||
|
a.map->dso->hit = 1;
|
||||||
|
|
||||||
|
ret = hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool has_annotation(struct perf_annotate *ann)
|
||||||
|
{
|
||||||
|
return ui__has_annotation() || ann->use_stdio2;
|
||||||
|
}
|
||||||
|
|
||||||
static int perf_evsel__add_sample(struct perf_evsel *evsel,
|
static int perf_evsel__add_sample(struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
struct addr_location *al,
|
struct addr_location *al,
|
||||||
struct perf_annotate *ann)
|
struct perf_annotate *ann,
|
||||||
|
struct machine *machine)
|
||||||
{
|
{
|
||||||
struct hists *hists = evsel__hists(evsel);
|
struct hists *hists = evsel__hists(evsel);
|
||||||
struct hist_entry *he;
|
struct hist_entry *he;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (ann->sym_hist_filter != NULL &&
|
if ((!ann->has_br_stack || !has_annotation(ann)) &&
|
||||||
|
ann->sym_hist_filter != NULL &&
|
||||||
(al->sym == NULL ||
|
(al->sym == NULL ||
|
||||||
strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
|
strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
|
||||||
/* We're only interested in a symbol named sym_hist_filter */
|
/* We're only interested in a symbol named sym_hist_filter */
|
||||||
|
@ -178,6 +241,9 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
|
||||||
*/
|
*/
|
||||||
process_branch_stack(sample->branch_stack, al, sample);
|
process_branch_stack(sample->branch_stack, al, sample);
|
||||||
|
|
||||||
|
if (ann->has_br_stack && has_annotation(ann))
|
||||||
|
return process_branch_callback(evsel, sample, al, ann, machine);
|
||||||
|
|
||||||
he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
|
he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -206,7 +272,8 @@ static int process_sample_event(struct perf_tool *tool,
|
||||||
if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap))
|
if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap))
|
||||||
goto out_put;
|
goto out_put;
|
||||||
|
|
||||||
if (!al.filtered && perf_evsel__add_sample(evsel, sample, &al, ann)) {
|
if (!al.filtered &&
|
||||||
|
perf_evsel__add_sample(evsel, sample, &al, ann, machine)) {
|
||||||
pr_warning("problem incrementing symbol count, "
|
pr_warning("problem incrementing symbol count, "
|
||||||
"skipping event\n");
|
"skipping event\n");
|
||||||
ret = -1;
|
ret = -1;
|
||||||
|
@ -220,8 +287,11 @@ static int hist_entry__tty_annotate(struct hist_entry *he,
|
||||||
struct perf_evsel *evsel,
|
struct perf_evsel *evsel,
|
||||||
struct perf_annotate *ann)
|
struct perf_annotate *ann)
|
||||||
{
|
{
|
||||||
return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
|
if (!ann->use_stdio2)
|
||||||
ann->print_line, ann->full_paths, 0, 0);
|
return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
|
||||||
|
ann->print_line, ann->full_paths, 0, 0);
|
||||||
|
return symbol__tty_annotate2(he->ms.sym, he->ms.map, evsel,
|
||||||
|
ann->print_line, ann->full_paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hists__find_annotations(struct hists *hists,
|
static void hists__find_annotations(struct hists *hists,
|
||||||
|
@ -238,6 +308,10 @@ static void hists__find_annotations(struct hists *hists,
|
||||||
if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
|
if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
|
||||||
goto find_next;
|
goto find_next;
|
||||||
|
|
||||||
|
if (ann->sym_hist_filter &&
|
||||||
|
(strcmp(he->ms.sym->name, ann->sym_hist_filter) != 0))
|
||||||
|
goto find_next;
|
||||||
|
|
||||||
notes = symbol__annotation(he->ms.sym);
|
notes = symbol__annotation(he->ms.sym);
|
||||||
if (notes->src == NULL) {
|
if (notes->src == NULL) {
|
||||||
find_next:
|
find_next:
|
||||||
|
@ -269,6 +343,7 @@ find_next:
|
||||||
nd = rb_next(nd);
|
nd = rb_next(nd);
|
||||||
} else if (use_browser == 1) {
|
} else if (use_browser == 1) {
|
||||||
key = hist_entry__tui_annotate(he, evsel, NULL);
|
key = hist_entry__tui_annotate(he, evsel, NULL);
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case -1:
|
case -1:
|
||||||
if (!ann->skip_missing)
|
if (!ann->skip_missing)
|
||||||
|
@ -420,6 +495,9 @@ int cmd_annotate(int argc, const char **argv)
|
||||||
OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"),
|
OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"),
|
||||||
OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"),
|
OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"),
|
||||||
OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),
|
OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),
|
||||||
|
OPT_BOOLEAN(0, "stdio2", &annotate.use_stdio2, "Use the stdio interface"),
|
||||||
|
OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux,
|
||||||
|
"don't load vmlinux even if found"),
|
||||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||||
"file", "vmlinux pathname"),
|
"file", "vmlinux pathname"),
|
||||||
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
|
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
|
||||||
|
@ -489,20 +567,22 @@ int cmd_annotate(int argc, const char **argv)
|
||||||
if (annotate.session == NULL)
|
if (annotate.session == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
annotate.has_br_stack = perf_header__has_feat(&annotate.session->header,
|
||||||
|
HEADER_BRANCH_STACK);
|
||||||
|
|
||||||
ret = symbol__annotation_init();
|
ret = symbol__annotation_init();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out_delete;
|
goto out_delete;
|
||||||
|
|
||||||
|
annotation_config__init();
|
||||||
|
|
||||||
symbol_conf.try_vmlinux_path = true;
|
symbol_conf.try_vmlinux_path = true;
|
||||||
|
|
||||||
ret = symbol__init(&annotate.session->header.env);
|
ret = symbol__init(&annotate.session->header.env);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out_delete;
|
goto out_delete;
|
||||||
|
|
||||||
if (setup_sorting(NULL) < 0)
|
if (annotate.use_stdio || annotate.use_stdio2)
|
||||||
usage_with_options(annotate_usage, options);
|
|
||||||
|
|
||||||
if (annotate.use_stdio)
|
|
||||||
use_browser = 0;
|
use_browser = 0;
|
||||||
else if (annotate.use_tui)
|
else if (annotate.use_tui)
|
||||||
use_browser = 1;
|
use_browser = 1;
|
||||||
|
@ -511,6 +591,15 @@ int cmd_annotate(int argc, const char **argv)
|
||||||
|
|
||||||
setup_browser(true);
|
setup_browser(true);
|
||||||
|
|
||||||
|
if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack) {
|
||||||
|
sort__mode = SORT_MODE__BRANCH;
|
||||||
|
if (setup_sorting(annotate.session->evlist) < 0)
|
||||||
|
usage_with_options(annotate_usage, options);
|
||||||
|
} else {
|
||||||
|
if (setup_sorting(NULL) < 0)
|
||||||
|
usage_with_options(annotate_usage, options);
|
||||||
|
}
|
||||||
|
|
||||||
ret = __cmd_annotate(&annotate);
|
ret = __cmd_annotate(&annotate);
|
||||||
|
|
||||||
out_delete:
|
out_delete:
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "evsel.h"
|
#include "evsel.h"
|
||||||
#include "ui/browsers/hists.h"
|
#include "ui/browsers/hists.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
#include "mem2node.h"
|
||||||
|
|
||||||
struct c2c_hists {
|
struct c2c_hists {
|
||||||
struct hists hists;
|
struct hists hists;
|
||||||
|
@ -49,6 +50,7 @@ struct c2c_hist_entry {
|
||||||
struct c2c_hists *hists;
|
struct c2c_hists *hists;
|
||||||
struct c2c_stats stats;
|
struct c2c_stats stats;
|
||||||
unsigned long *cpuset;
|
unsigned long *cpuset;
|
||||||
|
unsigned long *nodeset;
|
||||||
struct c2c_stats *node_stats;
|
struct c2c_stats *node_stats;
|
||||||
unsigned int cacheline_idx;
|
unsigned int cacheline_idx;
|
||||||
|
|
||||||
|
@ -59,6 +61,11 @@ struct c2c_hist_entry {
|
||||||
* because of its callchain dynamic entry
|
* because of its callchain dynamic entry
|
||||||
*/
|
*/
|
||||||
struct hist_entry he;
|
struct hist_entry he;
|
||||||
|
|
||||||
|
unsigned long paddr;
|
||||||
|
unsigned long paddr_cnt;
|
||||||
|
bool paddr_zero;
|
||||||
|
char *nodestr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static char const *coalesce_default = "pid,iaddr";
|
static char const *coalesce_default = "pid,iaddr";
|
||||||
|
@ -66,6 +73,7 @@ static char const *coalesce_default = "pid,iaddr";
|
||||||
struct perf_c2c {
|
struct perf_c2c {
|
||||||
struct perf_tool tool;
|
struct perf_tool tool;
|
||||||
struct c2c_hists hists;
|
struct c2c_hists hists;
|
||||||
|
struct mem2node mem2node;
|
||||||
|
|
||||||
unsigned long **nodes;
|
unsigned long **nodes;
|
||||||
int nodes_cnt;
|
int nodes_cnt;
|
||||||
|
@ -123,6 +131,10 @@ static void *c2c_he_zalloc(size_t size)
|
||||||
if (!c2c_he->cpuset)
|
if (!c2c_he->cpuset)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
c2c_he->nodeset = bitmap_alloc(c2c.nodes_cnt);
|
||||||
|
if (!c2c_he->nodeset)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
|
c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
|
||||||
if (!c2c_he->node_stats)
|
if (!c2c_he->node_stats)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -145,6 +157,8 @@ static void c2c_he_free(void *he)
|
||||||
}
|
}
|
||||||
|
|
||||||
free(c2c_he->cpuset);
|
free(c2c_he->cpuset);
|
||||||
|
free(c2c_he->nodeset);
|
||||||
|
free(c2c_he->nodestr);
|
||||||
free(c2c_he->node_stats);
|
free(c2c_he->node_stats);
|
||||||
free(c2c_he);
|
free(c2c_he);
|
||||||
}
|
}
|
||||||
|
@ -194,6 +208,28 @@ static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
|
||||||
set_bit(sample->cpu, c2c_he->cpuset);
|
set_bit(sample->cpu, c2c_he->cpuset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void c2c_he__set_node(struct c2c_hist_entry *c2c_he,
|
||||||
|
struct perf_sample *sample)
|
||||||
|
{
|
||||||
|
int node;
|
||||||
|
|
||||||
|
if (!sample->phys_addr) {
|
||||||
|
c2c_he->paddr_zero = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = mem2node__node(&c2c.mem2node, sample->phys_addr);
|
||||||
|
if (WARN_ONCE(node < 0, "WARNING: failed to find node\n"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
set_bit(node, c2c_he->nodeset);
|
||||||
|
|
||||||
|
if (c2c_he->paddr != sample->phys_addr) {
|
||||||
|
c2c_he->paddr_cnt++;
|
||||||
|
c2c_he->paddr = sample->phys_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void compute_stats(struct c2c_hist_entry *c2c_he,
|
static void compute_stats(struct c2c_hist_entry *c2c_he,
|
||||||
struct c2c_stats *stats,
|
struct c2c_stats *stats,
|
||||||
u64 weight)
|
u64 weight)
|
||||||
|
@ -237,9 +273,12 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||||
if (mi == NULL)
|
if (mi == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
mi_dup = memdup(mi, sizeof(*mi));
|
/*
|
||||||
if (!mi_dup)
|
* The mi object is released in hists__add_entry_ops,
|
||||||
goto free_mi;
|
* if it gets sorted out into existing data, so we need
|
||||||
|
* to take the copy now.
|
||||||
|
*/
|
||||||
|
mi_dup = mem_info__get(mi);
|
||||||
|
|
||||||
c2c_decode_stats(&stats, mi);
|
c2c_decode_stats(&stats, mi);
|
||||||
|
|
||||||
|
@ -247,13 +286,14 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||||
&al, NULL, NULL, mi,
|
&al, NULL, NULL, mi,
|
||||||
sample, true);
|
sample, true);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
goto free_mi_dup;
|
goto free_mi;
|
||||||
|
|
||||||
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||||
c2c_add_stats(&c2c_he->stats, &stats);
|
c2c_add_stats(&c2c_he->stats, &stats);
|
||||||
c2c_add_stats(&c2c_hists->stats, &stats);
|
c2c_add_stats(&c2c_hists->stats, &stats);
|
||||||
|
|
||||||
c2c_he__set_cpu(c2c_he, sample);
|
c2c_he__set_cpu(c2c_he, sample);
|
||||||
|
c2c_he__set_node(c2c_he, sample);
|
||||||
|
|
||||||
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
|
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
|
||||||
ret = hist_entry__append_callchain(he, sample);
|
ret = hist_entry__append_callchain(he, sample);
|
||||||
|
@ -272,19 +312,15 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||||
|
|
||||||
mi = mi_dup;
|
mi = mi_dup;
|
||||||
|
|
||||||
mi_dup = memdup(mi, sizeof(*mi));
|
|
||||||
if (!mi_dup)
|
|
||||||
goto free_mi;
|
|
||||||
|
|
||||||
c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2);
|
c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2);
|
||||||
if (!c2c_hists)
|
if (!c2c_hists)
|
||||||
goto free_mi_dup;
|
goto free_mi;
|
||||||
|
|
||||||
he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
|
he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
|
||||||
&al, NULL, NULL, mi,
|
&al, NULL, NULL, mi,
|
||||||
sample, true);
|
sample, true);
|
||||||
if (he == NULL)
|
if (he == NULL)
|
||||||
goto free_mi_dup;
|
goto free_mi;
|
||||||
|
|
||||||
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||||
c2c_add_stats(&c2c_he->stats, &stats);
|
c2c_add_stats(&c2c_he->stats, &stats);
|
||||||
|
@ -294,6 +330,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||||
compute_stats(c2c_he, &stats, sample->weight);
|
compute_stats(c2c_he, &stats, sample->weight);
|
||||||
|
|
||||||
c2c_he__set_cpu(c2c_he, sample);
|
c2c_he__set_cpu(c2c_he, sample);
|
||||||
|
c2c_he__set_node(c2c_he, sample);
|
||||||
|
|
||||||
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
|
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
|
||||||
ret = hist_entry__append_callchain(he, sample);
|
ret = hist_entry__append_callchain(he, sample);
|
||||||
|
@ -303,10 +340,9 @@ out:
|
||||||
addr_location__put(&al);
|
addr_location__put(&al);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
free_mi_dup:
|
|
||||||
free(mi_dup);
|
|
||||||
free_mi:
|
free_mi:
|
||||||
free(mi);
|
mem_info__put(mi_dup);
|
||||||
|
mem_info__put(mi);
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -457,6 +493,31 @@ static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||||
return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
|
return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dcacheline_node_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||||
|
struct hist_entry *he)
|
||||||
|
{
|
||||||
|
struct c2c_hist_entry *c2c_he;
|
||||||
|
int width = c2c_width(fmt, hpp, he->hists);
|
||||||
|
|
||||||
|
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||||
|
if (WARN_ON_ONCE(!c2c_he->nodestr))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return scnprintf(hpp->buf, hpp->size, "%*s", width, c2c_he->nodestr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dcacheline_node_count(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||||
|
struct hist_entry *he)
|
||||||
|
{
|
||||||
|
struct c2c_hist_entry *c2c_he;
|
||||||
|
int width = c2c_width(fmt, hpp, he->hists);
|
||||||
|
|
||||||
|
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||||
|
return scnprintf(hpp->buf, hpp->size, "%*lu", width, c2c_he->paddr_cnt);
|
||||||
|
}
|
||||||
|
|
||||||
static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||||
struct hist_entry *he)
|
struct hist_entry *he)
|
||||||
{
|
{
|
||||||
|
@ -1202,23 +1263,47 @@ cl_idx_empty_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct c2c_dimension dim_dcacheline = {
|
static struct c2c_dimension dim_dcacheline = {
|
||||||
.header = HEADER_LOW("Cacheline"),
|
.header = HEADER_SPAN("--- Cacheline ----", "Address", 2),
|
||||||
.name = "dcacheline",
|
.name = "dcacheline",
|
||||||
.cmp = dcacheline_cmp,
|
.cmp = dcacheline_cmp,
|
||||||
.entry = dcacheline_entry,
|
.entry = dcacheline_entry,
|
||||||
.width = 18,
|
.width = 18,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct c2c_header header_offset_tui = HEADER_LOW("Off");
|
static struct c2c_dimension dim_dcacheline_node = {
|
||||||
|
.header = HEADER_LOW("Node"),
|
||||||
|
.name = "dcacheline_node",
|
||||||
|
.cmp = empty_cmp,
|
||||||
|
.entry = dcacheline_node_entry,
|
||||||
|
.width = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct c2c_dimension dim_dcacheline_count = {
|
||||||
|
.header = HEADER_LOW("PA cnt"),
|
||||||
|
.name = "dcacheline_count",
|
||||||
|
.cmp = empty_cmp,
|
||||||
|
.entry = dcacheline_node_count,
|
||||||
|
.width = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct c2c_header header_offset_tui = HEADER_SPAN("-----", "Off", 2);
|
||||||
|
|
||||||
static struct c2c_dimension dim_offset = {
|
static struct c2c_dimension dim_offset = {
|
||||||
.header = HEADER_BOTH("Data address", "Offset"),
|
.header = HEADER_SPAN("--- Data address -", "Offset", 2),
|
||||||
.name = "offset",
|
.name = "offset",
|
||||||
.cmp = offset_cmp,
|
.cmp = offset_cmp,
|
||||||
.entry = offset_entry,
|
.entry = offset_entry,
|
||||||
.width = 18,
|
.width = 18,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct c2c_dimension dim_offset_node = {
|
||||||
|
.header = HEADER_LOW("Node"),
|
||||||
|
.name = "offset_node",
|
||||||
|
.cmp = empty_cmp,
|
||||||
|
.entry = dcacheline_node_entry,
|
||||||
|
.width = 4,
|
||||||
|
};
|
||||||
|
|
||||||
static struct c2c_dimension dim_iaddr = {
|
static struct c2c_dimension dim_iaddr = {
|
||||||
.header = HEADER_LOW("Code address"),
|
.header = HEADER_LOW("Code address"),
|
||||||
.name = "iaddr",
|
.name = "iaddr",
|
||||||
|
@ -1538,7 +1623,10 @@ static struct c2c_dimension dim_dcacheline_num_empty = {
|
||||||
|
|
||||||
static struct c2c_dimension *dimensions[] = {
|
static struct c2c_dimension *dimensions[] = {
|
||||||
&dim_dcacheline,
|
&dim_dcacheline,
|
||||||
|
&dim_dcacheline_node,
|
||||||
|
&dim_dcacheline_count,
|
||||||
&dim_offset,
|
&dim_offset,
|
||||||
|
&dim_offset_node,
|
||||||
&dim_iaddr,
|
&dim_iaddr,
|
||||||
&dim_tot_hitm,
|
&dim_tot_hitm,
|
||||||
&dim_lcl_hitm,
|
&dim_lcl_hitm,
|
||||||
|
@ -1841,20 +1929,56 @@ static inline int valid_hitm_or_store(struct hist_entry *he)
|
||||||
return has_hitm || c2c_he->stats.store;
|
return has_hitm || c2c_he->stats.store;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void calc_width(struct hist_entry *he)
|
static void set_node_width(struct c2c_hist_entry *c2c_he, int len)
|
||||||
|
{
|
||||||
|
struct c2c_dimension *dim;
|
||||||
|
|
||||||
|
dim = &c2c.hists == c2c_he->hists ?
|
||||||
|
&dim_dcacheline_node : &dim_offset_node;
|
||||||
|
|
||||||
|
if (len > dim->width)
|
||||||
|
dim->width = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_nodestr(struct c2c_hist_entry *c2c_he)
|
||||||
|
{
|
||||||
|
char buf[30];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (c2c_he->nodestr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (bitmap_weight(c2c_he->nodeset, c2c.nodes_cnt)) {
|
||||||
|
len = bitmap_scnprintf(c2c_he->nodeset, c2c.nodes_cnt,
|
||||||
|
buf, sizeof(buf));
|
||||||
|
} else {
|
||||||
|
len = scnprintf(buf, sizeof(buf), "N/A");
|
||||||
|
}
|
||||||
|
|
||||||
|
set_node_width(c2c_he, len);
|
||||||
|
c2c_he->nodestr = strdup(buf);
|
||||||
|
return c2c_he->nodestr ? 0 : -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calc_width(struct c2c_hist_entry *c2c_he)
|
||||||
{
|
{
|
||||||
struct c2c_hists *c2c_hists;
|
struct c2c_hists *c2c_hists;
|
||||||
|
|
||||||
c2c_hists = container_of(he->hists, struct c2c_hists, hists);
|
c2c_hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
|
||||||
hists__calc_col_len(&c2c_hists->hists, he);
|
hists__calc_col_len(&c2c_hists->hists, &c2c_he->he);
|
||||||
|
set_nodestr(c2c_he);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int filter_cb(struct hist_entry *he)
|
static int filter_cb(struct hist_entry *he)
|
||||||
{
|
{
|
||||||
|
struct c2c_hist_entry *c2c_he;
|
||||||
|
|
||||||
|
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||||
|
|
||||||
if (c2c.show_src && !he->srcline)
|
if (c2c.show_src && !he->srcline)
|
||||||
he->srcline = hist_entry__get_srcline(he);
|
he->srcline = hist_entry__get_srcline(he);
|
||||||
|
|
||||||
calc_width(he);
|
calc_width(c2c_he);
|
||||||
|
|
||||||
if (!valid_hitm_or_store(he))
|
if (!valid_hitm_or_store(he))
|
||||||
he->filtered = HIST_FILTER__C2C;
|
he->filtered = HIST_FILTER__C2C;
|
||||||
|
@ -1871,12 +1995,11 @@ static int resort_cl_cb(struct hist_entry *he)
|
||||||
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
c2c_he = container_of(he, struct c2c_hist_entry, he);
|
||||||
c2c_hists = c2c_he->hists;
|
c2c_hists = c2c_he->hists;
|
||||||
|
|
||||||
calc_width(he);
|
|
||||||
|
|
||||||
if (display && c2c_hists) {
|
if (display && c2c_hists) {
|
||||||
static unsigned int idx;
|
static unsigned int idx;
|
||||||
|
|
||||||
c2c_he->cacheline_idx = idx++;
|
c2c_he->cacheline_idx = idx++;
|
||||||
|
calc_width(c2c_he);
|
||||||
|
|
||||||
c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort);
|
c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort);
|
||||||
|
|
||||||
|
@ -2350,14 +2473,66 @@ static void perf_c2c_display(struct perf_session *session)
|
||||||
}
|
}
|
||||||
#endif /* HAVE_SLANG_SUPPORT */
|
#endif /* HAVE_SLANG_SUPPORT */
|
||||||
|
|
||||||
static void ui_quirks(void)
|
static char *fill_line(const char *orig, int len)
|
||||||
{
|
{
|
||||||
|
int i, j, olen = strlen(orig);
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
buf = zalloc(len + 1);
|
||||||
|
if (!buf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
j = len / 2 - olen / 2;
|
||||||
|
|
||||||
|
for (i = 0; i < j - 1; i++)
|
||||||
|
buf[i] = '-';
|
||||||
|
|
||||||
|
buf[i++] = ' ';
|
||||||
|
|
||||||
|
strcpy(buf + i, orig);
|
||||||
|
|
||||||
|
i += olen;
|
||||||
|
|
||||||
|
buf[i++] = ' ';
|
||||||
|
|
||||||
|
for (; i < len; i++)
|
||||||
|
buf[i] = '-';
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ui_quirks(void)
|
||||||
|
{
|
||||||
|
const char *nodestr = "Data address";
|
||||||
|
char *buf;
|
||||||
|
|
||||||
if (!c2c.use_stdio) {
|
if (!c2c.use_stdio) {
|
||||||
dim_offset.width = 5;
|
dim_offset.width = 5;
|
||||||
dim_offset.header = header_offset_tui;
|
dim_offset.header = header_offset_tui;
|
||||||
|
nodestr = "CL";
|
||||||
}
|
}
|
||||||
|
|
||||||
dim_percent_hitm.header = percent_hitm_header[c2c.display];
|
dim_percent_hitm.header = percent_hitm_header[c2c.display];
|
||||||
|
|
||||||
|
/* Fix the zero line for dcacheline column. */
|
||||||
|
buf = fill_line("Cacheline", dim_dcacheline.width +
|
||||||
|
dim_dcacheline_node.width +
|
||||||
|
dim_dcacheline_count.width + 4);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dim_dcacheline.header.line[0].text = buf;
|
||||||
|
|
||||||
|
/* Fix the zero line for offset column. */
|
||||||
|
buf = fill_line(nodestr, dim_offset.width +
|
||||||
|
dim_offset_node.width +
|
||||||
|
dim_dcacheline_count.width + 4);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dim_offset.header.line[0].text = buf;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent"
|
#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent"
|
||||||
|
@ -2473,7 +2648,7 @@ static int build_cl_output(char *cl_sort, bool no_source)
|
||||||
"percent_lcl_hitm,"
|
"percent_lcl_hitm,"
|
||||||
"percent_stores_l1hit,"
|
"percent_stores_l1hit,"
|
||||||
"percent_stores_l1miss,"
|
"percent_stores_l1miss,"
|
||||||
"offset,",
|
"offset,offset_node,dcacheline_count,",
|
||||||
add_pid ? "pid," : "",
|
add_pid ? "pid," : "",
|
||||||
add_tid ? "tid," : "",
|
add_tid ? "tid," : "",
|
||||||
add_iaddr ? "iaddr," : "",
|
add_iaddr ? "iaddr," : "",
|
||||||
|
@ -2602,17 +2777,21 @@ static int perf_c2c__report(int argc, const char **argv)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = setup_callchain(session->evlist);
|
err = mem2node__init(&c2c.mem2node, &session->header.env);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_session;
|
goto out_session;
|
||||||
|
|
||||||
|
err = setup_callchain(session->evlist);
|
||||||
|
if (err)
|
||||||
|
goto out_mem2node;
|
||||||
|
|
||||||
if (symbol__init(&session->header.env) < 0)
|
if (symbol__init(&session->header.env) < 0)
|
||||||
goto out_session;
|
goto out_mem2node;
|
||||||
|
|
||||||
/* No pipe support at the moment. */
|
/* No pipe support at the moment. */
|
||||||
if (perf_data__is_pipe(session->data)) {
|
if (perf_data__is_pipe(session->data)) {
|
||||||
pr_debug("No pipe support at the moment.\n");
|
pr_debug("No pipe support at the moment.\n");
|
||||||
goto out_session;
|
goto out_mem2node;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c2c.use_stdio)
|
if (c2c.use_stdio)
|
||||||
|
@ -2625,12 +2804,14 @@ static int perf_c2c__report(int argc, const char **argv)
|
||||||
err = perf_session__process_events(session);
|
err = perf_session__process_events(session);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("failed to process sample\n");
|
pr_err("failed to process sample\n");
|
||||||
goto out_session;
|
goto out_mem2node;
|
||||||
}
|
}
|
||||||
|
|
||||||
c2c_hists__reinit(&c2c.hists,
|
c2c_hists__reinit(&c2c.hists,
|
||||||
"cl_idx,"
|
"cl_idx,"
|
||||||
"dcacheline,"
|
"dcacheline,"
|
||||||
|
"dcacheline_node,"
|
||||||
|
"dcacheline_count,"
|
||||||
"tot_recs,"
|
"tot_recs,"
|
||||||
"percent_hitm,"
|
"percent_hitm,"
|
||||||
"tot_hitm,lcl_hitm,rmt_hitm,"
|
"tot_hitm,lcl_hitm,rmt_hitm,"
|
||||||
|
@ -2652,10 +2833,15 @@ static int perf_c2c__report(int argc, const char **argv)
|
||||||
|
|
||||||
ui_progress__finish();
|
ui_progress__finish();
|
||||||
|
|
||||||
ui_quirks();
|
if (ui_quirks()) {
|
||||||
|
pr_err("failed to setup UI\n");
|
||||||
|
goto out_mem2node;
|
||||||
|
}
|
||||||
|
|
||||||
perf_c2c_display(session);
|
perf_c2c_display(session);
|
||||||
|
|
||||||
|
out_mem2node:
|
||||||
|
mem2node__exit(&c2c.mem2node);
|
||||||
out_session:
|
out_session:
|
||||||
perf_session__delete(session);
|
perf_session__delete(session);
|
||||||
out:
|
out:
|
||||||
|
@ -2706,7 +2892,7 @@ static int perf_c2c__record(int argc, const char **argv)
|
||||||
argc = parse_options(argc, argv, options, record_mem_usage,
|
argc = parse_options(argc, argv, options, record_mem_usage,
|
||||||
PARSE_OPT_KEEP_UNKNOWN);
|
PARSE_OPT_KEEP_UNKNOWN);
|
||||||
|
|
||||||
rec_argc = argc + 10; /* max number of arguments */
|
rec_argc = argc + 11; /* max number of arguments */
|
||||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||||
if (!rec_argv)
|
if (!rec_argv)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2722,6 +2908,7 @@ static int perf_c2c__record(int argc, const char **argv)
|
||||||
rec_argv[i++] = "-W";
|
rec_argv[i++] = "-W";
|
||||||
|
|
||||||
rec_argv[i++] = "-d";
|
rec_argv[i++] = "-d";
|
||||||
|
rec_argv[i++] = "--phys-data";
|
||||||
rec_argv[i++] = "--sample-cpu";
|
rec_argv[i++] = "--sample-cpu";
|
||||||
|
|
||||||
for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
|
for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
|
||||||
|
|
|
@ -72,6 +72,7 @@ static int __write_tracing_file(const char *name, const char *val, bool append)
|
||||||
ssize_t size = strlen(val);
|
ssize_t size = strlen(val);
|
||||||
int flags = O_WRONLY;
|
int flags = O_WRONLY;
|
||||||
char errbuf[512];
|
char errbuf[512];
|
||||||
|
char *val_copy;
|
||||||
|
|
||||||
file = get_tracing_file(name);
|
file = get_tracing_file(name);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
|
@ -91,12 +92,23 @@ static int __write_tracing_file(const char *name, const char *val, bool append)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write(fd, val, size) == size)
|
/*
|
||||||
|
* Copy the original value and append a '\n'. Without this,
|
||||||
|
* the kernel can hide possible errors.
|
||||||
|
*/
|
||||||
|
val_copy = strdup(val);
|
||||||
|
if (!val_copy)
|
||||||
|
goto out_close;
|
||||||
|
val_copy[size] = '\n';
|
||||||
|
|
||||||
|
if (write(fd, val_copy, size + 1) == size + 1)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
else
|
else
|
||||||
pr_debug("write '%s' to tracing/%s failed: %s\n",
|
pr_debug("write '%s' to tracing/%s failed: %s\n",
|
||||||
val, name, str_error_r(errno, errbuf, sizeof(errbuf)));
|
val, name, str_error_r(errno, errbuf, sizeof(errbuf)));
|
||||||
|
|
||||||
|
free(val_copy);
|
||||||
|
out_close:
|
||||||
close(fd);
|
close(fd);
|
||||||
out:
|
out:
|
||||||
put_tracing_file(file);
|
put_tracing_file(file);
|
||||||
|
@ -280,8 +292,10 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
|
||||||
signal(SIGCHLD, sig_handler);
|
signal(SIGCHLD, sig_handler);
|
||||||
signal(SIGPIPE, sig_handler);
|
signal(SIGPIPE, sig_handler);
|
||||||
|
|
||||||
if (reset_tracing_files(ftrace) < 0)
|
if (reset_tracing_files(ftrace) < 0) {
|
||||||
|
pr_err("failed to reset ftrace\n");
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* reset ftrace buffer */
|
/* reset ftrace buffer */
|
||||||
if (write_tracing_file("trace", "0") < 0)
|
if (write_tracing_file("trace", "0") < 0)
|
||||||
|
|
|
@ -743,16 +743,23 @@ static bool verify_vcpu(int vcpu)
|
||||||
static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
|
static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
|
||||||
u64 *mmap_time)
|
u64 *mmap_time)
|
||||||
{
|
{
|
||||||
|
struct perf_evlist *evlist = kvm->evlist;
|
||||||
union perf_event *event;
|
union perf_event *event;
|
||||||
|
struct perf_mmap *md;
|
||||||
u64 timestamp;
|
u64 timestamp;
|
||||||
s64 n = 0;
|
s64 n = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
*mmap_time = ULLONG_MAX;
|
*mmap_time = ULLONG_MAX;
|
||||||
while ((event = perf_evlist__mmap_read(kvm->evlist, idx)) != NULL) {
|
md = &evlist->mmap[idx];
|
||||||
err = perf_evlist__parse_sample_timestamp(kvm->evlist, event, ×tamp);
|
err = perf_mmap__read_init(md);
|
||||||
|
if (err < 0)
|
||||||
|
return (err == -EAGAIN) ? 0 : -1;
|
||||||
|
|
||||||
|
while ((event = perf_mmap__read_event(md)) != NULL) {
|
||||||
|
err = perf_evlist__parse_sample_timestamp(evlist, event, ×tamp);
|
||||||
if (err) {
|
if (err) {
|
||||||
perf_evlist__mmap_consume(kvm->evlist, idx);
|
perf_mmap__consume(md);
|
||||||
pr_err("Failed to parse sample\n");
|
pr_err("Failed to parse sample\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -762,7 +769,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
|
||||||
* FIXME: Here we can't consume the event, as perf_session__queue_event will
|
* FIXME: Here we can't consume the event, as perf_session__queue_event will
|
||||||
* point to it, and it'll get possibly overwritten by the kernel.
|
* point to it, and it'll get possibly overwritten by the kernel.
|
||||||
*/
|
*/
|
||||||
perf_evlist__mmap_consume(kvm->evlist, idx);
|
perf_mmap__consume(md);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("Failed to enqueue sample: %d\n", err);
|
pr_err("Failed to enqueue sample: %d\n", err);
|
||||||
|
@ -779,6 +786,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
perf_mmap__read_done(md);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
@ -70,7 +71,6 @@ struct record {
|
||||||
struct auxtrace_record *itr;
|
struct auxtrace_record *itr;
|
||||||
struct perf_evlist *evlist;
|
struct perf_evlist *evlist;
|
||||||
struct perf_session *session;
|
struct perf_session *session;
|
||||||
const char *progname;
|
|
||||||
int realtime_prio;
|
int realtime_prio;
|
||||||
bool no_buildid;
|
bool no_buildid;
|
||||||
bool no_buildid_set;
|
bool no_buildid_set;
|
||||||
|
@ -273,6 +273,24 @@ static void record__read_auxtrace_snapshot(struct record *rec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int record__auxtrace_init(struct record *rec)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!rec->itr) {
|
||||||
|
rec->itr = auxtrace_record__init(rec->evlist, &err);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts,
|
||||||
|
rec->opts.auxtrace_snapshot_opts);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return auxtrace_parse_filters(rec->evlist);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
|
@ -293,6 +311,11 @@ int auxtrace_record__snapshot_start(struct auxtrace_record *itr __maybe_unused)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int record__auxtrace_init(struct record *rec __maybe_unused)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int record__mmap_evlist(struct record *rec,
|
static int record__mmap_evlist(struct record *rec,
|
||||||
|
@ -509,7 +532,7 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli
|
||||||
struct auxtrace_mmap *mm = &maps[i].auxtrace_mmap;
|
struct auxtrace_mmap *mm = &maps[i].auxtrace_mmap;
|
||||||
|
|
||||||
if (maps[i].base) {
|
if (maps[i].base) {
|
||||||
if (perf_mmap__push(&maps[i], overwrite, rec, record__pushfn) != 0) {
|
if (perf_mmap__push(&maps[i], rec, record__pushfn) != 0) {
|
||||||
rc = -1;
|
rc = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -731,13 +754,10 @@ static int record__synthesize(struct record *rec, bool tail)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (data->is_pipe) {
|
if (data->is_pipe) {
|
||||||
err = perf_event__synthesize_features(
|
/*
|
||||||
tool, session, rec->evlist, process_synthesized_event);
|
* We need to synthesize events first, because some
|
||||||
if (err < 0) {
|
* features works on top of them (on report side).
|
||||||
pr_err("Couldn't synthesize features.\n");
|
*/
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = perf_event__synthesize_attrs(tool, session,
|
err = perf_event__synthesize_attrs(tool, session,
|
||||||
process_synthesized_event);
|
process_synthesized_event);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
@ -745,6 +765,13 @@ static int record__synthesize(struct record *rec, bool tail)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = perf_event__synthesize_features(tool, session, rec->evlist,
|
||||||
|
process_synthesized_event);
|
||||||
|
if (err < 0) {
|
||||||
|
pr_err("Couldn't synthesize features.\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
if (have_tracepoints(&rec->evlist->entries)) {
|
if (have_tracepoints(&rec->evlist->entries)) {
|
||||||
/*
|
/*
|
||||||
* FIXME err <= 0 here actually means that
|
* FIXME err <= 0 here actually means that
|
||||||
|
@ -830,7 +857,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||||
int status = 0;
|
int status = 0;
|
||||||
unsigned long waking = 0;
|
unsigned long waking = 0;
|
||||||
const bool forks = argc > 0;
|
const bool forks = argc > 0;
|
||||||
struct machine *machine;
|
|
||||||
struct perf_tool *tool = &rec->tool;
|
struct perf_tool *tool = &rec->tool;
|
||||||
struct record_opts *opts = &rec->opts;
|
struct record_opts *opts = &rec->opts;
|
||||||
struct perf_data *data = &rec->data;
|
struct perf_data *data = &rec->data;
|
||||||
|
@ -838,8 +864,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||||
bool disabled = false, draining = false;
|
bool disabled = false, draining = false;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
rec->progname = argv[0];
|
|
||||||
|
|
||||||
atexit(record__sig_exit);
|
atexit(record__sig_exit);
|
||||||
signal(SIGCHLD, sig_handler);
|
signal(SIGCHLD, sig_handler);
|
||||||
signal(SIGINT, sig_handler);
|
signal(SIGINT, sig_handler);
|
||||||
|
@ -935,8 +959,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||||
goto out_child;
|
goto out_child;
|
||||||
}
|
}
|
||||||
|
|
||||||
machine = &session->machines.host;
|
|
||||||
|
|
||||||
err = record__synthesize(rec, false);
|
err = record__synthesize(rec, false);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out_child;
|
goto out_child;
|
||||||
|
@ -964,6 +986,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||||
* Let the child rip
|
* Let the child rip
|
||||||
*/
|
*/
|
||||||
if (forks) {
|
if (forks) {
|
||||||
|
struct machine *machine = &session->machines.host;
|
||||||
union perf_event *event;
|
union perf_event *event;
|
||||||
pid_t tgid;
|
pid_t tgid;
|
||||||
|
|
||||||
|
@ -1260,10 +1283,12 @@ static int perf_record_config(const char *var, const char *value, void *cb)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!strcmp(var, "record.call-graph"))
|
if (!strcmp(var, "record.call-graph")) {
|
||||||
var = "call-graph.record-mode"; /* fall-through */
|
var = "call-graph.record-mode";
|
||||||
|
return perf_default_config(var, value, cb);
|
||||||
|
}
|
||||||
|
|
||||||
return perf_default_config(var, value, cb);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct clockid_map {
|
struct clockid_map {
|
||||||
|
@ -1551,7 +1576,11 @@ static struct option __record_options[] = {
|
||||||
OPT_BOOLEAN(0, "tail-synthesize", &record.opts.tail_synthesize,
|
OPT_BOOLEAN(0, "tail-synthesize", &record.opts.tail_synthesize,
|
||||||
"synthesize non-sample events at the end of output"),
|
"synthesize non-sample events at the end of output"),
|
||||||
OPT_BOOLEAN(0, "overwrite", &record.opts.overwrite, "use overwrite mode"),
|
OPT_BOOLEAN(0, "overwrite", &record.opts.overwrite, "use overwrite mode"),
|
||||||
OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
|
OPT_BOOLEAN(0, "strict-freq", &record.opts.strict_freq,
|
||||||
|
"Fail if the specified frequency can't be used"),
|
||||||
|
OPT_CALLBACK('F', "freq", &record.opts, "freq or 'max'",
|
||||||
|
"profile at this frequency",
|
||||||
|
record__parse_freq),
|
||||||
OPT_CALLBACK('m', "mmap-pages", &record.opts, "pages[,pages]",
|
OPT_CALLBACK('m', "mmap-pages", &record.opts, "pages[,pages]",
|
||||||
"number of mmap data pages and AUX area tracing mmap pages",
|
"number of mmap data pages and AUX area tracing mmap pages",
|
||||||
record__parse_mmap_pages),
|
record__parse_mmap_pages),
|
||||||
|
@ -1660,6 +1689,8 @@ int cmd_record(int argc, const char **argv)
|
||||||
struct record *rec = &record;
|
struct record *rec = &record;
|
||||||
char errbuf[BUFSIZ];
|
char errbuf[BUFSIZ];
|
||||||
|
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
#ifndef HAVE_LIBBPF_SUPPORT
|
#ifndef HAVE_LIBBPF_SUPPORT
|
||||||
# define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, "NO_LIBBPF=1", c)
|
# define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, "NO_LIBBPF=1", c)
|
||||||
set_nobuild('\0', "clang-path", true);
|
set_nobuild('\0', "clang-path", true);
|
||||||
|
@ -1720,17 +1751,6 @@ int cmd_record(int argc, const char **argv)
|
||||||
alarm(rec->switch_output.time);
|
alarm(rec->switch_output.time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rec->itr) {
|
|
||||||
rec->itr = auxtrace_record__init(rec->evlist, &err);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts,
|
|
||||||
rec->opts.auxtrace_snapshot_opts);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allow aliases to facilitate the lookup of symbols for address
|
* Allow aliases to facilitate the lookup of symbols for address
|
||||||
* filters. Refer to auxtrace_parse_filters().
|
* filters. Refer to auxtrace_parse_filters().
|
||||||
|
@ -1739,7 +1759,7 @@ int cmd_record(int argc, const char **argv)
|
||||||
|
|
||||||
symbol__init(NULL);
|
symbol__init(NULL);
|
||||||
|
|
||||||
err = auxtrace_parse_filters(rec->evlist);
|
err = record__auxtrace_init(rec);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -1812,7 +1832,7 @@ int cmd_record(int argc, const char **argv)
|
||||||
err = target__validate(&rec->opts.target);
|
err = target__validate(&rec->opts.target);
|
||||||
if (err) {
|
if (err) {
|
||||||
target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
|
target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
|
||||||
ui__warning("%s", errbuf);
|
ui__warning("%s\n", errbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = target__parse_uid(&rec->opts.target);
|
err = target__parse_uid(&rec->opts.target);
|
||||||
|
|
|
@ -68,6 +68,7 @@ struct report {
|
||||||
bool header;
|
bool header;
|
||||||
bool header_only;
|
bool header_only;
|
||||||
bool nonany_branch_mode;
|
bool nonany_branch_mode;
|
||||||
|
bool group_set;
|
||||||
int max_stack;
|
int max_stack;
|
||||||
struct perf_read_values show_threads_values;
|
struct perf_read_values show_threads_values;
|
||||||
const char *pretty_printing_style;
|
const char *pretty_printing_style;
|
||||||
|
@ -193,6 +194,45 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Events in data file are not collect in groups, but we still want
|
||||||
|
* the group display. Set the artificial group and set the leader's
|
||||||
|
* forced_leader flag to notify the display code.
|
||||||
|
*/
|
||||||
|
static void setup_forced_leader(struct report *report,
|
||||||
|
struct perf_evlist *evlist)
|
||||||
|
{
|
||||||
|
if (report->group_set && !evlist->nr_groups) {
|
||||||
|
struct perf_evsel *leader = perf_evlist__first(evlist);
|
||||||
|
|
||||||
|
perf_evlist__set_leader(evlist);
|
||||||
|
leader->forced_leader = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_feature_event(struct perf_tool *tool,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_session *session __maybe_unused)
|
||||||
|
{
|
||||||
|
struct report *rep = container_of(tool, struct report, tool);
|
||||||
|
|
||||||
|
if (event->feat.feat_id < HEADER_LAST_FEATURE)
|
||||||
|
return perf_event__process_feature(tool, event, session);
|
||||||
|
|
||||||
|
if (event->feat.feat_id != HEADER_LAST_FEATURE) {
|
||||||
|
pr_err("failed: wrong feature ID: %" PRIu64 "\n",
|
||||||
|
event->feat.feat_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All features are received, we can force the
|
||||||
|
* group if needed.
|
||||||
|
*/
|
||||||
|
setup_forced_leader(rep, session->evlist);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int process_sample_event(struct perf_tool *tool,
|
static int process_sample_event(struct perf_tool *tool,
|
||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
|
@ -400,8 +440,10 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
|
||||||
|
|
||||||
nr_samples = convert_unit(nr_samples, &unit);
|
nr_samples = convert_unit(nr_samples, &unit);
|
||||||
ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
|
ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
|
||||||
if (evname != NULL)
|
if (evname != NULL) {
|
||||||
ret += fprintf(fp, " of event '%s'", evname);
|
ret += fprintf(fp, " of event%s '%s'",
|
||||||
|
evsel->nr_members > 1 ? "s" : "", evname);
|
||||||
|
}
|
||||||
|
|
||||||
if (rep->time_str)
|
if (rep->time_str)
|
||||||
ret += fprintf(fp, " (time slices: %s)", rep->time_str);
|
ret += fprintf(fp, " (time slices: %s)", rep->time_str);
|
||||||
|
@ -614,6 +656,7 @@ static int stats_print(struct report *rep)
|
||||||
static void tasks_setup(struct report *rep)
|
static void tasks_setup(struct report *rep)
|
||||||
{
|
{
|
||||||
memset(&rep->tool, 0, sizeof(rep->tool));
|
memset(&rep->tool, 0, sizeof(rep->tool));
|
||||||
|
rep->tool.ordered_events = true;
|
||||||
if (rep->mmaps_mode) {
|
if (rep->mmaps_mode) {
|
||||||
rep->tool.mmap = perf_event__process_mmap;
|
rep->tool.mmap = perf_event__process_mmap;
|
||||||
rep->tool.mmap2 = perf_event__process_mmap2;
|
rep->tool.mmap2 = perf_event__process_mmap2;
|
||||||
|
@ -954,7 +997,7 @@ int cmd_report(int argc, const char **argv)
|
||||||
.id_index = perf_event__process_id_index,
|
.id_index = perf_event__process_id_index,
|
||||||
.auxtrace_info = perf_event__process_auxtrace_info,
|
.auxtrace_info = perf_event__process_auxtrace_info,
|
||||||
.auxtrace = perf_event__process_auxtrace,
|
.auxtrace = perf_event__process_auxtrace,
|
||||||
.feature = perf_event__process_feature,
|
.feature = process_feature_event,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
.ordering_requires_timestamps = true,
|
.ordering_requires_timestamps = true,
|
||||||
},
|
},
|
||||||
|
@ -975,6 +1018,8 @@ int cmd_report(int argc, const char **argv)
|
||||||
OPT_BOOLEAN(0, "mmaps", &report.mmaps_mode, "Display recorded tasks memory maps"),
|
OPT_BOOLEAN(0, "mmaps", &report.mmaps_mode, "Display recorded tasks memory maps"),
|
||||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||||
"file", "vmlinux pathname"),
|
"file", "vmlinux pathname"),
|
||||||
|
OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux,
|
||||||
|
"don't load vmlinux even if found"),
|
||||||
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
|
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
|
||||||
"file", "kallsyms pathname"),
|
"file", "kallsyms pathname"),
|
||||||
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
|
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
|
||||||
|
@ -1056,7 +1101,7 @@ int cmd_report(int argc, const char **argv)
|
||||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||||
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
|
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
|
||||||
"Show a column with the sum of periods"),
|
"Show a column with the sum of periods"),
|
||||||
OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
|
OPT_BOOLEAN_SET(0, "group", &symbol_conf.event_group, &report.group_set,
|
||||||
"Show event group information together"),
|
"Show event group information together"),
|
||||||
OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "",
|
OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "",
|
||||||
"use branch records for per branch histogram filling",
|
"use branch records for per branch histogram filling",
|
||||||
|
@ -1173,6 +1218,8 @@ repeat:
|
||||||
has_br_stack = perf_header__has_feat(&session->header,
|
has_br_stack = perf_header__has_feat(&session->header,
|
||||||
HEADER_BRANCH_STACK);
|
HEADER_BRANCH_STACK);
|
||||||
|
|
||||||
|
setup_forced_leader(&report, session->evlist);
|
||||||
|
|
||||||
if (itrace_synth_opts.last_branch)
|
if (itrace_synth_opts.last_branch)
|
||||||
has_br_stack = true;
|
has_br_stack = true;
|
||||||
|
|
||||||
|
@ -1295,6 +1342,7 @@ repeat:
|
||||||
symbol_conf.priv_size += sizeof(u32);
|
symbol_conf.priv_size += sizeof(u32);
|
||||||
symbol_conf.sort_by_name = true;
|
symbol_conf.sort_by_name = true;
|
||||||
}
|
}
|
||||||
|
annotation_config__init();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbol__init(&session->header.env) < 0)
|
if (symbol__init(&session->header.env) < 0)
|
||||||
|
@ -1332,6 +1380,15 @@ repeat:
|
||||||
report.range_num = 1;
|
report.range_num = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (session->tevent.pevent &&
|
||||||
|
pevent_set_function_resolver(session->tevent.pevent,
|
||||||
|
machine__resolve_kernel_addr,
|
||||||
|
&session->machines.host) < 0) {
|
||||||
|
pr_err("%s: failed to set libtraceevent function resolver\n",
|
||||||
|
__func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
sort__setup_elide(stdout);
|
sort__setup_elide(stdout);
|
||||||
|
|
||||||
ret = __cmd_report(&report);
|
ret = __cmd_report(&report);
|
||||||
|
|
|
@ -254,6 +254,10 @@ struct thread_runtime {
|
||||||
u64 total_delay_time;
|
u64 total_delay_time;
|
||||||
|
|
||||||
int last_state;
|
int last_state;
|
||||||
|
|
||||||
|
char shortname[3];
|
||||||
|
bool comm_changed;
|
||||||
|
|
||||||
u64 migrations;
|
u64 migrations;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -897,6 +901,37 @@ struct sort_dimension {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handle runtime stats saved per thread
|
||||||
|
*/
|
||||||
|
static struct thread_runtime *thread__init_runtime(struct thread *thread)
|
||||||
|
{
|
||||||
|
struct thread_runtime *r;
|
||||||
|
|
||||||
|
r = zalloc(sizeof(struct thread_runtime));
|
||||||
|
if (!r)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
init_stats(&r->run_stats);
|
||||||
|
thread__set_priv(thread, r);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct thread_runtime *thread__get_runtime(struct thread *thread)
|
||||||
|
{
|
||||||
|
struct thread_runtime *tr;
|
||||||
|
|
||||||
|
tr = thread__priv(thread);
|
||||||
|
if (tr == NULL) {
|
||||||
|
tr = thread__init_runtime(thread);
|
||||||
|
if (tr == NULL)
|
||||||
|
pr_debug("Failed to malloc memory for runtime data.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r)
|
thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r)
|
||||||
{
|
{
|
||||||
|
@ -1480,6 +1515,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||||
{
|
{
|
||||||
const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
|
const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
|
||||||
struct thread *sched_in;
|
struct thread *sched_in;
|
||||||
|
struct thread_runtime *tr;
|
||||||
int new_shortname;
|
int new_shortname;
|
||||||
u64 timestamp0, timestamp = sample->time;
|
u64 timestamp0, timestamp = sample->time;
|
||||||
s64 delta;
|
s64 delta;
|
||||||
|
@ -1519,22 +1555,28 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||||
if (sched_in == NULL)
|
if (sched_in == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
tr = thread__get_runtime(sched_in);
|
||||||
|
if (tr == NULL) {
|
||||||
|
thread__put(sched_in);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
sched->curr_thread[this_cpu] = thread__get(sched_in);
|
sched->curr_thread[this_cpu] = thread__get(sched_in);
|
||||||
|
|
||||||
printf(" ");
|
printf(" ");
|
||||||
|
|
||||||
new_shortname = 0;
|
new_shortname = 0;
|
||||||
if (!sched_in->shortname[0]) {
|
if (!tr->shortname[0]) {
|
||||||
if (!strcmp(thread__comm_str(sched_in), "swapper")) {
|
if (!strcmp(thread__comm_str(sched_in), "swapper")) {
|
||||||
/*
|
/*
|
||||||
* Don't allocate a letter-number for swapper:0
|
* Don't allocate a letter-number for swapper:0
|
||||||
* as a shortname. Instead, we use '.' for it.
|
* as a shortname. Instead, we use '.' for it.
|
||||||
*/
|
*/
|
||||||
sched_in->shortname[0] = '.';
|
tr->shortname[0] = '.';
|
||||||
sched_in->shortname[1] = ' ';
|
tr->shortname[1] = ' ';
|
||||||
} else {
|
} else {
|
||||||
sched_in->shortname[0] = sched->next_shortname1;
|
tr->shortname[0] = sched->next_shortname1;
|
||||||
sched_in->shortname[1] = sched->next_shortname2;
|
tr->shortname[1] = sched->next_shortname2;
|
||||||
|
|
||||||
if (sched->next_shortname1 < 'Z') {
|
if (sched->next_shortname1 < 'Z') {
|
||||||
sched->next_shortname1++;
|
sched->next_shortname1++;
|
||||||
|
@ -1552,6 +1594,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||||
for (i = 0; i < cpus_nr; i++) {
|
for (i = 0; i < cpus_nr; i++) {
|
||||||
int cpu = sched->map.comp ? sched->map.comp_cpus[i] : i;
|
int cpu = sched->map.comp ? sched->map.comp_cpus[i] : i;
|
||||||
struct thread *curr_thread = sched->curr_thread[cpu];
|
struct thread *curr_thread = sched->curr_thread[cpu];
|
||||||
|
struct thread_runtime *curr_tr;
|
||||||
const char *pid_color = color;
|
const char *pid_color = color;
|
||||||
const char *cpu_color = color;
|
const char *cpu_color = color;
|
||||||
|
|
||||||
|
@ -1569,9 +1612,14 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||||
else
|
else
|
||||||
color_fprintf(stdout, cpu_color, "*");
|
color_fprintf(stdout, cpu_color, "*");
|
||||||
|
|
||||||
if (sched->curr_thread[cpu])
|
if (sched->curr_thread[cpu]) {
|
||||||
color_fprintf(stdout, pid_color, "%2s ", sched->curr_thread[cpu]->shortname);
|
curr_tr = thread__get_runtime(sched->curr_thread[cpu]);
|
||||||
else
|
if (curr_tr == NULL) {
|
||||||
|
thread__put(sched_in);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
color_fprintf(stdout, pid_color, "%2s ", curr_tr->shortname);
|
||||||
|
} else
|
||||||
color_fprintf(stdout, color, " ");
|
color_fprintf(stdout, color, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1580,14 +1628,15 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||||
|
|
||||||
timestamp__scnprintf_usec(timestamp, stimestamp, sizeof(stimestamp));
|
timestamp__scnprintf_usec(timestamp, stimestamp, sizeof(stimestamp));
|
||||||
color_fprintf(stdout, color, " %12s secs ", stimestamp);
|
color_fprintf(stdout, color, " %12s secs ", stimestamp);
|
||||||
if (new_shortname || (verbose > 0 && sched_in->tid)) {
|
if (new_shortname || tr->comm_changed || (verbose > 0 && sched_in->tid)) {
|
||||||
const char *pid_color = color;
|
const char *pid_color = color;
|
||||||
|
|
||||||
if (thread__has_color(sched_in))
|
if (thread__has_color(sched_in))
|
||||||
pid_color = COLOR_PIDS;
|
pid_color = COLOR_PIDS;
|
||||||
|
|
||||||
color_fprintf(stdout, pid_color, "%s => %s:%d",
|
color_fprintf(stdout, pid_color, "%s => %s:%d",
|
||||||
sched_in->shortname, thread__comm_str(sched_in), sched_in->tid);
|
tr->shortname, thread__comm_str(sched_in), sched_in->tid);
|
||||||
|
tr->comm_changed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sched->map.comp && new_cpu)
|
if (sched->map.comp && new_cpu)
|
||||||
|
@ -1691,6 +1740,37 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int perf_sched__process_comm(struct perf_tool *tool __maybe_unused,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
struct thread_runtime *tr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = perf_event__process_comm(tool, event, sample, machine);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
thread = machine__find_thread(machine, sample->pid, sample->tid);
|
||||||
|
if (!thread) {
|
||||||
|
pr_err("Internal error: can't find thread\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr = thread__get_runtime(thread);
|
||||||
|
if (tr == NULL) {
|
||||||
|
thread__put(thread);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr->comm_changed = true;
|
||||||
|
thread__put(thread);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int perf_sched__read_events(struct perf_sched *sched)
|
static int perf_sched__read_events(struct perf_sched *sched)
|
||||||
{
|
{
|
||||||
const struct perf_evsel_str_handler handlers[] = {
|
const struct perf_evsel_str_handler handlers[] = {
|
||||||
|
@ -2200,37 +2280,6 @@ static void save_idle_callchain(struct idle_thread_runtime *itr,
|
||||||
callchain_cursor__copy(&itr->cursor, &callchain_cursor);
|
callchain_cursor__copy(&itr->cursor, &callchain_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* handle runtime stats saved per thread
|
|
||||||
*/
|
|
||||||
static struct thread_runtime *thread__init_runtime(struct thread *thread)
|
|
||||||
{
|
|
||||||
struct thread_runtime *r;
|
|
||||||
|
|
||||||
r = zalloc(sizeof(struct thread_runtime));
|
|
||||||
if (!r)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
init_stats(&r->run_stats);
|
|
||||||
thread__set_priv(thread, r);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct thread_runtime *thread__get_runtime(struct thread *thread)
|
|
||||||
{
|
|
||||||
struct thread_runtime *tr;
|
|
||||||
|
|
||||||
tr = thread__priv(thread);
|
|
||||||
if (tr == NULL) {
|
|
||||||
tr = thread__init_runtime(thread);
|
|
||||||
if (tr == NULL)
|
|
||||||
pr_debug("Failed to malloc memory for runtime data.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return tr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct thread *timehist_get_thread(struct perf_sched *sched,
|
static struct thread *timehist_get_thread(struct perf_sched *sched,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
struct machine *machine,
|
struct machine *machine,
|
||||||
|
@ -3291,7 +3340,7 @@ int cmd_sched(int argc, const char **argv)
|
||||||
struct perf_sched sched = {
|
struct perf_sched sched = {
|
||||||
.tool = {
|
.tool = {
|
||||||
.sample = perf_sched__process_tracepoint_sample,
|
.sample = perf_sched__process_tracepoint_sample,
|
||||||
.comm = perf_event__process_comm,
|
.comm = perf_sched__process_comm,
|
||||||
.namespaces = perf_event__process_namespaces,
|
.namespaces = perf_event__process_namespaces,
|
||||||
.lost = perf_event__process_lost,
|
.lost = perf_event__process_lost,
|
||||||
.fork = perf_sched__process_fork_event,
|
.fork = perf_sched__process_fork_event,
|
||||||
|
|
|
@ -1489,6 +1489,7 @@ struct perf_script {
|
||||||
bool show_switch_events;
|
bool show_switch_events;
|
||||||
bool show_namespace_events;
|
bool show_namespace_events;
|
||||||
bool show_lost_events;
|
bool show_lost_events;
|
||||||
|
bool show_round_events;
|
||||||
bool allocated;
|
bool allocated;
|
||||||
bool per_event_dump;
|
bool per_event_dump;
|
||||||
struct cpu_map *cpus;
|
struct cpu_map *cpus;
|
||||||
|
@ -2104,6 +2105,16 @@ process_lost_event(struct perf_tool *tool,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_finished_round_event(struct perf_tool *tool __maybe_unused,
|
||||||
|
union perf_event *event,
|
||||||
|
struct ordered_events *oe __maybe_unused)
|
||||||
|
|
||||||
|
{
|
||||||
|
perf_event__fprintf(event, stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void sig_handler(int sig __maybe_unused)
|
static void sig_handler(int sig __maybe_unused)
|
||||||
{
|
{
|
||||||
session_done = 1;
|
session_done = 1;
|
||||||
|
@ -2200,6 +2211,10 @@ static int __cmd_script(struct perf_script *script)
|
||||||
script->tool.namespaces = process_namespaces_event;
|
script->tool.namespaces = process_namespaces_event;
|
||||||
if (script->show_lost_events)
|
if (script->show_lost_events)
|
||||||
script->tool.lost = process_lost_event;
|
script->tool.lost = process_lost_event;
|
||||||
|
if (script->show_round_events) {
|
||||||
|
script->tool.ordered_events = false;
|
||||||
|
script->tool.finished_round = process_finished_round_event;
|
||||||
|
}
|
||||||
|
|
||||||
if (perf_script__setup_per_event_dump(script)) {
|
if (perf_script__setup_per_event_dump(script)) {
|
||||||
pr_err("Couldn't create the per event dump files\n");
|
pr_err("Couldn't create the per event dump files\n");
|
||||||
|
@ -2659,8 +2674,8 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
|
||||||
}
|
}
|
||||||
|
|
||||||
for_each_lang(scripts_path, scripts_dir, lang_dirent) {
|
for_each_lang(scripts_path, scripts_dir, lang_dirent) {
|
||||||
snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
|
scnprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
|
||||||
lang_dirent->d_name);
|
lang_dirent->d_name);
|
||||||
lang_dir = opendir(lang_path);
|
lang_dir = opendir(lang_path);
|
||||||
if (!lang_dir)
|
if (!lang_dir)
|
||||||
continue;
|
continue;
|
||||||
|
@ -2669,8 +2684,8 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
|
||||||
script_root = get_script_root(script_dirent, REPORT_SUFFIX);
|
script_root = get_script_root(script_dirent, REPORT_SUFFIX);
|
||||||
if (script_root) {
|
if (script_root) {
|
||||||
desc = script_desc__findnew(script_root);
|
desc = script_desc__findnew(script_root);
|
||||||
snprintf(script_path, MAXPATHLEN, "%s/%s",
|
scnprintf(script_path, MAXPATHLEN, "%s/%s",
|
||||||
lang_path, script_dirent->d_name);
|
lang_path, script_dirent->d_name);
|
||||||
read_script_info(desc, script_path);
|
read_script_info(desc, script_path);
|
||||||
free(script_root);
|
free(script_root);
|
||||||
}
|
}
|
||||||
|
@ -2706,7 +2721,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
|
||||||
int match, len;
|
int match, len;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
sprintf(filename, "%s/bin/%s-record", dir_name, scriptname);
|
scnprintf(filename, MAXPATHLEN, "%s/bin/%s-record", dir_name, scriptname);
|
||||||
|
|
||||||
fp = fopen(filename, "r");
|
fp = fopen(filename, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
|
@ -2784,8 +2799,8 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
|
||||||
}
|
}
|
||||||
|
|
||||||
for_each_lang(scripts_path, scripts_dir, lang_dirent) {
|
for_each_lang(scripts_path, scripts_dir, lang_dirent) {
|
||||||
snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path,
|
scnprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path,
|
||||||
lang_dirent->d_name);
|
lang_dirent->d_name);
|
||||||
#ifdef NO_LIBPERL
|
#ifdef NO_LIBPERL
|
||||||
if (strstr(lang_path, "perl"))
|
if (strstr(lang_path, "perl"))
|
||||||
continue;
|
continue;
|
||||||
|
@ -2840,8 +2855,8 @@ static char *get_script_path(const char *script_root, const char *suffix)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for_each_lang(scripts_path, scripts_dir, lang_dirent) {
|
for_each_lang(scripts_path, scripts_dir, lang_dirent) {
|
||||||
snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
|
scnprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
|
||||||
lang_dirent->d_name);
|
lang_dirent->d_name);
|
||||||
lang_dir = opendir(lang_path);
|
lang_dir = opendir(lang_path);
|
||||||
if (!lang_dir)
|
if (!lang_dir)
|
||||||
continue;
|
continue;
|
||||||
|
@ -2852,8 +2867,8 @@ static char *get_script_path(const char *script_root, const char *suffix)
|
||||||
free(__script_root);
|
free(__script_root);
|
||||||
closedir(lang_dir);
|
closedir(lang_dir);
|
||||||
closedir(scripts_dir);
|
closedir(scripts_dir);
|
||||||
snprintf(script_path, MAXPATHLEN, "%s/%s",
|
scnprintf(script_path, MAXPATHLEN, "%s/%s",
|
||||||
lang_path, script_dirent->d_name);
|
lang_path, script_dirent->d_name);
|
||||||
return strdup(script_path);
|
return strdup(script_path);
|
||||||
}
|
}
|
||||||
free(__script_root);
|
free(__script_root);
|
||||||
|
@ -3139,6 +3154,8 @@ int cmd_script(int argc, const char **argv)
|
||||||
"Show namespace events (if recorded)"),
|
"Show namespace events (if recorded)"),
|
||||||
OPT_BOOLEAN('\0', "show-lost-events", &script.show_lost_events,
|
OPT_BOOLEAN('\0', "show-lost-events", &script.show_lost_events,
|
||||||
"Show lost events (if recorded)"),
|
"Show lost events (if recorded)"),
|
||||||
|
OPT_BOOLEAN('\0', "show-round-events", &script.show_round_events,
|
||||||
|
"Show round events (if recorded)"),
|
||||||
OPT_BOOLEAN('\0', "per-event-dump", &script.per_event_dump,
|
OPT_BOOLEAN('\0', "per-event-dump", &script.per_event_dump,
|
||||||
"Dump trace output to files named by the monitored events"),
|
"Dump trace output to files named by the monitored events"),
|
||||||
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
|
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
|
||||||
|
|
|
@ -168,6 +168,7 @@ static struct timespec ref_time;
|
||||||
static struct cpu_map *aggr_map;
|
static struct cpu_map *aggr_map;
|
||||||
static aggr_get_id_t aggr_get_id;
|
static aggr_get_id_t aggr_get_id;
|
||||||
static bool append_file;
|
static bool append_file;
|
||||||
|
static bool interval_count;
|
||||||
static const char *output_name;
|
static const char *output_name;
|
||||||
static int output_fd;
|
static int output_fd;
|
||||||
static int print_free_counters_hint;
|
static int print_free_counters_hint;
|
||||||
|
@ -507,14 +508,13 @@ static int perf_stat_synthesize_config(bool is_pipe)
|
||||||
|
|
||||||
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
|
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
|
||||||
|
|
||||||
static int __store_counter_ids(struct perf_evsel *counter,
|
static int __store_counter_ids(struct perf_evsel *counter)
|
||||||
struct cpu_map *cpus,
|
|
||||||
struct thread_map *threads)
|
|
||||||
{
|
{
|
||||||
int cpu, thread;
|
int cpu, thread;
|
||||||
|
|
||||||
for (cpu = 0; cpu < cpus->nr; cpu++) {
|
for (cpu = 0; cpu < xyarray__max_x(counter->fd); cpu++) {
|
||||||
for (thread = 0; thread < threads->nr; thread++) {
|
for (thread = 0; thread < xyarray__max_y(counter->fd);
|
||||||
|
thread++) {
|
||||||
int fd = FD(counter, cpu, thread);
|
int fd = FD(counter, cpu, thread);
|
||||||
|
|
||||||
if (perf_evlist__id_add_fd(evsel_list, counter,
|
if (perf_evlist__id_add_fd(evsel_list, counter,
|
||||||
|
@ -534,7 +534,7 @@ static int store_counter_ids(struct perf_evsel *counter)
|
||||||
if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr))
|
if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
return __store_counter_ids(counter, cpus, threads);
|
return __store_counter_ids(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool perf_evsel__should_store_id(struct perf_evsel *counter)
|
static bool perf_evsel__should_store_id(struct perf_evsel *counter)
|
||||||
|
@ -571,6 +571,8 @@ static struct perf_evsel *perf_evsel__reset_weak_group(struct perf_evsel *evsel)
|
||||||
static int __run_perf_stat(int argc, const char **argv)
|
static int __run_perf_stat(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
int interval = stat_config.interval;
|
int interval = stat_config.interval;
|
||||||
|
int times = stat_config.times;
|
||||||
|
int timeout = stat_config.timeout;
|
||||||
char msg[BUFSIZ];
|
char msg[BUFSIZ];
|
||||||
unsigned long long t0, t1;
|
unsigned long long t0, t1;
|
||||||
struct perf_evsel *counter;
|
struct perf_evsel *counter;
|
||||||
|
@ -584,6 +586,9 @@ static int __run_perf_stat(int argc, const char **argv)
|
||||||
if (interval) {
|
if (interval) {
|
||||||
ts.tv_sec = interval / USEC_PER_MSEC;
|
ts.tv_sec = interval / USEC_PER_MSEC;
|
||||||
ts.tv_nsec = (interval % USEC_PER_MSEC) * NSEC_PER_MSEC;
|
ts.tv_nsec = (interval % USEC_PER_MSEC) * NSEC_PER_MSEC;
|
||||||
|
} else if (timeout) {
|
||||||
|
ts.tv_sec = timeout / USEC_PER_MSEC;
|
||||||
|
ts.tv_nsec = (timeout % USEC_PER_MSEC) * NSEC_PER_MSEC;
|
||||||
} else {
|
} else {
|
||||||
ts.tv_sec = 1;
|
ts.tv_sec = 1;
|
||||||
ts.tv_nsec = 0;
|
ts.tv_nsec = 0;
|
||||||
|
@ -632,7 +637,19 @@ try_again:
|
||||||
if (verbose > 0)
|
if (verbose > 0)
|
||||||
ui__warning("%s\n", msg);
|
ui__warning("%s\n", msg);
|
||||||
goto try_again;
|
goto try_again;
|
||||||
}
|
} else if (target__has_per_thread(&target) &&
|
||||||
|
evsel_list->threads &&
|
||||||
|
evsel_list->threads->err_thread != -1) {
|
||||||
|
/*
|
||||||
|
* For global --per-thread case, skip current
|
||||||
|
* error thread.
|
||||||
|
*/
|
||||||
|
if (!thread_map__remove(evsel_list->threads,
|
||||||
|
evsel_list->threads->err_thread)) {
|
||||||
|
evsel_list->threads->err_thread = -1;
|
||||||
|
goto try_again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
perf_evsel__open_strerror(counter, &target,
|
perf_evsel__open_strerror(counter, &target,
|
||||||
errno, msg, sizeof(msg));
|
errno, msg, sizeof(msg));
|
||||||
|
@ -696,10 +713,14 @@ try_again:
|
||||||
perf_evlist__start_workload(evsel_list);
|
perf_evlist__start_workload(evsel_list);
|
||||||
enable_counters();
|
enable_counters();
|
||||||
|
|
||||||
if (interval) {
|
if (interval || timeout) {
|
||||||
while (!waitpid(child_pid, &status, WNOHANG)) {
|
while (!waitpid(child_pid, &status, WNOHANG)) {
|
||||||
nanosleep(&ts, NULL);
|
nanosleep(&ts, NULL);
|
||||||
|
if (timeout)
|
||||||
|
break;
|
||||||
process_interval();
|
process_interval();
|
||||||
|
if (interval_count && !(--times))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
waitpid(child_pid, &status, 0);
|
waitpid(child_pid, &status, 0);
|
||||||
|
@ -716,8 +737,13 @@ try_again:
|
||||||
enable_counters();
|
enable_counters();
|
||||||
while (!done) {
|
while (!done) {
|
||||||
nanosleep(&ts, NULL);
|
nanosleep(&ts, NULL);
|
||||||
if (interval)
|
if (timeout)
|
||||||
|
break;
|
||||||
|
if (interval) {
|
||||||
process_interval();
|
process_interval();
|
||||||
|
if (interval_count && !(--times))
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1225,6 +1251,31 @@ static void aggr_update_shadow(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void uniquify_event_name(struct perf_evsel *counter)
|
||||||
|
{
|
||||||
|
char *new_name;
|
||||||
|
char *config;
|
||||||
|
|
||||||
|
if (!counter->pmu_name || !strncmp(counter->name, counter->pmu_name,
|
||||||
|
strlen(counter->pmu_name)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
config = strchr(counter->name, '/');
|
||||||
|
if (config) {
|
||||||
|
if (asprintf(&new_name,
|
||||||
|
"%s%s", counter->pmu_name, config) > 0) {
|
||||||
|
free(counter->name);
|
||||||
|
counter->name = new_name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (asprintf(&new_name,
|
||||||
|
"%s [%s]", counter->name, counter->pmu_name) > 0) {
|
||||||
|
free(counter->name);
|
||||||
|
counter->name = new_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void collect_all_aliases(struct perf_evsel *counter,
|
static void collect_all_aliases(struct perf_evsel *counter,
|
||||||
void (*cb)(struct perf_evsel *counter, void *data,
|
void (*cb)(struct perf_evsel *counter, void *data,
|
||||||
bool first),
|
bool first),
|
||||||
|
@ -1253,7 +1304,9 @@ static bool collect_data(struct perf_evsel *counter,
|
||||||
if (counter->merged_stat)
|
if (counter->merged_stat)
|
||||||
return false;
|
return false;
|
||||||
cb(counter, data, true);
|
cb(counter, data, true);
|
||||||
if (!no_merge && counter->auto_merge_stats)
|
if (no_merge)
|
||||||
|
uniquify_event_name(counter);
|
||||||
|
else if (counter->auto_merge_stats)
|
||||||
collect_all_aliases(counter, cb, data);
|
collect_all_aliases(counter, cb, data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1891,6 +1944,10 @@ static const struct option stat_options[] = {
|
||||||
"command to run after to the measured command"),
|
"command to run after to the measured command"),
|
||||||
OPT_UINTEGER('I', "interval-print", &stat_config.interval,
|
OPT_UINTEGER('I', "interval-print", &stat_config.interval,
|
||||||
"print counts at regular interval in ms (>= 10)"),
|
"print counts at regular interval in ms (>= 10)"),
|
||||||
|
OPT_INTEGER(0, "interval-count", &stat_config.times,
|
||||||
|
"print counts for fixed number of times"),
|
||||||
|
OPT_UINTEGER(0, "timeout", &stat_config.timeout,
|
||||||
|
"stop workload and print counts after a timeout period in ms (>= 10ms)"),
|
||||||
OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode,
|
OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode,
|
||||||
"aggregate counts per processor socket", AGGR_SOCKET),
|
"aggregate counts per processor socket", AGGR_SOCKET),
|
||||||
OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode,
|
OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode,
|
||||||
|
@ -2274,11 +2331,16 @@ static int add_default_attributes(void)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (transaction_run) {
|
if (transaction_run) {
|
||||||
|
struct parse_events_error errinfo;
|
||||||
|
|
||||||
if (pmu_have_event("cpu", "cycles-ct") &&
|
if (pmu_have_event("cpu", "cycles-ct") &&
|
||||||
pmu_have_event("cpu", "el-start"))
|
pmu_have_event("cpu", "el-start"))
|
||||||
err = parse_events(evsel_list, transaction_attrs, NULL);
|
err = parse_events(evsel_list, transaction_attrs,
|
||||||
|
&errinfo);
|
||||||
else
|
else
|
||||||
err = parse_events(evsel_list, transaction_limited_attrs, NULL);
|
err = parse_events(evsel_list,
|
||||||
|
transaction_limited_attrs,
|
||||||
|
&errinfo);
|
||||||
if (err) {
|
if (err) {
|
||||||
fprintf(stderr, "Cannot set up transaction events\n");
|
fprintf(stderr, "Cannot set up transaction events\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2688,7 +2750,7 @@ int cmd_stat(int argc, const char **argv)
|
||||||
int status = -EINVAL, run_idx;
|
int status = -EINVAL, run_idx;
|
||||||
const char *mode;
|
const char *mode;
|
||||||
FILE *output = stderr;
|
FILE *output = stderr;
|
||||||
unsigned int interval;
|
unsigned int interval, timeout;
|
||||||
const char * const stat_subcommands[] = { "record", "report" };
|
const char * const stat_subcommands[] = { "record", "report" };
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
@ -2719,6 +2781,7 @@ int cmd_stat(int argc, const char **argv)
|
||||||
return __cmd_report(argc, argv);
|
return __cmd_report(argc, argv);
|
||||||
|
|
||||||
interval = stat_config.interval;
|
interval = stat_config.interval;
|
||||||
|
timeout = stat_config.timeout;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For record command the -o is already taken care of.
|
* For record command the -o is already taken care of.
|
||||||
|
@ -2871,6 +2934,33 @@ int cmd_stat(int argc, const char **argv)
|
||||||
"Please proceed with caution.\n");
|
"Please proceed with caution.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stat_config.times && interval)
|
||||||
|
interval_count = true;
|
||||||
|
else if (stat_config.times && !interval) {
|
||||||
|
pr_err("interval-count option should be used together with "
|
||||||
|
"interval-print.\n");
|
||||||
|
parse_options_usage(stat_usage, stat_options, "interval-count", 0);
|
||||||
|
parse_options_usage(stat_usage, stat_options, "I", 1);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout && timeout < 100) {
|
||||||
|
if (timeout < 10) {
|
||||||
|
pr_err("timeout must be >= 10ms.\n");
|
||||||
|
parse_options_usage(stat_usage, stat_options, "timeout", 0);
|
||||||
|
goto out;
|
||||||
|
} else
|
||||||
|
pr_warning("timeout < 100ms. "
|
||||||
|
"The overhead percentage could be high in some cases. "
|
||||||
|
"Please proceed with caution.\n");
|
||||||
|
}
|
||||||
|
if (timeout && interval) {
|
||||||
|
pr_err("timeout option is not supported with interval-print.\n");
|
||||||
|
parse_options_usage(stat_usage, stat_options, "timeout", 0);
|
||||||
|
parse_options_usage(stat_usage, stat_options, "I", 1);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (perf_evlist__alloc_stats(evsel_list, interval))
|
if (perf_evlist__alloc_stats(evsel_list, interval))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
|
@ -817,14 +817,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
||||||
struct perf_session *session = top->session;
|
struct perf_session *session = top->session;
|
||||||
union perf_event *event;
|
union perf_event *event;
|
||||||
struct machine *machine;
|
struct machine *machine;
|
||||||
u64 end, start;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
md = opts->overwrite ? &evlist->overwrite_mmap[idx] : &evlist->mmap[idx];
|
md = opts->overwrite ? &evlist->overwrite_mmap[idx] : &evlist->mmap[idx];
|
||||||
if (perf_mmap__read_init(md, opts->overwrite, &start, &end) < 0)
|
if (perf_mmap__read_init(md) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while ((event = perf_mmap__read_event(md, opts->overwrite, &start, end)) != NULL) {
|
while ((event = perf_mmap__read_event(md)) != NULL) {
|
||||||
ret = perf_evlist__parse_sample(evlist, event, &sample);
|
ret = perf_evlist__parse_sample(evlist, event, &sample);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Can't parse sample, err = %d\n", ret);
|
pr_err("Can't parse sample, err = %d\n", ret);
|
||||||
|
@ -879,7 +878,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
||||||
} else
|
} else
|
||||||
++session->evlist->stats.nr_unknown_events;
|
++session->evlist->stats.nr_unknown_events;
|
||||||
next_event:
|
next_event:
|
||||||
perf_mmap__consume(md, opts->overwrite);
|
perf_mmap__consume(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
perf_mmap__read_done(md);
|
perf_mmap__read_done(md);
|
||||||
|
@ -1224,8 +1223,10 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||||
|
|
||||||
static int perf_top_config(const char *var, const char *value, void *cb __maybe_unused)
|
static int perf_top_config(const char *var, const char *value, void *cb __maybe_unused)
|
||||||
{
|
{
|
||||||
if (!strcmp(var, "top.call-graph"))
|
if (!strcmp(var, "top.call-graph")) {
|
||||||
var = "call-graph.record-mode"; /* fall-through */
|
var = "call-graph.record-mode";
|
||||||
|
return perf_default_config(var, value, cb);
|
||||||
|
}
|
||||||
if (!strcmp(var, "top.children")) {
|
if (!strcmp(var, "top.children")) {
|
||||||
symbol_conf.cumulate_callchain = perf_config_bool(var, value);
|
symbol_conf.cumulate_callchain = perf_config_bool(var, value);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1307,7 +1308,9 @@ int cmd_top(int argc, const char **argv)
|
||||||
OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name",
|
OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name",
|
||||||
"symbol to annotate"),
|
"symbol to annotate"),
|
||||||
OPT_BOOLEAN('z', "zero", &top.zero, "zero history across updates"),
|
OPT_BOOLEAN('z', "zero", &top.zero, "zero history across updates"),
|
||||||
OPT_UINTEGER('F', "freq", &opts->user_freq, "profile at this frequency"),
|
OPT_CALLBACK('F', "freq", &top.record_opts, "freq or 'max'",
|
||||||
|
"profile at this frequency",
|
||||||
|
record__parse_freq),
|
||||||
OPT_INTEGER('E', "entries", &top.print_entries,
|
OPT_INTEGER('E', "entries", &top.print_entries,
|
||||||
"display this many functions"),
|
"display this many functions"),
|
||||||
OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols,
|
OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols,
|
||||||
|
@ -1490,6 +1493,8 @@ int cmd_top(int argc, const char **argv)
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto out_delete_evlist;
|
goto out_delete_evlist;
|
||||||
|
|
||||||
|
annotation_config__init();
|
||||||
|
|
||||||
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
|
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
|
||||||
if (symbol__init(NULL) < 0)
|
if (symbol__init(NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <traceevent/event-parse.h>
|
#include <traceevent/event-parse.h>
|
||||||
#include <api/fs/tracing_path.h>
|
#include <api/fs/tracing_path.h>
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
|
#include "util/cgroup.h"
|
||||||
#include "util/color.h"
|
#include "util/color.h"
|
||||||
#include "util/debug.h"
|
#include "util/debug.h"
|
||||||
#include "util/env.h"
|
#include "util/env.h"
|
||||||
|
@ -83,6 +84,7 @@ struct trace {
|
||||||
struct perf_evlist *evlist;
|
struct perf_evlist *evlist;
|
||||||
struct machine *host;
|
struct machine *host;
|
||||||
struct thread *current;
|
struct thread *current;
|
||||||
|
struct cgroup *cgroup;
|
||||||
u64 base_time;
|
u64 base_time;
|
||||||
FILE *output;
|
FILE *output;
|
||||||
unsigned long nr_events;
|
unsigned long nr_events;
|
||||||
|
@ -2370,6 +2372,34 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||||
trace__sched_stat_runtime))
|
trace__sched_stat_runtime))
|
||||||
goto out_error_sched_stat_runtime;
|
goto out_error_sched_stat_runtime;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a global cgroup was set, apply it to all the events without an
|
||||||
|
* explicit cgroup. I.e.:
|
||||||
|
*
|
||||||
|
* trace -G A -e sched:*switch
|
||||||
|
*
|
||||||
|
* Will set all raw_syscalls:sys_{enter,exit}, pgfault, vfs_getname, etc
|
||||||
|
* _and_ sched:sched_switch to the 'A' cgroup, while:
|
||||||
|
*
|
||||||
|
* trace -e sched:*switch -G A
|
||||||
|
*
|
||||||
|
* will only set the sched:sched_switch event to the 'A' cgroup, all the
|
||||||
|
* other events (raw_syscalls:sys_{enter,exit}, etc are left "without"
|
||||||
|
* a cgroup (on the root cgroup, sys wide, etc).
|
||||||
|
*
|
||||||
|
* Multiple cgroups:
|
||||||
|
*
|
||||||
|
* trace -G A -e sched:*switch -G B
|
||||||
|
*
|
||||||
|
* the syscall ones go to the 'A' cgroup, the sched:sched_switch goes
|
||||||
|
* to the 'B' cgroup.
|
||||||
|
*
|
||||||
|
* evlist__set_default_cgroup() grabs a reference of the passed cgroup
|
||||||
|
* only for the evsels still without a cgroup, i.e. evsel->cgroup == NULL.
|
||||||
|
*/
|
||||||
|
if (trace->cgroup)
|
||||||
|
evlist__set_default_cgroup(trace->evlist, trace->cgroup);
|
||||||
|
|
||||||
err = perf_evlist__create_maps(evlist, &trace->opts.target);
|
err = perf_evlist__create_maps(evlist, &trace->opts.target);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
fprintf(trace->output, "Problems parsing the target to trace, check your options!\n");
|
fprintf(trace->output, "Problems parsing the target to trace, check your options!\n");
|
||||||
|
@ -2472,8 +2502,13 @@ again:
|
||||||
|
|
||||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||||
union perf_event *event;
|
union perf_event *event;
|
||||||
|
struct perf_mmap *md;
|
||||||
|
|
||||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
md = &evlist->mmap[i];
|
||||||
|
if (perf_mmap__read_init(md) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while ((event = perf_mmap__read_event(md)) != NULL) {
|
||||||
struct perf_sample sample;
|
struct perf_sample sample;
|
||||||
|
|
||||||
++trace->nr_events;
|
++trace->nr_events;
|
||||||
|
@ -2486,7 +2521,7 @@ again:
|
||||||
|
|
||||||
trace__handle_event(trace, event, &sample);
|
trace__handle_event(trace, event, &sample);
|
||||||
next_event:
|
next_event:
|
||||||
perf_evlist__mmap_consume(evlist, i);
|
perf_mmap__consume(md);
|
||||||
|
|
||||||
if (interrupted)
|
if (interrupted)
|
||||||
goto out_disable;
|
goto out_disable;
|
||||||
|
@ -2496,6 +2531,7 @@ next_event:
|
||||||
draining = true;
|
draining = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
perf_mmap__read_done(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trace->nr_events == before) {
|
if (trace->nr_events == before) {
|
||||||
|
@ -2533,6 +2569,7 @@ out_delete_evlist:
|
||||||
trace__symbols__exit(trace);
|
trace__symbols__exit(trace);
|
||||||
|
|
||||||
perf_evlist__delete(evlist);
|
perf_evlist__delete(evlist);
|
||||||
|
cgroup__put(trace->cgroup);
|
||||||
trace->evlist = NULL;
|
trace->evlist = NULL;
|
||||||
trace->live = false;
|
trace->live = false;
|
||||||
return err;
|
return err;
|
||||||
|
@ -2972,6 +3009,18 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int trace__parse_cgroups(const struct option *opt, const char *str, int unset)
|
||||||
|
{
|
||||||
|
struct trace *trace = opt->value;
|
||||||
|
|
||||||
|
if (!list_empty(&trace->evlist->entries))
|
||||||
|
return parse_cgroups(opt, str, unset);
|
||||||
|
|
||||||
|
trace->cgroup = evlist__findnew_cgroup(trace->evlist, str);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int cmd_trace(int argc, const char **argv)
|
int cmd_trace(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
const char *trace_usage[] = {
|
const char *trace_usage[] = {
|
||||||
|
@ -3062,6 +3111,8 @@ int cmd_trace(int argc, const char **argv)
|
||||||
"print the PERF_RECORD_SAMPLE PERF_SAMPLE_ info, for debugging"),
|
"print the PERF_RECORD_SAMPLE PERF_SAMPLE_ info, for debugging"),
|
||||||
OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout,
|
OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout,
|
||||||
"per thread proc mmap processing timeout in ms"),
|
"per thread proc mmap processing timeout in ms"),
|
||||||
|
OPT_CALLBACK('G', "cgroup", &trace, "name", "monitor event in cgroup name only",
|
||||||
|
trace__parse_cgroups),
|
||||||
OPT_UINTEGER('D', "delay", &trace.opts.initial_delay,
|
OPT_UINTEGER('D', "delay", &trace.opts.initial_delay,
|
||||||
"ms to wait before starting measurement after program "
|
"ms to wait before starting measurement after program "
|
||||||
"start"),
|
"start"),
|
||||||
|
@ -3088,6 +3139,11 @@ int cmd_trace(int argc, const char **argv)
|
||||||
argc = parse_options_subcommand(argc, argv, trace_options, trace_subcommands,
|
argc = parse_options_subcommand(argc, argv, trace_options, trace_subcommands,
|
||||||
trace_usage, PARSE_OPT_STOP_AT_NON_OPTION);
|
trace_usage, PARSE_OPT_STOP_AT_NON_OPTION);
|
||||||
|
|
||||||
|
if ((nr_cgroups || trace.cgroup) && !trace.opts.target.system_wide) {
|
||||||
|
usage_with_options_msg(trace_usage, trace_options,
|
||||||
|
"cgroup monitoring only available in system-wide mode");
|
||||||
|
}
|
||||||
|
|
||||||
err = bpf__setup_stdout(trace.evlist);
|
err = bpf__setup_stdout(trace.evlist);
|
||||||
if (err) {
|
if (err) {
|
||||||
bpf__strerror_setup_stdout(trace.evlist, err, bf, sizeof(bf));
|
bpf__strerror_setup_stdout(trace.evlist, err, bf, sizeof(bf));
|
||||||
|
|
|
@ -42,6 +42,7 @@ arch/parisc/include/uapi/asm/errno.h
|
||||||
arch/powerpc/include/uapi/asm/errno.h
|
arch/powerpc/include/uapi/asm/errno.h
|
||||||
arch/sparc/include/uapi/asm/errno.h
|
arch/sparc/include/uapi/asm/errno.h
|
||||||
arch/x86/include/uapi/asm/errno.h
|
arch/x86/include/uapi/asm/errno.h
|
||||||
|
arch/powerpc/include/uapi/asm/unistd.h
|
||||||
include/asm-generic/bitops/arch_hweight.h
|
include/asm-generic/bitops/arch_hweight.h
|
||||||
include/asm-generic/bitops/const_hweight.h
|
include/asm-generic/bitops/const_hweight.h
|
||||||
include/asm-generic/bitops/__fls.h
|
include/asm-generic/bitops/__fls.h
|
||||||
|
@ -58,6 +59,7 @@ check () {
|
||||||
file=$1
|
file=$1
|
||||||
|
|
||||||
shift
|
shift
|
||||||
|
opts=
|
||||||
while [ -n "$*" ]; do
|
while [ -n "$*" ]; do
|
||||||
opts="$opts \"$1\""
|
opts="$opts \"$1\""
|
||||||
shift
|
shift
|
||||||
|
|
|
@ -61,6 +61,7 @@ struct record_opts {
|
||||||
bool tail_synthesize;
|
bool tail_synthesize;
|
||||||
bool overwrite;
|
bool overwrite;
|
||||||
bool ignore_missing_thread;
|
bool ignore_missing_thread;
|
||||||
|
bool strict_freq;
|
||||||
bool sample_id;
|
bool sample_id;
|
||||||
unsigned int freq;
|
unsigned int freq;
|
||||||
unsigned int mmap_pages;
|
unsigned int mmap_pages;
|
||||||
|
@ -83,4 +84,6 @@ struct record_opts {
|
||||||
struct option;
|
struct option;
|
||||||
extern const char * const *record_usage;
|
extern const char * const *record_usage;
|
||||||
extern struct option *record_options;
|
extern struct option *record_options;
|
||||||
|
|
||||||
|
int record__parse_freq(const struct option *opt, const char *str, int unset);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
hostprogs := jevents
|
hostprogs := jevents
|
||||||
|
|
||||||
jevents-y += json.o jsmn.o jevents.o
|
jevents-y += json.o jsmn.o jevents.o
|
||||||
|
CHOSTFLAGS_jevents.o = -I$(srctree)/tools/include
|
||||||
pmu-events-y += pmu-events.o
|
pmu-events-y += pmu-events.o
|
||||||
JDIR = pmu-events/arch/$(SRCARCH)
|
JDIR = pmu-events/arch/$(SRCARCH)
|
||||||
JSON = $(shell [ -d $(JDIR) ] && \
|
JSON = $(shell [ -d $(JDIR) ] && \
|
||||||
find $(JDIR) -name '*.json' -o -name 'mapfile.csv')
|
find $(JDIR) -name '*.json' -o -name 'mapfile.csv')
|
||||||
|
|
||||||
#
|
#
|
||||||
# Locate/process JSON files in pmu-events/arch/
|
# Locate/process JSON files in pmu-events/arch/
|
||||||
# directory and create tables in pmu-events.c.
|
# directory and create tables in pmu-events.c.
|
||||||
|
|
|
@ -11,12 +11,17 @@ tree tools/perf/pmu-events/arch/foo.
|
||||||
- Regular files with '.json' extension in the name are assumed to be
|
- Regular files with '.json' extension in the name are assumed to be
|
||||||
JSON files, each of which describes a set of PMU events.
|
JSON files, each of which describes a set of PMU events.
|
||||||
|
|
||||||
- Regular files with basename starting with 'mapfile.csv' are assumed
|
- The CSV file that maps a specific CPU to its set of PMU events is to
|
||||||
to be a CSV file that maps a specific CPU to its set of PMU events.
|
be named 'mapfile.csv' (see below for mapfile format).
|
||||||
(see below for mapfile format)
|
|
||||||
|
|
||||||
- Directories are traversed, but all other files are ignored.
|
- Directories are traversed, but all other files are ignored.
|
||||||
|
|
||||||
|
- To reduce JSON event duplication per architecture, platform JSONs may
|
||||||
|
use "ArchStdEvent" keyword to dereference an "Architecture standard
|
||||||
|
events", defined in architecture standard JSONs.
|
||||||
|
Architecture standard JSONs must be located in the architecture root
|
||||||
|
folder. Matching is based on the "EventName" field.
|
||||||
|
|
||||||
The PMU events supported by a CPU model are expected to grouped into topics
|
The PMU events supported by a CPU model are expected to grouped into topics
|
||||||
such as Pipelining, Cache, Memory, Floating-point etc. All events for a topic
|
such as Pipelining, Cache, Memory, Floating-point etc. All events for a topic
|
||||||
should be placed in a separate JSON file - where the file name identifies
|
should be placed in a separate JSON file - where the file name identifies
|
||||||
|
@ -29,6 +34,10 @@ sub directory. Thus for the Silvermont X86 CPU:
|
||||||
Cache.json Memory.json Virtual-Memory.json
|
Cache.json Memory.json Virtual-Memory.json
|
||||||
Frontend.json Pipeline.json
|
Frontend.json Pipeline.json
|
||||||
|
|
||||||
|
The JSONs folder for a CPU model/family may be placed in the root arch
|
||||||
|
folder, or may be placed in a vendor sub-folder under the arch folder
|
||||||
|
for instances where the arch and vendor are not the same.
|
||||||
|
|
||||||
Using the JSON files and the mapfile, 'jevents' generates the C source file,
|
Using the JSON files and the mapfile, 'jevents' generates the C source file,
|
||||||
'pmu-events.c', which encodes the two sets of tables:
|
'pmu-events.c', which encodes the two sets of tables:
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
[
|
[
|
||||||
{,
|
{
|
||||||
"EventCode": "0x7A",
|
"ArchStdEvent": "BR_INDIRECT_SPEC",
|
||||||
"EventName": "BR_INDIRECT_SPEC",
|
|
||||||
"BriefDescription": "Branch speculatively executed - Indirect branch"
|
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xC9",
|
"EventCode": "0xC9",
|
||||||
"EventName": "BR_COND",
|
"EventName": "BR_COND",
|
||||||
"BriefDescription": "Conditional branch executed"
|
"BriefDescription": "Conditional branch executed"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xCA",
|
"EventCode": "0xCA",
|
||||||
"EventName": "BR_INDIRECT_MISPRED",
|
"EventName": "BR_INDIRECT_MISPRED",
|
||||||
"BriefDescription": "Indirect branch mispredicted"
|
"BriefDescription": "Indirect branch mispredicted"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xCB",
|
"EventCode": "0xCB",
|
||||||
"EventName": "BR_INDIRECT_MISPRED_ADDR",
|
"EventName": "BR_INDIRECT_MISPRED_ADDR",
|
||||||
"BriefDescription": "Indirect branch mispredicted because of address miscompare"
|
"BriefDescription": "Indirect branch mispredicted because of address miscompare"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xCC",
|
"EventCode": "0xCC",
|
||||||
"EventName": "BR_COND_MISPRED",
|
"EventName": "BR_COND_MISPRED",
|
||||||
"BriefDescription": "Conditional branch mispredicted"
|
"BriefDescription": "Conditional branch mispredicted"
|
|
@ -0,0 +1,8 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ArchStdEvent": "BUS_ACCESS_RD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ArchStdEvent": "BUS_ACCESS_WR",
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,27 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"EventCode": "0xC2",
|
||||||
|
"EventName": "PREFETCH_LINEFILL",
|
||||||
|
"BriefDescription": "Linefill because of prefetch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EventCode": "0xC3",
|
||||||
|
"EventName": "PREFETCH_LINEFILL_DROP",
|
||||||
|
"BriefDescription": "Instruction Cache Throttle occurred"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EventCode": "0xC4",
|
||||||
|
"EventName": "READ_ALLOC_ENTER",
|
||||||
|
"BriefDescription": "Entering read allocate mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EventCode": "0xC5",
|
||||||
|
"EventName": "READ_ALLOC",
|
||||||
|
"BriefDescription": "Read allocate mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EventCode": "0xC8",
|
||||||
|
"EventName": "EXT_SNOOP",
|
||||||
|
"BriefDescription": "SCU Snooped data from another CPU for this CPU"
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,20 +1,10 @@
|
||||||
[
|
[
|
||||||
{,
|
{
|
||||||
"EventCode": "0x60",
|
|
||||||
"EventName": "BUS_ACCESS_LD",
|
|
||||||
"BriefDescription": "Bus access - Read"
|
|
||||||
},
|
|
||||||
{,
|
|
||||||
"EventCode": "0x61",
|
|
||||||
"EventName": "BUS_ACCESS_ST",
|
|
||||||
"BriefDescription": "Bus access - Write"
|
|
||||||
},
|
|
||||||
{,
|
|
||||||
"EventCode": "0xC0",
|
"EventCode": "0xC0",
|
||||||
"EventName": "EXT_MEM_REQ",
|
"EventName": "EXT_MEM_REQ",
|
||||||
"BriefDescription": "External memory request"
|
"BriefDescription": "External memory request"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xC1",
|
"EventCode": "0xC1",
|
||||||
"EventName": "EXT_MEM_REQ_NC",
|
"EventName": "EXT_MEM_REQ_NC",
|
||||||
"BriefDescription": "Non-cacheable external memory request"
|
"BriefDescription": "Non-cacheable external memory request"
|
|
@ -0,0 +1,28 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ArchStdEvent": "EXC_IRQ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ArchStdEvent": "EXC_FIQ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EventCode": "0xC6",
|
||||||
|
"EventName": "PRE_DECODE_ERR",
|
||||||
|
"BriefDescription": "Pre-decode error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EventCode": "0xD0",
|
||||||
|
"EventName": "L1I_CACHE_ERR",
|
||||||
|
"BriefDescription": "L1 Instruction Cache (data or tag) memory error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EventCode": "0xD1",
|
||||||
|
"EventName": "L1D_CACHE_ERR",
|
||||||
|
"BriefDescription": "L1 Data Cache (data, tag or dirty) memory error, correctable or non-correctable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EventCode": "0xD2",
|
||||||
|
"EventName": "TLB_ERR",
|
||||||
|
"BriefDescription": "TLB memory error"
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,50 +1,50 @@
|
||||||
[
|
[
|
||||||
{,
|
{
|
||||||
"EventCode": "0xC7",
|
"EventCode": "0xC7",
|
||||||
"EventName": "STALL_SB_FULL",
|
"EventName": "STALL_SB_FULL",
|
||||||
"BriefDescription": "Data Write operation that stalls the pipeline because the store buffer is full"
|
"BriefDescription": "Data Write operation that stalls the pipeline because the store buffer is full"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE0",
|
"EventCode": "0xE0",
|
||||||
"EventName": "OTHER_IQ_DEP_STALL",
|
"EventName": "OTHER_IQ_DEP_STALL",
|
||||||
"BriefDescription": "Cycles that the DPU IQ is empty and that is not because of a recent micro-TLB miss, instruction cache miss or pre-decode error"
|
"BriefDescription": "Cycles that the DPU IQ is empty and that is not because of a recent micro-TLB miss, instruction cache miss or pre-decode error"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE1",
|
"EventCode": "0xE1",
|
||||||
"EventName": "IC_DEP_STALL",
|
"EventName": "IC_DEP_STALL",
|
||||||
"BriefDescription": "Cycles the DPU IQ is empty and there is an instruction cache miss being processed"
|
"BriefDescription": "Cycles the DPU IQ is empty and there is an instruction cache miss being processed"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE2",
|
"EventCode": "0xE2",
|
||||||
"EventName": "IUTLB_DEP_STALL",
|
"EventName": "IUTLB_DEP_STALL",
|
||||||
"BriefDescription": "Cycles the DPU IQ is empty and there is an instruction micro-TLB miss being processed"
|
"BriefDescription": "Cycles the DPU IQ is empty and there is an instruction micro-TLB miss being processed"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE3",
|
"EventCode": "0xE3",
|
||||||
"EventName": "DECODE_DEP_STALL",
|
"EventName": "DECODE_DEP_STALL",
|
||||||
"BriefDescription": "Cycles the DPU IQ is empty and there is a pre-decode error being processed"
|
"BriefDescription": "Cycles the DPU IQ is empty and there is a pre-decode error being processed"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE4",
|
"EventCode": "0xE4",
|
||||||
"EventName": "OTHER_INTERLOCK_STALL",
|
"EventName": "OTHER_INTERLOCK_STALL",
|
||||||
"BriefDescription": "Cycles there is an interlock other than Advanced SIMD/Floating-point instructions or load/store instruction"
|
"BriefDescription": "Cycles there is an interlock other than Advanced SIMD/Floating-point instructions or load/store instruction"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE5",
|
"EventCode": "0xE5",
|
||||||
"EventName": "AGU_DEP_STALL",
|
"EventName": "AGU_DEP_STALL",
|
||||||
"BriefDescription": "Cycles there is an interlock for a load/store instruction waiting for data to calculate the address in the AGU"
|
"BriefDescription": "Cycles there is an interlock for a load/store instruction waiting for data to calculate the address in the AGU"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE6",
|
"EventCode": "0xE6",
|
||||||
"EventName": "SIMD_DEP_STALL",
|
"EventName": "SIMD_DEP_STALL",
|
||||||
"BriefDescription": "Cycles there is an interlock for an Advanced SIMD/Floating-point operation."
|
"BriefDescription": "Cycles there is an interlock for an Advanced SIMD/Floating-point operation."
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE7",
|
"EventCode": "0xE7",
|
||||||
"EventName": "LD_DEP_STALL",
|
"EventName": "LD_DEP_STALL",
|
||||||
"BriefDescription": "Cycles there is a stall in the Wr stage because of a load miss"
|
"BriefDescription": "Cycles there is a stall in the Wr stage because of a load miss"
|
||||||
},
|
},
|
||||||
{,
|
{
|
||||||
"EventCode": "0xE8",
|
"EventCode": "0xE8",
|
||||||
"EventName": "ST_DEP_STALL",
|
"EventName": "ST_DEP_STALL",
|
||||||
"BriefDescription": "Cycles there is a stall in the Wr stage because of a store"
|
"BriefDescription": "Cycles there is a stall in the Wr stage because of a store"
|
|
@ -0,0 +1,452 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data cache access, read",
|
||||||
|
"EventCode": "0x40",
|
||||||
|
"EventName": "L1D_CACHE_RD",
|
||||||
|
"BriefDescription": "L1D cache access, read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data cache access, write",
|
||||||
|
"EventCode": "0x41",
|
||||||
|
"EventName": "L1D_CACHE_WR",
|
||||||
|
"BriefDescription": "L1D cache access, write"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data cache refill, read",
|
||||||
|
"EventCode": "0x42",
|
||||||
|
"EventName": "L1D_CACHE_REFILL_RD",
|
||||||
|
"BriefDescription": "L1D cache refill, read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data cache refill, write",
|
||||||
|
"EventCode": "0x43",
|
||||||
|
"EventName": "L1D_CACHE_REFILL_WR",
|
||||||
|
"BriefDescription": "L1D cache refill, write"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data cache refill, inner",
|
||||||
|
"EventCode": "0x44",
|
||||||
|
"EventName": "L1D_CACHE_REFILL_INNER",
|
||||||
|
"BriefDescription": "L1D cache refill, inner"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data cache refill, outer",
|
||||||
|
"EventCode": "0x45",
|
||||||
|
"EventName": "L1D_CACHE_REFILL_OUTER",
|
||||||
|
"BriefDescription": "L1D cache refill, outer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data cache Write-Back, victim",
|
||||||
|
"EventCode": "0x46",
|
||||||
|
"EventName": "L1D_CACHE_WB_VICTIM",
|
||||||
|
"BriefDescription": "L1D cache Write-Back, victim"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Level 1 data cache Write-Back, cleaning and coherency",
|
||||||
|
"EventCode": "0x47",
|
||||||
|
"EventName": "L1D_CACHE_WB_CLEAN",
|
||||||
|
"BriefDescription": "L1D cache Write-Back, cleaning and coherency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data cache invalidate",
|
||||||
|
"EventCode": "0x48",
|
||||||
|
"EventName": "L1D_CACHE_INVAL",
|
||||||
|
"BriefDescription": "L1D cache invalidate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data TLB refill, read",
|
||||||
|
"EventCode": "0x4C",
|
||||||
|
"EventName": "L1D_TLB_REFILL_RD",
|
||||||
|
"BriefDescription": "L1D tlb refill, read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data TLB refill, write",
|
||||||
|
"EventCode": "0x4D",
|
||||||
|
"EventName": "L1D_TLB_REFILL_WR",
|
||||||
|
"BriefDescription": "L1D tlb refill, write"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data or unified TLB access, read",
|
||||||
|
"EventCode": "0x4E",
|
||||||
|
"EventName": "L1D_TLB_RD",
|
||||||
|
"BriefDescription": "L1D tlb access, read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 1 data or unified TLB access, write",
|
||||||
|
"EventCode": "0x4F",
|
||||||
|
"EventName": "L1D_TLB_WR",
|
||||||
|
"BriefDescription": "L1D tlb access, write"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data cache access, read",
|
||||||
|
"EventCode": "0x50",
|
||||||
|
"EventName": "L2D_CACHE_RD",
|
||||||
|
"BriefDescription": "L2D cache access, read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data cache access, write",
|
||||||
|
"EventCode": "0x51",
|
||||||
|
"EventName": "L2D_CACHE_WR",
|
||||||
|
"BriefDescription": "L2D cache access, write"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data cache refill, read",
|
||||||
|
"EventCode": "0x52",
|
||||||
|
"EventName": "L2D_CACHE_REFILL_RD",
|
||||||
|
"BriefDescription": "L2D cache refill, read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data cache refill, write",
|
||||||
|
"EventCode": "0x53",
|
||||||
|
"EventName": "L2D_CACHE_REFILL_WR",
|
||||||
|
"BriefDescription": "L2D cache refill, write"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data cache Write-Back, victim",
|
||||||
|
"EventCode": "0x56",
|
||||||
|
"EventName": "L2D_CACHE_WB_VICTIM",
|
||||||
|
"BriefDescription": "L2D cache Write-Back, victim"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Level 2 data cache Write-Back, cleaning and coherency",
|
||||||
|
"EventCode": "0x57",
|
||||||
|
"EventName": "L2D_CACHE_WB_CLEAN",
|
||||||
|
"BriefDescription": "L2D cache Write-Back, cleaning and coherency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data cache invalidate",
|
||||||
|
"EventCode": "0x58",
|
||||||
|
"EventName": "L2D_CACHE_INVAL",
|
||||||
|
"BriefDescription": "L2D cache invalidate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data or unified TLB refill, read",
|
||||||
|
"EventCode": "0x5c",
|
||||||
|
"EventName": "L2D_TLB_REFILL_RD",
|
||||||
|
"BriefDescription": "L2D cache refill, read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data or unified TLB refill, write",
|
||||||
|
"EventCode": "0x5d",
|
||||||
|
"EventName": "L2D_TLB_REFILL_WR",
|
||||||
|
"BriefDescription": "L2D cache refill, write"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data or unified TLB access, read",
|
||||||
|
"EventCode": "0x5e",
|
||||||
|
"EventName": "L2D_TLB_RD",
|
||||||
|
"BriefDescription": "L2D cache access, read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 2 data or unified TLB access, write",
|
||||||
|
"EventCode": "0x5f",
|
||||||
|
"EventName": "L2D_TLB_WR",
|
||||||
|
"BriefDescription": "L2D cache access, write"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Bus access read",
|
||||||
|
"EventCode": "0x60",
|
||||||
|
"EventName": "BUS_ACCESS_RD",
|
||||||
|
"BriefDescription": "Bus access read"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PublicDescription": "Bus access write",
|
||||||
|
"EventCode": "0x61",
|
||||||
|
"EventName": "BUS_ACCESS_WR",
|
||||||
|
"BriefDescription": "Bus access write"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Bus access, Normal, Cacheable, Shareable",
|
||||||
|
"EventCode": "0x62",
|
||||||
|
"EventName": "BUS_ACCESS_SHARED",
|
||||||
|
"BriefDescription": "Bus access, Normal, Cacheable, Shareable"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Bus access, not Normal, Cacheable, Shareable",
|
||||||
|
"EventCode": "0x63",
|
||||||
|
"EventName": "BUS_ACCESS_NOT_SHARED",
|
||||||
|
"BriefDescription": "Bus access, not Normal, Cacheable, Shareable"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Bus access, Normal",
|
||||||
|
"EventCode": "0x64",
|
||||||
|
"EventName": "BUS_ACCESS_NORMAL",
|
||||||
|
"BriefDescription": "Bus access, Normal"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Bus access, peripheral",
|
||||||
|
"EventCode": "0x65",
|
||||||
|
"EventName": "BUS_ACCESS_PERIPH",
|
||||||
|
"BriefDescription": "Bus access, peripheral"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Data memory access, read",
|
||||||
|
"EventCode": "0x66",
|
||||||
|
"EventName": "MEM_ACCESS_RD",
|
||||||
|
"BriefDescription": "Data memory access, read"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Data memory access, write",
|
||||||
|
"EventCode": "0x67",
|
||||||
|
"EventName": "MEM_ACCESS_WR",
|
||||||
|
"BriefDescription": "Data memory access, write"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Unaligned access, read",
|
||||||
|
"EventCode": "0x68",
|
||||||
|
"EventName": "UNALIGNED_LD_SPEC",
|
||||||
|
"BriefDescription": "Unaligned access, read"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Unaligned access, write",
|
||||||
|
"EventCode": "0x69",
|
||||||
|
"EventName": "UNALIGNED_ST_SPEC",
|
||||||
|
"BriefDescription": "Unaligned access, write"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Unaligned access",
|
||||||
|
"EventCode": "0x6a",
|
||||||
|
"EventName": "UNALIGNED_LDST_SPEC",
|
||||||
|
"BriefDescription": "Unaligned access"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exclusive operation speculatively executed, LDREX or LDX",
|
||||||
|
"EventCode": "0x6c",
|
||||||
|
"EventName": "LDREX_SPEC",
|
||||||
|
"BriefDescription": "Exclusive operation speculatively executed, LDREX or LDX"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exclusive operation speculatively executed, STREX or STX pass",
|
||||||
|
"EventCode": "0x6d",
|
||||||
|
"EventName": "STREX_PASS_SPEC",
|
||||||
|
"BriefDescription": "Exclusive operation speculatively executed, STREX or STX pass"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exclusive operation speculatively executed, STREX or STX fail",
|
||||||
|
"EventCode": "0x6e",
|
||||||
|
"EventName": "STREX_FAIL_SPEC",
|
||||||
|
"BriefDescription": "Exclusive operation speculatively executed, STREX or STX fail"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exclusive operation speculatively executed, STREX or STX",
|
||||||
|
"EventCode": "0x6f",
|
||||||
|
"EventName": "STREX_SPEC",
|
||||||
|
"BriefDescription": "Exclusive operation speculatively executed, STREX or STX"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Operation speculatively executed, load",
|
||||||
|
"EventCode": "0x70",
|
||||||
|
"EventName": "LD_SPEC",
|
||||||
|
"BriefDescription": "Operation speculatively executed, load"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Operation speculatively executed, store"
|
||||||
|
"EventCode": "0x71",
|
||||||
|
"EventName": "ST_SPEC",
|
||||||
|
"BriefDescription": "Operation speculatively executed, store"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Operation speculatively executed, load or store",
|
||||||
|
"EventCode": "0x72",
|
||||||
|
"EventName": "LDST_SPEC",
|
||||||
|
"BriefDescription": "Operation speculatively executed, load or store"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Operation speculatively executed, integer data processing",
|
||||||
|
"EventCode": "0x73",
|
||||||
|
"EventName": "DP_SPEC",
|
||||||
|
"BriefDescription": "Operation speculatively executed, integer data processing"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Operation speculatively executed, Advanced SIMD instruction",
|
||||||
|
"EventCode": "0x74",
|
||||||
|
"EventName": "ASE_SPEC",
|
||||||
|
"BriefDescription": "Operation speculatively executed, Advanced SIMD instruction",
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Operation speculatively executed, floating-point instruction",
|
||||||
|
"EventCode": "0x75",
|
||||||
|
"EventName": "VFP_SPEC",
|
||||||
|
"BriefDescription": "Operation speculatively executed, floating-point instruction"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Operation speculatively executed, software change of the PC",
|
||||||
|
"EventCode": "0x76",
|
||||||
|
"EventName": "PC_WRITE_SPEC",
|
||||||
|
"BriefDescription": "Operation speculatively executed, software change of the PC"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Operation speculatively executed, Cryptographic instruction",
|
||||||
|
"EventCode": "0x77",
|
||||||
|
"EventName": "CRYPTO_SPEC",
|
||||||
|
"BriefDescription": "Operation speculatively executed, Cryptographic instruction"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Branch speculatively executed, immediate branch"
|
||||||
|
"EventCode": "0x78",
|
||||||
|
"EventName": "BR_IMMED_SPEC",
|
||||||
|
"BriefDescription": "Branch speculatively executed, immediate branch"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Branch speculatively executed, procedure return"
|
||||||
|
"EventCode": "0x79",
|
||||||
|
"EventName": "BR_RETURN_SPEC",
|
||||||
|
"BriefDescription": "Branch speculatively executed, procedure return"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Branch speculatively executed, indirect branch"
|
||||||
|
"EventCode": "0x7a",
|
||||||
|
"EventName": "BR_INDIRECT_SPEC",
|
||||||
|
"BriefDescription": "Branch speculatively executed, indirect branch"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Barrier speculatively executed, ISB"
|
||||||
|
"EventCode": "0x7c",
|
||||||
|
"EventName": "ISB_SPEC",
|
||||||
|
"BriefDescription": "Barrier speculatively executed, ISB"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Barrier speculatively executed, DSB"
|
||||||
|
"EventCode": "0x7d",
|
||||||
|
"EventName": "DSB_SPEC",
|
||||||
|
"BriefDescription": "Barrier speculatively executed, DSB"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Barrier speculatively executed, DMB"
|
||||||
|
"EventCode": "0x7e",
|
||||||
|
"EventName": "DMB_SPEC",
|
||||||
|
"BriefDescription": "Barrier speculatively executed, DMB"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Other synchronous"
|
||||||
|
"EventCode": "0x81",
|
||||||
|
"EventName": "EXC_UNDEF",
|
||||||
|
"BriefDescription": "Exception taken, Other synchronous"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Supervisor Call"
|
||||||
|
"EventCode": "0x82",
|
||||||
|
"EventName": "EXC_SVC",
|
||||||
|
"BriefDescription": "Exception taken, Supervisor Call"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Instruction Abort"
|
||||||
|
"EventCode": "0x83",
|
||||||
|
"EventName": "EXC_PABORT",
|
||||||
|
"BriefDescription": "Exception taken, Instruction Abort"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Data Abort and SError"
|
||||||
|
"EventCode": "0x84",
|
||||||
|
"EventName": "EXC_DABORT",
|
||||||
|
"BriefDescription": "Exception taken, Data Abort and SError"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, IRQ"
|
||||||
|
"EventCode": "0x86",
|
||||||
|
"EventName": "EXC_IRQ",
|
||||||
|
"BriefDescription": "Exception taken, IRQ"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, FIQ"
|
||||||
|
"EventCode": "0x87",
|
||||||
|
"EventName": "EXC_FIQ",
|
||||||
|
"BriefDescription": "Exception taken, FIQ"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Secure Monitor Call"
|
||||||
|
"EventCode": "0x88",
|
||||||
|
"EventName": "EXC_SMC",
|
||||||
|
"BriefDescription": "Exception taken, Secure Monitor Call"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Hypervisor Call"
|
||||||
|
"EventCode": "0x8a",
|
||||||
|
"EventName": "EXC_HVC",
|
||||||
|
"BriefDescription": "Exception taken, Hypervisor Call"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Instruction Abort not taken locally"
|
||||||
|
"EventCode": "0x8b",
|
||||||
|
"EventName": "EXC_TRAP_PABORT",
|
||||||
|
"BriefDescription": "Exception taken, Instruction Abort not taken locally"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Data Abort or SError not taken locally"
|
||||||
|
"EventCode": "0x8c",
|
||||||
|
"EventName": "EXC_TRAP_DABORT",
|
||||||
|
"BriefDescription": "Exception taken, Data Abort or SError not taken locally"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, Other traps not taken locally"
|
||||||
|
"EventCode": "0x8d",
|
||||||
|
"EventName": "EXC_TRAP_OTHER",
|
||||||
|
"BriefDescription": "Exception taken, Other traps not taken locally"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, IRQ not taken locally"
|
||||||
|
"EventCode": "0x8e",
|
||||||
|
"EventName": "EXC_TRAP_IRQ",
|
||||||
|
"BriefDescription": "Exception taken, IRQ not taken locally"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Exception taken, FIQ not taken locally"
|
||||||
|
"EventCode": "0x8f",
|
||||||
|
"EventName": "EXC_TRAP_FIQ",
|
||||||
|
"BriefDescription": "Exception taken, FIQ not taken locally"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Release consistency operation speculatively executed, Load-Acquire"
|
||||||
|
"EventCode": "0x90",
|
||||||
|
"EventName": "RC_LD_SPEC",
|
||||||
|
"BriefDescription": "Release consistency operation speculatively executed, Load-Acquire"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Release consistency operation speculatively executed, Store-Release"
|
||||||
|
"EventCode": "0x91",
|
||||||
|
"EventName": "RC_ST_SPEC",
|
||||||
|
"BriefDescription": "Release consistency operation speculatively executed, Store-Release"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 3 data or unified cache access, read"
|
||||||
|
"EventCode": "0xa0",
|
||||||
|
"EventName": "L3D_CACHE_RD",
|
||||||
|
"BriefDescription": "Attributable Level 3 data or unified cache access, read"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 3 data or unified cache access, write"
|
||||||
|
"EventCode": "0xa1",
|
||||||
|
"EventName": "L3D_CACHE_WR",
|
||||||
|
"BriefDescription": "Attributable Level 3 data or unified cache access, write"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 3 data or unified cache refill, read"
|
||||||
|
"EventCode": "0xa2",
|
||||||
|
"EventName": "L3D_CACHE_REFILL_RD",
|
||||||
|
"BriefDescription": "Attributable Level 3 data or unified cache refill, read"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 3 data or unified cache refill, write"
|
||||||
|
"EventCode": "0xa3",
|
||||||
|
"EventName": "L3D_CACHE_REFILL_WR",
|
||||||
|
"BriefDescription": "Attributable Level 3 data or unified cache refill, write"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 3 data or unified cache Write-Back, victim"
|
||||||
|
"EventCode": "0xa6",
|
||||||
|
"EventName": "L3D_CACHE_WB_VICTIM",
|
||||||
|
"BriefDescription": "Attributable Level 3 data or unified cache Write-Back, victim"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 3 data or unified cache Write-Back, cache clean"
|
||||||
|
"EventCode": "0xa7",
|
||||||
|
"EventName": "L3D_CACHE_WB_CLEAN",
|
||||||
|
"BriefDescription": "Attributable Level 3 data or unified cache Write-Back, cache clean"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"PublicDescription": "Attributable Level 3 data or unified cache access, invalidate"
|
||||||
|
"EventCode": "0xa8",
|
||||||
|
"EventName": "L3D_CACHE_INVAL",
|
||||||
|
"BriefDescription": "Attributable Level 3 data or unified cache access, invalidate"
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,62 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"PublicDescription": "Attributable Level 1 data cache access, read",
|
|
||||||
"EventCode": "0x40",
|
|
||||||
"EventName": "l1d_cache_rd",
|
|
||||||
"BriefDescription": "L1D cache read",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Attributable Level 1 data cache access, write ",
|
|
||||||
"EventCode": "0x41",
|
|
||||||
"EventName": "l1d_cache_wr",
|
|
||||||
"BriefDescription": "L1D cache write",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Attributable Level 1 data cache refill, read",
|
|
||||||
"EventCode": "0x42",
|
|
||||||
"EventName": "l1d_cache_refill_rd",
|
|
||||||
"BriefDescription": "L1D cache refill read",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Attributable Level 1 data cache refill, write",
|
|
||||||
"EventCode": "0x43",
|
|
||||||
"EventName": "l1d_cache_refill_wr",
|
|
||||||
"BriefDescription": "L1D refill write",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Attributable Level 1 data TLB refill, read",
|
|
||||||
"EventCode": "0x4C",
|
|
||||||
"EventName": "l1d_tlb_refill_rd",
|
|
||||||
"BriefDescription": "L1D tlb refill read",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Attributable Level 1 data TLB refill, write",
|
|
||||||
"EventCode": "0x4D",
|
|
||||||
"EventName": "l1d_tlb_refill_wr",
|
|
||||||
"BriefDescription": "L1D tlb refill write",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Attributable Level 1 data or unified TLB access, read",
|
|
||||||
"EventCode": "0x4E",
|
|
||||||
"EventName": "l1d_tlb_rd",
|
|
||||||
"BriefDescription": "L1D tlb read",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Attributable Level 1 data or unified TLB access, write",
|
|
||||||
"EventCode": "0x4F",
|
|
||||||
"EventName": "l1d_tlb_wr",
|
|
||||||
"BriefDescription": "L1D tlb write",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Bus access read",
|
|
||||||
"EventCode": "0x60",
|
|
||||||
"EventName": "bus_access_rd",
|
|
||||||
"BriefDescription": "Bus access read",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"PublicDescription": "Bus access write",
|
|
||||||
"EventCode": "0x61",
|
|
||||||
"EventName": "bus_access_wr",
|
|
||||||
"BriefDescription": "Bus access write",
|
|
||||||
}
|
|
||||||
]
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue