add kprobes provider for dt

this allows us to dynamically trace function boundaries with btrace by patching
prologues and epilogues with a breakpoint upon which the handler records the data,
sends it back to userland for btrace to consume.
currently it's hidden behind DDBPROF, and there is still a lot to cleanup and
improve, but basic scripts that observe return codes from a probed function
work.

from Tom Rollet, with various changes by me
feedback and ok mpi@
This commit is contained in:
jasper 2021-09-03 16:45:44 +00:00
parent e4c12d7a86
commit 840df46fd9
12 changed files with 650 additions and 198 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: vector.S,v 1.84 2020/11/13 05:32:08 guenther Exp $ */
/* $OpenBSD: vector.S,v 1.85 2021/09/03 16:45:44 jasper Exp $ */
/* $NetBSD: vector.S,v 1.5 2004/06/28 09:13:11 fvdl Exp $ */
/*
@ -188,10 +188,11 @@ INTRENTRY_LABEL(trap03):
sti
cld
SMAP_CLAC
movq %rsp, %rdi
call _C_LABEL(db_prof_hook)
cmpl $1, %eax
jne .Lreal_kern_trap
leaq _C_LABEL(dt_prov_kprobe), %rdi
movq %rsp, %rsi
call _C_LABEL(dt_prov_kprobe_hook)
cmpl $0, %eax
je .Lreal_kern_trap
cli
movq TF_RDI(%rsp),%rdi
@ -210,6 +211,11 @@ INTRENTRY_LABEL(trap03):
movq TF_R11(%rsp),%r11
/* %rax restored below, after being used to shift the stack */
cmpl $2, %eax
je .Lemulate_ret
.Lemulate_push_rbp:
/*
* We are returning from a probe trap so we need to fix the
* stack layout and emulate the patched instruction.
@ -217,6 +223,9 @@ INTRENTRY_LABEL(trap03):
*/
subq $16, %rsp
movq (TF_RAX + 16)(%rsp), %rax
movq %rax, TF_RAX(%rsp)
/* Shift hardware-saved registers. */
movq (TF_RIP + 16)(%rsp), %rax
movq %rax, TF_RIP(%rsp)
@ -237,7 +246,20 @@ INTRENTRY_LABEL(trap03):
/* Finally restore %rax */
movq (TF_RAX + 16)(%rsp),%rax
jmp .ret_int3
.Lemulate_ret:
/* Store a new return address in %rip */
movq TF_RSP(%rsp), %rax
movq (%rax), %rax
movq %rax, TF_RIP(%rsp)
addq $8, TF_RSP(%rsp)
/* Finally restore %rax */
movq (TF_RAX)(%rsp),%rax
.ret_int3:
addq $TF_RIP,%rsp
iretq
#endif /* !defined(GPROF) && defined(DDBPROF) */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: locore.s,v 1.192 2021/09/02 12:32:22 jasper Exp $ */
/* $OpenBSD: locore.s,v 1.193 2021/09/03 16:45:44 jasper Exp $ */
/* $NetBSD: locore.s,v 1.145 1996/05/03 19:41:19 christos Exp $ */
/*-
@ -205,7 +205,8 @@ INTRENTRY_LABEL(label): /* from kernel */ ; \
#define INTRFASTEXIT \
jmp intr_fast_exit
#define INTR_FAKE_TRAP 0xbadabada
#define INTR_FAKE_TRAP_PUSH_RPB 0xbadabada
#define INTR_FAKE_TRAP_POP_RBP 0xbcbcbcbc
/*
* PTmap is recursive pagemap at top of virtual address space.
@ -1259,17 +1260,32 @@ calltrap:
jne .Lreal_trap
pushl %esp
call _C_LABEL(db_prof_hook)
addl $4,%esp
cmpl $1,%eax
jne .Lreal_trap
subl $4, %esp
pushl %eax
leal _C_LABEL(dt_prov_kprobe), %eax
movl %eax, 4(%esp)
popl %eax
call _C_LABEL(dt_prov_kprobe_hook)
addl $8, %esp
cmpl $0, %eax
je .Lreal_trap
/*
* Abuse the error field to indicate that INTRFASTEXIT needs
* to emulate the patched instruction.
*/
movl $INTR_FAKE_TRAP, TF_ERR(%esp)
jz .Lalltraps_check_asts
cmpl $1, %eax
je .Lset_emulate_push_rbp
cmpl $2, %eax
je .Lset_emulate_ret
.Lset_emulate_push_rbp:
movl $INTR_FAKE_TRAP_PUSH_RPB, TF_ERR(%esp)
jmp .Lalltraps_check_asts
.Lset_emulate_ret:
movl $INTR_FAKE_TRAP_POP_RBP, TF_ERR(%esp)
jmp .Lalltraps_check_asts
.Lreal_trap:
#endif /* !defined(GPROF) && defined(DDBPROF) */
pushl %esp
@ -1298,8 +1314,10 @@ calltrap:
* The code below does that by trashing %eax, so it MUST be
* restored afterward.
*/
cmpl $INTR_FAKE_TRAP, TF_ERR(%esp)
je .Lprobe_fixup
cmpl $INTR_FAKE_TRAP_PUSH_RPB, TF_ERR(%esp)
je .Lprobe_fixup_push_rbp
cmpl $INTR_FAKE_TRAP_POP_RBP, TF_ERR(%esp)
je .Lprobe_fixup_pop_rbp
#endif /* !defined(GPROF) && defined(DDBPROF) */
#ifndef DIAGNOSTIC
INTRFASTEXIT
@ -1327,7 +1345,7 @@ spl_lowered:
.text
#if !defined(GPROF) && defined(DDBPROF)
.Lprobe_fixup:
.Lprobe_fixup_push_rbp:
/* Restore all register unwinding the stack. */
INTR_RESTORE_ALL
@ -1352,6 +1370,26 @@ spl_lowered:
popl %eax
iret
.Lprobe_fixup_pop_rbp:
/* Restore all register unwinding the stack. */
INTR_RESTORE_ALL
movl %eax, 0(%esp)
/* pop %ebp */
movl 20(%esp), %ebp
/* Shift hardware-saved registers: eflags, cs, eip */
movl 16(%esp), %eax
movl %eax, 20(%esp)
movl 12(%esp), %eax
movl %eax, 16(%esp)
movl 8(%esp), %eax
movl %eax, 12(%esp)
/* Pop eax and restore the stack pointer */
popl %eax
addl $8, %esp
iret
#endif /* !defined(GPROF) && defined(DDBPROF) */
.text

View File

@ -1,4 +1,4 @@
# $OpenBSD: files,v 1.703 2021/06/29 01:42:14 jsg Exp $
# $OpenBSD: files,v 1.704 2021/09/03 16:45:45 jasper Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@ -632,6 +632,7 @@ file dev/dt/dt_dev.c dt needs-flag
file dev/dt/dt_prov_profile.c dt
file dev/dt/dt_prov_syscall.c dt
file dev/dt/dt_prov_static.c dt
file dev/dt/dt_prov_kprobe.c dt
# XXX machine-independent SCSI files should live somewhere here, maybe

View File

@ -1,20 +1,5 @@
/* $OpenBSD: db_prof.c,v 1.4 2017/08/11 15:14:23 nayden Exp $ */
/* $OpenBSD: db_prof.c,v 1.5 2021/09/03 16:45:45 jasper Exp $ */
/*
* Copyright (c) 2016 Martin Pieuchot
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*-
* Copyright (c) 1983, 1992, 1993
* The Regents of the University of California. All rights reserved.
@ -43,7 +28,6 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
@ -54,173 +38,58 @@
#include <machine/db_machdep.h>
#include <ddb/db_extern.h>
#include <ddb/db_access.h> /* for db_write_bytes() */
#include <ddb/db_sym.h>
extern char etext[];
#include "dt.h" /* for NDT */
struct prof_probe {
const char *pp_name;
Elf_Sym *pp_symb;
SLIST_ENTRY(prof_probe) pp_next;
vaddr_t pp_inst;
int pp_on;
};
#define PPTSIZE PAGE_SIZE
#define PPTMASK ((PPTSIZE / sizeof(struct prof_probe)) - 1)
#define INSTTOIDX(inst) ((((unsigned long)(inst)) >> 4) & PPTMASK)
SLIST_HEAD(, prof_probe) *pp_table;
#if NDT > 0
#include <dev/dt/dtvar.h>
#endif
extern int db_profile; /* Allow dynamic profiling */
int db_prof_on; /* Profiling state On/Off */
vaddr_t db_get_pc(struct trapframe *);
void dt_prov_kprobe_patch_all_entry(void);
void dt_prov_kprobe_depatch_all_entry(void);
vaddr_t db_get_probe_addr(struct trapframe *);
void db_prof_forall(Elf_Sym *, char *, char *, int, void *);
void db_prof_count(unsigned long, unsigned long);
void
db_prof_init(void)
{
unsigned long nentries;
pp_table = malloc(PPTSIZE, M_TEMP, M_NOWAIT|M_ZERO);
if (pp_table == NULL)
return;
db_elf_sym_forall(db_prof_forall, &nentries);
printf("ddb probe table references %lu entry points\n", nentries);
}
void
db_prof_forall(Elf_Sym *sym, char *name, char *suff, int pre, void *xarg)
{
Elf_Sym *symb = sym;
unsigned long *nentries = xarg;
struct prof_probe *pp;
vaddr_t inst;
if (ELF_ST_TYPE(symb->st_info) != STT_FUNC)
return;
inst = symb->st_value;
if (inst < KERNBASE || inst >= (vaddr_t)&etext)
return;
if (*((uint8_t *)inst) != SSF_INST)
return;
if (strncmp(name, "db_", 3) == 0 || strncmp(name, "trap", 4) == 0)
return;
#ifdef __i386__
/* Avoid a recursion in db_write_text(). */
if (strncmp(name, "pmap_pte", 8) == 0)
return;
#endif
pp = malloc(sizeof(struct prof_probe), M_TEMP, M_NOWAIT|M_ZERO);
if (pp == NULL)
return;
pp->pp_name = name;
pp->pp_inst = inst;
pp->pp_symb = symb;
SLIST_INSERT_HEAD(&pp_table[INSTTOIDX(pp->pp_inst)], pp, pp_next);
(*nentries)++;
}
vaddr_t db_get_pc(struct trapframe *);
int
db_prof_enable(void)
{
#if defined(__amd64__) || defined(__i386__)
struct prof_probe *pp;
uint8_t patch = BKPT_INST;
unsigned long s;
int i;
#if NDT > 0
if (!db_profile)
return EPERM;
if (pp_table == NULL)
return ENOENT;
KASSERT(BKPT_SIZE == SSF_SIZE);
s = intr_disable();
for (i = 0; i < (PPTSIZE / sizeof(*pp)); i++) {
SLIST_FOREACH(pp, &pp_table[i], pp_next) {
pp->pp_on = 1;
db_write_bytes(pp->pp_inst, BKPT_SIZE, &patch);
}
}
intr_restore(s);
dt_prov_kprobe_patch_all_entry();
db_prof_on = 1;
return 0;
#else
return ENOENT;
#endif
#endif /* NDT > 0 */
}
void
db_prof_disable(void)
{
struct prof_probe *pp;
uint8_t patch = SSF_INST;
unsigned long s;
int i;
#if NDT > 0
db_prof_on = 0;
s = intr_disable();
for (i = 0; i < (PPTSIZE / sizeof(*pp)); i++) {
SLIST_FOREACH(pp, &pp_table[i], pp_next) {
db_write_bytes(pp->pp_inst, SSF_SIZE, &patch);
pp->pp_on = 0;
}
}
intr_restore(s);
}
int
db_prof_hook(struct trapframe *frame)
{
struct prof_probe *pp;
vaddr_t pc, inst;
if (pp_table == NULL)
return 0;
pc = db_get_pc(frame);
inst = db_get_probe_addr(frame);
SLIST_FOREACH(pp, &pp_table[INSTTOIDX(inst)], pp_next) {
if (pp->pp_on && pp->pp_inst == inst) {
if (db_prof_on)
db_prof_count(pc, inst);
return 1;
}
}
return 0;
dt_prov_kprobe_depatch_all_entry();
#endif /* NDT > 0 */
}
/*
* Equivalent to mcount(), must be called with interrupt disabled.
*/
void
db_prof_count(unsigned long frompc, unsigned long selfpc)
db_prof_count(struct trapframe *frame)
{
unsigned short *frompcindex;
struct tostruct *top, *prevtop;
struct gmonparam *p;
long toindex;
unsigned long frompc, selfpc;
if ((p = curcpu()->ci_gmon) == NULL)
return;
@ -232,6 +101,9 @@ db_prof_count(unsigned long frompc, unsigned long selfpc)
if (p->state != GMON_PROF_ON)
return;
frompc = db_get_pc(frame);
selfpc = db_get_probe_addr(frame);
/*
* check that frompcindex is a reasonable pc value.
* for example: signal catchers get called from the stack,

View File

@ -1,4 +1,4 @@
/* $OpenBSD: dt_dev.c,v 1.14 2021/05/22 21:25:38 bluhm Exp $ */
/* $OpenBSD: dt_dev.c,v 1.15 2021/09/03 16:45:45 jasper Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@ -124,7 +124,7 @@ int dt_ioctl_get_stats(struct dt_softc *, struct dtioc_stat *);
int dt_ioctl_record_start(struct dt_softc *);
void dt_ioctl_record_stop(struct dt_softc *);
int dt_ioctl_probe_enable(struct dt_softc *, struct dtioc_req *);
void dt_ioctl_probe_disable(struct dt_softc *, struct dtioc_req *);
int dt_ioctl_probe_disable(struct dt_softc *, struct dtioc_req *);
int dt_pcb_ring_copy(struct dt_pcb *, struct dt_evt *, size_t, uint64_t *);
@ -138,6 +138,9 @@ dtattach(struct device *parent, struct device *self, void *aux)
dt_nprobes += dt_prov_profile_init();
dt_nprobes += dt_prov_syscall_init();
dt_nprobes += dt_prov_static_init();
#ifdef DDBPROF
dt_nprobes += dt_prov_kprobe_init();
#endif
printf("dt: %u probes\n", dt_nprobes);
}
@ -275,6 +278,7 @@ dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
return dt_ioctl_get_stats(sc, (struct dtioc_stat *)addr);
case DTIOCRECORD:
case DTIOCPRBENABLE:
case DTIOCPRBDISABLE:
/* root only ioctl(2) */
break;
default:
@ -295,6 +299,9 @@ dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
case DTIOCPRBENABLE:
error = dt_ioctl_probe_enable(sc, (struct dtioc_req *)addr);
break;
case DTIOCPRBDISABLE:
error = dt_ioctl_probe_disable(sc, (struct dtioc_req *)addr);
break;
default:
KASSERT(0);
}
@ -478,6 +485,35 @@ dt_ioctl_probe_enable(struct dt_softc *sc, struct dtioc_req *dtrq)
return 0;
}
int
dt_ioctl_probe_disable(struct dt_softc *sc, struct dtioc_req *dtrq)
{
struct dt_probe *dtp;
int error;
KASSERT(suser(curproc) == 0);
if (!dtioc_req_isvalid(dtrq))
return EINVAL;
SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next) {
if (dtp->dtp_pbn == dtrq->dtrq_pbn)
break;
}
if (dtp == NULL)
return ENOENT;
if (dtp->dtp_prov->dtpv_dealloc) {
error = dtp->dtp_prov->dtpv_dealloc(dtp, sc, dtrq);
if (error)
return error;
}
DPRINTF("dt%d: pid %d dealloc\n", sc->ds_unit, sc->ds_pid,
dtrq->dtrq_pbn);
return 0;
}
struct dt_probe *
dt_dev_alloc_probe(const char *func, const char *name, struct dt_provider *dtpv)
{
@ -492,6 +528,9 @@ dt_dev_alloc_probe(const char *func, const char *name, struct dt_provider *dtpv)
dtp->dtp_func = func;
dtp->dtp_name = name;
dtp->dtp_sysnum = -1;
dtp->dtp_ref = 0;
mtx_init(&dtp->dtp_mtx, IPL_HIGH);
return dtp;
}

464
sys/dev/dt/dt_prov_kprobe.c Normal file
View File

@ -0,0 +1,464 @@
/* $OpenBSD: dt_prov_kprobe.c,v 1.1 2021/09/03 16:45:45 jasper Exp $ */
/*
* Copyright (c) 2020 Tom Rollet <tom.rollet@epita.fr>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#if defined(DDBPROF) && (defined(__amd64__) || defined(__i386__))
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/atomic.h>
#include <sys/exec_elf.h>
#include <ddb/db_elf.h>
#include <machine/db_machdep.h>
#include <ddb/db_extern.h>
#include <ddb/db_access.h>
#include <ddb/db_sym.h>
#include <ddb/db_interface.h>
#include <dev/dt/dtvar.h>
int dt_prov_kprobe_alloc(struct dt_probe *dtp, struct dt_softc *sc,
struct dt_pcb_list *plist, struct dtioc_req *dtrq);
int dt_prov_kprobe_hook(struct dt_provider *dtpv, ...);
int dt_prov_kprobe_dealloc(struct dt_probe *dtp, struct dt_softc *sc,
struct dtioc_req *dtrq);
void db_prof_count(struct trapframe *frame);
struct kprobe_probe {
struct dt_probe* dtp;
SLIST_ENTRY(kprobe_probe) kprobe_next;
};
/* Bob Jenkin's public domain 32-bit integer hashing function.
* Original at https://burtleburtle.net/bob/hash/integer.html.
*/
uint32_t
ptr_hash(uint32_t a) {
a = (a + 0x7ed55d16) + (a<<12);
a = (a ^ 0xc761c23c) ^ (a>>19);
a = (a + 0x165667b1) + (a<<5);
a = (a + 0xd3a2646c) ^ (a<<9);
a = (a + 0xfd7046c5) + (a<<3);
a = (a ^ 0xb55a4f09) ^ (a>>16);
return a;
}
#define PPTSIZE PAGE_SIZE * 30
#define PPTMASK ((PPTSIZE / sizeof(struct kprobe_probe)) - 1)
#define INSTTOIDX(inst) (ptr_hash(inst) & PPTMASK)
SLIST_HEAD(, kprobe_probe) *dtpf_entry;
SLIST_HEAD(, kprobe_probe) *dtpf_return;
int nb_probes_entry = 0;
int nb_probes_return = 0;
#define DTEVT_PROV_KPROBE (DTEVT_COMMON|DTEVT_FUNCARGS)
#define KPROBE_ENTRY "entry"
#define KPROBE_RETURN "return"
#if defined(__amd64__)
#define KPROBE_RETGUARD_MOV_1 0x4c
#define KPROBE_RETGUARD_MOV_2 0x8b
#define KPROBE_RETGUARD_MOV_3 0x1d
#define KPROBE_RETGUARD_MOV_SIZE 7
#define KPROBE_RETGUARD_XOR_1 0x4c
#define KPROBE_RETGUARD_XOR_2 0x33
#define KPROBE_RETGUARD_XOR_3 0x1c
#define KPROBE_RETGUARD_XOR_SIZE 4
#define RET 0xc3
#define RET_SIZE 1
#elif defined(__i386__)
#define POP_RBP 0x5d
#define POP_RBP_SIZE 1
#endif
struct dt_provider dt_prov_kprobe = {
.dtpv_name = "kprobe",
.dtpv_alloc = dt_prov_kprobe_alloc,
.dtpv_enter = dt_prov_kprobe_hook,
.dtpv_leave = NULL,
.dtpv_dealloc = dt_prov_kprobe_dealloc,
};
extern db_symtab_t db_symtab;
extern char __kutext_end[];
extern int db_prof_on;
/* Initialize all entry and return probes and store them in global arrays */
int
dt_prov_kprobe_init(void)
{
struct dt_probe *dtp;
struct kprobe_probe *kprobe_dtp;
Elf_Sym *symp, *symtab_start, *symtab_end;
char *strtab, *name;
vaddr_t inst, limit;
int nb_sym, nb_probes;
nb_sym = (db_symtab.end - db_symtab.start) / sizeof (Elf_Sym);
nb_probes = nb_probes_entry = nb_probes_return = 0;
dtpf_entry = malloc(PPTSIZE, M_DT, M_NOWAIT|M_ZERO);
if (dtpf_entry == NULL)
goto end;
dtpf_return = malloc(PPTSIZE, M_DT, M_NOWAIT|M_ZERO);
if (dtpf_return == NULL)
goto end;
db_symtab_t *stab = &db_symtab;
symtab_start = STAB_TO_SYMSTART(stab);
symtab_end = STAB_TO_SYMEND(stab);
strtab = db_elf_find_strtab(stab);
for (symp = symtab_start; symp < symtab_end; symp++) {
if (ELF_ST_TYPE(symp->st_info) != STT_FUNC)
continue;
inst = symp->st_value;
name = strtab + symp->st_name;
limit = symp->st_value + symp->st_size;
/* Filter function that are not mapped in memory */
if (inst < KERNBASE || inst >= (vaddr_t)&__kutext_end)
continue;
/* Remove some function to avoid recursive tracing */
if (strncmp(name, "dt_", 3) == 0 || strncmp(name, "trap", 4) == 0 ||
strncmp(name, "db_", 3) == 0)
continue;
#if defined(__amd64__)
/* Find if there is a retguard, if so move the inst pointer to the later 'push rbp' */
if (*((uint8_t *)inst) != SSF_INST) {
/* No retguards in i386 */
if (((uint8_t *)inst)[0] != KPROBE_RETGUARD_MOV_1 ||
((uint8_t *)inst)[1] != KPROBE_RETGUARD_MOV_2 ||
((uint8_t *)inst)[2] != KPROBE_RETGUARD_MOV_3 ||
((uint8_t *)inst)[KPROBE_RETGUARD_MOV_SIZE] != KPROBE_RETGUARD_XOR_1 ||
((uint8_t *)inst)[KPROBE_RETGUARD_MOV_SIZE + 1] != KPROBE_RETGUARD_XOR_2 ||
((uint8_t *)inst)[KPROBE_RETGUARD_MOV_SIZE + 2] != KPROBE_RETGUARD_XOR_3 ||
((uint8_t *)inst)[KPROBE_RETGUARD_MOV_SIZE + KPROBE_RETGUARD_XOR_SIZE] != SSF_INST)
continue;
inst = (vaddr_t)&(((uint8_t *)inst)[KPROBE_RETGUARD_MOV_SIZE + KPROBE_RETGUARD_XOR_SIZE]);
}
#elif defined(__i386__)
if (*((uint8_t *)inst) != SSF_INST)
continue;
#endif
dtp = dt_dev_alloc_probe(name, KPROBE_ENTRY, &dt_prov_kprobe);
if (dtp == NULL)
goto end;
kprobe_dtp = malloc(sizeof(struct kprobe_probe), M_TEMP, M_NOWAIT|M_ZERO);
if (kprobe_dtp == NULL)
goto end;
kprobe_dtp->dtp = dtp;
dtp->dtp_addr = inst;
dtp->dtp_nargs = db_ctf_func_numargs(symp);
dt_dev_register_probe(dtp);
SLIST_INSERT_HEAD(&dtpf_entry[INSTTOIDX(dtp->dtp_addr)], kprobe_dtp, kprobe_next);
nb_probes++;
nb_probes_entry++;
/*
* Poor method to find the return point
* => we would need a disassembler to find all return points
* For now we start from the end of the function, iterate on
* int3 inserted for retguard until we find a ret
*/
#if defined(__amd64__)
if (*(uint8_t *)(limit - 1) != RET)
continue;
inst = limit - 1;
#elif defined(__i386__)
/*
* Little temporary hack to find some return probe
* => always int3 after 'pop %rpb; ret'
*/
while(*((uint8_t *)inst) == 0xcc)
(*(uint8_t *)inst) -= 1;
if (*(uint8_t *)(limit - 2) != POP_RBP)
continue;
inst = limit - 2;
#endif
dtp = dt_dev_alloc_probe(name, KPROBE_RETURN, &dt_prov_kprobe);
if (dtp == NULL)
goto end;
kprobe_dtp = malloc(sizeof(struct kprobe_probe), M_TEMP, M_NOWAIT|M_ZERO);
if (kprobe_dtp == NULL)
goto end;
kprobe_dtp->dtp = dtp;
dtp->dtp_addr = inst;
dt_dev_register_probe(dtp);
SLIST_INSERT_HEAD(&dtpf_return[INSTTOIDX(dtp->dtp_addr)], kprobe_dtp, kprobe_next);
nb_probes++;
nb_probes_return++;
}
end:
return nb_probes;
}
int
dt_prov_kprobe_alloc(struct dt_probe *dtp, struct dt_softc *sc,
struct dt_pcb_list *plist, struct dtioc_req *dtrq)
{
uint8_t patch = BKPT_INST;
struct dt_pcb *dp;
unsigned s;
dp = dt_pcb_alloc(dtp, sc);
if (dp == NULL)
return ENOMEM;
/* Patch only if it's first pcb referencing this probe */
mtx_enter(&dtp->dtp_mtx);
dtp->dtp_ref++;
KASSERT(dtp->dtp_ref != 0);
if (dtp->dtp_ref == 1) {
s = intr_disable();
db_write_bytes(dtp->dtp_addr, BKPT_SIZE, &patch);
intr_restore(s);
}
mtx_leave(&dtp->dtp_mtx);
dp->dp_filter = dtrq->dtrq_filter;
dp->dp_evtflags = dtrq->dtrq_evtflags & DTEVT_PROV_KPROBE;
TAILQ_INSERT_HEAD(plist, dp, dp_snext);
return 0;
}
int
dt_prov_kprobe_dealloc(struct dt_probe *dtp, struct dt_softc *sc,
struct dtioc_req *dtrq)
{
uint8_t patch;
int size;
unsigned s;
if (strcmp(dtp->dtp_name, KPROBE_ENTRY) == 0) {
patch = SSF_INST;
size = SSF_SIZE;
} else if (strcmp(dtp->dtp_name, KPROBE_RETURN) == 0) {
#if defined(__amd64__)
patch = RET;
size = RET_SIZE;
#elif defined(__i386__)
patch = POP_RBP;
size = POP_RBP_SIZE;
#endif
} else
KASSERT(0 && "Trying to dealloc not yet implemented probe type");
mtx_enter(&dtp->dtp_mtx);
dtp->dtp_ref--;
if (dtp->dtp_ref == 0) {
s = intr_disable();
db_write_bytes(dtp->dtp_addr, size, &patch);
intr_restore(s);
}
mtx_leave(&dtp->dtp_mtx);
/* Deallocation of PCB is done by dt_pcb_purge when closing the dev */
return 0;
}
int
dt_prov_kprobe_hook(struct dt_provider *dtpv, ...)
{
struct dt_probe *dtp;
struct dt_pcb *dp;
struct trapframe *tf;
struct kprobe_probe *kprobe_dtp;
va_list ap;
int is_dt_bkpt = 0;
int error; /* Return values for return probes*/
vaddr_t *args;
size_t argsize;
register_t retval[2];
KASSERT(dtpv == &dt_prov_kprobe);
va_start(ap, dtpv);
tf = va_arg(ap, struct trapframe*);
va_end(ap);
#if defined(__amd64__)
vaddr_t addr = tf->tf_rip - BKPT_SIZE;
#elif defined(__i386)
vaddr_t addr = tf->tf_eip - BKPT_SIZE;
#endif
SLIST_FOREACH(kprobe_dtp, &dtpf_entry[INSTTOIDX(addr)], kprobe_next) {
dtp = kprobe_dtp->dtp;
if (dtp->dtp_addr != addr)
continue;
is_dt_bkpt = 1;
if (db_prof_on)
db_prof_count(tf);
if (!dtp->dtp_recording)
continue;
smr_read_enter();
SMR_SLIST_FOREACH(dp, &dtp->dtp_pcbs, dp_pnext) {
struct dt_evt *dtev;
dtev = dt_pcb_ring_get(dp, 0);
if (dtev == NULL)
continue;
#if defined(__amd64__)
args = (vaddr_t *)tf->tf_rdi;
/* XXX: use CTF to get the number of arguments. */
argsize = 6;
#elif defined(__i386__)
/* All args on stack */
args = (vaddr_t *)(tf->tf_esp + 4);
argsize = 10;
#endif
if (ISSET(dp->dp_evtflags, DTEVT_FUNCARGS))
memcpy(dtev->dtev_args, args, argsize);
dt_pcb_ring_consume(dp, dtev);
}
smr_read_leave();
}
if (is_dt_bkpt)
return is_dt_bkpt;
SLIST_FOREACH(kprobe_dtp, &dtpf_return[INSTTOIDX(addr)], kprobe_next) {
dtp = kprobe_dtp->dtp;
if (dtp->dtp_addr != addr)
continue;
is_dt_bkpt = 2;
if (!dtp->dtp_recording)
continue;
smr_read_enter();
SMR_SLIST_FOREACH(dp, &dtp->dtp_pcbs, dp_pnext) {
struct dt_evt *dtev;
dtev = dt_pcb_ring_get(dp, 0);
if (dtev == NULL)
continue;
#if defined(__amd64__)
retval[0] = tf->tf_rax;
retval[1] = 0;
error = 0;
#elif defined(__i386)
retval[0] = tf->tf_eax;
retval[1] = 0;
error = 0;
#endif
dtev->dtev_retval[0] = retval[0];
dtev->dtev_retval[1] = retval[1];
dtev->dtev_error = error;
dt_pcb_ring_consume(dp, dtev);
}
smr_read_leave();
}
return is_dt_bkpt;
}
/* Function called by ddb to patch all functions without allocating 1 pcb per probe */
void
dt_prov_kprobe_patch_all_entry(void)
{
uint8_t patch = BKPT_INST;
struct dt_probe *dtp;
struct kprobe_probe *kprobe_dtp;
size_t i;
for (i = 0; i < PPTMASK; ++i) {
SLIST_FOREACH(kprobe_dtp, &dtpf_entry[i], kprobe_next) {
dtp = kprobe_dtp->dtp;
mtx_enter(&dtp->dtp_mtx);
dtp->dtp_ref++;
if (dtp->dtp_ref == 1) {
unsigned s;
s = intr_disable();
db_write_bytes(dtp->dtp_addr, BKPT_SIZE, &patch);
intr_restore(s);
}
mtx_leave(&dtp->dtp_mtx);
}
}
}
/* Function called by ddb to patch all functions without allocating 1 pcb per probe */
void
dt_prov_kprobe_depatch_all_entry(void)
{
uint8_t patch = SSF_INST;
struct dt_probe *dtp;
struct kprobe_probe *kprobe_dtp;
size_t i;
for (i = 0; i < PPTMASK; ++i) {
SLIST_FOREACH(kprobe_dtp, &dtpf_entry[i], kprobe_next) {
dtp = kprobe_dtp->dtp;
mtx_enter(&dtp->dtp_mtx);
dtp->dtp_ref--;
if (dtp->dtp_ref == 0) {
unsigned s;
s = intr_disable();
db_write_bytes(dtp->dtp_addr, SSF_SIZE, &patch);
intr_restore(s);
}
mtx_leave(&dtp->dtp_mtx);
}
}
}
#endif /* __amd64__ || __i386__ */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: dt_prov_profile.c,v 1.3 2020/06/26 13:11:23 mpi Exp $ */
/* $OpenBSD: dt_prov_profile.c,v 1.4 2021/09/03 16:45:45 jasper Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@ -31,14 +31,15 @@ struct dt_probe *dtpp_interval; /* global periodic probe */
int dt_prov_profile_alloc(struct dt_probe *, struct dt_softc *,
struct dt_pcb_list *, struct dtioc_req *);
void dt_prov_profile_enter(struct dt_provider *, ...);
void dt_prov_interval_enter(struct dt_provider *, ...);
int dt_prov_profile_enter(struct dt_provider *, ...);
int dt_prov_interval_enter(struct dt_provider *, ...);
struct dt_provider dt_prov_profile = {
.dtpv_name = "profile",
.dtpv_alloc = dt_prov_profile_alloc,
.dtpv_enter = dt_prov_profile_enter,
.dtpv_leave = NULL,
.dtpv_dealloc = NULL,
};
struct dt_provider dt_prov_interval = {
@ -46,6 +47,7 @@ struct dt_provider dt_prov_interval = {
.dtpv_alloc = dt_prov_profile_alloc,
.dtpv_enter = dt_prov_interval_enter,
.dtpv_leave = NULL,
.dtpv_dealloc = NULL,
};
int
@ -114,7 +116,7 @@ dt_prov_profile_fire(struct dt_pcb *dp)
dp->dp_nticks = 0;
}
void
int
dt_prov_profile_enter(struct dt_provider *dtpv, ...)
{
struct cpu_info *ci = curcpu();
@ -130,9 +132,10 @@ dt_prov_profile_enter(struct dt_provider *dtpv, ...)
dt_prov_profile_fire(dp);
}
smr_read_leave();
return 0;
}
void
int
dt_prov_interval_enter(struct dt_provider *dtpv, ...)
{
struct dt_pcb *dp;
@ -144,4 +147,5 @@ dt_prov_interval_enter(struct dt_provider *dtpv, ...)
dt_prov_profile_fire(dp);
}
smr_read_leave();
return 0;
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: dt_prov_static.c,v 1.9 2021/09/02 17:21:39 jasper Exp $ */
/* $OpenBSD: dt_prov_static.c,v 1.10 2021/09/03 16:45:45 jasper Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@ -25,12 +25,13 @@
int dt_prov_static_alloc(struct dt_probe *, struct dt_softc *,
struct dt_pcb_list *, struct dtioc_req *);
void dt_prov_static_hook(struct dt_provider *, ...);
int dt_prov_static_hook(struct dt_provider *, ...);
struct dt_provider dt_prov_static = {
.dtpv_name = "tracepoint",
.dtpv_alloc = dt_prov_static_alloc,
.dtpv_enter = dt_prov_static_hook,
.dtpv_name = "tracepoint",
.dtpv_alloc = dt_prov_static_alloc,
.dtpv_enter = dt_prov_static_hook,
.dtpv_dealloc = NULL,
};
/*
@ -137,7 +138,7 @@ dt_prov_static_alloc(struct dt_probe *dtp, struct dt_softc *sc,
return 0;
}
void
int
dt_prov_static_hook(struct dt_provider *dtpv, ...)
{
struct dt_probe *dtp;
@ -172,4 +173,5 @@ dt_prov_static_hook(struct dt_provider *dtpv, ...)
dt_pcb_ring_consume(dp, dtev);
}
smr_read_leave();
return 1;
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: dt_prov_syscall.c,v 1.5 2021/09/02 17:21:39 jasper Exp $ */
/* $OpenBSD: dt_prov_syscall.c,v 1.6 2021/09/03 16:45:45 jasper Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@ -37,7 +37,7 @@ unsigned int dtps_nsysent = SYS_MAXSYSCALL;
int dt_prov_syscall_alloc(struct dt_probe *, struct dt_softc *,
struct dt_pcb_list *, struct dtioc_req *);
void dt_prov_syscall_entry(struct dt_provider *, ...);
int dt_prov_syscall_entry(struct dt_provider *, ...);
void dt_prov_syscall_return(struct dt_provider *, ...);
struct dt_provider dt_prov_syscall = {
@ -45,6 +45,7 @@ struct dt_provider dt_prov_syscall = {
.dtpv_alloc = dt_prov_syscall_alloc,
.dtpv_enter = dt_prov_syscall_entry,
.dtpv_leave = dt_prov_syscall_return,
.dtpv_dealloc = NULL,
};
int
@ -119,7 +120,7 @@ dt_prov_syscall_alloc(struct dt_probe *dtp, struct dt_softc *sc,
return 0;
}
void
int
dt_prov_syscall_entry(struct dt_provider *dtpv, ...)
{
struct dt_probe *dtp;
@ -139,11 +140,11 @@ dt_prov_syscall_entry(struct dt_provider *dtpv, ...)
KASSERT((argsize / sizeof(register_t)) <= DTMAXFUNCARGS);
if (sysnum < 0 || sysnum >= dtps_nsysent)
return;
return 0;
dtp = dtps_entry[sysnum];
if (!dtp->dtp_recording)
return;
return 0;
smr_read_enter();
SMR_SLIST_FOREACH(dp, &dtp->dtp_pcbs, dp_pnext) {
@ -159,6 +160,7 @@ dt_prov_syscall_entry(struct dt_provider *dtpv, ...)
dt_pcb_ring_consume(dp, dtev);
}
smr_read_leave();
return 0;
}
void

View File

@ -1,4 +1,4 @@
/* $OpenBSD: dtvar.h,v 1.7 2021/09/02 19:41:48 jasper Exp $ */
/* $OpenBSD: dtvar.h,v 1.8 2021/09/03 16:45:45 jasper Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@ -63,7 +63,6 @@ struct dt_evt {
#define dtev_args _args.E_entry /* function args. */
#define dtev_retval _args.E_return.__retval /* function retval */
#define dtev_error _args.E_return.__error /* function error */
};
/*
@ -132,6 +131,7 @@ struct dtioc_stat {
#define DTIOCRECORD _IOW('D', 3, int)
#define DTIOCPRBENABLE _IOW('D', 4, struct dtioc_req)
#define DTIOCPRBDISABLE _IOW('D', 5, struct dtioc_req)
#ifdef _KERNEL
@ -205,6 +205,7 @@ void dt_pcb_ring_consume(struct dt_pcb *, struct dt_evt *);
* K kernel lock
* D dt_lock
* D,S dt_lock for writting and SMR for reading
* M dtp mutex
*/
struct dt_probe {
SIMPLEQ_ENTRY(dt_probe) dtp_next; /* [K] global list of probes */
@ -213,12 +214,15 @@ struct dt_probe {
const char *dtp_func; /* [I] probe function */
const char *dtp_name; /* [I] probe name */
uint32_t dtp_pbn; /* [I] unique ID */
volatile uint32_t dtp_recording; /* [D] is it recording? */
uint8_t dtp_nargs; /* [I] # of arguments */
volatile uint32_t dtp_recording; /* [d] is it recording? */
struct mutex dtp_mtx;
unsigned dtp_ref; /* [m] # of PCBs referencing the probe */
/* Provider specific fields. */
int dtp_sysnum; /* [I] related # of syscall */
const char *dtp_argtype[5];/* [I] type of arguments */
int dtp_nargs; /* [I] # of arguments */
vaddr_t dtp_addr; /* [I] address of breakpint */
};
@ -231,13 +235,18 @@ struct dt_provider {
int (*dtpv_alloc)(struct dt_probe *, struct dt_softc *,
struct dt_pcb_list *, struct dtioc_req *);
void (*dtpv_enter)(struct dt_provider *, ...);
int (*dtpv_enter)(struct dt_provider *, ...);
void (*dtpv_leave)(struct dt_provider *, ...);
int (*dtpv_dealloc)(struct dt_probe *, struct dt_softc *,
struct dtioc_req *);
};
extern struct dt_provider dt_prov_kprobe;
int dt_prov_profile_init(void);
int dt_prov_syscall_init(void);
int dt_prov_static_init(void);
int dt_prov_kprobe_init(void);
struct dt_probe *dt_dev_alloc_probe(const char *, const char *,
struct dt_provider *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: subr_prof.c,v 1.30 2016/09/04 09:22:29 mpi Exp $ */
/* $OpenBSD: subr_prof.c,v 1.31 2021/09/03 16:45:45 jasper Exp $ */
/* $NetBSD: subr_prof.c,v 1.12 1996/04/22 01:38:50 christos Exp $ */
/*-
@ -70,10 +70,6 @@ prof_init(void)
char *cp;
int size;
#if !defined(GPROF) && defined(DDBPROF)
db_prof_init();
#endif
/*
* Round lowpc and highpc to multiples of the density we're using
* so the rest of the scaling (here and in gprof) stays in ints.

View File

@ -1,4 +1,4 @@
/* $OpenBSD: btrace.c,v 1.46 2021/09/02 17:21:39 jasper Exp $ */
/* $OpenBSD: btrace.c,v 1.47 2021/09/03 16:45:44 jasper Exp $ */
/*
* Copyright (c) 2019 - 2021 Martin Pieuchot <mpi@openbsd.org>
@ -503,15 +503,18 @@ rules_teardown(int fd)
}
TAILQ_FOREACH(r, &g_rules, br_next) {
dtrq = r->br_cookie;
if (r->br_type != B_RT_PROBE) {
if (r->br_type == B_RT_END)
rend = r;
continue;
}
} else {
if (ioctl(fd, DTIOCPRBDISABLE, dtrq))
err(1, "DTIOCPRBDISABLE");
}
dtrq = r->br_cookie;
if (dtrq->dtrq_evtflags & DTEVT_KSTACK)
dokstack = 1;
if (dtrq->dtrq_evtflags & DTEVT_KSTACK)
dokstack = 1;
}
if (dokstack)