Author: gonzo
Date: Mon Mar 12 01:19:41 2012
New Revision: 232846
URL: http://svn.freebsd.org/changeset/base/232846

Log:
  Implement pmc_save_user_callchain and pmc_save_kernel_callchain for MIPS

Modified:
  head/sys/dev/hwpmc/hwpmc_mips.c

Modified: head/sys/dev/hwpmc/hwpmc_mips.c
==============================================================================
--- head/sys/dev/hwpmc/hwpmc_mips.c     Mon Mar 12 01:15:58 2012        
(r232845)
+++ head/sys/dev/hwpmc/hwpmc_mips.c     Mon Mar 12 01:19:41 2012        
(r232846)
@@ -34,6 +34,272 @@ __FBSDID("$FreeBSD$");
 
 #include <machine/pmc_mdep.h>
 #include <machine/md_var.h>
+#include <machine/mips_opcode.h>
+#include <machine/vmparam.h>
+
+#if defined(__mips_n64)
+#      define  MIPS_IS_VALID_KERNELADDR(reg)   ((((reg) & 3) == 0) && \
+                                       ((vm_offset_t)(reg) >= 
MIPS_XKPHYS_START))
+#else
+#      define  MIPS_IS_VALID_KERNELADDR(reg)   ((((reg) & 3) == 0) && \
+                                       ((vm_offset_t)(reg) >= 
MIPS_KSEG0_START))
+#endif
+
+/*
+ * We need some reasonable default to prevent backtrace code
+ * from wandering too far
+ */
+#define        MAX_FUNCTION_SIZE 0x10000
+#define        MAX_PROLOGUE_SIZE 0x100
+
+static int
+pmc_next_frame(register_t *pc, register_t *sp)
+{
+       InstFmt i;
+       uintptr_t va;
+       uint32_t instr, mask;
+       int more, stksize;
+       register_t ra = 0;
+
+       /* Jump here after a nonstandard (interrupt handler) frame */
+       stksize = 0;
+
+       /* check for bad SP: could foul up next frame */
+       if (!MIPS_IS_VALID_KERNELADDR(*sp)) {
+               goto error;
+       }
+
+       /* check for bad PC */
+       if (!MIPS_IS_VALID_KERNELADDR(*pc)) {
+               goto error;
+       }
+
+       /*
+        * Find the beginning of the current subroutine by scanning
+        * backwards from the current PC for the end of the previous
+        * subroutine.
+        */
+       va = *pc - sizeof(int);
+       while (1) {
+               instr = *((uint32_t *)va);
+
+               /* [d]addiu sp,sp,-X */
+               if (((instr & 0xffff8000) == 0x27bd8000)
+                   || ((instr & 0xffff8000) == 0x67bd8000))
+                       break;
+
+               /* jr   ra */
+               if (instr == 0x03e00008) {
+                       /* skip over branch-delay slot instruction */
+                       va += 2 * sizeof(int);
+                       break;
+               }
+
+               va -= sizeof(int);
+       }
+
+       /* skip over nulls which might separate .o files */
+       while ((instr = *((uint32_t *)va)) == 0)
+               va += sizeof(int);
+
+       /* scan forwards to find stack size and any saved registers */
+       stksize = 0;
+       more = 3;
+       mask = 0;
+       for (; more; va += sizeof(int),
+           more = (more == 3) ? 3 : more - 1) {
+               /* stop if hit our current position */
+               if (va >= *pc)
+                       break;
+               instr = *((uint32_t *)va);
+               i.word = instr;
+               switch (i.JType.op) {
+               case OP_SPECIAL:
+                       switch (i.RType.func) {
+                       case OP_JR:
+                       case OP_JALR:
+                               more = 2;       /* stop after next instruction 
*/
+                               break;
+
+                       case OP_SYSCALL:
+                       case OP_BREAK:
+                               more = 1;       /* stop now */
+                       };
+                       break;
+
+               case OP_BCOND:
+               case OP_J:
+               case OP_JAL:
+               case OP_BEQ:
+               case OP_BNE:
+               case OP_BLEZ:
+               case OP_BGTZ:
+                       more = 2;       /* stop after next instruction */
+                       break;
+
+               case OP_COP0:
+               case OP_COP1:
+               case OP_COP2:
+               case OP_COP3:
+                       switch (i.RType.rs) {
+                       case OP_BCx:
+                       case OP_BCy:
+                               more = 2;       /* stop after next instruction 
*/
+                       };
+                       break;
+
+               case OP_SW:
+               case OP_SD:
+                       /* look for saved registers on the stack */
+                       if (i.IType.rs != 29)
+                               break;
+                       /* only restore the first one */
+                       if (mask & (1 << i.IType.rt))
+                               break;
+                       mask |= (1 << i.IType.rt);
+                       if (i.IType.rt == 31)
+                               ra = *((register_t *)(*sp + 
(short)i.IType.imm));
+                       break;
+
+               case OP_ADDI:
+               case OP_ADDIU:
+               case OP_DADDI:
+               case OP_DADDIU:
+                       /* look for stack pointer adjustment */
+                       if (i.IType.rs != 29 || i.IType.rt != 29)
+                               break;
+                       stksize = -((short)i.IType.imm);
+               }
+       }
+
+       if (!MIPS_IS_VALID_KERNELADDR(ra))
+               return (-1);
+
+       *pc = ra;
+       *sp += stksize;
+
+       return (0);
+
+error:
+       return (-1);
+}
+
+static int
+pmc_next_uframe(register_t *pc, register_t *sp, register_t *ra)
+{
+       int offset, registers_on_stack;
+       uint32_t opcode, mask;
+       register_t function_start;
+       int stksize;
+       InstFmt i;
+
+       registers_on_stack = 0;
+       mask = 0;
+       function_start = 0;
+       offset = 0;
+       stksize = 0;
+
+       while (offset < MAX_FUNCTION_SIZE) {
+               opcode = fuword32((void *)(*pc - offset));
+
+               /* [d]addiu sp, sp, -X*/
+               if (((opcode & 0xffff8000) == 0x27bd8000)
+                   || ((opcode & 0xffff8000) == 0x67bd8000)) {
+                       function_start = *pc - offset;
+                       registers_on_stack = 1;
+                       break;
+               }
+
+               /* lui gp, X */
+               if ((opcode & 0xffff8000) == 0x3c1c0000) {
+                       /*
+                        * Function might start with this instruction
+                        * Keep an eye on "jr ra" and sp correction
+                        * with positive value further on
+                        */
+                       function_start = *pc - offset;
+               }
+
+               if (function_start) {
+                       /*
+                        * Stop looking further. Possible end of
+                        * function instruction: it means there is no
+                        * stack modifications, sp is unchanged
+                        */
+
+                       /* [d]addiu sp,sp,X */
+                       if (((opcode & 0xffff8000) == 0x27bd0000)
+                           || ((opcode & 0xffff8000) == 0x67bd0000))
+                               break;
+
+                       if (opcode == 0x03e00008)
+                               break;
+               }
+
+               offset += sizeof(int);
+       }
+
+       if (!function_start)
+               return (-1);
+
+       if (registers_on_stack) {
+               offset = 0;
+               while ((offset < MAX_PROLOGUE_SIZE)
+                   && ((function_start + offset) < *pc)) {
+                       i.word = fuword32((void *)(function_start + offset));
+                       switch (i.JType.op) {
+                       case OP_SW:
+                               /* look for saved registers on the stack */
+                               if (i.IType.rs != 29)
+                                       break;
+                               /* only restore the first one */
+                               if (mask & (1 << i.IType.rt))
+                                       break;
+                               mask |= (1 << i.IType.rt);
+                               if (i.IType.rt == 31)
+                                       *ra = fuword32((void *)(*sp + 
(short)i.IType.imm));
+                               break;
+
+#if defined(__mips_n64)
+                       case OP_SD:
+                               /* look for saved registers on the stack */
+                               if (i.IType.rs != 29)
+                                       break;
+                               /* only restore the first one */
+                               if (mask & (1 << i.IType.rt))
+                                       break;
+                               mask |= (1 << i.IType.rt);
+                               /* ra */
+                               if (i.IType.rt == 31)
+                                       *ra = fuword64((void *)(*sp + 
(short)i.IType.imm));
+                       break;
+#endif
+
+                       case OP_ADDI:
+                       case OP_ADDIU:
+                       case OP_DADDI:
+                       case OP_DADDIU:
+                               /* look for stack pointer adjustment */
+                               if (i.IType.rs != 29 || i.IType.rt != 29)
+                                       break;
+                               stksize = -((short)i.IType.imm);
+                       }
+
+                       offset += sizeof(int);
+               }
+       }
+
+       /*
+        * We reached the end of backtrace
+        */
+       if (*pc == *ra)
+               return (-1);
+
+       *pc = *ra;
+       *sp += stksize;
+
+       return (0);
+}
 
 struct pmc_mdep *
 pmc_md_initialize()
@@ -55,21 +321,53 @@ pmc_md_finalize(struct pmc_mdep *md)
 }
 
 int
-pmc_save_kernel_callchain(uintptr_t *cc, int maxsamples,
+pmc_save_kernel_callchain(uintptr_t *cc, int nframes,
     struct trapframe *tf)
 {
-       (void) cc;
-       (void) maxsamples;
-       (void) tf;
-       return (0);
+       register_t pc, ra, sp;
+       int frames = 0;
+
+       pc = (uint64_t)tf->pc;
+       sp = (uint64_t)tf->sp;
+       ra = (uint64_t)tf->ra;
+
+       /*
+        * Unwind, and unwind, and unwind
+        */
+       while (1) {
+               cc[frames++] = pc;
+               if (frames >= nframes)
+                       break;
+
+               if (pmc_next_frame(&pc, &sp) < 0)
+                       break;
+       }
+
+       return (frames);
 }
 
 int
-pmc_save_user_callchain(uintptr_t *cc, int maxsamples,
+pmc_save_user_callchain(uintptr_t *cc, int nframes,
     struct trapframe *tf)
 {
-       (void) cc;
-       (void) maxsamples;
-       (void) tf;
-       return (0);
+       register_t pc, ra, sp;
+       int frames = 0;
+
+       pc = (uint64_t)tf->pc;
+       sp = (uint64_t)tf->sp;
+       ra = (uint64_t)tf->ra;
+
+       /*
+        * Unwind, and unwind, and unwind
+        */
+       while (1) {
+               cc[frames++] = pc;
+               if (frames >= nframes)
+                       break;
+
+               if (pmc_next_uframe(&pc, &sp, &ra) < 0)
+                       break;
+       }
+
+       return (frames);
 }
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to