Hi
Hardware traces, such as instruction traces, can produce a vast amount of
trace data, so being able to reduce tracing to more specific circumstances
can be useful.
The ability to pause or resume tracing when another event happens, can do
that.
These patches add such a facilty and show how it would work for Intel
Processor Trace.
Maintainers of other AUX area tracing implementations are requested to
consider if this is something they might employ and then whether or not
the ABI would work for them.
Changes to perf tools are not fleshed out yet.
Changes in RFC V2:
Use ->stop() / ->start() instead of ->pause_resume()
Move aux_start_paused bit into aux_output_cfg
Tighten up when Intel PT pause / resume is allowed
Add an example of how it might work for CoreSight
Adrian Hunter (4):
perf/core: Add aux_pause, aux_resume, aux_start_paused
perf/x86/intel/pt: Add support for pause / resume
perf tools: Add support for AUX area pause / resume
coresight: Have a stab at support for pause / resume
arch/x86/events/intel/pt.c | 63 ++++++++++++++++++++-
arch/x86/events/intel/pt.h | 4 ++
drivers/hwtracing/coresight/coresight-etm-perf.c | 29 ++++++++--
include/linux/perf_event.h | 15 +++++
include/uapi/linux/perf_event.h | 11 +++-
kernel/events/core.c | 72 +++++++++++++++++++++++-
kernel/events/internal.h | 1 +
tools/include/uapi/linux/perf_event.h | 11 +++-
tools/perf/util/auxtrace.c | 4 ++
tools/perf/util/evsel.c | 9 +++
tools/perf/util/evsel_config.h | 6 ++
tools/perf/util/parse-events.c | 33 +++++++++++
tools/perf/util/parse-events.h | 3 +
tools/perf/util/parse-events.l | 3 +
tools/perf/util/perf_event_attr_fprintf.c | 3 +
15 files changed, 255 insertions(+), 12 deletions(-)
Regards
Adrian
Prevent tracing to start if aux_paused.
Implement support for PERF_EF_PAUSE / PERF_EF_RESUME. When aux_paused, stop
tracing. When not aux_paused, only start tracing if it isn't currently
meant to be stopped.
Signed-off-by: Adrian Hunter <[email protected]>
---
arch/x86/events/intel/pt.c | 63 ++++++++++++++++++++++++++++++++++++--
arch/x86/events/intel/pt.h | 4 +++
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index 42a55794004a..692b51849d1c 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -418,6 +418,9 @@ static void pt_config_start(struct perf_event *event)
struct pt *pt = this_cpu_ptr(&pt_ctx);
u64 ctl = event->hw.config;
+ if (READ_ONCE(event->aux_paused))
+ return;
+
ctl |= RTIT_CTL_TRACEEN;
if (READ_ONCE(pt->vmx_on))
perf_aux_output_flag(&pt->handle, PERF_AUX_FLAG_PARTIAL);
@@ -534,7 +537,20 @@ static void pt_config(struct perf_event *event)
reg |= (event->attr.config & PT_CONFIG_MASK);
event->hw.config = reg;
+
+ /*
+ * Allow resume before starting so as not to overwrite a value set by a
+ * PMI.
+ */
+ WRITE_ONCE(pt->resume_allowed, 1);
+
pt_config_start(event);
+
+ /*
+ * Allow pause after starting so its pt_config_stop() doesn't race with
+ * pt_config_start().
+ */
+ WRITE_ONCE(pt->pause_allowed, 1);
}
static void pt_config_stop(struct perf_event *event)
@@ -1507,6 +1523,7 @@ void intel_pt_interrupt(void)
buf = perf_aux_output_begin(&pt->handle, event);
if (!buf) {
event->hw.state = PERF_HES_STOPPED;
+ pt->resume_allowed = 0;
return;
}
@@ -1515,6 +1532,7 @@ void intel_pt_interrupt(void)
ret = pt_buffer_reset_markers(buf, &pt->handle);
if (ret) {
perf_aux_output_end(&pt->handle, 0);
+ pt->resume_allowed = 0;
return;
}
@@ -1569,6 +1587,26 @@ static void pt_event_start(struct perf_event *event, int mode)
struct pt *pt = this_cpu_ptr(&pt_ctx);
struct pt_buffer *buf;
+ if (mode & PERF_EF_RESUME) {
+ if (READ_ONCE(pt->resume_allowed)) {
+ u64 status;
+
+ /*
+ * Only if the trace is not active and the error and
+ * stopped bits are clear, is it safe to start, but a
+ * PMI might have just cleared these, so resume_allowed
+ * must be checked again also.
+ */
+ rdmsrl(MSR_IA32_RTIT_STATUS, status);
+ if (!(status & (RTIT_STATUS_TRIGGEREN |
+ RTIT_STATUS_ERROR |
+ RTIT_STATUS_STOPPED)) &&
+ READ_ONCE(pt->resume_allowed))
+ pt_config_start(event);
+ }
+ return;
+ }
+
buf = perf_aux_output_begin(&pt->handle, event);
if (!buf)
goto fail_stop;
@@ -1597,6 +1635,16 @@ static void pt_event_stop(struct perf_event *event, int mode)
{
struct pt *pt = this_cpu_ptr(&pt_ctx);
+ if (mode & PERF_EF_PAUSE) {
+ if (READ_ONCE(pt->pause_allowed))
+ pt_config_stop(event);
+ return;
+ }
+
+ /* Protect against racing */
+ WRITE_ONCE(pt->pause_allowed, 0);
+ WRITE_ONCE(pt->resume_allowed, 0);
+
/*
* Protect against the PMI racing with disabling wrmsr,
* see comment in intel_pt_interrupt().
@@ -1655,8 +1703,12 @@ static long pt_event_snapshot_aux(struct perf_event *event,
/*
* Here, handle_nmi tells us if the tracing is on
*/
- if (READ_ONCE(pt->handle_nmi))
+ if (READ_ONCE(pt->handle_nmi)) {
+ /* Protect against racing */
+ WRITE_ONCE(pt->pause_allowed, 0);
+ WRITE_ONCE(pt->resume_allowed, 0);
pt_config_stop(event);
+ }
pt_read_offset(buf);
pt_update_head(pt);
@@ -1673,8 +1725,11 @@ static long pt_event_snapshot_aux(struct perf_event *event,
* Compiler barrier not needed as we couldn't have been
* preempted by anything that touches pt->handle_nmi.
*/
- if (pt->handle_nmi)
+ if (pt->handle_nmi) {
+ WRITE_ONCE(pt->resume_allowed, 1);
pt_config_start(event);
+ WRITE_ONCE(pt->pause_allowed, 1);
+ }
return ret;
}
@@ -1790,7 +1845,9 @@ static __init int pt_init(void)
if (!intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries))
pt_pmu.pmu.capabilities = PERF_PMU_CAP_AUX_NO_SG;
- pt_pmu.pmu.capabilities |= PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE;
+ pt_pmu.pmu.capabilities |= PERF_PMU_CAP_EXCLUSIVE |
+ PERF_PMU_CAP_ITRACE |
+ PERF_PMU_CAP_AUX_PAUSE;
pt_pmu.pmu.attr_groups = pt_attr_groups;
pt_pmu.pmu.task_ctx_nr = perf_sw_context;
pt_pmu.pmu.event_init = pt_event_init;
diff --git a/arch/x86/events/intel/pt.h b/arch/x86/events/intel/pt.h
index 96906a62aacd..b9527205e028 100644
--- a/arch/x86/events/intel/pt.h
+++ b/arch/x86/events/intel/pt.h
@@ -117,6 +117,8 @@ struct pt_filters {
* @filters: last configured filters
* @handle_nmi: do handle PT PMI on this cpu, there's an active event
* @vmx_on: 1 if VMX is ON on this cpu
+ * @pause_allowed: PERF_EF_PAUSE is allowed to stop tracing
+ * @resume_allowed: PERF_EF_RESUME is allowed to start tracing
* @output_base: cached RTIT_OUTPUT_BASE MSR value
* @output_mask: cached RTIT_OUTPUT_MASK MSR value
*/
@@ -125,6 +127,8 @@ struct pt {
struct pt_filters filters;
int handle_nmi;
int vmx_on;
+ int pause_allowed;
+ int resume_allowed;
u64 output_base;
u64 output_mask;
};
--
2.34.1
Add config terms aux-pause, aux-resume and aux-start-paused.
Still to do: validation, fallbacks for perf_event_open, documentation.
Signed-off-by: Adrian Hunter <[email protected]>
---
tools/include/uapi/linux/perf_event.h | 11 +++++++-
tools/perf/util/auxtrace.c | 4 +++
tools/perf/util/evsel.c | 9 +++++++
tools/perf/util/evsel_config.h | 6 +++++
tools/perf/util/parse-events.c | 33 +++++++++++++++++++++++
tools/perf/util/parse-events.h | 3 +++
tools/perf/util/parse-events.l | 3 +++
tools/perf/util/perf_event_attr_fprintf.c | 3 +++
8 files changed, 71 insertions(+), 1 deletion(-)
diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h
index 3a64499b0f5d..9db32bc10d5b 100644
--- a/tools/include/uapi/linux/perf_event.h
+++ b/tools/include/uapi/linux/perf_event.h
@@ -511,7 +511,16 @@ struct perf_event_attr {
__u16 sample_max_stack;
__u16 __reserved_2;
__u32 aux_sample_size;
- __u32 __reserved_3;
+
+ union {
+ __u32 aux_output_cfg;
+ struct {
+ __u64 aux_pause : 1, /* on overflow, pause AUX area tracing */
+ aux_resume : 1, /* on overflow, resume AUX area tracing */
+ aux_start_paused : 1, /* start AUX area tracing paused */
+ __reserved_3 : 29;
+ };
+ };
/*
* User provided data if sigtrap=1, passed back to user via
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index a0368202a746..4a7ca8b0d100 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -814,6 +814,10 @@ void auxtrace_regroup_aux_output(struct evlist *evlist)
if (evsel__is_aux_event(evsel))
aux_evsel = evsel;
term = evsel__get_config_term(evsel, AUX_OUTPUT);
+ if (!term)
+ term = evsel__get_config_term(evsel, AUX_PAUSE);
+ if (!term)
+ term = evsel__get_config_term(evsel, AUX_RESUME);
/* If possible, group with the AUX event */
if (term && aux_evsel)
evlist__regroup(evlist, aux_evsel, evsel);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index a5da74e3a517..03553c104954 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1001,6 +1001,15 @@ static void evsel__apply_config_terms(struct evsel *evsel,
case EVSEL__CONFIG_TERM_AUX_OUTPUT:
attr->aux_output = term->val.aux_output ? 1 : 0;
break;
+ case EVSEL__CONFIG_TERM_AUX_PAUSE:
+ attr->aux_pause = term->val.aux_pause ? 1 : 0;
+ break;
+ case EVSEL__CONFIG_TERM_AUX_RESUME:
+ attr->aux_resume = term->val.aux_resume ? 1 : 0;
+ break;
+ case EVSEL__CONFIG_TERM_AUX_START_PAUSED:
+ attr->aux_start_paused = term->val.aux_start_paused ? 1 : 0;
+ break;
case EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE:
/* Already applied by auxtrace */
break;
diff --git a/tools/perf/util/evsel_config.h b/tools/perf/util/evsel_config.h
index aee6f808b512..85ad183b5637 100644
--- a/tools/perf/util/evsel_config.h
+++ b/tools/perf/util/evsel_config.h
@@ -25,6 +25,9 @@ enum evsel_term_type {
EVSEL__CONFIG_TERM_BRANCH,
EVSEL__CONFIG_TERM_PERCORE,
EVSEL__CONFIG_TERM_AUX_OUTPUT,
+ EVSEL__CONFIG_TERM_AUX_PAUSE,
+ EVSEL__CONFIG_TERM_AUX_RESUME,
+ EVSEL__CONFIG_TERM_AUX_START_PAUSED,
EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE,
EVSEL__CONFIG_TERM_CFG_CHG,
};
@@ -44,6 +47,9 @@ struct evsel_config_term {
unsigned long max_events;
bool percore;
bool aux_output;
+ bool aux_pause;
+ bool aux_resume;
+ bool aux_start_paused;
u32 aux_sample_size;
u64 cfg_chg;
char *str;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index aa2f5c6fc7fc..615b04d5fb30 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -768,6 +768,9 @@ static const char *config_term_name(enum parse_events__term_type term_type)
[PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config",
[PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore",
[PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output",
+ [PARSE_EVENTS__TERM_TYPE_AUX_PAUSE] = "aux-pause",
+ [PARSE_EVENTS__TERM_TYPE_AUX_RESUME] = "aux-resume",
+ [PARSE_EVENTS__TERM_TYPE_AUX_START_PAUSED] = "aux-start-paused",
[PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size",
[PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id",
[PARSE_EVENTS__TERM_TYPE_RAW] = "raw",
@@ -817,6 +820,9 @@ config_term_avail(enum parse_events__term_type term_type, struct parse_events_er
case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
+ case PARSE_EVENTS__TERM_TYPE_AUX_PAUSE:
+ case PARSE_EVENTS__TERM_TYPE_AUX_RESUME:
+ case PARSE_EVENTS__TERM_TYPE_AUX_START_PAUSED:
case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
case PARSE_EVENTS__TERM_TYPE_RAW:
case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
@@ -936,6 +942,15 @@ do { \
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
CHECK_TYPE_VAL(NUM);
break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_PAUSE:
+ CHECK_TYPE_VAL(NUM);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_RESUME:
+ CHECK_TYPE_VAL(NUM);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_START_PAUSED:
+ CHECK_TYPE_VAL(NUM);
+ break;
case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
CHECK_TYPE_VAL(NUM);
if (term->val.num > UINT_MAX) {
@@ -1036,6 +1051,9 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
+ case PARSE_EVENTS__TERM_TYPE_AUX_PAUSE:
+ case PARSE_EVENTS__TERM_TYPE_AUX_RESUME:
+ case PARSE_EVENTS__TERM_TYPE_AUX_START_PAUSED:
case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
return config_term_common(attr, term, err);
case PARSE_EVENTS__TERM_TYPE_USER:
@@ -1170,6 +1188,18 @@ do { \
ADD_CONFIG_TERM_VAL(AUX_OUTPUT, aux_output,
term->val.num ? 1 : 0, term->weak);
break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_PAUSE:
+ ADD_CONFIG_TERM_VAL(AUX_PAUSE, aux_pause,
+ term->val.num ? 1 : 0, term->weak);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_RESUME:
+ ADD_CONFIG_TERM_VAL(AUX_RESUME, aux_resume,
+ term->val.num ? 1 : 0, term->weak);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_START_PAUSED:
+ ADD_CONFIG_TERM_VAL(AUX_START_PAUSED, aux_start_paused,
+ term->val.num ? 1 : 0, term->weak);
+ break;
case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
ADD_CONFIG_TERM_VAL(AUX_SAMPLE_SIZE, aux_sample_size,
term->val.num, term->weak);
@@ -1232,6 +1262,9 @@ static int get_config_chgs(struct perf_pmu *pmu, struct parse_events_terms *head
case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
case PARSE_EVENTS__TERM_TYPE_PERCORE:
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
+ case PARSE_EVENTS__TERM_TYPE_AUX_PAUSE:
+ case PARSE_EVENTS__TERM_TYPE_AUX_RESUME:
+ case PARSE_EVENTS__TERM_TYPE_AUX_START_PAUSED:
case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
case PARSE_EVENTS__TERM_TYPE_METRIC_ID:
case PARSE_EVENTS__TERM_TYPE_RAW:
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 63c0a36a4bf1..ff0871385b50 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -74,6 +74,9 @@ enum parse_events__term_type {
PARSE_EVENTS__TERM_TYPE_DRV_CFG,
PARSE_EVENTS__TERM_TYPE_PERCORE,
PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT,
+ PARSE_EVENTS__TERM_TYPE_AUX_PAUSE,
+ PARSE_EVENTS__TERM_TYPE_AUX_RESUME,
+ PARSE_EVENTS__TERM_TYPE_AUX_START_PAUSED,
PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE,
PARSE_EVENTS__TERM_TYPE_METRIC_ID,
PARSE_EVENTS__TERM_TYPE_RAW,
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index e86c45675e1d..56963013c3af 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -244,6 +244,9 @@ overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE); }
no-overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE); }
percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); }
aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); }
+aux-pause { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_PAUSE); }
+aux-resume { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_RESUME); }
+aux-start-paused { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_START_PAUSED); }
aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); }
metric-id { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID); }
cpu-cycles|cycles { return hw_term(yyscanner, PERF_COUNT_HW_CPU_CYCLES); }
diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c
index 8f04d3b7f3ec..e6ba0ac73182 100644
--- a/tools/perf/util/perf_event_attr_fprintf.c
+++ b/tools/perf/util/perf_event_attr_fprintf.c
@@ -309,6 +309,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
PRINT_ATTRf(inherit_thread, p_unsigned);
PRINT_ATTRf(remove_on_exec, p_unsigned);
PRINT_ATTRf(sigtrap, p_unsigned);
+ PRINT_ATTRf(aux_start_paused, p_unsigned);
PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned, false);
PRINT_ATTRf(bp_type, p_unsigned);
@@ -323,6 +324,8 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
PRINT_ATTRf(sample_max_stack, p_unsigned);
PRINT_ATTRf(aux_sample_size, p_unsigned);
PRINT_ATTRf(sig_data, p_unsigned);
+ PRINT_ATTRf(aux_pause, p_unsigned);
+ PRINT_ATTRf(aux_resume, p_unsigned);
return ret;
}
--
2.34.1
For discussion only, un-tested, not even compiled...
Signed-off-by: Adrian Hunter <[email protected]>
---
.../hwtracing/coresight/coresight-etm-perf.c | 29 ++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 5ca6278baff4..36e774405c51 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -45,6 +45,7 @@ static bool etm_perf_up;
struct etm_ctxt {
struct perf_output_handle handle;
struct etm_event_data *event_data;
+ int pr_allowed;
};
static DEFINE_PER_CPU(struct etm_ctxt, etm_ctxt);
@@ -452,6 +453,13 @@ static void etm_event_start(struct perf_event *event, int flags)
struct list_head *path;
u64 hw_id;
+ if (mode & PERF_EF_RESUME) {
+ if (!READ_ONCE(ctxt->pr_allowed))
+ return;
+ } else if (READ_ONCE(event->aux_paused)) {
+ goto out_pr_allowed;
+ }
+
if (!csdev)
goto fail;
@@ -514,6 +522,8 @@ static void etm_event_start(struct perf_event *event, int flags)
event->hw.state = 0;
/* Save the event_data for this ETM */
ctxt->event_data = event_data;
+out_pr_allowed:
+ WRITE_ONCE(ctxt->pr_allowed, 1);
return;
fail_disable_path:
@@ -530,6 +540,7 @@ static void etm_event_start(struct perf_event *event, int flags)
}
fail:
event->hw.state = PERF_HES_STOPPED;
+ WRITE_ONCE(ctxt->pr_allowed, 0);
return;
}
@@ -543,6 +554,11 @@ static void etm_event_stop(struct perf_event *event, int mode)
struct etm_event_data *event_data;
struct list_head *path;
+ if (mode & PERF_EF_PAUSE && !READ_ONCE(ctxt->pr_allowed))
+ return;
+
+ WRITE_ONCE(ctxt->pr_allowed, 0);
+
/*
* If we still have access to the event_data via handle,
* confirm that we haven't messed up the tracking.
@@ -556,7 +572,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
ctxt->event_data = NULL;
if (event->hw.state == PERF_HES_STOPPED)
- return;
+ goto out_pr_allowed;
/* We must have a valid event_data for a running event */
if (WARN_ON(!event_data))
@@ -627,6 +643,10 @@ static void etm_event_stop(struct perf_event *event, int mode)
/* Disabling the path make its elements available to other sessions */
coresight_disable_path(path);
+
+out_pr_allowed:
+ if (mode & PERF_EF_PAUSE)
+ WRITE_ONCE(ctxt->pr_allowed, 1);
}
static int etm_event_add(struct perf_event *event, int mode)
@@ -634,7 +654,7 @@ static int etm_event_add(struct perf_event *event, int mode)
int ret = 0;
struct hw_perf_event *hwc = &event->hw;
- if (mode & PERF_EF_START) {
+ if (mode & PERF_EF_START && !READ_ONCE(event->aux_paused)) {
etm_event_start(event, 0);
if (hwc->state & PERF_HES_STOPPED)
ret = -EINVAL;
@@ -886,8 +906,9 @@ int __init etm_perf_init(void)
{
int ret;
- etm_pmu.capabilities = (PERF_PMU_CAP_EXCLUSIVE |
- PERF_PMU_CAP_ITRACE);
+ etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE |
+ PERF_PMU_CAP_ITRACE |
+ PERF_PMU_CAP_AUX_PAUSE;
etm_pmu.attr_groups = etm_pmu_attr_groups;
etm_pmu.task_ctx_nr = perf_sw_context;
--
2.34.1
For discussion only, un-tested...
Signed-off-by: Adrian Hunter <[email protected]>
---
Changes in V3:
'mode' -> 'flags' so it at least compiles
.../hwtracing/coresight/coresight-etm-perf.c | 29 ++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 5ca6278baff4..7a69e6417ed4 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -45,6 +45,7 @@ static bool etm_perf_up;
struct etm_ctxt {
struct perf_output_handle handle;
struct etm_event_data *event_data;
+ int pr_allowed;
};
static DEFINE_PER_CPU(struct etm_ctxt, etm_ctxt);
@@ -452,6 +453,13 @@ static void etm_event_start(struct perf_event *event, int flags)
struct list_head *path;
u64 hw_id;
+ if (flags & PERF_EF_RESUME) {
+ if (!READ_ONCE(ctxt->pr_allowed))
+ return;
+ } else if (READ_ONCE(event->aux_paused)) {
+ goto out_pr_allowed;
+ }
+
if (!csdev)
goto fail;
@@ -514,6 +522,8 @@ static void etm_event_start(struct perf_event *event, int flags)
event->hw.state = 0;
/* Save the event_data for this ETM */
ctxt->event_data = event_data;
+out_pr_allowed:
+ WRITE_ONCE(ctxt->pr_allowed, 1);
return;
fail_disable_path:
@@ -530,6 +540,7 @@ static void etm_event_start(struct perf_event *event, int flags)
}
fail:
event->hw.state = PERF_HES_STOPPED;
+ WRITE_ONCE(ctxt->pr_allowed, 0);
return;
}
@@ -543,6 +554,11 @@ static void etm_event_stop(struct perf_event *event, int mode)
struct etm_event_data *event_data;
struct list_head *path;
+ if (mode & PERF_EF_PAUSE && !READ_ONCE(ctxt->pr_allowed))
+ return;
+
+ WRITE_ONCE(ctxt->pr_allowed, 0);
+
/*
* If we still have access to the event_data via handle,
* confirm that we haven't messed up the tracking.
@@ -556,7 +572,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
ctxt->event_data = NULL;
if (event->hw.state == PERF_HES_STOPPED)
- return;
+ goto out_pr_allowed;
/* We must have a valid event_data for a running event */
if (WARN_ON(!event_data))
@@ -627,6 +643,10 @@ static void etm_event_stop(struct perf_event *event, int mode)
/* Disabling the path make its elements available to other sessions */
coresight_disable_path(path);
+
+out_pr_allowed:
+ if (mode & PERF_EF_PAUSE)
+ WRITE_ONCE(ctxt->pr_allowed, 1);
}
static int etm_event_add(struct perf_event *event, int mode)
@@ -634,7 +654,7 @@ static int etm_event_add(struct perf_event *event, int mode)
int ret = 0;
struct hw_perf_event *hwc = &event->hw;
- if (mode & PERF_EF_START) {
+ if (mode & PERF_EF_START && !READ_ONCE(event->aux_paused)) {
etm_event_start(event, 0);
if (hwc->state & PERF_HES_STOPPED)
ret = -EINVAL;
@@ -886,8 +906,9 @@ int __init etm_perf_init(void)
{
int ret;
- etm_pmu.capabilities = (PERF_PMU_CAP_EXCLUSIVE |
- PERF_PMU_CAP_ITRACE);
+ etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE |
+ PERF_PMU_CAP_ITRACE |
+ PERF_PMU_CAP_AUX_PAUSE;
etm_pmu.attr_groups = etm_pmu_attr_groups;
etm_pmu.task_ctx_nr = perf_sw_context;
--
2.34.1
On 8/12/23 19:24, Adrian Hunter wrote:
> Hi
>
> Hardware traces, such as instruction traces, can produce a vast amount of
> trace data, so being able to reduce tracing to more specific circumstances
> can be useful.
>
> The ability to pause or resume tracing when another event happens, can do
> that.
>
> These patches add such a facilty and show how it would work for Intel
> Processor Trace.
>
> Maintainers of other AUX area tracing implementations are requested to
> consider if this is something they might employ and then whether or not
> the ABI would work for them.
>
> Changes to perf tools are not fleshed out yet.
>
>
> Changes in RFC V2:
>
> Use ->stop() / ->start() instead of ->pause_resume()
> Move aux_start_paused bit into aux_output_cfg
> Tighten up when Intel PT pause / resume is allowed
> Add an example of how it might work for CoreSight
Any comments?
>
>
> Adrian Hunter (4):
> perf/core: Add aux_pause, aux_resume, aux_start_paused
> perf/x86/intel/pt: Add support for pause / resume
> perf tools: Add support for AUX area pause / resume
> coresight: Have a stab at support for pause / resume
>
> arch/x86/events/intel/pt.c | 63 ++++++++++++++++++++-
> arch/x86/events/intel/pt.h | 4 ++
> drivers/hwtracing/coresight/coresight-etm-perf.c | 29 ++++++++--
> include/linux/perf_event.h | 15 +++++
> include/uapi/linux/perf_event.h | 11 +++-
> kernel/events/core.c | 72 +++++++++++++++++++++++-
> kernel/events/internal.h | 1 +
> tools/include/uapi/linux/perf_event.h | 11 +++-
> tools/perf/util/auxtrace.c | 4 ++
> tools/perf/util/evsel.c | 9 +++
> tools/perf/util/evsel_config.h | 6 ++
> tools/perf/util/parse-events.c | 33 +++++++++++
> tools/perf/util/parse-events.h | 3 +
> tools/perf/util/parse-events.l | 3 +
> tools/perf/util/perf_event_attr_fprintf.c | 3 +
> 15 files changed, 255 insertions(+), 12 deletions(-)
>
>
> Regards
> Adrian
On 15/12/2023 06:42, Adrian Hunter wrote:
> For discussion only, un-tested...
>
If anyone wants to test Coresight, the diff below is required to get the
most basic use case working. It also probably needs more thought and
some edge case handling:
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 596c01e37624..bd0767356277 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -556,7 +556,8 @@ static void etm_event_stop(struct perf_event *event, int mode)
struct etm_event_data *event_data;
struct list_head *path;
- if (mode & PERF_EF_PAUSE && !READ_ONCE(ctxt->pr_allowed))
+ if ((mode & PERF_EF_PAUSE && !READ_ONCE(ctxt->pr_allowed)) ||
+ event->hw.state == PERF_HES_STOPPED)
return;
WRITE_ONCE(ctxt->pr_allowed, 0);
@@ -573,9 +574,6 @@ static void etm_event_stop(struct perf_event *event, int mode)
/* Clear the event_data as this ETM is stopping the trace. */
ctxt->event_data = NULL;
- if (event->hw.state == PERF_HES_STOPPED)
- goto out_pr_allowed;
-
/* We must have a valid event_data for a running event */
if (WARN_ON(!event_data))
return;
@@ -586,7 +584,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
* nothing needs to be torn down other than outputting a
* zero sized record.
*/
- if (handle->event && (mode & PERF_EF_UPDATE) &&
+ if (handle->event && (mode & (PERF_EF_UPDATE | PERF_EF_PAUSE)) &&
!cpumask_test_cpu(cpu, &event_data->mask)) {
event->hw.state = PERF_HES_STOPPED;
perf_aux_output_end(handle, 0);
@@ -616,7 +614,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
* handle due to lack of buffer space), we don't
* have to do anything here.
*/
- if (handle->event && (mode & PERF_EF_UPDATE)) {
+ if (handle->event && (mode & (PERF_EF_UPDATE | PERF_EF_PAUSE))) {
if (WARN_ON_ONCE(handle->event != event))
return;
@@ -646,7 +644,6 @@ static void etm_event_stop(struct perf_event *event, int mode)
/* Disabling the path make its elements available to other sessions */
coresight_disable_path(path);
-out_pr_allowed:
if (mode & PERF_EF_PAUSE)
WRITE_ONCE(ctxt->pr_allowed, 1);
}
@@ -656,7 +653,7 @@ static int etm_event_add(struct perf_event *event, int mode)
int ret = 0;
struct hw_perf_event *hwc = &event->hw;
- if (mode & PERF_EF_START && !READ_ONCE(event->aux_paused)) {
+ if (mode & PERF_EF_START) {
etm_event_start(event, 0);
if (hwc->state & PERF_HES_STOPPED)
ret = -EINVAL;
--
2.34.1
On 20/12/23 17:59, James Clark wrote:
>
>
> On 15/12/2023 06:42, Adrian Hunter wrote:
>> For discussion only, un-tested...
>>
>
> If anyone wants to test Coresight, the diff below is required to get the
> most basic use case working. It also probably needs more thought and
> some edge case handling:
Makes sense to me
>
> diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
> index 596c01e37624..bd0767356277 100644
> --- a/drivers/hwtracing/coresight/coresight-etm-perf.c
> +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
> @@ -556,7 +556,8 @@ static void etm_event_stop(struct perf_event *event, int mode)
> struct etm_event_data *event_data;
> struct list_head *path;
>
> - if (mode & PERF_EF_PAUSE && !READ_ONCE(ctxt->pr_allowed))
> + if ((mode & PERF_EF_PAUSE && !READ_ONCE(ctxt->pr_allowed)) ||
> + event->hw.state == PERF_HES_STOPPED)
> return;
>
> WRITE_ONCE(ctxt->pr_allowed, 0);
> @@ -573,9 +574,6 @@ static void etm_event_stop(struct perf_event *event, int mode)
> /* Clear the event_data as this ETM is stopping the trace. */
> ctxt->event_data = NULL;
>
> - if (event->hw.state == PERF_HES_STOPPED)
> - goto out_pr_allowed;
> -
> /* We must have a valid event_data for a running event */
> if (WARN_ON(!event_data))
> return;
> @@ -586,7 +584,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
> * nothing needs to be torn down other than outputting a
> * zero sized record.
> */
> - if (handle->event && (mode & PERF_EF_UPDATE) &&
> + if (handle->event && (mode & (PERF_EF_UPDATE | PERF_EF_PAUSE)) &&
> !cpumask_test_cpu(cpu, &event_data->mask)) {
> event->hw.state = PERF_HES_STOPPED;
> perf_aux_output_end(handle, 0);
> @@ -616,7 +614,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
> * handle due to lack of buffer space), we don't
> * have to do anything here.
> */
> - if (handle->event && (mode & PERF_EF_UPDATE)) {
> + if (handle->event && (mode & (PERF_EF_UPDATE | PERF_EF_PAUSE))) {
> if (WARN_ON_ONCE(handle->event != event))
> return;
>
> @@ -646,7 +644,6 @@ static void etm_event_stop(struct perf_event *event, int mode)
> /* Disabling the path make its elements available to other sessions */
> coresight_disable_path(path);
>
> -out_pr_allowed:
> if (mode & PERF_EF_PAUSE)
> WRITE_ONCE(ctxt->pr_allowed, 1);
> }
> @@ -656,7 +653,7 @@ static int etm_event_add(struct perf_event *event, int mode)
> int ret = 0;
> struct hw_perf_event *hwc = &event->hw;
>
> - if (mode & PERF_EF_START && !READ_ONCE(event->aux_paused)) {
> + if (mode & PERF_EF_START) {
> etm_event_start(event, 0);
> if (hwc->state & PERF_HES_STOPPED)
> ret = -EINVAL;