Difference between revisions of "Debugging"

From HaskellWiki
Jump to navigation Jump to search
m
m (the)
(34 intermediate revisions by 18 users not shown)
Line 1: Line 1:
  +
== Stack trace ==
  +
  +
  +
  +
=== General usage ===
  +
  +
Recent versions of GHC allow a dump of a stack trace (of all cost centres) when an exception is raised. In order to enable this, compile with <code>-prof</code>, and run with <code>+RTS -xc</code>. (Since only the cost centre stack will be printed, you may want to add <code>-auto-all -caf-all</code> to the compilation step to include all definitions in the trace.) Since GHC version 7.8, the function [http://hackage.haskell.org/package/base/docs/GHC-Stack.html#v:errorWithStackTrace errorWithStackTrace] can be used to programmatically dump the stack trace, see [http://www.reddit.com/r/haskell/comments/2fwwx3/how_to_get_approx_stack_traces_with_profiled/ How to get (approx) stack traces with profiled builds].
  +
  +
A more detailed list of options can be found [http://www.haskell.org/ghc/docs/latest/html/users_guide/runtime-control.html#rts-options-debugging in the RTS section] of the GHC user's guide.
  +
  +
  +
  +
=== Example ===
  +
  +
<haskell>
  +
-- test.hs
  +
crash = sum [1,2,3,undefined,5,6,7,8]
  +
main = print crash
  +
</haskell>
  +
  +
  +
> ghc-7.6.3 test.hs -prof -auto-all -caf-all && ./test +RTS -xc
  +
*** Exception (reporting due to +RTS -xc): (THUNK_2_0), stack trace:
  +
GHC.Err.CAF
  +
--> evaluated by: Main.crash,
  +
called from Main.CAF:crash_reH
  +
test: Prelude.undefined
  +
  +
 
== Printf and friends ==
 
== Printf and friends ==
  +
 
The simplest approach is to use <tt>Debug.Trace.trace</tt>:
 
The simplest approach is to use <tt>Debug.Trace.trace</tt>:
 
<pre>
 
<pre>
Line 15: Line 45:
 
You must keep in mind that due to lazy evaluation your traces will only print if the value they wrap is ever demanded.
 
You must keep in mind that due to lazy evaluation your traces will only print if the value they wrap is ever demanded.
   
  +
The trace function is located in the base package. The package [http://hackage.haskell.org/package/htrace htrace] defines a trace function similar to the one in the base package, but with indentation for better visual effect (see the [http://www.haskell.org/pipermail/haskell-cafe/2012-January/098847.html mailing list thread] for examples). Other tools can be found at [http://hackage.haskell.org/packages/#cat:Debug the debug category in Hackage].
   
  +
A more powerful alternative for this approach is [http://hackage.haskell.org/package/hood Hood]. Even if it hasn't been
A more powerful
 
alternative for this approach is [[www.haskell.org/hood Hood]]. Even if it hasn't been
 
 
updated in some time, Hood works perfectly with the current ghc
 
updated in some time, Hood works perfectly with the current ghc
 
distribution. Even more, Hugs has it already integrated, see the [http://cvs.haskell.org/Hugs/pages/users_guide/observe.html manual page]. Add an <tt>import Observe</tt> and start inserting observations in your code.
 
distribution. Even more, Hugs has it already integrated, see the [http://cvs.haskell.org/Hugs/pages/users_guide/observe.html manual page]. Add an <tt>import Observe</tt> and start inserting observations in your code.
Line 46: Line 76:
 
outputs a report of all the invocations of f and their result.
 
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.
+
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.
  +
  +
The [http://hackage.haskell.org/package/GHood GHood] package adds a graphical back-end to Hood. See also the [http://community.haskell.org/~claus/GHood/ GHood homepage].
  +
  +
  +
== The Safe Library ==
  +
  +
There is a safe library of functions from the Prelude that can crash, see [http://community.haskell.org/~ndm/safe/ the safe library]. If you get an error message such as "pattern match failure, head []", you can then use <tt>headNote "extra information"</tt> to get a more detailed error message for that particular call to <tt>head</tt>. The safe library also has functions that return default values and wrap their computation in <tt>Maybe</tt> as required.
   
   
 
== Offline analysis of traces ==
 
== Offline analysis of traces ==
The most advanced debugging tools are based in offline analysis of traces. [http://www.haskell.org/hat Hat] is probably the most up-to-date tool for this, offering a comprehensive set of tools. [[User:NeilMitchell|Neil Mitchell]] has made available a Windows port of Hat at [http://www-users.cs.york.ac.uk/~ndm/projects/windows.php his site].
+
The most advanced debugging tools are based in offline analysis of traces.
   
  +
=== Haskell Tracer HAT ===
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.
 
   
  +
[http://projects.haskell.org/hat/ Hat] is probably the most advanced tool for this, offering a comprehensive set of tools. [[User:NeilMitchell|Neil Mitchell]] has made available a Windows port of Hat at [http://projects.haskell.org/~ndm/hat/ his site].
''Some Hat user should complete this section''
 
   
  +
The disadvantage of traditional Haskell tracers is that they either need to transform the whole program or require a specialized run-time system. Therefore they are not always compatible with the latest libraries, so you can put them to use only in some cases.
== Dynamic breakpoints in GHCi ==
 
  +
Finally, the [[GHC/GHCiDebugger]] project aims to bring dynamic
 
  +
=== Hoed - The Lightweight Haskell Tracer and Debugger ===
breakpoints and intermediate values observation to GHCi in a near
 
  +
future. Right now the tool is only available from the site as a
 
  +
[https://github.com/MaartenFaddegon/Hoed Hoed] is a tracer/debugger that offers most of HATs functionality, and works with untransformed libraries. Hoed can therefore be used to debug much more programs than traditional tracer/debuggers.
modified version of GHC, so unfortunately you will have to compile it
 
  +
yourself if you want to have it.
 
  +
To locate a defect with Hoed you annotate suspected functions and compile as usual. Then you run your program, information about the annotated functions is collected. Finally you connect to a debugging session using a webbrowser.
  +
  +
== Dynamic breakpoints in GHCi ==
  +
  +
Finally, the [[GHC/GHCi debugger| GHCi debugger]] enables dynamic
  +
breakpoints and intermediate values observation.
   
 
This tool allows to set breakpoints in your code, directly from the GHCi command prompt. An example session:
 
This tool allows to set breakpoints in your code, directly from the GHCi command prompt. An example session:
 
<pre>
 
<pre>
*main:Main> :break add Main 2
+
*main:Main> :break Main 2
 
Breakpoint set at (2,15)
 
Breakpoint set at (2,15)
 
*main:Main> qsort [10,9..1]
 
*main:Main> qsort [10,9..1]
Line 74: Line 117:
 
x = _
 
x = _
 
qsort2.hs:2:15-46> x
 
qsort2.hs:2:15-46> x
  +
</pre>
This is an untyped, unevaluated computation. You can use seq to
 
force its evaluation and then :print to recover its type
+
This is an untyped, unevaluated computation. You can use <hask>seq</hask> to force its evaluation and then <code>:print</code> to recover its type
  +
<pre>
 
qsort2.hs:2:15-46> seq x ()
 
qsort2.hs:2:15-46> seq x ()
 
()
 
()
Line 82: Line 126:
 
</pre>
 
</pre>
   
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 <tt>':print'</tt> command can be very useful to explore the lazyness of your code.
+
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 <code>:print</code> command can be very useful to explore the laziness of your code.
   
== Catching Assert trick ==
 
   
  +
== Source-located errors ==
See the mail [http://www.mail-archive.com/haskell-cafe@haskell.org/msg13034.html message].
 
  +
  +
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/loch LocH] provides wrappers over
  +
<hask>assert</hask> for generating source-located exceptions and errors.
  +
  +
Consider the use of a located <hask>fromJust</hask>:
  +
  +
<haskell>
  +
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)
  +
</haskell>
  +
  +
This will result in:
  +
  +
<haskell>
  +
$ ./a.out
  +
a.out: A.hs:12:20-25: Maybe.fromJust: Nothing
  +
</haskell>
  +
  +
This can be automated, using the 'loch' preprocessor, so a program
  +
failing with:
  +
  +
<code>
  +
$ ghc A.hs --make -no-recomp
  +
[1 of 1] Compiling Main ( A.hs, A.o )
  +
Linking A ...
  +
  +
$ ./A
  +
A: Maybe.fromJust: Nothing
  +
</code>
  +
  +
Can be transformed to a src-located one by adding:
  +
  +
<haskell>
  +
import Debug.Trace.Location
  +
</haskell>
  +
  +
and then recompiling with the preprocessor on:
  +
  +
<code>
  +
$ 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
  +
</code>
  +
   
 
== 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 description of relevant runtime options].
  +
* Some tips how to use GHCi debugger are also in [http://www.haskell.org/pipermail/glasgow-haskell-users/2009-February/016571.html this message].
  +
  +
  +
=== 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:
  +
<haskell>
  +
head, tail, fromJust
  +
</haskell>
  +
  +
and others is to avoid these functions and to use explicit matching instead.
  +
  +
For example, consider:
  +
  +
<haskell>
  +
g x = h $ fromJust $ f x,
  +
</haskell>
  +
  +
ghc-6.6 often loses the reference to <hask>g</hask>, <hask>f</hask>,
  +
and <hask>h</hask> in its run-time error report, when <hask>f</hask>
  +
returns <hask>Nothing</hask>.
  +
  +
But for the program:
  +
  +
<haskell>
  +
g x = let Just y = f x in h y,
  +
</haskell>
  +
  +
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.
  +
  +
  +
=== Mysterious parse errors ===
  +
  +
GHC provides `-ferror-spans`, which will give you the exactly position
  +
of the start and end of an offending statement.
  +
  +
  +
=== Infinite loops ===
  +
  +
On glasgow-haskell-users on 21 Nov 2007, pepe made the following suggestion for detecting the cause infinite loops in GHCi. Assuming the offending function is named `loop`, and takes one argument:
  +
  +
# enable the flag -fbreak-on-error (`:set -fbreak-on-error` in GHCi)
  +
# run your expression with :trace (`:trace loop 'a'`)
  +
# hit Ctrl-C while your program is stuck in the loop to have the debugger break in the loop
  +
# use :history and :back to find out where the loop is located and why.
  +
  +
''(For which versions? ghci >= 6.8?)''
  +
  +
[[Category:Tools]]
  +
[[Category:Development tools]]

Revision as of 06:55, 30 October 2015

Stack trace

General usage

Recent versions of GHC allow a dump of a stack trace (of all cost centres) when an exception is raised. In order to enable this, compile with -prof, and run with +RTS -xc. (Since only the cost centre stack will be printed, you may want to add -auto-all -caf-all to the compilation step to include all definitions in the trace.) Since GHC version 7.8, the function errorWithStackTrace can be used to programmatically dump the stack trace, see How to get (approx) stack traces with profiled builds.

A more detailed list of options can be found in the RTS section of the GHC user's guide.


Example

  -- test.hs
  crash = sum [1,2,3,undefined,5,6,7,8]
  main  = print crash


 > ghc-7.6.3 test.hs -prof -auto-all -caf-all && ./test +RTS -xc
 *** Exception (reporting due to +RTS -xc): (THUNK_2_0), stack trace: 
   GHC.Err.CAF
   --> evaluated by: Main.crash,
   called from Main.CAF:crash_reH
 test: Prelude.undefined


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.

The trace function is located in the base package. The package htrace defines a trace function similar to the one in the base package, but with indentation for better visual effect (see the mailing list thread for examples). Other tools can be found at the debug category in Hackage.

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.

The GHood package adds a graphical back-end to Hood. See also the GHood homepage.


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.


Offline analysis of traces

The most advanced debugging tools are based in offline analysis of traces.

Haskell Tracer HAT

Hat is probably the most advanced 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 traditional Haskell tracers is that they either need to transform the whole program or require a specialized run-time system. Therefore they are not always compatible with the latest libraries, so you can put them to use only in some cases.

Hoed - The Lightweight Haskell Tracer and Debugger

Hoed is a tracer/debugger that offers most of HATs functionality, and works with untransformed libraries. Hoed can therefore be used to debug much more programs than traditional tracer/debuggers.

To locate a defect with Hoed you annotate suspected functions and compile as usual. Then you run your program, information about the annotated functions is collected. Finally you connect to a debugging session using a webbrowser.

Dynamic breakpoints in GHCi

Finally, the GHCi debugger enables dynamic breakpoints and intermediate values observation.

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

*main:Main> :break 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 laziness of your code.


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


Other tricks


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 loses 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.


Mysterious parse errors

GHC provides `-ferror-spans`, which will give you the exactly position of the start and end of an offending statement.


Infinite loops

On glasgow-haskell-users on 21 Nov 2007, pepe made the following suggestion for detecting the cause infinite loops in GHCi. Assuming the offending function is named `loop`, and takes one argument:

  1. enable the flag -fbreak-on-error (`:set -fbreak-on-error` in GHCi)
  2. run your expression with :trace (`:trace loop 'a'`)
  3. hit Ctrl-C while your program is stuck in the loop to have the debugger break in the loop
  4. use :history and :back to find out where the loop is located and why.

(For which versions? ghci >= 6.8?)