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.
This commit is contained in:
parent
703a371f1a
commit
912e603778
@ -38,7 +38,14 @@ static char sccsid[] = "@(#)mcount.c 8.1 (Berkeley) 6/4/93";
|
||||
#include <sys/param.h>
|
||||
#include <sys/gmon.h>
|
||||
#ifdef KERNEL
|
||||
#include <i386/include/cpufunc.h>
|
||||
#include <sys/systm.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
void bintr __P((void));
|
||||
void btrap __P((void));
|
||||
void eintr __P((void));
|
||||
void user __P((void));
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -57,39 +64,127 @@ static char sccsid[] = "@(#)mcount.c 8.1 (Berkeley) 6/4/93";
|
||||
* perform this optimization.
|
||||
*/
|
||||
_MCOUNT_DECL(frompc, selfpc) /* _mcount; may be static, inline, etc */
|
||||
register u_long frompc, selfpc;
|
||||
register fptrint_t frompc, selfpc;
|
||||
{
|
||||
#ifdef GUPROF
|
||||
u_int delta;
|
||||
#endif
|
||||
register fptrdiff_t frompci;
|
||||
register u_short *frompcindex;
|
||||
register struct tostruct *top, *prevtop;
|
||||
register struct gmonparam *p;
|
||||
register long toindex;
|
||||
#ifdef KERNEL
|
||||
register int s;
|
||||
u_long save_eflags;
|
||||
register int s; /* XXX */
|
||||
u_long save_eflags; /* XXX */
|
||||
#endif
|
||||
|
||||
p = &_gmonparam;
|
||||
#ifndef GUPROF /* XXX */
|
||||
/*
|
||||
* check that we are profiling
|
||||
* and that we aren't recursively invoked.
|
||||
*/
|
||||
if (p->state != GMON_PROF_ON)
|
||||
return;
|
||||
#endif
|
||||
#ifdef KERNEL
|
||||
MCOUNT_ENTER;
|
||||
#else
|
||||
p->state = GMON_PROF_BUSY;
|
||||
#endif
|
||||
frompci = frompc - p->lowpc;
|
||||
|
||||
#ifdef KERNEL
|
||||
/*
|
||||
* check that frompcindex is a reasonable pc value.
|
||||
* When we are called from an exception handler, frompci may be
|
||||
* for a user address. Convert such frompci's to the index of
|
||||
* user() to merge all user counts.
|
||||
*/
|
||||
if (frompci >= p->textsize) {
|
||||
if (frompci + p->lowpc
|
||||
>= (fptrint_t)(VM_MAXUSER_ADDRESS + UPAGES * NBPG))
|
||||
goto done;
|
||||
frompci = (fptrint_t)user - p->lowpc;
|
||||
if (frompci >= p->textsize)
|
||||
goto done;
|
||||
}
|
||||
#endif /* KERNEL */
|
||||
|
||||
#ifdef GUPROF
|
||||
if (p->state != GMON_PROF_HIRES)
|
||||
goto skip_guprof_stuff;
|
||||
/*
|
||||
* Look at the clock and add the count of clock cycles since the
|
||||
* clock was last looked at to a counter for frompc. This
|
||||
* solidifies the count for the function containing frompc and
|
||||
* effectively starts another clock for the current function.
|
||||
* The count for the new clock will be solidified when another
|
||||
* function call is made or the function returns.
|
||||
*
|
||||
* We use the usual sampling counters since they can be located
|
||||
* efficiently. 4-byte counters are usually necessary.
|
||||
*
|
||||
* There are many complications for subtracting the profiling
|
||||
* overheads from the counts for normal functions and adding
|
||||
* them to the counts for mcount(), mexitcount() and cputime().
|
||||
* We attempt to handle fractional cycles, but the overheads
|
||||
* are usually underestimated because they are calibrated for
|
||||
* a simpler than usual setup.
|
||||
*/
|
||||
delta = cputime() - p->mcount_overhead;
|
||||
p->cputime_overhead_resid += p->cputime_overhead_frac;
|
||||
p->mcount_overhead_resid += p->mcount_overhead_frac;
|
||||
if ((int)delta < 0)
|
||||
*p->mcount_count += delta + p->mcount_overhead
|
||||
- p->cputime_overhead;
|
||||
else if (delta != 0) {
|
||||
if (p->cputime_overhead_resid >= CALIB_SCALE) {
|
||||
p->cputime_overhead_resid -= CALIB_SCALE;
|
||||
++*p->cputime_count;
|
||||
--delta;
|
||||
}
|
||||
if (delta != 0) {
|
||||
if (p->mcount_overhead_resid >= CALIB_SCALE) {
|
||||
p->mcount_overhead_resid -= CALIB_SCALE;
|
||||
++*p->mcount_count;
|
||||
--delta;
|
||||
}
|
||||
KCOUNT(p, frompci) += delta;
|
||||
}
|
||||
*p->mcount_count += p->mcount_overhead_sub;
|
||||
}
|
||||
*p->cputime_count += p->cputime_overhead;
|
||||
skip_guprof_stuff:
|
||||
#endif /* GUPROF */
|
||||
|
||||
#ifdef KERNEL
|
||||
/*
|
||||
* When we are called from an exception handler, frompc is faked
|
||||
* to be for where the exception occurred. We've just solidified
|
||||
* the count for there. Now convert frompci to the index of btrap()
|
||||
* for trap handlers and bintr() for interrupt handlers to make
|
||||
* exceptions appear in the call graph as calls from btrap() and
|
||||
* bintr() instead of calls from all over.
|
||||
*/
|
||||
if ((fptrint_t)selfpc >= (fptrint_t)btrap
|
||||
&& (fptrint_t)selfpc < (fptrint_t)eintr) {
|
||||
if ((fptrint_t)selfpc >= (fptrint_t)bintr)
|
||||
frompci = (fptrint_t)bintr - p->lowpc;
|
||||
else
|
||||
frompci = (fptrint_t)btrap - p->lowpc;
|
||||
}
|
||||
#endif /* KERNEL */
|
||||
|
||||
/*
|
||||
* check that frompc is a reasonable pc value.
|
||||
* for example: signal catchers get called from the stack,
|
||||
* not from text space. too bad.
|
||||
*/
|
||||
frompc -= p->lowpc;
|
||||
if (frompc > p->textsize)
|
||||
if (frompci >= p->textsize)
|
||||
goto done;
|
||||
|
||||
frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
|
||||
frompcindex = &p->froms[frompci / (p->hashfraction * sizeof(*p->froms))];
|
||||
toindex = *frompcindex;
|
||||
if (toindex == 0) {
|
||||
/*
|
||||
@ -180,3 +275,48 @@ _MCOUNT_DECL(frompc, selfpc) /* _mcount; may be static, inline, etc */
|
||||
* which is included by <sys/gmon.h>.
|
||||
*/
|
||||
MCOUNT
|
||||
|
||||
#ifdef GUPROF
|
||||
void
|
||||
mexitcount(selfpc)
|
||||
fptrint_t selfpc;
|
||||
{
|
||||
struct gmonparam *p;
|
||||
fptrint_t selfpcdiff;
|
||||
|
||||
p = &_gmonparam;
|
||||
selfpcdiff = selfpc - (fptrint_t)p->lowpc;
|
||||
if (selfpcdiff < p->textsize) {
|
||||
u_int delta;
|
||||
|
||||
/*
|
||||
* Solidify the count for the current function.
|
||||
*/
|
||||
delta = cputime() - p->mexitcount_overhead;
|
||||
p->cputime_overhead_resid += p->cputime_overhead_frac;
|
||||
p->mexitcount_overhead_resid += p->mexitcount_overhead_frac;
|
||||
if ((int)delta < 0)
|
||||
*p->mexitcount_count += delta + p->mexitcount_overhead
|
||||
- p->cputime_overhead;
|
||||
else if (delta != 0) {
|
||||
if (p->cputime_overhead_resid >= CALIB_SCALE) {
|
||||
p->cputime_overhead_resid -= CALIB_SCALE;
|
||||
++*p->cputime_count;
|
||||
--delta;
|
||||
}
|
||||
if (delta != 0) {
|
||||
if (p->mexitcount_overhead_resid
|
||||
>= CALIB_SCALE) {
|
||||
p->mexitcount_overhead_resid
|
||||
-= CALIB_SCALE;
|
||||
++*p->mexitcount_count;
|
||||
--delta;
|
||||
}
|
||||
KCOUNT(p, selfpcdiff) += delta;
|
||||
}
|
||||
*p->mexitcount_count += p->mexitcount_overhead_sub;
|
||||
}
|
||||
*p->cputime_count += p->cputime_overhead;
|
||||
}
|
||||
}
|
||||
#endif /* GUPROF */
|
||||
|
153
sys/amd64/amd64/prof_machdep.c
Normal file
153
sys/amd64/amd64/prof_machdep.c
Normal file
@ -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 */
|
@ -30,7 +30,7 @@
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: asmacros.h,v 1.4 1994/08/19 11:20:11 jkh Exp $
|
||||
* $Id: asmacros.h,v 1.5 1994/09/08 12:25:18 bde Exp $
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_ASMACROS_H_
|
||||
@ -38,47 +38,83 @@
|
||||
|
||||
#ifdef KERNEL
|
||||
|
||||
/* XXX too much duplication in various asm*.h's and gprof.h's */
|
||||
|
||||
#define ALIGN_DATA .align 2 /* 4 byte alignment, zero filled */
|
||||
#define ALIGN_TEXT .align 2,0x90 /* 4-byte alignment, nop filled */
|
||||
#define SUPERALIGN_TEXT .align 4,0x90 /* 16-byte alignment (better for 486), nop filled */
|
||||
|
||||
#define GEN_ENTRY(name) ALIGN_TEXT; .globl name; name:
|
||||
#define NON_GPROF_ENTRY(name) GEN_ENTRY(_/**/name)
|
||||
|
||||
/* These three are place holders for future changes to the profiling code */
|
||||
#define MCOUNT_LABEL(name)
|
||||
#define MEXITCOUNT
|
||||
#define FAKE_MCOUNT(caller)
|
||||
#define GEN_ENTRY(name) ALIGN_TEXT; .globl _/**/name; _/**/name:
|
||||
#define NON_GPROF_ENTRY(name) GEN_ENTRY(name)
|
||||
|
||||
#ifdef GPROF
|
||||
/*
|
||||
* ALTENTRY() must be before a corresponding ENTRY() so that it can jump
|
||||
* over the mcounting.
|
||||
* __mcount is like mcount except that doesn't require its caller to set
|
||||
* up a frame pointer. It must be called before pushing anything onto the
|
||||
* stack. gcc should eventually generate code to call __mcount in most
|
||||
* cases. This would make -pg in combination with -fomit-frame-pointer
|
||||
* useful. gcc has a configuration variable PROFILE_BEFORE_PROLOGUE to
|
||||
* allow profiling before setting up the frame pointer, but this is
|
||||
* inadequate for good handling of special cases, e.g., -fpic works best
|
||||
* with profiling after the prologue.
|
||||
*
|
||||
* Neither __mcount nor mcount requires %eax to point to 4 bytes of data,
|
||||
* so don't waste space allocating the data or time setting it up. Changes
|
||||
* to avoid the wastage in gcc-2.4.5-compiled code are available.
|
||||
*
|
||||
* mexitcount is a new profiling feature to allow accurate timing of all
|
||||
* functions if an accurate clock is available. Changes to gcc-2.4.5 to
|
||||
* support it are are available. The changes currently don't allow not
|
||||
* generating mexitcounts for non-kernel code. It is best to call
|
||||
* mexitcount right at the end of a function like the MEXITCOUNT macro
|
||||
* does, but the changes to gcc only implement calling it as the first
|
||||
* thing in the epilogue to avoid problems with -fpic.
|
||||
*
|
||||
* mcount and __mexitcount may clobber the call-used registers and %ef.
|
||||
* mexitcount may clobber %ecx and %ef.
|
||||
*
|
||||
* Cross-jumping makes accurate timing more difficult. It is handled in
|
||||
* many cases by calling mexitcount before jumping. It is not handled
|
||||
* for some conditional jumps (e.g., in bcopyx) or for some fault-handling
|
||||
* jumps. It is handled for some fault-handling jumps by not sharing the
|
||||
* exit routine.
|
||||
*
|
||||
* ALTENTRY() must be before a corresponding ENTRY() so that it can jump to
|
||||
* the main entry point. Note that alt entries are counted twice. They
|
||||
* have to be counted as ordinary entries for gprof to get the call times
|
||||
* right for the ordinary entries.
|
||||
*
|
||||
* High local labels are used in macros to avoid clashes with local labels
|
||||
* in functions.
|
||||
*
|
||||
* "ret" is used instead of "RET" because there are a lot of "ret"s.
|
||||
* 0xc3 is the opcode for "ret" (#define ret ... ret fails because this
|
||||
* file is preprocessed in traditional mode). "ret" clobbers eflags
|
||||
* but this doesn't matter.
|
||||
*/
|
||||
#define ALTENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; jmp 2f
|
||||
#define ENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; 2:
|
||||
/*
|
||||
* The call to mcount supports the usual (bad) conventions. We allocate
|
||||
* some data and pass a pointer to it although the FreeBSD doesn't use
|
||||
* the data. We set up a frame before calling mcount because that is
|
||||
* the standard convention although it makes work for both mcount and
|
||||
* callers.
|
||||
*/
|
||||
#define MCOUNT .data; ALIGN_DATA; 1:; .long 0; .text; \
|
||||
pushl %ebp; movl %esp,%ebp; \
|
||||
movl $1b,%eax; call mcount; popl %ebp
|
||||
#else
|
||||
#define ALTENTRY(name) GEN_ENTRY(name) ; MCOUNT ; MEXITCOUNT ; jmp 9f
|
||||
#define ENTRY(name) GEN_ENTRY(name) ; 9: ; MCOUNT
|
||||
#define FAKE_MCOUNT(caller) pushl caller ; call __mcount ; popl %ecx
|
||||
#define MCOUNT call __mcount
|
||||
#define MCOUNT_LABEL(name) GEN_ENTRY(name) ; nop ; ALIGN_TEXT
|
||||
#define MEXITCOUNT call mexitcount
|
||||
#define ret MEXITCOUNT ; .byte 0xc3
|
||||
#else /* not GPROF */
|
||||
/*
|
||||
* ALTENTRY() has to align because it is before a corresponding ENTRY().
|
||||
* ENTRY() has to align to because there may be no ALTENTRY() before it.
|
||||
* If there is a previous ALTENTRY() then the alignment code is empty.
|
||||
* If there is a previous ALTENTRY() then the alignment code for ENTRY()
|
||||
* is empty.
|
||||
*/
|
||||
#define ALTENTRY(name) GEN_ENTRY(_/**/name)
|
||||
#define ENTRY(name) GEN_ENTRY(_/**/name)
|
||||
#define ALTENTRY(name) GEN_ENTRY(name)
|
||||
#define ENTRY(name) GEN_ENTRY(name)
|
||||
#define FAKE_MCOUNT(caller)
|
||||
#define MCOUNT
|
||||
#define MCOUNT_LABEL(name)
|
||||
#define MEXITCOUNT
|
||||
#endif /* GPROF */
|
||||
|
||||
#endif
|
||||
|
||||
/* XXX NOP and FASTER_NOP are misleadingly named */
|
||||
#ifdef DUMMY_NOPS /* this will break some older machines */
|
||||
#define FASTER_NOP
|
||||
#define NOP
|
||||
|
@ -31,35 +31,59 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)profile.h 8.1 (Berkeley) 6/11/93
|
||||
* $Id: profile.h,v 1.3 1994/08/21 04:55:29 paul Exp $
|
||||
* $Id: profile.h,v 1.4 1994/09/15 16:27:14 paul Exp $
|
||||
*/
|
||||
|
||||
#ifndef _I386_MACHINE_PROFILE_H_
|
||||
#define _I386_MACHINE_PROFILE_H_
|
||||
#ifndef _MACHINE_PROFILE_H_
|
||||
#define _MACHINE_PROFILE_H_
|
||||
|
||||
#if 0
|
||||
#define _MCOUNT_DECL static inline void _mcount
|
||||
|
||||
#define MCOUNT \
|
||||
extern void mcount() asm("mcount"); void mcount() { \
|
||||
int selfpc, frompcindex; \
|
||||
fptrint_t selfpc, frompc; \
|
||||
/* \
|
||||
* find the return address for mcount, \
|
||||
* Find the return address for mcount, \
|
||||
* and the return address for mcount's caller. \
|
||||
* \
|
||||
* selfpc = pc pushed by mcount call \
|
||||
* selfpc = pc pushed by call to mcount \
|
||||
*/ \
|
||||
asm("movl 4(%%ebp),%0" : "=r" (selfpc)); \
|
||||
/* \
|
||||
* frompcindex = pc pushed by jsr into self. \
|
||||
* In GCC the caller's stack frame has already been built so we \
|
||||
* have to chase a6 to find caller's raddr. \
|
||||
* frompc = pc pushed by call to mcount's caller. \
|
||||
* 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. \
|
||||
*/ \
|
||||
asm("movl (%%ebp),%0" : "=r" (frompcindex)); \
|
||||
frompcindex = ((int *)frompcindex)[1]; \
|
||||
_mcount(frompcindex, selfpc); \
|
||||
asm("movl (%%ebp),%0" : "=r" (frompc)); \
|
||||
frompc = ((fptrint_t *)frompc)[1]; \
|
||||
_mcount(frompc, selfpc); \
|
||||
}
|
||||
|
||||
#define MCOUNT_ENTER save_eflags = read_eflags(); disable_intr()
|
||||
#define MCOUNT_EXIT write_eflags(save_eflags)
|
||||
|
||||
#else
|
||||
#define _MCOUNT_DECL void mcount
|
||||
#define MCOUNT
|
||||
#endif
|
||||
|
||||
#define MCOUNT_ENTER { save_eflags = read_eflags(); disable_intr(); }
|
||||
#define MCOUNT_EXIT (write_eflags(save_eflags))
|
||||
|
||||
#define CALIB_SCALE 1000
|
||||
#define KCOUNT(p,index) ((p)->kcount[(index) \
|
||||
/ (HISTFRACTION * sizeof(*(p)->kcount))])
|
||||
#define PC_TO_I(p, pc) ((fptrint_t)(pc) - (fptrint_t)(p)->lowpc)
|
||||
|
||||
/* An unsigned integral type that can hold function pointers. */
|
||||
typedef u_int fptrint_t;
|
||||
|
||||
/*
|
||||
* An unsigned integral type that can hold non-negative difference between
|
||||
* function pointers.
|
||||
*/
|
||||
typedef int fptrdiff_t;
|
||||
|
||||
u_int cputime __P((void));
|
||||
void mcount __P((fptrint_t frompc, fptrint_t selfpc));
|
||||
void mexitcount __P((fptrint_t selfpc));
|
||||
|
||||
#endif /* !MACHINE_PROFILE_H */
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file tells config what files go into building a kernel,
|
||||
# files marked standard are always included.
|
||||
#
|
||||
# $Id: files.i386,v 1.120 1995/12/26 12:50:01 bde Exp $
|
||||
# $Id: files.i386,v 1.121 1995/12/26 13:57:56 bde Exp $
|
||||
#
|
||||
aic7xxx_asm optional ahc device-driver \
|
||||
dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \
|
||||
@ -115,6 +115,7 @@ i386/isa/pcvt/pcvt_kbd.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_out.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_sup.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
|
||||
i386/isa/prof_machdep.c optional profiling-routine
|
||||
i386/isa/psm.c optional psm device-driver
|
||||
i386/isa/random_machdep.c standard
|
||||
i386/isa/rc.c optional rc device-driver
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file tells config what files go into building a kernel,
|
||||
# files marked standard are always included.
|
||||
#
|
||||
# $Id: files.i386,v 1.120 1995/12/26 12:50:01 bde Exp $
|
||||
# $Id: files.i386,v 1.121 1995/12/26 13:57:56 bde Exp $
|
||||
#
|
||||
aic7xxx_asm optional ahc device-driver \
|
||||
dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \
|
||||
@ -115,6 +115,7 @@ i386/isa/pcvt/pcvt_kbd.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_out.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_sup.c optional vt device-driver
|
||||
i386/isa/pcvt/pcvt_vtf.c optional vt device-driver
|
||||
i386/isa/prof_machdep.c optional profiling-routine
|
||||
i386/isa/psm.c optional psm device-driver
|
||||
i386/isa/random_machdep.c standard
|
||||
i386/isa/rc.c optional rc device-driver
|
||||
|
@ -30,7 +30,7 @@
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: asmacros.h,v 1.4 1994/08/19 11:20:11 jkh Exp $
|
||||
* $Id: asmacros.h,v 1.5 1994/09/08 12:25:18 bde Exp $
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_ASMACROS_H_
|
||||
@ -38,47 +38,83 @@
|
||||
|
||||
#ifdef KERNEL
|
||||
|
||||
/* XXX too much duplication in various asm*.h's and gprof.h's */
|
||||
|
||||
#define ALIGN_DATA .align 2 /* 4 byte alignment, zero filled */
|
||||
#define ALIGN_TEXT .align 2,0x90 /* 4-byte alignment, nop filled */
|
||||
#define SUPERALIGN_TEXT .align 4,0x90 /* 16-byte alignment (better for 486), nop filled */
|
||||
|
||||
#define GEN_ENTRY(name) ALIGN_TEXT; .globl name; name:
|
||||
#define NON_GPROF_ENTRY(name) GEN_ENTRY(_/**/name)
|
||||
|
||||
/* These three are place holders for future changes to the profiling code */
|
||||
#define MCOUNT_LABEL(name)
|
||||
#define MEXITCOUNT
|
||||
#define FAKE_MCOUNT(caller)
|
||||
#define GEN_ENTRY(name) ALIGN_TEXT; .globl _/**/name; _/**/name:
|
||||
#define NON_GPROF_ENTRY(name) GEN_ENTRY(name)
|
||||
|
||||
#ifdef GPROF
|
||||
/*
|
||||
* ALTENTRY() must be before a corresponding ENTRY() so that it can jump
|
||||
* over the mcounting.
|
||||
* __mcount is like mcount except that doesn't require its caller to set
|
||||
* up a frame pointer. It must be called before pushing anything onto the
|
||||
* stack. gcc should eventually generate code to call __mcount in most
|
||||
* cases. This would make -pg in combination with -fomit-frame-pointer
|
||||
* useful. gcc has a configuration variable PROFILE_BEFORE_PROLOGUE to
|
||||
* allow profiling before setting up the frame pointer, but this is
|
||||
* inadequate for good handling of special cases, e.g., -fpic works best
|
||||
* with profiling after the prologue.
|
||||
*
|
||||
* Neither __mcount nor mcount requires %eax to point to 4 bytes of data,
|
||||
* so don't waste space allocating the data or time setting it up. Changes
|
||||
* to avoid the wastage in gcc-2.4.5-compiled code are available.
|
||||
*
|
||||
* mexitcount is a new profiling feature to allow accurate timing of all
|
||||
* functions if an accurate clock is available. Changes to gcc-2.4.5 to
|
||||
* support it are are available. The changes currently don't allow not
|
||||
* generating mexitcounts for non-kernel code. It is best to call
|
||||
* mexitcount right at the end of a function like the MEXITCOUNT macro
|
||||
* does, but the changes to gcc only implement calling it as the first
|
||||
* thing in the epilogue to avoid problems with -fpic.
|
||||
*
|
||||
* mcount and __mexitcount may clobber the call-used registers and %ef.
|
||||
* mexitcount may clobber %ecx and %ef.
|
||||
*
|
||||
* Cross-jumping makes accurate timing more difficult. It is handled in
|
||||
* many cases by calling mexitcount before jumping. It is not handled
|
||||
* for some conditional jumps (e.g., in bcopyx) or for some fault-handling
|
||||
* jumps. It is handled for some fault-handling jumps by not sharing the
|
||||
* exit routine.
|
||||
*
|
||||
* ALTENTRY() must be before a corresponding ENTRY() so that it can jump to
|
||||
* the main entry point. Note that alt entries are counted twice. They
|
||||
* have to be counted as ordinary entries for gprof to get the call times
|
||||
* right for the ordinary entries.
|
||||
*
|
||||
* High local labels are used in macros to avoid clashes with local labels
|
||||
* in functions.
|
||||
*
|
||||
* "ret" is used instead of "RET" because there are a lot of "ret"s.
|
||||
* 0xc3 is the opcode for "ret" (#define ret ... ret fails because this
|
||||
* file is preprocessed in traditional mode). "ret" clobbers eflags
|
||||
* but this doesn't matter.
|
||||
*/
|
||||
#define ALTENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; jmp 2f
|
||||
#define ENTRY(name) GEN_ENTRY(_/**/name); MCOUNT; 2:
|
||||
/*
|
||||
* The call to mcount supports the usual (bad) conventions. We allocate
|
||||
* some data and pass a pointer to it although the FreeBSD doesn't use
|
||||
* the data. We set up a frame before calling mcount because that is
|
||||
* the standard convention although it makes work for both mcount and
|
||||
* callers.
|
||||
*/
|
||||
#define MCOUNT .data; ALIGN_DATA; 1:; .long 0; .text; \
|
||||
pushl %ebp; movl %esp,%ebp; \
|
||||
movl $1b,%eax; call mcount; popl %ebp
|
||||
#else
|
||||
#define ALTENTRY(name) GEN_ENTRY(name) ; MCOUNT ; MEXITCOUNT ; jmp 9f
|
||||
#define ENTRY(name) GEN_ENTRY(name) ; 9: ; MCOUNT
|
||||
#define FAKE_MCOUNT(caller) pushl caller ; call __mcount ; popl %ecx
|
||||
#define MCOUNT call __mcount
|
||||
#define MCOUNT_LABEL(name) GEN_ENTRY(name) ; nop ; ALIGN_TEXT
|
||||
#define MEXITCOUNT call mexitcount
|
||||
#define ret MEXITCOUNT ; .byte 0xc3
|
||||
#else /* not GPROF */
|
||||
/*
|
||||
* ALTENTRY() has to align because it is before a corresponding ENTRY().
|
||||
* ENTRY() has to align to because there may be no ALTENTRY() before it.
|
||||
* If there is a previous ALTENTRY() then the alignment code is empty.
|
||||
* If there is a previous ALTENTRY() then the alignment code for ENTRY()
|
||||
* is empty.
|
||||
*/
|
||||
#define ALTENTRY(name) GEN_ENTRY(_/**/name)
|
||||
#define ENTRY(name) GEN_ENTRY(_/**/name)
|
||||
#define ALTENTRY(name) GEN_ENTRY(name)
|
||||
#define ENTRY(name) GEN_ENTRY(name)
|
||||
#define FAKE_MCOUNT(caller)
|
||||
#define MCOUNT
|
||||
#define MCOUNT_LABEL(name)
|
||||
#define MEXITCOUNT
|
||||
#endif /* GPROF */
|
||||
|
||||
#endif
|
||||
|
||||
/* XXX NOP and FASTER_NOP are misleadingly named */
|
||||
#ifdef DUMMY_NOPS /* this will break some older machines */
|
||||
#define FASTER_NOP
|
||||
#define NOP
|
||||
|
@ -31,35 +31,59 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)profile.h 8.1 (Berkeley) 6/11/93
|
||||
* $Id: profile.h,v 1.3 1994/08/21 04:55:29 paul Exp $
|
||||
* $Id: profile.h,v 1.4 1994/09/15 16:27:14 paul Exp $
|
||||
*/
|
||||
|
||||
#ifndef _I386_MACHINE_PROFILE_H_
|
||||
#define _I386_MACHINE_PROFILE_H_
|
||||
#ifndef _MACHINE_PROFILE_H_
|
||||
#define _MACHINE_PROFILE_H_
|
||||
|
||||
#if 0
|
||||
#define _MCOUNT_DECL static inline void _mcount
|
||||
|
||||
#define MCOUNT \
|
||||
extern void mcount() asm("mcount"); void mcount() { \
|
||||
int selfpc, frompcindex; \
|
||||
fptrint_t selfpc, frompc; \
|
||||
/* \
|
||||
* find the return address for mcount, \
|
||||
* Find the return address for mcount, \
|
||||
* and the return address for mcount's caller. \
|
||||
* \
|
||||
* selfpc = pc pushed by mcount call \
|
||||
* selfpc = pc pushed by call to mcount \
|
||||
*/ \
|
||||
asm("movl 4(%%ebp),%0" : "=r" (selfpc)); \
|
||||
/* \
|
||||
* frompcindex = pc pushed by jsr into self. \
|
||||
* In GCC the caller's stack frame has already been built so we \
|
||||
* have to chase a6 to find caller's raddr. \
|
||||
* frompc = pc pushed by call to mcount's caller. \
|
||||
* 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. \
|
||||
*/ \
|
||||
asm("movl (%%ebp),%0" : "=r" (frompcindex)); \
|
||||
frompcindex = ((int *)frompcindex)[1]; \
|
||||
_mcount(frompcindex, selfpc); \
|
||||
asm("movl (%%ebp),%0" : "=r" (frompc)); \
|
||||
frompc = ((fptrint_t *)frompc)[1]; \
|
||||
_mcount(frompc, selfpc); \
|
||||
}
|
||||
|
||||
#define MCOUNT_ENTER save_eflags = read_eflags(); disable_intr()
|
||||
#define MCOUNT_EXIT write_eflags(save_eflags)
|
||||
|
||||
#else
|
||||
#define _MCOUNT_DECL void mcount
|
||||
#define MCOUNT
|
||||
#endif
|
||||
|
||||
#define MCOUNT_ENTER { save_eflags = read_eflags(); disable_intr(); }
|
||||
#define MCOUNT_EXIT (write_eflags(save_eflags))
|
||||
|
||||
#define CALIB_SCALE 1000
|
||||
#define KCOUNT(p,index) ((p)->kcount[(index) \
|
||||
/ (HISTFRACTION * sizeof(*(p)->kcount))])
|
||||
#define PC_TO_I(p, pc) ((fptrint_t)(pc) - (fptrint_t)(p)->lowpc)
|
||||
|
||||
/* An unsigned integral type that can hold function pointers. */
|
||||
typedef u_int fptrint_t;
|
||||
|
||||
/*
|
||||
* An unsigned integral type that can hold non-negative difference between
|
||||
* function pointers.
|
||||
*/
|
||||
typedef int fptrdiff_t;
|
||||
|
||||
u_int cputime __P((void));
|
||||
void mcount __P((fptrint_t frompc, fptrint_t selfpc));
|
||||
void mexitcount __P((fptrint_t selfpc));
|
||||
|
||||
#endif /* !MACHINE_PROFILE_H */
|
||||
|
153
sys/i386/isa/prof_machdep.c
Normal file
153
sys/i386/isa/prof_machdep.c
Normal file
@ -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 */
|
@ -31,7 +31,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)subr_prof.c 8.3 (Berkeley) 9/23/93
|
||||
* $Id: subr_prof.c,v 1.14 1995/12/14 08:31:44 phk Exp $
|
||||
* $Id: subr_prof.c,v 1.15 1995/12/26 01:21:39 bde Exp $
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -62,6 +62,11 @@ kmstartup(dummy)
|
||||
{
|
||||
char *cp;
|
||||
struct gmonparam *p = &_gmonparam;
|
||||
#ifdef GUPROF
|
||||
fptrint_t kmstartup_addr;
|
||||
int i;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@ -89,9 +94,74 @@ kmstartup(dummy)
|
||||
bzero(cp, p->kcountsize + p->tossize + p->fromssize);
|
||||
p->tos = (struct tostruct *)cp;
|
||||
cp += p->tossize;
|
||||
p->kcount = (u_short *)cp;
|
||||
p->kcount = (HISTCOUNTER *)cp;
|
||||
cp += p->kcountsize;
|
||||
p->froms = (u_short *)cp;
|
||||
|
||||
#ifdef GUPROF
|
||||
/*
|
||||
* Initialize pointers to overhead counters.
|
||||
*/
|
||||
p->cputime_count = &KCOUNT(p, PC_TO_I(p, cputime));
|
||||
p->mcount_count = &KCOUNT(p, PC_TO_I(p, mcount));
|
||||
p->mexitcount_count = &KCOUNT(p, PC_TO_I(p, mexitcount));
|
||||
|
||||
/*
|
||||
* Determine overheads.
|
||||
*/
|
||||
disable_intr();
|
||||
p->state = GMON_PROF_HIRES;
|
||||
|
||||
p->cputime_overhead = 0;
|
||||
(void)cputime();
|
||||
for (i = 0; i < CALIB_SCALE; i++)
|
||||
p->cputime_overhead += cputime();
|
||||
|
||||
(void)cputime();
|
||||
for (i = 0; i < CALIB_SCALE; i++)
|
||||
#if defined(i386) && __GNUC__ >= 2
|
||||
/*
|
||||
* Underestimate slightly by always calling __mcount, never
|
||||
* mcount.
|
||||
*/
|
||||
asm("pushl %0; call __mcount; popl %%ecx"
|
||||
:
|
||||
: "i" (kmstartup)
|
||||
: "ax", "bx", "cx", "dx", "memory");
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
p->mcount_overhead = KCOUNT(p, PC_TO_I(p, kmstartup));
|
||||
|
||||
(void)cputime();
|
||||
for (i = 0; i < CALIB_SCALE; i++)
|
||||
#if defined(i386) && __GNUC__ >= 2
|
||||
asm("call mexitcount; 1:"
|
||||
: : : "ax", "bx", "cx", "dx", "memory");
|
||||
asm("movl $1b,%0" : "=rm" (kmstartup_addr));
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
p->mexitcount_overhead = KCOUNT(p, PC_TO_I(p, kmstartup_addr));
|
||||
|
||||
p->state = GMON_PROF_OFF;
|
||||
enable_intr();
|
||||
|
||||
p->mcount_overhead_sub = p->mcount_overhead - p->cputime_overhead;
|
||||
p->mexitcount_overhead_sub = p->mexitcount_overhead
|
||||
- p->cputime_overhead;
|
||||
printf("Profiling overheads: %u+%u %u+%u\n",
|
||||
p->cputime_overhead, p->mcount_overhead_sub,
|
||||
p->cputime_overhead, p->mexitcount_overhead_sub);
|
||||
p->cputime_overhead_frac = p->cputime_overhead % CALIB_SCALE;
|
||||
p->cputime_overhead /= CALIB_SCALE;
|
||||
p->mcount_overhead_frac = p->mcount_overhead_sub % CALIB_SCALE;
|
||||
p->mcount_overhead_sub /= CALIB_SCALE;
|
||||
p->mcount_overhead /= CALIB_SCALE;
|
||||
p->mexitcount_overhead_frac = p->mexitcount_overhead_sub % CALIB_SCALE;
|
||||
p->mexitcount_overhead_sub /= CALIB_SCALE;
|
||||
p->mexitcount_overhead /= CALIB_SCALE;
|
||||
#endif /* GUPROF */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -104,6 +174,7 @@ sysctl_kern_prof SYSCTL_HANDLER_ARGS
|
||||
u_int namelen = arg2;
|
||||
struct gmonparam *gp = &_gmonparam;
|
||||
int error;
|
||||
int state;
|
||||
|
||||
/* all sysctl names at this level are terminal */
|
||||
if (namelen != 1)
|
||||
@ -111,13 +182,27 @@ sysctl_kern_prof SYSCTL_HANDLER_ARGS
|
||||
|
||||
switch (name[0]) {
|
||||
case GPROF_STATE:
|
||||
error = sysctl_handle_int(oidp, &gp->state, 0, req);
|
||||
state = gp->state;
|
||||
error = sysctl_handle_int(oidp, &state, 0, req);
|
||||
if (error)
|
||||
return (error);
|
||||
if (gp->state == GMON_PROF_OFF)
|
||||
if (!req->newptr)
|
||||
return (0);
|
||||
if (state == GMON_PROF_OFF) {
|
||||
stopprofclock(&proc0);
|
||||
else
|
||||
gp->state = state;
|
||||
} else if (state == GMON_PROF_ON) {
|
||||
gp->profrate = profhz;
|
||||
gp->state = state;
|
||||
startprofclock(&proc0);
|
||||
#ifdef GUPROF
|
||||
} else if (state == GMON_PROF_HIRES) {
|
||||
gp->profrate = 1193182; /* XXX */
|
||||
stopprofclock(&proc0);
|
||||
gp->state = state;
|
||||
#endif
|
||||
} else if (state != gp->state)
|
||||
return (EINVAL);
|
||||
return (0);
|
||||
case GPROF_COUNT:
|
||||
return (sysctl_handle_opaque(oidp,
|
||||
|
@ -1,180 +1,4 @@
|
||||
/*-
|
||||
* Copyright (c) 1983, 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: mcount.c,v 1.3 1994/09/15 15:41:48 paul Exp $
|
||||
*/
|
||||
#define GPROF4 1 /* XXX can't get at kernel options */
|
||||
#define GUPROF 1 /* XXX can't get at kernel options */
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/gmon.h>
|
||||
#ifdef KERNEL
|
||||
#include <i386/include/cpufunc.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* mcount is called on entry to each function compiled with the profiling
|
||||
* switch set. _mcount(), which is declared in a machine-dependent way
|
||||
* with _MCOUNT_DECL, does the actual work and is either inlined into a
|
||||
* C routine or called by an assembly stub. In any case, this magic is
|
||||
* taken care of by the MCOUNT definition in <machine/profile.h>.
|
||||
*
|
||||
* _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(frompc, selfpc) /* _mcount; may be static, inline, etc */
|
||||
register u_long frompc, selfpc;
|
||||
{
|
||||
register u_short *frompcindex;
|
||||
register struct tostruct *top, *prevtop;
|
||||
register struct gmonparam *p;
|
||||
register long toindex;
|
||||
#ifdef KERNEL
|
||||
register int s;
|
||||
u_long save_eflags;
|
||||
#endif
|
||||
|
||||
p = &_gmonparam;
|
||||
/*
|
||||
* check that we are profiling
|
||||
* and that we aren't recursively invoked.
|
||||
*/
|
||||
if (p->state != GMON_PROF_ON)
|
||||
return;
|
||||
#ifdef KERNEL
|
||||
MCOUNT_ENTER;
|
||||
#else
|
||||
p->state = GMON_PROF_BUSY;
|
||||
#endif
|
||||
/*
|
||||
* check that frompcindex is a reasonable pc value.
|
||||
* for example: signal catchers get called from the stack,
|
||||
* not from text space. too bad.
|
||||
*/
|
||||
frompc -= p->lowpc;
|
||||
if (frompc > p->textsize)
|
||||
goto done;
|
||||
|
||||
frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
|
||||
toindex = *frompcindex;
|
||||
if (toindex == 0) {
|
||||
/*
|
||||
* first time traversing this arc
|
||||
*/
|
||||
toindex = ++p->tos[0].link;
|
||||
if (toindex >= p->tolimit)
|
||||
/* halt further profiling */
|
||||
goto overflow;
|
||||
|
||||
*frompcindex = toindex;
|
||||
top = &p->tos[toindex];
|
||||
top->selfpc = selfpc;
|
||||
top->count = 1;
|
||||
top->link = 0;
|
||||
goto done;
|
||||
}
|
||||
top = &p->tos[toindex];
|
||||
if (top->selfpc == selfpc) {
|
||||
/*
|
||||
* arc at front of chain; usual case.
|
||||
*/
|
||||
top->count++;
|
||||
goto done;
|
||||
}
|
||||
/*
|
||||
* have to go looking down chain for it.
|
||||
* top points to what we are looking at,
|
||||
* prevtop points to previous top.
|
||||
* we know it is not at the head of the chain.
|
||||
*/
|
||||
for (; /* goto done */; ) {
|
||||
if (top->link == 0) {
|
||||
/*
|
||||
* top is end of the chain and none of the chain
|
||||
* had top->selfpc == selfpc.
|
||||
* so we allocate a new tostruct
|
||||
* and link it to the head of the chain.
|
||||
*/
|
||||
toindex = ++p->tos[0].link;
|
||||
if (toindex >= p->tolimit)
|
||||
goto overflow;
|
||||
|
||||
top = &p->tos[toindex];
|
||||
top->selfpc = selfpc;
|
||||
top->count = 1;
|
||||
top->link = *frompcindex;
|
||||
*frompcindex = toindex;
|
||||
goto done;
|
||||
}
|
||||
/*
|
||||
* otherwise, check the next arc on the chain.
|
||||
*/
|
||||
prevtop = top;
|
||||
top = &p->tos[top->link];
|
||||
if (top->selfpc == selfpc) {
|
||||
/*
|
||||
* there it is.
|
||||
* increment its count
|
||||
* move it to the head of the chain.
|
||||
*/
|
||||
top->count++;
|
||||
toindex = prevtop->link;
|
||||
prevtop->link = top->link;
|
||||
top->link = *frompcindex;
|
||||
*frompcindex = toindex;
|
||||
goto done;
|
||||
}
|
||||
|
||||
}
|
||||
done:
|
||||
#ifdef KERNEL
|
||||
MCOUNT_EXIT;
|
||||
#else
|
||||
p->state = GMON_PROF_ON;
|
||||
#endif
|
||||
return;
|
||||
overflow:
|
||||
p->state = GMON_PROF_ERROR;
|
||||
#ifdef KERNEL
|
||||
MCOUNT_EXIT;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Actual definition of mcount function. Defined in <machine/profile.h>,
|
||||
* which is included by <sys/gmon.h>.
|
||||
*/
|
||||
MCOUNT
|
||||
#include "../lib/libc/gmon/mcount.c" /* XXX */
|
||||
|
@ -31,7 +31,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)gmon.h 8.2 (Berkeley) 1/4/94
|
||||
* $Id: gmon.h,v 1.6 1995/05/30 08:14:22 rgrimes Exp $
|
||||
* $Id: gmon.h,v 1.7 1995/08/29 03:09:14 bde Exp $
|
||||
*/
|
||||
|
||||
#ifndef _SYS_GMON_H_
|
||||
@ -49,18 +49,33 @@ struct gmonhdr {
|
||||
int version; /* version number */
|
||||
int profrate; /* profiling clock rate */
|
||||
int spare[3]; /* reserved */
|
||||
/* XXX should record counter size and density */
|
||||
};
|
||||
#define GMONVERSION 0x00051879
|
||||
|
||||
/*
|
||||
* histogram counters are unsigned shorts (according to the kernel).
|
||||
* Type of histogram counters used in the kernel.
|
||||
*/
|
||||
#ifdef GPROF4
|
||||
#define HISTCOUNTER unsigned
|
||||
#else
|
||||
#define HISTCOUNTER unsigned short
|
||||
#endif
|
||||
|
||||
/*
|
||||
* fraction of text space to allocate for histogram counters here, 1/2
|
||||
* Fraction of text space to allocate for histogram counters.
|
||||
* We allocate counters at the same or higher density as function
|
||||
* addresses, so that each counter belongs to a unique function.
|
||||
* A lower density of counters would give less resolution but a
|
||||
* higher density would be wasted.
|
||||
*
|
||||
* Assume that function addresses are at least 4-byte-aligned.
|
||||
* It would be better to get the linker to align functions more
|
||||
* strictly so that we could use smaller tables.
|
||||
*/
|
||||
#define HISTFRACTION 2
|
||||
#define FUNCTION_ALIGNMENT 4
|
||||
#define HISTFRACTION (FUNCTION_ALIGNMENT / sizeof(HISTCOUNTER) == 0 \
|
||||
? 1 : FUNCTION_ALIGNMENT / sizeof(HISTCOUNTER))
|
||||
|
||||
/*
|
||||
* Fraction of text space to allocate for from hash buckets.
|
||||
@ -90,7 +105,23 @@ struct gmonhdr {
|
||||
* profiling data structures without (in practice) sacrificing
|
||||
* any granularity.
|
||||
*/
|
||||
#define HASHFRACTION 2
|
||||
/*
|
||||
* XXX I think the above analysis completely misses the point. I think
|
||||
* the point is that addresses in different functions must hash to
|
||||
* different values. Since the hash is essentially division by
|
||||
* sizeof(unsigned short), the correct formula is:
|
||||
*
|
||||
* HASHFRACTION = MIN_FUNCTION_ALIGNMENT / sizeof(unsigned short)
|
||||
*
|
||||
* Note that he unsigned short here has nothing to do with the one for
|
||||
* HISTFRACTION.
|
||||
*
|
||||
* Hash collisions from a two call sequence don't matter. They get
|
||||
* handled like collisions for calls to different addresses from the
|
||||
* same address through a function pointer.
|
||||
*/
|
||||
#define HASHFRACTION (FUNCTION_ALIGNMENT / sizeof(unsigned short) == 0 \
|
||||
? 1 : FUNCTION_ALIGNMENT / sizeof(unsigned short))
|
||||
|
||||
/*
|
||||
* percent of text space to allocate for tostructs with a minimum.
|
||||
@ -132,17 +163,33 @@ struct rawarc {
|
||||
*/
|
||||
struct gmonparam {
|
||||
int state;
|
||||
u_short *kcount;
|
||||
HISTCOUNTER *kcount;
|
||||
u_long kcountsize;
|
||||
u_short *froms;
|
||||
u_long fromssize;
|
||||
struct tostruct *tos;
|
||||
u_long tossize;
|
||||
long tolimit;
|
||||
u_long lowpc;
|
||||
u_long highpc;
|
||||
fptrint_t lowpc;
|
||||
fptrint_t highpc;
|
||||
u_long textsize;
|
||||
u_long hashfraction;
|
||||
u_long profrate;
|
||||
HISTCOUNTER *cputime_count;
|
||||
u_int cputime_overhead;
|
||||
u_int cputime_overhead_frac;
|
||||
u_int cputime_overhead_resid;
|
||||
u_int cputime_overhead_sub;
|
||||
HISTCOUNTER *mcount_count;
|
||||
u_int mcount_overhead;
|
||||
u_int mcount_overhead_frac;
|
||||
u_int mcount_overhead_resid;
|
||||
u_int mcount_overhead_sub;
|
||||
HISTCOUNTER *mexitcount_count;
|
||||
u_int mexitcount_overhead;
|
||||
u_int mexitcount_overhead_frac;
|
||||
u_int mexitcount_overhead_resid;
|
||||
u_int mexitcount_overhead_sub;
|
||||
};
|
||||
extern struct gmonparam _gmonparam;
|
||||
|
||||
@ -153,6 +200,7 @@ extern struct gmonparam _gmonparam;
|
||||
#define GMON_PROF_BUSY 1
|
||||
#define GMON_PROF_ERROR 2
|
||||
#define GMON_PROF_OFF 3
|
||||
#define GMON_PROF_HIRES 4
|
||||
|
||||
/*
|
||||
* Sysctl definitions for extracting profiling information from the kernel.
|
||||
|
@ -1,5 +1,5 @@
|
||||
# From: @(#)Makefile 8.3 (Berkeley) 1/7/94
|
||||
# $Id: Makefile,v 1.62 1995/10/03 12:29:28 bde Exp $
|
||||
# $Id: Makefile,v 1.63 1995/10/23 16:44:22 peter Exp $
|
||||
|
||||
# XXX MISSING: deroff diction graph learn plot
|
||||
# spell spline struct units xsend
|
||||
@ -25,6 +25,7 @@ SUBDIR= apply ar at banner basename biff cal calendar \
|
||||
unexpand unifdef uniq unvis users uudecode uuencode vacation \
|
||||
vgrind vi vis w wall wc what whereis which who whois window \
|
||||
write xargs xinstall xstr yacc yes ypcat ypmatch ypwhich
|
||||
SUBDIR+=gprof4
|
||||
|
||||
.if !exists(../secure) || defined(NOSECURE)
|
||||
SUBDIR+=telnet
|
||||
|
14
usr.bin/gprof4/Makefile
Normal file
14
usr.bin/gprof4/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
# This was cloned from the Makefile for gprof by changing PROG from gprof
|
||||
# to gprof4, adding NOMAN and PATH, adding -DGPROF4 to CFLAGS and deleting
|
||||
# beforeinstall.
|
||||
|
||||
# @(#)Makefile 5.17 (Berkeley) 5/11/90
|
||||
|
||||
PROG= gprof4
|
||||
NOMAN= noman
|
||||
SRCS= gprof.c arcs.c dfn.c lookup.c ${MACHINE}.c hertz.c \
|
||||
printgprof.c printlist.c
|
||||
CFLAGS+=-DGPROF4
|
||||
.PATH: ${.CURDIR}/../../usr.bin/gprof
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -88,6 +88,11 @@ will configure a system for profiling; for example,
|
||||
.Xr kgmon 8
|
||||
and
|
||||
.Xr gprof 1 .
|
||||
If two or more
|
||||
.Fl p
|
||||
options are supplied,
|
||||
.Nm config
|
||||
will configure a system for high resolution profiling.
|
||||
.It Fl n
|
||||
If the
|
||||
.Fl n
|
||||
|
@ -157,8 +157,10 @@ makefile()
|
||||
}
|
||||
fprintf(ofp, "KERN_IDENT=%s\n", raise(ident));
|
||||
fprintf(ofp, "IDENT=");
|
||||
if (profiling)
|
||||
if (profiling >= 1)
|
||||
fprintf(ofp, " -DGPROF");
|
||||
if (profiling >= 2)
|
||||
fprintf(ofp, " -DGPROF4 -DGUPROF");
|
||||
|
||||
if (cputype == 0) {
|
||||
printf("cpu type must be specified\n");
|
||||
@ -202,8 +204,10 @@ makefile()
|
||||
fprintf(ofp, "%s=%s\n", op->op_name, op->op_value);
|
||||
if (debugging)
|
||||
fprintf(ofp, "DEBUG=-g\n");
|
||||
if (profiling)
|
||||
if (profiling >= 1)
|
||||
fprintf(ofp, "PROF=-pg\n");
|
||||
if (profiling >= 2)
|
||||
fprintf(ofp, "PROF+=-mprofiler-epilogue\n");
|
||||
while (fgets(line, BUFSIZ, ifp) != 0) {
|
||||
if (*line != '%') {
|
||||
fprintf(ofp, "%s", line);
|
||||
|
@ -39,7 +39,7 @@
|
||||
.Nd generate a dump of the operating system's profile buffers
|
||||
.Sh SYNOPSIS
|
||||
.Nm kgmon
|
||||
.Op Fl bhpr
|
||||
.Op Fl Bbhpr
|
||||
.Op Fl M core
|
||||
.Op Fl N system
|
||||
.Sh DESCRIPTION
|
||||
@ -62,8 +62,10 @@ file suitable for later analysis by
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl B
|
||||
Resume the collection of high resolution profile data.
|
||||
.It Fl b
|
||||
Resume the collection of profile data.
|
||||
Resume the collection of low resolution profile data.
|
||||
.It Fl h
|
||||
Stop the collection of profile data.
|
||||
.It Fl p
|
||||
@ -86,6 +88,8 @@ default ``/kernel''.
|
||||
.El
|
||||
.Pp
|
||||
If neither
|
||||
.Fl B
|
||||
nor
|
||||
.Fl b
|
||||
nor
|
||||
.Fl h
|
||||
@ -96,6 +100,9 @@ flag is specified and profile data is being collected,
|
||||
profiling will be momentarily suspended,
|
||||
the operating system profile buffers will be dumped,
|
||||
and profiling will be immediately resumed.
|
||||
.Pp
|
||||
The profile buffers should be reset when the resolution
|
||||
of the profile data is changed.
|
||||
.Sh FILES
|
||||
.Bl -tag -width /dev/kmemx -compact
|
||||
.It Pa /kernel
|
||||
|
@ -69,7 +69,7 @@ struct kvmvars {
|
||||
struct gmonparam gpm;
|
||||
};
|
||||
|
||||
int bflag, hflag, kflag, rflag, pflag;
|
||||
int Bflag, bflag, hflag, kflag, rflag, pflag;
|
||||
int debug = 0;
|
||||
void setprof __P((struct kvmvars *kvp, int state));
|
||||
void dumpstate __P((struct kvmvars *kvp));
|
||||
@ -87,7 +87,7 @@ main(int argc, char **argv)
|
||||
seteuid(getuid());
|
||||
kmemf = NULL;
|
||||
system = NULL;
|
||||
while ((ch = getopt(argc, argv, "M:N:bhpr")) != EOF) {
|
||||
while ((ch = getopt(argc, argv, "M:N:Bbhpr")) != EOF) {
|
||||
switch((char)ch) {
|
||||
|
||||
case 'M':
|
||||
@ -99,6 +99,10 @@ main(int argc, char **argv)
|
||||
system = optarg;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
Bflag = 1;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
bflag = 1;
|
||||
break;
|
||||
@ -117,7 +121,7 @@ main(int argc, char **argv)
|
||||
|
||||
default:
|
||||
(void)fprintf(stderr,
|
||||
"usage: kgmon [-bhrp] [-M core] [-N system]\n");
|
||||
"usage: kgmon [-Bbhrp] [-M core] [-N system]\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -140,6 +144,8 @@ main(int argc, char **argv)
|
||||
mode = getprof(&kvmvars);
|
||||
if (hflag)
|
||||
disp = GMON_PROF_OFF;
|
||||
else if (Bflag)
|
||||
disp = GMON_PROF_HIRES;
|
||||
else if (bflag)
|
||||
disp = GMON_PROF_ON;
|
||||
else
|
||||
@ -151,7 +157,12 @@ main(int argc, char **argv)
|
||||
if (accessmode == O_RDWR)
|
||||
setprof(&kvmvars, disp);
|
||||
(void)fprintf(stdout, "kgmon: kernel profiling is %s.\n",
|
||||
disp == GMON_PROF_OFF ? "off" : "running");
|
||||
disp == GMON_PROF_OFF ? "off" :
|
||||
disp == GMON_PROF_HIRES ? "running (high resolution)" :
|
||||
disp == GMON_PROF_ON ? "running" :
|
||||
disp == GMON_PROF_BUSY ? "busy" :
|
||||
disp == GMON_PROF_ERROR ? "off (error)" :
|
||||
"in an unknown state");
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -176,8 +187,9 @@ openfiles(system, kmemf, kvp)
|
||||
"kgmon: profiling not defined in kernel.\n");
|
||||
exit(20);
|
||||
}
|
||||
if (!(bflag || hflag || rflag ||
|
||||
(pflag && state == GMON_PROF_ON)))
|
||||
if (!(Bflag || bflag || hflag || rflag ||
|
||||
(pflag &&
|
||||
(state == GMON_PROF_HIRES || state == GMON_PROF_ON))))
|
||||
return (O_RDONLY);
|
||||
(void)seteuid(0);
|
||||
if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
|
||||
@ -186,7 +198,8 @@ openfiles(system, kmemf, kvp)
|
||||
kern_readonly(state);
|
||||
return (O_RDONLY);
|
||||
}
|
||||
openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY;
|
||||
openmode = (Bflag || bflag || hflag || pflag || rflag)
|
||||
? O_RDWR : O_RDONLY;
|
||||
kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf);
|
||||
if (kvp->kd == NULL) {
|
||||
if (openmode == O_RDWR) {
|
||||
@ -221,15 +234,17 @@ kern_readonly(mode)
|
||||
{
|
||||
|
||||
(void)fprintf(stderr, "kgmon: kernel read-only: ");
|
||||
if (pflag && mode == GMON_PROF_ON)
|
||||
if (pflag && (mode == GMON_PROF_HIRES || mode == GMON_PROF_ON))
|
||||
(void)fprintf(stderr, "data may be inconsistent\n");
|
||||
if (rflag)
|
||||
(void)fprintf(stderr, "-r supressed\n");
|
||||
if (Bflag)
|
||||
(void)fprintf(stderr, "-B supressed\n");
|
||||
if (bflag)
|
||||
(void)fprintf(stderr, "-b supressed\n");
|
||||
if (hflag)
|
||||
(void)fprintf(stderr, "-h supressed\n");
|
||||
rflag = bflag = hflag = 0;
|
||||
rflag = Bflag = bflag = hflag = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -324,7 +339,9 @@ dumpstate(kvp)
|
||||
h.hpc = kvp->gpm.highpc;
|
||||
h.ncnt = kvp->gpm.kcountsize + sizeof(h);
|
||||
h.version = GMONVERSION;
|
||||
h.profrate = getprofhz(kvp);
|
||||
h.profrate = kvp->gpm.profrate;
|
||||
if (h.profrate == 0)
|
||||
h.profrate = getprofhz(kvp); /* ancient kernel */
|
||||
fwrite((char *)&h, sizeof(h), 1, fp);
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user