summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormpi <mpi@openbsd.org>2016-09-04 09:22:28 +0000
committermpi <mpi@openbsd.org>2016-09-04 09:22:28 +0000
commit6950c8e205c43756d30d56ca678aa8205e9896fa (patch)
tree520bceb313719cacbfa77f438a8dc2fe9bf4a4f0
parentsync (diff)
downloadwireguard-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.c16
-rw-r--r--sys/arch/amd64/amd64/locore.S45
-rw-r--r--sys/arch/amd64/amd64/vector.S19
-rw-r--r--sys/arch/amd64/include/cpu.h4
-rw-r--r--sys/arch/amd64/include/cpufunc.h18
-rw-r--r--sys/arch/amd64/include/db_machdep.h5
-rw-r--r--sys/arch/amd64/include/frameasm.h3
-rw-r--r--sys/conf/GENERIC3
-rw-r--r--sys/conf/files3
-rw-r--r--sys/ddb/db_extern.h7
-rw-r--r--sys/ddb/db_prof.c307
-rw-r--r--sys/ddb/db_usrreq.c20
-rw-r--r--sys/ddb/db_var.h7
-rw-r--r--sys/kern/init_main.c7
-rw-r--r--sys/kern/kern_clock.c8
-rw-r--r--sys/kern/kern_sysctl.c4
-rw-r--r--sys/kern/subr_prof.c61
-rw-r--r--sys/sys/sysctl.h4
-rw-r--r--sys/sys/systm.h6
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);