Provide call stacks in GHCi
I want call stacks to be available in GHCi with no effort on the part of the user, or changes to the source code. The call stack will be available both programatically and via GHCi commands:
-
GHC.Stack.currentCallStack
returns the current call stack as[String]
. This can be used fromerror
and copied into the exception string. -
Debug.Trace.traceStack
prints out the stack anywhere. - GHCi will have a new command
:where
to print out the stack when stopped at a breakpoint
The stack trace will be the accurate (i.e. not exposing details of lazy evaluation or tail calls) and detailed (including locations of calls, not just the enclosing function definition).
Implementation
Here's how it's going to work:
- We make GHCi work with profiling (done: D1407)
- We make breakpoint ticks behave like SCC annotations, and update the interpreter to implement the cost-centre-stack semantics.
Example
Here's an example from my prototype:
g :: Int -> [Int]
g n = traceStack "g" [n]
h :: Int -> Bool
h n = case g n of
[] -> True
(x:xs) -> False
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs
k n = map h [n]
[1 of 1] Compiling Main ( /home/smarlow/scratch/dbg1.hs, interpreted )
Ok, modules loaded: Main.
*Main> k 1
[g
Stack trace:
Main.g (/home/smarlow/scratch/dbg1.hs:13:7-24)
Main.g (/home/smarlow/scratch/dbg1.hs:13:1-24)
Main.h (/home/smarlow/scratch/dbg1.hs:16:12-14)
Main.h (/home/smarlow/scratch/dbg1.hs:(16,7)-(18,24))
Main.h (/home/smarlow/scratch/dbg1.hs:(16,1)-(18,24))
Main.map (/home/smarlow/scratch/dbg1.hs:22:16-18)
Main.map (/home/smarlow/scratch/dbg1.hs:22:16-29)
Main.map (/home/smarlow/scratch/dbg1.hs:(21,1)-(22,29))
Main.k (/home/smarlow/scratch/dbg1.hs:24:7-15)
Main.k (/home/smarlow/scratch/dbg1.hs:24:1-15)
False]
*Main>
We could trim some of the extra detail from the stack trace so that each function appears once; there are several choices here, currently I'm collecting and displaying the most detail.
Deployment
One disadvantage of this is that it requires GHCi to be built with profiling, and all the libraries have to be built with profiling too. There are two options for deployment:
- We deploy two versions of GHCi (profiled and non-profiled), or
- We expand on what GHCJS did, and make the interpreted code run in a separate process, thus separating the GHCi binary itself from the code being interpreted. This would allow the interpreted code to be run with the profiled RTS while GHCi itself is unprofiled.