find_call_path
Trace how one C/C++ function reaches another via BFS on libclang's call graph, including function-pointer and dispatch edges. Returns up to five shortest paths.
Instructions
Find call paths between two C/C++ functions via BFS in the libclang call graph, including function-pointer edges, ISR vector registrations, implicit constructors, and synthetic dispatch edges (event loops, thread starts). libclang-powered: follows function-pointer edges and ISR vector registrations that text-based search cannot resolve.
Use to answer "how does A reach B?" — e.g. tracing how a high-level
event handler eventually calls a low-level driver. Returns up to 5
shortest paths, each with depth (edge count) and chain
(e.g. "main → app_run → modem_init").
Edge types traversed: The BFS includes call, indirect
(function pointers / ISRs), implicit_construct (global/static
object constructors), and dispatch (synthetic edges through event
loops like EventQueue::dispatch_forever and thread starts like
Thread::start).
Limitations:
Caveat 1 — Callback/event-loop dispatch: Callbacks registered via dispatch-registration APIs (
EventQueue::call_every,k_work_submit,xTimerStart) are bridged to their dispatch entry point (e.g.dispatch_forever,z_work_q_main) via a built-in mapping for mbed-os, Zephyr, and FreeRTOS. Custom or uncommon RTOS dispatch patterns can be added via[call_graph.dispatch_bridges]in.fw-context/config.toml. Bridges whose entry point symbol does not exist in the index are silently skipped — the map can list all supported platforms without causing errors.Caveat 2 — Ambiguous name resolution in fallback paths: When libclang cannot resolve a call (e.g. template-obscured
_timeout.attach(...)), a source-line regex fallback attempts to match the method name. If multiple methods share the same unqualified name AND neither the receiver field type nor the caller's class provide disambiguation, the edge is not created (conservative — avoids false paths).Caveat 3 — Global constructor bridging: File-scope
implicit_constructedges are reachable through a synthetic<global ctors>node injected betweenmainand all global constructors. Works for any call-path query that can reachmain— not limited to queries starting frommain.
When find_call_path returns empty and you believe a path
should exist:
Check for async dispatch — use
search_bodies("call_every")orsearch_bodies("attach")to find callback registrations.Manually trace through callbacks with
find_callerson intermediate symbols.Raise
max_depth(default 10, max 50) if the call chain is long.Use
find_indirect_call_sites/find_indirect_targetsto verify function-pointer wiring.
For one-sided exploration use find_all_callers_recursive (who reaches
this?) or find_callees_recursive (what does this reach?).
For exact call-graph verification use find_callers or
find_references.
Read-only. No side effects. Requires both symbols to be in the index
and refs enabled (fw-context index — refs on by default).
Args: from_name: Starting symbol for path search. to_name: Target symbol to find path to. project_root: Project root. Auto-detected if omitted. max_depth: Maximum BFS depth for path search (default 10, max 50).
Returns:
list of dicts, each with: depth (edge count, int), chain (str —
e.g. "main → app_run → modem_init"). Empty list when no
path exists within the depth limit.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| image | No | Sysbuild image name within the variant (multi-project). Omit for all images of the variant. | |
| to_name | Yes | Target symbol to find path to. | |
| variant | No | Build variant name (multi-project). Omit to use default_variant or fail-closed. Use '*' for all variants. | |
| from_name | Yes | Starting symbol for path search. | |
| max_depth | No | Maximum BFS depth for path search (default 10). | |
| project_root | No | Project root. Auto-detected if omitted. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |