Static analysis tool for generating function call graphs from OCaml source code.
This tool analyzes compiled type annotation (.cmt) files to trace function calls and generates graphviz-compatible DOT output for visualization.
New: Emacs integration with interactive hierarchical tree view and jump-to-definition!
- Function-centric analysis: Start from a named function and recursively follow the call chain
- Interactive Emacs integration: Hierarchical tree view with fold/unfold and jump-to-definition
- Dynamic external library detection: Automatically queries opam switch to detect ALL installed packages
- Configurable filtering of standard library and external dependencies
- Multiple output formats: DOT (Graphviz), Graph::Easy (human-readable text), and Org-mode (hierarchical tree)
- Clean output separation: Debug logs to stderr, graph to stdout
- Pure compiler-libs implementation - minimal dependencies
# Clone the repository
git clone https://github.com/koonwen/ocaml-callgraph.git
cd ocaml-callgraph
# Build with dune
dune build
# Install binaries (optional)
dune installocamlopt -bin-annot -c myfile.mlThis generates myfile.cmt which contains the typed AST needed for analysis.
# Basic usage - analyze one function (DOT format by default)
# You can provide either .cmt or .ml file (auto-discovers .cmt in _build/)
dune exec -- cg my_function myfile.ml > graph.dot
# Filter out stdlib functions for cleaner output
dune exec -- cg --no-stdlib my_function myfile.ml > graph.dot
# Show only local functions (exclude stdlib and external libraries)
dune exec -- cg --no-stdlib --no-external my_function myfile.ml > graph.dot
# Output in Graph::Easy format (ASCII-friendly text format)
dune exec -- cg --format grapheasy my_function myfile.ml > graph.txt
# Output in Org-mode format for Emacs
dune exec -- cg --format org --no-stdlib my_function myfile.ml > callgraph.org
# Pipe directly to graphviz (debug logs go to stderr)
dune exec -- cg --no-stdlib my_function myfile.ml 2>/dev/null | dot -Tpng > graph.png
# You can still use .cmt files directly if needed
dune exec -- cg my_function _build/default/myfile.cmt > graph.dot# Generate PNG
dot -Tpng graph.dot -o graph.png
# Generate SVG
dot -Tsvg graph.dot -o graph.svg
# View directly (Linux)
xdg-open graph.png
# Or use online viewers
# https://dreampuf.github.io/GraphvizOnline/
# https://edotor.net/# The Graph::Easy format is human-readable as-is:
cat graph.txt
# Or render as ASCII art (requires Graph::Easy perl module):
graph-easy graph.txt
# Convert to other formats:
graph-easy graph.txt --as boxart
graph-easy graph.txt --as svg > graph.svgNote: Graph::Easy's ASCII layouter works best for simple, relatively linear call graphs. For complex graphs with many cycles and interconnections, the layouter may give up and not render all nodes/edges. In such cases, use DOT format with Graphviz for better results.
# Generate org-mode hierarchical tree
cg --format org --no-stdlib my_function file.cmt > callgraph.org
# Open in Emacs
emacs callgraph.orgThe org-mode format creates a hierarchical, foldable tree structure:
- Click on function links to jump to their definitions
- Use TAB to fold/unfold sections
- Navigate with standard org-mode keybindings
- Cycle detection shows
(cycle)indicators to prevent infinite recursion
Example output:
* main [[file:example.ml::10][main]]
** process_data [[file:example.ml::15][process_data]]
*** helper_function [[file:example.ml::5][helper_function]]
** another_helper [[file:example.ml::20][another_helper]]
*** helper_function [[file:example.ml::5][helper_function]]
The cg tool performs function-centric call graph analysis:
cg [OPTIONS] <function_name> <file.cmt|file.ml>
Options:
--no-stdlib Exclude Stdlib.* functions (List.map, Printf.printf, +, -, etc.)
--no-external Exclude external opam packages (dynamically detected from opam switch)
--format FORMAT Output format: 'dot' (default), 'grapheasy', or 'org'
--emit-locations Emit function location information to stderr (file:line:col)
--help, -h Show help message
Formats:
dot Graphviz DOT format (default) - for visualization with graphviz
grapheasy Graph::Easy text format - human-readable, can be rendered as ASCII art
org Org-mode hierarchical tree - for Emacs org-mode with jump-to-definition
Input File:
You can provide either a .cmt file directly or a .ml file.
When given a .ml file, the tool automatically searches for the
corresponding .cmt file in the _build/default/ directory.Features:
- Starts from a named function and recursively follows call chain
- Automatic .cmt discovery: Provide .ml files, tool finds .cmt in _build/ automatically
- Dynamic opam package detection: Queries your opam switch to detect ALL external libraries
- Configurable filtering to remove noise from stdlib and external packages
- Separate debug output (stderr) doesn't pollute graph (stdout)
- Handles cycles gracefully with visited function tracking
- Perfect for piping to graphviz
Output Format Recommendations:
- DOT format (default): Best for all graphs, especially complex ones with cycles. Use with Graphviz for visualization.
- Graph::Easy format: Best for simple, relatively linear call graphs. ASCII art generation works well for small graphs but may fail on complex graphs with many cycles and interconnections.
- Org format: Best for Emacs users. Creates a hierarchical, foldable tree with clickable links to jump to function definitions. Works well with org-mode's navigation features.
Example:
# Analyze 'process_request' function, show only local calls (DOT format)
cg --no-stdlib --no-external process_request server.cmt | dot -Tpng > graph.png
# For simple graphs, Graph::Easy format provides nice ASCII art
cg --format grapheasy --no-stdlib simple_function file.cmt | graph-easy
# Generate org-mode hierarchical tree for Emacs (automatically enables location tracking)
cg --format org --no-stdlib my_function file.cmt > callgraph.org
# Emit location metadata for editor integration (jump-to-definition)
cg --emit-locations my_function file.cmt 2> locations.txt > graph.dotThe --emit-locations flag outputs function location information to stderr in the format:
function_name:file:line:col
This metadata can be used for editor integration, allowing jump-to-definition functionality when viewing call graphs. Location output is separated to stderr so it doesn't interfere with the graph output on stdout.
Example:
# Capture both graph and locations
cg --emit-locations my_function file.cmt > graph.dot 2> locations.txt
# The locations file will contain:
# my_function:file.ml:10:4
# helper_function:file.ml:5:4
# process_data:file.ml:15:4An Emacs package is provided for interactive call graph viewing with jump-to-definition support.
Features:
- Hierarchical tree view with the
hierarchypackage (recommended!) - View call graphs in Emacs buffer with ASCII art rendering
- Generate org-mode call graphs for documentation
- Click on function names to jump to definitions
- Fold/unfold tree nodes with TAB
- Toggle filtering with hotkeys (
sfor stdlib,efor external) - Automatic function name and .cmt file inference
- Automatic location tracking
Quick start:
;; In ~/.emacs.d/init.el
(add-to-list 'load-path "/path/to/ocaml-callgraph/emacs")
(require 'ocaml-callgraph)
;; Optional: Add keybindings
(global-set-key (kbd "C-c g h") 'ocaml-callgraph-generate-hierarchy) ; Hierarchy tree (recommended)
(global-set-key (kbd "C-c g g") 'ocaml-callgraph-generate) ; ASCII graph viewer
(global-set-key (kbd "C-c g o") 'ocaml-callgraph-generate-org) ; Org-mode fileUsage:
;; Interactive hierarchy tree viewer (RECOMMENDED)
M-x ocaml-callgraph-generate-hierarchy RET
;; Interactive graph viewer with ASCII art
M-x ocaml-callgraph-generate RET
;; Org-mode hierarchical call graph (creates a file)
M-x ocaml-callgraph-generate-org RET
;; All commands automatically infer function name and .cmt file location
;; Use C-u prefix to manually specify: C-u M-x ocaml-callgraph-generate-hierarchy RET
Hierarchy Tree Features (recommended):
- Interactive, foldable tree structure (no external files needed)
- Press TAB to fold/unfold nodes
- Click or press RET to jump to function definitions
- Press
gto regenerate after changing filters - Press
s/eto toggle stdlib/external filtering - All in a single Emacs buffer - fast and responsive
Org-mode Features:
- Hierarchical, foldable tree structure
- Click on
[[file:...]]links to jump to definitions - Use TAB to fold/unfold sections
- Navigate with standard org-mode keybindings
- Saved to project root as
callgraph-<function-name>.org - Good for documentation and sharing
See emacs/EMACS.md and HIERARCHY_FEATURE.md for detailed documentation.
The examples/ directory contains sample OCaml files demonstrating various use cases:
cd examples
# Build examples and generate graphs
make graphs
# Generate PNG images
make images
# Generate only local function graphs
make graphs FILTER='--no-stdlib --no-external'
# See all options
make helpSee examples/README.md for detailed examples.
The tool uses OCaml's compiler-libs to analyze typed AST:
- Query opam switch to get list of installed packages (cached on first use)
- Read .cmt files using
Cmt_format.read_cmt - Find the starting function in the typed AST
- Traverse typed AST (
Typedtree) to extract function calls - Pattern match on function calls (
Texp_apply) - Extract function paths using
Path.name - Classify each function as local, stdlib, or external (using opam package list)
- Recursively process callees (with cycle detection)
- Output call graph as DOT format
The --no-stdlib and --no-external flags help focus on relevant code:
Functions from OCaml's standard library (always have Stdlib. prefix):
Stdlib.List.mapStdlib.Printf.printfStdlib.+,Stdlib.-,Stdlib.*(operators)
Functions from opam packages (dynamically detected by querying opam list --installed):
Base.List.mapLwt.bindYojson.Basic.from_stringCore.String.split- Any other package installed in your current opam switch
Functions defined in your .cmt file:
my_helperprocess_datahandle_request
Having issues? See the Troubleshooting Guide.
Emacs users: Run M-x ocaml-callgraph-diagnose for quick diagnostics.
Common issues:
- Permission denied: Check executable permissions or set custom org directory
- Cannot find executable: Install with
dune installor set full path - No .cmt file found: Run
dune buildfirst
- OCaml >= 4.14
- Dune >= 3.16
- opam (for dynamic external library detection)
- Graphviz (optional, for visualization)
Contributions are welcome! Please feel free to submit issues or pull requests.
MIT License - see LICENSE file for details.
Koon Wen Lee
- GitHub: https://github.com/koonwen/ocaml-callgraph
- Examples: examples/README.md