Tracing in Erlang with dbg
View SourceThe dbg
module in Erlang provides a text-based interface for tracing function calls, processes, ports, and messages. It simplifies the use of the underlying trace:process/4
, trace:port/4
, and trace:function/4
BIFs (Built-In Functions). This guide will walk you through the basics of using dbg
for your Erlang applications.
This facility is useful for both quick debugging sessions in the shell and for more structured system testing, especially where other tools might have too much performance impact.
Quick Start
To trace a call to a function with minimal fuss, call dbg:c(Module, Name, Arguments)
. It starts a temporary trace
receiver, enables all trace flags, and calls the designated function
from a temporary process. For example, here is how to trace a call
to application:which_applications/0
:
1> dbg:c(application, which_applications, []).
(<0.92.0>) <0.45.0> ! {'$gen_call',{<0.92.0>,
[alias|
#Ref<0.0.11779.270031856.1478295555.230456>]},
which_applications} (Timestamp: {1710,
847802,
479222})
(<0.92.0>) out {gen,do_call,4} (Timestamp: {1710,847802,479231})
(<0.92.0>) in {gen,do_call,4} (Timestamp: {1710,847802,479271})
(<0.92.0>) << {[alias|#Ref<0.0.11779.270031856.1478295555.230456>],
[{stdlib,"ERTS CXC 138 10","5.2.1"},
{kernel,"ERTS CXC 138 10","9.2.2"}]} (Timestamp: {1710,
847802,
479274})
[{stdlib,"ERTS CXC 138 10","5.2.1"},
{kernel,"ERTS CXC 138 10","9.2.2"}]
In this example, four trace events are generated:
- A send event (
!
) for the sending of a request from the current process to theapplication_controller
process. - A schedule-out event (
out
) when the current process schedules out while waiting in areceive
for the reply to arrive. - A schedule-in event (
in
) when the current process is scheduled in when reply has arrived. - A
receive
event (<<
) when the current process retrieves the reply from theapplication_controller
process.
The dbg:c/4
function has a fourth argument for specifying the trace flags,
(see flags).
How-to Trace Systems
For more control, another way of tracing is to explicitly start a
tracer and set the trace flags of your choice on the processes you want to
trace. This is useful, when there is a complex system of processes, ports or nodes
interacting where dbg:c/3
is to blunt.
Starting a Tracer (dbg:tracer/0,2
)
First, you need to start a tracer process that will receive and display trace messages.
1> dbg:tracer(). % Start the default trace message receiver
{ok,<0.90.0>} % <0.90.0> is the PID of the tracer process
This starts a server on the local node that will be the recipient of all trace messages. It uses a default handler that prints formatted trace messages to the Erlang shell.
If you need a custom tracer other than the default, you can create a tracer using
dbg:tracer(Type, Data)
:
Type = process
:Data
is{HandlerFun, InitialState}
.HandlerFun
is afun/2
that takes the trace message and the previous state, returning a new state.Type = port
:Data
is afun/0
that returns a new trace port (e.g., created bydbg:trace_port/2
).Type = module
:Data
is{TracerModule, TracerState}
or afun/0
returning this, for use witherl_tracer
.Type = file
:Data
is a filename where traces will be printed.
Note that only one tracer using this method can be started at a time. You can use trace sessions to start multiple tracers (see trace sessions).
Tracing Processes and Ports (dbg:p/1,2
)
Once the tracer is started, you can tell dbg
which processes or ports to trace
and what events to trace for them using dbg:p(Item, Flags)
.
Item
can be:
pid/0
orport/0
- The corresponding process or port is traced. The process or port can be a remote process or port (on another Erlang node). The node must be in the list of traced nodes (seedbg:n/1
anddbg:tracer/3
).all
- All processes and ports in the system as well as all processes and ports created hereafter are to be traced.processes
- All processes in the system as well as all processes created hereafter are to be traced.ports
- All ports in the system as well as all ports created hereafter are to be traced.new
- All processes and ports created after the call are to be traced.new_processes
- All processes created after the call are to be traced.new_ports
- All ports created after the call are to be traced.existing
- All existing processes and ports are traced.existing_processes
- All existing processes are traced.existing_ports
- All existing ports are traced.atom/0
- The process or port with the corresponding registered name is traced. The process or port can on another Erlang node. The node must be in the list of traced nodes (seedbg:n/1
anddbg:tracer/3
).integer/0
- The process<0.Item.0>
is traced.{X, Y, Z}
- The process<X.Y.Z>
is traced.string/0
- If theItem
is a string "<X.Y.Z>" as returned frompid_to_list/1
, the process<X.Y.Z>
is traced.
Flags
can be a single atom or a list of flags. The available flags are:
s (send)
- Traces the messages the process or port sends.r (receive)
- Traces the messages the process or port receives.m (messages)
- Traces the messages the process or port receives and sends.c (call)
- Traces global function calls for the process according to the trace patterns set in the system (seedbg:tp/2
).p (procs)
- Traces process related events to the process.ports
- Traces port related events to the port.sos (set on spawn)
- Lets all processes created by the traced process inherit the trace flags of the traced process.sol (set on link)
- Lets another process,P2
, inherit the trace flags of the traced process whenever the traced process links toP2
.sofs (set on first spawn)
- This is the same assos
, but only for the first process spawned by the traced process.sofl (set on first link)
- This is the same assol
, but only for the first call tolink/1
by the traced process.all
- Sets all flags exceptsilent
.clear
- Clears all flags.Other flags accepted by
trace:process/4
ortrace:port/4
(e.g.,timestamp
,arity
,return_to
).
dbg:p(Item)
is a shorthand for dbg:p(Item, [m])
.
This function returns either an error tuple or an {ok, List}
tuple. The List
consists of specifications of how many processes and ports that matched (in the
case of a single pid exactly 1). The specification of matched processes is
{matched, Node, N}
. If the remote processor call (using rpc
) to a remote
node fails, the rpc
error message is returned as the fourth element in the
tuple and the number of matched processes is 0.
Example: Trace messages and process events for a specific process
1> Pid = spawn(fun() -> receive {From,Msg} -> From ! Msg end end).
<0.90.0>
2> dbg:tracer().
{ok,<0.92.0>}
3> dbg:p(Pid, [m,procs]). % Trace messages and process events for Pid
{ok,[{matched,nonode@nohost,1}]}
4> Pid ! {self(),hello}.
(<0.90.0>) << {<0.88.0>,hello} % Received by Pid
{<0.88.0>,hello}
(<0.90.0>) <0.88.0> ! hello % Sent by Pid
(<0.90.0>) exit normal % Process event: Pid exited
5> flush().
Shell got hello
ok
Tracing Function Calls (dbg:tp/2,3,4
, dbg:tpl/2,3,4
)
To trace function calls, you need to:
- Enable the
c
/call
flag for the process(es) that will make the calls (usingdbg:p/2
). - Set a trace pattern for the function(s) you want to trace using
dbg:tp/2
ordbg:tpl/2
.
tp
stands for trace pattern (for exported functions by default).
tpl
stands for trace pattern local (for local or remote calls to
local and exported functions).
The general syntax is dbg:tp(ModuleOrMFA, MatchSpec)
or
dbg:tpl(ModuleOrMFA, MatchSpec)
.
ModuleOrMFA
can be:
Module :: atom()
: Equivalent to{Module, '_', '_'}
(trace all functions in the module).{Module, Function, Arity}
: Trace specific function.'_'
can be used as a wildcard.
Note that if the Module
is specified as '_'
, the
Function
and Arity
parts must be specified as '_'
as well. The
same holds for the Function
in relation to Arity
.
MatchSpec
defines what to trace and how, see
match specifications.
- For simple call tracing, you can insert the empty list
[]
. - The most common generic match specifications used can be found as built-in
aliases.
x
orexception_trace
: Shows function names, parameters, return values, and exceptions.[{'_',[],[{exception_trace}]}]
c
orcaller_trace
: Shows function names, parameters, and caller information.[{'_',[],[{message,{caller_line}}]}]
cx
orcaller_exception_trace
: Combinesx
andc
.[{'_',[],[{exception_trace},{message,{caller_line}}]}]
Example using built-in aliases:
1> dbg:tracer().
{ok,<0.90.0>}
2> dbg:p(all, c). % Short for dbg:p(all, call)
{ok,[{matched,nonode@nohost,49}]}
3> dbg:tp(lists, seq, cx). % cx: call and exception tracing with caller info
{ok,[{matched,nonode@nohost,2},{saved,cx}]}
4> lists:seq(1, 3).
(<0.88.0>) call lists:seq(1,3) ({erl_eval,do_apply,7,{"erl_eval.erl",904}})
[1,2,3]
(<0.88.0>) returned from lists:seq/2 -> [1,2,3]
Note that the caller info is the function that called lists:seq with file and line number.
Tracing Message Events (dbg:tpe/2
)
By default, if send
or receive
tracing is enabled for a process, all such
events are traced. dbg:tpe(Event, MatchSpec)
allows you to
filter these events.
Event
:send
or'receive'
.MatchSpec
: A match specifications.- For
send
: Matches on[Receiver, Msg]
. - For
'receive'
: Matches on[Node, Sender, Msg]
.
- For
Managing Trace Patterns
You can display, remove, save and load trace pattern matchspecifications, if any of these bullets are of interest, click the title to read the documentation.
dbg:ltp()
(List Trace Patterns): Lists all saved match specifications (from previoustp
calls where a pattern was complex enough to be saved) and built-in aliases.dbg:dtp()
(Delete Trace Patterns): Deletes all saved (not built-in) match specifications.dbg:dtp(N)
(Delete Trace PatternN
): Deletes a specific saved pattern by its IDN
.dbg:wtp(FileName)
(Write Trace Patterns): Saves current (saved and built-in) match specifications toFileName
.dbg:rtp(FileName)
(Read Trace Patterns): Reads match specifications fromFileName
and merges them.
To stop tracing specific functions, you clear their trace patterns.
dbg:ctp(ModuleOrMFA)
: Clears both global and local trace patterns.ctp()
: Clears all trace patterns for all functions.ctp(Module)
: Clears patterns for all functions inModule
.ctp(Module, Function)
/ctp(Module, Function, Arity)
: More specific.
dbg:ctpl(ModuleOrMFA)
: Clears only local trace patterns (set bytpl
).dbg:ctpg(ModuleOrMFA)
: Clears only global trace patterns (set bytp
).dbg:ctpe(Event)
: Clears the match specification forsend
or'receive'
, reverting to tracing all such events if the flag is set.
Match Specifications
Match Specifications are a powerful mini-language used to define conditions for
tracing and actions to take. The dbg:tp/2
, dbg:tpl/2
and dbg:tpe/2
functions
accept them. For a description of the format for the MatchSpec
argument, see
Match Specifications in Erlang, which explains the
general match specification language. For most users, dbg:fun2ms/1
explained
below will do.
A match specification is a list of tuples: [{MatchHead, Guard, BodyActions}]
.
MatchHead
: Patterns to match function arguments.'_'
matches anything.'$1'
is a variable.Guard
: Conditions that must be true.BodyActions
: Actions like{return_trace}
(trace return value),{message, term()}
(include extra info),{set_seq_token, ...}
.
Creating Match Specifications with dbg:fun2ms/1
You can use dbg:fun2ms/1
to translate a literal Erlang fun into a match
specification. This often feels more natural than writing the raw match spec.
The fun must take a single list argument (matching the function arguments) and
its body can use guard expressions and special tracing functions.
The parse transform module ms_transform
must be enabled. The easiest way to
enable it is by adding the following line to the source file:
-include_lib("stdlib/include/ms_transform.hrl").
In the shell its already enabled.
The head of the fun must be a single pattern that matches a list. That pattern will be used to match the arguments for the call:
1> dbg:fun2ms(fun([_,_]) -> true end). % Matches a function with two arguments
[{['_','_'],[],[true]}]
2> dbg:fun2ms(fun([A]) when is_atom(A) -> return_trace() end).
[{['$1'],[{is_atom,'$1'}],[{return_trace}]}]
The first match specification matches when a function having two arguments is called. The second matches when a function, taking one atom as an argument, is called.
Trace Sessions
To avoid interference between different tracing activities, you can create
isolated dbg
sessions.
First you create a session with dbg:session_create(Name)
where the name is an atom, a session/0
is returned.
Several sessions may have the same name.
When you have the session/0
, you use dbg:session(Session, Fun)
.
This function runs dbg
commands within Fun
using the specified session.
Any dbg
function that is called with in the provided fun
will use the session/0
provided instead of the default
dbg
session. This means that the tracing will be isolated
from other tracing users on the system.
When you no longer need the session, use dbg:session_destroy(Session)
.
Example:
1> S = dbg:session_create(my_session).
<0.91.0>
2> dbg:session(S, fun() -> dbg:tracer(), dbg:p(all,c), dbg:tp(lists,seq,x) end).
{ok,[{matched,nonode@nohost,2},{saved,x}]}
3> lists:seq(1, 10).
(<0.89.0>) call lists:seq(1,10)
(<0.89.0>) returned from lists:seq/2 -> [1,2,3,4,5,6,7,8,9,10]
[1,2,3,4,5,6,7,8,9,10]
4> dbg:session_destroy(S).
ok
The state of the session/0
is preserved in between dbg:session/2
calls, so
you can call dbg:session/2
multiple times when debugging you application.
Example:
1> S = dbg:session_create(my_session).
<0.91.0>
%% Setup the initial traces
2> dbg:session(S, fun() -> dbg:tracer(), dbg:p(self(),c), dbg:tp(lists,seq,x) end).
{ok,[{matched,nonode@nohost,2},{saved,x}]}
3> lists:seq(1, 3).
(<0.89.0>) call lists:seq(1,3)
(<0.89.0>) returned from lists:seq/2 -> [1,2,3]
[1,2,3]
%% Add an additional trace pattern
4> dbg:session(S, fun() -> dbg:tpl(lists,seq_loop,x) end).
ok
5> lists:seq(1, 3).
(<0.89.0>) call lists:seq(1,3)
(<0.89.0>) call lists:seq_loop(3,3,[])
(<0.89.0>) call lists:seq_loop(1,1,[2,3])
(<0.89.0>) returned from lists:seq_loop/3 -> [1,2,3]
(<0.89.0>) returned from lists:seq_loop/3 -> [1,2,3]
(<0.89.0>) returned from lists:seq/2 -> [1,2,3]
[1,2,3]
6> dbg:session_destroy(S).
ok
Trace on Remote Nodes
The dbg
server keeps a list of nodes where tracing should be
performed. Whenever a dbg:tp/2
call or a dbg:p/2
call is made, it is
executed for all nodes in this list including the local node (except
for dbg:p/2
with a specific pid/0
or port/0
as first argument,
in which case the command is executed only on the node where the
designated process or port resides).
dbg:n(Nodename)
: When this function is called, it starts a
tracer process on the remote node, which will send all trace messages to the
tracer process on the local node (via the Erlang distribution). If no tracer
process is running on the local node, the error reason no_local_tracer
is
returned. The tracer process on the local node must be started with
the dbg:tracer/0,2
function.
If Nodename
is the local node, the error reason cant_add_local_node
is
returned.
The function will also return an error if the node Nodename
is not reachable.
If a trace port (see dbg:trace_port/2
) is running on the local node, remote nodes
cannot be traced with a tracer process. The error reason
cant_trace_remote_pid_to_local_port
is returned. However, a trace port can be
started on the remote node with the dbg:tracer/3
function.
dbg:tracer(Nodename, Type, Data)
: An independent tracer
is started on the node (Nodename
) and the node is added to the list of traced nodes.
Note
dbg:tracer(Nodename, Type, Data)
is not equivalent to dbg:n/1
. While dbg:n/1
starts a process tracer
which redirects all trace information to a process tracer on the local node
(that is, the trace control node), dbg:tracer/3
starts any type of tracer,
independent of the type of tracer on the trace control node.
Managing nodes
dbg
can trace processes and functions on other Erlang nodes in a distributed system.
dbg:cn(Nodename)
(Clear Node): RemovesNodename
from the list. Tracing already active on that node continues but new globaltp/p
calls won't affect it.dbg:ln()
(List Nodes): Shows the list of currently traced nodes.
Trace Ports for Lower Overhead
For high-volume tracing, sending messages to an Erlang process can be too slow. A trace port is an Erlang port to a dynamically linked-in driver that handles trace messages directly, without the overhead of sending them as messages to an Erlang process. Using a trace port significantly lowers the overhead imposed by tracing.
Creating Trace Ports (dbg:trace_port/2
)
dbg:trace_port(Type, Parameters)
returns a fun/0
that,
when called, creates and returns a port. This fun is then passed as the second argument to
dbg:tracer(port, Fun)
.
Two trace drivers are available: the file
and the ip
trace drivers.
file
: Writes trace messages to binary file(s).Parameters
:Filename
or a wrap files specification:{Filename, wrap, Suffix}
{Filename, wrap, Suffix, WrapSize}
{Filename, wrap, Suffix, WrapSize, WrapCnt}
{Filename, wrap, Suffix, {time, WrapTime}, WrapCnt}
Wrap files limit disk space by rotating throughWrapCnt
files, each up toWrapSize
or open forWrapTime
.
ip
: Opens a TCP/IP listening port. A client connects to receive trace messages.Parameters
:PortNumber
or{PortNumber, QueSize}
.
The file
trace driver expects a filename or a wrap files
specification as parameter. A file is written with a high degree of
buffering, which is why there is no guarantee that all are saved in the
file in case of a system crash.
A wrap files specification is used to limit the disk space consumed by the
trace. The trace is written to a limited number of files each with a limited
size. The actual filenames are Filename ++ SeqCnt ++ Suffix
, where SeqCnt
counts as a decimal string from 0
to WrapCnt
and then around again from 0
.
When a trace term written to the current file makes it longer than WrapSize
,
that file is closed, and if the number of files in this wrap trace is as many as
WrapCnt
the oldest file is deleted, and a new file is opened to become the
current. Thus, when a wrap trace has been stopped, there are at most WrapCnt
trace files saved with a size of at least WrapSize
(but not much larger),
except for the last file that might even be empty. The default values are
WrapSize = 128*1024
and WrapCnt = 8
.
The SeqCnt
values in the filenames are all in the range 0
through WrapCnt
with a gap in the circular sequence. The gap is needed to find the end of the
trace.
If the WrapSize
is specified as {time, WrapTime}
, the current file is closed
when it has been open more than WrapTime
milliseconds, regardless of it being
empty or not.
The ip
trace driver has a queue of QueSize
messages waiting to be delivered.
If the driver cannot deliver messages as fast as they are produced by the
runtime system, a special message is sent, which indicates how many messages
that are dropped. That message will arrive at the handler function specified in
dbg:trace_client/3
as the tuple {drop, N}
where N
is the number of consecutive
messages dropped. In case of heavy tracing, drops are likely to occur, and they
surely occur if no client is reading the trace messages. The default value of
QueSize
is 200.
Reading Trace Port Data (dbg:trace_client/2,3
)
dbg:trace_client(Type, Parameters)
Starts a trace
client that reads the output
created by a trace port driver (see dbg:trace_port/2
) and handles it in mostly
the same way as a tracer process created by the dbg:tracer/0
function.
dbg:trace_client(Type, Parameters, HandlerSpec)
This
function works exactly as
dbg:trace_client/2
, but allows you to write your own handler function.
If Type
is file
, the client reads all trace messages stored in the
file named Filename
or specified by WrapFilesSpec
(must be the
same as used when creating the trace) and lets the default handler
function format the messages on the console. This is one way to
interpret the data stored in a file by the file trace port driver.
If Type
is follow_file
, the client behaves as in the file
case, but keeps
trying to read (and process) more data from the file until stopped by
dbg:stop_trace_client/1
. WrapFilesSpec
is not allowed as second argument for
this Type
.
If Type
is ip
, the client connects to the TCP/IP port PortNumber
on the
host Hostname
, from where it reads trace messages until the TCP/IP connection
is closed. If no Hostname
is specified, the local host is assumed.
The handler function works mostly as the one described in dbg:tracer/2
,
but must also be prepared to handle trace messages of the form {drop, N}
, where N
is the number of dropped messages. This pseudo trace
message will only occur if the ip
trace driver is used.
For trace type file
, the pseudo trace message end_of_trace
will appear at
the end of the trace. The return value from the handler function is in this case
ignored.
Example: Using an IP trace port and connecting to it from another node As an example, one can let trace messages be sent over the network to another Erlang node (preferably not distributed), where the formatting occurs.
On the node stack
there exists an Erlang node ant@stack
. In the
shell, type the following:
ant@stack> dbg:tracer(port, dbg:trace_port(ip, 4711)).
<0.17.0>
ant@stack> dbg:p(self(), send).
{ok,1}
All trace messages are now sent to the trace port driver, which in turn listens for connections on the TCP/IP port 4711. If we want to see the messages on another node, preferably on another host, we do like this:
1> dbg:trace_client(ip, {"stack", 4711}).
<0.42.0>
If we now send a message from the shell on the node ant@stack
, where all sends
from the shell are traced:
ant@stack> self() ! hello.
hello
The following will appear at the console on the node that started the trace client:
(<0.23.0>) <0.23.0> ! hello
(<0.23.0>) <0.22.0> ! {shell_rep,<0.23.0>,{value,hello,[],[]}}
The last line is generated due to internal message passing in the Erlang shell. The pids will vary.
Controlling Trace Ports
dbg:flush_trace_port()
/dbg:flush_trace_port(Node)
: Flushes internal buffers of the trace port driver on the local/specified node (currently forfile
driver).dbg:trace_port_control(Operation)
/dbg:trace_port_control(Node, Operation)
:Operation = flush
: Same as above.Operation = get_listen_port
: Forip
driver, returns{ok, IpPortNumber}
.
dbg:stop_trace_client(Pid)
: Shuts down the trace clientPid
.
Sequential Tracing
The dbg
module is primarily targeted towards tracing through the
trace:process/4
function. It is sometimes desired to trace messages in a more
delicate way, which can be done with the help of the seq_trace
module.
seq_trace
implements sequential tracing (known in the AXE10 world, and
sometimes called "forlopp tracing"). dbg
can interpret messages generated from
seq_trace
and the same tracer function for both types of tracing can be used.
The seq_trace
messages can also be sent to a trace port for further analysis.
As a match specification can turn on sequential tracing, the combination of
dbg
and seq_trace
can be powerful. This brief example shows a session
where sequential tracing is used to trace the dbg
module and the trace itself:
1> dbg:tracer().
{ok,<0.30.0>}
2> {ok, Tracer} = dbg:get_tracer().
{ok,<0.31.0>}
3> seq_trace:set_system_tracer(Tracer).
false
4> dbg:tp(dbg, get_tracer, 0, [{[],[],[{set_seq_token, send, true}]}]).
{ok,[{matched,nonode@nohost,1},{saved,1}]}
5> dbg:p(all,call).
{ok,[{matched,nonode@nohost,22}]}
6> dbg:get_tracer(), seq_trace:set_token([]).
(<0.25.0>) call dbg:get_tracer()
SeqTrace [0]: (<0.25.0>) <0.30.0> ! {<0.25.0>,get_tracer} [Serial: {2,4}]
SeqTrace [0]: (<0.30.0>) <0.25.0> ! {dbg,{ok,<0.31.0>}} [Serial: {4,5}]
{1,0,5,<0.30.0>,4}
This session sets the system_tracer to the same process as the
ordinary tracer process (i. e. <0.31.0>) and sets the trace pattern
for the function dbg:get_tracer
to one that has the action of
setting a sequential token. When the function is called by a traced
process (all processes are traced in this case), the process gets
"contaminated" by the token and seq_trace
messages are sent both for
the server request and the response. The seq_trace:set_token([])
after the call clears the seq_trace
token, which is why no messages
are sent when the answer propagates via the shell to the console
port. Otherwise the output would have been more noisy.
Avoiding Overloads
Tracing can generate a significant amount of data, potentially overwhelming your system if not managed carefully. To prevent performance degradation or even crashes, consider these strategies:
Time-Limited Tracing: One effective method is to automatically stop tracing after a set period.
dbg:tracer(), dbg:p(all,[c]), dbg:tpl(lists,map,x), timer:sleep(1000), dbg:stop().
Be Specific:
Processes: Instead of dbg:p(all, Flags).
, try to pinpoint specific
processes if you know which ones are relevant: dbg:p(Pid, Flags)
. You can also
trace newly spawned processes with dbg:p(new, Flags).
.
Modules & Functions: Rather than tracing all calls, narrow down to specific
modules and functions with dbg:tp/2
or dbg:tpl/2
Use Match Specifications: For example, to only trace calls to
my_module:my_function/1
when the first argument is the atom error:
dbg:tpl(my_module, my_function, dbg:fun2ms(fun([error])->true end)).
Limit Trace Flags: Only enable the flags essential for your debugging task (e.g., m for message passing, c for function calls).
Trace to File for High Volumes: If you anticipate a large volume of trace data, tracing directly to the console can become a bottleneck. Consider tracing to a file instead, see trace ports.
dbg:tracer(port, {file, "trace_output.log"}),
% ... your other dbg commands ...
timer:sleep(5000),
dbg:stop().
Avoiding Deadlocks
When tracing function calls on a group leader process (an I/O process), there is
risk of causing a deadlock. This will happen if a group leader process generates
a trace message and the tracer process, by calling the trace handler function,
sends an I/O request to the same group leader. The problem can only occur if the
trace handler prints to the tty using an io
function such as
format/2
. Note that when dbg:p(all, call)
is called, IO
processes are also traced. Here is an example:
%% Using a default line editing shell
1> dbg:tracer(process, {fun(Msg,_) -> io:format("~p~n", [Msg]), 0 end, 0}).
{ok,<0.37.0>}
2> dbg:p(all, [call]).
{ok,[{matched,nonode@nohost,25}]}
3> dbg:tp(mymod,[{'_',[],[]}]).
{ok,[{matched,nonode@nohost,0},{saved,1}]}
4> mymod: % TAB pressed here
%% -- Deadlock --
Here is another example:
%% Using a shell without line editing (oldshell)
1> dbg:tracer(process).
{ok,<0.31.0>}
2> dbg:p(all, [call]).
{ok,[{matched,nonode@nohost,25}]}
3> dbg:tp(lists,[{'_',[],[]}]).
{ok,[{matched,nonode@nohost,0},{saved,1}]}
% -- Deadlock --
The reason we get a deadlock in the first example is because when TAB is pressed
to expand the function name, the group leader (which handles character input)
calls mymod:module_info()
. This generates a trace message which, in turn,
causes the tracer process to send an IO request to the group leader (by calling
io:format/2
). We end up in a deadlock.
In the second example we use the default trace handler function. This
handler prints to the tty by sending IO requests to the user
process. When Erlang is started in the oldshell mode, the shell
process will have user
as its group leader and so will the tracer
process in this example. Since user
calls functions in lists
we
end up in a deadlock as soon as the first IO request is sent.
Here are a few suggestions for avoiding deadlock:
- Do not trace the group leader of the tracer process. If tracing has been
switched on for all processes, call
dbg:p(TracerGLPid, clear)
to stop tracing the group leader (TracerGLPid
).process_info(TracerPid, group_leader)
tells you which process this is (TracerPid
is returned fromdbg:get_tracer/0
). - Do not trace the
user
process if using the default trace handler function. - In your own trace handler function, call
erlang:display/1
instead of anio
function or, ifuser
is not used as group leader, print touser
instead of the default group leader. Example:io:format(user, Str, Args)
.
Getting Information and Help
dbg:i()
(Information): Displays information about all currently traced processes and ports and their active trace flags.dbg:h()
(Help): Lists available help items.dbg:h(Item :: atom())
: Gives brief help for a specificdbg
function or concept (e.g.,dbg:h(tp)
).dbg:get_tracer()
/dbg:get_tracer(Node)
: Returns the process, port, or tracer module handling traces on the local/specified node.- Consult the dbg module documentation.