Debugging cabal-install and Cabal with ghci 6.7
Pepe Iborra
mnislaih at gmail.com
Tue May 22 05:02:19 EDT 2007
Simon (and whom ever is interested)
As promised, here go the details of my debugging session with Cabal.
I was updating cabal-install to use Distribution.Verbosity,
and as I was finishing I hit a bug, either in cabal-install or Cabal.
This happened while I was asking my modified cabal-install to
install a random library for me, in this case Lemmih's xslt binding,
for me.
$ cabal-install install xslt
ghc-6.7.20070520: malformed integer argument in -vNormal
Usage: For basic information, try the `--help' option.
*** Exception: exit: ExitFailure 1
Since I'm not a contributor to either of two projects, I don't know
how things work in
the insides. I decided this was a great chance to use the debugger.
Firstly I loaded cabal-install in GHCi in the obvious way.
I first tried to capture the exception in a breakpoint with :trace plus
-fbreak-on-exception, and go back in the lazy trace from there
using :history,
which has worked wonders for me so far with bugs of the type head of
empty list.
Unfortunately that didn't work very well this time. For the record,
I used this macro to launch main, with arguments, under :trace
let ss args = concat $ Data.List.intersperse "," (map (\ s -> ('"':s)
++"\"") (words args))
:def maintraced \args -> return (":trace " ++ '[': ss args ++ "]
`System.Environment.withArgs` main")
And then launched cabal-install from the GHCi command line with the
command
:maintraced install xslt
What happened is that GHCi started to interrupt on all sorts of
exceptions
that I wasn't interested in.
It turns out that these exceptions were not actually uncaught, but still
the debugger would interrupt at them; as you told me there are still
some
issues with -fbreak-on-exception.
I decided to try another route and use manual breakpoints instead.
So I made a guess and put a breakpoint approximately near the bug
source, by
line, and :step'ped from there. I'd have done it by function name,
which is the most
convenient way for setting breakpoints, but unfortunately this name
was not
in scope (it looks like only module exported names are available, is
this by
design or a bug?).
This worked pretty well. I used :step in combination with :list
thanks to
this macro that you suggested which lives now in my .ghci
:set stop :list
:list is awesome, but I hit this bug in GHCi itself with :list and
paths. It was
easy to fix -and yes, I tried and did load ghc itself in the
debugger, but
that's a tale for another day- and I continued my debugging adventures.
In a few :step's I found the offending call. Turns out the bug was in
Cabal
Distribution.SetupWrapper.setupWrapper function, which was being
called from
cabal-install, and since it is not possible to put breakpoints in
library
code, I needed to load Cabal in GHCi. That was not a problem at all, but
note that I had two options here:
a) Load cabal-install _plus_ Cabal in GHCi, using an include option
pointing
to the folder with Cabal sources
b) Load only Cabal and call setupWrapper myself with the same arguments
I guess a) would be the right thing in general, but in this case I opted
for b). So before leaving
I :print'ed the values of the arguments. By the way, no need to use type
reconstruction at all in this real life example. All the bindings were
monomorphic most of the time, lots of type signatures in the Cabal and
cabal-install sources. Hell, I know it is a good practice, but after
so much
work on :print I was almost angry at those type signatures! :)
I loaded Cabal in GHCi, set my breakpoint in setupWrapper (by now I
had a
pretty good idea of where the bug was, but I wanted to keep going
till the
end). I again tried to use -fbreak-on-exception but unfortunately had
the same issues. It looks like there is some piece of code
(using System.Directory.withFileStatus perhaps?) that when asked
to find a file in the PATH, works by trying every path component and
just
handling the raised exception in case of failure, but -fbreak-on-
exception
was capturing that exception nonetheless.
So all in all, I couldn't really succeed with -fbreak-on-exception in
the
whole session. Finding the bug however proceeded very easily anyway
using :step's.
Now that I think, I could have
used the trick of approximating a breakpoint which I know is _after_ the
problematic exceptions code, stopped there, enabled
-fbreak-on-exception at that point, and continued with :trace.
Lessons learned:
a) If technically possible, we really must have -fbreak-on-exception
at any cost :)
b) :history works wonders too, but perhaps storing only applications
would
make it more agile. Worth a try?
c) Real Life code is much less intrincate than we had expected! No HOF
debugging, no multiple threads, no polymorphic bindings, no
existentials,
circular structures,
unboxed values, template haskell or younameit involved! (just
kidding)
This was a reassuring experience that the debugger will break
less than
expected. In retrospect, I must say that this was a rather simple
debugging session.
d) The debugger is easily usable at this point. There are still some
wibbles
and details to improve, but things are looking really really nice.
e) To me, it looks like the debugger will become a really useful
tool, but
I am biased :)
A list of wibbles:
- Autocompletion for :break only provides names, not modules
- :break <functionName> only works for exported names
- :history could show the top level name for each row
I include some logs of the whole experience below, for the curious. A
1280x1024 terminal is advised ;)
Notes for the session below:
* I loaded both cabal-install and Cabal in ghci doing option a) above
for the logs
* Failed attempts with -fbreak-on-exception removed
* I used the trick of enabling -fbreak-on-exception after the exception
raising code.
* Those lines of code look really much prettier in my console, with
the current
subexpression highlighted.
pep:~/scratch/cabal-install$ ghci -isrc
Network.Hackage.CabalInstall.Main -idist/build/autogen -idependencies/
HTTP-2006.7.7/ -i../Cabal.work
___ ___ _
/ _ \ /\ /\/ __(_)
/ /_\// /_/ / / | | GHC Interactive, version 6.7.20070520, for
Haskell 98.
/ /_\\/ __ / /___| | http://www.haskell.org/ghc/
\____/\/ /_/\____/|_| Type :? for help.
Loading package base ... linking ... done.
[ 1 of 55] Compiling Network.Stream ( dependencies/HTTP-2006.7.7//
Network/Stream.hs, interpreted )
..
..
[55 of 55] Compiling Network.Hackage.CabalInstall.Main ( src/Network/
Hackage/CabalInstall/Main.hs, interpreted )
Ok, modules loaded: Distribution.Compiler,
Distribution.InstalledPackageInfo, Distribution.License,
Distribution.Make, Distribution.Program, Distribution.Package,
Distribution.PackageDescription, Distribution.ParseUtils,
Distribution.PreProcess, Distribution.PreProcess.Unlit,
Distribution.Setup, Distribution.SetupWrapper, Distribution.Simple,
Distribution.Simple.Build, Distribution.Simple.Configure,
Distribution.Simple.GHC, Distribution.Simple.GHCPackageConfig,
Distribution.Simple.Hugs, Distribution.Simple.Install,
Distribution.Simple.JHC, Distribution.Simple.LocalBuildInfo,
Distribution.Simple.NHC, Distribution.Simple.Register,
Distribution.Simple.SrcDist, Distribution.Simple.Utils,
Distribution.Verbosity, Distribution.Version,
Distribution.Compat.ReadP, Language.Haskell.Extension,
Distribution.Compat.FilePath, Distribution.GetOpt,
Distribution.Compat.Map, Distribution.Compat.Directory,
Distribution.Compat.Exception, Distribution.Compat.RawSystem,
Distribution.Compat.TempFile, Network.Hackage.CabalInstall.Main,
Network.Hackage.CabalInstall.BuildDep,
Network.Hackage.CabalInstall.Types,
Network.Hackage.CabalInstall.Install,
Network.Hackage.CabalInstall.TarUtils,
Network.Hackage.CabalInstall.Fetch,
Network.Hackage.CabalInstall.Dependency,
Network.Hackage.CabalInstall.Config, Paths_cabal_install,
Network.HTTP, Network.TCP, Network.Stream,
Network.Hackage.CabalInstall.Clean,
Network.Hackage.CabalInstall.Update,
Network.Hackage.CabalInstall.Utils,
Network.Hackage.CabalInstall.Info, Network.Hackage.CabalInstall.List,
Network.Hackage.CabalInstall.Configure,
Network.Hackage.CabalInstall.Setup.
*Network.Hackage.CabalInstall.Main> :main install xslt
Loading package filepath-1.0 ... linking ... done.
..
..
Loading package regex-compat-0.71 ... linking ... done.
configure: Reading installed packages...
'libxml-0.1' is present.
Building 'libxml-0.1'
Configuring...
ghc-6.7.20070520: malformed integer argument in -vNormal
Usage: For basic information, try the `--help' option.
*** Exception: exit: ExitFailure 1
*Network.Hackage.CabalInstall.Main> :break
Network.Hackage.CabalInstall.Install 108
Breakpoint 0 activated at src/Network/Hackage/CabalInstall/Install.hs:
108:25-61
*Network.Hackage.CabalInstall.Main> :main install xslt
configure: Reading installed packages...
'libxml-0.1' is present.
Building 'libxml-0.1'
Configuring...
Stopped at src/Network/Hackage/CabalInstall/Install.hs:108:25-61
_result :: IO ()
path :: FilePath
cmd :: String
cmdOps :: [String]
107 unwords ["setupWrapper", show
(cmd:cmdOps), show path]
108 setupWrapper (cmd:cmdOps) (Just path)
109 bracket_ (createDirectoryIfMissing True tmpDirPath)
[src/Network/Hackage/CabalInstall/Install.hs:108:25-61]
*Network.Hackage.CabalInstall.Main> :p cmd
cmd = (_t1::String)
[src/Network/Hackage/CabalInstall/Install.hs:108:25-61]
*Network.Hackage.CabalInstall.Main> :force cmd
cmd = "configure"
[src/Network/Hackage/CabalInstall/Install.hs:108:25-61]
*Network.Hackage.CabalInstall.Main> :force cmdOps
cmdOps = ["--verbose=1","--user","--prefix=/Users/pep/usr"]
[src/Network/Hackage/CabalInstall/Install.hs:108:25-61]
*Network.Hackage.CabalInstall.Main> :force path
path = "/tmp/TMPlibxml-0.1TMP/libxml-0.1"
[src/Network/Hackage/CabalInstall/Install.hs:108:25-61]
*Network.Hackage.CabalInstall.Main> :continue
ghc-6.7.20070520: malformed integer argument in -vNormal
Usage: For basic information, try the `--help' option.
*** Exception: exit: ExitFailure 1
*Network.Hackage.CabalInstall.Main> :break Distribution.SetupWrapper 81
*Network.Hackage.CabalInstall.Main> :delete 0
*Network.Hackage.CabalInstall.Main> :main install xslt
configure: Reading installed packages...
'libxml-0.1' is present.
Building 'libxml-0.1'
Configuring...
Stopped at ../Cabal.work/Distribution/SetupWrapper.hs:81:14-30
_result :: FilePath
comp :: Distribution.Compiler.Compiler
80 rawSystemExit (verbosity flags)
81 (compilerPath comp)
82 (cabal_flag ++
[../Cabal.work/Distribution/SetupWrapper.hs:81:14-30]
*Network.Hackage.CabalInstall.Main> :set -fbreak-on-exception
[../Cabal.work/Distribution/SetupWrapper.hs:81:14-30]
*Network.Hackage.CabalInstall.Main> :trace
ghc-6.7.20070520: malformed integer argument in -vNormal
Usage: For basic information, try the `--help' option.
Stopped at <exception thrown>
_exception :: e
unable to list source for <exception thrown>
[<exception thrown>] *Network.Hackage.CabalInstall.Main> :history
-1 : ../Cabal.work/Distribution/Simple/Utils.hs:144:32-43
-2 : ../Cabal.work/Distribution/Simple/Utils.hs:144:10-27
-3 : ../Cabal.work/Distribution/Simple/Utils.hs:144:2-28
-4 : ../Cabal.work/Distribution/Simple/Utils.hs:144:2-43
-5 : ../Cabal.work/Distribution/SetupWrapper.hs:83:55-69
-6 : ../Cabal.work/Distribution/Verbosity.hs:51:5-13
-7 : ../Cabal.work/Distribution/SetupWrapper.hs:83:49-70
-8 : ../Cabal.work/Distribution/SetupWrapper.hs:83:43-70
-9 : ../Cabal.work/Distribution/SetupWrapper.hs:(82,14)-(83,71)
-10 : ../Cabal.work/Distribution/Simple/Configure.hs:(398,15)-(401,26)
<end of history>
[<exception thrown>] *Network.Hackage.CabalInstall.Main> :back
Logged breakpoint at ../Cabal.work/Distribution/Simple/Utils.hs:
144:32-43
_result :: IO ()
res :: GHC.IOBase.ExitCode
143 res <- cmd
144 unless (res == ExitSuccess) $ exitWith res
145
[-1: ../Cabal.work/Distribution/Simple/Utils.hs:144:32-43]
*Network.Hackage.CabalInstall.Main> :back
Logged breakpoint at ../Cabal.work/Distribution/Simple/Utils.hs:
144:10-27
_result :: Bool
res :: GHC.IOBase.ExitCode
143 res <- cmd
144 unless (res == ExitSuccess) $ exitWith res
145
[-2: ../Cabal.work/Distribution/Simple/Utils.hs:144:10-27]
*Network.Hackage.CabalInstall.Main> :back
Logged breakpoint at ../Cabal.work/Distribution/Simple/Utils.hs:144:2-28
_result :: IO () -> IO ()
res :: GHC.IOBase.ExitCode
143 res <- cmd
144 unless (res == ExitSuccess) $ exitWith res
145
[-3: ../Cabal.work/Distribution/Simple/Utils.hs:144:2-28]
*Network.Hackage.CabalInstall.Main> :back
Logged breakpoint at ../Cabal.work/Distribution/Simple/Utils.hs:144:2-43
_result :: IO ()
res :: GHC.IOBase.ExitCode
143 res <- cmd
144 unless (res == ExitSuccess) $ exitWith res
145
[-4: ../Cabal.work/Distribution/Simple/Utils.hs:144:2-43]
*Network.Hackage.CabalInstall.Main> :back
Logged breakpoint at ../Cabal.work/Distribution/SetupWrapper.hs:83:55-69
_result :: Distribution.Verbosity.Verbosity
flags :: Distribution.SetupWrapper.Flags
82 (cabal_flag ++
83 ["--make", f, "-o", "setup", "-v"++show (verbosity
flags)])
84 rawSystemExit (verbosity flags)
[-5: ../Cabal.work/Distribution/SetupWrapper.hs:83:55-69]
*Network.Hackage.CabalInstall.Main> _result
Normal
[-5: ../Cabal.work/Distribution/SetupWrapper.hs:83:55-69]
*Network.Hackage.CabalInstall.Main> show it
"Normal"
[-5: ../Cabal.work/Distribution/SetupWrapper.hs:83:55-69]
*Network.Hackage.CabalInstall.Main>
More information about the Cvs-ghc
mailing list