6.5. hp2ps––heap profile to PostScript


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:


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.


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.


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.


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.


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.


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.


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.


Use a small box for the title.


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.


Generate colour output.


Ignore marks.


Print out usage information.

6.5.1. Manipulating the hp file

(Notes kindly offered by Jan-Willhem 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:

DATE "Thu Dec 26 18:17 2002"
SAMPLE_UNIT "seconds"
VALUE_UNIT "bytes"
  ... sample data ...
  ... sample data ...
... etc.
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.

6.5.2. Zooming in on regions of your profile

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.

6.5.3. Viewing the heap profile of a running program

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.

6.5.4. Viewing a heap profile in real time

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:

  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

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:

  head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \
    | hp2ps > FOO.ps
  gv FOO.ps &
  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