gsp*_*spr 20 debugging haskell infinite-loop ghc ghci
我第一次在Haskell程序中遇到了无限循环.我已经将它缩小到一个非常具体的代码部分,但我似乎无法精确指出我有一个非终止递归定义的位置.我模糊地熟悉:跟踪和:GHCi中的历史,但问题是我的代码的某些分支涉及相当多的递归修改Data.Map.Map
,因为地图x
是通过基于值adjust
的地图中x'
的某些东西获得的在另一张地图上取决于x'
.具体细节在这里并不重要,但正如你可能知道的那样,如果这是以交织在一起的递归方式发生的,那么我的通话历史会在地图lookup
s,adjust
ments和insert
离子所涉及的所有各种比较中完全陷入困境.
任何人都可以推荐一种更有效的方法来定位无限循环?例如,它可以帮助将呼叫历史限制为来自单个源文件的调用.
cro*_*eea 13
我很惊讶没有人提到所有Haskell性能问题普遍存在的响应(无限运行时是"性能问题"的一个相当极端的例子):分析!
我只能使用分析快速识别无限循环.为了完整性,编译-prof -fprof-auto
,然后运行程序足够长的时间,在分析统计中应该显示有问题的功能.例如,我希望我的程序在<1秒内完成,所以我让分析器运行大约30秒,然后用Ctrl + C杀死我的程序.(注意:分析会保存增量结果,因此即使在程序运行完成之前终止程序,您仍然可以获得有意义的数据.编辑:除非它没有.)
在.prof文件中,我找到了以下块:
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
...
primroot.\ Zq 764 3 10.3 13.8 99.5 100.0
primroot.isGen Zq 1080 50116042 5.3 6.9 89.2 86.2
primroot.isGen.\ Zq 1087 50116042 43.4 51.7 83.8 79.3
fromInteger ZqBasic 1088 0 40.4 27.6 40.4 27.6
Run Code Online (Sandbox Code Playgroud)
所以有5000万个条目primroot.isGen
,而下一个最被调用的函数只有1024个调用.此外,99.5%的运行时用于计算primroot
,这似乎非常可疑.我检查了那个函数并很快找到了错误,在我的情况下是一个简单的拼写错误:(`div` foo)
而不是(div foo)
.
我认为值得注意的是,GHC警告不会发现这个问题-fbreak-on-exceptions
.代码库很大; 试图通过插入调试语句(任何类型)来追踪问题并没有让我任何地方.我也没有成功使用GHCi调试器,因为历史基本上不存在,HPC没有透露任何有用的东西.
正如ShiDoiSi所说,"通过眼睛"解决通常是最成功的方式.
如果您在相同的函数中绑定到不同的类似名称的变量x,x'等,您可以尝试在文件顶部启用警告:
{-# OPTIONS -Wall #-}
Run Code Online (Sandbox Code Playgroud)
如果这是一个问题,你绑定到错误的东西并进行失控的递归,这可能会帮助你发现它 - 例如通过指示意外使用阴影.
确保你已经完全使用了GHCi调试器,包括设置-fbreak-on-exception(如果你得到的话很有用<<loop>>
,是吗?)并确保你已经尝试了Stephen关于使用GHC警告的建议.
如果这些失败(GHCi调试器真的不应该'失败',这只是解释数据的问题)然后尝试在循环的情况下运行HPC,这样你就可以直观地看到未被评估的分支和值,如果它是循环的然后应该完成的事情可能甚至没有被评估,并且将显示在标记的HTML中.
我正在进行长时间的调试,以找到无限循环的原因.我变得非常接近,这对我帮助最大.假设您的循环是由以下内容引起的:
...
x1 = f1 x2 y
x2 = f2 z x3
x3 = f3 y x1
...
Run Code Online (Sandbox Code Playgroud)
因此x1取决于x2,x2取决于x3,x3取决于x1.坏!
在f1,f2,f3的定义中撒上跟踪函数.就像是:
f1 x y | trace ("f1: ") False = undefined
f1 x y = ... -- definition of f1
f2 x y | trace ("f2: ") False = undefined
f2 x y = ... -- definition of f2
-- same for f3
Run Code Online (Sandbox Code Playgroud)
运行程序以查看调用了哪些函数.输出可能是这样的
f3:
f2:
f1:
<<loop>>
Run Code Online (Sandbox Code Playgroud)
然后开始在跟踪函数中显示一些变量.例如,如果将f2的轨迹更改为
f2 x y | trace ("f2: x: " ++ show x) False = undefined
Run Code Online (Sandbox Code Playgroud)
然后输出将看起来像:
f3:
f2: x: x_value
f1:
<<loop>>
Run Code Online (Sandbox Code Playgroud)
但是,如果你然后将f2的轨迹更改为
f2 x y | trace ("f2: x: " show x ++ " y: " ++ show y) False = undefined
Run Code Online (Sandbox Code Playgroud)
然后输出将是
f3:
<<loop>>
Run Code Online (Sandbox Code Playgroud)
因为循环依赖不能评估f2的第二个参数.
所以你现在知道无限循环中的一个函数是f2,它的第二个参数(但不是它的第一个)具有循环依赖性.
快乐的调试!
归档时间: |
|
查看次数: |
5719 次 |
最近记录: |