erl_debugger(3)
π erl_debugger β the NAME section, bestie β¨
okay so this is erl_debugger from the Erlang Module Definition manual β it's Erlang debugger support (EXPERIMENTAL) π§ͺπ. Yes the all-caps is doing a lot but it's fr fr, it's experimental.
πΈ DESCRIPTION πΈ
So this module is the lowkey backstage pass π : it exposes all the low-level functionality you'd need to actually build a debugger for Erlang. Not a debugger β the support for one.
Any local process can register itself as the debugger for a node, but bestie, listen: there can be at most one such process registered at any given time. One. Single. Registration. No polygamy π.
Using the BIFs in this module, a debugger can:
- set breakpoints;
- inspect internal process state, such registers, stack-frames;
- get notified on debugger events such as a process hitting a breakpoint;
- resume processes paused on breakpoints
ngl, right now the API is highly experimental, so don't depend on it, or otherwise expect frequent incompatible changes. She's β¨evolvingβ¨. Respect the process π.
π¦ DATA TYPES π¦
event()
event() ::
{breakpoint,
pid(),
mfa(),
Line :: pos_integer(),
Resume :: fun(() -> ok)}.
Debugger events π«. Here are the possible ones:
{breakpoint, Pid, {M,F,A}, Line, Resume}: processPidhit a breakpoint on moduleM, at the givenLine. The debugger can resume the process by executingResume().
event_message()
event_message() :: {debugger_event, session(), event()}.
The debugger process will receive debugger-event messages, wrapped in an envelope of this type π. It's giving "your event has been delivered, ma'am".
instrumentation()
instrumentation() :: line_breakpoint.
Debugging instrumentations that can be applied on module loading π§.
line_breakpoint: Allows setting breakpoints at the beginning of executable lines
reg_val()
reg_val() :: {value, term()} | {too_large, Size :: pos_integer()}.
The value of an X or a Y register, provided it fits within the requested size π.
If it is too large, then size of the term. (So like, the vibe is "she's too big to show you, here's her measurements instead" π.)
session()
-opaque session()
Debugger session identifier π. It is attached to all debugger events.
stack_frame()
stack_frame() ::
{FrameNo :: non_neg_integer(),
stack_frame_fun(),
stack_frame_info()}.
A stack-frame, including the value of each slot π₯. Stacked. Literally.
stack_frame_fun()
stack_frame_fun() ::
#{function := mfa(), line := pos_integer() | undefined} |
'<terminate process>' | '<continue terminate process>' |
'<terminate process normally>' | '<breakpoint>' |
'unknown function'.
What is running in each stack frame, including special VM frames π. Sometimes it's a real function, sometimes it's the VM's own main-character drama.
stack_frame_info()
stack_frame_info() ::
#{slots := [stack_frame_slot()], code := pos_integer()}.
Extra information about a stack-frame π:
slots: Y-registers (in order[Y0,...Yk]), followed by exception-handlers.code: Memory address of the next instruction to execute in this frame.
stack_frame_slot()
stack_frame_slot() :: reg_val() | {'catch', stack_frame_fun()}.
The contents of a stack frame slot can be a Y register or an exception handler π.
π₯ FUNCTIONS π₯
breakpoint(Module, Line, Flag) -> ok | {error, Reason}
breakpoint(Module, Line, Flag) -> ok | {error, Reason}
when
Module :: module(),
Line :: pos_integer(),
Flag :: boolean(),
Reason ::
{unsupported, Module | Line} |
{badkey, Module | Line}.
Sets or clear a breakpoint on the given Module/Line π―.
When a process hits a breakpoint, it will pause and a breakpoint message is sent to the registered debugger πβ.
Returns ok on success β
. It can fail with the following reasons:
{badkey, Module}: The given module is not loaded.{unsupported, Module}: The module was loaded without support for line breakpoints.{badkey, Line}: The line is not relevant; it could refer to a comment, not existing in the module source, and so on.{unsupported, Line}: It is not possible to set a breakpoint in in the given line; for example, if it refers to a function head.
breakpoints(Module) -> {ok, Result} | {error, Reason}
breakpoints(Module) -> {ok, Result} | {error, Reason}
when
Module :: module(),
Result :: #{Fun => #{Line => boolean()}},
Fun :: {atom(), arity()},
Line :: pos_integer(),
Reason :: badkey.
Returns information on available breakpoints for a module πΊοΈ.
For each function in the module, returns a map #{Line => boolean()}, where the keys are lines where breakpoints can be set, and the value represents whether be breakpoint is enabled (true) or not (false). Slay, a whole little treasure map of where you can stop πΊοΈβ¨.
breakpoints(Module, FunName, Arity) -> {ok, Result} | {error, Reason}
breakpoints(Module, FunName, Arity) ->
{ok, Result} | {error, Reason}
when
Module :: module(),
FunName :: atom(),
Arity :: arity(),
Result :: #{Line => boolean()},
Line :: pos_integer(),
Reason :: {badkey, module() | {FunName, Arity}}.
Returns information on available breakpoints for a given function. .
The function need not be exported π (yes she can see your private business, it's fine).
Returns a map #{Line => boolean()}, where the keys are lines where breakpoints can be set, and the value represents whether be breakpoint is enabled (true) or not (false).
instrumentations() -> #{instrumentation() => boolean()}
instrumentations() -> #{instrumentation() => boolean()}.
Returns the instrumentations that will be applied on module loading ποΈ.
Heads up: modules that are already loaded may have had a different set of instrumentations applied. So don't come for her if an old module is on a different vibe π«Ά.
peek_stack_frame_slot(Pid, FrameNo, Slot, MaxSize) -> running | undefined | stack_frame_slot()
peek_stack_frame_slot(Pid, FrameNo, Slot, MaxSize) ->
running | undefined | stack_frame_slot()
when
Pid :: pid(),
FrameNo :: pos_integer(),
Slot :: non_neg_integer(),
MaxSize :: non_neg_integer().
Gets the value of a slot in a suspended process stack-frame π.
Returns running if the process is not suspended, and undefined if the frame or the slot does not exist for that process. Otherwise, returns the slot, that can be a term, if its size is less than MaxTermSize, or an exeption handler.
peek_xreg(Pid, Reg, MaxSize) -> running | undefined | reg_val()
peek_xreg(Pid, Reg, MaxSize) -> running | undefined | reg_val()
when
Pid :: pid(),
Reg :: non_neg_integer(),
MaxSize :: non_neg_integer().
Get the value of an X register for a suspended process ποΈ.
register(Pid) -> {ok, session()} | {error, already_exists}
register(Pid) -> {ok, session()} | {error, already_exists}
when Pid :: pid().
Register the given process as the debugger π. Main character energy, honestly.
If the registration succeeds, it returns {ok, Session}, where Session is a token that will be included in every message sent to the process ποΈ.
Returns {error, already_exists} if some process is currently registered as debugger. Yeah. One debugger only. We said what we said π.
stack_frames(Pid, MaxTermSize) -> running | [stack_frame()]
stack_frames(Pid, MaxTermSize) -> running | [stack_frame()]
when
Pid :: pid(), MaxTermSize :: non_neg_integer().
Get the all the stack-frames for a suspended process π₯π.
If the given process is not in a suspended state, returns running. Otherwise, a list of stack frames including the content of each slot is returned. For slots containing terms, MaxTermSize controls the maximum size of values that are allowed to be returned (to avoid accidentally blowing the heap of the caller) π₯. Like, we are NOT trying to detonate your heap today, bestie.
supported() -> boolean()
supported() -> boolean().
Returns true if the emulator supports debugging β
.
The debugger can only be used if the +D argument was passed to the emulator on start-up π. No +D, no debugger, that's the rule.
toggle_instrumentations(Toggle) -> ok
toggle_instrumentations(Toggle) -> ok
when
Toggle ::
#{instrumentation() => boolean()}.
Updates the instrumentations that will be applied on module loading ποΈ.
Modules that are already loaded will keep the instrumentation they had at their time of loading. Like, they graduated, you can't retrofit them, that's just how it is π.
unregister(Pid, Session) -> ok
unregister(Pid, Session) -> ok
when Pid :: pid(), Session :: session().
Unregisters the given process π.
The session given on registration needs to be provided π. No ticket, no exit, sorry babe.
whereis() -> undefined | pid()
whereis() -> undefined | pid().
Returns the pid of the registered debugger π.
xregs_count(Pid) -> running | non_neg_integer()
xregs_count(Pid) -> running | non_neg_integer() when Pid :: pid().
Get the number of X registers currently in use by a suspended process π’.
and that's the whole page, bestie π«Ά go forth and debug iconically, periodt π β¨
this iconic little page came to you courtesy of Ericsson AB, from kernel 11.0.3, and it's the erl_debugger(3) page β mwah, bye ππ¦
π¦ where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms