Commit bbfd5e4f authored by Kan Liang's avatar Kan Liang Committed by Ingo Molnar

perf/core: Add new branch sample type for HW index of raw branch records

The low level index is the index in the underlying hardware buffer of
the most recently captured taken branch which is always saved in
branch_entries[0]. It is very useful for reconstructing the call stack.
For example, in Intel LBR call stack mode, the depth of reconstructed
LBR call stack limits to the number of LBR registers. With the low level
index information, perf tool may stitch the stacks of two samples. The
reconstructed LBR call stack can break the HW limitation.

Add a new branch sample type to retrieve low level index of raw branch
records. The low level index is between -1 (unknown) and max depth which
can be retrieved in /sys/devices/cpu/caps/branches.

Only when the new branch sample type is set, the low level index
information is dumped into the PERF_SAMPLE_BRANCH_STACK output.
Perf tool should check the attr.branch_sample_type, and apply the
corresponding format for PERF_SAMPLE_BRANCH_STACK samples.
Otherwise, some user case may be broken. For example, users may parse a
perf.data, which include the new branch sample type, with an old version
perf tool (without the check). Users probably get incorrect information
without any warning.
Signed-off-by: default avatarKan Liang <kan.liang@linux.intel.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
Link: https://lkml.kernel.org/r/20200127165355.27495-2-kan.liang@linux.intel.com
parent 6c1c07b3
...@@ -518,6 +518,7 @@ static void power_pmu_bhrb_read(struct perf_event *event, struct cpu_hw_events * ...@@ -518,6 +518,7 @@ static void power_pmu_bhrb_read(struct perf_event *event, struct cpu_hw_events *
} }
} }
cpuhw->bhrb_stack.nr = u_index; cpuhw->bhrb_stack.nr = u_index;
cpuhw->bhrb_stack.hw_idx = -1ULL;
return; return;
} }
......
...@@ -585,6 +585,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) ...@@ -585,6 +585,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc)
cpuc->lbr_entries[i].reserved = 0; cpuc->lbr_entries[i].reserved = 0;
} }
cpuc->lbr_stack.nr = i; cpuc->lbr_stack.nr = i;
cpuc->lbr_stack.hw_idx = -1ULL;
} }
/* /*
...@@ -680,6 +681,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) ...@@ -680,6 +681,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc)
out++; out++;
} }
cpuc->lbr_stack.nr = out; cpuc->lbr_stack.nr = out;
cpuc->lbr_stack.hw_idx = -1ULL;
} }
void intel_pmu_lbr_read(void) void intel_pmu_lbr_read(void)
...@@ -1120,6 +1122,7 @@ void intel_pmu_store_pebs_lbrs(struct pebs_lbr *lbr) ...@@ -1120,6 +1122,7 @@ void intel_pmu_store_pebs_lbrs(struct pebs_lbr *lbr)
int i; int i;
cpuc->lbr_stack.nr = x86_pmu.lbr_nr; cpuc->lbr_stack.nr = x86_pmu.lbr_nr;
cpuc->lbr_stack.hw_idx = -1ULL;
for (i = 0; i < x86_pmu.lbr_nr; i++) { for (i = 0; i < x86_pmu.lbr_nr; i++) {
u64 info = lbr->lbr[i].info; u64 info = lbr->lbr[i].info;
struct perf_branch_entry *e = &cpuc->lbr_entries[i]; struct perf_branch_entry *e = &cpuc->lbr_entries[i];
......
...@@ -93,14 +93,26 @@ struct perf_raw_record { ...@@ -93,14 +93,26 @@ struct perf_raw_record {
/* /*
* branch stack layout: * branch stack layout:
* nr: number of taken branches stored in entries[] * nr: number of taken branches stored in entries[]
* hw_idx: The low level index of raw branch records
* for the most recent branch.
* -1ULL means invalid/unknown.
* *
* Note that nr can vary from sample to sample * Note that nr can vary from sample to sample
* branches (to, from) are stored from most recent * branches (to, from) are stored from most recent
* to least recent, i.e., entries[0] contains the most * to least recent, i.e., entries[0] contains the most
* recent branch. * recent branch.
* The entries[] is an abstraction of raw branch records,
* which may not be stored in age order in HW, e.g. Intel LBR.
* The hw_idx is to expose the low level index of raw
* branch record for the most recent branch aka entries[0].
* The hw_idx index is between -1 (unknown) and max depth,
* which can be retrieved in /sys/devices/cpu/caps/branches.
* For the architectures whose raw branch records are
* already stored in age order, the hw_idx should be 0.
*/ */
struct perf_branch_stack { struct perf_branch_stack {
__u64 nr; __u64 nr;
__u64 hw_idx;
struct perf_branch_entry entries[0]; struct perf_branch_entry entries[0];
}; };
......
...@@ -181,6 +181,8 @@ enum perf_branch_sample_type_shift { ...@@ -181,6 +181,8 @@ enum perf_branch_sample_type_shift {
PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, /* save branch type */ PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, /* save branch type */
PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, /* save low level index of raw branch records */
PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */ PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */
}; };
...@@ -208,6 +210,8 @@ enum perf_branch_sample_type { ...@@ -208,6 +210,8 @@ enum perf_branch_sample_type {
PERF_SAMPLE_BRANCH_TYPE_SAVE = PERF_SAMPLE_BRANCH_TYPE_SAVE =
1U << PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT, 1U << PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT,
PERF_SAMPLE_BRANCH_HW_INDEX = 1U << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT,
PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT, PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT,
}; };
...@@ -853,7 +857,9 @@ enum perf_event_type { ...@@ -853,7 +857,9 @@ enum perf_event_type {
* char data[size];}&& PERF_SAMPLE_RAW * char data[size];}&& PERF_SAMPLE_RAW
* *
* { u64 nr; * { u64 nr;
* { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK * { u64 hw_idx; } && PERF_SAMPLE_BRANCH_HW_INDEX
* { u64 from, to, flags } lbr[nr];
* } && PERF_SAMPLE_BRANCH_STACK
* *
* { u64 abi; # enum perf_sample_regs_abi * { u64 abi; # enum perf_sample_regs_abi
* u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
......
...@@ -6555,6 +6555,11 @@ static void perf_output_read(struct perf_output_handle *handle, ...@@ -6555,6 +6555,11 @@ static void perf_output_read(struct perf_output_handle *handle,
perf_output_read_one(handle, event, enabled, running); perf_output_read_one(handle, event, enabled, running);
} }
static inline bool perf_sample_save_hw_index(struct perf_event *event)
{
return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX;
}
void perf_output_sample(struct perf_output_handle *handle, void perf_output_sample(struct perf_output_handle *handle,
struct perf_event_header *header, struct perf_event_header *header,
struct perf_sample_data *data, struct perf_sample_data *data,
...@@ -6643,6 +6648,8 @@ void perf_output_sample(struct perf_output_handle *handle, ...@@ -6643,6 +6648,8 @@ void perf_output_sample(struct perf_output_handle *handle,
* sizeof(struct perf_branch_entry); * sizeof(struct perf_branch_entry);
perf_output_put(handle, data->br_stack->nr); perf_output_put(handle, data->br_stack->nr);
if (perf_sample_save_hw_index(event))
perf_output_put(handle, data->br_stack->hw_idx);
perf_output_copy(handle, data->br_stack->entries, size); perf_output_copy(handle, data->br_stack->entries, size);
} else { } else {
/* /*
...@@ -6836,6 +6843,9 @@ void perf_prepare_sample(struct perf_event_header *header, ...@@ -6836,6 +6843,9 @@ void perf_prepare_sample(struct perf_event_header *header,
if (sample_type & PERF_SAMPLE_BRANCH_STACK) { if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
int size = sizeof(u64); /* nr */ int size = sizeof(u64); /* nr */
if (data->br_stack) { if (data->br_stack) {
if (perf_sample_save_hw_index(event))
size += sizeof(u64);
size += data->br_stack->nr size += data->br_stack->nr
* sizeof(struct perf_branch_entry); * sizeof(struct perf_branch_entry);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment