Personal tools

Debugging

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(Note about locating a library function failure)
(wiki syntax)
Line 149: Line 149:
   
 
== Other tricks ==
 
== Other tricks ==
  +
 
* If you use GHC, you can get a stack trace in the console when your program fails with an error condition. See the [http://www.haskell.org/ghc/docs/latest/html/users_guide/runtime-control.html#rts-options-debugging manual page]
 
* If you use GHC, you can get a stack trace in the console when your program fails with an error condition. See the [http://www.haskell.org/ghc/docs/latest/html/users_guide/runtime-control.html#rts-options-debugging manual page]
   
  +
=== Locating a failure in a library function ===
   
Note about locating a library function failure
+
The simplest way to provide locating in the source code a mismatch
----------------------------------------------
+
run-time error in the library functions:
  +
<haskell>
  +
head, tail, fromJust
  +
</haskell>
   
Maybe, the simplest way to provide locating in the source code a mismatch
+
and others is to avoid these functions and to use explicit matching instead.
run-time error in the library functions
 
head, tail, fromJust, and such,
 
   
is to avoid these functions and to use explicit matching instead.
+
For example, consider:
   
For example, for the program g x = h $ fromJust $ f x,
+
<haskell>
  +
g x = h $ fromJust $ f x,
  +
</haskell>
   
ghc-6.6 often looses the reference to g, f, and h in its run-time error
+
ghc-6.6 often looses the reference to <hask>g</hask>, <hask>f</hask>,
report -- when f returns Nothing.
+
and <hask>h</hask> in its run-time error report, when <hask>f</hask>
But for the program
+
returns <hask>Nothing</hask>.
g x = let Just y = f x in h y,
 
   
the ghc run-time report is like this:
+
But for the program:
   
Main: M1.hs:9:11-22:
+
<haskell>
Irrefutable pattern failed for pattern Data.Maybe.Just y
+
g x = let Just y = f x in h y,
  +
</haskell>
   
-- it points to the source line!
+
GHC reports:
   
  +
<haskell>
  +
Main: M1.hs:9:11-22:
  +
Irrefutable pattern failed for pattern Data.Maybe.Just y
  +
</haskell>
   
  +
Indicating the source of the failure.
   
 
[[Category:Tools]]
 
[[Category:Tools]]

Revision as of 12:18, 15 November 2006

Contents

1 Printf and friends

The simplest approach is to use Debug.Trace.trace:

trace :: String -> a -> a
"When called, trace outputs the string in its first argument, before returning the second argument as its result.'"

A common idiom to trace a function is:

myfun a b | trace ("myfun " ++ show a ++ " " ++ show b) False = undefined
myfun a b = ...

The advantage is that disabling and enabling the trace takes only one line comment.

You must keep in mind that due to lazy evaluation your traces will only print if the value they wrap is ever demanded.


A more powerful alternative for this approach is Hood. Even if it hasn't been updated in some time, Hood works perfectly with the current ghc distribution. Even more, Hugs has it already integrated, see the manual page. Add an import Observe and start inserting observations in your code. For instance:

import Hugs.Observe

f'  = observe "Informative name for f" f 
f x = if odd x then x*2 else 0

And then in hugs:

Main> map f' [1..5]
[2,0,6,0,10]

>>>>>>> Observations <<<<<<

Informative name for f
  { \ 5  -> 10
  , \ 4  -> 0
  , \ 3  -> 6
  , \ 2  -> 0
  , \ 1  -> 2
  }

outputs a report of all the invocations of f and their result.

I have a handy bogus Hugs.Observe module with no-ops for the observations so that I don't need to remove them manually, expecting that the compiler will optimize them away.


2 The Safe Library

There is a safe library of functions from the Prelude that can crash, see the safe library. If you get an error message such as "pattern match failure, head []", you can then use headNote "extra information" to get a more detailed error message for that particular call to head. The safe library also has functions that return default values and wrap their computation in Maybe as required.

3 Offline analysis of traces

The most advanced debugging tools are based in offline analysis of traces. Hat is probably the most up-to-date tool for this, offering a comprehensive set of tools. Neil Mitchell has made available a Windows port of Hat at his site.

The disadvantage of these tools is that they are not always compatible with the latest libraries, so you can put them to use only in some cases.

Some Hat user should complete this section

4 Dynamic breakpoints in GHCi

Finally, the GHC/GHCiDebugger project aims to bring dynamic breakpoints and intermediate values observation to GHCi in a near future. Right now the tool is only available from the site as a modified version of GHC, so unfortunately you will have to compile it yourself if you want to have it.

This tool allows to set breakpoints in your code, directly from the GHCi command prompt. An example session:

*main:Main> :break add Main 2
Breakpoint set at (2,15)
*main:Main> qsort [10,9..1]
Local bindings in scope:
  x :: a, xs :: [a], left :: [a], right :: [a]
 
qsort2.hs:2:15-46> :sprint x
x = _
qsort2.hs:2:15-46> x
This is an untyped, unevaluated computation. You can use seq to 
force its evaluation and then :print to recover its type
qsort2.hs:2:15-46> seq x ()
() 
qsort2.hs:2:15-46> :p x
x - 10

Once a breakpoint is hit, you can explore the bindings in scope, as well as to evaluate any haskell expression, as you would do in a normal GHCi prompt. The ':print' command can be very useful to explore the lazyness of your code.

5 Source-located errors

LocH provides wrappers over

assert
for generating source-located exceptions and errors. Consider the use of a located
fromJust
:
import Debug.Trace.Location
import qualified Data.Map as M
import Data.Maybe
 
main = do print f
 
f = let m = M.fromList
                [(1,"1")
                ,(2,"2")
                ,(3,"3")]
        s = M.lookup 4 m
    in fromJustSafe assert s
 
fromJustSafe a s = check a (fromJust s)

This will result in:

$ ./a.out
a.out: A.hs:12:20-25: Maybe.fromJust: Nothing

This can be automated, using the 'loch' preprocessor, so a program failing with:

   $ ghc A.hs --make -no-recomp
   [1 of 1] Compiling Main             ( A.hs, A.o )
   Linking A ...
   $ ./A
   A: Maybe.fromJust: Nothing

Can be transformed to a src-located one by adding:

import Debug.Trace.Location

and then recompiling with the preprocessor on:

   $ ghc A.hs --make -pgmF loch -F -no-recomp
   [1 of 1] Compiling Main             ( A.hs, A.o )
   Linking A ...
   $ ./A
   A: A.hs:14:14-19: Maybe.fromJust: Nothing

6 Other tricks

  • If you use GHC, you can get a stack trace in the console when your program fails with an error condition. See the manual page

6.1 Locating a failure in a library function

The simplest way to provide locating in the source code a mismatch run-time error in the library functions:

head, tail, fromJust

and others is to avoid these functions and to use explicit matching instead.

For example, consider:

g x = h $ fromJust $ f x,
ghc-6.6 often looses the reference to
g
,
f
, and
h
in its run-time error report, when
f
returns
Nothing
.

But for the program:

g x = let Just y = f x in h y,

GHC reports:

    Main: M1.hs:9:11-22:
    Irrefutable pattern failed for pattern Data.Maybe.Just y

Indicating the source of the failure.