From 586cc683d875b37dce82c825feb9ccc7d884b35e Mon Sep 17 00:00:00 2001 From: bde Date: Fri, 29 Dec 1995 15:30:05 +0000 Subject: 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. --- sys/i386/isa/prof_machdep.c | 153 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 sys/i386/isa/prof_machdep.c (limited to 'sys/i386/isa/prof_machdep.c') 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 +#include +#include +#include +#include + +#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 */ -- cgit v1.1