Type Points From

The type points from analysis creates a graph where the nodes are info table addresses and there is an edge between nodes if there’s a pointer from one node to the other. The TPF graph can be seen as a quotiented version of the original heap graph. This makes the size of graph tractable to deal with unlike the original heap graph which is usually too large to represent fully in memory.

The type points from graph is used in the Cork leak detection analysis. A more detailed description of the graph structure and the detectLeaks analysis can be read in the paper.

Making the Graph

The graph is created using the typePointsFrom function, which takes a set of roots as the location to start from. The output is a TypePointsFrom graph, the nodes of the graph can be retrieved using getNodes and the edges by getNodes.

p45 e = do
  pause e
  t <- runTrace e $ do
    _ <- precacheBlocks
    rs <- gcRoots
    typePointsFrom rs
  let es = (reverse $ sortBy (comparing (cssize . snd)) (Map.assocs (getEdges t)))
  es' <- runTrace e $ mapM (\(Edge e1 e2, c) -> do
                                e' <- (,) <$> getKey e1 <*> getKey e2
                                return (e', c)) es
  mapM_ print es'

This program just gets the edges and prints them out so you can use a grep based interface to look at specific edges you care about.

Understanding the Output

The display modes for the TPF graph are not very refined yet. I just print out all the edges and then use a less based TUI in order to understand the output.

Using this interface we can have a closer look at what is retaining TyConApp nodes than any census method allowed us so far. By filtering the output for edges which point from a TyConApp, you can find the edge which points from TyConApp to :.

(("CONSTR_2_0:TyConApp_GHC.Core.TyCo.Rep_12_con_info::compiler/GHC/Core/TyCo/Rep.hs:1029:20-22","CONSTR_2_0::_GHC.Base_2_con_info::libraries/base/GHC/Base.hs:1246:16-29"),CS {cscount = Count 3335397, cssize = Size {getSize = 80049528}, csmax = Max {getMax = Size {getSize = 24}}})

From the source information you can see these : applications come from the map function, which is not so useful. Let’s see why these : are being retained.

(("CONSTR_2_0::_GHC.Base_2_con_info::libraries/base/GHC/Base.hs:1246:16-29","CONSTR_2_0::_GHC.Base_2_con_info::libraries/base/GHC/Base.hs:1246:16-29"),CS {cscount = Count 3406303, cssize = Size {getSize = 81751272}, csmax = Max {getMax = Size {getSize = 24}}})
(("CONSTR_2_0::_GHC.Base_2_con_info::libraries/base/GHC/Base.hs:1246:16-29","CONSTR_2_0:TyConApp_GHC.Core.TyCo.Rep_12_con_info::compiler/GHC/Core/TyCo/Rep.hs:1029:20-22"),CS {cscount = Count 1966843, cssize = Size {getSize = 47204232}, csmax = Max {getMax = Size {getSize = 24}}})
(("CONSTR_2_0::_GHC.Base_2_con_info::libraries/base/GHC/Base.hs:1246:16-29","CONSTR:IfaceCase_GHC.CoreToIface_0_con_info::compiler/GHC/CoreToIface.hs:548:31-88"),CS {cscount = Count 18957, cssize = Size {getSize = 454968}, csmax = Max {getMax = Size {getSize = 24}}})

The top two entries dominate, which correpond to pointing to its own tail but interestingly pointing back to a TyConApp constructor. So we end up with a lot of nested TyConApp calls and so on.. The analysis could continue further but you sometimes need to go a long way up the tree to find out why something is being retained. A better way to process the graph rather than looking at the text dump would be useful.

In future it would be good to generalise this analysis so it could create different kinds of graphs in a similar way to the normal census mode.

(Experimental) Automatically Detecting Leaks

There is also an experimental implementation of the leak detection algorithm described in the Cork paper. This needs more testing to be generally usable.

The analysis works by pausing the program in a given interval and comparing the difference between TPF graphs, in order to try to find the parts of the graph which are getting bigger over time. The output is rendered as a dot graph.

detectLeaks :: Int  -- Sample interval in seconds.
            -> Debuggee
            -> IO ()

If you want to use this then it’s best to read the source code, as you will probably need to modify it to make it useful for you.