简体   繁体   English

在使用GHC编译Haskell时如何禁用<< loop >>异常?

[英]How to disable the <<loop>> exception when compiling Haskell with GHC?

If my program reaches an infinite loop, I would like it to actually get stuck: run forever, run out of memory or crash with a stack overflow. 如果我的程序达到无限循环,我希望它实际上卡住:永远运行,耗尽内存或崩溃堆栈溢出。

I don't want it to quit immediately with the <<loop>> error message. 我不希望它立即退出<<loop>>错误消息。 How do I disable the run-time infinite loop detection? 如何禁用运行时无限循环检测?

Here's a horrible hack that might work. 这是一个可能有用的可怕黑客。 You can create a fake info table for the non-termination exception that's created when a blackhole loop is detected and make it loop. 您可以为检测到黑洞循环时创建的非终止异常创建伪信息表,并使其循环。

Suppose you have a Haskell program: 假设您有一个Haskell程序:

-- Loop.hs
foo :: Int
foo = foo
main = print $ foo

If you compile this with: 如果你用以下代码编译它:

$ ghc -O2 Loop.hs

and run it, it'll generate a Loop: <<loop>> error. 并运行它,它将生成一个Loop: <<loop>>错误。

But, if you create an assembly file: 但是,如果您创建一个程序集文件:

# halt_and_catch_fire.s
# version for Linux x86_64 only
.globl base_ControlziExceptionziBase_nonTermination_closure
.section .data
.align 8
base_ControlziExceptionziBase_nonTermination_closure:
    .quad loop
.section .text
.align 8
    .quad 0
    .quad 14            # FUN_STATIC
loop:   jmp loop

and compile it with your Haskell program (with appropriate linker flags to ignore the duplicate definition): 并使用您的Haskell程序编译它(使用适当的链接器标志来忽略重复的定义):

$ ghc -O2 Loop.hs halt_and_catch_fire.s -optl -zmuldefs

and run it, it'll lock up. 并运行它,它会锁定。

Note that the above assembly works on x86_64 Linux. 请注意,上面的程序集适用于x86_64 Linux。 On any other architecture (including 32-bit Linux), it would need to be modified, as the closure layout is very architecture-dependent. 在任何其他体系结构(包括32位Linux)上,都需要进行修改,因为闭包布局非常依赖于体系结构。

This isn't really an answer but wouldn't fit in a comment. 这不是一个真正的答案,但不适合评论。 In this situation one should ask what infinite loop detection is and how it works. 在这种情况下,人们应该问无限循环检测是什么以及它是如何工作的。 I'm going to explain it by converting from Haskell to a Haskell-look-alike language with strict (non lazy) evaluation (think eg JavaScript with Haskell syntax) and then explaining it. 我将通过严格(非惰性)评估(从例如用Haskell语法编写JavaScript)然后解释它来将Haskell转换为Haskell看起来相似的语言来解释它。

Haskell: 哈斯克尔:

let f () = f () in f ()

Convert to strict: 转换为严格:

let f () = make_thunk (\() -> eval_thunk f ()) in eval_thunk (make_thunk (\() -> f ()))

Now let's do an optimisation: 现在让我们做一个优化:

let f () = eval_thunk f_unit
    f_unit = make_thunk (\() -> f ())
in
eval_thunk f_unit

Now let's write down a fake definition for thunks: 现在让我们写下一个关于thunk的假定义:

data ThunkInner a = Done a | Fresh (() -> a) | Working
data Thunk a = Thunk { mutable inner :: ThunkInner a }
make_thunk f = Thunk (Fresh f)
eval_thunk t = case t.inner of
  | Done a -> a
  | Fresh f -> (t.inner <- Working; let result = f () in t.inner <- Done result; result)
  | Working -> error "<<loop>>"

So we see that we get an infinite loop when we try to evaluate something as part of working out its value. 因此,当我们尝试将某些东西作为计算其价值的一部分进行评估时,我们会看到无限循环。 You can trace the above program by hand to see how this error could arise. 您可以手动跟踪上述程序,以了解如何出现此错误。

But what if that optimisation hadn't been made? 但是,如果没有进行优化呢? Well then (assuming you don't optimise to strictness) you would get a stack overflow and could report this as a <<loop>> error. 那么(假设你没有优化到严格性)你会得到一个堆栈溢出并且可以将其报告为<<loop>>错误。 If it were optimised to be truly strict then maybe you would get a stack overflow error or maybe you would get an infinite loop. 如果它被优化为真正严格,那么也许你会得到一个堆栈溢出错误或者你可能会得到一个无限循环。

One might ask how to avoid this error and the answer could be: 有人可能会问如何避免这个错误,答案可能是:

  1. Don't write infinite loops 不要写无限循环
  2. Maybe compile with no optimisations 也许编译没有优化

But you say that sometimes infinite loops are useful because eg event loops or long lived servers. 但是你说有时无限循环很有用,例如事件循环或长寿命服务器。 The reply is that in Haskell infinite loops are not useful because to be useful you need IO . 回答是在Haskell中无限循环没有用,因为有用的是你需要IO Consider a function be_useful :: IO () and now one can write: 考虑一个函数be_useful :: IO () ,现在可以编写:

f = be_useful >>= \() -> f

And now there are two steps in doing f : 现在做f步骤有两个:

  1. Evaluating the thunk 评估thunk
  2. Doing the IO actions in that value 在该值中执行IO操作
  3. Doing the next IO action (computing the thunk again) and so on. 执行下一个IO操作(再次计算thunk)等等。

This need not <<loop>> . 这不需要<<loop>>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM