Skip to content

Reports & CFG visualization

The report system is for debugging and understanding — what changed, where, and which pass did it. It emits a machine-readable obfuscation map plus per-pass CFG snapshots, and a Python viewer renders them as HTML.

Generate reports

Run opt with a report directory and a fixed seed:

opt -passes=obfuscation -S app.ll -o app.obf.ll \
    -obf-seed=1 -obf-deterministic \
    -obf-report-dir=obf_report

This produces:

obf_report/
├─ obf_report.json                          # machine-readable obfuscation map
└─ cfg/<fn>/
   ├─ before.dot                            # CFG before any pass
   ├─ after.dot                             # CFG after all passes
   └─ per_pass/<passId>/
      ├─ after.dot                          # CFG after this pass
      └─ diff.dot                           # optional diff overlay
Flag Effect
-obf-report-dir=<dir> Emit CFG DOT files + JSON into this directory.
-obf-report-json=<path> Write just the report JSON to a path (- = stdout).

The HTML viewer

python utils/obf_report_html.py \
    --json obf_report/obf_report.json \
    --out  obf_report/obf_report.html \
    --renderer dot

Per-function views show pass-by-pass instruction-count deltas, CFG before/after, per-pass diff overlays, and a difficulty score (cyclomatic complexity, opaque-predicate count, MBA depth, …).

Renderers

--renderer Needs Notes
dot (recommended) Graphviz dot on PATH Renders graphs offline as inline SVG — no CDN, no WASM.
wasm served over HTTP python -m http.server 8000 --directory obf_report then open the page.
text nothing Metadata tables only, no graphs — works anywhere.

Reading the vm entry

For virtualised functions the report's passes[] array holds a vm entry with status, changed, insts_before, insts_after, delta_insts. A large delta_insts is expected (the interpreter is much bigger than the original). status = "skipped" carries a skip_reason (see VM eligibility), and the CFG diff shows the whole original graph collapsed into the wrapper + engine.

Theme-safe CFG SVGs

utils/cfg_svg.py regenerates CFG SVGs directly from the obf-report .dot files — pure Python, no Graphviz needed. It produces light+dark theme-safe SVGs (single and pair subcommands, with --stack / --dense). These are the diagrams used throughout this documentation.

Debugging workflow

  1. Reproduce with a fixed seed (-obf-seed=1 -obf-deterministic).
  2. Add -obf-verify to catch invalid IR at the pass that introduced it.
  3. Generate a report; open the HTML viewer and find the offending pass by its CFG diff.
  4. Bisect: start with a single pass, confirm it works, add passes one at a time.

See Diagnostics for obf-dump-config and obf-metrics.