Implement pmc_save_user_callchain and pmc_save_kernel_callchain for MIPS

This commit is contained in:
Oleksandr Tymoshenko 2012-03-12 01:19:41 +00:00
parent f0dc1b857c
commit 5fca32c865

View File

@ -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);
}