arm64: perf: add guest vs host discrimination
authorMarc Zyngier <Marc.Zyngier@arm.com>
Wed, 23 Jan 2013 16:52:18 +0000 (16:52 +0000)
committerCatalin Marinas <catalin.marinas@arm.com>
Tue, 29 Jan 2013 16:56:17 +0000 (16:56 +0000)
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/arm64.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
arch/arm64/include/asm/perf_event.h
arch/arm64/kernel/perf_event.c

index a6fffd511c5e8a032188a136761b74f4e69152bd..d26d1d53c0d706bbe14f8000558cc04bac2f2ad3 100644 (file)
 #ifndef __ASM_PERF_EVENT_H
 #define __ASM_PERF_EVENT_H
 
-/* It's quiet around here... */
+#ifdef CONFIG_HW_PERF_EVENTS
+struct pt_regs;
+extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
+extern unsigned long perf_misc_flags(struct pt_regs *regs);
+#define perf_misc_flags(regs)  perf_misc_flags(regs)
+#endif
 
 #endif
index f7073c7b1ca91de056f5ca80f13aad12e3383e91..1e49e5eb81e977fe44fccc369be1974617680cac 100644 (file)
@@ -1331,6 +1331,11 @@ void perf_callchain_user(struct perf_callchain_entry *entry,
 {
        struct frame_tail __user *tail;
 
+       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
+               /* We don't support guest os callchain now */
+               return;
+       }
+
        tail = (struct frame_tail __user *)regs->regs[29];
 
        while (entry->nr < PERF_MAX_STACK_DEPTH &&
@@ -1355,8 +1360,40 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry,
 {
        struct stackframe frame;
 
+       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
+               /* We don't support guest os callchain now */
+               return;
+       }
+
        frame.fp = regs->regs[29];
        frame.sp = regs->sp;
        frame.pc = regs->pc;
        walk_stackframe(&frame, callchain_trace, entry);
 }
+
+unsigned long perf_instruction_pointer(struct pt_regs *regs)
+{
+       if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
+               return perf_guest_cbs->get_guest_ip();
+
+       return instruction_pointer(regs);
+}
+
+unsigned long perf_misc_flags(struct pt_regs *regs)
+{
+       int misc = 0;
+
+       if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
+               if (perf_guest_cbs->is_user_mode())
+                       misc |= PERF_RECORD_MISC_GUEST_USER;
+               else
+                       misc |= PERF_RECORD_MISC_GUEST_KERNEL;
+       } else {
+               if (user_mode(regs))
+                       misc |= PERF_RECORD_MISC_USER;
+               else
+                       misc |= PERF_RECORD_MISC_KERNEL;
+       }
+
+       return misc;
+}