summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/prof_machdep.c
diff options
context:
space:
mode:
authorbde <bde@FreeBSD.org>1995-12-29 15:30:05 +0000
committerbde <bde@FreeBSD.org>1995-12-29 15:30:05 +0000
commit586cc683d875b37dce82c825feb9ccc7d884b35e (patch)
tree9ce1e55534d3d930aead3ff55aeb7fcedbc086a4 /sys/i386/isa/prof_machdep.c
parentff6f507f6bbb3fda77fb14c7201db37bafea7a3f (diff)
downloadFreeBSD-src-586cc683d875b37dce82c825feb9ccc7d884b35e.zip
FreeBSD-src-586cc683d875b37dce82c825feb9ccc7d884b35e.tar.gz
Implemented non-statistical kernel profiling. This is based on
looking at a high resolution clock for each of the following events: function call, function return, interrupt entry, interrupt exit, and interesting branches. The differences between the times of these events are added at appropriate places in a ordinary histogram (as if very fast statistical profiling sampled the pc at those places) so that ordinary gprof can be used to analyze the times. gmon.h: Histogram counters need to be 4 bytes for microsecond resolutions. They will need to be larger for the 586 clock. The comments were vax-centric and wrong even on vaxes. Does anyone disagree? gprof4.c: The standard gprof should support counters of all integral sizes and the size of the counter should be in the gmon header. This hack will do until then. (Use gprof4 -u to examine the results of non-statistical profiling.) config/*: Non-statistical profiling is configured with `config -pp'. `config -p' still gives ordinary profiling. kgmon/*: Non-statistical profiling is enabled with `kgmon -B'. `kgmon -b' still enables ordinary profiling (and distables non-statistical profiling) if non-statistical profiling is configured.
Diffstat (limited to 'sys/i386/isa/prof_machdep.c')
-rw-r--r--sys/i386/isa/prof_machdep.c153
1 files changed, 153 insertions, 0 deletions
diff --git a/sys/i386/isa/prof_machdep.c b/sys/i386/isa/prof_machdep.c
new file mode 100644
index 0000000..2aa6787
--- /dev/null
+++ b/sys/i386/isa/prof_machdep.c
@@ -0,0 +1,153 @@
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <machine/clock.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/timerreg.h>
+
+#ifdef GUPROF
+extern u_int cputime __P((void));
+#endif
+
+#ifdef __GNUC__
+asm("
+GM_STATE = 0
+GMON_PROF_OFF = 3
+
+ .text
+ .align 4,0x90
+ .globl __mcount
+__mcount:
+ #
+ # Check that we are profiling. Do it early for speed.
+ #
+ cmpl $GMON_PROF_OFF,__gmonparam+GM_STATE
+ je Lmcount_exit
+ #
+ # __mcount is the same as mcount except the caller hasn't changed
+ # the stack except to call here, so the caller's raddr is above
+ # our raddr.
+ #
+ movl 4(%esp),%edx
+ jmp Lgot_frompc
+
+ .align 4,0x90
+ .globl mcount
+mcount:
+ cmpl $GMON_PROF_OFF,__gmonparam+GM_STATE
+ je Lmcount_exit
+ #
+ # The caller's stack frame has already been built, so %ebp is
+ # the caller's frame pointer. The caller's raddr is in the
+ # caller's frame following the caller's caller's frame pointer.
+ #
+ movl 4(%ebp),%edx
+Lgot_frompc:
+ #
+ # Our raddr is the caller's pc.
+ #
+ movl (%esp),%eax
+
+ pushf
+ pushl %eax
+ pushl %edx
+ cli
+ call _mcount
+ addl $8,%esp
+ popf
+Lmcount_exit:
+ ret
+");
+#else /* !__GNUC__ */
+#error
+#endif /* __GNUC__ */
+
+#ifdef GUPROF
+/*
+ * mexitcount saves the return register(s), loads selfpc and calls
+ * mexitcount(selfpc) to do the work. Someday it should be in a machine
+ * dependent file together with cputime(), __mcount and mcount. cputime()
+ * can't just be put in machdep.c because it has to be compiled without -pg.
+ */
+#ifdef __GNUC__
+asm("
+ .text
+#
+# Dummy label to be seen when gprof -u hides mexitcount.
+#
+ .align 4,0x90
+ .globl __mexitcount
+__mexitcount:
+ nop
+
+GMON_PROF_HIRES = 4
+
+ .align 4,0x90
+ .globl mexitcount
+mexitcount:
+ cmpl $GMON_PROF_HIRES,__gmonparam+GM_STATE
+ jne Lmexitcount_exit
+ pushl %edx
+ pushl %eax
+ movl 8(%esp),%eax
+ pushf
+ pushl %eax
+ cli
+ call _mexitcount
+ addl $4,%esp
+ popf
+ popl %eax
+ popl %edx
+Lmexitcount_exit:
+ ret
+");
+#else /* !__GNUC__ */
+#error
+#endif /* __GNUC__ */
+
+/*
+ * Return the time elapsed since the last call. The units are machine-
+ * dependent.
+ */
+u_int
+cputime()
+{
+ u_int count;
+ u_int delta;
+ u_char low;
+ static u_int prev_count;
+
+ /*
+ * Read the current value of the 8254 timer counter 0.
+ */
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
+ low = inb(TIMER_CNTR0);
+ count = low | (inb(TIMER_CNTR0) << 8);
+
+ /*
+ * The timer counts down from TIMER_CNTR0_MAX to 0 and then resets.
+ * While profiling is enabled, this routine is called at least twice
+ * per timer reset (for mcounting and mexitcounting hardclock()),
+ * so at most one reset has occurred since the last call, and one
+ * has occurred iff the current count is larger than the previous
+ * count. This allows counter underflow to be detected faster
+ * than in microtime().
+ */
+ delta = prev_count - count;
+ prev_count = count;
+ if ((int) delta <= 0)
+ return (delta + timer0_max_count);
+ return (delta);
+}
+#else /* not GUPROF */
+#ifdef __GNUC__
+asm("
+ .text
+ .align 4,0x90
+ .globl mexitcount
+mexitcount:
+ ret
+");
+#else /* !__GNUC__ */
+#error
+#endif /* __GNUC__ */
+#endif /* GUPROF */
OpenPOWER on IntegriCloud