diff options
author | bde <bde@FreeBSD.org> | 1995-12-29 15:30:05 +0000 |
---|---|---|
committer | bde <bde@FreeBSD.org> | 1995-12-29 15:30:05 +0000 |
commit | 586cc683d875b37dce82c825feb9ccc7d884b35e (patch) | |
tree | 9ce1e55534d3d930aead3ff55aeb7fcedbc086a4 /sys/i386/isa/prof_machdep.c | |
parent | ff6f507f6bbb3fda77fb14c7201db37bafea7a3f (diff) | |
download | FreeBSD-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.c | 153 |
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 */ |