From owner-svn-src-all@FreeBSD.ORG Mon Mar 12 01:19:42 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 2E626106564A; Mon, 12 Mar 2012 01:19:42 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 192D38FC08; Mon, 12 Mar 2012 01:19:42 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q2C1JfKr035536; Mon, 12 Mar 2012 01:19:41 GMT (envelope-from gonzo@svn.freebsd.org) Received: (from gonzo@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q2C1JfjA035534; Mon, 12 Mar 2012 01:19:41 GMT (envelope-from gonzo@svn.freebsd.org) Message-Id: <201203120119.q2C1JfjA035534@svn.freebsd.org> From: Oleksandr Tymoshenko Date: Mon, 12 Mar 2012 01:19:41 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r232846 - head/sys/dev/hwpmc X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 12 Mar 2012 01:19:42 -0000 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 #include +#include +#include + +#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); }