Search code examples
debugginghaskellghccabalghci

Debugging/Stepping into a package module via cabal repl


So I have the following code from Preventing caching of computation in Criterion benchmark and my aim is to be able to step from main directly into the function defaultMain in Criterion.Main :

{-# OPTIONS -fno-full-laziness #-}
{-# OPTIONS_GHC -fno-cse #-}
{-# LANGUAGE BangPatterns #-}
module Main where
import Criterion.Main
import Data.List

num :: Int
num = 100000000

lst :: a -> [Int]
lst _ = [1,2..num]

myadd :: Int -> Int -> Int
myadd !x !y = let !result = x + y in
  result

mysum = foldl' myadd 0

main :: IO ()
main = defaultMain [
  bgroup "summation" 
    [bench "mysum" $ whnf (mysum . lst) ()]
  ]

and the cabal file is :

name:                test
version:             0.1.0.0
build-type:          Simple
cabal-version:       >=1.10

executable test
  main-is:             Main.hs
  build-depends:       base >=4.8 && <4.9,
                       criterion==1.1.0.0
  default-language:    Haskell2010
  ghc-options:         "-O3"

(using ghc 7.10.1 and cabal 1.22.0.0).

If from within cabal repl I try to set a breakpoint in criterion I get the following error :

*Main> :break Criterion.Main.defaultMain
cannot set breakpoint on defaultMain: module Criterion.Main is not interpreted

Furthermore if I try to add the package I get the following error :

*Main> :add *Criterion

<no location info>: module ‘Criterion’ is a package module
Failed, modules loaded: Main.

If I do within the directory git clone https://github.com/bos/criterion and then add the following two lines to my cabal file :

other-modules:       Criterion
hs-source-dirs:      .
                 ./criterion

then upon doing cabal build I get the following errors :

criterion/Criterion/IO.hs:23:0:
     error: missing binary operator before token "("
     #if MIN_VERSION_binary(0, 6, 3)

so I suspect that I have to do a full on merge of the criterion cabal file with my cabal file above, which feels a bit excessive.

Is there an easier way for me to go about setting a breakpoint in Criterion, so that I can step (when debugging in cabal repl/ghci) directly from my source into criterion's source? Thanks

p.s. There is a related question at Debugging IO in a package module inside GHCi but unfortunately it did not help.


Solution

  • This is how I managed to achieve the desired goal of being able to step (within cabal repl) from my code into the criterion source :

    1. First do :

      mkdir /tmp/testCrit
      cd /tmp/testCrit
      
    2. Download criterion-1.1.0.0.tar.gz

    3. Unzip into /tmp/testCrit, so we should have /tmp/testCrit/criterion-1.1.0.0. In this directory we have Criterion.hs etc.

    4. Then jump into the folder containing the criterion source and do :

      cd /tmp/testCrit/criterion-1.1.0.0
      cabal sandbox init
      cabal install -j
      

      Note that this creates a directory : /tmp/testCrit/criterion-1.1.0.0/dist/dist-sandbox-782e42f0/build/autogen which we shall use later

    5. Back in /tmp/testCrit create a Main.hs file containing the benchmark code above and also the cabal file above, but merge it with the criterion cabal file contained in /tmp/testCrit/criterion-1.1.0.0 in the following way. Note the main new additions are the lines :

      cc-options: -fPIC
      

      which allows one to run it in cabal repl, and the following lines :

      hs-source-dirs: 
        ./
        ./criterion-1.1.0.0
        ./criterion-1.1.0.0/dist/dist-sandbox-782e42f0/build/autogen
      

      The full cabal file should then look like :

        name:                test
        version:             0.1.0.0
        build-type:          Simple
        cabal-version:       >=1.10
      
        executable test
          main-is:             Main.hs
          build-depends:       
            base >=4.8 && <4.9,
            aeson >= 0.8,
            ansi-wl-pprint >= 0.6.7.2,
            base >= 4.5 && < 5,
            binary >= 0.5.1.0,
            bytestring >= 0.9 && < 1.0,
            cassava >= 0.3.0.0,
            containers,
            deepseq >= 1.1.0.0,
            directory,
            filepath,
            Glob >= 0.7.2,
            hastache >= 0.6.0,
            mtl >= 2,
            mwc-random >= 0.8.0.3,
            optparse-applicative >= 0.11,
            parsec >= 3.1.0,
            statistics >= 0.13.2.1,
            text >= 0.11,
            time,
            transformers,
            transformers-compat >= 0.4,
            vector >= 0.7.1,
            vector-algorithms >= 0.4
          default-language:    Haskell2010
          ghc-options:         "-O3"
          c-sources: 
            ./criterion-1.1.0.0/cbits/cycles.c
            ./criterion-1.1.0.0/cbits/time-posix.c
          hs-source-dirs: 
            ./
            ./criterion-1.1.0.0
            ./criterion-1.1.0.0/dist/dist-sandbox-782e42f0/build/autogen
          cc-options: -fPIC
      
    6. Then in the main directory do :

      cd /tmp/testCrit/
      cabal sandbox init
      cabal install -j
      
    7. Then we can spin up a cabal repl and step directly into criterion from our Main.hs code :

      *Main> :break Criterion.Main.defaultMain
      Breakpoint 0 activated at criterion-1.1.0.0/Criterion/Main.hs:79:15-43
      *Main> main
      Stopped at criterion-1.1.0.0/Criterion/Main.hs:79:15-43
      _result :: [Benchmark] -> IO () = _
      [criterion-1.1.0.0/Criterion/Main.hs:79:15-43] *Main> :step
      Stopped at criterion-1.1.0.0/Criterion/Main.hs:(131,1)-(147,39)
      _result :: IO () = _
      [criterion-1.1.0.0/Criterion/Main.hs:(131,1)-(147,39)] *Main> :step
      Stopped at criterion-1.1.0.0/Criterion/Main.hs:(131,29)-(147,39)
      _result :: IO () = _
      bs :: [Benchmark] = [_]
      defCfg :: Criterion.Types.Config = _
      [criterion-1.1.0.0/Criterion/Main.hs:(131,29)-(147,39)] *Main> :step
      Stopped at criterion-1.1.0.0/Criterion/Main.hs:132:10-37
      _result :: IO Criterion.Main.Options.Mode = _
      defCfg :: Criterion.Types.Config = _