Swirly Mein Kopf

Thursday, December 20. 2012

GHCi integration for GHC.HeapView

Digital World

Given the very positive feedback for Dennis Felsing’s tool ghc-vis, which visualizes the heap representation of a Haskell value, including all the gory details such as thunks, values retained by thunks indirections, sharing etc, I saw the need to provide this information also directly in GHCi, without having to load a graphics library or opening extra libraries. So I added the required features (traversing the heap and pretty-printing the results) to my ghc-heap-view package and, also following ghc-vis’s lead, added a ghci file that, when loaded, provides you with a :printHeap command. Here you can see it in action:

A plain value

Prelude> :script /home/jojo/.cabal/share/ghc-heap-view-
Prelude> let x = [1..10]
Prelude> x
Prelude> :printHeap x
_bh [S# 1,S# 2,S# 3,S# 4,S# 5,S# 6,S# 7,S# 8,S# 9,S# 10]

Note that the tools shows us that the list is a list of S# constructors, and also that it is still hidden behind a blackhole. After running System.Mem.performGC, this disappears.

A thunk

Prelude> let x = Just (1 + 1)
Prelude> :printHeap x
Just _bco
Prelude> x
Just 2
Prelude> System.Mem.performGC
Prelude> :printHeap x
Just (S# 2)

Here, we see how the calculation was deferred until forced by showing the value of x. The name _bco stands for a bytecode object as used by the interpreter. Getting useful information from them is a bit harder than for compiled thunks, so for more accurate results put the code in a Haskell source file, compile it and use the GHC.HeapView API to print the interesting parts.

A partial application

Prelude> let a = "hi"
Prelude> let partial = (a ++)
Prelude> partial ""
Prelude> System.Mem.performGC
Prelude> let x = (a, partial)
Prelude> :printHeap x
let x1 = "hi"
in (x1,_fun x1)

The information which function is called there (++ in this case) is lost at runtime, but we still see that the second element of the tuple is a partial application of some value to the first element.

A cyclic structure

Prelude> let s = "ho"
Prelude> let x = cycle s
Prelude> length (take 100 (show x))
Prelude> System.Mem.performGC
Prelude> :printHeap x
let x0 = C# 'h' : C# 'o' : x0
in x0
Prelude> let y = map Data.Char.toUpper x
Prelude> length (take 100 (show y))
Prelude> :printHeap y
C# 'H' : C# 'O' : C# 'H' : C# 'O' : C# 'H' : C# 'O' : C# 'H' : C# 'O' : C# 'H' : C# 'O' : _bh (C# 'H' : C# 'O' : C# 'H' : C# 'O' : C# 'H' : C# 'O' : C# 'H' : C# 'O' : ... : ...)

The cyclic, tying-the-knot structure of cycle is very visible. But can also see how easily it is broken, in this case by mapping a function over the list.

Mutual recursion

Prelude> let {x = 'H' : y ; y = 'o' : x }
Prelude> length (show (take 10 x, take 10 y)) `seq` return ()
Prelude> System.Mem.performGC
Prelude> :printHeap (x,y)
let x1 = C# 'H' : x3
    x3 = C# 'o' : x1
in (x1,x3)

If you want to look at multiple variables at once, just pass a tuple to printHeap

In the hope that this will be a useful tool for you, I uploaded version of ghc-heap-view to hackage.


No Trackbacks


Display comments as (Linear | Threaded)

*Great! Sounds like a tool to have. I'll try it next time figuring space leaks.

The post about assertNF is very useful too. Thanks.
#1 Sergey on 2013-04-05 10:37 (Reply)

Add Comment

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.

Gravatar, Favatar, Identica author images supported.
What is the first name of the owner of this blog? / Wie heißt der Betreiber dieses Blogs mit Vornamen?
Nach oben