gmile
Gathering runtime statistics about function calls across entire app
How does one approach gathering runtime statistics about function calls from modules that belong to a specific app?
In my app, using a combination of decorator and telemetry packages, I am able to collect statistics about function calls like this:
defmodule MyApp.FunctionTracing do
use Decorator.Define, span: 0
def span(body, context) do
quote do
metadata = %{
function: "#{unquote(context.module)}.#{unquote(context.name)}/#{unquote(length(context.args))}"
}
:telemetry.span([:my_app, :function_call], metadata, fn ->
{unquote(body), metadata}
end)
end
end
end
defmodule MyApp.MyModule do
use MyApp.FunctionTracing
@decorate span()
def create_session(args) do
# ...
end
end
This is then exposed via a family of telemetry packages in a form of GET /metrics endpoint for Prometheus collector to come and pick up.
I can do the above manually for functions that I suspect may not be called. Could someone think of a way to scale this approach? E.g. is there a way I could tell Elixir compiler: please, when compiling ALL modules that belong to my own app, do wrap them in such a way?
The end goal here is to highlight & eliminate dead from from a fairly large codebase. Having all functions instrumented this way, I’d deploy the code in staging or production, collect the statistics for a week or two, then use it to base decisions about code clean up.
Or is this too crazy of a thing to want for such a use case?
If it’s too crazy, what would be a good alternative?
I’ve heard one could go a long way with tools already available in Erlang VM / stdlib for tracing and such, - if so, could someone point me in the right direction? At this point all I’m interested in is just counting all function calls coming from modules that belong to my own app.
Ideally, after a week or two of “counting”, I could filter out all function calls with 0 calls, and begin cleaning up the code.
Marked As Solved
voltone
This may be relatively safe in production:
On application startup, call :erlang.trace_pattern({:_, :_, :_}, true, [:call_count]). If code loading is dynamic (i.e. you’re not running as part of a release), also call :erlang.trace_pattern(:on_load, true, [:call_count]).
Then whenever you want to gather call stats for all public functions that have been called at least once:
for {m, _} <- :code.all_loaded(),
{f, a} <- m.module_info(:functions),
{:call_count, c} = :erlang.trace_info({m, f, a}, :call_count),
c > 0,
do: {{m, f, a}, c} # or if you prefer: {"#{inspect(m)}.#{f}/#{a}", c}
If you want to include functions that were never called, just drop the c > 0, line.
Or, for your use-case of finding unused functions in a given application:
app = :my_app
for m <- Application.spec(app)[:modules],
{f, a} <- m.module_info(:functions),
{:call_count, c} = :erlang.trace_info({m, f, a}, :call_count),
c == 0,
do: {{m, f, a}, c} # or if you prefer: {"#{inspect(m)}.#{f}/#{a}", c}
In that case you can also selectively enable call count tracing for only the modules of your application, instead of for all modules in the system.
You can reset the counters with :erlang.trace_pattern({:_, :_, :_}, :restart, [:call_count]) or stop tracing with :erlang.trace_pattern({:_, :_, :_}, false, [:call_count]).
Also Liked
LostKobrakai
And this is a much more practical introduction: Debugging With Tracing in Elixir
For elixir I suggest Extrace to also get elixir syntax for traces.
al2o3cr
This article discusses the BEAM’s tracing machinery and some of the Elixir wrappers available.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








