LIBFEBUG.PY(3) Library Functions Manual LIBFEBUG.PY(3)

febug.SOCKET, febug.SIGNUM, febug.ControlledSocket(), febug.CONTROLLED_SOCKET, febug.debug_handler(), febug.WrapperUser-space debugfs ABI wrapper library for Python

import febug

febug.SOCKET = "/run/febug.sock"
febug.SIGNUM = signal.SIGUSR2

"FEBUG_DONT" in os.environ
os.environ["FEBUG_SOCKET"]

def febug.ControlledSocket(path = febug.SOCKET):
socket.socket|None febug.CONTROLLED_SOCKET = febug.ControlledSocket()

def febug.debug_handler(_, _)


class febug.Wrapper:      of:
Any
     def __init__(self, of, name, signal = febug.SIGNUM)
     def __enter__(self):
febug.Wrapper
     def __exit__(self, ...)

febug.FebugMessage     = struct.Struct("=QQB4079s")
febug.StopFebugMessage = struct.Struct("=Q")
febug.AttnFebugMessage = struct.Struct("=QQ")

Simplifies writing Python programs debuggable with febug(8) by presenting a high-level interface to febug-abi(5).

There are two environment variables that allow a user to customise its behaviour:

If set, don't try to connect to febug(8), so all library functions become no-ops.
If set, use its value instead of the built-in FEBUG_SOCKET to connect to febug(8).

Unless $FEBUG_DONT, the global febug.CONTROLLED_SOCKET automatically connects to febug(8) at $FEBUG_SOCKET or febug.SOCKET.

The program needs to install () (or a wrapper around it) as the signal handler for FEBUG_SIGNUM (and any other signals, if different ones are explicitly requested); if notifications are disabled (by requesting SIGKILL), some event loop that answers on febug.CONTROLLED_SOCKET must be in place. It's a no-op if febug.CONTROLLED_SOCKET is None.

All objects registered via febug.Wrapper are formatted as-if via (obj), and all others are rejected. The handler is a no-op if febug.CONTROLLED_SOCKET is None.

At any time, when the program wishes to expose a variable, it can construct and operate the context manager machinery of a febug.Wrapper, which will send a febug_message with the specified name and signal number (defaulting to febug.SIGNUM), and type equal to () and ID to the address of the Wrapper object. It's a no-op if febug::CONTROLLED_SOCKET is None.

When leaving the context manager context, febug.Wrapper will send a stop_febug_message. It's a no-op if febug.CONTROLLED_SOCKET is None.

When it wishes to stop being debugged, the program may close () and reset it to None.

The following program transforms a list(int) into a list(list(int)) of its factors, but waits a tenth of a second between checks for each factor; the list and the amount of checks can be inspected via a febug(8) mount:

#!/usr/bin/env python3
# SPDX-License-Identifier: 0BSD


import febug, signal, time

signal.signal(febug.SIGNUM, febug.debug_handler)

data = list(range(20))
with febug.Wrapper(0, "tests") as tests:
  with febug.Wrapper(data, "cool_data"):
    for i in range(len(data)):
      fact = []
      while data[i] > 1:
        for t in range(2, data[i] + 1):
          tests.of += 1
          time.sleep(0.1)
          if data[i] % t == 0:
            data[i] //= t
            fact.append(t)
      data[i] = fact
    time.sleep(2)

febug-abi(5) — the ABI wrapped by this library.
libfebug(3), libfebug++(3), and libfebug.rs(3) — equivalent C, C++, and Rust libraries.

To all who support further development, in particular:

febug tracker

febug mailing list: <~nabijaczleweli/febug@lists.sr.ht>, archived at https://lists.sr.ht/~nabijaczleweli/febug

December 12, 2023 febug 1.0.0-1-g3324bf81