Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to understand compiled Elixir Erlang function names

Tags:

elixir

In ExProf, Elixir function names are printed out as what I assume is their Erlang names, after compilation. One example is

Enum.reduce/3

which is printed as

'Elixir.Enum':'-reduce/3-lists^foldl/2-0-'/3

How do I parse this string? Where does the -lists^foldl/2-0- part come from? Why are there multiple /3? Why are some names - prefixed? What does the ^ mean? Why the 2-0-?

like image 728
Filip Haglund Avatar asked Jul 21 '16 15:07

Filip Haglund


1 Answers

How do I parse this string?

'Elixir.Enum':'-reduce/3-lists^foldl/2-0-'/3 is the function reference syntax in Erlang referring to a function named -reduce/3-lists^foldl/2-0- in the module Elixir.Enum, with arity 3, similar to &Enum."-reduce/3-lists^foldl/2-0-"/3 in Elixir.

Where does the -lists^foldl/2-0- part come from?

The -$fn/$arity-$something-$count- is the name returned by Erlang in stacktraces (and apparently profiling output) for an anonymous function defined inside $fn/$arity. Normally, you would see something like -main/0-fun-0-, i.e. $something == "fun", for example, this:

defmodule Foo do
  def main do
    try do
      (fn -> raise("foo") end).()
    rescue
      _ -> IO.inspect System.stacktrace
    end
  end
end

prints:

[{Foo, :"-main/0-fun-0-", 0, [file: 'foo.ex', line: 4]},
 {Foo, :main, 0, [file: 'foo.ex', line: 4]},
 {:erl_eval, :do_apply, 6, [file: 'erl_eval.erl', line: 670]},
 {:elixir, :erl_eval, 3, [file: 'src/elixir.erl', line: 223]},
 {:elixir, :eval_forms, 4, [file: 'src/elixir.erl', line: 211]},
 {Code, :eval_string, 3, [file: 'lib/code.ex', line: 168]},
 {Kernel.CLI, :wrapper, 1, [file: 'lib/kernel/cli.ex', line: 437]},
 {Enum, :"-map/2-lists^map/1-0-", 2, [file: 'lib/enum.ex', line: 1184]}]

It's just that you're less likely to see -fun- ones in Elixir's default error messages because they're normalized, to anonymous fn/0 in Foo.main/0 in this case (this is why I printed the stacktrace above by calling System.stacktrace/0 explicitly).

So where does lists^foldl/2 come from? That's generated by sys_core_fold_lists, a module called by sys_core_fold for modules defining inline_list_funcs compile attribute (the Enum module in Elixir does do that), which "Inlines high order lists functions from the lists module". This inlining also gives the name "lists^foldl/2" to the anonymous function instead of just being "fun".

Here's a simple demo:

defmodule Fold do
  @compile :inline_list_funcs

  def main do
    sum([1, 2, 3])
  end

  def sum(list) do
    :lists.foldl(fn a, b -> raise "foo" end, 0, list)
  end
end

Fold.main

With @compile :inline_list_funcs, the output is:

** (RuntimeError) foo
    fold.exs:9: anonymous fn/2 in Fold.sum/1
    fold.exs:9: Fold."-sum/1-lists^foldl/2-0-"/3
    (elixir) lib/code.ex:363: Code.require_file/2

and without it, the output is:

** (RuntimeError) foo
    fold.exs:9: anonymous fn/2 in Fold.sum/1
    (stdlib) lists.erl:1263: :lists.foldl/3
    (elixir) lib/code.ex:363: Code.require_file/2

With that attribute, we don't have any stacktrace entry for the lists module, even though we're explicitly calling :lists:foldl.

Why are there multiple /3?

That seems to be a side effect of Erlang including the arity of the current function when naming an anonymous function.

Why are some names - prefixed?

Explained above.

What does the ^ mean?

It's just a name that sys_core_fold_lists:call/4 chose.

Why the 2-0-?

2 comes from sys_core_fold_lists:call/4. 0 is referred to as "count" in Exception.format_mfa/3 but I'm not sure what that means.

like image 200
Dogbert Avatar answered Oct 04 '22 19:10

Dogbert