假设一个大而复杂的Haskell程序在执行期间的某个时间产生NaN。如何在不花费大量时间在我的代码中添加大量NaN检查的情况下找到我的代码中的哪个位置?我只对调试感兴趣,所以我不关心可移植性或性能。
这是五年前在Haskell-cafe上讨论过的。提出了一种可能的解决方案,但没有进一步讨论。 https://mail.haskell.org/pipermail/haskell-cafe/2011-May/091858.html
下面是我尝试使用AccessToken.getCurrentAccessToken().getPermissions()
按照Haskell-cafe讨论中的建议将堆栈跟踪到达生成NaN的点(在一个小示例程序中):
feenableexcept
不幸的是,运行此代码不会产生堆栈跟踪:(
-- https://www.gnu.org/software/libc/manual/html_node/Control-Functions.html
foreign import ccall "feenableexcept" enableFloatException :: Int -> IO Int
allFloatExceptions :: Int
allFloatExceptions = 1 {-INVALID-} + 4 {-DIVBYZERO-} + 8 {-OVERFLOW-} + 16 {-UNDERFLOW-}
main :: IO ()
main = do
_ <- enableFloatException allFloatExceptions
print $ (0/0 :: Double)
我假设(但实际上不知道)我没有得到堆栈跟踪,因为当异常中止程序时GHC运行时不受控制。接下来我尝试使用$ ghc -rtsopts -prof -fprof-auto testNaN.hs && ./testNaN +RTS -xc
[1 of 1] Compiling Main ( testNaN.hs, testNaN.o )
Linking testNaN ...
Floating point exception (core dumped)
中的installHandler
来尝试使程序在GHC的运行时崩溃:
System.Posix.Signals
不幸的是,这会导致更神秘的错误,但仍然没有给我一个堆栈跟踪:(
import qualified System.Posix.Signals as Signals
-- https://www.gnu.org/software/libc/manual/html_node/Control-Functions.html
foreign import ccall "feenableexcept" enableFloatException :: Int -> IO Int
allFloatExceptions :: Int
allFloatExceptions = 1 {-INVALID-} + 4 {-DIVBYZERO-} + 8 {-OVERFLOW-} + 16 {-UNDERFLOW-}
catchFloatException :: IO ()
catchFloatException = error "print stack trace?"
main :: IO ()
main = do
_ <- enableFloatException allFloatExceptions
_ <- Signals.installHandler Signals.floatingPointException (Signals.Catch catchFloatException) Nothing
print $ (0/0 :: Double)
我也试过使用多个线程。大部分时间都会发生以下情况。
$ ghc -rtsopts -prof -fprof-auto testNaN.hs && ./testNaN +RTS -xc
[1 of 1] Compiling Main ( testNaN.hs, testNaN.o )
Linking testNaN ...
testNaN: too many pending signals
虽然,一旦发生这种情况。 http://pastebin.com/u3u2cnHE
我采取了正确的方法吗?有没有办法修改我的示例,以便我可以获得堆栈跟踪?