diff options
author | 2016-09-04 09:22:28 +0000 | |
---|---|---|
committer | 2016-09-04 09:22:28 +0000 | |
commit | 6950c8e205c43756d30d56ca678aa8205e9896fa (patch) | |
tree | 520bceb313719cacbfa77f438a8dc2fe9bf4a4f0 | |
parent | sync (diff) | |
download | wireguard-openbsd-6950c8e205c43756d30d56ca678aa8205e9896fa.tar.xz wireguard-openbsd-6950c8e205c43756d30d56ca678aa8205e9896fa.zip |
Introduce Dynamic Profiling, a ddb(4) based & gprof compatible kernel
profiling framework.
Code patching is used to enable probes when entering functions. The
probes will call a mcount()-like function to match the behavior of a
GPROF kernel.
Currently only available on amd64 and guarded under DDBPROF. Support
for other archs will follow soon.
A new sysctl knob, ddb.console, need to be set to 1 in securelevel 0
to be able to use this feature.
Inputs and ok guenther@
-rw-r--r-- | sys/arch/amd64/amd64/db_trace.c | 16 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/locore.S | 45 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/vector.S | 19 | ||||
-rw-r--r-- | sys/arch/amd64/include/cpu.h | 4 | ||||
-rw-r--r-- | sys/arch/amd64/include/cpufunc.h | 18 | ||||
-rw-r--r-- | sys/arch/amd64/include/db_machdep.h | 5 | ||||
-rw-r--r-- | sys/arch/amd64/include/frameasm.h | 3 | ||||
-rw-r--r-- | sys/conf/GENERIC | 3 | ||||
-rw-r--r-- | sys/conf/files | 3 | ||||
-rw-r--r-- | sys/ddb/db_extern.h | 7 | ||||
-rw-r--r-- | sys/ddb/db_prof.c | 307 | ||||
-rw-r--r-- | sys/ddb/db_usrreq.c | 20 | ||||
-rw-r--r-- | sys/ddb/db_var.h | 7 | ||||
-rw-r--r-- | sys/kern/init_main.c | 7 | ||||
-rw-r--r-- | sys/kern/kern_clock.c | 8 | ||||
-rw-r--r-- | sys/kern/kern_sysctl.c | 4 | ||||
-rw-r--r-- | sys/kern/subr_prof.c | 61 | ||||
-rw-r--r-- | sys/sys/sysctl.h | 4 | ||||
-rw-r--r-- | sys/sys/systm.h | 6 |
19 files changed, 507 insertions, 40 deletions
diff --git a/sys/arch/amd64/amd64/db_trace.c b/sys/arch/amd64/amd64/db_trace.c index 4c50cc987bc..417a908d06b 100644 --- a/sys/arch/amd64/amd64/db_trace.c +++ b/sys/arch/amd64/amd64/db_trace.c @@ -1,4 +1,4 @@ -/* $OpenBSD: db_trace.c,v 1.19 2016/09/03 21:12:00 jasper Exp $ */ +/* $OpenBSD: db_trace.c,v 1.20 2016/09/04 09:22:28 mpi Exp $ */ /* $NetBSD: db_trace.c,v 1.1 2003/04/26 18:39:27 fvdl Exp $ */ /* @@ -312,3 +312,17 @@ db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, (*pr)(":\n"); } } + +vaddr_t +db_get_pc(struct trapframe *tf) +{ + struct callframe *cf = (struct callframe *)(tf->tf_rsp - 8); + + return db_get_value((db_addr_t)&cf->f_retaddr, 8, 0); +} + +vaddr_t +db_get_probe_addr(struct trapframe *tf) +{ + return tf->tf_rip - BKPT_SIZE; +} diff --git a/sys/arch/amd64/amd64/locore.S b/sys/arch/amd64/amd64/locore.S index e902b1ac9d7..68b0b0e56dc 100644 --- a/sys/arch/amd64/amd64/locore.S +++ b/sys/arch/amd64/amd64/locore.S @@ -1,4 +1,4 @@ -/* $OpenBSD: locore.S,v 1.82 2016/07/16 06:04:29 mlarkin Exp $ */ +/* $OpenBSD: locore.S,v 1.83 2016/09/04 09:22:28 mpi Exp $ */ /* $NetBSD: locore.S,v 1.13 2004/03/25 18:33:17 drochner Exp $ */ /* @@ -1153,6 +1153,19 @@ NENTRY(intr_fast_exit) movq TF_RCX(%rsp),%rcx movq TF_R11(%rsp),%r11 movq TF_RAX(%rsp),%rax + +#if !defined(GPROF) && defined(DDBPROF) + /* + * If we are returning from a probe trap we need to fix the + * stack layout and emulate the patched instruction. + * + * The code below does that by trashing %rax, so it MUST be + * restored afterward. + */ + cmpl $INTR_FAKE_TRAP, TF_ERR(%rsp) + je .Lprobe_fixup +#endif /* !defined(GPROF) && defined(DDBPROF) */ + addq $TF_RIP,%rsp .globl _C_LABEL(doreti_iret) @@ -1160,6 +1173,36 @@ _C_LABEL(doreti_iret): iretq +#if !defined(GPROF) && defined(DDBPROF) +.Lprobe_fixup: + /* Reserve enough room to emulate "pushq %rbp". */ + subq $16, %rsp + + /* Shift hardware-saved registers. */ + movq (TF_RIP + 16)(%rsp), %rax + movq %rax, TF_RIP(%rsp) + movq (TF_CS + 16)(%rsp), %rax + movq %rax, TF_CS(%rsp) + movq (TF_RFLAGS + 16)(%rsp), %rax + movq %rax, TF_RFLAGS(%rsp) + movq (TF_RSP + 16)(%rsp), %rax + movq %rax, TF_RSP(%rsp) + movq (TF_SS + 16)(%rsp), %rax + movq %rax, TF_SS(%rsp) + + /* Pull 8 bytes off the stack and store the content of the register. */ + movq TF_RSP(%rsp), %rax + subq $8, %rax + movq %rax, TF_RSP(%rsp) + movq %rbp, (%rax) + + /* Write back overwritten %rax */ + movq (TF_RAX + 16)(%rsp),%rax + + addq $TF_RIP,%rsp + iretq +#endif /* !defined(GPROF) && defined(DDBPROF) */ + ENTRY(pagezero) movq $-PAGE_SIZE,%rdx subq %rdx,%rdi diff --git a/sys/arch/amd64/amd64/vector.S b/sys/arch/amd64/amd64/vector.S index 137ede9334c..96f8ff34241 100644 --- a/sys/arch/amd64/amd64/vector.S +++ b/sys/arch/amd64/amd64/vector.S @@ -1,4 +1,4 @@ -/* $OpenBSD: vector.S,v 1.46 2016/06/22 01:12:38 mikeb Exp $ */ +/* $OpenBSD: vector.S,v 1.47 2016/09/04 09:22:28 mpi Exp $ */ /* $NetBSD: vector.S,v 1.5 2004/06/28 09:13:11 fvdl Exp $ */ /* @@ -249,6 +249,23 @@ calltrap: #ifdef DIAGNOSTIC movl CPUVAR(ILEVEL),%ebx #endif /* DIAGNOSTIC */ +#if !defined(GPROF) && defined(DDBPROF) + cmpl $T_BPTFLT,TF_TRAPNO(%rsp) + jne .Lreal_trap + + movq %rsp, %rdi + call _C_LABEL(db_prof_hook) + cmpl $1, %eax + jne .Lreal_trap + + /* + * Abuse the error field to indicate that intr_fast_exit needs + * to emulate the patched instruction. + */ + movl $INTR_FAKE_TRAP, TF_ERR(%rsp) + jz 2f +.Lreal_trap: +#endif /* !defined(GPROF) && defined(DDBPROF) */ movq %rsp, %rdi call _C_LABEL(trap) 2: /* Check for ASTs on exit to user mode. */ diff --git a/sys/arch/amd64/include/cpu.h b/sys/arch/amd64/include/cpu.h index cfa735af48d..81050a320f0 100644 --- a/sys/arch/amd64/include/cpu.h +++ b/sys/arch/amd64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.102 2016/07/28 21:57:57 kettenis Exp $ */ +/* $OpenBSD: cpu.h,v 1.103 2016/09/04 09:22:28 mpi Exp $ */ /* $NetBSD: cpu.h,v 1.1 2003/04/26 18:39:39 fvdl Exp $ */ /*- @@ -173,7 +173,7 @@ struct cpu_info { struct ksensordev ci_sensordev; struct ksensor ci_sensor; -#ifdef GPROF +#if defined(GPROF) || defined(DDBPROF) struct gmonparam *ci_gmon; #endif u_int32_t ci_vmm_flags; diff --git a/sys/arch/amd64/include/cpufunc.h b/sys/arch/amd64/include/cpufunc.h index b5eeacaec53..f0599634079 100644 --- a/sys/arch/amd64/include/cpufunc.h +++ b/sys/arch/amd64/include/cpufunc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpufunc.h,v 1.12 2015/03/21 20:42:38 kettenis Exp $ */ +/* $OpenBSD: cpufunc.h,v 1.13 2016/09/04 09:22:28 mpi Exp $ */ /* $NetBSD: cpufunc.h,v 1.3 2003/05/08 10:27:43 fvdl Exp $ */ /*- @@ -218,6 +218,22 @@ write_rflags(u_long ef) __asm volatile("pushq %0; popfq" : : "r" (ef)); } +static __inline u_long +intr_disable(void) +{ + u_long ef; + + ef = read_rflags(); + disable_intr(); + return (ef); +} + +static __inline void +intr_restore(u_long ef) +{ + write_rflags(ef); +} + static __inline u_int64_t rdmsr(u_int msr) { diff --git a/sys/arch/amd64/include/db_machdep.h b/sys/arch/amd64/include/db_machdep.h index 64740270691..6d554ebc9e3 100644 --- a/sys/arch/amd64/include/db_machdep.h +++ b/sys/arch/amd64/include/db_machdep.h @@ -1,4 +1,4 @@ -/* $OpenBSD: db_machdep.h,v 1.14 2016/04/27 11:10:48 mpi Exp $ */ +/* $OpenBSD: db_machdep.h,v 1.15 2016/09/04 09:22:28 mpi Exp $ */ /* $NetBSD: db_machdep.h,v 1.2 2003/04/29 17:06:04 scw Exp $ */ /* @@ -54,6 +54,9 @@ extern db_regs_t ddb_regs; /* register state */ #define BKPT_SIZE (1) /* size of breakpoint inst */ #define BKPT_SET(inst) (BKPT_INST) +#define SSF_INST 0x55 +#define SSF_SIZE (1) + #define FIXUP_PC_AFTER_BREAK(regs) ((regs)->tf_rip -= BKPT_SIZE) #define db_clear_single_step(regs) ((regs)->tf_rflags &= ~PSL_T) diff --git a/sys/arch/amd64/include/frameasm.h b/sys/arch/amd64/include/frameasm.h index b3ec74e7365..b48d11f4e2d 100644 --- a/sys/arch/amd64/include/frameasm.h +++ b/sys/arch/amd64/include/frameasm.h @@ -1,4 +1,4 @@ -/* $OpenBSD: frameasm.h,v 1.9 2015/07/17 15:37:58 guenther Exp $ */ +/* $OpenBSD: frameasm.h,v 1.10 2016/09/04 09:22:28 mpi Exp $ */ /* $NetBSD: frameasm.h,v 1.1 2003/04/26 18:39:40 fvdl Exp $ */ #ifndef _AMD64_MACHINE_FRAMEASM_H @@ -70,6 +70,7 @@ swapgs ; \ movw %ax,%gs +#define INTR_FAKE_TRAP 0xbadabada #define CHECK_ASTPENDING(reg) movq CPUVAR(CURPROC),reg ; \ cmpq $0, reg ; \ diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC index 483fd9a5733..9e7e2b750a3 100644 --- a/sys/conf/GENERIC +++ b/sys/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.232 2016/09/03 14:20:25 phessler Exp $ +# $OpenBSD: GENERIC,v 1.233 2016/09/04 09:22:28 mpi Exp $ # # Machine-independent option; used by all architectures for their # GENERIC kernel @@ -6,6 +6,7 @@ #option INSECURE # default to secure option DDB # in-kernel debugger +#option DDBPROF # ddb(4) based profiling #option DDB_SAFE_CONSOLE # allow break into ddb during boot #makeoptions DEBUG="-g" # compile full symbol table #makeoptions PROF="-pg" # build profiled kernel diff --git a/sys/conf/files b/sys/conf/files index ce3ca166851..add35747c24 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.626 2016/09/03 14:20:26 phessler Exp $ +# $OpenBSD: files,v 1.627 2016/09/04 09:22:28 mpi Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -609,6 +609,7 @@ file ddb/db_hangman.c ddb file ddb/db_input.c ddb file ddb/db_lex.c ddb file ddb/db_output.c ddb +file ddb/db_prof.c ddb & ddbprof & !gprof file ddb/db_run.c ddb | kgdb file ddb/db_struct.c ddb & ddb_struct file ddb/db_sym.c ddb diff --git a/sys/ddb/db_extern.h b/sys/ddb/db_extern.h index b935cec99fa..cf4b8e852bc 100644 --- a/sys/ddb/db_extern.h +++ b/sys/ddb/db_extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: db_extern.h,v 1.16 2016/04/19 12:23:25 mpi Exp $ */ +/* $OpenBSD: db_extern.h,v 1.17 2016/09/04 09:22:29 mpi Exp $ */ /* $NetBSD: db_extern.h,v 1.1 1996/02/05 01:57:00 christos Exp $ */ /* @@ -54,4 +54,9 @@ int db_readline(char *, int); /* db_trap.c */ void db_trap(int, int); +/* db_prof.c */ +void db_prof_init(void); +int db_prof_enable(void); +void db_prof_disable(void); + #endif /* _DDB_DB_EXTERN_H_ */ diff --git a/sys/ddb/db_prof.c b/sys/ddb/db_prof.c new file mode 100644 index 00000000000..3b2c46b16c8 --- /dev/null +++ b/sys/ddb/db_prof.c @@ -0,0 +1,307 @@ +/* $OpenBSD: db_prof.c,v 1.1 2016/09/04 09:22:29 mpi 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. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/exec_elf.h> +#include <sys/malloc.h> +#include <sys/gmon.h> + +#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[]; + +struct prof_probe { + const char *pp_name; + vaddr_t pp_inst; + Elf_Sym *pp_symb; + SLIST_ENTRY(prof_probe) pp_next; +}; + +#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; + +extern int db_profile; /* Allow dynamic profiling */ + +vaddr_t db_get_pc(struct trapframe *); +vaddr_t db_get_probe_addr(struct trapframe *); + +void db_prof_forall(db_sym_t, 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(db_sym_t sym, char *name, char *suff, int pre, void *xarg) +{ + Elf_Sym *symb = (Elf_Sym *)sym; + unsigned long *nentries = xarg; + struct prof_probe *pp; + vaddr_t inst; + + if (ELFDEFNNAME(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; + + 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)++; +} + +int +db_prof_enable(void) +{ +#ifdef __amd64__ + struct prof_probe *pp; + uint8_t patch = BKPT_INST; + unsigned long s; + int i; + + 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) { + db_write_bytes(pp->pp_inst, BKPT_SIZE, &patch); + } + } + intr_restore(s); + + return 0; +#else + return ENOENT; +#endif +} + +void +db_prof_disable(void) +{ + struct prof_probe *pp; + uint8_t patch = SSF_INST; + unsigned long s; + int i; + + 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); + } + 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_inst == inst) { + db_prof_count(pc, inst); + return 1; + } + } + return 0; +} + +/* + * Equivalent to mcount(), must be called with interrupt disabled. + */ +void +db_prof_count(unsigned long frompc, unsigned long selfpc) +{ + unsigned short *frompcindex; + struct tostruct *top, *prevtop; + struct gmonparam *p; + long toindex; + + if ((p = curcpu()->ci_gmon) == NULL) + return; + + /* + * check that we are profiling + * and that we aren't recursively invoked. + */ + if (p->state != GMON_PROF_ON) + return; + + /* + * check that frompcindex is a reasonable pc value. + * for example: signal catchers get called from the stack, + * not from text space. too bad. + */ + frompc -= p->lowpc; + if (frompc > p->textsize) + return; + +#if (HASHFRACTION & (HASHFRACTION - 1)) == 0 + if (p->hashfraction == HASHFRACTION) + frompcindex = + &p->froms[frompc / (HASHFRACTION * sizeof(*p->froms))]; + else +#endif + frompcindex = + &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))]; + toindex = *frompcindex; + if (toindex == 0) { + /* + * first time traversing this arc + */ + toindex = ++p->tos[0].link; + if (toindex >= p->tolimit) + /* halt further profiling */ + goto overflow; + + *frompcindex = toindex; + top = &p->tos[toindex]; + top->selfpc = selfpc; + top->count = 1; + top->link = 0; + return; + } + top = &p->tos[toindex]; + if (top->selfpc == selfpc) { + /* + * arc at front of chain; usual case. + */ + top->count++; + return; + } + /* + * have to go looking down chain for it. + * top points to what we are looking at, + * prevtop points to previous top. + * we know it is not at the head of the chain. + */ + for (; /* return */; ) { + if (top->link == 0) { + /* + * top is end of the chain and none of the chain + * had top->selfpc == selfpc. + * so we allocate a new tostruct + * and link it to the head of the chain. + */ + toindex = ++p->tos[0].link; + if (toindex >= p->tolimit) + goto overflow; + + top = &p->tos[toindex]; + top->selfpc = selfpc; + top->count = 1; + top->link = *frompcindex; + *frompcindex = toindex; + return; + } + /* + * otherwise, check the next arc on the chain. + */ + prevtop = top; + top = &p->tos[top->link]; + if (top->selfpc == selfpc) { + /* + * there it is. + * increment its count + * move it to the head of the chain. + */ + top->count++; + toindex = prevtop->link; + prevtop->link = top->link; + top->link = *frompcindex; + *frompcindex = toindex; + return; + } + } + +overflow: + p->state = GMON_PROF_ERROR; +} diff --git a/sys/ddb/db_usrreq.c b/sys/ddb/db_usrreq.c index c574dc24930..40e736d67f4 100644 --- a/sys/ddb/db_usrreq.c +++ b/sys/ddb/db_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: db_usrreq.c,v 1.17 2015/03/14 03:38:46 jsg Exp $ */ +/* $OpenBSD: db_usrreq.c,v 1.18 2016/09/04 09:22:29 mpi Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff. All rights reserved. @@ -35,6 +35,7 @@ #include <ddb/db_var.h> int db_log = 1; +int db_profile; /* Allow dynamic profiling */ int ddb_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, @@ -101,6 +102,23 @@ ddb_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, return (ENODEV); } return (sysctl_rdint(oldp, oldlenp, newp, 0)); +#if defined(DDBPROF) + case DBCTL_PROFILE: + if (securelevel > 0) + return (sysctl_int_lower(oldp, oldlenp, newp, newlen, + &db_profile)); + else { + ctlval = db_profile; + if ((error = sysctl_int(oldp, oldlenp, newp, newlen, + &ctlval)) || newp == NULL) + return (error); + if (ctlval != 1 && ctlval != 0) + return (EINVAL); + db_profile = ctlval; + return (0); + } + break; +#endif /* DDBPROF */ default: return (EOPNOTSUPP); } diff --git a/sys/ddb/db_var.h b/sys/ddb/db_var.h index b9aef32aa10..3bb02b5d34d 100644 --- a/sys/ddb/db_var.h +++ b/sys/ddb/db_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: db_var.h,v 1.11 2011/04/03 16:46:19 drahn Exp $ */ +/* $OpenBSD: db_var.h,v 1.12 2016/09/04 09:22:29 mpi Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff. All rights reserved. @@ -43,7 +43,8 @@ #define DBCTL_CONSOLE 6 #define DBCTL_LOG 7 #define DBCTL_TRIGGER 8 -#define DBCTL_MAXID 9 +#define DBCTL_PROFILE 9 +#define DBCTL_MAXID 10 #define CTL_DDB_NAMES { \ { NULL, 0 }, \ @@ -55,6 +56,7 @@ { "console", CTLTYPE_INT }, \ { "log", CTLTYPE_INT }, \ { "trigger", CTLTYPE_INT }, \ + { "profile", CTLTYPE_INT }, \ } #ifdef _KERNEL @@ -66,6 +68,7 @@ extern int db_panic; extern int db_console; extern int db_log; extern int db_is_active; +extern int db_profile; int ddb_sysctl(int *, u_int, void *, size_t *, void *, size_t, struct proc *); diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 563f3c23264..b0710b9f935 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init_main.c,v 1.256 2016/09/03 14:46:56 naddy Exp $ */ +/* $OpenBSD: init_main.c,v 1.257 2016/09/04 09:22:29 mpi Exp $ */ /* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */ /* @@ -139,6 +139,7 @@ void start_cleaner(void *); void start_update(void *); void start_reaper(void *); void crypto_init(void); +void prof_init(void); void init_exec(void); void kqueue_init(void); void taskq_init(void); @@ -400,9 +401,9 @@ main(void *framep) initconsbuf(); -#ifdef GPROF +#if defined(GPROF) || defined(DDBPROF) /* Initialize kernel profiling. */ - kmstartup(); + prof_init(); #endif /* init exec and emul */ diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index 61ec41af87d..106e41a5a31 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_clock.c,v 1.90 2016/03/24 05:40:56 dlg Exp $ */ +/* $OpenBSD: kern_clock.c,v 1.91 2016/09/04 09:22:29 mpi Exp $ */ /* $NetBSD: kern_clock.c,v 1.34 1996/06/09 04:51:03 briggs Exp $ */ /*- @@ -51,7 +51,7 @@ #include <sys/timetc.h> -#ifdef GPROF +#if defined(GPROF) || defined(DDBPROF) #include <sys/gmon.h> #endif @@ -317,7 +317,7 @@ stopprofclock(struct process *pr) void statclock(struct clockframe *frame) { -#ifdef GPROF +#if defined(GPROF) || defined(DDBPROF) struct gmonparam *g; u_long i; #endif @@ -356,7 +356,7 @@ statclock(struct clockframe *frame) else spc->spc_cp_time[CP_USER]++; } else { -#ifdef GPROF +#if defined(GPROF) || defined(DDBPROF) /* * Kernel statistics are just like addupc_intr, only easier. */ diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index 68e1f38e09a..f82c286f652 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sysctl.c,v 1.307 2016/08/23 23:28:02 tedu Exp $ */ +/* $OpenBSD: kern_sysctl.c,v 1.308 2016/09/04 09:22:29 mpi Exp $ */ /* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */ /*- @@ -389,7 +389,7 @@ kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, case KERN_MBSTAT: return (sysctl_rdstruct(oldp, oldlenp, newp, &mbstat, sizeof(mbstat))); -#ifdef GPROF +#if defined(GPROF) || defined(DDBPROF) case KERN_PROF: return (sysctl_doprof(name + 1, namelen - 1, oldp, oldlenp, newp, newlen)); diff --git a/sys/kern/subr_prof.c b/sys/kern/subr_prof.c index d7a59693f12..1de0726ab95 100644 --- a/sys/kern/subr_prof.c +++ b/sys/kern/subr_prof.c @@ -1,4 +1,4 @@ -/* $OpenBSD: subr_prof.c,v 1.29 2015/12/05 10:11:53 tedu Exp $ */ +/* $OpenBSD: subr_prof.c,v 1.30 2016/09/04 09:22:29 mpi Exp $ */ /* $NetBSD: subr_prof.c,v 1.12 1996/04/22 01:38:50 christos Exp $ */ /*- @@ -41,12 +41,15 @@ #include <sys/syscallargs.h> -#ifdef GPROF +#if defined(GPROF) || defined(DDBPROF) #include <sys/malloc.h> #include <sys/gmon.h> #include <uvm/uvm_extern.h> +#include <machine/db_machdep.h> +#include <ddb/db_extern.h> + /* * Flag to prevent CPUs from executing the mcount() monitor function * until we're sure they are in a sane state. @@ -56,7 +59,7 @@ int gmoninit = 0; extern char etext[]; void -kmstartup(void) +prof_init(void) { CPU_INFO_ITERATOR cii; struct cpu_info *ci; @@ -67,6 +70,10 @@ kmstartup(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. @@ -74,8 +81,10 @@ kmstartup(void) lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER)); highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER)); textsize = highpc - lowpc; +#ifdef GPROF printf("Profiling kernel, textsize=%ld [%lx..%lx]\n", textsize, lowpc, highpc); +#endif kcountsize = textsize / HISTFRACTION; fromssize = textsize / HASHFRACTION; tolimit = textsize * ARCDENSITY / 100; @@ -116,6 +125,41 @@ kmstartup(void) } } +int +prof_state_toggle(struct gmonparam *gp, int oldstate) +{ + int error = 0; + + if (gp->state == oldstate) + return (0); + + switch (gp->state) { + case GMON_PROF_ON: +#if !defined(GPROF) + /* + * If this is not a profiling kernel, we need to patch + * all symbols that can be instrummented. + */ + error = db_prof_enable(); +#endif + if (error == 0) + startprofclock(&process0); + break; + default: + error = EINVAL; + gp->state = GMON_PROF_OFF; + /* FALLTHROUGH */ + case GMON_PROF_OFF: + stopprofclock(&process0); +#if !defined(GPROF) + db_prof_disable(); +#endif + break; + } + + return (error); +} + /* * Return kernel profiling information. */ @@ -126,7 +170,7 @@ sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, CPU_INFO_ITERATOR cii; struct cpu_info *ci; struct gmonparam *gp = NULL; - int error, cpuid, op; + int error, cpuid, op, state; /* all sysctl names at this level are name and field */ if (namelen != 2) @@ -150,14 +194,11 @@ sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, switch (op) { case GPROF_STATE: + state = gp->state; error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state); if (error) return (error); - if (gp->state == GMON_PROF_OFF) - stopprofclock(&process0); - else - startprofclock(&process0); - return (0); + return (prof_state_toggle(gp, state)); case GPROF_COUNT: return (sysctl_struct(oldp, oldlenp, newp, newlen, gp->kcount, gp->kcountsize)); @@ -174,7 +215,7 @@ sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, } /* NOTREACHED */ } -#endif /* GPROF */ +#endif /* GPROF || DDBPROF */ /* * Profiling system call. diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 04ec9e8fcbd..d66d7d5aa26 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sysctl.h,v 1.163 2016/05/27 19:45:04 deraadt Exp $ */ +/* $OpenBSD: sysctl.h,v 1.164 2016/09/04 09:22:29 mpi Exp $ */ /* $NetBSD: sysctl.h,v 1.16 1996/04/09 20:55:36 cgd Exp $ */ /* @@ -938,7 +938,7 @@ int sysctl_dumpentry(struct rtentry *, void *, unsigned int); int sysctl_rtable(int *, u_int, void *, size_t *, void *, size_t); int sysctl_clockrate(char *, size_t *, void *); int sysctl_vnode(char *, size_t *, struct proc *); -#ifdef GPROF +#if defined(GPROF) || defined(DDBPROF) int sysctl_doprof(int *, u_int, void *, size_t *, void *, size_t); #endif int sysctl_dopool(int *, u_int, char *, size_t *); diff --git a/sys/sys/systm.h b/sys/sys/systm.h index 90ac6743c80..374805b91b8 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -1,4 +1,4 @@ -/* $OpenBSD: systm.h,v 1.115 2016/09/03 14:46:56 naddy Exp $ */ +/* $OpenBSD: systm.h,v 1.116 2016/09/04 09:22:29 mpi Exp $ */ /* $NetBSD: systm.h,v 1.50 1996/06/09 04:55:09 briggs Exp $ */ /*- @@ -297,10 +297,6 @@ void cpu_startup(void); void cpu_configure(void); void diskconf(void); -#ifdef GPROF -void kmstartup(void); -#endif - int nfs_mountroot(void); int dk_mountroot(void); extern int (*mountroot)(void); |