summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2013-03-12 09:37:17 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2013-03-12 09:37:17 +0000
commitf0edf71d0715678b64251c85b679f3192b0020d8 (patch)
treeecfdecfd831ac54a526ebf9f664903cfddf09372
parent22d6176c7a5b0d9982db2ad3b4a2e6befb249d3b (diff)
Fix kernel profiling on MP systems by using per-CPU buffers and teach
kgmon(8) to deal with them, this time without public header changes. Previously various CPUs were iterating over the same global buffer at the same time to modify it and never ended. This diff includes some ideas submited by Thor Simon to NetBSD via miod@. ok deraadt@, mikeb@, haesbaert@
-rw-r--r--lib/libc/gmon/mcount.c22
-rw-r--r--sys/arch/alpha/include/cpu.h5
-rw-r--r--sys/arch/amd64/include/cpu.h5
-rw-r--r--sys/arch/arm/include/cpu.h5
-rw-r--r--sys/arch/hppa/include/cpu.h5
-rw-r--r--sys/arch/hppa64/include/cpu.h5
-rw-r--r--sys/arch/i386/include/cpu.h5
-rw-r--r--sys/arch/m68k/include/cpu.h5
-rw-r--r--sys/arch/m88k/include/cpu.h5
-rw-r--r--sys/arch/mips64/include/cpu.h5
-rw-r--r--sys/arch/powerpc/include/cpu.h5
-rw-r--r--sys/arch/sh/include/cpu.h5
-rw-r--r--sys/arch/sparc64/include/cpu.h5
-rw-r--r--sys/arch/vax/include/cpu.h5
-rw-r--r--sys/kern/kern_clock.c6
-rw-r--r--sys/kern/subr_prof.c107
-rw-r--r--sys/lib/libkern/mcount.c24
-rw-r--r--sys/sys/gmon.h6
-rw-r--r--usr.sbin/kgmon/kgmon.814
-rw-r--r--usr.sbin/kgmon/kgmon.c144
20 files changed, 268 insertions, 120 deletions
diff --git a/lib/libc/gmon/mcount.c b/lib/libc/gmon/mcount.c
index e7619c7599b..942d8c1887f 100644
--- a/lib/libc/gmon/mcount.c
+++ b/lib/libc/gmon/mcount.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mcount.c,v 1.13 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: mcount.c,v 1.14 2013/03/12 09:37:16 mpi Exp $ */
/*-
* Copyright (c) 1983, 1992, 1993
* The Regents of the University of California. All rights reserved.
@@ -41,10 +41,6 @@
* _mcount updates data structures that represent traversals of the
* program's call graph edges. frompc and selfpc are the return
* address and function address that represents the given call graph edge.
- *
- * Note: the original BSD code used the same variable (frompcindex) for
- * both frompcindex and frompc. Any reasonable, modern compiler will
- * perform this optimization.
*/
_MCOUNT_DECL(u_long frompc, u_long selfpc) __used;
/* _mcount; may be static, inline, etc */
@@ -56,9 +52,21 @@ _MCOUNT_DECL(u_long frompc, u_long selfpc)
long toindex;
#ifdef _KERNEL
int s;
-#endif
+ /*
+ * Do not profile execution if memory for the current CPU
+ * desciptor and profiling buffers has not yet been allocated
+ * or if the CPU we are running on has not yet set its trap
+ * handler.
+ */
+ if (gmoninit == 0)
+ return;
+
+ if ((p = curcpu()->ci_gmon) == NULL)
+ return;
+#else
p = &_gmonparam;
+#endif
/*
* check that we are profiling
* and that we aren't recursively invoked.
@@ -171,10 +179,8 @@ overflow:
return;
}
-#ifndef lint
/*
* Actual definition of mcount function. Defined in <machine/profile.h>,
* which is included by <sys/gmon.h>.
*/
MCOUNT
-#endif
diff --git a/sys/arch/alpha/include/cpu.h b/sys/arch/alpha/include/cpu.h
index 6ce34749606..d43c510d202 100644
--- a/sys/arch/alpha/include/cpu.h
+++ b/sys/arch/alpha/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.48 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.49 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.45 2000/08/21 02:03:12 thorpej Exp $ */
/*-
@@ -203,6 +203,9 @@ struct cpu_info {
u_long ci_ipis; /* interprocessor interrupts pending */
#endif
u_int32_t ci_randseed;
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
#define CPUF_PRIMARY 0x01 /* CPU is primary CPU */
diff --git a/sys/arch/amd64/include/cpu.h b/sys/arch/amd64/include/cpu.h
index 61e9889d71f..c9977c895bb 100644
--- a/sys/arch/amd64/include/cpu.h
+++ b/sys/arch/amd64/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.78 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.79 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.1 2003/04/26 18:39:39 fvdl Exp $ */
/*-
@@ -128,6 +128,9 @@ struct cpu_info {
struct ksensordev ci_sensordev;
struct ksensor ci_sensor;
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
#define CPUF_BSP 0x0001 /* CPU is the original BSP */
diff --git a/sys/arch/arm/include/cpu.h b/sys/arch/arm/include/cpu.h
index 7e1ca74122c..5da7d8333bc 100644
--- a/sys/arch/arm/include/cpu.h
+++ b/sys/arch/arm/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.33 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.34 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.34 2003/06/23 11:01:08 martin Exp $ */
/*
@@ -198,6 +198,9 @@ struct cpu_info {
uint32_t ci_cpl;
uint32_t ci_ipending;
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
#ifndef MULTIPROCESSOR
diff --git a/sys/arch/hppa/include/cpu.h b/sys/arch/hppa/include/cpu.h
index b477fc6b15a..536ae85f1fb 100644
--- a/sys/arch/hppa/include/cpu.h
+++ b/sys/arch/hppa/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.83 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.84 2013/03/12 09:37:16 mpi Exp $ */
/*
* Copyright (c) 2000-2004 Michael Shalayeff
@@ -103,6 +103,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
} __attribute__((__aligned__(64)));
#define CPUF_RUNNING 0x0001 /* CPU is running. */
diff --git a/sys/arch/hppa64/include/cpu.h b/sys/arch/hppa64/include/cpu.h
index 509a8a21b14..193a1bc7588 100644
--- a/sys/arch/hppa64/include/cpu.h
+++ b/sys/arch/hppa64/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.30 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.31 2013/03/12 09:37:16 mpi Exp $ */
/*
* Copyright (c) 2005 Michael Shalayeff
@@ -109,6 +109,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
struct cpu_info *curcpu(void);
diff --git a/sys/arch/i386/include/cpu.h b/sys/arch/i386/include/cpu.h
index 2fe58c1d2a5..99a7448a763 100644
--- a/sys/arch/i386/include/cpu.h
+++ b/sys/arch/i386/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.127 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.128 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.35 1996/05/05 19:29:26 christos Exp $ */
/*-
@@ -148,6 +148,9 @@ struct cpu_info {
struct ksensordev ci_sensordev;
struct ksensor ci_sensor;
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
/*
diff --git a/sys/arch/m68k/include/cpu.h b/sys/arch/m68k/include/cpu.h
index bac5eedc662..02c99c399a7 100644
--- a/sys/arch/m68k/include/cpu.h
+++ b/sys/arch/m68k/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.28 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.29 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.3 1997/02/02 06:56:57 thorpej Exp $ */
/*
@@ -59,6 +59,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
extern struct cpu_info cpu_info_store;
diff --git a/sys/arch/m88k/include/cpu.h b/sys/arch/m88k/include/cpu.h
index f5055a5463e..a9c7089d92d 100644
--- a/sys/arch/m88k/include/cpu.h
+++ b/sys/arch/m88k/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.58 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.59 2013/03/12 09:37:16 mpi Exp $ */
/*
* Copyright (c) 1996 Nivas Madhur
* Copyright (c) 1992, 1993
@@ -172,6 +172,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
extern cpuid_t master_cpu;
diff --git a/sys/arch/mips64/include/cpu.h b/sys/arch/mips64/include/cpu.h
index c7ba15de925..7c036339265 100644
--- a/sys/arch/mips64/include/cpu.h
+++ b/sys/arch/mips64/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.93 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.94 2013/03/12 09:37:16 mpi Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -198,6 +198,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
#define CPUF_PRIMARY 0x01 /* CPU is primary CPU */
diff --git a/sys/arch/powerpc/include/cpu.h b/sys/arch/powerpc/include/cpu.h
index a9561d6e820..3f47bdc7953 100644
--- a/sys/arch/powerpc/include/cpu.h
+++ b/sys/arch/powerpc/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.50 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.51 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.1 1996/09/30 16:34:21 ws Exp $ */
/*
@@ -85,6 +85,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
static __inline struct cpu_info *
diff --git a/sys/arch/sh/include/cpu.h b/sys/arch/sh/include/cpu.h
index e313d701d22..31959429bd7 100644
--- a/sys/arch/sh/include/cpu.h
+++ b/sys/arch/sh/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.24 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.25 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.41 2006/01/21 04:24:12 uwe Exp $ */
/*-
@@ -65,6 +65,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
extern struct cpu_info cpu_info_store;
diff --git a/sys/arch/sparc64/include/cpu.h b/sys/arch/sparc64/include/cpu.h
index 7b5e4fee0a0..e1bca2f7fa1 100644
--- a/sys/arch/sparc64/include/cpu.h
+++ b/sys/arch/sparc64/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.81 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.82 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.28 2001/06/14 22:56:58 thorpej Exp $ */
/*
@@ -156,6 +156,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
#define CPUF_RUNNING 0x0001 /* CPU is running */
diff --git a/sys/arch/vax/include/cpu.h b/sys/arch/vax/include/cpu.h
index 509556a3358..6284fd9e230 100644
--- a/sys/arch/vax/include/cpu.h
+++ b/sys/arch/vax/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.44 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: cpu.h,v 1.45 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: cpu.h,v 1.41 1999/10/21 20:01:36 ragge Exp $ */
/*
@@ -55,6 +55,9 @@ struct cpu_info {
#ifdef DIAGNOSTIC
int ci_mutex_level;
#endif
+#ifdef GPROF
+ struct gmonparam *ci_gmon;
+#endif
};
extern struct cpu_info cpu_info_store;
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
index d7d51646c2c..7aec79d3e69 100644
--- a/sys/kern/kern_clock.c
+++ b/sys/kern/kern_clock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_clock.c,v 1.78 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: kern_clock.c,v 1.79 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: kern_clock.c,v 1.34 1996/06/09 04:51:03 briggs Exp $ */
/*-
@@ -421,8 +421,8 @@ statclock(struct clockframe *frame)
/*
* Kernel statistics are just like addupc_intr, only easier.
*/
- g = &_gmonparam;
- if (g->state == GMON_PROF_ON) {
+ g = ci->ci_gmon;
+ if (g != NULL && g->state == GMON_PROF_ON) {
i = CLKF_PC(frame) - g->lowpc;
if (i < g->textsize) {
i /= HISTFRACTION * sizeof(*g->kcount);
diff --git a/sys/kern/subr_prof.c b/sys/kern/subr_prof.c
index 3786cb11df8..5289ab7f8b4 100644
--- a/sys/kern/subr_prof.c
+++ b/sys/kern/subr_prof.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_prof.c,v 1.23 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: subr_prof.c,v 1.24 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: subr_prof.c,v 1.12 1996/04/22 01:38:50 christos Exp $ */
/*-
@@ -49,49 +49,72 @@
#include <uvm/uvm_extern.h>
/*
- * Froms is actually a bunch of unsigned shorts indexing tos
+ * Flag to prevent CPUs from executing the mcount() monitor function
+ * until we're sure they are in a sane state.
*/
-struct gmonparam _gmonparam = { GMON_PROF_OFF };
+int gmoninit = 0;
extern char etext[];
-
void
kmstartup(void)
{
+ CPU_INFO_ITERATOR cii;
+ struct cpu_info *ci;
+ struct gmonparam *p;
+ u_long lowpc, highpc, textsize;
+ u_long kcountsize, fromssize, tossize;
+ long tolimit;
char *cp;
- struct gmonparam *p = &_gmonparam;
int size;
/*
* 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.
*/
- p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
- p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
- p->textsize = p->highpc - p->lowpc;
+ lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
+ highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
+ textsize = highpc - lowpc;
printf("Profiling kernel, textsize=%ld [%lx..%lx]\n",
- p->textsize, p->lowpc, p->highpc);
- p->kcountsize = p->textsize / HISTFRACTION;
- p->hashfraction = HASHFRACTION;
- p->fromssize = p->textsize / HASHFRACTION;
- p->tolimit = p->textsize * ARCDENSITY / 100;
- if (p->tolimit < MINARCS)
- p->tolimit = MINARCS;
- else if (p->tolimit > MAXARCS)
- p->tolimit = MAXARCS;
- p->tossize = p->tolimit * sizeof(struct tostruct);
- size = p->kcountsize + p->fromssize + p->tossize;
- cp = (char *)uvm_km_zalloc(kernel_map, round_page(size));
- if (cp == 0) {
- printf("No memory for profiling.\n");
- return;
+ textsize, lowpc, highpc);
+ kcountsize = textsize / HISTFRACTION;
+ fromssize = textsize / HASHFRACTION;
+ tolimit = textsize * ARCDENSITY / 100;
+ if (tolimit < MINARCS)
+ tolimit = MINARCS;
+ else if (tolimit > MAXARCS)
+ tolimit = MAXARCS;
+ tossize = tolimit * sizeof(struct tostruct);
+ size = sizeof(*p) + kcountsize + fromssize + tossize;
+
+ /* Allocate and initialize one profiling buffer per CPU. */
+ CPU_INFO_FOREACH(cii, ci) {
+ cp = km_alloc(round_page(size), &kv_any, &kp_zero, &kd_nowait);
+ if (cp == NULL) {
+ printf("No memory for profiling.\n");
+ return;
+ }
+
+ p = (struct gmonparam *)cp;
+ cp += sizeof(*p);
+ p->tos = (struct tostruct *)cp;
+ cp += tossize;
+ p->kcount = (u_short *)cp;
+ cp += kcountsize;
+ p->froms = (u_short *)cp;
+
+ p->state = GMON_PROF_OFF;
+ p->lowpc = lowpc;
+ p->highpc = highpc;
+ p->textsize = textsize;
+ p->hashfraction = HASHFRACTION;
+ p->kcountsize = kcountsize;
+ p->fromssize = fromssize;
+ p->tolimit = tolimit;
+ p->tossize = tossize;
+
+ ci->ci_gmon = p;
}
- p->tos = (struct tostruct *)cp;
- cp += p->tossize;
- p->kcount = (u_short *)cp;
- cp += p->kcountsize;
- p->froms = (u_short *)cp;
}
/*
@@ -101,14 +124,32 @@ int
sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
- struct gmonparam *gp = &_gmonparam;
- int error;
+ CPU_INFO_ITERATOR cii;
+ struct cpu_info *ci;
+ struct gmonparam *gp = NULL;
+ int error, cpuid, op;
- /* all sysctl names at this level are terminal */
- if (namelen != 1)
+ /* all sysctl names at this level are name and field */
+ if (namelen != 2)
return (ENOTDIR); /* overloaded */
- switch (name[0]) {
+ op = name[0];
+ cpuid = name[1];
+
+ CPU_INFO_FOREACH(cii, ci) {
+ if (cpuid == CPU_INFO_UNIT(ci)) {
+ gp = ci->ci_gmon;
+ break;
+ }
+ }
+
+ if (gp == NULL)
+ return (EOPNOTSUPP);
+
+ /* Assume that if we're here it is safe to execute profiling. */
+ gmoninit = 1;
+
+ switch (op) {
case GPROF_STATE:
error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
if (error)
diff --git a/sys/lib/libkern/mcount.c b/sys/lib/libkern/mcount.c
index c66314cefe0..684ddadb9aa 100644
--- a/sys/lib/libkern/mcount.c
+++ b/sys/lib/libkern/mcount.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mcount.c,v 1.12 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: mcount.c,v 1.13 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: mcount.c,v 1.3.6.1 1996/06/12 04:23:01 cgd Exp $ */
/*-
@@ -43,13 +43,10 @@
* _mcount updates data structures that represent traversals of the
* program's call graph edges. frompc and selfpc are the return
* address and function address that represents the given call graph edge.
- *
- * Note: the original BSD code used the same variable (frompcindex) for
- * both frompcindex and frompc. Any reasonable, modern compiler will
- * perform this optimization.
*/
_MCOUNT_DECL(u_long frompc, u_long selfpc) __used;
-_MCOUNT_DECL(u_long frompc, u_long selfpc) /* _mcount; may be static, inline, etc */
+/* _mcount; may be static, inline, etc */
+_MCOUNT_DECL(u_long frompc, u_long selfpc)
{
u_short *frompcindex;
struct tostruct *top, *prevtop;
@@ -57,9 +54,21 @@ _MCOUNT_DECL(u_long frompc, u_long selfpc) /* _mcount; may be static, inline, et
long toindex;
#ifdef _KERNEL
int s;
-#endif
+ /*
+ * Do not profile execution if memory for the current CPU
+ * desciptor and profiling buffers has not yet been allocated
+ * or if the CPU we are running on has not yet set its trap
+ * handler.
+ */
+ if (gmoninit == 0)
+ return;
+
+ if ((p = curcpu()->ci_gmon) == NULL)
+ return;
+#else
p = &_gmonparam;
+#endif
/*
* check that we are profiling
* and that we aren't recursively invoked.
@@ -156,7 +165,6 @@ _MCOUNT_DECL(u_long frompc, u_long selfpc) /* _mcount; may be static, inline, et
*frompcindex = toindex;
goto done;
}
-
}
done:
#ifdef _KERNEL
diff --git a/sys/sys/gmon.h b/sys/sys/gmon.h
index 192b7c7ebbc..303d756a327 100644
--- a/sys/sys/gmon.h
+++ b/sys/sys/gmon.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: gmon.h,v 1.6 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: gmon.h,v 1.7 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: gmon.h,v 1.5 1996/04/09 20:55:30 cgd Exp $ */
/*-
@@ -137,7 +137,11 @@ struct gmonparam {
u_long textsize;
u_long hashfraction;
};
+#ifdef _KERNEL
+extern int gmoninit; /* Is the kernel ready for beeing profiled? */
+#else
extern struct gmonparam _gmonparam;
+#endif
/*
* Possible states of profiling.
diff --git a/usr.sbin/kgmon/kgmon.8 b/usr.sbin/kgmon/kgmon.8
index 6f12062f5c5..f3229800a46 100644
--- a/usr.sbin/kgmon/kgmon.8
+++ b/usr.sbin/kgmon/kgmon.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: kgmon.8,v 1.12 2013/02/12 08:06:22 mpi Exp $
+.\" $OpenBSD: kgmon.8,v 1.13 2013/03/12 09:37:16 mpi Exp $
.\" Copyright (c) 1983, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
@@ -27,9 +27,8 @@
.\" SUCH DAMAGE.
.\"
.\" from: @(#)kgmon.8 8.1 (Berkeley) 6/6/93
-.\" $Id: kgmon.8,v 1.12 2013/02/12 08:06:22 mpi Exp $
.\"
-.Dd $Mdocdate: February 12 2013 $
+.Dd $Mdocdate: March 12 2013 $
.Dt KGMON 8
.Os
.Sh NAME
@@ -38,6 +37,7 @@
.Sh SYNOPSIS
.Nm kgmon
.Op Fl bhpr
+.Op Fl c Ar cpuid
.Op Fl M Ar core
.Op Fl N Ar system
.Sh DESCRIPTION
@@ -45,7 +45,7 @@
is a tool used when profiling the operating system.
When no arguments are supplied,
.Nm kgmon
-indicates the state of operating system profiling as
+indicates the state of per-CPU operating system profilings as
.Dq running ,
.Dq off ,
or
@@ -117,10 +117,8 @@ Users with only read permission on
.Pa /dev/kmem
cannot change the state
of profiling collection.
-They can get a
-.Pa gmon.out
-file with the warning that the data may be
-inconsistent if profiling is in progress.
+They can get profile files with the warning that the data may be inconsistent
+if profiling is in progress.
.Sh SEE ALSO
.Xr gprof 1 ,
.Xr config 8
diff --git a/usr.sbin/kgmon/kgmon.c b/usr.sbin/kgmon/kgmon.c
index 82b9700f98f..eced000d57a 100644
--- a/usr.sbin/kgmon/kgmon.c
+++ b/usr.sbin/kgmon/kgmon.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kgmon.c,v 1.17 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: kgmon.c,v 1.18 2013/03/12 09:37:16 mpi Exp $ */
/*
* Copyright (c) 1983, 1992, 1993
@@ -58,28 +58,32 @@ struct kvmvars {
struct gmonparam gpm;
};
-int bflag, hflag, kflag, rflag, pflag;
+extern char *__progname;
+
+int bflag, cflag, hflag, kflag, rflag, pflag;
int debug = 0;
-void setprof(struct kvmvars *, int);
-void dumpstate(struct kvmvars *);
-void reset(struct kvmvars *);
+void kgmon(char *, char *, struct kvmvars *, int);
+void setprof(struct kvmvars *, int, int);
+void dumpstate(struct kvmvars *, int);
+void reset(struct kvmvars *, int);
void kern_readonly(int);
-int getprof(struct kvmvars *);
+int getprof(struct kvmvars *, int);
int getprofhz(struct kvmvars *);
-int openfiles(char *, char *, struct kvmvars *);
+int openfiles(char *, char *, struct kvmvars *, int);
+int getncpu(void);
int
main(int argc, char **argv)
{
- extern char *__progname;
- int ch, mode, disp, accessmode;
+ int ch, err, ncpu, cpuid = -1;
struct kvmvars kvmvars;
char *sys, *kmemf;
+ const char *p;
seteuid(getuid());
kmemf = NULL;
sys = NULL;
- while ((ch = getopt(argc, argv, "M:N:bhpr")) != -1) {
+ while ((ch = getopt(argc, argv, "M:N:bc:hpr")) != -1) {
switch((char)ch) {
case 'M':
@@ -95,6 +99,13 @@ main(int argc, char **argv)
bflag = 1;
break;
+ case 'c':
+ cflag = 1;
+ cpuid = strtonum(optarg, 0, 1024, &p);
+ if (p)
+ errx(1, "illegal CPU id %s: %s", optarg, p);
+ break;
+
case 'h':
hflag = 1;
break;
@@ -108,9 +119,8 @@ main(int argc, char **argv)
break;
default:
- fprintf(stderr,
- "usage: %s [-bhpr] [-M core] [-N system]\n",
- __progname);
+ fprintf(stderr, "usage: %s [-bhpr] "
+ "[-c cpuid] [-M core] [-N system]\n", __progname);
exit(1);
}
}
@@ -127,8 +137,25 @@ main(int argc, char **argv)
}
}
#endif
- accessmode = openfiles(sys, kmemf, &kvmvars);
- mode = getprof(&kvmvars);
+
+ if (cflag) {
+ kgmon(sys, kmemf, &kvmvars, cpuid);
+ } else {
+ ncpu = getncpu();
+ for (cpuid = 0; cpuid < ncpu; cpuid++)
+ kgmon(sys, kmemf, &kvmvars, cpuid);
+ }
+
+ return (0);
+}
+
+void
+kgmon(char *sys, char *kmemf, struct kvmvars *kvp, int cpuid)
+{
+ int mode, disp, accessmode;
+
+ accessmode = openfiles(sys, kmemf, kvp, cpuid);
+ mode = getprof(kvp, cpuid);
if (hflag)
disp = GMON_PROF_OFF;
else if (bflag)
@@ -136,23 +163,22 @@ main(int argc, char **argv)
else
disp = mode;
if (pflag)
- dumpstate(&kvmvars);
+ dumpstate(kvp, cpuid);
if (rflag)
- reset(&kvmvars);
+ reset(kvp, cpuid);
if (accessmode == O_RDWR)
- setprof(&kvmvars, disp);
- printf("%s: kernel profiling is %s.\n", __progname,
- disp == GMON_PROF_OFF ? "off" : "running");
- return (0);
+ setprof(kvp, cpuid, disp);
+ printf("%s: kernel profiling is %s for cpu %d.\n", __progname,
+ disp == GMON_PROF_OFF ? "off" : "running", cpuid);
}
/*
* Check that profiling is enabled and open any ncessary files.
*/
int
-openfiles(char *sys, char *kmemf, struct kvmvars *kvp)
+openfiles(char *sys, char *kmemf, struct kvmvars *kvp, int cpuid)
{
- int mib[3], state, openmode;
+ int mib[4], state, openmode;
size_t size;
char errbuf[_POSIX2_LINE_MAX];
@@ -160,14 +186,15 @@ openfiles(char *sys, char *kmemf, struct kvmvars *kvp)
mib[0] = CTL_KERN;
mib[1] = KERN_PROF;
mib[2] = GPROF_STATE;
+ mib[3] = cpuid;
size = sizeof state;
- if (sysctl(mib, 3, &state, &size, NULL, 0) < 0)
+ if (sysctl(mib, 4, &state, &size, NULL, 0) < 0)
errx(20, "profiling not defined in kernel.");
if (!(bflag || hflag || rflag ||
(pflag && state == GMON_PROF_ON)))
return (O_RDONLY);
(void)seteuid(0);
- if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
+ if (sysctl(mib, 4, NULL, NULL, &state, size) >= 0)
return (O_RDWR);
(void)seteuid(getuid());
kern_readonly(state);
@@ -216,9 +243,9 @@ kern_readonly(int mode)
* Get the state of kernel profiling.
*/
int
-getprof(struct kvmvars *kvp)
+getprof(struct kvmvars *kvp, int cpuid)
{
- int mib[3];
+ int mib[4];
size_t size;
if (kflag) {
@@ -228,8 +255,9 @@ getprof(struct kvmvars *kvp)
mib[0] = CTL_KERN;
mib[1] = KERN_PROF;
mib[2] = GPROF_GMONPARAM;
+ mib[3] = cpuid;
size = sizeof kvp->gpm;
- if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
+ if (sysctl(mib, 4, &kvp->gpm, &size, NULL, 0) < 0)
size = 0;
}
if (size != sizeof kvp->gpm)
@@ -242,10 +270,10 @@ getprof(struct kvmvars *kvp)
* Enable or disable kernel profiling according to the state variable.
*/
void
-setprof(struct kvmvars *kvp, int state)
+setprof(struct kvmvars *kvp, int cpuid, int state)
{
struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
- int mib[3], oldstate;
+ int mib[4], oldstate;
size_t sz;
sz = sizeof(state);
@@ -253,12 +281,13 @@ setprof(struct kvmvars *kvp, int state)
mib[0] = CTL_KERN;
mib[1] = KERN_PROF;
mib[2] = GPROF_STATE;
- if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
+ mib[3] = cpuid;
+ if (sysctl(mib, 4, &oldstate, &sz, NULL, 0) < 0)
goto bad;
if (oldstate == state)
return;
(void)seteuid(0);
- if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
+ if (sysctl(mib, 4, NULL, NULL, &state, sz) >= 0) {
(void)seteuid(getuid());
return;
}
@@ -275,22 +304,25 @@ bad:
* Build the gmon.out file.
*/
void
-dumpstate(struct kvmvars *kvp)
+dumpstate(struct kvmvars *kvp, int cpuid)
{
FILE *fp;
struct rawarc rawarc;
struct tostruct *tos;
u_long frompc;
u_short *froms, *tickbuf;
- int mib[3];
+ int mib[4];
size_t i;
struct gmonhdr h;
int fromindex, endfrom, toindex;
+ char buf[16];
- setprof(kvp, GMON_PROF_OFF);
- fp = fopen("gmon.out", "w");
+ snprintf(buf, sizeof(buf), "gmon-%02d.out", cpuid);
+
+ setprof(kvp, cpuid, GMON_PROF_OFF);
+ fp = fopen(buf, "w");
if (fp == 0) {
- perror("gmon.out");
+ perror(buf);
return;
}
@@ -317,8 +349,9 @@ dumpstate(struct kvmvars *kvp)
kvp->gpm.kcountsize);
} else {
mib[2] = GPROF_COUNT;
+ mib[3] = cpuid;
i = kvp->gpm.kcountsize;
- if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
+ if (sysctl(mib, 4, tickbuf, &i, NULL, 0) < 0)
i = 0;
}
if (i != kvp->gpm.kcountsize)
@@ -339,8 +372,9 @@ dumpstate(struct kvmvars *kvp)
kvp->gpm.fromssize);
} else {
mib[2] = GPROF_FROMS;
+ mib[3] = cpuid;
i = kvp->gpm.fromssize;
- if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
+ if (sysctl(mib, 4, froms, &i, NULL, 0) < 0)
i = 0;
}
if (i != kvp->gpm.fromssize)
@@ -354,8 +388,9 @@ dumpstate(struct kvmvars *kvp)
kvp->gpm.tossize);
} else {
mib[2] = GPROF_TOS;
+ mib[3] = cpuid;
i = kvp->gpm.tossize;
- if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
+ if (sysctl(mib, 4, tos, &i, NULL, 0) < 0)
i = 0;
}
if (i != kvp->gpm.tossize)
@@ -415,13 +450,13 @@ getprofhz(struct kvmvars *kvp)
* Reset the kernel profiling date structures.
*/
void
-reset(struct kvmvars *kvp)
+reset(struct kvmvars *kvp, int cpuid)
{
char *zbuf;
u_long biggest;
- int mib[3];
+ int mib[4];
- setprof(kvp, GMON_PROF_OFF);
+ setprof(kvp, cpuid, GMON_PROF_OFF);
biggest = kvp->gpm.kcountsize;
if (kvp->gpm.fromssize > biggest)
@@ -447,14 +482,31 @@ reset(struct kvmvars *kvp)
mib[0] = CTL_KERN;
mib[1] = KERN_PROF;
mib[2] = GPROF_COUNT;
- if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0)
+ mib[3] = cpuid;
+ if (sysctl(mib, 4, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0)
err(13, "tickbuf zero");
mib[2] = GPROF_FROMS;
- if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0)
+ if (sysctl(mib, 4, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0)
err(14, "froms zero");
mib[2] = GPROF_TOS;
- if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0)
+ if (sysctl(mib, 4, NULL, NULL, zbuf, kvp->gpm.tossize) < 0)
err(15, "tos zero");
(void)seteuid(getuid());
free(zbuf);
}
+
+int
+getncpu(void)
+{
+ int mib[2] = { CTL_HW, HW_NCPU };
+ size_t size;
+ int ncpu;
+
+ size = sizeof(ncpu);
+ if (sysctl(mib, 2, &ncpu, &size, NULL, 0) < 0) {
+ warnx("cannot read hw.ncpu");
+ return (1);
+ }
+
+ return (ncpu);
+}