Skip to content

Commit

Permalink
perf intel-pt: Add support for PERF_RECORD_AUX_OUTPUT_HW_ID
Browse files Browse the repository at this point in the history
Originally, software only supported redirecting at most one PEBS event to
Intel PT (PEBS-via-PT) because it was not able to differentiate one event
from another. To overcome that, add support for the
PERF_RECORD_AUX_OUTPUT_HW_ID side-band event.

Committer notes:

Cast the pointer arg to for_each_set_bit() to (unsigned long *), to fix
the build on 32-bit systems.

Reviewed-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: x86@kernel.org
Link: http://lore.kernel.org/lkml/20210907163903.11820-4-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
  • Loading branch information
Adrian Hunter authored and Arnaldo Carvalho de Melo committed Oct 25, 2021
1 parent 6175047 commit dedcc0e
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 5 deletions.
7 changes: 6 additions & 1 deletion tools/perf/Documentation/perf-intel-pt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,12 @@ Recording is selected by using the aux-output config term e.g.

perf record -c 10000 -e '{intel_pt/branch=0/,cycles/aux-output/ppp}' uname

Note that currently, software only supports redirecting at most one PEBS event.
Originally, software only supported redirecting at most one PEBS event because it
was not able to differentiate one event from another. To overcome that, more recent
kernels and perf tools add support for the PERF_RECORD_AUX_OUTPUT_HW_ID side-band event.
To check for the presence of that event in a PEBS-via-PT trace:

perf script -D --no-itrace | grep PERF_RECORD_AUX_OUTPUT_HW_ID

To display PEBS events from the Intel PT trace, use the itrace 'o' option e.g.

Expand Down
85 changes: 81 additions & 4 deletions tools/perf/util/intel-pt.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ struct intel_pt {
u64 cbr_id;
u64 psb_id;

bool single_pebs;
bool sample_pebs;
struct evsel *pebs_evsel;

Expand Down Expand Up @@ -148,6 +149,14 @@ enum switch_state {
INTEL_PT_SS_EXPECTING_SWITCH_IP,
};

/* applicable_counters is 64-bits */
#define INTEL_PT_MAX_PEBS 64

struct intel_pt_pebs_event {
struct evsel *evsel;
u64 id;
};

struct intel_pt_queue {
struct intel_pt *pt;
unsigned int queue_nr;
Expand Down Expand Up @@ -189,6 +198,7 @@ struct intel_pt_queue {
u64 last_br_cyc_cnt;
unsigned int cbr_seen;
char insn[INTEL_PT_INSN_BUF_SZ];
struct intel_pt_pebs_event pebs[INTEL_PT_MAX_PEBS];
};

static void intel_pt_dump(struct intel_pt *pt __maybe_unused,
Expand Down Expand Up @@ -1978,15 +1988,13 @@ static void intel_pt_add_lbrs(struct branch_stack *br_stack,
}
}

static int intel_pt_synth_pebs_sample(struct intel_pt_queue *ptq)
static int intel_pt_do_synth_pebs_sample(struct intel_pt_queue *ptq, struct evsel *evsel, u64 id)
{
const struct intel_pt_blk_items *items = &ptq->state->items;
struct perf_sample sample = { .ip = 0, };
union perf_event *event = ptq->event_buf;
struct intel_pt *pt = ptq->pt;
struct evsel *evsel = pt->pebs_evsel;
u64 sample_type = evsel->core.attr.sample_type;
u64 id = evsel->core.id[0];
u8 cpumode;
u64 regs[8 * sizeof(sample.intr_regs.mask)];

Expand Down Expand Up @@ -2112,6 +2120,45 @@ static int intel_pt_synth_pebs_sample(struct intel_pt_queue *ptq)
return intel_pt_deliver_synth_event(pt, event, &sample, sample_type);
}

static int intel_pt_synth_single_pebs_sample(struct intel_pt_queue *ptq)
{
struct intel_pt *pt = ptq->pt;
struct evsel *evsel = pt->pebs_evsel;
u64 id = evsel->core.id[0];

return intel_pt_do_synth_pebs_sample(ptq, evsel, id);
}

static int intel_pt_synth_pebs_sample(struct intel_pt_queue *ptq)
{
const struct intel_pt_blk_items *items = &ptq->state->items;
struct intel_pt_pebs_event *pe;
struct intel_pt *pt = ptq->pt;
int err = -EINVAL;
int hw_id;

if (!items->has_applicable_counters || !items->applicable_counters) {
if (!pt->single_pebs)
pr_err("PEBS-via-PT record with no applicable_counters\n");
return intel_pt_synth_single_pebs_sample(ptq);
}

for_each_set_bit(hw_id, (unsigned long *)&items->applicable_counters, INTEL_PT_MAX_PEBS) {
pe = &ptq->pebs[hw_id];
if (!pe->evsel) {
if (!pt->single_pebs)
pr_err("PEBS-via-PT record with no matching event, hw_id %d\n",
hw_id);
return intel_pt_synth_single_pebs_sample(ptq);
}
err = intel_pt_do_synth_pebs_sample(ptq, pe->evsel, pe->id);
if (err)
return err;
}

return err;
}

static int intel_pt_synth_error(struct intel_pt *pt, int code, int cpu,
pid_t pid, pid_t tid, u64 ip, u64 timestamp)
{
Expand Down Expand Up @@ -2882,6 +2929,30 @@ static int intel_pt_process_itrace_start(struct intel_pt *pt,
event->itrace_start.tid);
}

static int intel_pt_process_aux_output_hw_id(struct intel_pt *pt,
union perf_event *event,
struct perf_sample *sample)
{
u64 hw_id = event->aux_output_hw_id.hw_id;
struct auxtrace_queue *queue;
struct intel_pt_queue *ptq;
struct evsel *evsel;

queue = auxtrace_queues__sample_queue(&pt->queues, sample, pt->session);
evsel = evlist__id2evsel_strict(pt->session->evlist, sample->id);
if (!queue || !queue->priv || !evsel || hw_id > INTEL_PT_MAX_PEBS) {
pr_err("Bad AUX output hardware ID\n");
return -EINVAL;
}

ptq = queue->priv;

ptq->pebs[hw_id].evsel = evsel;
ptq->pebs[hw_id].id = sample->id;

return 0;
}

static int intel_pt_find_map(struct thread *thread, u8 cpumode, u64 addr,
struct addr_location *al)
{
Expand Down Expand Up @@ -3009,6 +3080,8 @@ static int intel_pt_process_event(struct perf_session *session,
err = intel_pt_process_switch(pt, sample);
else if (event->header.type == PERF_RECORD_ITRACE_START)
err = intel_pt_process_itrace_start(pt, event, sample);
else if (event->header.type == PERF_RECORD_AUX_OUTPUT_HW_ID)
err = intel_pt_process_aux_output_hw_id(pt, event, sample);
else if (event->header.type == PERF_RECORD_SWITCH ||
event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)
err = intel_pt_context_switch(pt, event, sample);
Expand Down Expand Up @@ -3393,9 +3466,13 @@ static void intel_pt_setup_pebs_events(struct intel_pt *pt)

evlist__for_each_entry(pt->session->evlist, evsel) {
if (evsel->core.attr.aux_output && evsel->core.id) {
if (pt->single_pebs) {
pt->single_pebs = false;
return;
}
pt->single_pebs = true;
pt->sample_pebs = true;
pt->pebs_evsel = evsel;
return;
}
}
}
Expand Down

0 comments on commit dedcc0e

Please sign in to comment.