Argus.Analyses.SyncCallInInit (Panoptes v0.13.0)

Copy Markdown View Source

Synchronous calls in init/1 detection.

Identifies GenServer/Supervisor modules whose init/1 callback makes synchronous calls (directly or transitively through the call graph). When combined with supervision ordering, detects guaranteed deadlocks: a child's init blocks on a sibling that hasn't started yet.

Supervision-aware filtering removes proven-safe findings:

  • Safe sibling: the callee starts earlier under the same supervisor, so it is already running when the caller's init/1 executes.
  • Safe cross-supervisor: the caller and callee are under disjoint supervisor trees, so the callee was started by a different supervisor and is already running.

Requires the OTP and Supervision extractors for sync_call, implements_behaviour, supervisor, and supervisor_child facts.

Output relations

  • sync_call_in_init(mod, callee_mod) — module whose init/1 sync-calls callee_mod (after filtering proven-safe cases).
  • init_deadlock_risk(sup, child, dep, child_pos, dep_pos) — child's init calls a later-starting sibling.
  • sup_call_in_init(mod, api, op, target, site) — init/1 reaches a supervisor management call (start_child, terminate_child, ...).
  • init_waits_on_blocking_server(mod, dep, handler, op_site) — a call from init/1 that the tree-order argument accepts, into a server whose handler itself blocks on a supervisor op or a GenServer.call.

Finding severities

  • sync_call_in_init:info. The target's liveness couldn't be proven either way; the call stalls startup whenever the target is slow or absent.
  • init_deadlock_risk:error. Supervisors start children in order and init/1 blocks that sequence, so an init that waits on a later-starting sibling is a deadlock by construction.
  • sup_call_in_init:info. Starting children from init/1 puts their inits on the startup path; whether one calls back is not known here.
  • init_waits_on_blocking_server:warning. The callee is running, but a handler of its blocks on something with no bound: a terminate_child/stop, or a GenServer.call with :infinity.