lkml.org 
[lkml]   [2010]   [Mar]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
SubjectRe: [RFC,PATCH 2/2] perf, x86: Utilize the LBRs for machine/oops debugging
From
On Mon, Mar 29, 2010 at 1:20 PM, Peter Zijlstra <a.p.zijlstra@chello.nl> wrote:
> The LBRs are relatively cheap to keep enabled and provide some history
> to OOPSen, also some CPUs are reported to keep them over soft-reset,
> which allows us to use them to debug things like tripple faults.
>
> Therefore introduce a boot option: lbr_debug=on, which always enable
> the LBRs and will print the LBRs on CPU init and die().
>

When this is enabled, it will prevent changing the LBR configuration to
record only selected branches. Unless you are willing to accept filtered
content in the kernel dump.


> Requested-by: Linus Torvalds <torvalds@linux-foundation.org>
> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
> ---
>  arch/x86/include/asm/perf_event.h          |    7 ++
>  arch/x86/kernel/cpu/perf_event_intel.c     |    5 -
>  arch/x86/kernel/cpu/perf_event_intel_lbr.c |   86 +++++++++++++++++++++++++++--
>  arch/x86/kernel/dumpstack.c                |    5 +
>  4 files changed, 95 insertions(+), 8 deletions(-)
>
> Index: linux-2.6/arch/x86/include/asm/perf_event.h
> ===================================================================
> --- linux-2.6.orig/arch/x86/include/asm/perf_event.h
> +++ linux-2.6/arch/x86/include/asm/perf_event.h
> @@ -155,9 +155,14 @@ extern void perf_events_lapic_init(void)
>
>  #define perf_instruction_pointer(regs) ((regs)->ip)
>
> +void dump_lbr_state(void);
> +void lbr_off(void);
> +
>  #else
>  static inline void init_hw_perf_events(void)           { }
> -static inline void perf_events_lapic_init(void)        { }
> +static inline void perf_events_lapic_init(void)                { }
> +static inline void dump_lbr_state(void)                        { }
> +static inline void lbr_off(void)                       { }
>  #endif
>
>  #endif /* _ASM_X86_PERF_EVENT_H */
> Index: linux-2.6/arch/x86/kernel/cpu/perf_event_intel.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/cpu/perf_event_intel.c
> +++ linux-2.6/arch/x86/kernel/cpu/perf_event_intel.c
> @@ -804,10 +804,7 @@ static __initconst const struct x86_pmu
>  static void intel_pmu_cpu_starting(int cpu)
>  {
>        init_debug_store_on_cpu(cpu);
> -       /*
> -        * Deal with CPUs that don't clear their LBRs on power-up.
> -        */
> -       intel_pmu_lbr_reset();
> +       intel_pmu_lbr_starting();
>  }
>
>  static void intel_pmu_cpu_dying(int cpu)
> Index: linux-2.6/arch/x86/kernel/cpu/perf_event_intel_lbr.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/cpu/perf_event_intel_lbr.c
> +++ linux-2.6/arch/x86/kernel/cpu/perf_event_intel_lbr.c
> @@ -1,12 +1,32 @@
>  #ifdef CONFIG_CPU_SUP_INTEL
>
>  enum {
> +       LBR_DEBUG_OFF           = 0,
> +       LBR_DEBUG_ON            = 1,
> +};
> +
> +static int lbr_debug_state __read_mostly;
> +
> +static int __init setup_lbr_debug(char *str)
> +{
> +       if (!strcmp(str, "on"))
> +               lbr_debug_state = LBR_DEBUG_ON;
> +       else
> +               return 0;
> +       return 1;
> +}
> +
> +__setup("lbr_debug=", setup_lbr_debug);
> +
> +enum {
>        LBR_FORMAT_32           = 0x00,
>        LBR_FORMAT_LIP          = 0x01,
>        LBR_FORMAT_EIP          = 0x02,
>        LBR_FORMAT_EIP_FLAGS    = 0x03,
>  };
>
> +static DEFINE_PER_CPU(int, lbr_print_done);
> +
>  /*
>  * We only support LBR implementations that have FREEZE_LBRS_ON_PMI
>  * otherwise it becomes near impossible to get a reliable stack.
> @@ -50,9 +70,6 @@ static void intel_pmu_lbr_reset_64(void)
>
>  static void intel_pmu_lbr_reset(void)
>  {
> -       if (!x86_pmu.lbr_nr)
> -               return;
> -
>        if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_32)
>                intel_pmu_lbr_reset_32();
>        else
> @@ -182,6 +199,8 @@ static void intel_pmu_lbr_read(void)
>  {
>        struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
>
> +       cpuc->lbr_stack.nr = 0;
> +
>        if (!cpuc->lbr_users)
>                return;
>
> @@ -215,4 +234,65 @@ static void intel_pmu_lbr_init_atom(void
>        x86_pmu.lbr_to     = 0x60;
>  }
>
> +static void __dump_lbr_state(void)
> +{
> +       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +       int i;
> +
> +       intel_pmu_lbr_read();
> +       for (i = 0; i < cpuc->lbr_stack.nr; i++) {
> +               printk(KERN_DEBUG "CPU%d LBR%d: %pF -> %pF\n",
> +                               smp_processor_id(), i,
> +                               (void *)cpuc->lbr_entries[i].from,
> +                               (void *)cpuc->lbr_entries[i].to);
> +       }
> +}
> +
> +static void intel_pmu_lbr_starting(void)
> +{
> +       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +
> +       if (!x86_pmu.lbr_nr)
> +               return;
> +
> +       cpuc->lbr_users = 1;
> +       if (lbr_debug_state && !__get_cpu_var(lbr_print_done)) {
> +               __get_cpu_var(lbr_print_done) = 1;
> +               __dump_lbr_state();
> +       }
> +
> +       intel_pmu_lbr_reset();
> +
> +       if (lbr_debug_state)
> +               __intel_pmu_lbr_enable();
> +       else
> +               cpuc->lbr_users = 0;
> +}
> +
> +void dump_lbr_state(void)
> +{
> +       if (!lbr_debug_state)
> +               return;
> +
> +       __dump_lbr_state();
> +}
> +
> +void lbr_off(void)
> +{
> +       if (!lbr_debug_state)
> +               return;
> +
> +       __intel_pmu_lbr_disable();
> +}
> +
> +#else
> +
> +void dump_lbr_state(void)
> +{
> +}
> +
> +void lbr_off(void)
> +{
> +}
> +
>  #endif /* CONFIG_CPU_SUP_INTEL */
> Index: linux-2.6/arch/x86/kernel/dumpstack.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/dumpstack.c
> +++ linux-2.6/arch/x86/kernel/dumpstack.c
> @@ -17,6 +17,7 @@
>  #include <linux/sysfs.h>
>
>  #include <asm/stacktrace.h>
> +#include <asm/perf_event.h>
>
>  #include "dumpstack.h"
>
> @@ -224,6 +225,8 @@ unsigned __kprobes long oops_begin(void)
>        int cpu;
>        unsigned long flags;
>
> +       lbr_off();
> +
>        oops_enter();
>
>        /* racy, but better than risking deadlock. */
> @@ -306,6 +309,7 @@ int __kprobes __die(const char *str, str
>        printk_address(regs->ip, 1);
>        printk(" RSP <%016lx>\n", regs->sp);
>  #endif
> +       dump_lbr_state();
>        return 0;
>  }
>
> @@ -343,6 +347,7 @@ die_nmi(char *str, struct pt_regs *regs,
>        printk(" on CPU%d, ip %08lx, registers:\n",
>                smp_processor_id(), regs->ip);
>        show_registers(regs);
> +       dump_lbr_state();
>        oops_end(flags, regs, 0);
>        if (do_panic || panic_on_oops)
>                panic("Non maskable interrupt");
>
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2010-03-29 14:49    [W:0.091 / U:0.736 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site