我如何使用GHC.Exts.breakpoint?

时间:2013-10-09 22:26:50

标签: debugging haskell ghc ghci

GHC.Exts个包导出breakpoint and breakpointCond。有谁知道如何使用这些功能?

从他们的名字我想他们会允许我设置永久GHCi断点,但是当我将它们添加到我的程序时没有任何反应。例如,当我将此程序加载到GHCi并使用main:main:trace main运行时,不会触发任何断点:

import GHC.Exts

idNat x = breakpointCond (x > 0) x

main = do
  putStrLn "Starting main"
  putStrLn . show $ idNat 3
  putStrLn $ breakpoint "Ending main"

注意:我知道如何使用:break在GHCi中设置断点,而我正在使用GHC 7.6.3。

2 个答案:

答案 0 :(得分:2)

查看提交历史记录,似乎是此功能used to work at some point。但是,显然它已被删除(无论是意外还是故意)when the implementation of breakpoints was reworked(grep for breakpointName)。我filed a ticket about this

答案 1 :(得分:1)

以下是一些解决方法的摘要,没有一个非常令人满意。

一种实际上无效的明显解决方法

  1. 将此Breakpoint模块放在源树中:

    module Breakpoint where
    
    breakpoint :: a -> a
    breakpoint x = x
    
    breakpointCond :: Bool -> a -> a
    breakpointCond True  x = x
    breakpointCond False x = x
    
  2. 导入Breakpoint而不是GHC.Exts。 (我们只能在解释代码上设置断点,因此我们不能简单地在GHC.Exts断点函数上设置断点。)

  3. 在GHCi中加载代码并在Breakpoint模块中设置适当的断点:

    ghci> :load <your main module>
    ghci> :break Breakpoint 4
    ghci> :break Breakpoint 7
    

    请注意,第二个断点位于True的{​​{1}}分支上。

  4. 跟踪您的代码:

    breakpointCond
  5. 明显方法的问题

    这种方法的问题在于你获得了一堆断点 在ghci> :trace main 模块中,但当您达到它们的实际值时 计算(你真正关心的)通常不在历史中 跟踪。我不明白为什么会这样,但这是一个例子 说明一下:

    Breakpoint

    然后:

    module Eg2 where
    import Breakpoint
    
    fib 0 = 0
    fib 1 = 1
    fib n = breakpoint $ fib (n-1) + fib (n-2)
    

    现在,请注意ghci> :load Eg2.hs ghci> :break Breakpoint 4 ,这样我们fib 3 = fib 2 + fib 1 = (fib 1 + fib 0) + fib 1会导致我们点击两个断点,一次是fib 3,一次是fib 3。但是:

    fib 2

    所以,是的,我们在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 达到了一个断点。但那时:

    fib 3

    只有[-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 在堆栈上?甚至没有fib 3电话 虽然我们目前停在断点处!的确,继续 完成,返回fib 2

    fib 3 = 2

    改进

    使[-2: Eg2.hs:6:13-46] *Eg2 ghci> :continue 2 调用的上下文可用并不困难 在破发点。虽然这并没有解决问题 历史记录没有告诉你如何到达断点,它确实如此 让你以交互方式检查上下文(比“printf”更好 调试“与breakpoint。”。

    为断点函数添加上下文参数:

    Debug.Trace

    因为GHCi仅在您休息时停止显示自由变量 一点,你不能用更简单的定义,如

    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
    

    现在再考虑我们的例子,但这次我们手动传递 我们关心breakpoint y x = x 的背景:

    breakpoint

    我们不能检查module Eg3 where import Breakpoint2 fib 0 = 0 fib 1 = 1 fib n = breakpoint ("n",n) $ fib (n-1) + fib (n-2)

    n

    第一次通话时见ghci> :load Eg3.hs ghci> :break Breakpoint2 5

    n = 3

    第二次通话时见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

    n = 2

    使用上下文进行交互式计算:

    Stopped at Breakpoint2.hs:5:7-15
    _result :: a = _
    x :: a = _
    y :: ([Char], Integer) = ("n",2)
    

    然而,跟踪历史仍然相当无用,包括 [Breakpoint2.hs:5:7-15] *Eg3 ghci> let (_,n) = y [Breakpoint2.hs:5:7-15] *Eg3 ghci> n * n 4 次来电,但会breakpoint来电。{/ p>

    直接在代码上设置断点的比较

    fib功能的目标是让您轻松设置 代码中的持久性断点,而不是使用一堆breakpoint 每次行号更改时变为无效的语句。但是,手动 设置断点确实会产生更好的历史记录。例如:

    :break <some line> <your module>

    直接设置断点:

    module Eg4 where
    
    fib 0 = 0
    fib 1 = 1
    fib n = fib (n-1) + fib (n-2)
    

    我们需要列号(13)来在RHS上建​​立断点 ghci> :load Eg4.hs ghci> :break Eg4 5 13 ,而不是整个定义。现在我们得到了适当的痕迹:

    fib

    关于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>

    的最后说明

    breakpointCond似乎更有用,因为GHCi没有 条件断点,但这些可能会受到编辑你的影响 代码在断点条件下包含无意义的分支。对于 例如,假设我们只想在breakpointCond为偶数时中断:

    n

    现在,在第6行,我们只停留在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) 。当然,这个 变换有点烦人,例如我们不能改为

    n

    从那以后fib n = case even n of True -> r False -> r where r = fib (n-1) + fib (n-2) 中的n不可用,我们不能使用“if”语句, 因为GHCi不会让你在其中设置一个断点。