Signed-off-by Andy Fleming <afleming at freescale.com>

* Added support for callgraphs (ie backtracing)

---
commit f3bcff9f931bdd36e8cd527408418ed5f3f7a85b
tree d08362251f25801221ff2c71e0371757b1fc49d0
parent 3294b2b37eb56ee4ad287776998293485a7c122e
author Andrew Fleming <afleming at freescale.com> Wed, 12 Oct 2005 16:09:23 
-0500
committer Andrew Fleming <afleming at freescale.com> Wed, 12 Oct 2005 16:09:23 
-0500

 arch/ppc/oprofile/Makefile             |    2 -
 arch/ppc/oprofile/backtrace.c          |  126 ++++++++++++++++++++++++++++++++
 arch/ppc/oprofile/common.c             |    6 +-
 arch/ppc/oprofile/op_model_fsl_booke.c |    7 --
 4 files changed, 133 insertions(+), 8 deletions(-)

diff --git a/arch/ppc/oprofile/Makefile b/arch/ppc/oprofile/Makefile
--- a/arch/ppc/oprofile/Makefile
+++ b/arch/ppc/oprofile/Makefile
@@ -6,7 +6,7 @@ DRIVER_OBJS := $(addprefix ../../../driv
                oprofilefs.o oprofile_stats.o \
                timer_int.o )
 
-oprofile-y := $(DRIVER_OBJS) common.o
+oprofile-y := $(DRIVER_OBJS) common.o backtrace.o
 
 ifeq ($(CONFIG_FSL_BOOKE),y)
        oprofile-y += op_model_fsl_booke.o
diff --git a/arch/ppc/oprofile/backtrace.c b/arch/ppc/oprofile/backtrace.c
new file mode 100644
--- /dev/null
+++ b/arch/ppc/oprofile/backtrace.c
@@ -0,0 +1,126 @@
+/*
+ * PPC 32 oprofile support
+ * Based on PPC64 oprofile backtrace support
+ * Copyright (C) 2005 Brian Rogan <bcr6 at cornell.edu>, IBM
+ *
+ * Copyright (C) Freescale Semiconductor, Inc 2005
+ *
+ * Author: Andy Fleming
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/sched.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+static unsigned int user_putsp32(unsigned int sp, int *is_first) {
+       unsigned int stack_frame[2], rv;
+       unsigned long t = sp;
+
+       /* If the page isn't accessible, then we've done all that we can do,
+        * a partial stack trace is better than none. 
+       */
+       rv = __copy_from_user_inatomic(stack_frame, (void *) t, 
sizeof(stack_frame));
+       if(rv != 0) {
+               /* The most likely reason for this is that we returned -EFAULT, 
+                * which means that we've done all that we can do from 
interrupt 
+                * context.  All other errors though are equally valid reasons 
to
+                * abort our backtrace. */
+               return 0;
+       }
+
+
+       /* SVR4 compliant executables have the Link Register offset by 4 bytes 
+        * from the beginning of the stack page */
+       //printk("Link was: %p\n", stack_frame[1]);
+       if(! *is_first) {
+               oprofile_add_trace(stack_frame[1]);
+       } else {
+               *is_first = 0;
+       }
+
+       /* Sanity check to make sure that the previous stack frame is actually 
above
+        * us in memory, like we would expect it to be from the PPC spec. 
+        * 
+        * The last stack pointer is always at offset 0 from the beginning of 
the 
+        * stack frame.
+        */
+       rv = stack_frame[0];
+       if(rv > sp) {
+               return rv;
+       } else {
+               return 0;
+       }
+}
+
+static int validate_sp(unsigned long sp)
+{
+       unsigned long prev_sp;
+       unsigned long stack_top;
+
+       prev_sp = (unsigned long) (current->thread_info + 1);
+       stack_top = (unsigned long) current->thread_info + THREAD_SIZE;
+
+       if (sp > prev_sp && sp < stack_top && (sp & 3) == 0)
+               return 1;
+
+       return 0;
+}
+
+static unsigned long kernel_putsp32(unsigned long sp, int *is_first) {
+       unsigned long * stack_frame = (unsigned long *) sp;
+       unsigned long rv;
+
+       if(!validate_sp(sp)) {
+               return 0;
+       }
+
+       //printk("Link was: %p\n", stack_frame[1]);
+       if(! *is_first) {
+               /* Same as before LR is offset 4 bytes from the beginning */
+               oprofile_add_trace(stack_frame[1]);             
+       } else {
+               *is_first = 0;
+       }
+
+       rv = stack_frame[0];
+       if(rv > sp) {
+               return rv;
+       } else {
+               return 0;
+       }
+}
+
+
+void
+op_ppc32_backtrace(struct pt_regs * const regs, unsigned int depth)
+{
+       unsigned long cursp = regs->gpr[1];
+       int first_frame = 1;
+
+       //printk("backtracing\n");
+       if (!user_mode(regs)) {
+               unsigned long sp = cursp;
+               while (depth--)
+               {
+                       sp = kernel_putsp32(sp, &first_frame);
+                       if(!sp) {
+                               break;
+                       }
+               }
+               return;
+       } else {
+               unsigned long sp = cursp;       
+               while(depth--) {
+                       sp = user_putsp32(sp, &first_frame);
+                       if(!sp) {
+                               break;
+                       }
+               }
+       }
+}
diff --git a/arch/ppc/oprofile/common.c b/arch/ppc/oprofile/common.c
--- a/arch/ppc/oprofile/common.c
+++ b/arch/ppc/oprofile/common.c
@@ -84,6 +84,9 @@ static void op_ppc32_stop(void)
        on_each_cpu(op_ppc32_cpu_stop, NULL, 0, 1);
 }
 
+extern void 
+op_ppc32_backtrace(struct pt_regs * const regs, unsigned int depth);
+
 static int op_ppc32_create_files(struct super_block *sb, struct dentry *root)
 {
        int i;
@@ -121,7 +124,8 @@ static struct oprofile_operations oprof_
        .shutdown       = op_ppc32_shutdown,
        .start          = op_ppc32_start,
        .stop           = op_ppc32_stop,
-       .cpu_type       = NULL          /* To be filled in below. */
+       .cpu_type       = NULL,         /* To be filled in below. */
+       .backtrace      = op_ppc32_backtrace,
 };
 
 int __init oprofile_arch_init(struct oprofile_operations *ops)
diff --git a/arch/ppc/oprofile/op_model_fsl_booke.c 
b/arch/ppc/oprofile/op_model_fsl_booke.c
--- a/arch/ppc/oprofile/op_model_fsl_booke.c
+++ b/arch/ppc/oprofile/op_model_fsl_booke.c
@@ -146,22 +146,17 @@ static void fsl_booke_stop(void)
 static void fsl_booke_handle_interrupt(struct pt_regs *regs,
                                    struct op_counter_config *ctr)
 {
-       unsigned long pc;
-       int is_kernel;
        int val;
        int i;
 
        /* set the PMM bit (see comment below) */
        mtmsr(mfmsr() | MSR_PMM);
 
-       pc = regs->nip;
-       is_kernel = (pc >= KERNELBASE);
-
        for (i = 0; i < num_counters; ++i) {
                val = ctr_read(i);
                if (val < 0) {
                        if (oprofile_running && ctr[i].enabled) {
-                               oprofile_add_pc(pc, is_kernel, i);
+                               oprofile_add_sample(regs, i);
                                ctr_write(i, reset_value[i]);
                        } else {
                                ctr_write(i, 0);

* Removed commented code

---
commit 7f50cb2cd6153dda4d999b13418dd8e3609def5c
tree 47ddd0af393ee7cb8dfa911fe5c3d383865fc217
parent f3bcff9f931bdd36e8cd527408418ed5f3f7a85b
author Andrew Fleming <afleming at freescale.com> Wed, 12 Oct 2005 16:12:05 
-0500
committer Andrew Fleming <afleming at freescale.com> Wed, 12 Oct 2005 16:12:05 
-0500

 arch/ppc/oprofile/backtrace.c |    3 ---
 1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/arch/ppc/oprofile/backtrace.c b/arch/ppc/oprofile/backtrace.c
--- a/arch/ppc/oprofile/backtrace.c
+++ b/arch/ppc/oprofile/backtrace.c
@@ -37,7 +37,6 @@ static unsigned int user_putsp32(unsigne
 
        /* SVR4 compliant executables have the Link Register offset by 4 bytes 
         * from the beginning of the stack page */
-       //printk("Link was: %p\n", stack_frame[1]);
        if(! *is_first) {
                oprofile_add_trace(stack_frame[1]);
        } else {
@@ -80,7 +79,6 @@ static unsigned long kernel_putsp32(unsi
                return 0;
        }
 
-       //printk("Link was: %p\n", stack_frame[1]);
        if(! *is_first) {
                /* Same as before LR is offset 4 bytes from the beginning */
                oprofile_add_trace(stack_frame[1]);             
@@ -103,7 +101,6 @@ op_ppc32_backtrace(struct pt_regs * cons
        unsigned long cursp = regs->gpr[1];
        int first_frame = 1;
 
-       //printk("backtracing\n");
        if (!user_mode(regs)) {
                unsigned long sp = cursp;
                while (depth--)

Reply via email to