Custom DTrace kernel module files plus FreeBSD-specific DTrace providers.

This commit is contained in:
John Birrell 2008-05-23 05:59:42 +00:00
parent 29d10c76f9
commit 91eaf3e183
30 changed files with 15340 additions and 0 deletions

View file

@ -0,0 +1,220 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
*
* Portions Copyright 2006-2008 John Birrell jb@freebsd.org
*
* $FreeBSD$
*
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/dtrace.h>
#include <sys/dtrace_bsd.h>
static d_open_t dtmalloc_open;
static int dtmalloc_unload(void);
static void dtmalloc_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
static void dtmalloc_provide(void *, dtrace_probedesc_t *);
static void dtmalloc_destroy(void *, dtrace_id_t, void *);
static void dtmalloc_enable(void *, dtrace_id_t, void *);
static void dtmalloc_disable(void *, dtrace_id_t, void *);
static void dtmalloc_load(void *);
static struct cdevsw dtmalloc_cdevsw = {
.d_version = D_VERSION,
.d_open = dtmalloc_open,
.d_name = "dtmalloc",
};
static dtrace_pattr_t dtmalloc_attr = {
{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
};
static dtrace_pops_t dtmalloc_pops = {
dtmalloc_provide,
NULL,
dtmalloc_enable,
dtmalloc_disable,
NULL,
NULL,
dtmalloc_getargdesc,
NULL,
NULL,
dtmalloc_destroy
};
static struct cdev *dtmalloc_cdev;
static dtrace_provider_id_t dtmalloc_id;
static void
dtmalloc_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
{
const char *p = NULL;
switch (desc->dtargd_ndx) {
case 0:
p = "struct malloc_type *";
break;
case 1:
p = "struct malloc_type_internal *";
break;
case 2:
p = "struct malloc_type_stats *";
break;
case 3:
p = "unsigned long";
break;
case 4:
p = "int";
break;
default:
desc->dtargd_ndx = DTRACE_ARGNONE;
break;
}
if (p != NULL)
strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
return;
}
static void
dtmalloc_type_cb(struct malloc_type *mtp, void *arg __unused)
{
char name[DTRACE_FUNCNAMELEN];
struct malloc_type_internal *mtip = mtp->ks_handle;
strlcpy(name, mtp->ks_shortdesc, sizeof(name));
if (dtrace_probe_lookup(dtmalloc_id, NULL, name, "malloc") != 0)
return;
(void) dtrace_probe_create(dtmalloc_id, NULL, name, "malloc", 0,
&mtip->mti_probes[DTMALLOC_PROBE_MALLOC]);
(void) dtrace_probe_create(dtmalloc_id, NULL, name, "free", 0,
&mtip->mti_probes[DTMALLOC_PROBE_FREE]);
}
static void
dtmalloc_provide(void *arg, dtrace_probedesc_t *desc)
{
if (desc != NULL)
return;
malloc_type_list(dtmalloc_type_cb, desc);
}
static void
dtmalloc_destroy(void *arg, dtrace_id_t id, void *parg)
{
}
static void
dtmalloc_enable(void *arg, dtrace_id_t id, void *parg)
{
uint32_t *p = parg;
*p = id;
}
static void
dtmalloc_disable(void *arg, dtrace_id_t id, void *parg)
{
uint32_t *p = parg;
*p = 0;
}
static void
dtmalloc_load(void *dummy)
{
/* Create the /dev/dtrace/dtmalloc entry. */
dtmalloc_cdev = make_dev(&dtmalloc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/dtmalloc");
if (dtrace_register("dtmalloc", &dtmalloc_attr, DTRACE_PRIV_USER,
NULL, &dtmalloc_pops, NULL, &dtmalloc_id) != 0)
return;
dtrace_malloc_probe = dtrace_probe;
}
static int
dtmalloc_unload()
{
int error = 0;
dtrace_malloc_probe = NULL;
if ((error = dtrace_unregister(dtmalloc_id)) != 0)
return (error);
destroy_dev(dtmalloc_cdev);
return (error);
}
static int
dtmalloc_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
static int
dtmalloc_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
{
return (0);
}
SYSINIT(dtmalloc_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_load, NULL);
SYSUNINIT(dtmalloc_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_unload, NULL);
DEV_MODULE(dtmalloc, dtmalloc_modevent, NULL);
MODULE_VERSION(dtmalloc, 1);
MODULE_DEPEND(dtmalloc, dtrace, 1, 1, 1);
MODULE_DEPEND(dtmalloc, opensolaris, 1, 1, 1);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#ifndef _DIS_TABLES_H
#define _DIS_TABLES_H
#if defined(sun)
#pragma ident "@(#)dis_tables.h 1.7 06/03/02 SMI"
#endif
/*
* Constants and prototypes for the IA32 disassembler backend. See dis_tables.c
* for usage information and documentation.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
#include <sys/param.h>
/*
* values for cpu mode
*/
#define SIZE16 1
#define SIZE32 2
#define SIZE64 3
#define OPLEN 256
#define PFIXLEN 8
#define NCPS 12 /* number of chars per symbol */
/*
* data structures that must be provided to dtrace_dis86()
*/
typedef struct d86opnd {
char d86_opnd[OPLEN]; /* symbolic rep of operand */
char d86_prefix[PFIXLEN]; /* any prefix string or "" */
uint_t d86_mode; /* mode for immediate */
uint_t d86_value_size; /* size in bytes of d86_value */
uint64_t d86_value; /* immediate value of opnd */
} d86opnd_t;
typedef struct dis86 {
uint_t d86_mode;
uint_t d86_error;
uint_t d86_len; /* instruction length */
int d86_rmindex; /* index of modrm byte or -1 */
uint_t d86_memsize; /* size of memory referenced */
char d86_bytes[16]; /* bytes of instruction */
char d86_mneu[OPLEN];
uint_t d86_numopnds;
uint_t d86_rex_prefix; /* value of REX prefix if !0 */
char *d86_seg_prefix; /* segment prefix, if any */
uint_t d86_opnd_size;
uint_t d86_addr_size;
uint_t d86_got_modrm;
struct d86opnd d86_opnd[3]; /* up to 3 operands */
int (*d86_check_func)(void *);
int (*d86_get_byte)(void *);
#ifdef DIS_TEXT
int (*d86_sym_lookup)(void *, uint64_t, char *, size_t);
int (*d86_sprintf_func)(char *, size_t, const char *, ...);
int d86_flags;
uint_t d86_imm_bytes;
#endif
void *d86_data;
} dis86_t;
extern int dtrace_disx86(dis86_t *x, uint_t cpu_mode);
#define DIS_OP_OCTAL 0x1 /* Print all numbers in octal */
#ifdef DIS_TEXT
extern void dtrace_disx86_str(dis86_t *x, uint_t cpu_mode, uintptr_t pc,
char *buf, size_t len);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _DIS_TABLES_H */

View file

@ -0,0 +1,573 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
*
* Portions Copyright 2008 John Birrell <jb@freebsd.org>
*
* $FreeBSD$
*
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#define _ASM
#include <machine/asmacros.h>
#include <sys/cpuvar_defs.h>
#include <sys/dtrace.h>
#include "assym.s"
#define INTR_POP \
MEXITCOUNT; \
movq TF_RDI(%rsp),%rdi; \
movq TF_RSI(%rsp),%rsi; \
movq TF_RDX(%rsp),%rdx; \
movq TF_RCX(%rsp),%rcx; \
movq TF_R8(%rsp),%r8; \
movq TF_R9(%rsp),%r9; \
movq TF_RAX(%rsp),%rax; \
movq TF_RBX(%rsp),%rbx; \
movq TF_RBP(%rsp),%rbp; \
movq TF_R10(%rsp),%r10; \
movq TF_R11(%rsp),%r11; \
movq TF_R12(%rsp),%r12; \
movq TF_R13(%rsp),%r13; \
movq TF_R14(%rsp),%r14; \
movq TF_R15(%rsp),%r15; \
testb $SEL_RPL_MASK,TF_CS(%rsp); \
jz 1f; \
cli; \
swapgs; \
1: addq $TF_RIP,%rsp;
.globl calltrap
.type calltrap,@function
ENTRY(dtrace_invop_start)
/*
* #BP traps with %rip set to the next address. We need to decrement
* the value to indicate the address of the int3 (0xcc) instruction
* that we substituted.
*/
movq TF_RIP(%rsp), %rdi
decq %rdi
movq TF_RSP(%rsp), %rsi
movq TF_RAX(%rsp), %rdx
pushq (%rsi)
movq %rsp, %rsi
call dtrace_invop
ALTENTRY(dtrace_invop_callsite)
addq $8, %rsp
cmpl $DTRACE_INVOP_PUSHL_EBP, %eax
je bp_push
cmpl $DTRACE_INVOP_LEAVE, %eax
je bp_leave
cmpl $DTRACE_INVOP_NOP, %eax
je bp_nop
cmpl $DTRACE_INVOP_RET, %eax
je bp_ret
/* When all else fails handle the trap in the usual way. */
jmpq *dtrace_invop_calltrap_addr
bp_push:
/*
* We must emulate a "pushq %rbp". To do this, we pull the stack
* down 8 bytes, and then store the base pointer.
*/
INTR_POP
subq $16, %rsp /* make room for %rbp */
pushq %rax /* push temp */
movq 24(%rsp), %rax /* load calling RIP */
movq %rax, 8(%rsp) /* store calling RIP */
movq 32(%rsp), %rax /* load calling CS */
movq %rax, 16(%rsp) /* store calling CS */
movq 40(%rsp), %rax /* load calling RFLAGS */
movq %rax, 24(%rsp) /* store calling RFLAGS */
movq 48(%rsp), %rax /* load calling RSP */
subq $8, %rax /* make room for %rbp */
movq %rax, 32(%rsp) /* store calling RSP */
movq 56(%rsp), %rax /* load calling SS */
movq %rax, 40(%rsp) /* store calling SS */
movq 32(%rsp), %rax /* reload calling RSP */
movq %rbp, (%rax) /* store %rbp there */
popq %rax /* pop off temp */
iretq /* return from interrupt */
/*NOTREACHED*/
bp_leave:
/*
* We must emulate a "leave", which is the same as a "movq %rbp, %rsp"
* followed by a "popq %rbp". This is quite a bit simpler on amd64
* than it is on i386 -- we can exploit the fact that the %rsp is
* explicitly saved to effect the pop without having to reshuffle
* the other data pushed for the trap.
*/
INTR_POP
pushq %rax /* push temp */
movq 8(%rsp), %rax /* load calling RIP */
movq %rax, 8(%rsp) /* store calling RIP */
movq (%rbp), %rax /* get new %rbp */
addq $8, %rbp /* adjust new %rsp */
movq %rbp, 32(%rsp) /* store new %rsp */
movq %rax, %rbp /* set new %rbp */
popq %rax /* pop off temp */
iretq /* return from interrupt */
/*NOTREACHED*/
bp_nop:
/* We must emulate a "nop". */
INTR_POP
iretq
/*NOTREACHED*/
bp_ret:
INTR_POP
pushq %rax /* push temp */
movq 32(%rsp), %rax /* load %rsp */
movq (%rax), %rax /* load calling RIP */
movq %rax, 8(%rsp) /* store calling RIP */
addq $8, 32(%rsp) /* adjust new %rsp */
popq %rax /* pop off temp */
iretq /* return from interrupt */
/*NOTREACHED*/
END(dtrace_invop_start)
/*
void dtrace_invop_init(void)
*/
ENTRY(dtrace_invop_init)
movq $dtrace_invop_start, dtrace_invop_jump_addr(%rip)
ret
END(dtrace_invop_init)
/*
void dtrace_invop_uninit(void)
*/
ENTRY(dtrace_invop_uninit)
movq $0, dtrace_invop_jump_addr(%rip)
ret
END(dtrace_invop_uninit)
/*
greg_t dtrace_getfp(void)
*/
ENTRY(dtrace_getfp)
movq %rbp, %rax
ret
END(dtrace_getfp)
/*
uint32_t
dtrace_cas32(uint32_t *target, uint32_t cmp, uint32_t new)
*/
ENTRY(dtrace_cas32)
movl %esi, %eax
lock
cmpxchgl %edx, (%rdi)
ret
END(dtrace_cas32)
/*
void *
dtrace_casptr(void *target, void *cmp, void *new)
*/
ENTRY(dtrace_casptr)
movq %rsi, %rax
lock
cmpxchgq %rdx, (%rdi)
ret
END(dtrace_casptr)
/*
uintptr_t
dtrace_caller(int aframes)
*/
ENTRY(dtrace_caller)
movq $-1, %rax
ret
END(dtrace_caller)
/*
void
dtrace_copy(uintptr_t src, uintptr_t dest, size_t size)
*/
ENTRY(dtrace_copy)
pushq %rbp
movq %rsp, %rbp
xchgq %rdi, %rsi /* make %rsi source, %rdi dest */
movq %rdx, %rcx /* load count */
repz /* repeat for count ... */
smovb /* move from %ds:rsi to %ed:rdi */
leave
ret
END(dtrace_copy)
/*
void
dtrace_copystr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
volatile uint16_t *flags)
*/
ENTRY(dtrace_copystr)
pushq %rbp
movq %rsp, %rbp
0:
movb (%rdi), %al /* load from source */
movb %al, (%rsi) /* store to destination */
addq $1, %rdi /* increment source pointer */
addq $1, %rsi /* increment destination pointer */
subq $1, %rdx /* decrement remaining count */
cmpb $0, %al
je 2f
testq $0xfff, %rdx /* test if count is 4k-aligned */
jnz 1f /* if not, continue with copying */
testq $CPU_DTRACE_BADADDR, (%rcx) /* load and test dtrace flags */
jnz 2f
1:
cmpq $0, %rdx
jne 0b
2:
leave
ret
END(dtrace_copystr)
/*
uintptr_t
dtrace_fulword(void *addr)
*/
ENTRY(dtrace_fulword)
movq (%rdi), %rax
ret
END(dtrace_fulword)
/*
uint8_t
dtrace_fuword8_nocheck(void *addr)
*/
ENTRY(dtrace_fuword8_nocheck)
xorq %rax, %rax
movb (%rdi), %al
ret
END(dtrace_fuword8_nocheck)
/*
uint16_t
dtrace_fuword16_nocheck(void *addr)
*/
ENTRY(dtrace_fuword16_nocheck)
xorq %rax, %rax
movw (%rdi), %ax
ret
END(dtrace_fuword16_nocheck)
/*
uint32_t
dtrace_fuword32_nocheck(void *addr)
*/
ENTRY(dtrace_fuword32_nocheck)
xorq %rax, %rax
movl (%rdi), %eax
ret
END(dtrace_fuword32_nocheck)
/*
uint64_t
dtrace_fuword64_nocheck(void *addr)
*/
ENTRY(dtrace_fuword64_nocheck)
movq (%rdi), %rax
ret
END(dtrace_fuword64_nocheck)
/*
void
dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
int fault, int fltoffs, uintptr_t illval)
*/
ENTRY(dtrace_probe_error)
pushq %rbp
movq %rsp, %rbp
subq $0x8, %rsp
movq %r9, (%rsp)
movq %r8, %r9
movq %rcx, %r8
movq %rdx, %rcx
movq %rsi, %rdx
movq %rdi, %rsi
movl dtrace_probeid_error(%rip), %edi
call dtrace_probe
addq $0x8, %rsp
leave
ret
END(dtrace_probe_error)
/*
void
dtrace_membar_producer(void)
*/
ENTRY(dtrace_membar_producer)
rep; ret /* use 2 byte return instruction when branch target */
/* AMD Software Optimization Guide - Section 6.2 */
END(dtrace_membar_producer)
/*
void
dtrace_membar_consumer(void)
*/
ENTRY(dtrace_membar_consumer)
rep; ret /* use 2 byte return instruction when branch target */
/* AMD Software Optimization Guide - Section 6.2 */
END(dtrace_membar_consumer)
/*
dtrace_icookie_t
dtrace_interrupt_disable(void)
*/
ENTRY(dtrace_interrupt_disable)
pushfq
popq %rax
cli
ret
END(dtrace_interrupt_disable)
/*
void
dtrace_interrupt_enable(dtrace_icookie_t cookie)
*/
ENTRY(dtrace_interrupt_enable)
pushq %rdi
popfq
ret
END(dtrace_interrupt_enable)
/*
* The panic() and cmn_err() functions invoke vpanic() as a common entry point
* into the panic code implemented in panicsys(). vpanic() is responsible
* for passing through the format string and arguments, and constructing a
* regs structure on the stack into which it saves the current register
* values. If we are not dying due to a fatal trap, these registers will
* then be preserved in panicbuf as the current processor state. Before
* invoking panicsys(), vpanic() activates the first panic trigger (see
* common/os/panic.c) and switches to the panic_stack if successful. Note that
* DTrace takes a slightly different panic path if it must panic from probe
* context. Instead of calling panic, it calls into dtrace_vpanic(), which
* sets up the initial stack as vpanic does, calls dtrace_panic_trigger(), and
* branches back into vpanic().
*/
/*
void
vpanic(const char *format, va_list alist)
*/
ENTRY(vpanic) /* Initial stack layout: */
pushq %rbp /* | %rip | 0x60 */
movq %rsp, %rbp /* | %rbp | 0x58 */
pushfq /* | rfl | 0x50 */
pushq %r11 /* | %r11 | 0x48 */
pushq %r10 /* | %r10 | 0x40 */
pushq %rbx /* | %rbx | 0x38 */
pushq %rax /* | %rax | 0x30 */
pushq %r9 /* | %r9 | 0x28 */
pushq %r8 /* | %r8 | 0x20 */
pushq %rcx /* | %rcx | 0x18 */
pushq %rdx /* | %rdx | 0x10 */
pushq %rsi /* | %rsi | 0x8 alist */
pushq %rdi /* | %rdi | 0x0 format */
movq %rsp, %rbx /* %rbx = current %rsp */
leaq panic_quiesce(%rip), %rdi /* %rdi = &panic_quiesce */
call panic_trigger /* %eax = panic_trigger() */
vpanic_common:
/*
* The panic_trigger result is in %eax from the call above, and
* dtrace_panic places it in %eax before branching here.
* The rdmsr instructions that follow below will clobber %eax so
* we stash the panic_trigger result in %r11d.
*/
movl %eax, %r11d
cmpl $0, %r11d
je 0f
/*
* If panic_trigger() was successful, we are the first to initiate a
* panic: we now switch to the reserved panic_stack before continuing.
*/
leaq panic_stack(%rip), %rsp
addq $PANICSTKSIZE, %rsp
0: subq $REGSIZE, %rsp
/*
* Now that we've got everything set up, store the register values as
* they were when we entered vpanic() to the designated location in
* the regs structure we allocated on the stack.
*/
#ifdef notyet
movq 0x0(%rbx), %rcx
movq %rcx, REGOFF_RDI(%rsp)
movq 0x8(%rbx), %rcx
movq %rcx, REGOFF_RSI(%rsp)
movq 0x10(%rbx), %rcx
movq %rcx, REGOFF_RDX(%rsp)
movq 0x18(%rbx), %rcx
movq %rcx, REGOFF_RCX(%rsp)
movq 0x20(%rbx), %rcx
movq %rcx, REGOFF_R8(%rsp)
movq 0x28(%rbx), %rcx
movq %rcx, REGOFF_R9(%rsp)
movq 0x30(%rbx), %rcx
movq %rcx, REGOFF_RAX(%rsp)
movq 0x38(%rbx), %rcx
movq %rcx, REGOFF_RBX(%rsp)
movq 0x58(%rbx), %rcx
movq %rcx, REGOFF_RBP(%rsp)
movq 0x40(%rbx), %rcx
movq %rcx, REGOFF_R10(%rsp)
movq 0x48(%rbx), %rcx
movq %rcx, REGOFF_R11(%rsp)
movq %r12, REGOFF_R12(%rsp)
movq %r13, REGOFF_R13(%rsp)
movq %r14, REGOFF_R14(%rsp)
movq %r15, REGOFF_R15(%rsp)
xorl %ecx, %ecx
movw %ds, %cx
movq %rcx, REGOFF_DS(%rsp)
movw %es, %cx
movq %rcx, REGOFF_ES(%rsp)
movw %fs, %cx
movq %rcx, REGOFF_FS(%rsp)
movw %gs, %cx
movq %rcx, REGOFF_GS(%rsp)
movq $0, REGOFF_TRAPNO(%rsp)
movq $0, REGOFF_ERR(%rsp)
leaq vpanic(%rip), %rcx
movq %rcx, REGOFF_RIP(%rsp)
movw %cs, %cx
movzwq %cx, %rcx
movq %rcx, REGOFF_CS(%rsp)
movq 0x50(%rbx), %rcx
movq %rcx, REGOFF_RFL(%rsp)
movq %rbx, %rcx
addq $0x60, %rcx
movq %rcx, REGOFF_RSP(%rsp)
movw %ss, %cx
movzwq %cx, %rcx
movq %rcx, REGOFF_SS(%rsp)
/*
* panicsys(format, alist, rp, on_panic_stack)
*/
movq REGOFF_RDI(%rsp), %rdi /* format */
movq REGOFF_RSI(%rsp), %rsi /* alist */
movq %rsp, %rdx /* struct regs */
movl %r11d, %ecx /* on_panic_stack */
call panicsys
addq $REGSIZE, %rsp
#endif
popq %rdi
popq %rsi
popq %rdx
popq %rcx
popq %r8
popq %r9
popq %rax
popq %rbx
popq %r10
popq %r11
popfq
leave
ret
END(vpanic)
/*
void
dtrace_vpanic(const char *format, va_list alist)
*/
ENTRY(dtrace_vpanic) /* Initial stack layout: */
pushq %rbp /* | %rip | 0x60 */
movq %rsp, %rbp /* | %rbp | 0x58 */
pushfq /* | rfl | 0x50 */
pushq %r11 /* | %r11 | 0x48 */
pushq %r10 /* | %r10 | 0x40 */
pushq %rbx /* | %rbx | 0x38 */
pushq %rax /* | %rax | 0x30 */
pushq %r9 /* | %r9 | 0x28 */
pushq %r8 /* | %r8 | 0x20 */
pushq %rcx /* | %rcx | 0x18 */
pushq %rdx /* | %rdx | 0x10 */
pushq %rsi /* | %rsi | 0x8 alist */
pushq %rdi /* | %rdi | 0x0 format */
movq %rsp, %rbx /* %rbx = current %rsp */
leaq panic_quiesce(%rip), %rdi /* %rdi = &panic_quiesce */
call dtrace_panic_trigger /* %eax = dtrace_panic_trigger() */
jmp vpanic_common
END(dtrace_vpanic)
/*
int
panic_trigger(int *tp)
*/
ENTRY(panic_trigger)
xorl %eax, %eax
movl $0xdefacedd, %edx
lock
xchgl %edx, (%rdi)
cmpl $0, %edx
je 0f
movl $0, %eax
ret
0: movl $1, %eax
ret
END(panic_trigger)
/*
int
dtrace_panic_trigger(int *tp)
*/
ENTRY(dtrace_panic_trigger)
xorl %eax, %eax
movl $0xdefacedd, %edx
lock
xchgl %edx, (%rdi)
cmpl $0, %edx
je 0f
movl $0, %eax
ret
0: movl $1, %eax
ret
END(dtrace_panic_trigger)

View file

@ -0,0 +1,612 @@
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/stack.h>
#include <sys/pcpu.h>
#include <machine/frame.h>
#include <machine/md_var.h>
#include <machine/reg.h>
#include <machine/stack.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
extern uintptr_t kernbase;
uintptr_t kernelbase = (uintptr_t) &kernbase;
uint8_t dtrace_fuword8_nocheck(void *);
uint16_t dtrace_fuword16_nocheck(void *);
uint32_t dtrace_fuword32_nocheck(void *);
uint64_t dtrace_fuword64_nocheck(void *);
void
dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
uint32_t *intrpc)
{
int depth = 0;
register_t rbp;
struct amd64_frame *frame;
vm_offset_t callpc;
pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
if (intrpc != 0)
pcstack[depth++] = (pc_t) intrpc;
aframes++;
__asm __volatile("movq %%rbp,%0" : "=r" (rbp));
frame = (struct amd64_frame *)rbp;
while (depth < pcstack_limit) {
if (!INKERNEL((long) frame))
break;
callpc = frame->f_retaddr;
if (!INKERNEL(callpc))
break;
if (aframes > 0) {
aframes--;
if ((aframes == 0) && (caller != 0)) {
pcstack[depth++] = caller;
}
}
else {
pcstack[depth++] = callpc;
}
if (frame->f_frame <= frame ||
(vm_offset_t)frame->f_frame >=
(vm_offset_t)rbp + KSTACK_PAGES * PAGE_SIZE)
break;
frame = frame->f_frame;
}
for (; depth < pcstack_limit; depth++) {
pcstack[depth] = 0;
}
}
static int
dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
uintptr_t sp)
{
volatile uint16_t *flags =
(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
struct amd64_frame *frame;
int ret = 0;
ASSERT(pcstack == NULL || pcstack_limit > 0);
while (pc != 0 && sp != 0) {
ret++;
if (pcstack != NULL) {
*pcstack++ = (uint64_t)pc;
pcstack_limit--;
if (pcstack_limit <= 0)
break;
}
frame = (struct amd64_frame *) sp;
pc = dtrace_fulword(&frame->f_retaddr);
sp = dtrace_fulword(&frame->f_frame);
/*
* This is totally bogus: if we faulted, we're going to clear
* the fault and break. This is to deal with the apparently
* broken Java stacks on x86.
*/
if (*flags & CPU_DTRACE_FAULT) {
*flags &= ~CPU_DTRACE_FAULT;
break;
}
}
return (ret);
}
void
dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
{
proc_t *p = curproc;
struct trapframe *tf;
uintptr_t pc, sp;
volatile uint16_t *flags =
(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
int n;
if (*flags & CPU_DTRACE_FAULT)
return;
if (pcstack_limit <= 0)
return;
/*
* If there's no user context we still need to zero the stack.
*/
if (p == NULL || (tf = curthread->td_frame) == NULL)
goto zero;
*pcstack++ = (uint64_t)p->p_pid;
pcstack_limit--;
if (pcstack_limit <= 0)
return;
pc = tf->tf_rip;
sp = tf->tf_rsp;
if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
*pcstack++ = (uint64_t)pc;
pcstack_limit--;
if (pcstack_limit <= 0)
return;
pc = dtrace_fulword((void *) sp);
}
n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);
ASSERT(n >= 0);
ASSERT(n <= pcstack_limit);
pcstack += n;
pcstack_limit -= n;
zero:
while (pcstack_limit-- > 0)
*pcstack++ = 0;
}
int
dtrace_getustackdepth(void)
{
proc_t *p = curproc;
struct trapframe *tf;
uintptr_t pc, 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_rip;
sp = tf->tf_rsp;
if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
n++;
pc = dtrace_fulword((void *) sp);
}
n += dtrace_getustack_common(NULL, 0, pc, sp);
return (n);
}
#ifdef notyet
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;
volatile uint16_t *flags =
(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
size_t s1, s2;
if (*flags & CPU_DTRACE_FAULT)
return;
if (pcstack_limit <= 0)
return;
/*
* If there's no user context we still need to zero the stack.
*/
if (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL)
goto zero;
*pcstack++ = (uint64_t)p->p_pid;
pcstack_limit--;
if (pcstack_limit <= 0)
return;
pc = rp->r_pc;
sp = rp->r_fp;
oldcontext = lwp->lwp_oldcontext;
s1 = sizeof (struct xframe) + 2 * sizeof (long);
s2 = s1 + sizeof (siginfo_t);
if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
*pcstack++ = (uint64_t)pc;
*fpstack++ = 0;
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);
}
while (pc != 0 && sp != 0) {
*pcstack++ = (uint64_t)pc;
*fpstack++ = sp;
pcstack_limit--;
if (pcstack_limit <= 0)
break;
if (oldcontext == sp + s1 || oldcontext == sp + s2) {
ucontext_t *ucp = (ucontext_t *)oldcontext;
greg_t *gregs = ucp->uc_mcontext.gregs;
sp = dtrace_fulword(&gregs[REG_FP]);
pc = dtrace_fulword(&gregs[REG_PC]);
oldcontext = dtrace_fulword(&ucp->uc_link);
} else {
struct xframe *fr = (struct xframe *)sp;
pc = dtrace_fulword(&fr->fr_savpc);
sp = dtrace_fulword(&fr->fr_savfp);
}
/*
* This is totally bogus: if we faulted, we're going to clear
* the fault and break. This is to deal with the apparently
* broken Java stacks on x86.
*/
if (*flags & CPU_DTRACE_FAULT) {
*flags &= ~CPU_DTRACE_FAULT;
break;
}
}
zero:
while (pcstack_limit-- > 0)
*pcstack++ = NULL;
}
#endif
/*ARGSUSED*/
uint64_t
dtrace_getarg(int arg, int aframes)
{
uintptr_t val;
struct amd64_frame *fp = (struct amd64_frame *)dtrace_getfp();
uintptr_t *stack;
int i;
/*
* A total of 6 arguments are passed via registers; any argument with
* index of 5 or lower is therefore in a register.
*/
int inreg = 5;
for (i = 1; i <= aframes; i++) {
fp = fp->f_frame;
if (fp->f_retaddr == (long)dtrace_invop_callsite) {
/*
* In the case of amd64, we will use the pointer to the
* regs structure that was pushed when we took the
* trap. To get this structure, we must increment
* beyond the frame structure, and then again beyond
* the calling RIP stored in dtrace_invop(). If the
* argument that we're seeking is passed on the stack,
* we'll pull the true stack pointer out of the saved
* registers and decrement our argument by the number
* of arguments passed in registers; if the argument
* we're seeking is passed in regsiters, we can just
* load it directly.
*/
struct reg *rp = (struct reg *)((uintptr_t)&fp[1] +
sizeof (uintptr_t));
if (arg <= inreg) {
stack = (uintptr_t *)&rp->r_rdi;
} else {
stack = (uintptr_t *)(rp->r_rsp);
arg -= inreg;
}
goto load;
}
}
/*
* We know that we did not come through a trap to get into
* dtrace_probe() -- the provider simply called dtrace_probe()
* directly. As this is the case, we need to shift the argument
* that we're looking for: the probe ID is the first argument to
* dtrace_probe(), so the argument n will actually be found where
* one would expect to find argument (n + 1).
*/
arg++;
if (arg <= inreg) {
/*
* This shouldn't happen. If the argument is passed in a
* register then it should have been, well, passed in a
* register...
*/
DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
return (0);
}
arg -= (inreg + 1);
stack = (uintptr_t *)&fp[1];
load:
DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
val = stack[arg];
DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
return (val);
return (0);
}
int
dtrace_getstackdepth(int aframes)
{
int depth = 0;
struct amd64_frame *frame;
vm_offset_t rbp;
aframes++;
rbp = dtrace_getfp();
frame = (struct amd64_frame *)rbp;
depth++;
for(;;) {
if (!INKERNEL((long) frame))
break;
if (!INKERNEL((long) frame->f_frame))
break;
depth++;
if (frame->f_frame <= frame ||
(vm_offset_t)frame->f_frame >=
(vm_offset_t)rbp + KSTACK_PAGES * PAGE_SIZE)
break;
frame = frame->f_frame;
}
if (depth < aframes)
return 0;
else
return depth - aframes;
}
#ifdef notyet
ulong_t
dtrace_getreg(struct regs *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 */
};
if (reg <= SS) {
if (reg >= sizeof (regmap) / sizeof (int)) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
return (0);
}
reg = regmap[reg];
} else {
reg -= SS + 1;
}
switch (reg) {
case REG_RDI:
return (rp->r_rdi);
case REG_RSI:
return (rp->r_rsi);
case REG_RDX:
return (rp->r_rdx);
case REG_RCX:
return (rp->r_rcx);
case REG_R8:
return (rp->r_r8);
case REG_R9:
return (rp->r_r9);
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);
case REG_TRAPNO:
return (rp->r_trapno);
case REG_ERR:
return (rp->r_err);
case REG_RIP:
return (rp->r_rip);
case REG_CS:
return (rp->r_cs);
case REG_SS:
return (rp->r_ss);
case REG_RFL:
return (rp->r_rfl);
case REG_RSP:
return (rp->r_rsp);
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)
{
ASSERT(kaddr >= kernelbase && kaddr + size >= kaddr);
if (uaddr + size >= kernelbase || uaddr + size < uaddr) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
return (0);
}
return (1);
}
void
dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
volatile uint16_t *flags)
{
if (dtrace_copycheck(uaddr, kaddr, size))
dtrace_copy(uaddr, kaddr, size);
}
void
dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
volatile uint16_t *flags)
{
if (dtrace_copycheck(uaddr, kaddr, size))
dtrace_copy(kaddr, uaddr, size);
}
void
dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
volatile uint16_t *flags)
{
if (dtrace_copycheck(uaddr, kaddr, size))
dtrace_copystr(uaddr, kaddr, size, flags);
}
void
dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
volatile uint16_t *flags)
{
if (dtrace_copycheck(uaddr, kaddr, size))
dtrace_copystr(kaddr, uaddr, size, flags);
}
uint8_t
dtrace_fuword8(void *uaddr)
{
if ((uintptr_t)uaddr >= kernelbase) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
return (0);
}
return (dtrace_fuword8_nocheck(uaddr));
}
uint16_t
dtrace_fuword16(void *uaddr)
{
if ((uintptr_t)uaddr >= kernelbase) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
return (0);
}
return (dtrace_fuword16_nocheck(uaddr));
}
uint32_t
dtrace_fuword32(void *uaddr)
{
if ((uintptr_t)uaddr >= kernelbase) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
return (0);
}
return (dtrace_fuword32_nocheck(uaddr));
}
uint64_t
dtrace_fuword64(void *uaddr)
{
if ((uintptr_t)uaddr >= kernelbase) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
return (0);
}
return (dtrace_fuword64_nocheck(uaddr));
}

View file

@ -0,0 +1,507 @@
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/kmem.h>
#include <sys/smp.h>
#include <sys/dtrace_impl.h>
#include <sys/dtrace_bsd.h>
#include <machine/clock.h>
#include <machine/frame.h>
#include <vm/pmap.h>
extern uintptr_t kernelbase;
extern uintptr_t dtrace_in_probe_addr;
extern int dtrace_in_probe;
int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
typedef struct dtrace_invop_hdlr {
int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t);
struct dtrace_invop_hdlr *dtih_next;
} dtrace_invop_hdlr_t;
dtrace_invop_hdlr_t *dtrace_invop_hdlr;
int
dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax)
{
dtrace_invop_hdlr_t *hdlr;
int rval;
for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0)
return (rval);
return (0);
}
void
dtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
{
dtrace_invop_hdlr_t *hdlr;
hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
hdlr->dtih_func = func;
hdlr->dtih_next = dtrace_invop_hdlr;
dtrace_invop_hdlr = hdlr;
}
void
dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
{
dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
for (;;) {
if (hdlr == NULL)
panic("attempt to remove non-existent invop handler");
if (hdlr->dtih_func == func)
break;
prev = hdlr;
hdlr = hdlr->dtih_next;
}
if (prev == NULL) {
ASSERT(dtrace_invop_hdlr == hdlr);
dtrace_invop_hdlr = hdlr->dtih_next;
} else {
ASSERT(dtrace_invop_hdlr != hdlr);
prev->dtih_next = hdlr->dtih_next;
}
kmem_free(hdlr, 0);
}
/*ARGSUSED*/
void
dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
{
(*func)(0, (uintptr_t) addr_PTmap);
}
void
dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
{
cpumask_t cpus;
critical_enter();
if (cpu == DTRACE_CPUALL)
cpus = all_cpus;
else
cpus = (cpumask_t) (1 << cpu);
/* If the current CPU is in the set, call the function directly: */
if ((cpus & (1 << curcpu)) != 0) {
(*func)(arg);
/* Mask the current CPU from the set */
cpus &= ~(1 << curcpu);
}
/* If there are any CPUs in the set, cross-call to those CPUs */
if (cpus != 0)
smp_rendezvous_cpus(cpus, NULL, func, smp_no_rendevous_barrier, arg);
critical_exit();
}
static void
dtrace_sync_func(void)
{
}
void
dtrace_sync(void)
{
dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
}
#ifdef notyet
int (*dtrace_fasttrap_probe_ptr)(struct regs *);
int (*dtrace_pid_probe_ptr)(struct regs *);
int (*dtrace_return_probe_ptr)(struct regs *);
void
dtrace_user_probe(struct regs *rp, caddr_t addr, processorid_t cpuid)
{
krwlock_t *rwp;
proc_t *p = curproc;
extern void trap(struct regs *, caddr_t, processorid_t);
if (USERMODE(rp->r_cs) || (rp->r_ps & PS_VM)) {
if (curthread->t_cred != p->p_cred) {
cred_t *oldcred = curthread->t_cred;
/*
* DTrace accesses t_cred in probe context. t_cred
* must always be either NULL, or point to a valid,
* allocated cred structure.
*/
curthread->t_cred = crgetcred();
crfree(oldcred);
}
}
if (rp->r_trapno == T_DTRACE_RET) {
uint8_t step = curthread->t_dtrace_step;
uint8_t ret = curthread->t_dtrace_ret;
uintptr_t npc = curthread->t_dtrace_npc;
if (curthread->t_dtrace_ast) {
aston(curthread);
curthread->t_sig_check = 1;
}
/*
* Clear all user tracing flags.
*/
curthread->t_dtrace_ft = 0;
/*
* If we weren't expecting to take a return probe trap, kill
* the process as though it had just executed an unassigned
* trap instruction.
*/
if (step == 0) {
tsignal(curthread, SIGILL);
return;
}
/*
* If we hit this trap unrelated to a return probe, we're
* just here to reset the AST flag since we deferred a signal
* until after we logically single-stepped the instruction we
* copied out.
*/
if (ret == 0) {
rp->r_pc = npc;
return;
}
/*
* We need to wait until after we've called the
* dtrace_return_probe_ptr function pointer to set %pc.
*/
rwp = &CPU->cpu_ft_lock;
rw_enter(rwp, RW_READER);
if (dtrace_return_probe_ptr != NULL)
(void) (*dtrace_return_probe_ptr)(rp);
rw_exit(rwp);
rp->r_pc = npc;
} else if (rp->r_trapno == T_DTRACE_PROBE) {
rwp = &CPU->cpu_ft_lock;
rw_enter(rwp, RW_READER);
if (dtrace_fasttrap_probe_ptr != NULL)
(void) (*dtrace_fasttrap_probe_ptr)(rp);
rw_exit(rwp);
} else if (rp->r_trapno == T_BPTFLT) {
uint8_t instr;
rwp = &CPU->cpu_ft_lock;
/*
* The DTrace fasttrap provider uses the breakpoint trap
* (int 3). We let DTrace take the first crack at handling
* this trap; if it's not a probe that DTrace knowns about,
* we call into the trap() routine to handle it like a
* breakpoint placed by a conventional debugger.
*/
rw_enter(rwp, RW_READER);
if (dtrace_pid_probe_ptr != NULL &&
(*dtrace_pid_probe_ptr)(rp) == 0) {
rw_exit(rwp);
return;
}
rw_exit(rwp);
/*
* If the instruction that caused the breakpoint trap doesn't
* look like an int 3 anymore, it may be that this tracepoint
* was removed just after the user thread executed it. In
* that case, return to user land to retry the instuction.
*/
if (fuword8((void *)(rp->r_pc - 1), &instr) == 0 &&
instr != FASTTRAP_INSTR) {
rp->r_pc--;
return;
}
trap(rp, addr, cpuid);
} else {
trap(rp, addr, cpuid);
}
}
void
dtrace_safe_synchronous_signal(void)
{
kthread_t *t = curthread;
struct regs *rp = lwptoregs(ttolwp(t));
size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
ASSERT(t->t_dtrace_on);
/*
* If we're not in the range of scratch addresses, we're not actually
* tracing user instructions so turn off the flags. If the instruction
* we copied out caused a synchonous trap, reset the pc back to its
* original value and turn off the flags.
*/
if (rp->r_pc < t->t_dtrace_scrpc ||
rp->r_pc > t->t_dtrace_astpc + isz) {
t->t_dtrace_ft = 0;
} else if (rp->r_pc == t->t_dtrace_scrpc ||
rp->r_pc == t->t_dtrace_astpc) {
rp->r_pc = t->t_dtrace_pc;
t->t_dtrace_ft = 0;
}
}
int
dtrace_safe_defer_signal(void)
{
kthread_t *t = curthread;
struct regs *rp = lwptoregs(ttolwp(t));
size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
ASSERT(t->t_dtrace_on);
/*
* If we're not in the range of scratch addresses, we're not actually
* tracing user instructions so turn off the flags.
*/
if (rp->r_pc < t->t_dtrace_scrpc ||
rp->r_pc > t->t_dtrace_astpc + isz) {
t->t_dtrace_ft = 0;
return (0);
}
/*
* If we've executed the original instruction, but haven't performed
* the jmp back to t->t_dtrace_npc or the clean up of any registers
* used to emulate %rip-relative instructions in 64-bit mode, do that
* here and take the signal right away. We detect this condition by
* seeing if the program counter is the range [scrpc + isz, astpc).
*/
if (t->t_dtrace_astpc - rp->r_pc <
t->t_dtrace_astpc - t->t_dtrace_scrpc - isz) {
#ifdef __amd64
/*
* If there is a scratch register and we're on the
* instruction immediately after the modified instruction,
* restore the value of that scratch register.
*/
if (t->t_dtrace_reg != 0 &&
rp->r_pc == t->t_dtrace_scrpc + isz) {
switch (t->t_dtrace_reg) {
case REG_RAX:
rp->r_rax = t->t_dtrace_regv;
break;
case REG_RCX:
rp->r_rcx = t->t_dtrace_regv;
break;
case REG_R8:
rp->r_r8 = t->t_dtrace_regv;
break;
case REG_R9:
rp->r_r9 = t->t_dtrace_regv;
break;
}
}
#endif
rp->r_pc = t->t_dtrace_npc;
t->t_dtrace_ft = 0;
return (0);
}
/*
* Otherwise, make sure we'll return to the kernel after executing
* the copied out instruction and defer the signal.
*/
if (!t->t_dtrace_step) {
ASSERT(rp->r_pc < t->t_dtrace_astpc);
rp->r_pc += t->t_dtrace_astpc - t->t_dtrace_scrpc;
t->t_dtrace_step = 1;
}
t->t_dtrace_ast = 1;
return (1);
}
#endif
static int64_t tgt_cpu_tsc;
static int64_t hst_cpu_tsc;
static int64_t tsc_skew[MAXCPU];
static void
dtrace_gethrtime_init_sync(void *arg)
{
#ifdef CHECK_SYNC
/*
* Delay this function from returning on one
* of the CPUs to check that the synchronisation
* works.
*/
uintptr_t cpu = (uintptr_t) arg;
if (cpu == curcpu) {
int i;
for (i = 0; i < 1000000000; i++)
tgt_cpu_tsc = rdtsc();
tgt_cpu_tsc = 0;
}
#endif
}
static void
dtrace_gethrtime_init_cpu(void *arg)
{
uintptr_t cpu = (uintptr_t) arg;
if (cpu == curcpu)
tgt_cpu_tsc = rdtsc();
else
hst_cpu_tsc = rdtsc();
}
static void
dtrace_gethrtime_init(void *arg)
{
cpumask_t map;
int i;
struct pcpu *cp;
/* The current CPU is the reference one. */
tsc_skew[curcpu] = 0;
for (i = 0; i <= mp_maxid; i++) {
if (i == curcpu)
continue;
if ((cp = pcpu_find(i)) == NULL)
continue;
map = 0;
map |= (1 << curcpu);
map |= (1 << i);
smp_rendezvous_cpus(map, dtrace_gethrtime_init_sync,
dtrace_gethrtime_init_cpu,
smp_no_rendevous_barrier, (void *)(uintptr_t) i);
tsc_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;
}
}
SYSINIT(dtrace_gethrtime_init, SI_SUB_SMP, SI_ORDER_ANY, dtrace_gethrtime_init, NULL);
/*
* DTrace needs a high resolution time function which can
* be called from a probe context and guaranteed not to have
* instrumented with probes itself.
*
* Returns nanoseconds since boot.
*/
uint64_t
dtrace_gethrtime()
{
return ((rdtsc() + tsc_skew[curcpu]) * (int64_t) 1000000000 / tsc_freq);
}
uint64_t
dtrace_gethrestime(void)
{
printf("%s(%d): XXX\n",__func__,__LINE__);
return (0);
}
/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */
int
dtrace_trap(struct trapframe *frame, u_int type)
{
/*
* A trap can occur while DTrace executes a probe. Before
* executing the probe, DTrace blocks re-scheduling and sets
* a flag in it's per-cpu flags to indicate that it doesn't
* want to fault. On returning from the the probe, the no-fault
* flag is cleared and finally re-scheduling is enabled.
*
* Check if DTrace has enabled 'no-fault' mode:
*
*/
if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
/*
* There are only a couple of trap types that are expected.
* All the rest will be handled in the usual way.
*/
switch (type) {
/* Privilieged instruction fault. */
case T_PRIVINFLT:
break;
/* General protection fault. */
case T_PROTFLT:
/* Flag an illegal operation. */
cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
/*
* Offset the instruction pointer to the instruction
* following the one causing the fault.
*/
frame->tf_rip += dtrace_instr_size((u_char *) frame->tf_rip);
return (1);
/* Page fault. */
case T_PAGEFLT:
/* Flag a bad address. */
cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
cpu_core[curcpu].cpuc_dtrace_illval = frame->tf_addr;
/*
* Offset the instruction pointer to the instruction
* following the one causing the fault.
*/
frame->tf_rip += dtrace_instr_size((u_char *) frame->tf_rip);
return (1);
default:
/* Handle all other traps in the usual way. */
break;
}
}
/* Handle the trap in the usual way. */
return (0);
}

View file

@ -0,0 +1,132 @@
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#if defined(sun)
#pragma ident "@(#)instr_size.c 1.14 05/07/08 SMI"
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#if defined(sun)
#include <sys/cmn_err.h>
#include <sys/archsystm.h>
#include <sys/copyops.h>
#include <vm/seg_enum.h>
#include <sys/privregs.h>
#else
typedef u_int model_t;
#define DATAMODEL_NATIVE 0
int dtrace_instr_size(uchar_t *);
#endif
#include <dis_tables.h>
/*
* This subsystem (with the minor exception of the instr_size() function) is
* is called from DTrace probe context. This imposes several requirements on
* the implementation:
*
* 1. External subsystems and functions may not be referenced. The one current
* exception is for cmn_err, but only to signal the detection of table
* errors. Assuming the tables are correct, no combination of input is to
* trigger a cmn_err call.
*
* 2. These functions can't be allowed to be traced. To prevent this,
* all functions in the probe path (everything except instr_size()) must
* have names that begin with "dtrace_".
*/
typedef enum dis_isize {
DIS_ISIZE_INSTR,
DIS_ISIZE_OPERAND
} dis_isize_t;
/*
* get a byte from instruction stream
*/
static int
dtrace_dis_get_byte(void *p)
{
int ret;
uchar_t **instr = p;
ret = **instr;
*instr += 1;
return (ret);
}
/*
* Returns either the size of a given instruction, in bytes, or the size of that
* instruction's memory access (if any), depending on the value of `which'.
* If a programming error in the tables is detected, the system will panic to
* ease diagnosis. Invalid instructions will not be flagged. They will appear
* to have an instruction size between 1 and the actual size, and will be
* reported as having no memory impact.
*/
/* ARGSUSED2 */
static int
dtrace_dis_isize(uchar_t *instr, dis_isize_t which, model_t model, int *rmindex)
{
int sz;
dis86_t x;
uint_t mode = SIZE64;
#if defined(sun)
mode = (model == DATAMODEL_LP64) ? SIZE64 : SIZE32;
#endif
x.d86_data = (void **)&instr;
x.d86_get_byte = dtrace_dis_get_byte;
x.d86_check_func = NULL;
if (dtrace_disx86(&x, mode) != 0)
return (-1);
if (which == DIS_ISIZE_INSTR)
sz = x.d86_len; /* length of the instruction */
else
sz = x.d86_memsize; /* length of memory operand */
if (rmindex != NULL)
*rmindex = x.d86_rmindex;
return (sz);
}
int
dtrace_instr_size(uchar_t *instr)
{
return (dtrace_dis_isize(instr, DIS_ISIZE_INSTR, DATAMODEL_NATIVE,
NULL));
}

View file

@ -0,0 +1,84 @@
/*
* 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$
*/
/*
* DTrace Anonymous Enabling Functions
*/
static void
dtrace_anon_init(void *dummy)
{
dtrace_state_t *state = NULL;
dtrace_enabling_t *enab;
mutex_enter(&cpu_lock);
mutex_enter(&dtrace_provider_lock);
mutex_enter(&dtrace_lock);
dtrace_anon_property();
mutex_exit(&cpu_lock);
/*
* If there are already providers, we must ask them to provide their
* probes, and then match any anonymous enabling against them. Note
* that there should be no other retained enablings at this time:
* the only retained enablings at this time should be the anonymous
* enabling.
*/
if (dtrace_anon.dta_enabling != NULL) {
ASSERT(dtrace_retained == dtrace_anon.dta_enabling);
dtrace_enabling_provide(NULL);
state = dtrace_anon.dta_state;
/*
* We couldn't hold cpu_lock across the above call to
* dtrace_enabling_provide(), but we must hold it to actually
* enable the probes. We have to drop all of our locks, pick
* up cpu_lock, and regain our locks before matching the
* retained anonymous enabling.
*/
mutex_exit(&dtrace_lock);
mutex_exit(&dtrace_provider_lock);
mutex_enter(&cpu_lock);
mutex_enter(&dtrace_provider_lock);
mutex_enter(&dtrace_lock);
if ((enab = dtrace_anon.dta_enabling) != NULL)
(void) dtrace_enabling_match(enab, NULL);
mutex_exit(&cpu_lock);
}
mutex_exit(&dtrace_provider_lock);
mutex_exit(&dtrace_lock);
if (state != NULL) {
/*
* If we created any anonymous state, set it going now.
*/
(void) dtrace_state_go(state, &dtrace_anon.dta_beganon);
}
}

View file

@ -0,0 +1,134 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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$
*
*/
#ifndef _DTRACE_CDDL_H_
#define _DTRACE_CDDL_H_
#include <sys/proc.h>
#define LOCK_LEVEL 10
/*
* Kernel DTrace extension to 'struct proc' for FreeBSD.
*/
typedef struct kdtrace_proc {
int p_dtrace_probes; /* Are there probes for this proc? */
u_int64_t p_dtrace_count; /* Number of DTrace tracepoints */
void *p_dtrace_helpers; /* DTrace helpers, if any */
} kdtrace_proc_t;
/*
* Kernel DTrace extension to 'struct thread' for FreeBSD.
*/
typedef struct kdtrace_thread {
u_int8_t td_dtrace_stop; /* Indicates a DTrace-desired stop */
u_int8_t td_dtrace_sig; /* Signal sent via DTrace's raise() */
u_int td_predcache; /* DTrace predicate cache */
u_int64_t td_dtrace_vtime; /* DTrace virtual time */
u_int64_t td_dtrace_start; /* DTrace slice start time */
union __tdu {
struct __tds {
u_int8_t _td_dtrace_on;
/* Hit a fasttrap tracepoint. */
u_int8_t _td_dtrace_step;
/* About to return to kernel. */
u_int8_t _td_dtrace_ret;
/* Handling a return probe. */
u_int8_t _td_dtrace_ast;
/* Saved ast flag. */
} _tds;
u_long _td_dtrace_ft; /* Bitwise or of these flags. */
} _tdu;
#define td_dtrace_ft _tdu._td_dtrace_ft
#define td_dtrace_on _tdu._tds._td_dtrace_on
#define td_dtrace_step _tdu._tds._td_dtrace_step
#define td_dtrace_ret _tdu._tds._td_dtrace_ret
#define td_dtrace_ast _tdu._tds._td_dtrace_ast
uintptr_t td_dtrace_pc; /* DTrace saved pc from fasttrap. */
uintptr_t td_dtrace_npc; /* DTrace next pc from fasttrap. */
uintptr_t td_dtrace_scrpc;
/* DTrace per-thread scratch location. */
uintptr_t td_dtrace_astpc;
/* DTrace return sequence location. */
u_int64_t td_hrtime; /* Last time on cpu. */
int td_errno; /* Syscall return value. */
} kdtrace_thread_t;
/*
* Definitions to reference fields in the FreeBSD DTrace structures defined
* above using the names of fields in similar structures in Solaris. Note
* that the separation on FreeBSD is a licensing constraint designed to
* keep the GENERIC kernel BSD licensed.
*/
#define t_dtrace_vtime td_dtrace->td_dtrace_vtime
#define t_dtrace_start td_dtrace->td_dtrace_start
#define t_dtrace_stop td_dtrace->td_dtrace_stop
#define t_dtrace_sig td_dtrace->td_dtrace_sig
#define t_predcache td_dtrace->td_predcache
#define p_dtrace_helpers p_dtrace->p_dtrace_helpers
/*
* Definitions for fields in struct proc which are named differntly in FreeBSD.
*/
#define p_cred p_ucred
#define p_parent p_pptr
/*
* Definitions for fields in struct thread which are named differntly in FreeBSD.
*/
#define t_procp td_proc
#define t_tid td_tid
#define t_did td_tid
int priv_policy(const cred_t *, int, boolean_t, int, const char *);
boolean_t priv_policy_only(const cred_t *, int, boolean_t);
boolean_t priv_policy_choice(const cred_t *, int, boolean_t);
/*
* Test privilege. Audit success or failure, allow privilege debugging.
* Returns 0 for success, err for failure.
*/
#define PRIV_POLICY(cred, priv, all, err, reason) \
priv_policy((cred), (priv), (all), (err), (reason))
/*
* Test privilege. Audit success only, no privilege debugging.
* Returns 1 for success, and 0 for failure.
*/
#define PRIV_POLICY_CHOICE(cred, priv, all) \
priv_policy_choice((cred), (priv), (all))
/*
* Test privilege. No priv_debugging, no auditing.
* Returns 1 for success, and 0 for failure.
*/
#define PRIV_POLICY_ONLY(cred, priv, all) \
priv_policy_only((cred), (priv), (all))
#endif /* !_DTRACE_CDDL_H_ */

View file

@ -0,0 +1,61 @@
/*-
* Copyright (C) 2006 John Birrell <jb@freebsd.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice(s), this list of conditions and the following disclaimer as
* the first lines of this file unmodified other than the possible
* addition of one or more copyright notices.
* 2. Redistributions in binary form must reproduce the above copyright
* notice(s), this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* $FreeBSD$
*
*/
static void
dtrace_clone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev)
{
int u = -1;
size_t len;
if (*dev != NULL)
return;
len = strlen(name);
if (len != 6 && len != 13)
return;
if (bcmp(name,"dtrace",6) != 0)
return;
if (len == 13 && bcmp(name,"dtrace/dtrace",13) != 0)
return;
/* Clone the device to the new minor number. */
if (clone_create(&dtrace_clones, &dtrace_cdevsw, &u, dev, 0) != 0)
/* Create the /dev/dtrace/dtraceNN entry. */
*dev = make_dev_cred(&dtrace_cdevsw, unit2minor(u), cred,
UID_ROOT, GID_WHEEL, 0600, "dtrace/dtrace%d", u);
if (*dev != NULL) {
dev_ref(*dev);
(*dev)->si_flags |= SI_CHEAPCLONE;
}
}

View file

@ -0,0 +1,596 @@
/*-
* Copyright (C) 2008 John Birrell <jb@freebsd.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice(s), this list of conditions and the following disclaimer as
* the first lines of this file unmodified other than the possible
* addition of one or more copyright notices.
* 2. Redistributions in binary form must reproduce the above copyright
* notice(s), this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* $FreeBSD$
*
*/
#ifdef DEBUG
#if defined(__amd64__)
static __inline int
dtrace_cmpset_long(volatile u_long *dst, u_long exp, u_long src)
{
u_char res;
__asm __volatile(
" lock ; "
" cmpxchgq %2,%1 ; "
" sete %0 ; "
"1: "
"# dtrace_cmpset_long"
: "=a" (res), /* 0 */
"=m" (*dst) /* 1 */
: "r" (src), /* 2 */
"a" (exp), /* 3 */
"m" (*dst) /* 4 */
: "memory");
return (res);
}
#elif defined(__i386__)
static __inline int
dtrace_cmpset_long(volatile u_long *dst, u_long exp, u_long src)
{
u_char res;
__asm __volatile(
" lock ; "
" cmpxchgl %2,%1 ; "
" sete %0 ; "
"1: "
"# dtrace_cmpset_long"
: "=a" (res), /* 0 */
"=m" (*dst) /* 1 */
: "r" (src), /* 2 */
"a" (exp), /* 3 */
"m" (*dst) /* 4 */
: "memory");
return (res);
}
#endif
#define DTRACE_DEBUG_BUFR_SIZE (32 * 1024)
struct dtrace_debug_data {
char bufr[DTRACE_DEBUG_BUFR_SIZE];
char *first;
char *last;
char *next;
} dtrace_debug_data[MAXCPU];
static char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE];
static volatile u_long dtrace_debug_flag[MAXCPU];
static void
dtrace_debug_lock(int cpu)
{
while (dtrace_cmpset_long(&dtrace_debug_flag[cpu], 0, 1) == 0)
/* Loop until the lock is obtained. */
;
}
static void
dtrace_debug_unlock(int cpu)
{
dtrace_debug_flag[cpu] = 0;
}
static void
dtrace_debug_init(void *dummy)
{
int i;
struct dtrace_debug_data *d;
for (i = 0; i <= mp_maxid; i++) {
if (pcpu_find(i) == NULL)
continue;
d = &dtrace_debug_data[i];
if (d->first == NULL) {
d->first = d->bufr;
d->next = d->bufr;
d->last = d->bufr + DTRACE_DEBUG_BUFR_SIZE - 1;
*(d->last) = '\0';
}
}
}
SYSINIT(dtrace_debug_init, SI_SUB_KDTRACE, SI_ORDER_ANY, dtrace_debug_init, NULL);
SYSINIT(dtrace_debug_smpinit, SI_SUB_SMP, SI_ORDER_ANY, dtrace_debug_init, NULL);
static void
dtrace_debug_output(void)
{
char *p;
int i;
struct dtrace_debug_data *d;
uintptr_t count;
for (i = 0; i <= mp_maxid; i++) {
if (pcpu_find(i) == NULL)
continue;
dtrace_debug_lock(i);
d = &dtrace_debug_data[i];
count = 0;
if (d->first < d->next) {
char *p1 = dtrace_debug_bufr;
count = (uintptr_t) d->next - (uintptr_t) d->first;
for (p = d->first; p < d->next; p++)
*p1++ = *p;
} else if (d->next > d->first) {
char *p1 = dtrace_debug_bufr;
count = (uintptr_t) d->last - (uintptr_t) d->first;
for (p = d->first; p < d->last; p++)
*p1++ = *p;
count += (uintptr_t) d->next - (uintptr_t) d->bufr;
for (p = d->bufr; p < d->next; p++)
*p1++ = *p;
}
d->first = d->bufr;
d->next = d->bufr;
dtrace_debug_unlock(i);
if (count > 0) {
char *last = dtrace_debug_bufr + count;
p = dtrace_debug_bufr;
while (p < last) {
if (*p == '\0') {
p++;
continue;
}
printf("%s", p);
p += strlen(p);
}
}
}
}
/*
* Functions below here are called from the probe context, so they can't call
* _any_ functions outside the dtrace module without running foul of the function
* boundary trace provider (fbt). The purpose of these functions is limited to
* buffering debug strings for output when the probe completes on the current CPU.
*/
static __inline void
dtrace_debug__putc(char c)
{
struct dtrace_debug_data *d = &dtrace_debug_data[curcpu];
*d->next++ = c;
if (d->next == d->last)
d->next = d->bufr;
*(d->next) = '\0';
if (d->next == d->first)
d->first++;
if (d->first == d->last)
d->first = d->bufr;
}
static void __used
dtrace_debug_putc(char c)
{
dtrace_debug_lock(curcpu);
dtrace_debug__putc(c);
dtrace_debug_unlock(curcpu);
}
static void __used
dtrace_debug_puts(const char *s)
{
dtrace_debug_lock(curcpu);
while (*s != '\0')
dtrace_debug__putc(*s++);
dtrace_debug__putc('\0');
dtrace_debug_unlock(curcpu);
}
/*
* Snaffled from sys/kern/subr_prf.c
*
* Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
* order; return an optional length and a pointer to the last character
* written in the buffer (i.e., the first character of the string).
* The buffer pointed to by `nbuf' must have length >= MAXNBUF.
*/
static char *
dtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
{
char *p, c;
p = nbuf;
*p = '\0';
do {
c = hex2ascii(num % base);
*++p = upper ? toupper(c) : c;
} while (num /= base);
if (lenp)
*lenp = p - nbuf;
return (p);
}
#define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
static void
dtrace_debug_vprintf(const char *fmt, va_list ap)
{
char nbuf[MAXNBUF];
const char *p, *percent, *q;
u_char *up;
int ch, n;
uintmax_t num;
int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
int cflag, hflag, jflag, tflag, zflag;
int dwidth, upper;
int radix = 10;
char padc;
int stop = 0, retval = 0;
num = 0;
if (fmt == NULL)
fmt = "(fmt null)\n";
for (;;) {
padc = ' ';
width = 0;
while ((ch = (u_char)*fmt++) != '%' || stop) {
if (ch == '\0') {
dtrace_debug__putc('\0');
return;
}
dtrace_debug__putc(ch);
}
percent = fmt - 1;
qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
sign = 0; dot = 0; dwidth = 0; upper = 0;
cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
reswitch: switch (ch = (u_char)*fmt++) {
case '.':
dot = 1;
goto reswitch;
case '#':
sharpflag = 1;
goto reswitch;
case '+':
sign = 1;
goto reswitch;
case '-':
ladjust = 1;
goto reswitch;
case '%':
dtrace_debug__putc(ch);
break;
case '*':
if (!dot) {
width = va_arg(ap, int);
if (width < 0) {
ladjust = !ladjust;
width = -width;
}
} else {
dwidth = va_arg(ap, int);
}
goto reswitch;
case '0':
if (!dot) {
padc = '0';
goto reswitch;
}
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
for (n = 0;; ++fmt) {
n = n * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
if (dot)
dwidth = n;
else
width = n;
goto reswitch;
case 'b':
num = (u_int)va_arg(ap, int);
p = va_arg(ap, char *);
for (q = dtrace_debug_ksprintn(nbuf, num, *p++, NULL, 0); *q;)
dtrace_debug__putc(*q--);
if (num == 0)
break;
for (tmp = 0; *p;) {
n = *p++;
if (num & (1 << (n - 1))) {
dtrace_debug__putc(tmp ? ',' : '<');
for (; (n = *p) > ' '; ++p)
dtrace_debug__putc(n);
tmp = 1;
} else
for (; *p > ' '; ++p)
continue;
}
if (tmp)
dtrace_debug__putc('>');
break;
case 'c':
dtrace_debug__putc(va_arg(ap, int));
break;
case 'D':
up = va_arg(ap, u_char *);
p = va_arg(ap, char *);
if (!width)
width = 16;
while(width--) {
dtrace_debug__putc(hex2ascii(*up >> 4));
dtrace_debug__putc(hex2ascii(*up & 0x0f));
up++;
if (width)
for (q=p;*q;q++)
dtrace_debug__putc(*q);
}
break;
case 'd':
case 'i':
base = 10;
sign = 1;
goto handle_sign;
case 'h':
if (hflag) {
hflag = 0;
cflag = 1;
} else
hflag = 1;
goto reswitch;
case 'j':
jflag = 1;
goto reswitch;
case 'l':
if (lflag) {
lflag = 0;
qflag = 1;
} else
lflag = 1;
goto reswitch;
case 'n':
if (jflag)
*(va_arg(ap, intmax_t *)) = retval;
else if (qflag)
*(va_arg(ap, quad_t *)) = retval;
else if (lflag)
*(va_arg(ap, long *)) = retval;
else if (zflag)
*(va_arg(ap, size_t *)) = retval;
else if (hflag)
*(va_arg(ap, short *)) = retval;
else if (cflag)
*(va_arg(ap, char *)) = retval;
else
*(va_arg(ap, int *)) = retval;
break;
case 'o':
base = 8;
goto handle_nosign;
case 'p':
base = 16;
sharpflag = (width == 0);
sign = 0;
num = (uintptr_t)va_arg(ap, void *);
goto number;
case 'q':
qflag = 1;
goto reswitch;
case 'r':
base = radix;
if (sign)
goto handle_sign;
goto handle_nosign;
case 's':
p = va_arg(ap, char *);
if (p == NULL)
p = "(null)";
if (!dot)
n = strlen (p);
else
for (n = 0; n < dwidth && p[n]; n++)
continue;
width -= n;
if (!ladjust && width > 0)
while (width--)
dtrace_debug__putc(padc);
while (n--)
dtrace_debug__putc(*p++);
if (ladjust && width > 0)
while (width--)
dtrace_debug__putc(padc);
break;
case 't':
tflag = 1;
goto reswitch;
case 'u':
base = 10;
goto handle_nosign;
case 'X':
upper = 1;
case 'x':
base = 16;
goto handle_nosign;
case 'y':
base = 16;
sign = 1;
goto handle_sign;
case 'z':
zflag = 1;
goto reswitch;
handle_nosign:
sign = 0;
if (jflag)
num = va_arg(ap, uintmax_t);
else if (qflag)
num = va_arg(ap, u_quad_t);
else if (tflag)
num = va_arg(ap, ptrdiff_t);
else if (lflag)
num = va_arg(ap, u_long);
else if (zflag)
num = va_arg(ap, size_t);
else if (hflag)
num = (u_short)va_arg(ap, int);
else if (cflag)
num = (u_char)va_arg(ap, int);
else
num = va_arg(ap, u_int);
goto number;
handle_sign:
if (jflag)
num = va_arg(ap, intmax_t);
else if (qflag)
num = va_arg(ap, quad_t);
else if (tflag)
num = va_arg(ap, ptrdiff_t);
else if (lflag)
num = va_arg(ap, long);
else if (zflag)
num = va_arg(ap, size_t);
else if (hflag)
num = (short)va_arg(ap, int);
else if (cflag)
num = (char)va_arg(ap, int);
else
num = va_arg(ap, int);
number:
if (sign && (intmax_t)num < 0) {
neg = 1;
num = -(intmax_t)num;
}
p = dtrace_debug_ksprintn(nbuf, num, base, &tmp, upper);
if (sharpflag && num != 0) {
if (base == 8)
tmp++;
else if (base == 16)
tmp += 2;
}
if (neg)
tmp++;
if (!ladjust && padc != '0' && width
&& (width -= tmp) > 0)
while (width--)
dtrace_debug__putc(padc);
if (neg)
dtrace_debug__putc('-');
if (sharpflag && num != 0) {
if (base == 8) {
dtrace_debug__putc('0');
} else if (base == 16) {
dtrace_debug__putc('0');
dtrace_debug__putc('x');
}
}
if (!ladjust && width && (width -= tmp) > 0)
while (width--)
dtrace_debug__putc(padc);
while (*p)
dtrace_debug__putc(*p--);
if (ladjust && width && (width -= tmp) > 0)
while (width--)
dtrace_debug__putc(padc);
break;
default:
while (percent < fmt)
dtrace_debug__putc(*percent++);
/*
* Since we ignore an formatting argument it is no
* longer safe to obey the remaining formatting
* arguments as the arguments will no longer match
* the format specs.
*/
stop = 1;
break;
}
}
dtrace_debug__putc('\0');
}
void
dtrace_debug_printf(const char *fmt, ...)
{
va_list ap;
dtrace_debug_lock(curcpu);
va_start(ap, fmt);
dtrace_debug_vprintf(fmt, ap);
va_end(ap);
dtrace_debug_unlock(curcpu);
}
#else
#define dtrace_debug_output()
#define dtrace_debug_puts(_s)
#define dtrace_debug_printf(fmt, ...)
#endif

View file

@ -0,0 +1,13 @@
/* $FreeBSD$ */
/* XXX Hacks.... */
dtrace_cacheid_t dtrace_predcache_id;
int panic_quiesce;
char panic_stack[PANICSTKSIZE];
boolean_t
priv_policy_only(const cred_t *a, int b, boolean_t c)
{
return 0;
}

View file

@ -0,0 +1,777 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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$
*
*/
static int dtrace_verbose_ioctl;
SYSCTL_INT(_debug_dtrace, OID_AUTO, verbose_ioctl, CTLFLAG_RW, &dtrace_verbose_ioctl, 0, "");
#define DTRACE_IOCTL_PRINTF(fmt, ...) if (dtrace_verbose_ioctl) printf(fmt, ## __VA_ARGS__ )
/* ARGSUSED */
static int
dtrace_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
int flags __unused, struct thread *td)
{
dtrace_state_t *state = dev->si_drv1;
int error = 0;
if (state == NULL)
return (EINVAL);
if (state->dts_anon) {
ASSERT(dtrace_anon.dta_state == NULL);
state = state->dts_anon;
}
switch (cmd) {
case DTRACEIOC_AGGDESC: {
dtrace_aggdesc_t **paggdesc = (dtrace_aggdesc_t **) addr;
dtrace_aggdesc_t aggdesc;
dtrace_action_t *act;
dtrace_aggregation_t *agg;
int nrecs;
uint32_t offs;
dtrace_recdesc_t *lrec;
void *buf;
size_t size;
uintptr_t dest;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_AGGDESC\n",__func__,__LINE__);
if (copyin((void *) *paggdesc, &aggdesc, sizeof (aggdesc)) != 0)
return (EFAULT);
mutex_enter(&dtrace_lock);
if ((agg = dtrace_aggid2agg(state, aggdesc.dtagd_id)) == NULL) {
mutex_exit(&dtrace_lock);
return (EINVAL);
}
aggdesc.dtagd_epid = agg->dtag_ecb->dte_epid;
nrecs = aggdesc.dtagd_nrecs;
aggdesc.dtagd_nrecs = 0;
offs = agg->dtag_base;
lrec = &agg->dtag_action.dta_rec;
aggdesc.dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - offs;
for (act = agg->dtag_first; ; act = act->dta_next) {
ASSERT(act->dta_intuple ||
DTRACEACT_ISAGG(act->dta_kind));
/*
* If this action has a record size of zero, it
* denotes an argument to the aggregating action.
* Because the presence of this record doesn't (or
* shouldn't) affect the way the data is interpreted,
* we don't copy it out to save user-level the
* confusion of dealing with a zero-length record.
*/
if (act->dta_rec.dtrd_size == 0) {
ASSERT(agg->dtag_hasarg);
continue;
}
aggdesc.dtagd_nrecs++;
if (act == &agg->dtag_action)
break;
}
/*
* Now that we have the size, we need to allocate a temporary
* buffer in which to store the complete description. We need
* the temporary buffer to be able to drop dtrace_lock()
* across the copyout(), below.
*/
size = sizeof (dtrace_aggdesc_t) +
(aggdesc.dtagd_nrecs * sizeof (dtrace_recdesc_t));
buf = kmem_alloc(size, KM_SLEEP);
dest = (uintptr_t)buf;
bcopy(&aggdesc, (void *)dest, sizeof (aggdesc));
dest += offsetof(dtrace_aggdesc_t, dtagd_rec[0]);
for (act = agg->dtag_first; ; act = act->dta_next) {
dtrace_recdesc_t rec = act->dta_rec;
/*
* See the comment in the above loop for why we pass
* over zero-length records.
*/
if (rec.dtrd_size == 0) {
ASSERT(agg->dtag_hasarg);
continue;
}
if (nrecs-- == 0)
break;
rec.dtrd_offset -= offs;
bcopy(&rec, (void *)dest, sizeof (rec));
dest += sizeof (dtrace_recdesc_t);
if (act == &agg->dtag_action)
break;
}
mutex_exit(&dtrace_lock);
if (copyout(buf, (void *) *paggdesc, dest - (uintptr_t)buf) != 0) {
kmem_free(buf, size);
return (EFAULT);
}
kmem_free(buf, size);
return (0);
}
case DTRACEIOC_AGGSNAP:
case DTRACEIOC_BUFSNAP: {
dtrace_bufdesc_t **pdesc = (dtrace_bufdesc_t **) addr;
dtrace_bufdesc_t desc;
caddr_t cached;
dtrace_buffer_t *buf;
dtrace_debug_output();
if (copyin((void *) *pdesc, &desc, sizeof (desc)) != 0)
return (EFAULT);
DTRACE_IOCTL_PRINTF("%s(%d): %s curcpu %d cpu %d\n",
__func__,__LINE__,
cmd == DTRACEIOC_AGGSNAP ?
"DTRACEIOC_AGGSNAP":"DTRACEIOC_BUFSNAP",
curcpu, desc.dtbd_cpu);
if (desc.dtbd_cpu < 0 || desc.dtbd_cpu >= NCPU)
return (ENOENT);
if (pcpu_find(desc.dtbd_cpu) == NULL)
return (ENOENT);
mutex_enter(&dtrace_lock);
if (cmd == DTRACEIOC_BUFSNAP) {
buf = &state->dts_buffer[desc.dtbd_cpu];
} else {
buf = &state->dts_aggbuffer[desc.dtbd_cpu];
}
if (buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL)) {
size_t sz = buf->dtb_offset;
if (state->dts_activity != DTRACE_ACTIVITY_STOPPED) {
mutex_exit(&dtrace_lock);
return (EBUSY);
}
/*
* If this buffer has already been consumed, we're
* going to indicate that there's nothing left here
* to consume.
*/
if (buf->dtb_flags & DTRACEBUF_CONSUMED) {
mutex_exit(&dtrace_lock);
desc.dtbd_size = 0;
desc.dtbd_drops = 0;
desc.dtbd_errors = 0;
desc.dtbd_oldest = 0;
sz = sizeof (desc);
if (copyout(&desc, (void *) *pdesc, sz) != 0)
return (EFAULT);
return (0);
}
/*
* If this is a ring buffer that has wrapped, we want
* to copy the whole thing out.
*/
if (buf->dtb_flags & DTRACEBUF_WRAPPED) {
dtrace_buffer_polish(buf);
sz = buf->dtb_size;
}
if (copyout(buf->dtb_tomax, desc.dtbd_data, sz) != 0) {
mutex_exit(&dtrace_lock);
return (EFAULT);
}
desc.dtbd_size = sz;
desc.dtbd_drops = buf->dtb_drops;
desc.dtbd_errors = buf->dtb_errors;
desc.dtbd_oldest = buf->dtb_xamot_offset;
mutex_exit(&dtrace_lock);
if (copyout(&desc, (void *) *pdesc, sizeof (desc)) != 0)
return (EFAULT);
buf->dtb_flags |= DTRACEBUF_CONSUMED;
return (0);
}
if (buf->dtb_tomax == NULL) {
ASSERT(buf->dtb_xamot == NULL);
mutex_exit(&dtrace_lock);
return (ENOENT);
}
cached = buf->dtb_tomax;
ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH));
dtrace_xcall(desc.dtbd_cpu,
(dtrace_xcall_t)dtrace_buffer_switch, buf);
state->dts_errors += buf->dtb_xamot_errors;
/*
* If the buffers did not actually switch, then the cross call
* did not take place -- presumably because the given CPU is
* not in the ready set. If this is the case, we'll return
* ENOENT.
*/
if (buf->dtb_tomax == cached) {
ASSERT(buf->dtb_xamot != cached);
mutex_exit(&dtrace_lock);
return (ENOENT);
}
ASSERT(cached == buf->dtb_xamot);
DTRACE_IOCTL_PRINTF("%s(%d): copyout the buffer snapshot\n",__func__,__LINE__);
/*
* We have our snapshot; now copy it out.
*/
if (copyout(buf->dtb_xamot, desc.dtbd_data,
buf->dtb_xamot_offset) != 0) {
mutex_exit(&dtrace_lock);
return (EFAULT);
}
desc.dtbd_size = buf->dtb_xamot_offset;
desc.dtbd_drops = buf->dtb_xamot_drops;
desc.dtbd_errors = buf->dtb_xamot_errors;
desc.dtbd_oldest = 0;
mutex_exit(&dtrace_lock);
DTRACE_IOCTL_PRINTF("%s(%d): copyout buffer desc: size %zd drops %lu errors %lu\n",__func__,__LINE__,(size_t) desc.dtbd_size,(u_long) desc.dtbd_drops,(u_long) desc.dtbd_errors);
/*
* Finally, copy out the buffer description.
*/
if (copyout(&desc, (void *) *pdesc, sizeof (desc)) != 0)
return (EFAULT);
return (0);
}
case DTRACEIOC_CONF: {
dtrace_conf_t conf;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_CONF\n",__func__,__LINE__);
bzero(&conf, sizeof (conf));
conf.dtc_difversion = DIF_VERSION;
conf.dtc_difintregs = DIF_DIR_NREGS;
conf.dtc_diftupregs = DIF_DTR_NREGS;
conf.dtc_ctfmodel = CTF_MODEL_NATIVE;
*((dtrace_conf_t *) addr) = conf;
return (0);
}
case DTRACEIOC_DOFGET: {
dof_hdr_t **pdof = (dof_hdr_t **) addr;
dof_hdr_t hdr, *dof = *pdof;
int rval;
uint64_t len;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_DOFGET\n",__func__,__LINE__);
if (copyin((void *)dof, &hdr, sizeof (hdr)) != 0)
return (EFAULT);
mutex_enter(&dtrace_lock);
dof = dtrace_dof_create(state);
mutex_exit(&dtrace_lock);
len = MIN(hdr.dofh_loadsz, dof->dofh_loadsz);
rval = copyout(dof, (void *) *pdof, len);
dtrace_dof_destroy(dof);
return (rval == 0 ? 0 : EFAULT);
}
case DTRACEIOC_ENABLE: {
dof_hdr_t *dof = NULL;
dtrace_enabling_t *enab = NULL;
dtrace_vstate_t *vstate;
int err = 0;
int rval;
dtrace_enable_io_t *p = (dtrace_enable_io_t *) addr;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_ENABLE\n",__func__,__LINE__);
/*
* If a NULL argument has been passed, we take this as our
* cue to reevaluate our enablings.
*/
if (p->dof == NULL) {
dtrace_enabling_matchall();
return (0);
}
if ((dof = dtrace_dof_copyin((uintptr_t) p->dof, &rval)) == NULL)
return (EINVAL);
mutex_enter(&cpu_lock);
mutex_enter(&dtrace_lock);
vstate = &state->dts_vstate;
if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) {
mutex_exit(&dtrace_lock);
mutex_exit(&cpu_lock);
dtrace_dof_destroy(dof);
return (EBUSY);
}
if (dtrace_dof_slurp(dof, vstate, td->td_ucred, &enab, 0, B_TRUE) != 0) {
mutex_exit(&dtrace_lock);
mutex_exit(&cpu_lock);
dtrace_dof_destroy(dof);
return (EINVAL);
}
if ((rval = dtrace_dof_options(dof, state)) != 0) {
dtrace_enabling_destroy(enab);
mutex_exit(&dtrace_lock);
mutex_exit(&cpu_lock);
dtrace_dof_destroy(dof);
return (rval);
}
if ((err = dtrace_enabling_match(enab, &p->n_matched)) == 0) {
err = dtrace_enabling_retain(enab);
} else {
dtrace_enabling_destroy(enab);
}
mutex_exit(&cpu_lock);
mutex_exit(&dtrace_lock);
dtrace_dof_destroy(dof);
return (err);
}
case DTRACEIOC_EPROBE: {
dtrace_eprobedesc_t **pepdesc = (dtrace_eprobedesc_t **) addr;
dtrace_eprobedesc_t epdesc;
dtrace_ecb_t *ecb;
dtrace_action_t *act;
void *buf;
size_t size;
uintptr_t dest;
int nrecs;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_EPROBE\n",__func__,__LINE__);
if (copyin((void *)*pepdesc, &epdesc, sizeof (epdesc)) != 0)
return (EFAULT);
mutex_enter(&dtrace_lock);
if ((ecb = dtrace_epid2ecb(state, epdesc.dtepd_epid)) == NULL) {
mutex_exit(&dtrace_lock);
return (EINVAL);
}
if (ecb->dte_probe == NULL) {
mutex_exit(&dtrace_lock);
return (EINVAL);
}
epdesc.dtepd_probeid = ecb->dte_probe->dtpr_id;
epdesc.dtepd_uarg = ecb->dte_uarg;
epdesc.dtepd_size = ecb->dte_size;
nrecs = epdesc.dtepd_nrecs;
epdesc.dtepd_nrecs = 0;
for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple)
continue;
epdesc.dtepd_nrecs++;
}
/*
* Now that we have the size, we need to allocate a temporary
* buffer in which to store the complete description. We need
* the temporary buffer to be able to drop dtrace_lock()
* across the copyout(), below.
*/
size = sizeof (dtrace_eprobedesc_t) +
(epdesc.dtepd_nrecs * sizeof (dtrace_recdesc_t));
buf = kmem_alloc(size, KM_SLEEP);
dest = (uintptr_t)buf;
bcopy(&epdesc, (void *)dest, sizeof (epdesc));
dest += offsetof(dtrace_eprobedesc_t, dtepd_rec[0]);
for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple)
continue;
if (nrecs-- == 0)
break;
bcopy(&act->dta_rec, (void *)dest,
sizeof (dtrace_recdesc_t));
dest += sizeof (dtrace_recdesc_t);
}
mutex_exit(&dtrace_lock);
if (copyout(buf, (void *) *pepdesc, dest - (uintptr_t)buf) != 0) {
kmem_free(buf, size);
return (EFAULT);
}
kmem_free(buf, size);
return (0);
}
case DTRACEIOC_FORMAT: {
dtrace_fmtdesc_t *fmt = (dtrace_fmtdesc_t *) addr;
char *str;
int len;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_FORMAT\n",__func__,__LINE__);
mutex_enter(&dtrace_lock);
if (fmt->dtfd_format == 0 ||
fmt->dtfd_format > state->dts_nformats) {
mutex_exit(&dtrace_lock);
return (EINVAL);
}
/*
* Format strings are allocated contiguously and they are
* never freed; if a format index is less than the number
* of formats, we can assert that the format map is non-NULL
* and that the format for the specified index is non-NULL.
*/
ASSERT(state->dts_formats != NULL);
str = state->dts_formats[fmt->dtfd_format - 1];
ASSERT(str != NULL);
len = strlen(str) + 1;
if (len > fmt->dtfd_length) {
fmt->dtfd_length = len;
} else {
if (copyout(str, fmt->dtfd_string, len) != 0) {
mutex_exit(&dtrace_lock);
return (EINVAL);
}
}
mutex_exit(&dtrace_lock);
return (0);
}
case DTRACEIOC_GO: {
int rval;
processorid_t *cpuid = (processorid_t *) addr;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_GO\n",__func__,__LINE__);
rval = dtrace_state_go(state, cpuid);
return (rval);
}
case DTRACEIOC_PROBEARG: {
dtrace_argdesc_t *desc = (dtrace_argdesc_t *) addr;
dtrace_probe_t *probe;
dtrace_provider_t *prov;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_PROBEARG\n",__func__,__LINE__);
if (desc->dtargd_id == DTRACE_IDNONE)
return (EINVAL);
if (desc->dtargd_ndx == DTRACE_ARGNONE)
return (EINVAL);
mutex_enter(&dtrace_provider_lock);
mutex_enter(&mod_lock);
mutex_enter(&dtrace_lock);
if (desc->dtargd_id > dtrace_nprobes) {
mutex_exit(&dtrace_lock);
mutex_exit(&mod_lock);
mutex_exit(&dtrace_provider_lock);
return (EINVAL);
}
if ((probe = dtrace_probes[desc->dtargd_id - 1]) == NULL) {
mutex_exit(&dtrace_lock);
mutex_exit(&mod_lock);
mutex_exit(&dtrace_provider_lock);
return (EINVAL);
}
mutex_exit(&dtrace_lock);
prov = probe->dtpr_provider;
if (prov->dtpv_pops.dtps_getargdesc == NULL) {
/*
* There isn't any typed information for this probe.
* Set the argument number to DTRACE_ARGNONE.
*/
desc->dtargd_ndx = DTRACE_ARGNONE;
} else {
desc->dtargd_native[0] = '\0';
desc->dtargd_xlate[0] = '\0';
desc->dtargd_mapping = desc->dtargd_ndx;
prov->dtpv_pops.dtps_getargdesc(prov->dtpv_arg,
probe->dtpr_id, probe->dtpr_arg, desc);
}
mutex_exit(&mod_lock);
mutex_exit(&dtrace_provider_lock);
return (0);
}
case DTRACEIOC_PROBEMATCH:
case DTRACEIOC_PROBES: {
dtrace_probedesc_t *p_desc = (dtrace_probedesc_t *) addr;
dtrace_probe_t *probe = NULL;
dtrace_probekey_t pkey;
dtrace_id_t i;
int m = 0;
uint32_t priv = 0;
uid_t uid = 0;
zoneid_t zoneid = 0;
DTRACE_IOCTL_PRINTF("%s(%d): %s\n",__func__,__LINE__,
cmd == DTRACEIOC_PROBEMATCH ?
"DTRACEIOC_PROBEMATCH":"DTRACEIOC_PROBES");
p_desc->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
p_desc->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
p_desc->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
p_desc->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
/*
* Before we attempt to match this probe, we want to give
* all providers the opportunity to provide it.
*/
if (p_desc->dtpd_id == DTRACE_IDNONE) {
mutex_enter(&dtrace_provider_lock);
dtrace_probe_provide(p_desc, NULL);
mutex_exit(&dtrace_provider_lock);
p_desc->dtpd_id++;
}
if (cmd == DTRACEIOC_PROBEMATCH) {
dtrace_probekey(p_desc, &pkey);
pkey.dtpk_id = DTRACE_IDNONE;
}
dtrace_cred2priv(td->td_ucred, &priv, &uid, &zoneid);
mutex_enter(&dtrace_lock);
if (cmd == DTRACEIOC_PROBEMATCH) {
for (i = p_desc->dtpd_id; i <= dtrace_nprobes; i++) {
if ((probe = dtrace_probes[i - 1]) != NULL &&
(m = dtrace_match_probe(probe, &pkey,
priv, uid, zoneid)) != 0)
break;
}
if (m < 0) {
mutex_exit(&dtrace_lock);
return (EINVAL);
}
} else {
for (i = p_desc->dtpd_id; i <= dtrace_nprobes; i++) {
if ((probe = dtrace_probes[i - 1]) != NULL &&
dtrace_match_priv(probe, priv, uid, zoneid))
break;
}
}
if (probe == NULL) {
mutex_exit(&dtrace_lock);
return (ESRCH);
}
dtrace_probe_description(probe, p_desc);
mutex_exit(&dtrace_lock);
return (0);
}
case DTRACEIOC_PROVIDER: {
dtrace_providerdesc_t *pvd = (dtrace_providerdesc_t *) addr;
dtrace_provider_t *pvp;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_PROVIDER\n",__func__,__LINE__);
pvd->dtvd_name[DTRACE_PROVNAMELEN - 1] = '\0';
mutex_enter(&dtrace_provider_lock);
for (pvp = dtrace_provider; pvp != NULL; pvp = pvp->dtpv_next) {
if (strcmp(pvp->dtpv_name, pvd->dtvd_name) == 0)
break;
}
mutex_exit(&dtrace_provider_lock);
if (pvp == NULL)
return (ESRCH);
bcopy(&pvp->dtpv_priv, &pvd->dtvd_priv, sizeof (dtrace_ppriv_t));
bcopy(&pvp->dtpv_attr, &pvd->dtvd_attr, sizeof (dtrace_pattr_t));
return (0);
}
case DTRACEIOC_REPLICATE: {
dtrace_repldesc_t *desc = (dtrace_repldesc_t *) addr;
dtrace_probedesc_t *match = &desc->dtrpd_match;
dtrace_probedesc_t *create = &desc->dtrpd_create;
int err;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_REPLICATE\n",__func__,__LINE__);
match->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
match->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
match->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
match->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
create->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
create->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
create->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
create->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
mutex_enter(&dtrace_lock);
err = dtrace_enabling_replicate(state, match, create);
mutex_exit(&dtrace_lock);
return (err);
}
case DTRACEIOC_STATUS: {
dtrace_status_t *stat = (dtrace_status_t *) addr;
dtrace_dstate_t *dstate;
int i, j;
uint64_t nerrs;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_STATUS\n",__func__,__LINE__);
/*
* See the comment in dtrace_state_deadman() for the reason
* for setting dts_laststatus to INT64_MAX before setting
* it to the correct value.
*/
state->dts_laststatus = INT64_MAX;
dtrace_membar_producer();
state->dts_laststatus = dtrace_gethrtime();
bzero(stat, sizeof (*stat));
mutex_enter(&dtrace_lock);
if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) {
mutex_exit(&dtrace_lock);
return (ENOENT);
}
if (state->dts_activity == DTRACE_ACTIVITY_DRAINING)
stat->dtst_exiting = 1;
nerrs = state->dts_errors;
dstate = &state->dts_vstate.dtvs_dynvars;
for (i = 0; i < NCPU; i++) {
#if !defined(sun)
if (pcpu_find(i) == NULL)
continue;
#endif
dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[i];
stat->dtst_dyndrops += dcpu->dtdsc_drops;
stat->dtst_dyndrops_dirty += dcpu->dtdsc_dirty_drops;
stat->dtst_dyndrops_rinsing += dcpu->dtdsc_rinsing_drops;
if (state->dts_buffer[i].dtb_flags & DTRACEBUF_FULL)
stat->dtst_filled++;
nerrs += state->dts_buffer[i].dtb_errors;
for (j = 0; j < state->dts_nspeculations; j++) {
dtrace_speculation_t *spec;
dtrace_buffer_t *buf;
spec = &state->dts_speculations[j];
buf = &spec->dtsp_buffer[i];
stat->dtst_specdrops += buf->dtb_xamot_drops;
}
}
stat->dtst_specdrops_busy = state->dts_speculations_busy;
stat->dtst_specdrops_unavail = state->dts_speculations_unavail;
stat->dtst_stkstroverflows = state->dts_stkstroverflows;
stat->dtst_dblerrors = state->dts_dblerrors;
stat->dtst_killed =
(state->dts_activity == DTRACE_ACTIVITY_KILLED);
stat->dtst_errors = nerrs;
mutex_exit(&dtrace_lock);
return (0);
}
case DTRACEIOC_STOP: {
int rval;
processorid_t *cpuid = (processorid_t *) addr;
DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_STOP\n",__func__,__LINE__);
mutex_enter(&dtrace_lock);
rval = dtrace_state_stop(state, cpuid);
mutex_exit(&dtrace_lock);
return (rval);
}
default:
error = ENOTTY;
}
return (error);
}

View file

@ -0,0 +1,164 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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$
*
*/
static void
dtrace_ap_start(void *dummy)
{
int i;
mutex_enter(&cpu_lock);
/* Setup the rest of the CPUs. */
for (i = 1; i <= mp_maxid; i++) {
if (pcpu_find(i) == NULL)
continue;
(void) dtrace_cpu_setup(CPU_CONFIG, i);
}
mutex_exit(&cpu_lock);
}
SYSINIT(dtrace_ap_start, SI_SUB_SMP, SI_ORDER_ANY, dtrace_ap_start, NULL);
static void
dtrace_load(void *dummy)
{
dtrace_provider_id_t id;
/* Hook into the trap handler. */
dtrace_trap_func = dtrace_trap;
/* Hang our hook for thread switches. */
dtrace_vtime_switch_func = dtrace_vtime_switch;
/* Hang our hook for exceptions. */
dtrace_invop_init();
/*
* XXX This is a short term hack to avoid having to comment
* out lots and lots of lock/unlock calls.
*/
mutex_init(&mod_lock,"XXX mod_lock hack", MUTEX_DEFAULT, NULL);
/*
* Initialise the mutexes without 'witness' because the dtrace
* code is mostly written to wait for memory. To have the
* witness code change a malloc() from M_WAITOK to M_NOWAIT
* because a lock is held would surely create a panic in a
* low memory situation. And that low memory situation might be
* the very problem we are trying to trace.
*/
mutex_init(&dtrace_lock,"dtrace probe state", MUTEX_DEFAULT, NULL);
mutex_init(&dtrace_provider_lock,"dtrace provider state", MUTEX_DEFAULT, NULL);
mutex_init(&dtrace_meta_lock,"dtrace meta-provider state", MUTEX_DEFAULT, NULL);
mutex_init(&dtrace_errlock,"dtrace error lock", MUTEX_DEFAULT, NULL);
mutex_enter(&dtrace_provider_lock);
mutex_enter(&dtrace_lock);
mutex_enter(&cpu_lock);
ASSERT(MUTEX_HELD(&cpu_lock));
dtrace_arena = new_unrhdr(1, INT_MAX, &dtrace_unr_mtx);
dtrace_state_cache = kmem_cache_create("dtrace_state_cache",
sizeof (dtrace_dstate_percpu_t) * NCPU, DTRACE_STATE_ALIGN,
NULL, NULL, NULL, NULL, NULL, 0);
ASSERT(MUTEX_HELD(&cpu_lock));
dtrace_bymod = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_mod),
offsetof(dtrace_probe_t, dtpr_nextmod),
offsetof(dtrace_probe_t, dtpr_prevmod));
dtrace_byfunc = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_func),
offsetof(dtrace_probe_t, dtpr_nextfunc),
offsetof(dtrace_probe_t, dtpr_prevfunc));
dtrace_byname = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_name),
offsetof(dtrace_probe_t, dtpr_nextname),
offsetof(dtrace_probe_t, dtpr_prevname));
if (dtrace_retain_max < 1) {
cmn_err(CE_WARN, "illegal value (%lu) for dtrace_retain_max; "
"setting to 1", dtrace_retain_max);
dtrace_retain_max = 1;
}
/*
* Now discover our toxic ranges.
*/
dtrace_toxic_ranges(dtrace_toxrange_add);
/*
* Before we register ourselves as a provider to our own framework,
* we would like to assert that dtrace_provider is NULL -- but that's
* not true if we were loaded as a dependency of a DTrace provider.
* Once we've registered, we can assert that dtrace_provider is our
* pseudo provider.
*/
(void) dtrace_register("dtrace", &dtrace_provider_attr,
DTRACE_PRIV_NONE, 0, &dtrace_provider_ops, NULL, &id);
ASSERT(dtrace_provider != NULL);
ASSERT((dtrace_provider_id_t)dtrace_provider == id);
dtrace_probeid_begin = dtrace_probe_create((dtrace_provider_id_t)
dtrace_provider, NULL, NULL, "BEGIN", 0, NULL);
dtrace_probeid_end = dtrace_probe_create((dtrace_provider_id_t)
dtrace_provider, NULL, NULL, "END", 0, NULL);
dtrace_probeid_error = dtrace_probe_create((dtrace_provider_id_t)
dtrace_provider, NULL, NULL, "ERROR", 1, NULL);
mutex_exit(&cpu_lock);
/*
* If DTrace helper tracing is enabled, we need to allocate the
* trace buffer and initialize the values.
*/
if (dtrace_helptrace_enabled) {
ASSERT(dtrace_helptrace_buffer == NULL);
dtrace_helptrace_buffer =
kmem_zalloc(dtrace_helptrace_bufsize, KM_SLEEP);
dtrace_helptrace_next = 0;
}
mutex_exit(&dtrace_lock);
mutex_exit(&dtrace_provider_lock);
mutex_enter(&cpu_lock);
/* Setup the boot CPU */
(void) dtrace_cpu_setup(CPU_CONFIG, 0);
mutex_exit(&cpu_lock);
/* Enable device cloning. */
clone_setup(&dtrace_clones);
/* Setup device cloning events. */
eh_tag = EVENTHANDLER_REGISTER(dev_clone, dtrace_clone, 0, 1000);
return;
}

View file

@ -0,0 +1,47 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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$
*
*/
/* ARGSUSED */
static int
dtrace_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}

View file

@ -0,0 +1,82 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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$
*
*/
int dtrace_debug = 0;
TUNABLE_INT("debug.dtrace.debug", &dtrace_debug);
SYSCTL_INT(_debug_dtrace, OID_AUTO, debug, CTLFLAG_RW, &dtrace_debug, 0, "");
/* Report registered DTrace providers. */
static int
sysctl_dtrace_providers(SYSCTL_HANDLER_ARGS)
{
char *p_name = NULL;
dtrace_provider_t
*prov = dtrace_provider;
int error = 0;
size_t len = 0;
mutex_enter(&dtrace_provider_lock);
mutex_enter(&dtrace_lock);
/* Compute the length of the space-separated provider name string. */
while (prov != NULL) {
len += strlen(prov->dtpv_name) + 1;
prov = prov->dtpv_next;
}
if ((p_name = kmem_alloc(len, KM_SLEEP)) == NULL)
error = ENOMEM;
else {
/* Start with an empty string. */
*p_name = '\0';
/* Point to the first provider again. */
prov = dtrace_provider;
/* Loop through the providers, appending the names. */
while (prov != NULL) {
if (prov != dtrace_provider)
(void) strlcat(p_name, " ", len);
(void) strlcat(p_name, prov->dtpv_name, len);
prov = prov->dtpv_next;
}
}
mutex_exit(&dtrace_lock);
mutex_exit(&dtrace_provider_lock);
if (p_name != NULL) {
error = sysctl_handle_string(oidp, p_name, len, req);
kmem_free(p_name, 0);
}
return (error);
}
SYSCTL_PROC(_debug_dtrace, OID_AUTO, providers, CTLTYPE_STRING | CTLFLAG_RD,
0, 0, sysctl_dtrace_providers, "A", "");

View file

@ -0,0 +1,73 @@
/*-
* Copyright 2008 John Birrell <jb@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*
*/
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/vnode.h>
/*
* These are variables that the DTrace test suite references in the
* Solaris kernel. We define them here so that the tests function
* unaltered.
*/
int kmem_flags;
typedef struct vnode vnode_t;
vnode_t dummy;
vnode_t *rootvp = &dummy;
static int
dtrace_test_modevent(module_t mod, int type, void *data)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
DEV_MODULE(dtrace_test, dtrace_test_modevent, NULL);
MODULE_VERSION(dtrace_test, 1);
MODULE_DEPEND(dtrace_test, dtraceall, 1, 1, 1);

View file

@ -0,0 +1,138 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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$
*
*/
static int
dtrace_unload()
{
dtrace_state_t *state;
int error = 0;
/*
* Check if there is still an event handler callback
* registered.
*/
if (eh_tag != 0) {
/* De-register the device cloning event handler. */
EVENTHANDLER_DEREGISTER(dev_clone, eh_tag);
eh_tag = 0;
/* Stop device cloning. */
clone_cleanup(&dtrace_clones);
}
mutex_enter(&dtrace_provider_lock);
mutex_enter(&dtrace_lock);
mutex_enter(&cpu_lock);
ASSERT(dtrace_opens == 0);
if (dtrace_helpers > 0) {
mutex_exit(&cpu_lock);
mutex_exit(&dtrace_lock);
mutex_exit(&dtrace_provider_lock);
return (EBUSY);
}
if (dtrace_unregister((dtrace_provider_id_t)dtrace_provider) != 0) {
mutex_exit(&cpu_lock);
mutex_exit(&dtrace_lock);
mutex_exit(&dtrace_provider_lock);
return (EBUSY);
}
dtrace_provider = NULL;
if ((state = dtrace_anon_grab()) != NULL) {
/*
* If there were ECBs on this state, the provider should
* have not been allowed to detach; assert that there is
* none.
*/
ASSERT(state->dts_necbs == 0);
dtrace_state_destroy(state);
}
bzero(&dtrace_anon, sizeof (dtrace_anon_t));
mutex_exit(&cpu_lock);
if (dtrace_helptrace_enabled) {
kmem_free(dtrace_helptrace_buffer, 0);
dtrace_helptrace_buffer = NULL;
}
if (dtrace_probes != NULL) {
kmem_free(dtrace_probes, 0);
dtrace_probes = NULL;
dtrace_nprobes = 0;
}
dtrace_hash_destroy(dtrace_bymod);
dtrace_hash_destroy(dtrace_byfunc);
dtrace_hash_destroy(dtrace_byname);
dtrace_bymod = NULL;
dtrace_byfunc = NULL;
dtrace_byname = NULL;
kmem_cache_destroy(dtrace_state_cache);
delete_unrhdr(dtrace_arena);
if (dtrace_toxrange != NULL) {
kmem_free(dtrace_toxrange, 0);
dtrace_toxrange = NULL;
dtrace_toxranges = 0;
dtrace_toxranges_max = 0;
}
ASSERT(dtrace_vtime_references == 0);
ASSERT(dtrace_opens == 0);
ASSERT(dtrace_retained == NULL);
mutex_exit(&dtrace_lock);
mutex_exit(&dtrace_provider_lock);
mutex_destroy(&dtrace_meta_lock);
mutex_destroy(&dtrace_provider_lock);
mutex_destroy(&dtrace_lock);
mutex_destroy(&dtrace_errlock);
/* XXX Hack */
mutex_destroy(&mod_lock);
/* Reset our hook for exceptions. */
dtrace_invop_uninit();
/*
* Reset our hook for thread switches, but ensure that vtime isn't
* active first.
*/
dtrace_vtime_active = 0;
dtrace_vtime_switch_func = NULL;
/* Unhook from the trap handler. */
dtrace_trap_func = NULL;
return (error);
}

View file

@ -0,0 +1,101 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
void
dtrace_vtime_enable(void)
{
dtrace_vtime_state_t state, nstate = 0;
do {
state = dtrace_vtime_active;
switch (state) {
case DTRACE_VTIME_INACTIVE:
nstate = DTRACE_VTIME_ACTIVE;
break;
case DTRACE_VTIME_INACTIVE_TNF:
nstate = DTRACE_VTIME_ACTIVE_TNF;
break;
case DTRACE_VTIME_ACTIVE:
case DTRACE_VTIME_ACTIVE_TNF:
panic("DTrace virtual time already enabled");
/*NOTREACHED*/
}
} while (dtrace_cas32((uint32_t *)&dtrace_vtime_active,
state, nstate) != state);
}
void
dtrace_vtime_disable(void)
{
dtrace_vtime_state_t state, nstate = 0;
do {
state = dtrace_vtime_active;
switch (state) {
case DTRACE_VTIME_ACTIVE:
nstate = DTRACE_VTIME_INACTIVE;
break;
case DTRACE_VTIME_ACTIVE_TNF:
nstate = DTRACE_VTIME_INACTIVE_TNF;
break;
case DTRACE_VTIME_INACTIVE:
case DTRACE_VTIME_INACTIVE_TNF:
panic("DTrace virtual time already disabled");
/*NOTREACHED*/
}
} while (dtrace_cas32((uint32_t *)&dtrace_vtime_active,
state, nstate) != state);
}
void
dtrace_vtime_switch(kthread_t *next)
{
dtrace_icookie_t cookie;
hrtime_t ts;
cookie = dtrace_interrupt_disable();
ts = dtrace_gethrtime();
if (curthread->t_dtrace_start != 0) {
curthread->t_dtrace_vtime += ts - curthread->t_dtrace_start;
curthread->t_dtrace_start = 0;
}
if (next != NULL)
next->t_dtrace_start = ts;
dtrace_interrupt_enable(cookie);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#ifndef _DIS_TABLES_H
#define _DIS_TABLES_H
#if defined(sun)
#pragma ident "@(#)dis_tables.h 1.7 06/03/02 SMI"
#endif
/*
* Constants and prototypes for the IA32 disassembler backend. See dis_tables.c
* for usage information and documentation.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
#include <sys/param.h>
/*
* values for cpu mode
*/
#define SIZE16 1
#define SIZE32 2
#define SIZE64 3
#define OPLEN 256
#define PFIXLEN 8
#define NCPS 12 /* number of chars per symbol */
/*
* data structures that must be provided to dtrace_dis86()
*/
typedef struct d86opnd {
char d86_opnd[OPLEN]; /* symbolic rep of operand */
char d86_prefix[PFIXLEN]; /* any prefix string or "" */
uint_t d86_mode; /* mode for immediate */
uint_t d86_value_size; /* size in bytes of d86_value */
uint64_t d86_value; /* immediate value of opnd */
} d86opnd_t;
typedef struct dis86 {
uint_t d86_mode;
uint_t d86_error;
uint_t d86_len; /* instruction length */
int d86_rmindex; /* index of modrm byte or -1 */
uint_t d86_memsize; /* size of memory referenced */
char d86_bytes[16]; /* bytes of instruction */
char d86_mneu[OPLEN];
uint_t d86_numopnds;
uint_t d86_rex_prefix; /* value of REX prefix if !0 */
char *d86_seg_prefix; /* segment prefix, if any */
uint_t d86_opnd_size;
uint_t d86_addr_size;
uint_t d86_got_modrm;
struct d86opnd d86_opnd[3]; /* up to 3 operands */
int (*d86_check_func)(void *);
int (*d86_get_byte)(void *);
#ifdef DIS_TEXT
int (*d86_sym_lookup)(void *, uint64_t, char *, size_t);
int (*d86_sprintf_func)(char *, size_t, const char *, ...);
int d86_flags;
uint_t d86_imm_bytes;
#endif
void *d86_data;
} dis86_t;
extern int dtrace_disx86(dis86_t *x, uint_t cpu_mode);
#define DIS_OP_OCTAL 0x1 /* Print all numbers in octal */
#ifdef DIS_TEXT
extern void dtrace_disx86_str(dis86_t *x, uint_t cpu_mode, uintptr_t pc,
char *buf, size_t len);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _DIS_TABLES_H */

View file

@ -0,0 +1,527 @@
/*
* 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.
*/
#define _ASM
#include <machine/asmacros.h>
#include <sys/cpuvar_defs.h>
#include <sys/dtrace.h>
#include "assym.s"
.globl calltrap
.type calltrap,@function
ENTRY(dtrace_invop_start)
pushl %eax /* push %eax -- may be return value */
pushl %esp /* push stack pointer */
addl $48, (%esp) /* adjust to incoming args */
pushl 40(%esp) /* push calling EIP */
/*
* Call dtrace_invop to let it check if the exception was
* a fbt one. The return value in %eax will tell us what
* dtrace_invop wants us to do.
*/
call dtrace_invop
/*
* We pushed 3 times for the arguments to dtrace_invop,
* so we need to increment the stack pointer to get rid of
* those values.
*/
addl $12, %esp
ALTENTRY(dtrace_invop_callsite)
cmpl $DTRACE_INVOP_PUSHL_EBP, %eax
je invop_push
cmpl $DTRACE_INVOP_POPL_EBP, %eax
je invop_pop
cmpl $DTRACE_INVOP_LEAVE, %eax
je invop_leave
cmpl $DTRACE_INVOP_NOP, %eax
je invop_nop
/* When all else fails handle the trap in the usual way. */
jmpl *dtrace_invop_calltrap_addr
invop_push:
/*
* We must emulate a "pushl %ebp". To do this, we pull the stack
* down 4 bytes, and then store the base pointer.
*/
popal
subl $4, %esp /* make room for %ebp */
pushl %eax /* push temp */
movl 8(%esp), %eax /* load calling EIP */
incl %eax /* increment over LOCK prefix */
movl %eax, 4(%esp) /* store calling EIP */
movl 12(%esp), %eax /* load calling CS */
movl %eax, 8(%esp) /* store calling CS */
movl 16(%esp), %eax /* load calling EFLAGS */
movl %eax, 12(%esp) /* store calling EFLAGS */
movl %ebp, 16(%esp) /* push %ebp */
popl %eax /* pop off temp */
iret /* Return from interrupt. */
invop_pop:
/*
* We must emulate a "popl %ebp". To do this, we do the opposite of
* the above: we remove the %ebp from the stack, and squeeze up the
* saved state from the trap.
*/
popal
pushl %eax /* push temp */
movl 16(%esp), %ebp /* pop %ebp */
movl 12(%esp), %eax /* load calling EFLAGS */
movl %eax, 16(%esp) /* store calling EFLAGS */
movl 8(%esp), %eax /* load calling CS */
movl %eax, 12(%esp) /* store calling CS */
movl 4(%esp), %eax /* load calling EIP */
incl %eax /* increment over LOCK prefix */
movl %eax, 8(%esp) /* store calling EIP */
popl %eax /* pop off temp */
addl $4, %esp /* adjust stack pointer */
iret /* Return from interrupt. */
invop_leave:
/*
* We must emulate a "leave", which is the same as a "movl %ebp, %esp"
* followed by a "popl %ebp". This looks similar to the above, but
* requires two temporaries: one for the new base pointer, and one
* for the staging register.
*/
popa
pushl %eax /* push temp */
pushl %ebx /* push temp */
movl %ebp, %ebx /* set temp to old %ebp */
movl (%ebx), %ebp /* pop %ebp */
movl 16(%esp), %eax /* load calling EFLAGS */
movl %eax, (%ebx) /* store calling EFLAGS */
movl 12(%esp), %eax /* load calling CS */
movl %eax, -4(%ebx) /* store calling CS */
movl 8(%esp), %eax /* load calling EIP */
incl %eax /* increment over LOCK prefix */
movl %eax, -8(%ebx) /* store calling EIP */
movl %ebx, -4(%esp) /* temporarily store new %esp */
popl %ebx /* pop off temp */
popl %eax /* pop off temp */
movl -12(%esp), %esp /* set stack pointer */
subl $8, %esp /* adjust for three pushes, one pop */
iret /* return from interrupt */
invop_nop:
/*
* We must emulate a "nop". This is obviously not hard: we need only
* advance the %eip by one.
*/
popa
incl (%esp)
iret /* return from interrupt */
END(dtrace_invop_start)
/*
void dtrace_invop_init(void)
*/
ENTRY(dtrace_invop_init)
movl $dtrace_invop_start, dtrace_invop_jump_addr
ret
END(dtrace_invop_init)
/*
void dtrace_invop_uninit(void)
*/
ENTRY(dtrace_invop_uninit)
movl $0, dtrace_invop_jump_addr
ret
END(dtrace_invop_uninit)
/*
greg_t dtrace_getfp(void)
*/
ENTRY(dtrace_getfp)
movl %ebp, %eax
ret
END(dtrace_getfp)
/*
uint32_t dtrace_cas32(uint32_t *target, uint32_t cmp, uint32_t new)
*/
ENTRY(dtrace_cas32)
ALTENTRY(dtrace_casptr)
movl 4(%esp), %edx
movl 8(%esp), %eax
movl 12(%esp), %ecx
lock
cmpxchgl %ecx, (%edx)
ret
END(dtrace_casptr)
END(dtrace_cas32)
/*
uintptr_t dtrace_caller(int aframes)
*/
ENTRY(dtrace_caller)
movl $-1, %eax
ret
END(dtrace_caller)
/*
void dtrace_copy(uintptr_t src, uintptr_t dest, size_t size)
*/
ENTRY(dtrace_copy)
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %edi
movl 8(%ebp), %esi /* Load source address */
movl 12(%ebp), %edi /* Load destination address */
movl 16(%ebp), %ecx /* Load count */
repz /* Repeat for count... */
smovb /* move from %ds:si to %es:di */
popl %edi
popl %esi
movl %ebp, %esp
popl %ebp
ret
END(dtrace_copy)
/*
void dtrace_copystr(uintptr_t uaddr, uintptr_t kaddr, size_t size)
*/
ENTRY(dtrace_copystr)
pushl %ebp /* Setup stack frame */
movl %esp, %ebp
pushl %ebx /* Save registers */
movl 8(%ebp), %ebx /* Load source address */
movl 12(%ebp), %edx /* Load destination address */
movl 16(%ebp), %ecx /* Load count */
0:
movb (%ebx), %al /* Load from source */
movb %al, (%edx) /* Store to destination */
incl %ebx /* Increment source pointer */
incl %edx /* Increment destination pointer */
decl %ecx /* Decrement remaining count */
cmpb $0, %al
je 1f
cmpl $0, %ecx
jne 0b
1:
popl %ebx
movl %ebp, %esp
popl %ebp
ret
END(dtrace_copystr)
/*
uintptr_t dtrace_fulword(void *addr)
*/
ENTRY(dtrace_fulword)
movl 4(%esp), %ecx
xorl %eax, %eax
movl (%ecx), %eax
ret
END(dtrace_fulword)
/*
uint8_t dtrace_fuword8_nocheck(void *addr)
*/
ENTRY(dtrace_fuword8_nocheck)
movl 4(%esp), %ecx
xorl %eax, %eax
movzbl (%ecx), %eax
ret
END(dtrace_fuword8_nocheck)
/*
uint16_t dtrace_fuword16_nocheck(void *addr)
*/
ENTRY(dtrace_fuword16_nocheck)
movl 4(%esp), %ecx
xorl %eax, %eax
movzwl (%ecx), %eax
ret
END(dtrace_fuword16_nocheck)
/*
uint32_t dtrace_fuword32_nocheck(void *addr)
*/
ENTRY(dtrace_fuword32_nocheck)
movl 4(%esp), %ecx
xorl %eax, %eax
movl (%ecx), %eax
ret
END(dtrace_fuword32_nocheck)
/*
uint64_t dtrace_fuword64_nocheck(void *addr)
*/
ENTRY(dtrace_fuword64_nocheck)
movl 4(%esp), %ecx
xorl %eax, %eax
xorl %edx, %edx
movl (%ecx), %eax
movl 4(%ecx), %edx
ret
END(dtrace_fuword64_nocheck)
/*
void dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which, int fault, int fltoffs, uintptr_t illval)
*/
ENTRY(dtrace_probe_error)
pushl %ebp
movl %esp, %ebp
pushl 0x1c(%ebp)
pushl 0x18(%ebp)
pushl 0x14(%ebp)
pushl 0x10(%ebp)
pushl 0xc(%ebp)
pushl 0x8(%ebp)
pushl dtrace_probeid_error
call dtrace_probe
movl %ebp, %esp
popl %ebp
ret
END(dtrace_probe_error)
/*
void dtrace_membar_producer(void)
*/
ENTRY(dtrace_membar_producer)
rep; ret /* use 2 byte return instruction when branch target */
/* AMD Software Optimization Guide - Section 6.2 */
END(dtrace_membar_producer)
/*
void dtrace_membar_consumer(void)
*/
ENTRY(dtrace_membar_consumer)
rep; ret /* use 2 byte return instruction when branch target */
/* AMD Software Optimization Guide - Section 6.2 */
END(dtrace_membar_consumer)
/*
dtrace_icookie_t dtrace_interrupt_disable(void)
*/
ENTRY(dtrace_interrupt_disable)
pushfl
popl %eax
cli
ret
END(dtrace_interrupt_disable)
/*
void dtrace_interrupt_enable(dtrace_icookie_t cookie)
*/
ENTRY(dtrace_interrupt_enable)
movl 4(%esp), %eax
pushl %eax
popfl
ret
END(dtrace_interrupt_enable)
/*
* The panic() and cmn_err() functions invoke vpanic() as a common entry point
* into the panic code implemented in panicsys(). vpanic() is responsible
* for passing through the format string and arguments, and constructing a
* regs structure on the stack into which it saves the current register
* values. If we are not dying due to a fatal trap, these registers will
* then be preserved in panicbuf as the current processor state. Before
* invoking panicsys(), vpanic() activates the first panic trigger (see
* common/os/panic.c) and switches to the panic_stack if successful. Note that
* DTrace takes a slightly different panic path if it must panic from probe
* context. Instead of calling panic, it calls into dtrace_vpanic(), which
* sets up the initial stack as vpanic does, calls dtrace_panic_trigger(), and
* branches back into vpanic().
*/
/*
void vpanic(const char *format, va_list alist)
*/
ENTRY(vpanic) /* Initial stack layout: */
pushl %ebp /* | %eip | 20 */
movl %esp, %ebp /* | %ebp | 16 */
pushl %eax /* | %eax | 12 */
pushl %ebx /* | %ebx | 8 */
pushl %ecx /* | %ecx | 4 */
pushl %edx /* | %edx | 0 */
movl %esp, %ebx /* %ebx = current stack pointer */
lea panic_quiesce, %eax /* %eax = &panic_quiesce */
pushl %eax /* push &panic_quiesce */
call panic_trigger /* %eax = panic_trigger() */
addl $4, %esp /* reset stack pointer */
vpanic_common:
cmpl $0, %eax /* if (%eax == 0) */
je 0f /* goto 0f; */
/*
* If panic_trigger() was successful, we are the first to initiate a
* panic: we now switch to the reserved panic_stack before continuing.
*/
lea panic_stack, %esp /* %esp = panic_stack */
addl $PANICSTKSIZE, %esp /* %esp += PANICSTKSIZE */
0: subl $REGSIZE, %esp /* allocate struct regs */
/*
* Now that we've got everything set up, store the register values as
* they were when we entered vpanic() to the designated location in
* the regs structure we allocated on the stack.
*/
#ifdef notyet
mov %gs, %edx
mov %edx, REGOFF_GS(%esp)
mov %fs, %edx
mov %edx, REGOFF_FS(%esp)
mov %es, %edx
mov %edx, REGOFF_ES(%esp)
mov %ds, %edx
mov %edx, REGOFF_DS(%esp)
movl %edi, REGOFF_EDI(%esp)
movl %esi, REGOFF_ESI(%esp)
movl 16(%ebx), %ecx
movl %ecx, REGOFF_EBP(%esp)
movl %ebx, %ecx
addl $20, %ecx
movl %ecx, REGOFF_ESP(%esp)
movl 8(%ebx), %ecx
movl %ecx, REGOFF_EBX(%esp)
movl 0(%ebx), %ecx
movl %ecx, REGOFF_EDX(%esp)
movl 4(%ebx), %ecx
movl %ecx, REGOFF_ECX(%esp)
movl 12(%ebx), %ecx
movl %ecx, REGOFF_EAX(%esp)
movl $0, REGOFF_TRAPNO(%esp)
movl $0, REGOFF_ERR(%esp)
lea vpanic, %ecx
movl %ecx, REGOFF_EIP(%esp)
mov %cs, %edx
movl %edx, REGOFF_CS(%esp)
pushfl
popl %ecx
movl %ecx, REGOFF_EFL(%esp)
movl $0, REGOFF_UESP(%esp)
mov %ss, %edx
movl %edx, REGOFF_SS(%esp)
movl %esp, %ecx /* %ecx = &regs */
pushl %eax /* push on_panic_stack */
pushl %ecx /* push &regs */
movl 12(%ebp), %ecx /* %ecx = alist */
pushl %ecx /* push alist */
movl 8(%ebp), %ecx /* %ecx = format */
pushl %ecx /* push format */
call panicsys /* panicsys(); */
addl $16, %esp /* pop arguments */
addl $REGSIZE, %esp
#endif
popl %edx
popl %ecx
popl %ebx
popl %eax
leave
ret
END(vpanic)
/*
void dtrace_vpanic(const char *format, va_list alist)
*/
ENTRY(dtrace_vpanic) /* Initial stack layout: */
pushl %ebp /* | %eip | 20 */
movl %esp, %ebp /* | %ebp | 16 */
pushl %eax /* | %eax | 12 */
pushl %ebx /* | %ebx | 8 */
pushl %ecx /* | %ecx | 4 */
pushl %edx /* | %edx | 0 */
movl %esp, %ebx /* %ebx = current stack pointer */
lea panic_quiesce, %eax /* %eax = &panic_quiesce */
pushl %eax /* push &panic_quiesce */
call dtrace_panic_trigger /* %eax = dtrace_panic_trigger() */
addl $4, %esp /* reset stack pointer */
jmp vpanic_common /* jump back to common code */
END(dtrace_vpanic)
/*
int
panic_trigger(int *tp)
*/
ENTRY(panic_trigger)
xorl %eax, %eax
movl $0xdefacedd, %edx
lock
xchgl %edx, (%edi)
cmpl $0, %edx
je 0f
movl $0, %eax
ret
0: movl $1, %eax
ret
END(panic_trigger)
/*
int
dtrace_panic_trigger(int *tp)
*/
ENTRY(dtrace_panic_trigger)
xorl %eax, %eax
movl $0xdefacedd, %edx
lock
xchgl %edx, (%edi)
cmpl $0, %edx
je 0f
movl $0, %eax
ret
0: movl $1, %eax
ret
END(dtrace_panic_trigger)

View file

@ -0,0 +1,622 @@
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/stack.h>
#include <sys/pcpu.h>
#include <machine/md_var.h>
#include <machine/stack.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
extern uintptr_t kernbase;
uintptr_t kernelbase = (uintptr_t) &kernbase;
#define INKERNEL(va) (((vm_offset_t)(va)) >= USRSTACK && \
((vm_offset_t)(va)) < VM_MAX_KERNEL_ADDRESS)
uint8_t dtrace_fuword8_nocheck(void *);
uint16_t dtrace_fuword16_nocheck(void *);
uint32_t dtrace_fuword32_nocheck(void *);
uint64_t dtrace_fuword64_nocheck(void *);
void
dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
uint32_t *intrpc)
{
int depth = 0;
register_t ebp;
struct i386_frame *frame;
vm_offset_t callpc;
pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
if (intrpc != 0)
pcstack[depth++] = (pc_t) intrpc;
aframes++;
__asm __volatile("movl %%ebp,%0" : "=r" (ebp));
frame = (struct i386_frame *)ebp;
while (depth < pcstack_limit) {
if (!INKERNEL(frame))
break;
callpc = frame->f_retaddr;
if (!INKERNEL(callpc))
break;
if (aframes > 0) {
aframes--;
if ((aframes == 0) && (caller != 0)) {
pcstack[depth++] = caller;
}
}
else {
pcstack[depth++] = callpc;
}
if (frame->f_frame <= frame ||
(vm_offset_t)frame->f_frame >=
(vm_offset_t)ebp + KSTACK_PAGES * PAGE_SIZE)
break;
frame = frame->f_frame;
}
for (; depth < pcstack_limit; depth++) {
pcstack[depth] = 0;
}
}
#ifdef notyet
static int
dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
uintptr_t sp)
{
klwp_t *lwp = ttolwp(curthread);
proc_t *p = curproc;
uintptr_t oldcontext = lwp->lwp_oldcontext;
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);
if (p->p_model == DATAMODEL_NATIVE) {
s1 = sizeof (struct frame) + 2 * sizeof (long);
s2 = s1 + sizeof (siginfo_t);
} else {
s1 = sizeof (struct frame32) + 3 * sizeof (int);
s2 = s1 + sizeof (siginfo32_t);
}
while (pc != 0 && sp != 0) {
ret++;
if (pcstack != NULL) {
*pcstack++ = (uint64_t)pc;
pcstack_limit--;
if (pcstack_limit <= 0)
break;
}
if (oldcontext == sp + s1 || oldcontext == sp + s2) {
if (p->p_model == DATAMODEL_NATIVE) {
ucontext_t *ucp = (ucontext_t *)oldcontext;
greg_t *gregs = ucp->uc_mcontext.gregs;
sp = dtrace_fulword(&gregs[REG_FP]);
pc = dtrace_fulword(&gregs[REG_PC]);
oldcontext = dtrace_fulword(&ucp->uc_link);
} else {
ucontext32_t *ucp = (ucontext32_t *)oldcontext;
greg32_t *gregs = ucp->uc_mcontext.gregs;
sp = dtrace_fuword32(&gregs[EBP]);
pc = dtrace_fuword32(&gregs[EIP]);
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);
}
}
/*
* This is totally bogus: if we faulted, we're going to clear
* the fault and break. This is to deal with the apparently
* broken Java stacks on x86.
*/
if (*flags & CPU_DTRACE_FAULT) {
*flags &= ~CPU_DTRACE_FAULT;
break;
}
}
return (ret);
}
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;
volatile uint16_t *flags =
(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
int n;
if (*flags & CPU_DTRACE_FAULT)
return;
if (pcstack_limit <= 0)
return;
/*
* If there's no user context we still need to zero the stack.
*/
if (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL)
goto zero;
*pcstack++ = (uint64_t)p->p_pid;
pcstack_limit--;
if (pcstack_limit <= 0)
return;
pc = rp->r_pc;
sp = rp->r_fp;
if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
*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);
}
n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);
ASSERT(n >= 0);
ASSERT(n <= pcstack_limit);
pcstack += n;
pcstack_limit -= n;
zero:
while (pcstack_limit-- > 0)
*pcstack++ = NULL;
}
int
dtrace_getustackdepth(void)
{
}
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;
volatile uint16_t *flags =
(volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
size_t s1, s2;
if (*flags & CPU_DTRACE_FAULT)
return;
if (pcstack_limit <= 0)
return;
/*
* If there's no user context we still need to zero the stack.
*/
if (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL)
goto zero;
*pcstack++ = (uint64_t)p->p_pid;
pcstack_limit--;
if (pcstack_limit <= 0)
return;
pc = rp->r_pc;
sp = rp->r_fp;
oldcontext = lwp->lwp_oldcontext;
if (p->p_model == DATAMODEL_NATIVE) {
s1 = sizeof (struct frame) + 2 * sizeof (long);
s2 = s1 + sizeof (siginfo_t);
} else {
s1 = sizeof (struct frame32) + 3 * sizeof (int);
s2 = s1 + sizeof (siginfo32_t);
}
if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
*pcstack++ = (uint64_t)pc;
*fpstack++ = 0;
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);
}
while (pc != 0 && sp != 0) {
*pcstack++ = (uint64_t)pc;
*fpstack++ = sp;
pcstack_limit--;
if (pcstack_limit <= 0)
break;
if (oldcontext == sp + s1 || oldcontext == sp + s2) {
if (p->p_model == DATAMODEL_NATIVE) {
ucontext_t *ucp = (ucontext_t *)oldcontext;
greg_t *gregs = ucp->uc_mcontext.gregs;
sp = dtrace_fulword(&gregs[REG_FP]);
pc = dtrace_fulword(&gregs[REG_PC]);
oldcontext = dtrace_fulword(&ucp->uc_link);
} else {
ucontext_t *ucp = (ucontext_t *)oldcontext;
greg_t *gregs = ucp->uc_mcontext.gregs;
sp = dtrace_fuword32(&gregs[EBP]);
pc = dtrace_fuword32(&gregs[EIP]);
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);
}
}
/*
* This is totally bogus: if we faulted, we're going to clear
* the fault and break. This is to deal with the apparently
* broken Java stacks on x86.
*/
if (*flags & CPU_DTRACE_FAULT) {
*flags &= ~CPU_DTRACE_FAULT;
break;
}
}
zero:
while (pcstack_limit-- > 0)
*pcstack++ = NULL;
}
#endif
uint64_t
dtrace_getarg(int arg, int aframes)
{
uintptr_t val;
struct i386_frame *fp = (struct i386_frame *)dtrace_getfp();
uintptr_t *stack;
int i;
for (i = 1; i <= aframes; i++) {
fp = fp->f_frame;
if (fp->f_retaddr == (long)dtrace_invop_callsite) {
/*
* If we pass through the invalid op handler, we will
* use the pointer that it passed to the stack as the
* second argument to dtrace_invop() as the pointer to
* the stack. When using this stack, we must step
* beyond the EIP/RIP that was pushed when the trap was
* taken -- hence the "+ 1" below.
*/
stack = ((uintptr_t **)&fp[1])[1] + 1;
goto load;
}
}
/*
* We know that we did not come through a trap to get into
* dtrace_probe() -- the provider simply called dtrace_probe()
* directly. As this is the case, we need to shift the argument
* that we're looking for: the probe ID is the first argument to
* dtrace_probe(), so the argument n will actually be found where
* one would expect to find argument (n + 1).
*/
arg++;
stack = (uintptr_t *)&fp[1];
load:
DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
val = stack[arg];
DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
return (val);
}
int
dtrace_getstackdepth(int aframes)
{
int depth = 0;
struct i386_frame *frame;
vm_offset_t ebp;
aframes++;
ebp = dtrace_getfp();
frame = (struct i386_frame *)ebp;
depth++;
for(;;) {
if (!INKERNEL((long) frame))
break;
if (!INKERNEL((long) frame->f_frame))
break;
depth++;
if (frame->f_frame <= frame ||
(vm_offset_t)frame->f_frame >=
(vm_offset_t)ebp + KSTACK_PAGES * PAGE_SIZE)
break;
frame = frame->f_frame;
}
if (depth < aframes)
return 0;
else
return depth - aframes;
}
#ifdef notyet
ulong_t
dtrace_getreg(struct regs *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 */
};
if (reg <= SS) {
if (reg >= sizeof (regmap) / sizeof (int)) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
return (0);
}
reg = regmap[reg];
} else {
reg -= SS + 1;
}
switch (reg) {
case REG_RDI:
return (rp->r_rdi);
case REG_RSI:
return (rp->r_rsi);
case REG_RDX:
return (rp->r_rdx);
case REG_RCX:
return (rp->r_rcx);
case REG_R8:
return (rp->r_r8);
case REG_R9:
return (rp->r_r9);
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);
case REG_TRAPNO:
return (rp->r_trapno);
case REG_ERR:
return (rp->r_err);
case REG_RIP:
return (rp->r_rip);
case REG_CS:
return (rp->r_cs);
case REG_SS:
return (rp->r_ss);
case REG_RFL:
return (rp->r_rfl);
case REG_RSP:
return (rp->r_rsp);
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)
{
ASSERT(kaddr >= kernelbase && kaddr + size >= kaddr);
if (uaddr + size >= kernelbase || uaddr + size < uaddr) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
return (0);
}
return (1);
}
void
dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
volatile uint16_t *flags)
{
if (dtrace_copycheck(uaddr, kaddr, size))
dtrace_copy(uaddr, kaddr, size);
}
void
dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
volatile uint16_t *flags)
{
if (dtrace_copycheck(uaddr, kaddr, size))
dtrace_copy(kaddr, uaddr, size);
}
void
dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
volatile uint16_t *flags)
{
if (dtrace_copycheck(uaddr, kaddr, size))
dtrace_copystr(uaddr, kaddr, size, flags);
}
void
dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
volatile uint16_t *flags)
{
if (dtrace_copycheck(uaddr, kaddr, size))
dtrace_copystr(kaddr, uaddr, size, flags);
}
uint8_t
dtrace_fuword8(void *uaddr)
{
if ((uintptr_t)uaddr >= kernelbase) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
return (0);
}
return (dtrace_fuword8_nocheck(uaddr));
}
uint16_t
dtrace_fuword16(void *uaddr)
{
if ((uintptr_t)uaddr >= kernelbase) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
return (0);
}
return (dtrace_fuword16_nocheck(uaddr));
}
uint32_t
dtrace_fuword32(void *uaddr)
{
if ((uintptr_t)uaddr >= kernelbase) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
return (0);
}
return (dtrace_fuword32_nocheck(uaddr));
}
uint64_t
dtrace_fuword64(void *uaddr)
{
if ((uintptr_t)uaddr >= kernelbase) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
return (0);
}
return (dtrace_fuword64_nocheck(uaddr));
}

View file

@ -0,0 +1,503 @@
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/kmem.h>
#include <sys/smp.h>
#include <sys/dtrace_impl.h>
#include <sys/dtrace_bsd.h>
#include <machine/clock.h>
#include <machine/frame.h>
#include <vm/pmap.h>
extern uintptr_t kernelbase;
extern uintptr_t dtrace_in_probe_addr;
extern int dtrace_in_probe;
int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
typedef struct dtrace_invop_hdlr {
int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t);
struct dtrace_invop_hdlr *dtih_next;
} dtrace_invop_hdlr_t;
dtrace_invop_hdlr_t *dtrace_invop_hdlr;
int
dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax)
{
dtrace_invop_hdlr_t *hdlr;
int rval;
for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0)
return (rval);
return (0);
}
void
dtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
{
dtrace_invop_hdlr_t *hdlr;
hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
hdlr->dtih_func = func;
hdlr->dtih_next = dtrace_invop_hdlr;
dtrace_invop_hdlr = hdlr;
}
void
dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
{
dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
for (;;) {
if (hdlr == NULL)
panic("attempt to remove non-existent invop handler");
if (hdlr->dtih_func == func)
break;
prev = hdlr;
hdlr = hdlr->dtih_next;
}
if (prev == NULL) {
ASSERT(dtrace_invop_hdlr == hdlr);
dtrace_invop_hdlr = hdlr->dtih_next;
} else {
ASSERT(dtrace_invop_hdlr != hdlr);
prev->dtih_next = hdlr->dtih_next;
}
kmem_free(hdlr, 0);
}
void
dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
{
(*func)(0, kernelbase);
}
void
dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
{
cpumask_t cpus;
critical_enter();
if (cpu == DTRACE_CPUALL)
cpus = all_cpus;
else
cpus = (cpumask_t) (1 << cpu);
/* If the current CPU is in the set, call the function directly: */
if ((cpus & (1 << curcpu)) != 0) {
(*func)(arg);
/* Mask the current CPU from the set */
cpus &= ~(1 << curcpu);
}
/* If there are any CPUs in the set, cross-call to those CPUs */
if (cpus != 0)
smp_rendezvous_cpus(cpus, NULL, func, smp_no_rendevous_barrier, arg);
critical_exit();
}
static void
dtrace_sync_func(void)
{
}
void
dtrace_sync(void)
{
dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
}
#ifdef notyet
int (*dtrace_fasttrap_probe_ptr)(struct regs *);
int (*dtrace_pid_probe_ptr)(struct regs *);
int (*dtrace_return_probe_ptr)(struct regs *);
void
dtrace_user_probe(struct regs *rp, caddr_t addr, processorid_t cpuid)
{
krwlock_t *rwp;
proc_t *p = curproc;
extern void trap(struct regs *, caddr_t, processorid_t);
if (USERMODE(rp->r_cs) || (rp->r_ps & PS_VM)) {
if (curthread->t_cred != p->p_cred) {
cred_t *oldcred = curthread->t_cred;
/*
* DTrace accesses t_cred in probe context. t_cred
* must always be either NULL, or point to a valid,
* allocated cred structure.
*/
curthread->t_cred = crgetcred();
crfree(oldcred);
}
}
if (rp->r_trapno == T_DTRACE_RET) {
uint8_t step = curthread->t_dtrace_step;
uint8_t ret = curthread->t_dtrace_ret;
uintptr_t npc = curthread->t_dtrace_npc;
if (curthread->t_dtrace_ast) {
aston(curthread);
curthread->t_sig_check = 1;
}
/*
* Clear all user tracing flags.
*/
curthread->t_dtrace_ft = 0;
/*
* If we weren't expecting to take a return probe trap, kill
* the process as though it had just executed an unassigned
* trap instruction.
*/
if (step == 0) {
tsignal(curthread, SIGILL);
return;
}
/*
* If we hit this trap unrelated to a return probe, we're
* just here to reset the AST flag since we deferred a signal
* until after we logically single-stepped the instruction we
* copied out.
*/
if (ret == 0) {
rp->r_pc = npc;
return;
}
/*
* We need to wait until after we've called the
* dtrace_return_probe_ptr function pointer to set %pc.
*/
rwp = &CPU->cpu_ft_lock;
rw_enter(rwp, RW_READER);
if (dtrace_return_probe_ptr != NULL)
(void) (*dtrace_return_probe_ptr)(rp);
rw_exit(rwp);
rp->r_pc = npc;
} else if (rp->r_trapno == T_DTRACE_PROBE) {
rwp = &CPU->cpu_ft_lock;
rw_enter(rwp, RW_READER);
if (dtrace_fasttrap_probe_ptr != NULL)
(void) (*dtrace_fasttrap_probe_ptr)(rp);
rw_exit(rwp);
} else if (rp->r_trapno == T_BPTFLT) {
uint8_t instr;
rwp = &CPU->cpu_ft_lock;
/*
* The DTrace fasttrap provider uses the breakpoint trap
* (int 3). We let DTrace take the first crack at handling
* this trap; if it's not a probe that DTrace knowns about,
* we call into the trap() routine to handle it like a
* breakpoint placed by a conventional debugger.
*/
rw_enter(rwp, RW_READER);
if (dtrace_pid_probe_ptr != NULL &&
(*dtrace_pid_probe_ptr)(rp) == 0) {
rw_exit(rwp);
return;
}
rw_exit(rwp);
/*
* If the instruction that caused the breakpoint trap doesn't
* look like an int 3 anymore, it may be that this tracepoint
* was removed just after the user thread executed it. In
* that case, return to user land to retry the instuction.
*/
if (fuword8((void *)(rp->r_pc - 1), &instr) == 0 &&
instr != FASTTRAP_INSTR) {
rp->r_pc--;
return;
}
trap(rp, addr, cpuid);
} else {
trap(rp, addr, cpuid);
}
}
void
dtrace_safe_synchronous_signal(void)
{
kthread_t *t = curthread;
struct regs *rp = lwptoregs(ttolwp(t));
size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
ASSERT(t->t_dtrace_on);
/*
* If we're not in the range of scratch addresses, we're not actually
* tracing user instructions so turn off the flags. If the instruction
* we copied out caused a synchonous trap, reset the pc back to its
* original value and turn off the flags.
*/
if (rp->r_pc < t->t_dtrace_scrpc ||
rp->r_pc > t->t_dtrace_astpc + isz) {
t->t_dtrace_ft = 0;
} else if (rp->r_pc == t->t_dtrace_scrpc ||
rp->r_pc == t->t_dtrace_astpc) {
rp->r_pc = t->t_dtrace_pc;
t->t_dtrace_ft = 0;
}
}
int
dtrace_safe_defer_signal(void)
{
kthread_t *t = curthread;
struct regs *rp = lwptoregs(ttolwp(t));
size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
ASSERT(t->t_dtrace_on);
/*
* If we're not in the range of scratch addresses, we're not actually
* tracing user instructions so turn off the flags.
*/
if (rp->r_pc < t->t_dtrace_scrpc ||
rp->r_pc > t->t_dtrace_astpc + isz) {
t->t_dtrace_ft = 0;
return (0);
}
/*
* If we've executed the original instruction, but haven't performed
* the jmp back to t->t_dtrace_npc or the clean up of any registers
* used to emulate %rip-relative instructions in 64-bit mode, do that
* here and take the signal right away. We detect this condition by
* seeing if the program counter is the range [scrpc + isz, astpc).
*/
if (t->t_dtrace_astpc - rp->r_pc <
t->t_dtrace_astpc - t->t_dtrace_scrpc - isz) {
#ifdef __amd64
/*
* If there is a scratch register and we're on the
* instruction immediately after the modified instruction,
* restore the value of that scratch register.
*/
if (t->t_dtrace_reg != 0 &&
rp->r_pc == t->t_dtrace_scrpc + isz) {
switch (t->t_dtrace_reg) {
case REG_RAX:
rp->r_rax = t->t_dtrace_regv;
break;
case REG_RCX:
rp->r_rcx = t->t_dtrace_regv;
break;
case REG_R8:
rp->r_r8 = t->t_dtrace_regv;
break;
case REG_R9:
rp->r_r9 = t->t_dtrace_regv;
break;
}
}
#endif
rp->r_pc = t->t_dtrace_npc;
t->t_dtrace_ft = 0;
return (0);
}
/*
* Otherwise, make sure we'll return to the kernel after executing
* the copied out instruction and defer the signal.
*/
if (!t->t_dtrace_step) {
ASSERT(rp->r_pc < t->t_dtrace_astpc);
rp->r_pc += t->t_dtrace_astpc - t->t_dtrace_scrpc;
t->t_dtrace_step = 1;
}
t->t_dtrace_ast = 1;
return (1);
}
#endif
static int64_t tgt_cpu_tsc;
static int64_t hst_cpu_tsc;
static int64_t tsc_skew[MAXCPU];
static void
dtrace_gethrtime_init_sync(void *arg)
{
#ifdef CHECK_SYNC
/*
* Delay this function from returning on one
* of the CPUs to check that the synchronisation
* works.
*/
uintptr_t cpu = (uintptr_t) arg;
if (cpu == curcpu) {
int i;
for (i = 0; i < 1000000000; i++)
tgt_cpu_tsc = rdtsc();
tgt_cpu_tsc = 0;
}
#endif
}
static void
dtrace_gethrtime_init_cpu(void *arg)
{
uintptr_t cpu = (uintptr_t) arg;
if (cpu == curcpu)
tgt_cpu_tsc = rdtsc();
else
hst_cpu_tsc = rdtsc();
}
static void
dtrace_gethrtime_init(void *arg)
{
cpumask_t map;
int i;
struct pcpu *cp;
/* The current CPU is the reference one. */
tsc_skew[curcpu] = 0;
for (i = 0; i <= mp_maxid; i++) {
if (i == curcpu)
continue;
if ((cp = pcpu_find(i)) == NULL)
continue;
map = 0;
map |= (1 << curcpu);
map |= (1 << i);
smp_rendezvous_cpus(map, dtrace_gethrtime_init_sync,
dtrace_gethrtime_init_cpu,
smp_no_rendevous_barrier, (void *)(uintptr_t) i);
tsc_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;
}
}
SYSINIT(dtrace_gethrtime_init, SI_SUB_SMP, SI_ORDER_ANY, dtrace_gethrtime_init, NULL);
/*
* DTrace needs a high resolution time function which can
* be called from a probe context and guaranteed not to have
* instrumented with probes itself.
*
* Returns nanoseconds since boot.
*/
uint64_t
dtrace_gethrtime()
{
return ((rdtsc() + tsc_skew[curcpu]) * (int64_t) 1000000000 / tsc_freq);
}
uint64_t
dtrace_gethrestime(void)
{
printf("%s(%d): XXX\n",__func__,__LINE__);
return (0);
}
/* Function to handle DTrace traps during probes. See i386/i386/trap.c */
int
dtrace_trap(struct trapframe *frame, u_int type)
{
/*
* A trap can occur while DTrace executes a probe. Before
* executing the probe, DTrace blocks re-scheduling and sets
* a flag in it's per-cpu flags to indicate that it doesn't
* want to fault. On returning from the the probe, the no-fault
* flag is cleared and finally re-scheduling is enabled.
*
* Check if DTrace has enabled 'no-fault' mode:
*
*/
if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
/*
* There are only a couple of trap types that are expected.
* All the rest will be handled in the usual way.
*/
switch (type) {
/* General protection fault. */
case T_PROTFLT:
/* Flag an illegal operation. */
cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
/*
* Offset the instruction pointer to the instruction
* following the one causing the fault.
*/
frame->tf_eip += dtrace_instr_size((u_char *) frame->tf_eip);
return (1);
/* Page fault. */
case T_PAGEFLT:
/* Flag a bad address. */
cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
cpu_core[curcpu].cpuc_dtrace_illval = rcr2();
/*
* Offset the instruction pointer to the instruction
* following the one causing the fault.
*/
frame->tf_eip += dtrace_instr_size((u_char *) frame->tf_eip);
return (1);
default:
/* Handle all other traps in the usual way. */
break;
}
}
/* Handle the trap in the usual way. */
return (0);
}

View file

@ -0,0 +1,132 @@
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#if defined(sun)
#pragma ident "@(#)instr_size.c 1.14 05/07/08 SMI"
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#if defined(sun)
#include <sys/cmn_err.h>
#include <sys/archsystm.h>
#include <sys/copyops.h>
#include <vm/seg_enum.h>
#include <sys/privregs.h>
#else
typedef u_int model_t;
#define DATAMODEL_NATIVE 0
int dtrace_instr_size(uchar_t *);
#endif
#include <dis_tables.h>
/*
* This subsystem (with the minor exception of the instr_size() function) is
* is called from DTrace probe context. This imposes several requirements on
* the implementation:
*
* 1. External subsystems and functions may not be referenced. The one current
* exception is for cmn_err, but only to signal the detection of table
* errors. Assuming the tables are correct, no combination of input is to
* trigger a cmn_err call.
*
* 2. These functions can't be allowed to be traced. To prevent this,
* all functions in the probe path (everything except instr_size()) must
* have names that begin with "dtrace_".
*/
typedef enum dis_isize {
DIS_ISIZE_INSTR,
DIS_ISIZE_OPERAND
} dis_isize_t;
/*
* get a byte from instruction stream
*/
static int
dtrace_dis_get_byte(void *p)
{
int ret;
uchar_t **instr = p;
ret = **instr;
*instr += 1;
return (ret);
}
/*
* Returns either the size of a given instruction, in bytes, or the size of that
* instruction's memory access (if any), depending on the value of `which'.
* If a programming error in the tables is detected, the system will panic to
* ease diagnosis. Invalid instructions will not be flagged. They will appear
* to have an instruction size between 1 and the actual size, and will be
* reported as having no memory impact.
*/
/* ARGSUSED2 */
static int
dtrace_dis_isize(uchar_t *instr, dis_isize_t which, model_t model, int *rmindex)
{
int sz;
dis86_t x;
uint_t mode = SIZE32;
#if defined(sun)
mode = (model == DATAMODEL_LP64) ? SIZE64 : SIZE32;
#endif
x.d86_data = (void **)&instr;
x.d86_get_byte = dtrace_dis_get_byte;
x.d86_check_func = NULL;
if (dtrace_disx86(&x, mode) != 0)
return (-1);
if (which == DIS_ISIZE_INSTR)
sz = x.d86_len; /* length of the instruction */
else
sz = x.d86_memsize; /* length of memory operand */
if (rmindex != NULL)
*rmindex = x.d86_rmindex;
return (sz);
}
int
dtrace_instr_size(uchar_t *instr)
{
return (dtrace_dis_isize(instr, DIS_ISIZE_INSTR, DATAMODEL_NATIVE,
NULL));
}

1411
sys/cddl/dev/fbt/fbt.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,531 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
*
* Portions Copyright 2006-2008 John Birrell jb@freebsd.org
*
* $FreeBSD$
*
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/cpuvar.h>
#include <sys/fcntl.h>
#include <sys/filio.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/kthread.h>
#include <sys/limits.h>
#include <sys/linker.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/poll.h>
#include <sys/proc.h>
#include <sys/selinfo.h>
#include <sys/smp.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <machine/stdarg.h>
#include <sys/cyclic.h>
#include <sys/dtrace.h>
#include <sys/dtrace_bsd.h>
#define PROF_NAMELEN 15
#define PROF_PROFILE 0
#define PROF_TICK 1
#define PROF_PREFIX_PROFILE "profile-"
#define PROF_PREFIX_TICK "tick-"
/*
* Regardless of platform, there are five artificial frames in the case of the
* profile provider:
*
* profile_fire
* cyclic_expire
* cyclic_fire
* [ cbe ]
* [ locore ]
*
* On amd64, there are two frames associated with locore: one in locore, and
* another in common interrupt dispatch code. (i386 has not been modified to
* use this common layer.) Further, on i386, the interrupted instruction
* appears as its own stack frame. All of this means that we need to add one
* frame for amd64, and then take one away for both amd64 and i386.
*
* On SPARC, the picture is further complicated because the compiler
* optimizes away tail-calls -- so the following frames are optimized away:
*
* profile_fire
* cyclic_expire
*
* This gives three frames. However, on DEBUG kernels, the cyclic_expire
* frame cannot be tail-call eliminated, yielding four frames in this case.
*
* All of the above constraints lead to the mess below. Yes, the profile
* provider should ideally figure this out on-the-fly by hiting one of its own
* probes and then walking its own stack trace. This is complicated, however,
* and the static definition doesn't seem to be overly brittle. Still, we
* allow for a manual override in case we get it completely wrong.
*/
#ifdef __amd64
#define PROF_ARTIFICIAL_FRAMES 7
#else
#ifdef __i386
#define PROF_ARTIFICIAL_FRAMES 6
#else
#ifdef __sparc
#ifdef DEBUG
#define PROF_ARTIFICIAL_FRAMES 4
#else
#define PROF_ARTIFICIAL_FRAMES 3
#endif
#endif
#endif
#endif
typedef struct profile_probe {
char prof_name[PROF_NAMELEN];
dtrace_id_t prof_id;
int prof_kind;
hrtime_t prof_interval;
cyclic_id_t prof_cyclic;
} profile_probe_t;
typedef struct profile_probe_percpu {
hrtime_t profc_expected;
hrtime_t profc_interval;
profile_probe_t *profc_probe;
} profile_probe_percpu_t;
static d_open_t profile_open;
static int profile_unload(void);
static void profile_create(hrtime_t, char *, int);
static void profile_destroy(void *, dtrace_id_t, void *);
static void profile_enable(void *, dtrace_id_t, void *);
static void profile_disable(void *, dtrace_id_t, void *);
static void profile_load(void *);
static void profile_provide(void *, dtrace_probedesc_t *);
static int profile_rates[] = {
97, 199, 499, 997, 1999,
4001, 4999, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0
};
static int profile_ticks[] = {
1, 10, 100, 500, 1000,
5000, 0, 0, 0, 0,
0, 0, 0, 0, 0
};
/*
* profile_max defines the upper bound on the number of profile probes that
* can exist (this is to prevent malicious or clumsy users from exhausing
* system resources by creating a slew of profile probes). At mod load time,
* this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
* present in the profile.conf file.
*/
#define PROFILE_MAX_DEFAULT 1000 /* default max. number of probes */
static uint32_t profile_max = PROFILE_MAX_DEFAULT;
/* maximum number of profile probes */
static uint32_t profile_total; /* current number of profile probes */
static struct cdevsw profile_cdevsw = {
.d_version = D_VERSION,
.d_open = profile_open,
.d_name = "profile",
};
static dtrace_pattr_t profile_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};
static dtrace_pops_t profile_pops = {
profile_provide,
NULL,
profile_enable,
profile_disable,
NULL,
NULL,
NULL,
NULL,
NULL,
profile_destroy
};
static struct cdev *profile_cdev;
static dtrace_provider_id_t profile_id;
static hrtime_t profile_interval_min = NANOSEC / 5000; /* 5000 hz */
static int profile_aframes = 0; /* override */
static void
profile_fire(void *arg)
{
profile_probe_percpu_t *pcpu = arg;
profile_probe_t *prof = pcpu->profc_probe;
hrtime_t late;
solaris_cpu_t *c = &solaris_cpu[curcpu];
late = gethrtime() - pcpu->profc_expected;
pcpu->profc_expected += pcpu->profc_interval;
dtrace_probe(prof->prof_id, c->cpu_profile_pc,
c->cpu_profile_upc, late, 0, 0);
}
static void
profile_tick(void *arg)
{
profile_probe_t *prof = arg;
solaris_cpu_t *c = &solaris_cpu[curcpu];
dtrace_probe(prof->prof_id, c->cpu_profile_pc,
c->cpu_profile_upc, 0, 0, 0);
}
static void
profile_create(hrtime_t interval, char *name, int kind)
{
profile_probe_t *prof;
if (interval < profile_interval_min)
return;
if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
return;
atomic_add_32(&profile_total, 1);
if (profile_total > profile_max) {
atomic_add_32(&profile_total, -1);
return;
}
prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
(void) strcpy(prof->prof_name, name);
prof->prof_interval = interval;
prof->prof_cyclic = CYCLIC_NONE;
prof->prof_kind = kind;
prof->prof_id = dtrace_probe_create(profile_id,
NULL, NULL, name,
profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
}
/*ARGSUSED*/
static void
profile_provide(void *arg, dtrace_probedesc_t *desc)
{
int i, j, rate, kind;
hrtime_t val = 0, mult = 1, len = 0;
char *name, *suffix = NULL;
const struct {
char *prefix;
int kind;
} types[] = {
{ PROF_PREFIX_PROFILE, PROF_PROFILE },
{ PROF_PREFIX_TICK, PROF_TICK },
{ 0, 0 }
};
const struct {
char *name;
hrtime_t mult;
} suffixes[] = {
{ "ns", NANOSEC / NANOSEC },
{ "nsec", NANOSEC / NANOSEC },
{ "us", NANOSEC / MICROSEC },
{ "usec", NANOSEC / MICROSEC },
{ "ms", NANOSEC / MILLISEC },
{ "msec", NANOSEC / MILLISEC },
{ "s", NANOSEC / SEC },
{ "sec", NANOSEC / SEC },
{ "m", NANOSEC * (hrtime_t)60 },
{ "min", NANOSEC * (hrtime_t)60 },
{ "h", NANOSEC * (hrtime_t)(60 * 60) },
{ "hour", NANOSEC * (hrtime_t)(60 * 60) },
{ "d", NANOSEC * (hrtime_t)(24 * 60 * 60) },
{ "day", NANOSEC * (hrtime_t)(24 * 60 * 60) },
{ "hz", 0 },
{ NULL }
};
if (desc == NULL) {
char n[PROF_NAMELEN];
/*
* If no description was provided, provide all of our probes.
*/
for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
if ((rate = profile_rates[i]) == 0)
continue;
(void) snprintf(n, PROF_NAMELEN, "%s%d",
PROF_PREFIX_PROFILE, rate);
profile_create(NANOSEC / rate, n, PROF_PROFILE);
}
for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
if ((rate = profile_ticks[i]) == 0)
continue;
(void) snprintf(n, PROF_NAMELEN, "%s%d",
PROF_PREFIX_TICK, rate);
profile_create(NANOSEC / rate, n, PROF_TICK);
}
return;
}
name = desc->dtpd_name;
for (i = 0; types[i].prefix != NULL; i++) {
len = strlen(types[i].prefix);
if (strncmp(name, types[i].prefix, len) != 0)
continue;
break;
}
if (types[i].prefix == NULL)
return;
kind = types[i].kind;
j = strlen(name) - len;
/*
* We need to start before any time suffix.
*/
for (j = strlen(name); j >= len; j--) {
if (name[j] >= '0' && name[j] <= '9')
break;
suffix = &name[j];
}
ASSERT(suffix != NULL);
/*
* Now determine the numerical value present in the probe name.
*/
for (; j >= len; j--) {
if (name[j] < '0' || name[j] > '9')
return;
val += (name[j] - '0') * mult;
mult *= (hrtime_t)10;
}
if (val == 0)
return;
/*
* Look-up the suffix to determine the multiplier.
*/
for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
if (strcasecmp(suffixes[i].name, suffix) == 0) {
mult = suffixes[i].mult;
break;
}
}
if (suffixes[i].name == NULL && *suffix != '\0')
return;
if (mult == 0) {
/*
* The default is frequency-per-second.
*/
val = NANOSEC / val;
} else {
val *= mult;
}
profile_create(val, name, kind);
}
/* ARGSUSED */
static void
profile_destroy(void *arg, dtrace_id_t id, void *parg)
{
profile_probe_t *prof = parg;
ASSERT(prof->prof_cyclic == CYCLIC_NONE);
kmem_free(prof, sizeof (profile_probe_t));
ASSERT(profile_total >= 1);
atomic_add_32(&profile_total, -1);
}
/*ARGSUSED*/
static void
profile_online(void *arg, cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
{
profile_probe_t *prof = arg;
profile_probe_percpu_t *pcpu;
pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
pcpu->profc_probe = prof;
hdlr->cyh_func = profile_fire;
hdlr->cyh_arg = pcpu;
when->cyt_interval = prof->prof_interval;
when->cyt_when = gethrtime() + when->cyt_interval;
pcpu->profc_expected = when->cyt_when;
pcpu->profc_interval = when->cyt_interval;
}
/*ARGSUSED*/
static void
profile_offline(void *arg, cpu_t *cpu, void *oarg)
{
profile_probe_percpu_t *pcpu = oarg;
ASSERT(pcpu->profc_probe == arg);
kmem_free(pcpu, sizeof (profile_probe_percpu_t));
}
/* ARGSUSED */
static void
profile_enable(void *arg, dtrace_id_t id, void *parg)
{
profile_probe_t *prof = parg;
cyc_omni_handler_t omni;
cyc_handler_t hdlr;
cyc_time_t when;
ASSERT(prof->prof_interval != 0);
ASSERT(MUTEX_HELD(&cpu_lock));
if (prof->prof_kind == PROF_TICK) {
hdlr.cyh_func = profile_tick;
hdlr.cyh_arg = prof;
when.cyt_interval = prof->prof_interval;
when.cyt_when = gethrtime() + when.cyt_interval;
} else {
ASSERT(prof->prof_kind == PROF_PROFILE);
omni.cyo_online = profile_online;
omni.cyo_offline = profile_offline;
omni.cyo_arg = prof;
}
if (prof->prof_kind == PROF_TICK) {
prof->prof_cyclic = cyclic_add(&hdlr, &when);
} else {
prof->prof_cyclic = cyclic_add_omni(&omni);
}
}
/* ARGSUSED */
static void
profile_disable(void *arg, dtrace_id_t id, void *parg)
{
profile_probe_t *prof = parg;
ASSERT(prof->prof_cyclic != CYCLIC_NONE);
ASSERT(MUTEX_HELD(&cpu_lock));
cyclic_remove(prof->prof_cyclic);
prof->prof_cyclic = CYCLIC_NONE;
}
static void
profile_load(void *dummy)
{
/* Create the /dev/dtrace/profile entry. */
profile_cdev = make_dev(&profile_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/profile");
if (dtrace_register("profile", &profile_attr, DTRACE_PRIV_USER,
NULL, &profile_pops, NULL, &profile_id) != 0)
return;
}
static int
profile_unload()
{
int error = 0;
if ((error = dtrace_unregister(profile_id)) != 0)
return (error);
destroy_dev(profile_cdev);
return (error);
}
/* ARGSUSED */
static int
profile_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
/* ARGSUSED */
static int
profile_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
{
return (0);
}
SYSINIT(profile_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, profile_load, NULL);
SYSUNINIT(profile_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, profile_unload, NULL);
DEV_MODULE(profile, profile_modevent, NULL);
MODULE_VERSION(profile, 1);
MODULE_DEPEND(profile, dtrace, 1, 1, 1);
MODULE_DEPEND(profile, cyclic, 1, 1, 1);
MODULE_DEPEND(profile, opensolaris, 1, 1, 1);

144
sys/cddl/dev/prototype.c Normal file
View file

@ -0,0 +1,144 @@
/*
* This file is freeware. You are free to use it and add your own
* license.
*
* $FreeBSD$
*
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/dtrace.h>
static d_open_t prototype_open;
static int prototype_unload(void);
static void prototype_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
static void prototype_provide(void *, dtrace_probedesc_t *);
static void prototype_destroy(void *, dtrace_id_t, void *);
static void prototype_enable(void *, dtrace_id_t, void *);
static void prototype_disable(void *, dtrace_id_t, void *);
static void prototype_load(void *);
static struct cdevsw prototype_cdevsw = {
.d_version = D_VERSION,
.d_open = prototype_open,
.d_name = "prototype",
};
static dtrace_pattr_t prototype_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};
static dtrace_pops_t prototype_pops = {
prototype_provide,
NULL,
prototype_enable,
prototype_disable,
NULL,
NULL,
prototype_getargdesc,
NULL,
NULL,
prototype_destroy
};
static struct cdev *prototype_cdev;
static dtrace_provider_id_t prototype_id;
static void
prototype_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
{
}
static void
prototype_provide(void *arg, dtrace_probedesc_t *desc)
{
}
static void
prototype_destroy(void *arg, dtrace_id_t id, void *parg)
{
}
static void
prototype_enable(void *arg, dtrace_id_t id, void *parg)
{
}
static void
prototype_disable(void *arg, dtrace_id_t id, void *parg)
{
}
static void
prototype_load(void *dummy)
{
/* Create the /dev/dtrace/prototype entry. */
prototype_cdev = make_dev(&prototype_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/prototype");
if (dtrace_register("prototype", &prototype_attr, DTRACE_PRIV_USER,
NULL, &prototype_pops, NULL, &prototype_id) != 0)
return;
}
static int
prototype_unload()
{
int error = 0;
if ((error = dtrace_unregister(prototype_id)) != 0)
return (error);
destroy_dev(prototype_cdev);
return (error);
}
static int
prototype_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
static int
prototype_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
{
return (0);
}
SYSINIT(prototype_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, prototype_load, NULL);
SYSUNINIT(prototype_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, prototype_unload, NULL);
DEV_MODULE(prototype, prototype_modevent, NULL);
MODULE_VERSION(prototype, 1);
MODULE_DEPEND(prototype, dtrace, 1, 1, 1);
MODULE_DEPEND(prototype, opensolaris, 1, 1, 1);

254
sys/cddl/dev/sdt/sdt.c Normal file
View file

@ -0,0 +1,254 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
*
* Portions Copyright 2006-2008 John Birrell jb@freebsd.org
*
* $FreeBSD$
*
*/
#ifndef KDTRACE_HOOKS
#define KDTRACE_HOOKS
#endif
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/dtrace.h>
#include <sys/sdt.h>
#define SDT_ADDR2NDX(addr) (((uintptr_t)(addr)) >> 4)
static d_open_t sdt_open;
static int sdt_unload(void);
static void sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
static void sdt_provide_probes(void *, dtrace_probedesc_t *);
static void sdt_destroy(void *, dtrace_id_t, void *);
static void sdt_enable(void *, dtrace_id_t, void *);
static void sdt_disable(void *, dtrace_id_t, void *);
static void sdt_load(void *);
static struct cdevsw sdt_cdevsw = {
.d_version = D_VERSION,
.d_open = sdt_open,
.d_name = "sdt",
};
static dtrace_pattr_t sdt_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};
static dtrace_pops_t sdt_pops = {
sdt_provide_probes,
NULL,
sdt_enable,
sdt_disable,
NULL,
NULL,
sdt_getargdesc,
NULL,
NULL,
sdt_destroy
};
static struct cdev *sdt_cdev;
static int
sdt_argtype_callback(struct sdt_argtype *argtype, void *arg)
{
dtrace_argdesc_t *desc = arg;
if (desc->dtargd_ndx == argtype->ndx) {
desc->dtargd_mapping = desc->dtargd_ndx; /* XXX */
strlcpy(desc->dtargd_native, argtype->type,
sizeof(desc->dtargd_native));
desc->dtargd_xlate[0] = '\0'; /* XXX */
}
return (0);
}
static void
sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
{
struct sdt_probe *probe = parg;
if (desc->dtargd_ndx < probe->n_args)
(void) (sdt_argtype_listall(probe, sdt_argtype_callback, desc));
else
desc->dtargd_ndx = DTRACE_ARGNONE;
return;
}
static int
sdt_probe_callback(struct sdt_probe *probe, void *arg __unused)
{
struct sdt_provider *prov = probe->prov;
char mod[64];
char func[64];
char name[64];
/*
* Unfortunately this is necessary because the Solaris DTrace
* code mixes consts and non-consts with casts to override
* the incompatibilies. On FreeBSD, we use strict warnings
* in gcc, so we have to respect const vs non-const.
*/
strlcpy(mod, probe->mod, sizeof(mod));
strlcpy(func, probe->func, sizeof(func));
strlcpy(name, probe->name, sizeof(name));
if (dtrace_probe_lookup(prov->id, mod, func, name) != 0)
return (0);
(void) dtrace_probe_create(prov->id, probe->mod, probe->func,
probe->name, 0, probe);
return (0);
}
static int
sdt_provider_entry(struct sdt_provider *prov, void *arg)
{
return (sdt_probe_listall(prov, sdt_probe_callback, NULL));
}
static void
sdt_provide_probes(void *arg, dtrace_probedesc_t *desc)
{
if (desc != NULL)
return;
(void) sdt_provider_listall(sdt_provider_entry, NULL);
}
static void
sdt_destroy(void *arg, dtrace_id_t id, void *parg)
{
/* Nothing to do here. */
}
static void
sdt_enable(void *arg, dtrace_id_t id, void *parg)
{
struct sdt_probe *probe = parg;
probe->id = id;
}
static void
sdt_disable(void *arg, dtrace_id_t id, void *parg)
{
struct sdt_probe *probe = parg;
probe->id = 0;
}
static int
sdt_provider_reg_callback(struct sdt_provider *prov, void *arg __unused)
{
return (dtrace_register(prov->name, &sdt_attr, DTRACE_PRIV_USER,
NULL, &sdt_pops, NULL, (dtrace_provider_id_t *) &prov->id));
}
static void
sdt_load(void *dummy)
{
/* Create the /dev/dtrace/sdt entry. */
sdt_cdev = make_dev(&sdt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/sdt");
sdt_probe_func = dtrace_probe;
(void) sdt_provider_listall(sdt_provider_reg_callback, NULL);
}
static int
sdt_provider_unreg_callback(struct sdt_provider *prov, void *arg __unused)
{
return (dtrace_unregister(prov->id));
}
static int
sdt_unload()
{
int error = 0;
sdt_probe_func = sdt_probe_stub;
(void) sdt_provider_listall(sdt_provider_unreg_callback, NULL);
destroy_dev(sdt_cdev);
return (error);
}
/* ARGSUSED */
static int
sdt_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
/* ARGSUSED */
static int
sdt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
{
return (0);
}
SYSINIT(sdt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_load, NULL);
SYSUNINIT(sdt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_unload, NULL);
DEV_MODULE(sdt, sdt_modevent, NULL);
MODULE_VERSION(sdt, 1);
MODULE_DEPEND(sdt, dtrace, 1, 1, 1);
MODULE_DEPEND(sdt, opensolaris, 1, 1, 1);

View file

@ -0,0 +1,292 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
*
* Portions Copyright 2006-2008 John Birrell jb@freebsd.org
*
* $FreeBSD$
*
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/cpuvar.h>
#include <sys/fcntl.h>
#include <sys/filio.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/kthread.h>
#include <sys/limits.h>
#include <sys/linker.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/poll.h>
#include <sys/proc.h>
#include <sys/selinfo.h>
#include <sys/smp.h>
#include <sys/syscall.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <machine/stdarg.h>
#include <sys/dtrace.h>
#define SYSTRACE_ARTIFICIAL_FRAMES 1
#define SYSTRACE_SHIFT 16
#define SYSTRACE_ISENTRY(x) ((int)(x) >> SYSTRACE_SHIFT)
#define SYSTRACE_SYSNUM(x) ((int)(x) & ((1 << SYSTRACE_SHIFT) - 1))
#define SYSTRACE_ENTRY(id) ((1 << SYSTRACE_SHIFT) | (id))
#define SYSTRACE_RETURN(id) (id)
#if ((1 << SYSTRACE_SHIFT) <= SYS_MAXSYSCALL)
#error 1 << SYSTRACE_SHIFT must exceed number of system calls
#endif
extern char *syscallnames[];
static d_open_t systrace_open;
static int systrace_unload(void);
static void systrace_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
static void systrace_args(int, void *, u_int64_t *, int *);
static void systrace_probe(u_int32_t, int, struct sysent *, void *);
static void systrace_provide(void *, dtrace_probedesc_t *);
static void systrace_destroy(void *, dtrace_id_t, void *);
static void systrace_enable(void *, dtrace_id_t, void *);
static void systrace_disable(void *, dtrace_id_t, void *);
static void systrace_load(void *);
static struct cdevsw systrace_cdevsw = {
.d_version = D_VERSION,
.d_open = systrace_open,
.d_name = "systrace",
};
static dtrace_pattr_t systrace_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};
static dtrace_pops_t systrace_pops = {
systrace_provide,
NULL,
systrace_enable,
systrace_disable,
NULL,
NULL,
systrace_getargdesc,
NULL,
NULL,
systrace_destroy
};
static struct cdev *systrace_cdev;
static dtrace_provider_id_t systrace_id;
/*
* The syscall arguments are processed into a DTrace argument array
* using a generated function. See sys/kern/makesyscalls.sh.
*/
#include <kern/systrace_args.c>
static void
systrace_probe(u_int32_t id, int sysnum, struct sysent *sysent, void *params)
{
int n_args = 0;
u_int64_t uargs[8];
/*
* Check if this syscall has a custom argument conversion
* function registered. If so, it is a syscall registered
* by a loaded module.
*/
if (sysent->sy_systrace_args_func != NULL)
/*
* Convert the syscall parameters using the registered
* function.
*/
(*sysent->sy_systrace_args_func)(params, uargs, &n_args);
else
/*
* Use the built-in system call argument conversion
* function to translate the syscall structure fields
* into thhe array of 64-bit values that DTrace
* expects.
*/
systrace_args(sysnum, params, uargs, &n_args);
/* Process the probe using the converted argments. */
dtrace_probe(id, uargs[0], uargs[1], uargs[2], uargs[3], uargs[4]);
}
static void
systrace_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
{
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
systrace_setargdesc(sysnum, desc->dtargd_ndx, desc->dtargd_native,
sizeof(desc->dtargd_native));
if (desc->dtargd_native[0] == '\0')
desc->dtargd_ndx = DTRACE_ARGNONE;
return;
}
static void
systrace_provide(void *arg, dtrace_probedesc_t *desc)
{
int i;
if (desc != NULL)
return;
for (i = 0; i < SYS_MAXSYSCALL; i++) {
if (dtrace_probe_lookup(systrace_id, NULL,
syscallnames[i], "entry") != 0)
continue;
(void) dtrace_probe_create(systrace_id, NULL, syscallnames[i],
"entry", SYSTRACE_ARTIFICIAL_FRAMES,
(void *)((uintptr_t)SYSTRACE_ENTRY(i)));
(void) dtrace_probe_create(systrace_id, NULL, syscallnames[i],
"return", SYSTRACE_ARTIFICIAL_FRAMES,
(void *)((uintptr_t)SYSTRACE_RETURN(i)));
}
}
static void
systrace_destroy(void *arg, dtrace_id_t id, void *parg)
{
#ifdef DEBUG
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
/*
* There's nothing to do here but assert that we have actually been
* disabled.
*/
if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
ASSERT(sysent[sysnum].sy_entry == 0);
} else {
ASSERT(sysent[sysnum].sy_return == 0);
}
#endif
}
static void
systrace_enable(void *arg, dtrace_id_t id, void *parg)
{
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
if (SYSTRACE_ISENTRY((uintptr_t)parg))
sysent[sysnum].sy_entry = id;
else
sysent[sysnum].sy_return = id;
}
static void
systrace_disable(void *arg, dtrace_id_t id, void *parg)
{
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
sysent[sysnum].sy_entry = 0;
sysent[sysnum].sy_return = 0;
}
static void
systrace_load(void *dummy)
{
/* Create the /dev/dtrace/systrace entry. */
systrace_cdev = make_dev(&systrace_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/systrace");
if (dtrace_register("syscall", &systrace_attr, DTRACE_PRIV_USER,
NULL, &systrace_pops, NULL, &systrace_id) != 0)
return;
systrace_probe_func = systrace_probe;
}
static int
systrace_unload()
{
int error = 0;
if ((error = dtrace_unregister(systrace_id)) != 0)
return (error);
systrace_probe_func = NULL;
destroy_dev(systrace_cdev);
return (error);
}
static int
systrace_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
static int
systrace_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
{
return (0);
}
SYSINIT(systrace_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, systrace_load, NULL);
SYSUNINIT(systrace_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, systrace_unload, NULL);
DEV_MODULE(systrace, systrace_modevent, NULL);
MODULE_VERSION(systrace, 1);
MODULE_DEPEND(systrace, dtrace, 1, 1, 1);
MODULE_DEPEND(systrace, opensolaris, 1, 1, 1);