For easier visualization of profiling data, mlprof
can
create a call graph of the program in dot format, from which you can
use the graphviz
software package to create a PostScript or PNG graph. For example,
mlprof -call-graph foo.dot foo mlmon.out
will create foo.dot
with a complete call graph. For each source
function, there will be one node in the graph that contains the
function name (and source position with -show-line true
), as
well as the percentage of ticks. If you want to create a call graph
for your program without any profiling data, you can simply call
mlprof
without any mlmon.out
files, as in
mlprof -call-graph foo.dot foo
Because SML has higher-order functions, the call graph is is dependent on MLton’s analysis of which functions call each other. This analysis depends on many implementation details and might display spurious edges that a human could conclude are impossible. However, in practice, the call graphs tend to be very accurate.
Because call graphs can get big, mlprof
provides the -keep
option
to specify the nodes that you would like to see. This option also
controls which functions appear in the table that mlprof
prints.
The argument to -keep
is an expression describing a set of source
functions (i.e. graph nodes). The expression e should be of the
following form.
-
all
-
"s"
-
(and e …)
-
(from e)
-
(not e)
-
(or e)
-
(pred e)
-
(succ e)
-
(thresh x)
-
(thresh-gc x)
-
(thresh-stack x)
-
(to e)
In the grammar, all
denotes the set of all nodes. "s"
is
a regular expression denoting the set of functions whose name
(followed by a space and the source position) has a prefix matching
the regexp. The and
, not
, and or
expressions denote
intersection, complement, and union, respectively. The pred
and
succ
expressions add the set of immediate predecessors or successors
to their argument, respectively. The from
and to
expressions
denote the set of nodes that have paths from or to the set of nodes
denoted by their arguments, respectively. Finally, thresh
,
thresh-gc
, and thresh-stack
denote the set of nodes whose
percentage of ticks, gc ticks, or stack ticks, respectively, is
greater than or equal to the real number x.
For example, if you want to see the entire call graph for a program,
you can use -keep all
(this is the default). If you want to see
all nodes reachable from function foo
in your program, you would
use -keep '(from "foo")'
. Or, if you want to see all the
functions defined in subdirectory bar
of your project that used
at least 1% of the ticks, you would use
-keep '(and ".*/bar/" (thresh 1.0))'
To see all functions with ticks above a threshold, you can also use
-thresh x
, which is an abbreviation for -keep '(thresh x)'
. You
can not use multiple -keep
arguments or both -keep
and -thresh
.
When you use -keep
to display a subset of the functions, mlprof
will add dashed edges to the call graph to indicate a path in the
original call graph from one function to another.
When compiling with -profile-stack true
, you can use mlprof -gray
true
to make the nodes darker or lighter depending on whether their
stack percentage is higher or lower.
MLton’s optimizer may duplicate source functions for any of a number
of reasons (functor duplication, monomorphisation, polyvariance,
inlining). By default, all duplicates of a function are treated as
one. If you would like to treat the duplicates separately, you can
use mlprof -split regexp
, which will cause all duplicates of
functions whose name has a prefix matching the regular expression to
be treated separately. This can be especially useful for higher-order
utility functions like General.o
.
Caveats
Technically speaking, mlprof
produces a call-stack graph rather than
a call graph, because it describes the set of possible call stacks.
The difference is in how tail calls are displayed. For example if f
nontail calls g
and g
tail calls h
, then the call-stack graph
has edges from f
to g
and f
to h
, while the call graph has
edges from f
to g
and g
to h
. That is, a tail call from g
to h
removes g
from the call stack and replaces it with h
.