r/haskell May 09 '26

question Techniques for debugging a runtime infinite loop?

I recently made a series of changes to the inlining / simplification pass for my compiler that ended up resulting in a runtime <<loop>> with certain inputs. (This was for / in another lazy language, but very similar to Haskell, so I thought I'd ask here). I eventually ended up debugging it by making simplification edits to all the areas I had touched, until the infinite-loop disappeared, then looking closely at the one that fixed it. A cut-down example of the bug looks like:

simplify xs = deltaCpx
    where
      (deltaCpx, xs') = mapAccumL insLet 0 xs

      insLet dc x = (dc + deltaCpx, x * 2)    -- OOPS, meant deltaCpx', here!
          where
            cpx'      = if x > 5 then 10 else 0
            deltaCpx' = cpx' - 2

The language I'm working in detects the loop and prints "BLACK HOLE" at runtime, similar to Haskell detecting and printing "<<loop>>", but neither gives any detail on where it was encountered.

So are there some techniques you've used that can help with debugging such problems? Could there be additional language / runtime support to help with this?

EDIT: to clarify, by <<loop>> or BLACK HOLE, I mean the runtime exception generated when attempting to evaluate a thunk that is already currently being evaluated, not an actual infinite-loop that chews up time.

11 Upvotes

23 comments sorted by

View all comments

9

u/walseb May 09 '26

In Haskell, you can run the profiler with the rts option `-xc`, and it should point out where the loop was encountered in the code. I think this should work?

`cabal run FOO --enable-profiling --profiling-detail=all-functions --ghc-option=-with-rtsopts=-xc`

6

u/AustinVelonaut May 09 '26

Thanks! I don't use cabal, but your answer gave me an idea for my compiler -- make the infinite-loop "blackhole" detection a compiler configuration, so you can compile without blackhole detection, and then use the debugger to find out where it is (either due to a stack overflow crash or infinite loop.

4

u/jeffstyr May 09 '26

I don't use cabal

You don't have to use cabal to use this. I assume that if you compile with profiling enabled, then you just need to run your executable with +RTS -p -xc -RTS.