Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging IO in a package module inside GHCi

Tags:

I'm doing low-level IO (for library bindings) in Haskell and am experiencing a segfault. I would like to use GHCi's :break to figure out what's going on, but here's what happens:

> import SDL
> :break SDL.setPaletteColors
cannot set breakpoint on setPaletteColors: module SDL.Video.Renderer is not interpreted

Since the offending code is not inside my own modules, but rather inside a module in an external package, it's loaded as compiled code and apparently I can't use :break on compiled modules.

GHCi manual confirms this and provides a hint:

There is one major restriction: breakpoints and single-stepping are only available in interpreted modules; compiled code is invisible to the debugger[5].

[5] Note that packages only contain compiled code, so debugging a package requires finding its source and loading that directly.

Let's try it directly:

> :load some_path/sdl2/src/SDL/Video/Renderer.hs

some_path/sdl2/src/SDL/Video/Renderer.hs:101:8:
Could not find module ‘Control.Monad.IO.Class’
It is a member of the hidden package ‘transformers-0.3.0.0’.
Perhaps you need to add ‘transformers’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.

I can add the dependencies to my .cabal file, but this already feels wrong. Once I've done that:

> :load some_path/sdl2/src/SDL/Video/Renderer.hs

some_path/sdl2/src/SDL/Video/Renderer.hs:119:8:
Could not find module ‘SDL.Internal.Numbered’
it is a hidden module in the package ‘sdl2-2.0.0’
Use -v to see a list of the files searched for.

I could make those modules public (probably? by modifying the package .cabal?), but at this point it seems a really awkward way to do things and I didn't pursue it further.

EDIT:

I actually tried that and got the baffling result:

> :load some_path/sdl2/src/SDL/Video/Renderer.hs
[1 of 1] Compiling SDL.Video.Renderer ( some_path/sdl2/src/SDL/Video/Renderer.hs, interpreted )
Ok, modules loaded: SDL.Video.Renderer.
> :break SDL.setPaletteColors
cannot set breakpoint on SDL.setPaletteColors: module SDL.Video.Renderer is not interpreted

My (uneducated) guess: it's because the external module is still linked to my code as a binary, and loading it dynamically in interpreted mode doesn't change that.


So, to sum up the question: what is a good way to debug IO in an external package?

Additional notes:

  1. I do have the source to the package I need to debug; in fact, it's been added to the project with cabal sandbox add-source

  2. An alternative option to using GHCi would be to add traces to the package source, but this is an unfortunate option, since it involves recompilation of the package on each modification (whenever I need more information about the execution and modify the traces), and that takes a really long time. Interactive debugging with GHCi seems a better tool for this job, if only I knew how to use it.

like image 839
Vladimir Semyonov Avatar asked Feb 17 '15 20:02

Vladimir Semyonov


1 Answers

Stack has some support for this. Running stack ghci --load-local-deps $TARGET will load your project and any dependencies that are in the packages field of stack.yaml, including if they're marked as extra-deps. Breakpoints will work then. You can debug a dependency in GHCi by running stack unpack $PACKAGE and adding it to packages in stack.yaml.

This is not a panacea however. If the packages have conflicting package-global language extensions (or other dynamic flags) or module name clashes it won't work. For example, if your top-level package has default-extensions: NoImplicitPrelude and your dependencies don't, they won't have a prelude imported and will almost certainly not load. See this GHC bug.

like image 108
Echo Nolan Avatar answered Sep 22 '22 04:09

Echo Nolan