I have printed all code loaded as follows, why {lager_default_tracer,[]}, where beam file?
(lager_test_1@macbook.local)10> io:format("~p",[code:all_loaded()]).
[{io,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/io.beam"},
{erl_distribution,"/usr/local/lib/erlang/lib/kernel-3.1/ebin/erl_distribution.beam"},
{edlin,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/edlin.beam"},
{beam_clean,"/usr/local/lib/erlang/lib/compiler-5.0.3/ebin/beam_clean.beam"},
{v3_core,"/usr/local/lib/erlang/lib/compiler-5.0.3/ebin/v3_core.beam"},
{erl_epmd,"/usr/local/lib/erlang/lib/kernel-3.1/ebin/erl_epmd.beam"},
{love_misc,"/usr/local/bin/lager_test/lib/hanoch-0.0.1.6/ebin/love_misc.beam"},
{zlib,preloaded},
{error_handler,"/usr/local/lib/erlang/lib/kernel-3.1/ebin/error_handler.beam"},
{io_lib,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/io_lib.beam"},
{lib,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/lib.beam"},
{mnesia,"/usr/local/lib/erlang/lib/mnesia-4.12.4/ebin/mnesia.beam"},
{lager_test_app,"/usr/local/bin/lager_test/lib/lager_test-0.0.1.0/ebin/lager_test_app.beam"},
{beam_jump,"/usr/local/lib/erlang/lib/compiler-5.0.3/ebin/beam_jump.beam"},
{v3_codegen,"/usr/local/lib/erlang/lib/compiler-5.0.3/ebin/v3_codegen.beam"},
{beam_flatten,"/usr/local/lib/erlang/lib/compiler-5.0.3/ebin/beam_flatten.beam"},
{mnesia_tm,"/usr/local/lib/erlang/lib/mnesia-4.12.4/ebin/mnesia_tm.beam"},
{prim_eval,preloaded},
{beam_bool,"/usr/local/lib/erlang/lib/compiler-5.0.3/ebin/beam_bool.beam"},
{error_logger_lager_h,"/usr/local/bin/lager_test/lib/lager-2.0.0/ebin/error_logger_lager_h.beam"},
{lager_msg,"/usr/local/bin/lager_test/lib/lager-2.0.0/ebin/lager_msg.beam"},
{mnesia_frag,"/usr/local/lib/erlang/lib/mnesia-4.12.4/ebin/mnesia_frag.beam"},
{filename,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/filename.beam"},
{lager_default_tracer,[]},
{lager_default_formatter,"/usr/local/bin/lager_test/lib/lager-2.0.0/ebin/lager_default_formatter.beam"},
{mnesia_locker,"/usr/local/lib/erlang/lib/mnesia-4.12.4/ebin/mnesia_locker.beam"},
{mnesia_recover,"/usr/local/lib/erlang/lib/mnesia-4.12.4/ebin/mnesia_recover.beam"},
{mnesia_dumper,"/usr/local/lib/erlang/lib/mnesia-4.12.4/ebin/mnesia_dumper.beam"},
{mnesia_kernel_sup,"/usr/local/lib/erlang/lib/mnesia-4.12.4/ebin/mnesia_kernel_sup.beam"},
{mnesia_sp,"/usr/local/lib/erlang/lib/mnesia-4.12.4/ebin/mnesia_sp.beam"},
{erts_internal,preloaded},
{unicode,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/unicode.beam"},
{lager_backend_throttle,"/usr/local/bin/lager_test/lib/lager-2.0.0/ebin/lager_backend_throttle.beam"},
{beam_type,"/usr/local/lib/erlang/lib/compiler-5.0.3/ebin/beam_type.beam"},
{orddict,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/orddict.beam"},
{gb_sets,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/gb_sets.beam"},
{sofs,"/usr/local/lib/erlang/lib/stdlib-2.3/ebin/sofs.beam"},
{inet_db,"/usr/local/lib/erlang/lib/kernel-3.1/ebin/inet_db.beam"},
{lager_test_a,"/usr/local/bin/lager_test/lib/lager_test-0.0.1.0/ebin/lager_test_a.beam"},
{inet,"/usr/local/lib/erlang/lib/kernel-3.1/ebin/inet.beam"},
When I call module_info(), it is as follows:
(lager_test_1@macbook.local)11> lager_default_tracer:module_info().
[{exports,[{table,1},
{handle,1},
{module_info,0},
{module_info,1},
{info,1}]},
{imports,[]},
{attributes,[{vsn,[203040246025344403396962742072895880482]}]},
{compile,[{options,[]},
{version,"5.0.3"},
{time,{2017,8,27,5,43,32}},
{source,"/private/tmp/lager_test-0.0.1.0"}]}]
When I call lager_default_tracer:table(111)
, it is as follows:
(lager_test_1@macbook.local)12> lager_default_tracer:table(aaa).
** exception error: bad argument
in function lager_default_tracer:table/1
The lager_default_tracer
module has no associated beam file because it's created at runtime. The lager
application uses goldrush
, which uses runtime code compilation and loading.
If you look through the goldrush
sources you'll see that it calls the standard compile:forms/2
for dynamic compilation. By tracing that call, we can reconstruct the source code for lager_default_tracer
using the steps below, which assume you've already cloned the lager
source repository and successfully compiled it. In some of the steps, the output of the Erlang shell command is too large to put here, so those parts are abbreviated using ellipses.
In your already-compiled cloned lager
repo directory, run rebar shell
to start an Erlang shell, which ensures all necessary directories are in the Erlang load path.
In the shell, turn on dbg
tracing and trace compile:forms/2
:
1> dbg:tracer(), dbg:p(all, call).
{ok,[{matched,nonode@nohost,34}]}
2> dbg:tpl(compile, forms, []).
{ok,[{matched,nonode@nohost,2}]}
Start the lager
application and its dependencies:
3> application:ensure_all_started(lager).
This will cause the dbg
trace to produce some lengthy output like this:
(<0.94.0>) call compile:forms([{attribute,0,module,lager_default_tracer},
...
{tuple,0,[{integer,0,2},{integer,0,1}]}]}]}]}],[nowarn_unused_vars])
{ok,[syntax_tools,compiler,goldrush,lager]}
08:29:21.478 [info] Application lager started on node nonode@nohost
Here, the output is abbreviated to show only the first line and last few lines. The final two lines are the results from application:ensure_all_started/1
. Everything above those lines is the abstract format for the lager_default_tracer
module.
Copy the traced output starting from [{attribute,0,module,lager_default_tracer}
in the first line of the output trace all the way to {tuple,0,[{integer,0,2},{integer,0,1}]}]}]}]}]
in the final line of output trace. Do not include the ending text [nowarn_unused_vars])
in what you copy.
Paste the copied data to assign it to a variable named M
in the Erlang shell:
4> M = [{attribute,0,module,lager_default_tracer},
...
{tuple,0,[{integer,0,2},{integer,0,1}]}]}]}]}].
Don't forget to add the final period (aka full stop) to complete the expression.
Turn off dbg
tracing:
5> dbg:stop_clear().
ok
Compile the abstract format:
6> {ok, _, B} = compile:forms(M, [no_unused_vars, debug_info]).
{ok,lager_default_tracer,
<<70,79,82,49,0,0,6,164,66,69,65,77,65,116,85,56,0,0,1,9,
0,0,0,23,20,108,...>>}
The variable B
now holds, as a binary, the compiled code for the lager_default_tracer
module.
Extract the abstract code chunk from the compiled binary stored in B
:
7> {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(B,[abstract_code]).
{ok,{lager_default_tracer,
[{abstract_code,
...
[{clause,0,[{var,0,'Event'}],[],[{call,...}]}]}]}}]}}
The variable AC
now holds the abstract code.
Use the abstract code to print reconstructed source code for the lager_default_tracer
module:
8> io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).
-module(lager_default_tracer).
-export([info/1, reset_counters/1, table/1, handle/1]).
...
handle_(Event) ->
gr_counter:update_counter(table(counters), filter,
{2, 1}).
ok
This final step and the one before it are taken from the beam_lib
documentation, under "Reconstructing Source Code".
Unsurprisingly, the reconstructed source code shows calls of other goldrush
functions, so you'll need access to the goldrush
sources if you want to try to understand the reconstructed code.