Yeah, TreeView isn’t quite what you want, but it’s the closest thing I could think of at the time.
Maybe just using the functionality in
Base.Profile to build the call graph by sampling and visualizing it using ProfileView is closer to what you need.
But I was thinking a bit more about how to construct the call graph without sampling. I guess one thing you would need in order to get an actual call graph for a given function call is the ability to ‘dig into’ functions. So given e.g.
function f(x, y)
a = x * x
a + g(y * y)
somehow get the expression
a = x * x
return a + (Main.g)(y * y)
In general, the expression to return will of course depend on the types of the input arguments, because function
f might have several methods. Here is my attempt at accomplishing this:
function replace_slot_numbers_with_names!(expr::Expr, l::LambdaInfo)
for (i, arg) in enumerate(expr.args)
if isa(arg, SlotNumber)
expr.args[i] = l.slotnames[arg.id]
elseif isa(arg, Expr)
function function_body_expr(f, types)
l = first(code_lowered(f, types))
ast = Base.uncompressed_ast(l)
body = Expr(:block)
body.args = ast[2 : end]
body.typ = l.rettype
expr = Base.remove_linenums!(replace_slot_numbers_with_names!(body, l))
function_body_expr(f, (Int, Int))
which returns exactly the desired expression above. Note: unfortunately, TreeView / its dependency TikzGraphs can’t quite handle this expression due to some LaTeX issues. I worked around these issues by changing this line to
TreeView.walk_tree(function_body_expr(f, (Int, Int)))
Now this is just a piece of the puzzle:
- you’d need to do this recursively, e.g. ‘digging into’
g in the same way, while keeping track of the types of the arguments with which each function is called (i.e., which method is called). Maybe
code_typed is more useful than
code_lowered. Presumably, you’d do this up to some desired depth, or maybe recursing only into functions from a certain module. You’d also need to handle cycles.
- the graph you get out of TreeView isn’t exactly what you want; you only care about methods (things like
= should not appear), and the depth of a node in the tree should be the recursion depth (so all of the methods in the example I gave should actually be at the same depth in the tree, and the methods that
g calls should be nested under
So there’s still quite a lot of work to be done, but maybe this will get you (or somebody else) started. I think it would be very nice to be able to visualize call graphs without sampling.