erl_debugger(3)

πŸ“¦
version 29.0.6 Β· 3 versions
, in ubuntu stonking

πŸ’– 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}: process Pid hit a breakpoint on module M, at the given Line. The debugger can resume the process by executing Resume().

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