Debug(3pm)

๐ŸŽ€ Coro::Debug โ€” the debugger bestie ๐Ÿ’…

Manual page: Debug(3pm) โ€” and yes, she's from the User Contributed Perl Documentation section, we love a community queen. ๐Ÿ‘‘


โœจ NAME โœจ

Coro::Debug โ€” a whole lil toolbox of functions that help you debug your Coro programs. fr fr, debugging but make it cute. ๐Ÿฆ‹


๐ŸŒธ SYNOPSIS ๐ŸŒธ

use Coro::Debug;

our $server = new_unix_server Coro::Debug "/tmp/socketpath";

$ socat readline unix:/tmp/socketpath

okay so that's literally the whole vibe. โœจ


๐Ÿ’– DESCRIPTION ๐Ÿ’–

Heads up bestie: this module is an AnyEvent user, so you need to make sure you use and run a supported event loop. No loop, no vibes, that's just how it is. ๐Ÿ˜ญ

This module gives you some debugging facilities. And likeโ€ฆ most of them will, if you don't handle them carefully, severely compromise the security of your program ๐Ÿ’€ so use it only for debugging, or take other precautions. Not me telling you to lock the door before you spill the tea. ๐Ÿ”’

It mainly implements a very primitive debugger that is very easy to integrate into your program:

our $server = new_unix_server Coro::Debug "/tmp/somepath";
# see new_unix_server, below, for more info

๐Ÿ‘ฏโ™€๏ธ it lets you list running coroutines

like a little roll call for your coroutines, iconic honestly:

             state (rUnning, Ready, New or neither)
             |cctx allocated
             ||  resident set size (octets)
             ||  |   scheduled this many times
    > ps     ||  |   |
         PID SC  RSS USES Description              Where
    14572344 UC  62k 128k [main::]                 [dm-support.ext:47]
    14620056 -- 2260   13 [coro manager]           [Coro.pm:358]
    14620128 -- 2260  166 [unblock_sub scheduler]  [Coro.pm:358]
    17764008 N-  152    0 [EV idle process]        -
    13990784 -- 2596  10k timeslot manager         [cf.pm:454]
    81424176 --  18k 4758 [async pool idle]        [Coro.pm:257]
    23513336 -- 2624    1 follow handler           [follow.ext:52]
    40548312 --  15k 5597 player scheduler         [player-scheduler.ext:13]
    29138032 -- 2548  431 music scheduler          [player-env.ext:77]
    43449808 -- 2260 3493 worldmap updater         [item-worldmap.ext:115]
    33352488 --  19k 2845 [async pool idle]        [Coro.pm:257]
    81530072 --  13k  43k map scheduler            [map-scheduler.ext:65]
    30751144 --  15k 2204 [async pool idle]        [Coro.pm:257]

๐Ÿ”Ž backtraces on (about) any coroutine

she'll spill the whole call stack, no cap:

> bt 18334288
coroutine is at /opt/cf/ext/player-env.ext line 77
        eval {...} called at /opt/cf/ext/player-env.ext line 77
        ext::player_env::__ANON__ called at -e line 0
        Coro::_run_coro called at -e line 0

๐Ÿงฎ eval perl code

yes, just type math at it like it owes you money:

> 5+7
12

๐Ÿ•ต๏ธโ™€๏ธ eval perl code inside other coroutines

sneaky AND useful, we stan:

> eval 18334288 caller(1); $DB::args[0]->method
1

๐Ÿ“ tracing subroutine entry/exits

It can also trace subroutine entry/exits for most coroutines โ€” meaning those not having recursed into a C function โ€” and the output looks a lil something like this:

> loglevel 5
> trace 94652688
2007-09-27Z20:30:25.1368 (5) [94652688] enter Socket::sockaddr_in with (8481,\x{7f}\x{00}\x{00}\x{01})
2007-09-27Z20:30:25.1369 (5) [94652688] leave Socket::sockaddr_in returning (\x{02}\x{00}...)
2007-09-27Z20:30:25.1370 (5) [94652688] enter Net::FCP::Util::touc with (client_get)
2007-09-27Z20:30:25.1371 (5) [94652688] leave Net::FCP::Util::touc returning (ClientGet)
2007-09-27Z20:30:25.1372 (5) [94652688] enter AnyEvent::Impl::Event::io with (AnyEvent,fh,GLOB(0x9256250),poll,w,cb,CODE(0x8c963a0))
2007-09-27Z20:30:25.1373 (5) [94652688] enter Event::Watcher::__ANON__ with (Event,poll,w,fd,GLOB(0x9256250),cb,CODE(0x8c963a0))
2007-09-27Z20:30:25.1374 (5) [94652688] enter Event::io::new with (Event::io,poll,w,fd,GLOB(0x9256250),cb,CODE(0x8c963a0))
2007-09-27Z20:30:25.1375 (5) [94652688] enter Event::Watcher::init with (Event::io=HASH(0x8bfb120),HASH(0x9b7940))

๐Ÿ“ฌ the log facility

If your program uses the Coro::Debug::log facility:

Coro::Debug::log 0, "important message";
Coro::Debug::log 9, "unimportant message";

then you can even receive log messages in any debugging session. hello?? that's the dream ๐Ÿซถ

> loglevel 5
2007-09-26Z02:22:46 (9) unimportant message

Other commands are available in the shell โ€” just use the "help" command for a list. go explore, bestie. ๐Ÿงš


๐Ÿ”ฅ FUNCTIONS ๐Ÿ”ฅ

None of the functions are being exported. not a single one. she's private like that. ๐Ÿ’…
  • log $level, $msg โ€” Log a debug message of the given severity level to all interested parties. Reminder: 0 is the highest severity, and higher numbers are less important. ๐Ÿ“ข
  • stderr_loglevel $level โ€” Set the loglevel for logging to stderr. It defaults to the value of the environment variable PERL_CORO_STDERR_LOGLEVEL, or -1 if that's missing. ๐Ÿฃ
  • session_loglevel $level โ€” Set the default loglevel for new coro debug sessions. Defaults to the value of the environment variable PERL_CORO_DEFAULT_LOGLEVEL, or -1 if that's missing. ๐Ÿ†•
  • trace $coro, $loglevel โ€” Enables tracing the given coroutine at the given loglevel. If loglevel is omitted, use 5. If coro is omitted, trace the current coroutine. โš ๏ธ Tracing incurs a very high runtime overhead. like, she's expensive. ๐Ÿ’ธ

It is not uncommon to enable tracing on oneself by simply calling "Coro::Debug::trace". self-care but it's profiling yourself, honestly iconic.

A message will be logged at the given loglevel if it is not possible to enable tracing. ๐Ÿ˜ญ

  • untrace $coro โ€” Disables tracing on the given coroutine. bye bestie ๐Ÿ‘‹
  • command $string โ€” Execute a debugger command, sending any output to STDOUT. Used by "session", below. ๐Ÿ“ค
  • session $fh โ€” Run an interactive debugger session on the given filehandle. Each line entered is simply passed to "command" (with a few exceptions). ๐ŸŽฎ
  • $server = new_unix_server Coro::Debug $path โ€” Creates a new unix domain socket that listens for connection requests and runs "session" on any connection. Normal unix permission checks and umask apply, so you can protect your socket by putting it into a protected directory. ๐Ÿ”

The "socat" utility is an excellent way to connect to this socket:

socat readline /path/to/socket

Socat also offers history support, which is honestly a serve:

socat readline:history=/tmp/hist.corodebug /path/to/socket

The server accepts connections until it is destroyed, so you must keep the return value around as long as you want the server to stay available. don't let her get garbage collected, she'll ghost you. ๐Ÿ’€

  • $server = new_tcp_server Coro::Debug $port โ€” Similar to "new_unix_server", but binds on a TCP port. Note that this usually results in a gaping security hole. ๐Ÿšจ like, genuinely, be careful.

Currently, only a TCPv4 socket is created; in the future, a TCPv6 socket might also be created. ๐Ÿ”ฎ


๐Ÿ’Œ AUTHOR/SUPPORT/CONTACT ๐Ÿ’Œ

Marc A. Lehmann <schmorp@schmorp.de>
http://software.schmorp.de/pkg/Coro.html

and that's the whole page bestie โ€” this one's from perl v5.40.1, dated 2025-03-21, and it lives at Debug(3pm). ty for coming to my ted talk, now go debug something. ๐Ÿ’–โœจ๐ŸŒธ๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from