Tracing in Elixir/Erlang using dbg trips and tricks.
For context, I was trying to debug/understand a new codebase and wanted to do some tracing so that I could better understand the flow, so I started tracing these functions using the dbg
module from Erlang.
But one of the common cons of the dbg module is the documentation of the functions, and odd function names
iex(1)> :dbg.
c/3 c/4 cn/1
ctp/0 ctp/1 ctp/2
ctp/3 ctpe/1 ctpg/0
ctpg/1 ctpg/2 ctpg/3
ctpl/0 ctpl/1 ctpl/2
ctpl/3 deliver_and_flush/1 dhandler/2
dtp/0 dtp/1 erlang_trace/3
flush_trace_port/0 flush_trace_port/1 fun2ms/1
get_info/0 get_tracer/0 get_tracer/1
h/0 h/1 i/0
ln/0 ltp/0 match_0_9/1
match_front/2 match_rear/2 n/1
p/1 p/2 rtp/1
start/0 stop/0 stop_clear/0
stop_trace_client/1 tp/2 tp/3
tp/4 tpe/2 tpl/2
tpl/3 tpl/4 trace_client/2
trace_client/3 trace_port/2 trace_port_control/1
trace_port_control/2 tracer/0 tracer/2
tracer/3 transform_flags/1 wrap_postsort/1
wrap_presort/2 wrap_sort/2 wrap_sortfix/2
wtp/1
And without having the documentation in one hand, it would be impossible to remember all these function names and their arguments and options. I always wondered whether there would be a simpler way to do this. Of-course there are thin elixir wrappers around it which provide a much more friendly DSL, but it would mean adding another dependency.
There are also other libraries like recon
, redbug
, xprof
etc, but all has the same caveats.
So after searching for a while, I came across this stack overflow post where it provides a simple Erlang wrapper module with a more friendly function names.
Source code available here:
That was exactly the thing that I was looking for:
iex(5)> :dbg_tracer.help
** shell internal commands **
b() -- display all variable bindings
e(N) -- repeat the expression in query <N>
f() -- forget all variable bindings
f(X) -- forget the binding of variable X
h() -- history
history(N) -- set how many previous commands to keep
results(N) -- set how many previous command results to keep
catch_exception(B) -- how exceptions are handled
v(N) -- use the value of query <N>
rd(R,D) -- define a record
rf() -- remove all record information
rf(R) -- remove record information about R
rl() -- display all record information
rl(R) -- display record information about R
rp(Term) -- display Term using the shell's record information
rr(File) -- read record information from File (wildcards allowed)
rr(F,R) -- read selected record information from file(s)
rr(F,R,O) -- read selected record information with options
** commands in module c **
bt(Pid) -- stack backtrace for a process
c(Mod) -- compile and load module or file <Mod>
cd(Dir) -- change working directory
flush() -- flush any messages sent to the shell
help() -- help info
i() -- information about the system
ni() -- information about the networked system
i(X,Y,Z) -- information about pid <X,Y,Z>
l(Module) -- load or reload module
lm() -- load all modified modules
lc([File]) -- compile a list of Erlang modules
ls() -- list files in the current directory
ls(Dir) -- list files in directory <Dir>
m() -- which modules are loaded
m(Mod) -- information about module <Mod>
mm() -- list all modified modules
memory() -- memory allocation information
memory(T) -- memory allocation information of type <T>
nc(File) -- compile and load code in <File> on all nodes
nl(Module) -- load module on all nodes
pid(X,Y,Z) -- convert X,Y,Z to a Pid
pwd() -- print working directory
q() -- quit - shorthand for init:stop()
regs() -- information about registered processes
nregs() -- information about all registered processes
uptime() -- print node uptime
xm(M) -- cross reference check a module
y(File) -- generate a Yecc parser
** commands in module i (interpreter interface) **
ih() -- print help for the i module
** user extended commands **
dbgtc(File) -- use dbg:trace_client() to read data from File
dbgon(M) -- enable dbg tracer on all funs in module M
dbgon(M,Fun) -- enable dbg tracer for module M and function F
dbgon(M,File) -- enable dbg tracer for module M and log to File
dbgadd(M) -- enable call tracer for module M
dbgadd(M,F) -- enable call tracer for function M:F
dbgdel(M) -- disable call tracer for module M
dbgdel(M,F) -- disable call tracer for function M:F
dbgoff() -- disable dbg tracer (calls dbg:stop/0)
l() -- load all changed modules
la() -- load all modules
mm() -- list modified modules
Pro tip: You can add it to your .iex.exs
file if you need to be using the module quite often and in multiple projects. Copy the code into dbg_tracer.erl
and in your home directory, and add the following to .iex.exs
so that it compiles the erlang module and make it available.
c("dbg_tracer.erl")
Hope that helps someone. Cheers.
References: