Looking at the commit history, it seems like this feature used to work at some point. However, apparently it got removed (either accidentally or on purpose) when the implementation of breakpoints was reworked (grep for breakpointName
). I've filed a ticket about this.
How do I use GHC.Exts.breakpoint?
Question
The GHC.Exts
package exports breakpoint
and breakpointCond
. Does anyone know how to use these functions?
From their names I guess they would allow me to set up permanent GHCi breakpoints, but when I add them to my program nothing happens. For example, no breakpoints are triggered when I load this program into GHCi and run it with main
, :main
, or :trace main
:
import GHC.Exts
idNat x = breakpointCond (x > 0) x
main = do
putStrLn "Starting main"
putStrLn . show $ idNat 3
putStrLn $ breakpoint "Ending main"
Note: I know how to set breakpoints in GHCi using :break
and I'm using GHC 7.6.3.
Solution
OTHER TIPS
Here's a summary of some workarounds, none very satisfactory.
An obvious workaround that doesn't actually work
Put this
Breakpoint
module in your source tree:module Breakpoint where breakpoint :: a -> a breakpoint x = x breakpointCond :: Bool -> a -> a breakpointCond True x = x breakpointCond False x = x
Import
Breakpoint
instead ofGHC.Exts
. (We can only set breakpoints on interpreted code, so we can't simply set break points on theGHC.Exts
break point functions.)Load your code in GHCi and set appropriate break points in the
Breakpoint
module:ghci> :load <your main module> ghci> :break Breakpoint 4 ghci> :break Breakpoint 7
Note that the second break point is on the
True
branch ofbreakpointCond
.Trace your code:
ghci> :trace main
Problems with obvious approach
The problem with this approach is that you get a bunch of break points
in the Breakpoint
module, but when you reach them your actual
computation (what you really care about) is usually not in the history
trace. I don't understand why this is, but here's an example to
illustrate it:
module Eg2 where
import Breakpoint
fib 0 = 0
fib 1 = 1
fib n = breakpoint $ fib (n-1) + fib (n-2)
Then:
ghci> :load Eg2.hs
ghci> :break Breakpoint 4
Now, note that fib 3 = fib 2 + fib 1 = (fib 1 + fib 0) + fib 1
, so that we fib 3
should result in us hitting two break points, once at fib 3
and once at fib 2
. However:
ghci> :trace fib 3
Stopped at Breakpoint.hs:4:5-20
_result :: a = _
[Breakpoint.hs:4:5-20] *Eg2
ghci> :history
-1 : fib (Eg2.hs:6:13-46)
-2 : fib (Eg2.hs:(4,5)-(6,46))
<end of history>
[Breakpoint.hs:4:5-20] *Eg2
ghci> :back
Logged breakpoint at Eg2.hs:6:13-46
_result :: a1
n :: Integer
[-1: Eg2.hs:6:13-46] *Eg2
ghci> n
3
So, yes, we hit a break point at fib 3
. But then:
[-1: Eg2.hs:6:13-46] *Eg2
ghci> :continue
Stopped at Breakpoint.hs:4:5-20
_result :: a = _
[Breakpoint.hs:4:5-20] *Eg2
ghci> :history
-1 : breakpoint (Breakpoint.hs:4:5-20)
-2 : fib (Eg2.hs:6:13-46)
-3 : fib (Eg2.hs:(4,5)-(6,46))
<end of history>
[Breakpoint.hs:4:5-20] *Eg2
ghci> :back
Logged breakpoint at Breakpoint.hs:4:5-20
_result :: a
[-1: Breakpoint.hs:4:5-20] *Eg2
ghci> :back
Logged breakpoint at Eg2.hs:6:13-46
_result :: a1
n :: Integer
[-2: Eg2.hs:6:13-46] *Eg2
ghci> n
3
Only the fib 3
is on the stack? There is no fib 2
call, even
though we're currently stopped at its breakpoint! Indeed, continuing
finishes, returning fib 3 = 2
:
[-2: Eg2.hs:6:13-46] *Eg2
ghci> :continue
2
Improvement
It's not hard to make the context of the breakpoint
call available
at the break point. Although this doesn't solve the issue of the
history trace not telling you how you got to the break point, it does
let you inspect the context interactively (so better than "printf
debugging" with Debug.Trace
).
Add a context argument to the break point functions:
module Breakpoint2 where
breakpoint :: b -> a -> a
breakpoint y x =
const x y
breakpointCond :: Bool -> b -> a -> a
breakpointCond True y x =
const x y
breakpointCond False _ x = x
Because GHCi only shows the free variables when you stop at a break point, you can't go with a simpler definition like
breakpoint y x = x
Now consider our example again, but this time where we manually pass
the context we care about to breakpoint
:
module Eg3 where
import Breakpoint2
fib 0 = 0
fib 1 = 1
fib n = breakpoint ("n",n) $ fib (n-1) + fib (n-2)
No we can inspect n
:
ghci> :load Eg3.hs
ghci> :break Breakpoint2 5
See n = 3
on the first call:
ghci> :trace fib 3
Stopped at Breakpoint2.hs:5:7-15
_result :: a = _
x :: a = _
y :: ([Char], Integer) = ("n",3)
[Breakpoint2.hs:5:7-15] *Eg3
ghci> :continue
See n = 2
on the second call:
Stopped at Breakpoint2.hs:5:7-15
_result :: a = _
x :: a = _
y :: ([Char], Integer) = ("n",2)
Interactively compute using the context:
[Breakpoint2.hs:5:7-15] *Eg3
ghci> let (_,n) = y
[Breakpoint2.hs:5:7-15] *Eg3
ghci> n * n
4
However, the trace history is still fairly useless, including the
breakpoint
calls, but fib
calls in which they occur.
Comparison with setting a break point on directly on your code
The goal of the breakpoint
function is to allow you easily set
persistent break points in your code, versus using a bunch of :break <some line> <your module>
statements which become invalid each time your line numbers change. However, manually
setting breakpoints does yield much better history traces. For example:
module Eg4 where
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
Set breakpoint directly:
ghci> :load Eg4.hs
ghci> :break Eg4 5 13
We need to column number (13) to make the breakpoint on the RHS of
fib
, instead of on the whole definition. Now we get proper traces:
ghci> :trace fib 4
Stopped at Eg4.hs:5:13-33
_result :: a1 = _
n :: Integer = 4
[Eg4.hs:5:13-33] *Eg4
ghci> :continue
Stopped at Eg4.hs:5:13-33
_result :: a1 = _
n :: Integer = 3
[Eg4.hs:5:13-33] *Eg4
ghci> :continue
Stopped at Eg4.hs:5:13-33
_result :: a1 = _
n :: Integer = 2
[Eg4.hs:5:13-33] *Eg4
ghci> :continue
Stopped at Eg4.hs:5:13-33
_result :: a1 = _
n :: Integer = 2
[Eg4.hs:5:13-33] *Eg4
ghci> :back
Logged breakpoint at Eg4.hs:5:13-33
_result :: a1
n :: Integer
[-1: Eg4.hs:5:13-33] *Eg4
ghci> n
2
[-1: Eg4.hs:5:13-33] *Eg4
ghci> :back
Logged breakpoint at Eg4.hs:5:13-33
_result :: a1
n :: Integer
[-2: Eg4.hs:5:13-33] *Eg4
ghci> n
3
[-2: Eg4.hs:5:13-33] *Eg4
ghci> :back
Logged breakpoint at Eg4.hs:5:13-33
_result :: a1
n :: Integer
[-3: Eg4.hs:5:13-33] *Eg4
ghci> n
4
[-3: Eg4.hs:5:13-33] *Eg4
ghci> :history
-1 : fib (Eg4.hs:5:13-33)
-2 : fib (Eg4.hs:5:13-33)
-3 : fib (Eg4.hs:5:13-33)
-4 : fib (Eg4.hs:(3,5)-(5,33))
<end of history>
Final note on breakpointCond
The breakpointCond
seems even more useful, since GHCi doesn't have
conditional breakpoints, but these can be affected by editing your
code to include pointless branching on break point conditions. For
example, suppose we only want to break when n
is even:
module Eg5 where
fib 0 = 0
fib 1 = 1
fib n = case even n of
True -> fib (n-1) + fib (n-2)
False -> fib (n-1) + fib (n-2)
Now, breaking on line 6, we only stop on even n
. Of course, this
transformation is a little annoying, e.g. we can't instead do
fib n = case even n of
True -> r
False -> r
where r = fib (n-1) + fib (n-2)
since then n
is not free in r
, and we can't use an "if" statement,
since GHCi won't let you set a break point inside one.