Date: Wed, 23 May 2001 22:02:10 +0300 From: "Eli Zaretskii" Sender: halo1 AT zahav DOT net DOT il To: djgpp-workers AT delorie DOT com Message-Id: <7704-Wed23May2001220209+0300-eliz@is.elta.co.il> X-Mailer: Emacs 20.6 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.9 Subject: More control for profiling Reply-To: djgpp-workers AT delorie DOT com The following changes implement a couple of new functions (by mostly reusing existing code ;-) which let you control profiling programmatically from within the program being profiled. This is handly for profiling interactive programs which run for prolonged periods of time, but are most of that time idling waiting for user input. With these new functions, you can profile only specific parts of the program and at specific times, thus avoiding the dependence on how fast does the user type input. The interface is modeled on functions which exist on other platforms (GNU/Linux and FreeBSD at the very least). Could someone please tell what header should I put the function prototypes, though? One GNU/Linux box has monstartup in sys/gmon.h, but I wonder if this is standard, and if it makes sense to add a brand new header just for this? Any comments about the code and docs are welcome. --- src/libc/crt0/mcount.c~0 Sun Nov 2 10:18:44 1997 +++ src/libc/crt0/mcount.c Tue May 22 11:12:46 2001 @@ -9,13 +9,14 @@ #include #include #include +#include /* header of a GPROF type file */ typedef struct { - long low; - long high; - long nbytes; + unsigned long low; + unsigned long high; + unsigned long nbytes; } header; /* entry of a GPROF type file @@ -28,8 +29,9 @@ typedef struct { /* internal form - sizeof(MTAB) is 4096 for efficiency */ +#define N_MTABE 341 typedef struct MTAB { - MTABE calls[341]; + MTABE calls[N_MTABE]; struct MTAB *prev; } MTAB; @@ -39,8 +41,12 @@ static int mcount_skip = 1; static int histlen; static MTAB *mtab=0; +extern unsigned start __asm__ ("start"); +#define START (unsigned)&start extern int etext; +static int profiling_p; + /* called by functions. Use the pointer it provides to cache ** the last used MTABE, so that repeated calls to/from the same ** pair works quickly - no lookup. @@ -58,13 +64,20 @@ void mcount(int _to) asm("movl %%edx,%0" : "=g" (cache)); /* obtain the cached pointer */ + mcount_skip = 1; + /* Do nothing if profiling is disabled. */ + if (!profiling_p) + return; + if (&_to < &etext) *(int *)(-1) = 0; /* fault! */ - mcount_skip = 1; to = *((&_to)-1) - 12; ebp = *((&_to)-2); /* glean the caller's return address from the stack */ from = ((int *)ebp)[1]; + /* Do nothing if the FROM address is outside the sampling range. */ + if (from < h.low || from >= h.high) + return; if (*cache && ((*cache)->from == from) && ((*cache)->to == to)) { /* cache paid off - works quickly */ @@ -77,7 +90,7 @@ void mcount(int _to) mtabi = -1; for (m=mtab; m; m=m->prev) { - for (i=0; i<341; i++) + for (i=0; icalls[i].from == 0) { @@ -118,27 +131,72 @@ void mcount(int _to) mcount_skip = 0; } +/* ARGSUSED */ +static void +mcount_tick(int _x) +{ + unsigned bin; + + if(!mcount_skip) { + bin = __djgpp_exception_state->__eip; + if(bin >= h.low && bin < h.high) { + bin = (bin - h.low) / 4; /* 4 EIP's per bin */ + histogram[bin]++; + } + } +} + +/* start or stop profiling. */ +int moncontrol(int); +int +moncontrol(int mode) +{ + struct itimerval new_values; + int old_mode = profiling_p; + + if (mode) + { + /* here, do whatever it takes to initialize the timer interrupt */ + signal(SIGPROF, mcount_tick); + + /* 18.2 tics per second */ + new_values.it_value.tv_usec = new_values.it_interval.tv_usec = 5494; + new_values.it_value.tv_sec = new_values.it_interval.tv_sec = 0; + + setitimer(ITIMER_PROF, &new_values, NULL); + + profiling_p = 1; + mcount_skip = 0; + } + else + { + mcount_skip = 1; + profiling_p = 0; + + /* disable timer */ + new_values.it_value.tv_usec = new_values.it_interval.tv_usec = 0; + new_values.it_value.tv_sec = new_values.it_interval.tv_sec = 0; + setitimer(ITIMER_PROF, &new_values, NULL); + } + + return old_mode; +} + /* this is called during program exit (installed by atexit). */ static void mcount_write(void) { MTAB *m; int i, f; - struct itimerval new_values; - mcount_skip = 1; - - /* disable timer */ - new_values.it_value.tv_usec = new_values.it_interval.tv_usec = 0; - new_values.it_value.tv_sec = new_values.it_interval.tv_sec = 0; - setitimer(ITIMER_PROF, &new_values, NULL); + moncontrol(0); f = open("gmon.out", O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666); write(f, &h, sizeof(header)); write(f, histogram, histlen); for (m=mtab; m; m=m->prev) { - for (i=0; i<341; i++) + for (i=0; icalls[i].from == 0) break; write(f, m->calls, i*12); @@ -146,48 +204,34 @@ mcount_write(void) close(f); } -extern unsigned start __asm__ ("start"); -#define START (unsigned)&start -extern int etext; - -/* ARGSUSED */ -static void -mcount_tick(int _x) +/* set up profiling between LOWPC and HIGHPC, and start collecting. */ +int monstartup(void *, void *); +int +monstartup(void *lowpc, void *highpc) { - unsigned bin; - - if(!mcount_skip) { - bin = __djgpp_exception_state->__eip; - if(bin >= START && bin <= (unsigned)&etext) { - bin = (bin - START) / 4; /* 4 EIP's per bin */ - histogram[bin]++; - } - } -} - -/* this is called to initialize profiling before the program starts */ -void _mcount_init(void); -void -_mcount_init(void) -{ - struct itimerval new_values; - - h.low = START; - h.high = (int)&etext; - histlen = (h.high-h.low)/4*sizeof(short); + if ((unsigned *)lowpc < (unsigned *)&start + || (unsigned *)highpc > (unsigned *)&etext + /* Don't let them redefine histogram limits during the run. */ + || h.nbytes > 0) + return -1; + + h.low = (unsigned long)lowpc; + h.high = (unsigned long)highpc; + histlen = (h.high - h.low) / 4*sizeof(short); h.nbytes = sizeof(header) + histlen; histogram = (short *)sbrk(histlen); memset(histogram, 0, histlen); atexit(mcount_write); - /* here, do whatever it takes to initialize the timer interrupt */ - signal(SIGPROF, mcount_tick); + moncontrol(1); - /* 18.2 tics per second */ - new_values.it_value.tv_usec = new_values.it_interval.tv_usec = 5494; - new_values.it_value.tv_sec = new_values.it_interval.tv_sec = 0; - - setitimer(ITIMER_PROF, &new_values, NULL); + return 0; +} - mcount_skip = 0; +/* this is called to initialize profiling before the program starts */ +void _mcount_init(void); +void +_mcount_init(void) +{ + monstartup(&start, &etext); } --- /dev/null Tue May 22 11:52:37 2001 +++ src/libc/crt0/mcount.txh Tue May 22 11:52:08 2001 @@ -0,0 +1,104 @@ +@c ---------------------------------------------------------------------- +@node monstartup, profiling +@subheading Syntax + +@example +#include + +int monstartup (void *lowpc, void *highpc); +@end example + +@subheading Description + +This function allows to selectively collect profiling information for a +specific range of addresses. The arguments specify the address range +that is to be sampled: the lowest address is given by @var{lowpc} and +the highest is just below @var{highpc}. @code{monstartup} arranges for +the profiling data to be gathered and written at program exit time, and +then calls the @code{moncontrol} function (@pxref{moncontrol}) to start +profiling. + +The call graph printed by the +@code{gprof} utility will only include functions in this range compiled +with the @samp{-pg} option, but @code{EIP} sampling triggered by the +timer tick will measure execution time of all the functions in the +specified range. + +This function should be called by a program which was not linked with +the @samp{-pg} linker switch. If @samp{-pg} @emph{was} used during +linking, @code{monstartup} is called automatically by the startup code +with arguments which span the entire range of executable addresses in +the program, from the program's entry point to the highest code segment +address. + +Only the first call to this function has an effect; any further calls +will do nothing and return a failure indication. (In particular, in a +program linked with @samp{-pg}, this function always fails, since the +startup code already called it.) This is because @code{monstartup} sets +up some internal data structures which cannot be resized if a different +address range is requested. + +Profiling begins on return from this function. You can use +@code{moncontrol} (@pxref{moncontrol}) to turn profiling off and on. + +@subheading Return Value + +Zero on success, non-zero on failure. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@xref{moncontrol}. + +@c ---------------------------------------------------------------------- +@node moncontrol, profiling +@subheading Syntax + +@example +#include + +int moncontrol (int mode); +@end example + +@subheading Description + +This function allows to control collection of profiling information +during the program run. Profiling begins when a program linked with the +@samp{-pg} option starts, or when @code{monstartup} is called +(@pxref{monstartup}). To stop the collection of histogram ticks and +function call counts, call @code{moncontrol} with a zero argument +@var{mode}; this stops the timer used to sample the program counter +(@code{EIP}) values and disables the code which counts how many times +each function compiled with @samp{-pg} was called. To resume collection +of profile data, call @code{moncontrol} with a non-zero argument. + +Note that the profiling output is always written to the file +@file{gmon.out} at program exit time, regardless of whether profiling is +on or off. + +@subheading Return Value + +@code{moncontrol} returns the previous state of profiling: zero if it +was turned off, non-zero if it was on. + +@subheading Portability + +@portability !ansi, !posix + +@subheading Example + +@example + extern void my_func(); + extern void my_func_end(); + /* Profile only one function. */ + monstartup(my_func, my_func_end); + ... + /* Stop profiling. */ + moncontrol(0); + ... + /* Resume profiling. */ + moncontrol(1); +@end example --- include/libc/stubs.h~0 Sun Sep 7 18:07:18 1997 +++ include/libc/stubs.h Tue May 22 10:52:24 2001 @@ -45,6 +45,8 @@ #define getitimer __getitimer #define gettimeofday __gettimeofday #define modfl __modfl +#define moncontrol __moncontrol +#define monstartup __monstartup #define movedata __movedata #define pow10 __pow10 #define pow2 __pow2