Hat - a Haskell tracer for nhc98


Hat, the nhc98 tracer, is applicable to a wide class of Haskell computations, including those that:

  1. run to completion producing output, though perhaps not the output expected; or
  2. halt with a run-time error, such as a pattern-matching failure or a black hole; or
  3. appear to be stuck in an a recursive loop, whether or not output is being produced.

To obtain a trace of a computation:

  1. Compile all modules of the program with the -T option; also specify -T at link-time. Using hmake -T does all the necessary compiling and linking automatically.

  2. Run the program.

    If the program halts, any normal output from the program will be followed by a summary line indicating whether it terminated correctly or if there was an error. The trace is now available in a file called <program>.hat.

    If it seems the program is stuck in an unproductive loop force it to halt by keying an interrupt (usually ctrl-C).

  3. This is now a variety of tools you can use to explore this trace.

To explore a trace using the hat-trail browser

  1. If there is not already a trace browser running, start one by giving the shell command hat-trail, optionally adding the name of the program whose trail you wish to browse.

  2. ****
    Wrong here
    ****
    With a traced Haskell program in the trace-available state, click over the `connect' button in the upper part of the trace-browser window.

    Any program output is shown in the lower panel of the trace browser. Clicking over a section of the output causes a parent expression for that output to be displayed in the upper trace panel. There is one section of output for each monadic output operation performed by the program, and any currently selected section is shown in blue.

    If a run-time error has occurred, or the computation has been interrupted, the initial trace panel displays the expression under evaluation at the time.

  3. Moving the mouse over expressions in the trace panel causes the display of various forms of highlighting. The most important ones are:
    1. a red box highlights any currently-selected subexpression S;
    2. expressions with the same parent as S (siblings) are shown in blue; however, subexpressions of these siblings which have a different parent are not shown in blue;
    3. if the parent redex of S is already on display it is shown with a background highlight in yellow.

  4. Beyond the normal syntax for Haskell expressions, five special symbols may occur in trace expressions:

    bottom the undefined value, as usual;
    empty box a placeholder for a subexpression suppressed for the time-being (eg. to avoid over-wide displays);
    slant box a placeholder for an expression that is not available because it is part of a trusted computation not recorded in the trace -- however, the parent redex is available;
    cross box a placeholder for an expression that is not available -- nor are its ancestral redexes, as they have been pruned from the trace;
    within shown between the values of case subjects, conditions or guards and those of the expressions they belong to.

    Mouse clicks in the trace panel have the following effects:

    left: show the parent redex of S, if any; (or, if the parent is already on display, remove it along with any of its ancestors also on display)
    middle: if S is a place-holder, expand it; (or, if not, contract S to a place-holder)
    right: show where S originates in the source program, displayed in lower panel. (If S is a name, a plain right-click requests the corresponding point of use, but shift-right-click requests the definition.)

Partial traces (avoiding detail and saving space)

There are two ways to specify a partial trace:

  1. Trusting: details of computation within specified modules or functions are omitted from the trace. The choice of which components to trust and which to suspect can be made at compile-time but can also be altered at run-time.

    At compile-time, use -T -trusted to compile a trusted module.

    At run-time, after +RTS alter the compile-time specification of trusted modules or functions by -dt name, or suspected ones by -ds name. Name functions in full with the module name as prefix: for example, -ds BinaryTree.Delete.

    The flags -dtr and -dsr can be used to trust or suspect not only the named module but also all other modules that it (recursively) depends on.

    By default only the Prelude is trusted.

  2. Pruning:at each garbage collection, trace structure more than a specified distance k from all continuing evaluation points is discarded. Any pruning is specified at run-time: for example, +RTS -dk2 -RTS. Just using +RTS -dk -RTS prunes the trace structure to free just enough space to continue computation.

    By default there is no pruning.

Four limiting factors

  1. The tracer does not currently handle programs involving I/O other than to standard input and/or standard output.
  2. List comprehensions are traced in terms of their explicit translations to higher-order function applications.
  3. Traced execution can take 10-30 times longer than normal.
  4. Traced execution without pruning requires heap memory in rough proportion to the length of the computation. (As a rough rule of thumb, 100 bytes of heap memory are required for each traced reduction.)
We plan to address all four of these limitations in future versions.


The latest updates to these pages are available on the WWW from http://www.haskell.org/nhc98/

27th April 2001
York Functional Programming Group
Colin.Runciman@cs.york.ac.uk