912e603778
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.
154 lines
3.0 KiB
C
154 lines
3.0 KiB
C
#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 */
|