Skip to content

[feature] Headless whole-program call-graph API + export (DOT/JSON), reusing the engine behind the GUI graph views #2890

Description

@bbodisteanu-hacken

Summary

jadx already has everything needed to produce a program-wide call graph, but it
is only reachable as a GUI dialog. Please expose a headless, whole-program
call-graph builder in jadx-core (jadx.api) plus an export option
(DOT / JSON) usable from the API and the CLI without opening the GUI.

What already exists (and why it's not enough)

  1. Edge primitives — already public on master, exactly right:

    • JavaMethod.getUsed() resolved callees
    • JavaMethod.getUnresolvedUsed() unresolved (framework/lib) callees
    • JavaMethod.getUseIn() callers
    • virtual dispatch resolved by OverrideMethodVisitor, names post-rename.

    These are per-method only assembling the whole graph is left entirely to
    the consumer.

  2. GUI graph views (PR feat: Graph views, code pane sync, and more #2784, merged) not reusable headless:
    jadx-gui/.../graphs/CallGraphDialog.java builds a graph, but it is

    • rooted at one selected JavaMethod (not the whole program),
    • depth-limited (callerDepthLimit / calleeDepthLimit),
    • GUI-only (Swing dialog, GraphViz render) no programmatic entry point
      and no data export.
  3. CLI --cfg / --raw-cfg save a per-method control-flow graph to DOT a different thing entirely (intra-method, not the call graph).

So today, anyone building static-analysis tooling on jadx (reachability,
taint/call-graph analysis, security scanners) has to re-walk classes and
methods and re-assemble the graph from the primitives by hand, plus re-derive
everything getUsed() does not include (reflection Class.forName,
X.class constant refs, intent-action dispatch, etc.).

Proposed API

A small builder in jadx.api that materializes the program-wide graph from the
existing primitives:

try (JadxDecompiler jadx = new JadxDecompiler(args)) {
    jadx.load();

    CallGraph cg = jadx.buildCallGraph();           // whole program
    // or scoped/filtered:
    //   jadx.buildCallGraph(CallGraphOptions.builder()
    //        .includePackages("com.example")        // skip framework/libs
    //        .resolvedOnly(false)                    // include unresolved edges
    //        .build());

    for (CallGraph.Edge e : cg.edges()) {
        JavaMethod from = e.from();
        if (e.isResolved()) {
            JavaMethod to  = e.toMethod();       // resolved app callee
        } else {
            IMethodRef ref = e.toUnresolved();   // framework/lib ref (not a JavaNode)
        }
    }
    cg.writeDot(Path.of("callgraph.dot"));            // or writeJson(...)
}

Internally this is the same getUsed()/getUnresolvedUsed()/getUseIn() walk the
GUI already does just hoisted into core, run over getClasses() with no depth
cap, and given an export writer. (getUsed() requires
JadxArgs.setUsageInfoCache(new InMemoryUsageInfoCache()), which the builder can
set/validate itself.)

Proposed CLI

jadx --call-graph dot   -d out app.apk     # write out/callgraph.dot
jadx --call-graph json  -d out app.apk     # write out/callgraph.json

JSON form (stable, tool-friendly) — e.g.:

{
  "nodes": [{"id": 0, "method": "com.example.A.foo()V"}],
  "edges": [{"from": 0, "to": 1, "resolved": true}]
}

Why core, not GUI

  • enables headless / CI / batch analysis (no Swing, no GraphViz needed for JSON),
  • a single canonical implementation instead of every downstream tool
    re-deriving the graph slightly differently,
  • the GUI graph dialog could then be a thin consumer of the same core builder.

Nice-to-haves (optional, can be follow-ups)

  • include the edges getUsed() currently omits, or flag them as a separate edge
    kind: reflection (Class.forName, X.class), intent-action dispatch.
    (Related: the throws / constant-value usage gap see companion request.)
  • per-edge call-site metadata (arg constants), so consumers don't re-walk
    instructions to recover literal arguments.

Happy to help with a PR the hard part (edge computation) already exists; this
is mostly hoisting + an export writer.

Environment

  • jadx-core master 9d4babc (also on nightly r2658.c3f7027)
  • JDK 17+, embedded JadxDecompiler (library use), headless analysis pipeline

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions