* ir1-grapher
This library graphs SBCL [[https://cmucl.org/docs/internals/html/The-Implicit-Continuation-Representation.html#The-Implicit-Continuation-Representation][ir1]] and outputs graphviz.
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.
** How do I use it during compiler tracing?
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 [[https://common-lisp.net/project/asdf/asdf/Configuring-ASDF-to-find-your-systems.html][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.
** How do I use the system interactively?
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:
*** ~(make-and-dfs object distance)~
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:
*** ~(interactively-graph graph &optional filename)~
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~.
*** ~(output)~
This function outputs the current working graph to a string, and
if ~interactively-graph~ was called with a filename, writes it to
that file.
*** ~(expand codename)~
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.
*** ~(unexpand codename)~
Removes the node tied to the codename from the table, meaning that
it won't get its neighbours traversed when graph rendering is done.
*** ~(get-node codename)~
Returns the object tied to ~codename~ from the current interactive
graph.
** And if you don't want to use ~interactively-graph~:
*** ~(render-graph 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~.
*** ~(expand-codename graph codename)~
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~.
*** ~(unexpand-codename graph codename)~
Does the same thing as ~unexpand~, but without using
~interactively-graph~.
*** ~(get-node-from-codename graph codename)~
Return the node tied to ~codename~ in ~graph~.
Does the same thing as ~node~, but without using
~interactively-graph~.
** What good is ~interactively-graph~ if I need to run ~dot~ every time the graph gets output?
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.
** Help! It's saying there's a package locking error.
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.
** But what if the compiler breaks your library, or I want to muck around with it?
~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.
** After the ~.dot~ files get output, how do I render them?
~dot -T<output-format> input.dot > output~.
For example, ~dot -Tsvg trace-1-DEFUNFOO.dot > out.svg~.
** Can I see an example?
Yes, look at the ~example/~ folder. It contains the dot output
when running ~(compile-file "testfile" :trace-file t)~.