diff options
author | rpaulo <rpaulo@FreeBSD.org> | 2010-08-22 10:53:32 +0000 |
---|---|---|
committer | rpaulo <rpaulo@FreeBSD.org> | 2010-08-22 10:53:32 +0000 |
commit | 2b32a31ca3484e23ff35b71b7349d361ba187ee3 (patch) | |
tree | 5331cdd87b36fe6938f80fbe8dc90043bcfae583 /sys/cddl/dev/dtrace/i386 | |
parent | 3a636008c7f8dee94de368be809ac7ca686fba2b (diff) | |
download | FreeBSD-src-2b32a31ca3484e23ff35b71b7349d361ba187ee3.zip FreeBSD-src-2b32a31ca3484e23ff35b71b7349d361ba187ee3.tar.gz |
Kernel DTrace support for:
o uregs (sson@)
o ustack (sson@)
o /dev/dtrace/helper device (needed for USDT probes)
The work done by me was:
Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'sys/cddl/dev/dtrace/i386')
-rw-r--r-- | sys/cddl/dev/dtrace/i386/dtrace_isa.c | 285 | ||||
-rw-r--r-- | sys/cddl/dev/dtrace/i386/regset.h | 127 |
2 files changed, 286 insertions, 126 deletions
diff --git a/sys/cddl/dev/dtrace/i386/dtrace_isa.c b/sys/cddl/dev/dtrace/i386/dtrace_isa.c index bf891aa..3f73a50 100644 --- a/sys/cddl/dev/dtrace/i386/dtrace_isa.c +++ b/sys/cddl/dev/dtrace/i386/dtrace_isa.c @@ -33,13 +33,17 @@ #include <sys/stack.h> #include <sys/pcpu.h> +#include <machine/frame.h> #include <machine/md_var.h> +#include <machine/pcb.h> #include <machine/stack.h> #include <vm/vm.h> #include <vm/vm_param.h> #include <vm/pmap.h> +#include "regset.h" + extern uintptr_t kernbase; uintptr_t kernelbase = (uintptr_t) &kernbase; @@ -100,21 +104,22 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes, } } -#ifdef notyet static int dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, uintptr_t sp) { - klwp_t *lwp = ttolwp(curthread); +#ifdef notyet proc_t *p = curproc; - uintptr_t oldcontext = lwp->lwp_oldcontext; + uintptr_t oldcontext = lwp->lwp_oldcontext; /* XXX signal stack. */ + size_t s1, s2; +#endif volatile uint16_t *flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; - size_t s1, s2; int ret = 0; ASSERT(pcstack == NULL || pcstack_limit > 0); +#ifdef notyet /* XXX signal stack. */ if (p->p_model == DATAMODEL_NATIVE) { s1 = sizeof (struct frame) + 2 * sizeof (long); s2 = s1 + sizeof (siginfo_t); @@ -122,8 +127,9 @@ dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, s1 = sizeof (struct frame32) + 3 * sizeof (int); s2 = s1 + sizeof (siginfo32_t); } +#endif - while (pc != 0 && sp != 0) { + while (pc != 0) { ret++; if (pcstack != NULL) { *pcstack++ = (uint64_t)pc; @@ -132,6 +138,10 @@ dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, break; } + if (sp == 0) + break; + +#ifdef notyet /* XXX signal stack. */ if (oldcontext == sp + s1 || oldcontext == sp + s2) { if (p->p_model == DATAMODEL_NATIVE) { ucontext_t *ucp = (ucontext_t *)oldcontext; @@ -163,6 +173,11 @@ dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, sp = dtrace_fuword32(&fr->fr_savfp); } } +#else + pc = dtrace_fuword32((void *)(sp + + offsetof(struct i386_frame, f_retaddr))); + sp = dtrace_fuword32((void *)sp); +#endif /* ! notyet */ /* * This is totally bogus: if we faulted, we're going to clear @@ -181,10 +196,9 @@ dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, void dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) { - klwp_t *lwp = ttolwp(curthread); proc_t *p = curproc; - struct regs *rp; - uintptr_t pc, sp; + struct trapframe *tf; + uintptr_t pc, sp, fp; volatile uint16_t *flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; int n; @@ -198,7 +212,7 @@ dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) /* * If there's no user context we still need to zero the stack. */ - if (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL) + if (p == NULL || (tf = curthread->td_frame) == NULL) goto zero; *pcstack++ = (uint64_t)p->p_pid; @@ -207,19 +221,26 @@ dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) if (pcstack_limit <= 0) return; - pc = rp->r_pc; - sp = rp->r_fp; + pc = tf->tf_eip; + fp = tf->tf_ebp; + sp = tf->tf_esp; if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { + /* + * In an entry probe. The frame pointer has not yet been + * pushed (that happens in the function prologue). The + * best approach is to add the current pc as a missing top + * of stack and back the pc up to the caller, which is stored + * at the current stack pointer address since the call + * instruction puts it there right before the branch. + */ + *pcstack++ = (uint64_t)pc; pcstack_limit--; if (pcstack_limit <= 0) return; - if (p->p_model == DATAMODEL_NATIVE) - pc = dtrace_fulword((void *)rp->r_sp); - else - pc = dtrace_fuword32((void *)rp->r_sp); + pc = dtrace_fuword32((void *) sp); } n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp); @@ -231,24 +252,58 @@ dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) zero: while (pcstack_limit-- > 0) - *pcstack++ = NULL; + *pcstack++ = 0; } int dtrace_getustackdepth(void) { + proc_t *p = curproc; + struct trapframe *tf; + uintptr_t pc, fp, sp; + int n = 0; + + if (p == NULL || (tf = curthread->td_frame) == NULL) + return (0); + + if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT)) + return (-1); + + pc = tf->tf_eip; + fp = tf->tf_ebp; + sp = tf->tf_esp; + + if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { + /* + * In an entry probe. The frame pointer has not yet been + * pushed (that happens in the function prologue). The + * best approach is to add the current pc as a missing top + * of stack and back the pc up to the caller, which is stored + * at the current stack pointer address since the call + * instruction puts it there right before the branch. + */ + + pc = dtrace_fuword32((void *) sp); + n++; + } + + n += dtrace_getustack_common(NULL, 0, pc, fp); + + return (n); } void dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) { - klwp_t *lwp = ttolwp(curthread); proc_t *p = curproc; - struct regs *rp; - uintptr_t pc, sp, oldcontext; + struct trapframe *tf; + uintptr_t pc, sp, fp; volatile uint16_t *flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; +#ifdef notyet /* XXX signal stack */ + uintptr_t oldcontext; size_t s1, s2; +#endif if (*flags & CPU_DTRACE_FAULT) return; @@ -259,7 +314,7 @@ dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) /* * If there's no user context we still need to zero the stack. */ - if (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL) + if (p == NULL || (tf = curthread->td_frame) == NULL) goto zero; *pcstack++ = (uint64_t)p->p_pid; @@ -268,8 +323,11 @@ dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) if (pcstack_limit <= 0) return; - pc = rp->r_pc; - sp = rp->r_fp; + pc = tf->tf_eip; + fp = tf->tf_ebp; + sp = tf->tf_esp; + +#ifdef notyet /* XXX signal stack */ oldcontext = lwp->lwp_oldcontext; if (p->p_model == DATAMODEL_NATIVE) { @@ -279,6 +337,7 @@ dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) s1 = sizeof (struct frame32) + 3 * sizeof (int); s2 = s1 + sizeof (siginfo32_t); } +#endif if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { *pcstack++ = (uint64_t)pc; @@ -287,19 +346,20 @@ dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) if (pcstack_limit <= 0) return; - if (p->p_model == DATAMODEL_NATIVE) - pc = dtrace_fulword((void *)rp->r_sp); - else - pc = dtrace_fuword32((void *)rp->r_sp); + pc = dtrace_fuword32((void *)sp); } - while (pc != 0 && sp != 0) { + while (pc != 0) { *pcstack++ = (uint64_t)pc; - *fpstack++ = sp; + *fpstack++ = fp; pcstack_limit--; if (pcstack_limit <= 0) break; + if (fp == 0) + break; + +#ifdef notyet /* XXX signal stack */ if (oldcontext == sp + s1 || oldcontext == sp + s2) { if (p->p_model == DATAMODEL_NATIVE) { ucontext_t *ucp = (ucontext_t *)oldcontext; @@ -318,18 +378,12 @@ dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) oldcontext = dtrace_fuword32(&ucp->uc_link); } - } else { - if (p->p_model == DATAMODEL_NATIVE) { - struct frame *fr = (struct frame *)sp; - - pc = dtrace_fulword(&fr->fr_savpc); - sp = dtrace_fulword(&fr->fr_savfp); - } else { - struct frame32 *fr = (struct frame32 *)sp; - - pc = dtrace_fuword32(&fr->fr_savpc); - sp = dtrace_fuword32(&fr->fr_savfp); - } + } else +#endif /* XXX */ + { + pc = dtrace_fuword32((void *)(fp + + offsetof(struct i386_frame, f_retaddr))); + fp = dtrace_fuword32((void *)fp); } /* @@ -345,9 +399,8 @@ dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) zero: while (pcstack_limit-- > 0) - *pcstack++ = NULL; + *pcstack++ = 0; } -#endif uint64_t dtrace_getarg(int arg, int aframes) @@ -424,112 +477,92 @@ dtrace_getstackdepth(int aframes) return depth - aframes; } -#ifdef notyet ulong_t -dtrace_getreg(struct regs *rp, uint_t reg) +dtrace_getreg(struct trapframe *rp, uint_t reg) { -#if defined(__amd64) - int regmap[] = { - REG_GS, /* GS */ - REG_FS, /* FS */ - REG_ES, /* ES */ - REG_DS, /* DS */ - REG_RDI, /* EDI */ - REG_RSI, /* ESI */ - REG_RBP, /* EBP */ - REG_RSP, /* ESP */ - REG_RBX, /* EBX */ - REG_RDX, /* EDX */ - REG_RCX, /* ECX */ - REG_RAX, /* EAX */ - REG_TRAPNO, /* TRAPNO */ - REG_ERR, /* ERR */ - REG_RIP, /* EIP */ - REG_CS, /* CS */ - REG_RFL, /* EFL */ - REG_RSP, /* UESP */ - REG_SS /* SS */ + struct pcb *pcb; + int regmap[] = { /* Order is dependent on reg.d */ + REG_GS, /* 0 GS */ + REG_FS, /* 1 FS */ + REG_ES, /* 2 ES */ + REG_DS, /* 3 DS */ + REG_RDI, /* 4 EDI */ + REG_RSI, /* 5 ESI */ + REG_RBP, /* 6 EBP, REG_FP */ + REG_RSP, /* 7 ESP */ + REG_RBX, /* 8 EBX */ + REG_RDX, /* 9 EDX, REG_R1 */ + REG_RCX, /* 10 ECX */ + REG_RAX, /* 11 EAX, REG_R0 */ + REG_TRAPNO, /* 12 TRAPNO */ + REG_ERR, /* 13 ERR */ + REG_RIP, /* 14 EIP, REG_PC */ + REG_CS, /* 15 CS */ + REG_RFL, /* 16 EFL, REG_PS */ + REG_RSP, /* 17 UESP, REG_SP */ + REG_SS /* 18 SS */ }; - if (reg <= SS) { - if (reg >= sizeof (regmap) / sizeof (int)) { - DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); - return (0); - } + if (reg > SS) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); + return (0); + } - reg = regmap[reg]; - } else { - reg -= SS + 1; + if (reg >= sizeof (regmap) / sizeof (int)) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); + return (0); } - switch (reg) { + reg = regmap[reg]; + + switch(reg) { + case REG_GS: + if ((pcb = curthread->td_pcb) == NULL) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); + return (0); + } + return (pcb->pcb_gs); + case REG_FS: + return (rp->tf_fs); + case REG_ES: + return (rp->tf_es); + case REG_DS: + return (rp->tf_ds); case REG_RDI: - return (rp->r_rdi); + return (rp->tf_edi); case REG_RSI: - return (rp->r_rsi); - case REG_RDX: - return (rp->r_rdx); + return (rp->tf_esi); + case REG_RBP: + return (rp->tf_ebp); + case REG_RSP: + return (rp->tf_isp); + case REG_RBX: + return (rp->tf_ebx); case REG_RCX: - return (rp->r_rcx); - case REG_R8: - return (rp->r_r8); - case REG_R9: - return (rp->r_r9); + return (rp->tf_ecx); case REG_RAX: - return (rp->r_rax); - case REG_RBX: - return (rp->r_rbx); - case REG_RBP: - return (rp->r_rbp); - case REG_R10: - return (rp->r_r10); - case REG_R11: - return (rp->r_r11); - case REG_R12: - return (rp->r_r12); - case REG_R13: - return (rp->r_r13); - case REG_R14: - return (rp->r_r14); - case REG_R15: - return (rp->r_r15); - case REG_DS: - return (rp->r_ds); - case REG_ES: - return (rp->r_es); - case REG_FS: - return (rp->r_fs); - case REG_GS: - return (rp->r_gs); + return (rp->tf_eax); case REG_TRAPNO: - return (rp->r_trapno); + return (rp->tf_trapno); case REG_ERR: - return (rp->r_err); + return (rp->tf_err); case REG_RIP: - return (rp->r_rip); + return (rp->tf_eip); case REG_CS: - return (rp->r_cs); - case REG_SS: - return (rp->r_ss); + return (rp->tf_cs); case REG_RFL: - return (rp->r_rfl); + return (rp->tf_eflags); +#if 0 case REG_RSP: - return (rp->r_rsp); + return (rp->tf_esp); +#endif + case REG_SS: + return (rp->tf_ss); default: DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return (0); } - -#else - if (reg > SS) { - DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); - return (0); - } - - return ((&rp->r_gs)[reg]); -#endif } -#endif static int dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size) diff --git a/sys/cddl/dev/dtrace/i386/regset.h b/sys/cddl/dev/dtrace/i386/regset.h new file mode 100644 index 0000000..d6732ff --- /dev/null +++ b/sys/cddl/dev/dtrace/i386/regset.h @@ -0,0 +1,127 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * $FreeBSD$ + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +#ifndef _REGSET_H +#define _REGSET_H + +/* + * #pragma ident "@(#)regset.h 1.11 05/06/08 SMI" + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The names and offsets defined here should be specified by the + * AMD64 ABI suppl. + * + * We make fsbase and gsbase part of the lwp context (since they're + * the only way to access the full 64-bit address range via the segment + * registers) and thus belong here too. However we treat them as + * read-only; if %fs or %gs are updated, the results of the descriptor + * table lookup that those updates implicitly cause will be reflected + * in the corresponding fsbase and/or gsbase values the next time the + * context can be inspected. However it is NOT possible to override + * the fsbase/gsbase settings via this interface. + * + * Direct modification of the base registers (thus overriding the + * descriptor table base address) can be achieved with _lwp_setprivate. + */ + +#define REG_GSBASE 27 +#define REG_FSBASE 26 +#define REG_DS 25 +#define REG_ES 24 + +#define REG_GS 23 +#define REG_FS 22 +#define REG_SS 21 +#define REG_RSP 20 +#define REG_RFL 19 +#define REG_CS 18 +#define REG_RIP 17 +#define REG_ERR 16 +#define REG_TRAPNO 15 +#define REG_RAX 14 +#define REG_RCX 13 +#define REG_RDX 12 +#define REG_RBX 11 +#define REG_RBP 10 +#define REG_RSI 9 +#define REG_RDI 8 +#define REG_R8 7 +#define REG_R9 6 +#define REG_R10 5 +#define REG_R11 4 +#define REG_R12 3 +#define REG_R13 2 +#define REG_R14 1 +#define REG_R15 0 + +/* + * The names and offsets defined here are specified by i386 ABI suppl. + */ + +#define SS 18 /* only stored on a privilege transition */ +#define UESP 17 /* only stored on a privilege transition */ +#define EFL 16 +#define CS 15 +#define EIP 14 +#define ERR 13 +#define TRAPNO 12 +#define EAX 11 +#define ECX 10 +#define EDX 9 +#define EBX 8 +#define ESP 7 +#define EBP 6 +#define ESI 5 +#define EDI 4 +#define DS 3 +#define ES 2 +#define FS 1 +#define GS 0 + +#define REG_PC EIP +#define REG_FP EBP +#define REG_SP UESP +#define REG_PS EFL +#define REG_R0 EAX +#define REG_R1 EDX + +#ifdef __cplusplus +} +#endif + +#endif /* _REGSET_H */ |