Usage:
hp2ps [flags] [<file>[.hp]]
The program
hp2ps converts a heap profile as produced
by the -h<break-down>
runtime option into a
PostScript graph of the heap profile. By convention, the file to
be processed by hp2ps has a
.hp
extension. The PostScript output is
written to <file>@.ps
. If
<file>
is omitted entirely, then the
program behaves as a filter.
hp2ps is distributed in
ghc/utils/hp2ps
in a GHC source
distribution. It was originally developed by Dave Wakeling as part
of the HBC/LML heap profiler.
The flags are:
-d
In order to make graphs more readable,
hp2ps sorts the shaded bands for each
identifier. The default sort ordering is for the bands with
the largest area to be stacked on top of the smaller ones.
The -d
option causes rougher bands (those
representing series of values with the largest standard
deviations) to be stacked on top of smoother ones.
-b
Normally, hp2ps puts the title of
the graph in a small box at the top of the page. However, if
the JOB string is too long to fit in a small box (more than
35 characters), then hp2ps will choose to
use a big box instead. The -b
option
forces hp2ps to use a big box.
-e<float>[in|mm|pt]
Generate encapsulated PostScript suitable for
inclusion in LaTeX documents. Usually, the PostScript graph
is drawn in landscape mode in an area 9 inches wide by 6
inches high, and hp2ps arranges for this
area to be approximately centred on a sheet of a4 paper.
This format is convenient of studying the graph in detail,
but it is unsuitable for inclusion in LaTeX documents. The
-e
option causes the graph to be drawn in
portrait mode, with float specifying the width in inches,
millimetres or points (the default). The resulting
PostScript file conforms to the Encapsulated PostScript
(EPS) convention, and it can be included in a LaTeX document
using Rokicki's dvi-to-PostScript converter
dvips.
-g
Create output suitable for the gs PostScript previewer (or similar). In this case the graph is printed in portrait mode without scaling. The output is unsuitable for a laser printer.
-l
Normally a profile is limited to 20 bands with
additional identifiers being grouped into an
OTHER
band. The -l
flag
removes this 20 band and limit, producing as many bands as
necessary. No key is produced as it won't fit!. It is useful
for creation time profiles with many bands.
-m<int>
Normally a profile is limited to 20 bands with
additional identifiers being grouped into an
OTHER
band. The -m
flag
specifies an alternative band limit (the maximum is
20).
-m0
requests the band limit to be
removed. As many bands as necessary are produced. However no
key is produced as it won't fit! It is useful for displaying
creation time profiles with many bands.
-p
Use previous parameters. By default, the PostScript
graph is automatically scaled both horizontally and
vertically so that it fills the page. However, when
preparing a series of graphs for use in a presentation, it
is often useful to draw a new graph using the same scale,
shading and ordering as a previous one. The
-p
flag causes the graph to be drawn using
the parameters determined by a previous run of
hp2ps on file
. These
are extracted from file@.aux
.
-s
Use a small box for the title.
-t<float>
Normally trace elements which sum to a total of less
than 1% of the profile are removed from the
profile. The -t
option allows this
percentage to be modified (maximum 5%).
-t0
requests no trace elements to be
removed from the profile, ensuring that all the data will be
displayed.
-c
Generate colour output.
-y
Ignore marks.
-?
Print out usage information.
(Notes kindly offered by Jan-Willem Maessen.)
The FOO.hp
file produced when you ask for the
heap profile of a program FOO
is a text file with a particularly
simple structure. Here's a representative example, with much of the
actual data omitted:
JOB "FOO -hC" DATE "Thu Dec 26 18:17 2002" SAMPLE_UNIT "seconds" VALUE_UNIT "bytes" BEGIN_SAMPLE 0.00 END_SAMPLE 0.00 BEGIN_SAMPLE 15.07 ... sample data ... END_SAMPLE 15.07 BEGIN_SAMPLE 30.23 ... sample data ... END_SAMPLE 30.23 ... etc. BEGIN_SAMPLE 11695.47 END_SAMPLE 11695.47
The first four lines (JOB
, DATE
, SAMPLE_UNIT
, VALUE_UNIT
) form a
header. Each block of lines starting with BEGIN_SAMPLE
and ending
with END_SAMPLE
forms a single sample (you can think of this as a
vertical slice of your heap profile). The hp2ps utility should accept
any input with a properly-formatted header followed by a series of
*complete* samples.
You can look at particular regions of your profile simply by loading a
copy of the .hp
file into a text editor and deleting the unwanted
samples. The resulting .hp
file can be run through hp2ps and viewed
or printed.
The .hp
file is generated incrementally as your
program runs. In principle, running hp2ps on the incomplete file
should produce a snapshot of your program's heap usage. However, the
last sample in the file may be incomplete, causing hp2ps to fail. If
you are using a machine with UNIX utilities installed, it's not too
hard to work around this problem (though the resulting command line
looks rather Byzantine):
head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \ | hp2ps > FOO.ps
The command fgrep -n END_SAMPLE FOO.hp finds the
end of every complete sample in FOO.hp
, and labels each sample with
its ending line number. We then select the line number of the last
complete sample using tail and cut. This is used as a
parameter to head; the result is as if we deleted the final
incomplete sample from FOO.hp
. This results in a properly-formatted
.hp file which we feed directly to hp2ps.
The gv and ghostview programs have a "watch file" option can be used to view an up-to-date heap profile of your program as it runs. Simply generate an incremental heap profile as described in the previous section. Run gv on your profile:
gv -watch -seascape FOO.ps
If you forget the -watch
flag you can still select
"Watch file" from the "State" menu. Now each time you generate a new
profile FOO.ps
the view will update automatically.
This can all be encapsulated in a little script:
#!/bin/sh head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \ | hp2ps > FOO.ps gv -watch -seascape FOO.ps & while [ 1 ] ; do sleep 10 # We generate a new profile every 10 seconds. head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \ | hp2ps > FOO.ps done
Occasionally gv will choke as it tries to read an incomplete copy of
FOO.ps
(because hp2ps is still running as an update
occurs). A slightly more complicated script works around this
problem, by using the fact that sending a SIGHUP to gv will cause it
to re-read its input file:
#!/bin/sh head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \ | hp2ps > FOO.ps gv FOO.ps & gvpsnum=$! while [ 1 ] ; do sleep 10 head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \ | hp2ps > FOO.ps kill -HUP $gvpsnum done