Skip to content

LLVM and dot graphs

Wednesday, 1 November 2023

The other day I had to dive into the ksmserver code, the core of session management in KDE Plasma, but found it all a bit difficult to read. If only I could get a callgraph I thought…

Turns out it actually is possible!

In fact it is almost too easy with clang/llvm. There are already solutions that attach LLVM bitcode to existing binaries. e.g. gllvm. gllvm basically is a shim sitting between ninja and the actual compiler and injects some behavior for us. This enables the use without having to refit anything in our existing code. You might note that clazy, our most excellent code analyzer, works in a similar fashion.

Here is how to use it:

# install gllvm (NOTE: as a pre-requisite you should have Go properly set up)
go install -v

# clone plasma-workspace
git clone
# change directory
cd plasma-workspace
# export environment variables
export CXX=gclang++
export CC=gclang
# You may need to set some additional vars so gllvm can find your compilers
## export LLVM_CC_NAME=clang-16
## export LLVM_CXX_NAME=clazy
# Configure
cmake -S . -B build
# Build
cmake --build build/
# extract the bitcode (into build/bin/ksmserver.bc by default)
get-bc -m -v build/bin/ksmserver
# Run through llvm's opt program to generate a callgraph
opt --disable-output --passes=dot-callgraph build/bin/ksmserver.bc
# copy into working directory
cp build/bin/
# prettify the c++ names (this may produce invalid labels, so I'm skipping it here)
## cat build/bin/ | llvm-cxxfilt >
# generate an SVG of the graph
dot -x -Tsvg -ocallgraph.svg
# we now have a callgraph.svg \o/

Depending on the complexity of the software and how many libraries it uses this graph may be incredibly large and verbose though. We’ll need to apply some filtering to make it useful. Or at least I thought so when working with ksmserver.

To further filter the graph you can use the gvpr helper. It is a bit clunky but gets the job done. You can build a filter expression to only include interesting functions like so

    if ($.label!="@(*_ZN9KSMServer*)" || $.label=="@(*metacall*)")
        delete($G, $);

and then filter the dot graph with

# Filter out uninteresting functions (filter.prog is the filter expression from above)
gvpr -c -f filter.prog build/bin/ > intermediate.gv
# Filter out all empty nodes (they call nothing and are of no interest)
gvpr -c "N[$.degree==0]{delete(root, $)}" intermediate.gv > final.gv
# generate an SVG
cat final.gv | dot -x -Tsvg -ocallgraph.svg

Final result