Statprof
========

(statprof) is a fairly simple statistical profiler for Guile.

A simple use of statprof would look like this:

     (statprof-reset 0 50000 #t)
     (statprof-start)
     (do-something)
     (statprof-stop)
     (statprof-display)

This would reset statprof, clearing all accumulated statistics, then start profiling, run some code, stop profiling, and finally display a gprof flat-style table of statistics which will look something like this:

       %   cumulative      self              self    total
      time    seconds   seconds    calls  ms/call  ms/call  name
      35.29      0.23      0.23     2002     0.11     0.11  -
      23.53      0.15      0.15     2001     0.08     0.08  positive?
      23.53      0.15      0.15     2000     0.08     0.08  +
      11.76      0.23      0.08     2000     0.04     0.11  do-nothing
       5.88      0.64      0.04     2001     0.02     0.32  loop
       0.00      0.15      0.00        1     0.00   150.59  do-something
      ...

All of the numerical data with the exception of the calls column is statistically approximate. In the following column descriptions, and in all of statprof, "time" refers to execution time (both user and system), not wall clock time.

% time
     The percent of the time spent inside the procedure itself (not counting children).

cumulative seconds
     The total number of seconds spent in the procedure, including children.

self seconds
     The total number of seconds spent in the procedure itself (not counting children).

calls
     The total number of times the procedure was called.

self ms/call
     The average time taken by the procedure itself on each call, in ms.

total ms/call
     The average time taken by each call to the procedure, including time spent in child functions.

name
     The name of the procedure.

The profiler uses eq? and the procedure object itself to identify the procedures, so it won't confuse different procedures with the same name. They will show up as two different rows in the output.

Right now the profiler is quite simplistic. I cannot provide call-graphs or other higher level information. What you see in the table is pretty much all there is. Patches are welcome :-)

Implementation notes
====================

The profiler works by setting the unix profiling signal ITIMER_PROF to go off after the interval you define in the call to statprof-reset. When the signal fires, a sampling routine is run which looks at the current procedure that's executing, and then crawls up the stack, and for each procedure encountered, increments that procedure's sample count. Note that if a procedure is encountered multiple times on a given stack, it is only counted once. After the sampling is complete, the profiler resets profiling timer to fire again after the appropriate interval.

Meanwhile, the profiler keeps track, via get-internal-run-time, how much CPU time (system and user -- which is also what ITIMER_PROF tracks), has elapsed while code has been executing within a statprof-start/stop block.

The profiler also tries to avoid counting or timing its own code as much as possible.

Usage
=====

statprof-active? Returns #t if statprof-start has been called more times than statprof-stop, #f otherwise.

statprof-start Start the profiler.

statprof-stop Stop the profiler.

statprof-reset sample-seconds sample-microseconds count-calls? [full-stacks?] Reset the statprof sampler interval to SAMPLE-SECONDS and SAMPLE-MICROSECONDS. If COUNT-CALLS? is true, arrange to instrument procedure calls as well as collecting statistical profiling data. If FULL-STACKS? is true, collect all sampled stacks into a list for later analysis.

Enables traps and debugging as necessary.

statprof-accumulated-time Returns the time accumulated during the last statprof run.

statprof-sample-count Returns the number of samples taken during the last statprof run.

statprof-fold-call-data proc init Fold PROC over the call-data accumulated by statprof. Cannot be called while statprof is active. PROC should take two arguments, (CALL-DATA PRIOR-RESULT).

Note that a given proc-name may appear multiple times, but if it does, it represents different functions with the same name.

statprof-proc-call-data proc Returns the call-data associated with PROC, or #f if none is available.

statprof-call-data-name cd

statprof-call-data-calls cd

statprof-call-data-cum-samples cd

statprof-call-data-self-samples cd

statprof-call-data->stats call-data Returns an object of type statprof-stats.

statprof-stats-proc-name stats

statprof-stats-%-time-in-proc stats

statprof-stats-cum-secs-in-proc stats

statprof-stats-self-secs-in-proc stats

statprof-stats-calls stats

statprof-stats-self-secs-per-call stats

statprof-stats-cum-secs-per-call stats

statprof-display . _ Displays a gprof-like summary of the statistics collected. Unless an optional PORT argument is passed, uses the current output port.

statprof-display-anomolies A sanity check that attempts to detect anomolies in statprof's statistics.

statprof-fetch-stacks Returns a list of stacks, as they were captured since the last call to statprof-reset.

Note that stacks are only collected if the FULL-STACKS? argument to statprof-reset is true.

statprof-fetch-call-tree
Return a call tree for the previous statprof run.

The return value is a list of nodes, each of which is of the type:
@@code
 node ::= (@@var@{proc@} @@var@{count@} . @@var@{nodes@})
@@end code

statprof thunk [#:loop] [#:hz] [#:count-calls?] [#:full-stacks?] Profiles the execution of THUNK.

The stack will be sampled HZ times per second, and the thunk itself will be called LOOP times.

If COUNT-CALLS? is true, all procedure calls will be recorded. This operation is somewhat expensive.

If FULL-STACKS? is true, at each sample, statprof will store away the whole call tree, for later analysis. Use statprof-fetch-stacks or statprof-fetch-call-tree to retrieve the last-stored stacks.

with-statprof args Profiles the expressions in its body.

Keyword arguments:

#:loop
     Execute the body LOOP number of times, or #f for no looping

     default: #f

#:hz
     Sampling rate

     default: 20

#:count-calls?
     Whether to instrument each function call (expensive)

     default: #f

#:full-stacks?
     Whether to collect away all sampled stacks into a list

     default: #f

gcprof thunk [#:loop] [#:full-stacks?] Do an allocation profile of the execution of THUNK.

The stack will be sampled soon after every garbage collection, yielding an approximate idea of what is causing allocation in your program.

Since GC does not occur very frequently, you may need to use the LOOP parameter, to cause THUNK to be called LOOP times.

If FULL-STACKS? is true, at each sample, statprof will store away the whole call tree, for later analysis. Use statprof-fetch-stacks or statprof-fetch-call-tree to retrieve the last-stored stacks.