This library graphs SBCL ir1 and outputs graphviz.
This has been committed as a contrib module for SBCL, under
contrib/sb-graph
🎉
As of now, it is stil unfinished. There are a few tweaks that are needed to improve useablility, in particular, I think the rendering could be done a lot better, with subgraphs, to make it a lot more readable. Also to make it more readable, I’m planning on implementing rainbow-parens-esque arrow + label color randomization.
To load the system, you can call (asdf:load-system
:ir1-grapher)
once you have either loaded ir1-grapher.asd
manually, or placed the folder inside your ASDF load path.
Because of the way it hooks in the compiler, you shouldn’t actually
have to do anything after loading it except turning on trace output
of the compiler (SBCL), and adding :sb-graph
to
sb-c::*compile-trace-targets*
. Turning on the tracing can be done
by calling (compile-file "file" :trace-file t)
, and adding
:sb-graph
to sb-c::*compile-trace-targets*
can be done however
you want. After compilation is done, alongside the normal trace
file, SBCL is hooked into writing a series of .dot files, which
contain the graphviz DOT representation of all the components
compiled. If you set *compile-progress*
to T
, it will print out
progress information, and will tell you when and where it writes
out the graphviz files.
If you ran into a compiler error, and want to graph the still-in-memory code interactively, you can do so with the following functions:
This function takes an ir1 object object
, and integer
distance
, and returns a graph object with every node up to
distance
hops away from object
in its dfs-table
. Each object
is tagged with a “codename”, visible as a hex digit in braces at
the start of each graph node.
Then, to operate on the graph interactively, you can use:
This function takes a graph
and a filename
, and sets the
current working graph to it, and the current output file to the
filename. When calling output
and expand
, the graph will be
written to filename
.
This function outputs the current working graph to a string, and
if interactively-graph
was called with a filename, writes it to
that file.
After you’ve rendered the graph, if you want to add a node to the
dfs-table
(thus expanding the amount of the in-memory objects
rendered), call this function with the codename of the new object
you’d like to add. Example: (expand "A")
.
If you passed a filename to interactively-graph
, this function
will then write the render to file automatically.
Returns the object tied to codename
from the current interactive
graph.
Given a graph with objects in its dfs-table
, returns a string of
the rendering of the graph in DOT.
Does the same thing as output
, but without using
interactively-graph
.
Given a graph and codename, put the node tied to codename
into
the dfs-table
of the graph.
Does the same thing as expand
, but without using
interactively-graph
.
Return the node tied to codename
in graph
.
Does the same thing as node
, but without using
interactively-graph
.
That’s where render-on-change.sh
comes in. Run
render-on-change.sh
with two arguments. First is the input DOT
file, and second is the output SVG file. If you want to export to a
different format, just modify the -Tsvg
of the script.
Run (sb-ext:unlock-package :sb-c)
in the REPL, or compile SBCL
with --with-sb-devel
(if you’re doing compiler work, you should
probably do this anyways). In src/package.lisp
, there’s a
(sb-ext:unlock-package :sb-c)
statement, but it seems to not work
as expected.
hooking.lisp
contains all the code that is used to hook the
compiler directly, and graphing.lisp
goes from the compiler data
structures to the graphviz DOT format.
Right now, I’m assuming that the only place that the compiler will
ever call sb-c::ir2-convert
for each component is inside
%compile-component
, and only one time. If this becomes no longer
true, then the hooking location/manner will have to be modified.
dot -T<output-format> input.dot > output
.
For example, dot -Tsvg trace-1-DEFUNFOO.dot > out.svg
.
Yes, look at the example/
folder. It contains the dot output
when running (compile-file "testfile" :trace-file t)
.