Debugging the compiler

Basic strategies

When compiling GHC (see also Building Using):

  • add -DDEBUG to your GhcStage1HcOpts and/or GhcStage2HcOpts in mk/ This enables assertions and extra debug code. This is done for you if you choose the devel1 or devel2 mode of building.

When compiling the program (see also the relevant User Manual section and here):

  • Use -v3 or -v4 to get an idea about what GHC is doing when the problem occurs.
  • Add -dcore-lint the GHC command line when compiling each Haskell module. This makes GHC type-check the intermediate program after every optimisation pass, which often nails a fault.
  • Add -ddump-simpl to see the optimised Core output. There are a number of other -ddump-x flags; see the user manual.
  • The flag -dppr-debug makes the -ddump-x flags print much more verbose output. Use this if you are getting desperate! To be able to read the output, you may want to add -dsuppress-module-prefixes and -dsuppress-var-kinds as well.
  • While hacking on GHC, if you need to lookup types, you can do this:
    > ghc-stage2 --interactive
    GHCi, version 7.11.20151104:  :? for help
    Prelude> :set -package ghc         -- THIS IS THE KEY LINE
    package flags have changed, resetting and loading new packages...
    Prelude> import TcMType
    Prelude TcMType> :t newEvVar
     :: forall gbl lcl.
        TcType.TcPredType -> TcRnTypes.TcRnIf gbl lcl Var.EvVar
    Prelude TcMType> 

Adding debugging code to the compiler

  • Outputable.pprTrace is a nice way to print trace messages from the compiler. The output will only appear when the compiler has -DDEBUG turned on.
  • ASSERT(p), ASSERT2(p,msg), WARN(p,msg) are assertions and warning enabled only when the compiler is compiled with -DDEBUG. There are also variants of these that work better in a monad setting; see compiler/HsVersions.h.
  • If you have built a profiling compiler, an ASSERT failure will also print a stack trace. Stack traces use only GHC call centres, so you will probably also want -fprof-auto to get the most informative stack. One way to get the knobs set correctly is to choose the prof build in, and use this line in the prof section:
GhcStage2HcOpts    = -O0 $(GhcFAsm) -fprof-auto -DDEBUG
  • You can also get stack traces from pprTrace if you change the use of trace in the definition of pprTrace (in utils/Outputable.hs) to traceStack (from Debug.Trace).

Debugging segfaults

GHC is a nice, complicated piece of Haskell code, so if you make a bug in the compiler, you are likely to cause ghc-stage2 to segfault. Before you try debugging directly, see if you can build a simpler program using ghc-stage1 -debug which segfaults, and debug that instead. Of course, for subtle compiler bugs, GHC may be the easiest piece of code to induce the failure with. If you are doing in-place builds, you can rebuild the stage 2 compiler by deleting ghc/stage2/build/tmp/ghc-stage2 and then running make STAGE=2 EXTRA_HC_OPTS=-debug in the ghc directory to rebuild the compiler with the debugging RTS. (Substitute 2 with 1 if debugging the stage 1 compiler, although that one really shouldn't be segfaulting!)

It's not possible to directly run GHC using gdb inplace/bin/ghc-stage2 on Linux, because this file is not actually an executable: it's a shell script. You can convert it to run gdb by editing the last line to read exec gdb --args "$executablename" -B"$topdir" ${1+"$@"}; alternately, you can manually import the environment variables set by this script and then run GDB by hand on the result. (XXX: Someone should put instructions for how to run gdb on GHC, while feeding it information from stdin, on Windows.)

Last modified 3 years ago Last modified on Feb 19, 2017 11:41:29 AM