diff options
author | Robert Richter <robert.richter@amd.com> | 2012-04-02 20:19:11 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-05-09 15:23:13 +0200 |
commit | d47e8238cd76f1ffa7c8cd30e08b8e9074fd597e (patch) | |
tree | c172174db097b3afd7b36cc90a5b790e35a9f5f6 /arch/x86/kernel/cpu | |
parent | 6accb9cf76080422d400a641d9068b6b2a2c216f (diff) |
perf/x86-ibs: Take instruction pointer from ibs sample
Each IBS sample contains a linear address of the instruction that
caused the sample to trigger. This address is more precise than the
rip that was taken from the interrupt handler's stack. Update the rip
with that address. We use this in the next patch to implement
precise-event sampling on AMD systems using IBS.
Signed-off-by: Robert Richter <robert.richter@amd.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1333390758-10893-6-git-send-email-robert.richter@amd.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel/cpu')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_amd_ibs.c | 48 |
1 files changed, 31 insertions, 17 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index bc401bd9f14..cc1f3293d6c 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c @@ -9,6 +9,7 @@ #include <linux/perf_event.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/ptrace.h> #include <asm/apic.h> @@ -382,7 +383,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) struct perf_raw_record raw; struct pt_regs regs; struct perf_ibs_data ibs_data; - int offset, size, overflow, reenable; + int offset, size, check_rip, offset_max, throttle = 0; unsigned int msr; u64 *buf, config; @@ -413,28 +414,41 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) perf_ibs_event_update(perf_ibs, event, config); perf_sample_data_init(&data, 0, hwc->last_period); + if (!perf_ibs_set_period(perf_ibs, hwc, &config)) + goto out; /* no sw counter overflow */ + + ibs_data.caps = ibs_caps; + size = 1; + offset = 1; + check_rip = (perf_ibs == &perf_ibs_op && (ibs_caps & IBS_CAPS_RIPINVALIDCHK)); + if (event->attr.sample_type & PERF_SAMPLE_RAW) + offset_max = perf_ibs->offset_max; + else if (check_rip) + offset_max = 2; + else + offset_max = 1; + do { + rdmsrl(msr + offset, *buf++); + size++; + offset = find_next_bit(perf_ibs->offset_mask, + perf_ibs->offset_max, + offset + 1); + } while (offset < offset_max); + ibs_data.size = sizeof(u64) * size; + + regs = *iregs; + if (!check_rip || !(ibs_data.regs[2] & IBS_RIP_INVALID)) + instruction_pointer_set(®s, ibs_data.regs[1]); if (event->attr.sample_type & PERF_SAMPLE_RAW) { - ibs_data.caps = ibs_caps; - size = 1; - offset = 1; - do { - rdmsrl(msr + offset, *buf++); - size++; - offset = find_next_bit(perf_ibs->offset_mask, - perf_ibs->offset_max, - offset + 1); - } while (offset < perf_ibs->offset_max); - raw.size = sizeof(u32) + sizeof(u64) * size; + raw.size = sizeof(u32) + ibs_data.size; raw.data = ibs_data.data; data.raw = &raw; } - regs = *iregs; /* XXX: update ip from ibs sample */ - - overflow = perf_ibs_set_period(perf_ibs, hwc, &config); - reenable = !(overflow && perf_event_overflow(event, &data, ®s)); - config = (config >> 4) | (reenable ? perf_ibs->enable_mask : 0); + throttle = perf_event_overflow(event, &data, ®s); +out: + config = (config >> 4) | (throttle ? 0 : perf_ibs->enable_mask); perf_ibs_enable_event(hwc, config); perf_event_update_userpage(event); |