MobDev.OtpAudit (mob_dev v0.3.37)

Copy Markdown View Source

Reachability analysis for the bundled OTP runtime tree of a Mob app.

Walks every .beam file under an OTP root, extracts the imports chunk to learn who calls whom, and computes the transitive closure starting from the app's entry-point modules. Anything not reachable is a strip candidate — modules, whole libs, or duplicate library versions.

Used by mix mob.audit_otp (read-only report) and the planned mix mob.release --slim flag (auto-strip based on the report).

Entry points

Reachability seeding is deliberately generous:

  • App's start/2 callback module (read from each .app file's mod key)
  • All exported functions of kernel and stdlib (BEAM startup needs them)
  • elixir, logger, eex (runtime support that gets called via macros)

Anything reachable from those is kept; the rest is candidate-for-strip.

Output

Returns a map with:

  • :libs — every lib found, with reachable/total module counts and KB
  • :duplicates — libs that appear multiple times (only newest is kept)
  • :foreign_apps — non-OTP, non-app code in the lib dir (other projects)
  • :strippable_libs — libs with zero reachable modules
  • :total_kb / :reachable_kb / :strippable_kb — size summary

All sizes are post-strip — i.e. what mix mob.release actually ships, not raw OTP source.

Summary

Functions

Run the audit. otp_root is the directory containing lib/ and erts-*/ (typically the runtime tree extracted into the app bundle, or the cache the release packaging copies from).

Types

lib_name()

@type lib_name() :: String.t()

lib_report()

@type lib_report() :: %{
  name: lib_name(),
  version: String.t() | nil,
  path: String.t(),
  modules_total: non_neg_integer(),
  modules_reachable: non_neg_integer(),
  kb_total: non_neg_integer(),
  kb_reachable: non_neg_integer(),
  unreachable_modules: [module_atom()],
  is_app_under_test?: boolean()
}

module_atom()

@type module_atom() :: atom()

report()

@type report() :: %{
  otp_root: String.t(),
  app_name: String.t() | nil,
  libs: [lib_report()],
  duplicates: %{required(lib_name()) => [String.t()]},
  foreign_apps: [String.t()],
  strippable_libs: [lib_name()],
  total_kb: non_neg_integer(),
  reachable_kb: non_neg_integer(),
  strippable_kb: non_neg_integer()
}

Functions

audit(otp_root, opts \\ [])

@spec audit(
  String.t(),
  keyword()
) :: report()

Run the audit. otp_root is the directory containing lib/ and erts-*/ (typically the runtime tree extracted into the app bundle, or the cache the release packaging copies from).

:app_name (option) — the application's atom name (e.g. :air_cart_max). Used to seed reachability from the app's modules. If omitted, every lib with no mod callback is treated as a potential entry point (broader, finds less to strip).