mirror of
https://github.com/opnsense/src.git
synced 2026-02-16 00:58:21 -05:00
Custom DTrace kernel module files plus FreeBSD-specific DTrace providers.
This commit is contained in:
parent
29d10c76f9
commit
91eaf3e183
30 changed files with 15340 additions and 0 deletions
220
sys/cddl/dev/dtmalloc/dtmalloc.c
Normal file
220
sys/cddl/dev/dtmalloc/dtmalloc.c
Normal 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);
|
||||
3193
sys/cddl/dev/dtrace/amd64/dis_tables.c
Normal file
3193
sys/cddl/dev/dtrace/amd64/dis_tables.c
Normal file
File diff suppressed because it is too large
Load diff
112
sys/cddl/dev/dtrace/amd64/dis_tables.h
Normal file
112
sys/cddl/dev/dtrace/amd64/dis_tables.h
Normal 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 */
|
||||
573
sys/cddl/dev/dtrace/amd64/dtrace_asm.S
Normal file
573
sys/cddl/dev/dtrace/amd64/dtrace_asm.S
Normal 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)
|
||||
612
sys/cddl/dev/dtrace/amd64/dtrace_isa.c
Normal file
612
sys/cddl/dev/dtrace/amd64/dtrace_isa.c
Normal 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));
|
||||
}
|
||||
507
sys/cddl/dev/dtrace/amd64/dtrace_subr.c
Normal file
507
sys/cddl/dev/dtrace/amd64/dtrace_subr.c
Normal 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);
|
||||
}
|
||||
132
sys/cddl/dev/dtrace/amd64/instr_size.c
Normal file
132
sys/cddl/dev/dtrace/amd64/instr_size.c
Normal 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));
|
||||
}
|
||||
84
sys/cddl/dev/dtrace/dtrace_anon.c
Normal file
84
sys/cddl/dev/dtrace/dtrace_anon.c
Normal 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);
|
||||
}
|
||||
}
|
||||
134
sys/cddl/dev/dtrace/dtrace_cddl.h
Normal file
134
sys/cddl/dev/dtrace/dtrace_cddl.h
Normal 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_ */
|
||||
61
sys/cddl/dev/dtrace/dtrace_clone.c
Normal file
61
sys/cddl/dev/dtrace/dtrace_clone.c
Normal 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;
|
||||
}
|
||||
}
|
||||
596
sys/cddl/dev/dtrace/dtrace_debug.c
Normal file
596
sys/cddl/dev/dtrace/dtrace_debug.c
Normal 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
|
||||
13
sys/cddl/dev/dtrace/dtrace_hacks.c
Normal file
13
sys/cddl/dev/dtrace/dtrace_hacks.c
Normal 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;
|
||||
}
|
||||
777
sys/cddl/dev/dtrace/dtrace_ioctl.c
Normal file
777
sys/cddl/dev/dtrace/dtrace_ioctl.c
Normal 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);
|
||||
}
|
||||
164
sys/cddl/dev/dtrace/dtrace_load.c
Normal file
164
sys/cddl/dev/dtrace/dtrace_load.c
Normal 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;
|
||||
}
|
||||
47
sys/cddl/dev/dtrace/dtrace_modevent.c
Normal file
47
sys/cddl/dev/dtrace/dtrace_modevent.c
Normal 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);
|
||||
}
|
||||
82
sys/cddl/dev/dtrace/dtrace_sysctl.c
Normal file
82
sys/cddl/dev/dtrace/dtrace_sysctl.c
Normal 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", "");
|
||||
|
||||
73
sys/cddl/dev/dtrace/dtrace_test.c
Normal file
73
sys/cddl/dev/dtrace/dtrace_test.c
Normal 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);
|
||||
138
sys/cddl/dev/dtrace/dtrace_unload.c
Normal file
138
sys/cddl/dev/dtrace/dtrace_unload.c
Normal 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);
|
||||
}
|
||||
101
sys/cddl/dev/dtrace/dtrace_vtime.c
Normal file
101
sys/cddl/dev/dtrace/dtrace_vtime.c
Normal 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);
|
||||
}
|
||||
3193
sys/cddl/dev/dtrace/i386/dis_tables.c
Normal file
3193
sys/cddl/dev/dtrace/i386/dis_tables.c
Normal file
File diff suppressed because it is too large
Load diff
112
sys/cddl/dev/dtrace/i386/dis_tables.h
Normal file
112
sys/cddl/dev/dtrace/i386/dis_tables.h
Normal 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 */
|
||||
527
sys/cddl/dev/dtrace/i386/dtrace_asm.S
Normal file
527
sys/cddl/dev/dtrace/i386/dtrace_asm.S
Normal 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 = ®s */
|
||||
pushl %eax /* push on_panic_stack */
|
||||
pushl %ecx /* push ®s */
|
||||
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)
|
||||
622
sys/cddl/dev/dtrace/i386/dtrace_isa.c
Normal file
622
sys/cddl/dev/dtrace/i386/dtrace_isa.c
Normal 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));
|
||||
}
|
||||
503
sys/cddl/dev/dtrace/i386/dtrace_subr.c
Normal file
503
sys/cddl/dev/dtrace/i386/dtrace_subr.c
Normal 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);
|
||||
}
|
||||
132
sys/cddl/dev/dtrace/i386/instr_size.c
Normal file
132
sys/cddl/dev/dtrace/i386/instr_size.c
Normal 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
1411
sys/cddl/dev/fbt/fbt.c
Normal file
File diff suppressed because it is too large
Load diff
531
sys/cddl/dev/profile/profile.c
Normal file
531
sys/cddl/dev/profile/profile.c
Normal 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
144
sys/cddl/dev/prototype.c
Normal 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
254
sys/cddl/dev/sdt/sdt.c
Normal 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);
|
||||
292
sys/cddl/dev/systrace/systrace.c
Normal file
292
sys/cddl/dev/systrace/systrace.c
Normal 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);
|
||||
Loading…
Reference in a new issue