aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <Marc.Zyngier@arm.com>2012-09-13 11:40:46 -0400
committerWill Deacon <will.deacon@arm.com>2012-11-09 06:37:24 -0500
commite50c54189f7c6211a99539156e3978474f0b1a0b (patch)
treef2875cfc036a202270427471dd53e16f0ae48381
parent3d70f8c617a436c7146ecb81df2265b4626dfe89 (diff)
ARM: perf: add guest vs host discrimination
Add minimal guest support to perf, so it can distinguish whether the PMU interrupt was in the host or the guest, as well as collecting some very basic information (guest PC, user vs kernel mode). This is not feature complete though, as it doesn't support backtracing in the guest. Based on the x86 implementation, tested with KVM/ARM. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--arch/arm/include/asm/perf_event.h5
-rw-r--r--arch/arm/kernel/perf_event.c36
2 files changed, 41 insertions, 0 deletions
diff --git a/arch/arm/include/asm/perf_event.h b/arch/arm/include/asm/perf_event.h
index 625cd621a436..00416edecead 100644
--- a/arch/arm/include/asm/perf_event.h
+++ b/arch/arm/include/asm/perf_event.h
@@ -21,4 +21,9 @@
21#define C(_x) PERF_COUNT_HW_CACHE_##_x 21#define C(_x) PERF_COUNT_HW_CACHE_##_x
22#define CACHE_OP_UNSUPPORTED 0xFFFF 22#define CACHE_OP_UNSUPPORTED 0xFFFF
23 23
24struct pt_regs;
25extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
26extern unsigned long perf_misc_flags(struct pt_regs *regs);
27#define perf_misc_flags(regs) perf_misc_flags(regs)
28
24#endif /* __ARM_PERF_EVENT_H__ */ 29#endif /* __ARM_PERF_EVENT_H__ */
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 53c0304b734a..f8406af03279 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -576,6 +576,10 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
576{ 576{
577 struct frame_tail __user *tail; 577 struct frame_tail __user *tail;
578 578
579 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
580 /* We don't support guest os callchain now */
581 return;
582 }
579 583
580 tail = (struct frame_tail __user *)regs->ARM_fp - 1; 584 tail = (struct frame_tail __user *)regs->ARM_fp - 1;
581 585
@@ -603,9 +607,41 @@ perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
603{ 607{
604 struct stackframe fr; 608 struct stackframe fr;
605 609
610 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
611 /* We don't support guest os callchain now */
612 return;
613 }
614
606 fr.fp = regs->ARM_fp; 615 fr.fp = regs->ARM_fp;
607 fr.sp = regs->ARM_sp; 616 fr.sp = regs->ARM_sp;
608 fr.lr = regs->ARM_lr; 617 fr.lr = regs->ARM_lr;
609 fr.pc = regs->ARM_pc; 618 fr.pc = regs->ARM_pc;
610 walk_stackframe(&fr, callchain_trace, entry); 619 walk_stackframe(&fr, callchain_trace, entry);
611} 620}
621
622unsigned long perf_instruction_pointer(struct pt_regs *regs)
623{
624 if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
625 return perf_guest_cbs->get_guest_ip();
626
627 return instruction_pointer(regs);
628}
629
630unsigned long perf_misc_flags(struct pt_regs *regs)
631{
632 int misc = 0;
633
634 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
635 if (perf_guest_cbs->is_user_mode())
636 misc |= PERF_RECORD_MISC_GUEST_USER;
637 else
638 misc |= PERF_RECORD_MISC_GUEST_KERNEL;
639 } else {
640 if (user_mode(regs))
641 misc |= PERF_RECORD_MISC_USER;
642 else
643 misc |= PERF_RECORD_MISC_KERNEL;
644 }
645
646 return misc;
647}