Well, a tiny bit of progress.
I wrote a bash script that accepts a service name and tries to drop into a “pretty close” environment:
#!/usr/bin/env bash
set -Eeuf -o pipefail
log() {
printf "%s\n" "$*" >&2
}
err() {
log "err: $*"
exit 1
}
usage() {
printf 'systemd-service-env.sh :: Enters an environment somewhat like that of a specified systemd service.
USAGE: systemd-service-env.sh SERVICE-NAME
'
}
main() {
if [[ "${EUID}" -ne 0 ]]; then
sudo "$0" "$@"
exit $?
fi
case "${1:-}" in
"")
usage
exit 1
;;
-h)
usage
exit 0
;;
*)
:
local service=${1}
;;
esac
if ! systemctl is-active --quiet "${service}"; then
err "service must be running"
fi
local pid=$(systemctl show --property MainPID --value "${service}")
local environment
mapfile -t -d $'\0' environment < /proc/"${pid}"/environ
systemd-analyze unit-shell "${service}" env -i "${environment[@]}" /run/current-system/sw/bin/bash --norc --noprofile
}
main "$@"
If I add debugpy to the dependencies for the package in question, then use this script to enter that environment, I’m able to attach the vscode debugger by searching through the systemd service file and following its ExecStart path to eventually find where debugpy is installed, setting up an SSH tunnel to my local machine, then using it like so:
# /nix/store/f2l9dha8rg6hiy0pcm55ifdr8i3l4c9q-python3.13-debugpy-1.8.19/bin/debugpy \
--listen 5678 \
--pid "$(
systemctl show -p MainPID --value music-assistant
)"
Without the source code locally it has extremely limited utility so far, but I can import music_assistant without an error, suggesting that it’s working.
Unfortunately in this environment it’s pretty difficult for me to enter a nix-shell, so I haven’t yet discovered a technique that works without modifying the source derivation (to add debugpy).
Trying to do so from outside of this environment fails with some complaints about libpthread:
$ nix-shell -p 'python3.withPackages (ps: [ ps.debugpy ])' --command 'sudo debugpy --listen 45678 --pid "$(systemctl show --property MainPID --value music-assistant)"'
0.00s - Debugger warning: It seems that frozen modules are being used, which may
0.00s - make the debugger miss breakpoints. Please pass -Xfrozen_modules=off
0.00s - to python to disable frozen modules.
0.00s - Note: Debugging will proceed. Set PYDEVD_DISABLE_FILE_VALIDATION=1 to disable this validation.
PYDEVD_GDB_SCAN_SHARED_LIBRARIES not set (scanning all libraries for needed symbols).
Running: /nix/store/935m0ckihjv7l9mp10v1dw2fxkmffv29-gdb-17.1/bin/gdb --nw --nh --nx --pid 1874850 --batch --eval-command='set scheduler-locking off' --eval-command='set architecture auto' --eval-command='call (void*)dlopen("/nix/store/92yy94jnp1646vx71ry1sr9gcrpqkba4-python3-3.13.11-env/lib/python3.13/site-packages/debugpy/_vendored/pydevd/pydevd_attach_to_process/attach_linux_amd64.so", 2)' --eval-command='sharedlibrary attach_linux_amd64' --eval-command='call (int)DoAttach(0, "import codecs;import json;import sys;decode = lambda s: codecs.utf_8_decode(bytearray(s))[0] if s is not None else None;script_dir = decode([47, 110, 105, 120, 47, 115, 116, 111, 114, 101, 47, 57, 50, 121, 121, 57, 52, 106, 110, 112, 49, 54, 52, 54, 118, 120, 55, 49, 114, 121, 49, 115, 114, 57, 103, 99, 114, 112, 113, 107, 98, 97, 52, 45, 112, 121, 116, 104, 111, 110, 51, 45, 51, 46, 49, 51, 46, 49, 49, 45, 101, 110, 118, 47, 108, 105, 98, 47, 112, 121, 116, 104, 111, 110, 51, 46, 49, 51, 47, 115, 105, 116, 101, 45, 112, 97, 99, 107, 97, 103, 101, 115, 47, 100, 101, 98, 117, 103, 112, 121, 47, 115, 101, 114, 118, 101, 114]);setup = json.loads(decode([123, 34, 109, 111, 100, 101, 34, 58, 32, 34, 108, 105, 115, 116, 101, 110, 34, 44, 32, 34, 97, 100, 100, 114, 101, 115, 115, 34, 58, 32, 91, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 32, 52, 53, 54, 55, 56, 93, 44, 32, 34, 119, 97, 105, 116, 95, 102, 111, 114, 95, 99, 108, 105, 101, 110, 116, 34, 58, 32, 102, 97, 108, 115, 101, 44, 32, 34, 108, 111, 103, 95, 116, 111, 34, 58, 32, 110, 117, 108, 108, 44, 32, 34, 97, 100, 97, 112, 116, 101, 114, 95, 97, 99, 99, 101, 115, 115, 95, 116, 111, 107, 101, 110, 34, 58, 32, 110, 117, 108, 108, 125]));sys.path.insert(0, script_dir);import attach_pid_injected;del sys.path[0];attach_pid_injected.attach(setup);", 0)'
[New LWP 1874878]
[New LWP 1874877]
[New LWP 1874874]
[New LWP 1874872]
[New LWP 1874871]
[New LWP 1874870]
[New LWP 1874869]
[New LWP 1874868]
[New LWP 1874867]
[New LWP 1874866]
[New LWP 1874865]
[New LWP 1874864]
[New LWP 1874863]
[New LWP 1874862]
[New LWP 1874861]
[New LWP 1874860]
[New LWP 1874859]
[New LWP 1874858]
[New LWP 1874857]
[New LWP 1874856]
[New LWP 1874855]
warning: Expected absolute pathname for libpthread in the inferior, but got target:/nix/store/j193mfi0f921y0kfs8vjc1znnr45ispv-glibc-2.40-66/lib/libc.so.6.
warning: Unable to find libthread_db matching inferior's thread library, thread debugging will not be available.
0x00007f00aba31639 in PySlice_Unpack () from target:/nix/store/qzc04a3npl70cyyy6flnnrb2ig3kayxm-python3-3.13.11/lib/libpython3.13.so.1.0
The target architecture is set to "auto" (currently "i386:x86-64").
$1 = (void *) 0x55f1907e31d0
$2 = 0
[Inferior 1 (process 1874850) detached]