From 7ec44f6a0b2a4482c2551ce000ff2a3c1094978a Mon Sep 17 00:00:00 2001 From: Chris Kyrouac Date: Mon, 10 Jun 2024 11:18:08 -0400 Subject: [PATCH] hack: Add remote lldb utilities to hack dir This is useful for remote debugging bootc running in a VM. Until podman-bootc has support for forwarding arbitrary ports, this custom solution is needed. Signed-off-by: Chris Kyrouac --- HACKING.md | 17 +++ hack/lldb/Containerfile | 18 +++ hack/lldb/dap-example-vim.lua | 122 ++++++++++++++++++ hack/lldb/deploy.sh | 21 +++ hack/lldb/etc/sudoers.d/wheel-nopasswd | 2 + hack/lldb/etc/sysctl.conf | 2 + .../etc/systemd/system/lldb-server.service | 13 ++ 7 files changed, 195 insertions(+) create mode 100644 hack/lldb/Containerfile create mode 100644 hack/lldb/dap-example-vim.lua create mode 100755 hack/lldb/deploy.sh create mode 100644 hack/lldb/etc/sudoers.d/wheel-nopasswd create mode 100644 hack/lldb/etc/sysctl.conf create mode 100644 hack/lldb/etc/systemd/system/lldb-server.service diff --git a/HACKING.md b/HACKING.md index 1c53d96fd..d1590d57b 100644 --- a/HACKING.md +++ b/HACKING.md @@ -66,6 +66,23 @@ with the target container host) is to just run a webserver on the host, e.g. run `bootc usroverlay` once, and `curl -L -o /usr/bin/bootc http://10.0.1.2:8080/target/release/bootc && restorecon /usr/bin/bootc`. +### Debugging via lldb + +The `hack/lldb` directory contains an example of how to use lldb to debug bootc code. +`hack/lldb/deploy.sh` can be used to build and deploy a bootc VM in libvirt with an lldb-server +running as a systemd service. Depending on your editor, you can then connect to the lldb server +to use an interactive debugger, and set up the editor to build and push the new binary to the VM. +`hack/lldb/dap-example-vim.lua` is an example for neovim. + +The VM can be connected to via `ssh test@bootc-lldb` if you have [nss](https://libvirt.org/nss.html) +enabled. + +For some bootc install commands, it's simpler to run the lldb-server in a container, e.g. + +```bash +sudo podman run --pid=host --network=host --privileged --security-opt label=type:unconfined_t -v /var/lib/containers:/var/lib/containers -v /dev:/dev -v .:/output localhost/bootc-lldb lldb-server platform --listen "*:1234" --server +``` + ## Running the tests First, you can run many unit tests with `cargo test`. diff --git a/hack/lldb/Containerfile b/hack/lldb/Containerfile new file mode 100644 index 000000000..8d5415103 --- /dev/null +++ b/hack/lldb/Containerfile @@ -0,0 +1,18 @@ +FROM quay.io/centos-bootc/centos-bootc-dev:stream9 + +COPY ./etc/sysctl.conf /etc/sysctl.conf +COPY ./etc/systemd/system/lldb-server.service /etc/systemd/system/lldb-server.service +COPY ./etc/sudoers.d/wheel-nopasswd /etc/sudoers.d +ARG sshpubkey + +RUN dnf -y install lldb firewalld vim && \ + firewall-offline-cmd --add-port 1025-65535/tcp && \ + systemctl enable lldb-server.service && \ + + # add test user + if test -z "$sshpubkey"; then echo "must provide sshpubkey"; exit 1; fi; \ + useradd -G wheel test && \ + mkdir -m 0700 -p /home/test/.ssh && \ + echo $sshpubkey > /home/test/.ssh/authorized_keys && \ + chmod 0600 /home/test/.ssh/authorized_keys && \ + chown -R test: /home/test diff --git a/hack/lldb/dap-example-vim.lua b/hack/lldb/dap-example-vim.lua new file mode 100644 index 000000000..6bd95e833 --- /dev/null +++ b/hack/lldb/dap-example-vim.lua @@ -0,0 +1,122 @@ +-- This is an example of how to configure the DAP connection in an editor (neovim in this case) +-- It should be relatively straightforward to adapt to a different editor + +local dap = require("dap") +local job = require("plenary.job") + +-- This is a coroutine that runs the cargo build command and reports progress +local program = function() + return coroutine.create(function(dap_run_co) + local progress = require("fidget.progress") + + local cargo_build_fidget = progress.handle.create({ + title = "cargo build", + lsp_client = { name = "Debugger" }, + percentage = 0, + }) + + local cargo_build_job = job:new({ + command = "cargo", + args = { "build", "--color=never", "--profile=dev" }, + cwd = vim.fn.getcwd(), + enable_handlers = true, + on_stderr = vim.schedule_wrap(function(_, output) + cargo_build_fidget:report({ + message = output, + percentage = cargo_build_fidget.percentage + 0.3, + }) + end), + on_exit = function(_, return_val) + vim.schedule(function() + if return_val ~= 0 then + cargo_build_fidget:report({ + message = "Error during cargo build", + percentage = 100, + }) + else + cargo_build_fidget:finish() + coroutine.resume(dap_run_co, vim.fn.getcwd() .. "/target/debug/bootc") + end + end) + end, + }) + + cargo_build_job:start() + end) +end + +dap.adapters = { + lldb = { + executable = { + args = { + "--liblldb", + "~/.local/share/nvim/mason/packages/codelldb/extension/lldb/lib/liblldb.so", + "--port", + "${port}", + }, + command = "~/.local/share/nvim/mason/packages/codelldb/extension/adapter/codelldb", + }, + host = "127.0.0.1", + port = "${port}", + type = "server", + }, +} + +-- rust config that runs cargo build before opening dap ui and starting Debugger +-- shows cargo build status as fidget progress +-- the newly built bootc binary is copied to the VM and run in the lldb-server +dap.configurations.rust = { + { + args = { "status" }, + cwd = "/", + name = "[remote] status", + program = program, + request = "launch", + console = "integratedTerminal", + stopOnEntry = false, + type = "lldb", + initCommands = { + "platform select remote-linux", + "platform connect connect://bootc-lldb:1234", -- connect to the lldb-server running in the VM + "file target/debug/bootc", + }, + }, + { + args = { "upgrade" }, + cwd = "/", + name = "[remote] upgrade", + program = program, + request = "launch", + console = "integratedTerminal", + stopOnEntry = false, + type = "lldb", + initCommands = { + "platform select remote-linux", + "platform connect connect://bootc-lldb:1234", + "file target/debug/bootc", + }, + }, + + -- The install command can connect to a container instead of a VM. + -- The following command is an example of how to run a container and start a lldb-server: + -- sudo podman run --pid=host --network=host --privileged --security-opt label=type:unconfined_t -v /var/lib/containers:/var/lib/containers -v /dev:/dev -v .:/output localhost/bootc-lldb lldb-server platform --listen "*:1234" --server + { + args = { "install", "to-disk", "--generic-image", "--via-loopback", "--skip-fetch-check", "~/.cache/bootc-dev/disks/test.raw" }, + cwd = "/", + env = { + ["RUST_LOG"] = "debug", + ["BOOTC_DIRECT_IO"] = "on", + }, + name = "[remote] install to-disk", + program = program, + request = "launch", + console = "integratedTerminal", + stopOnEntry = false, + type = "lldb", + initCommands = { + "platform select remote-linux", + "platform connect connect://127.0.0.1:1234", -- connect to the lldb-server running in the container + "file target/debug/bootc", + }, + }, +} diff --git a/hack/lldb/deploy.sh b/hack/lldb/deploy.sh new file mode 100755 index 000000000..442ce9aa6 --- /dev/null +++ b/hack/lldb/deploy.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# connect to the VM using https://libvirt.org/nss.html + +set -e + +# build the container image +sudo podman build --build-arg "sshpubkey=$(cat ~/.ssh/id_rsa.pub)" -f Containerfile -t localhost/bootc-lldb . + +# build the disk image +mkdir -p ~/.cache/bootc-dev/disks +rm -f ~/.cache/bootc-dev/disks/lldb.raw +truncate -s 10G ~/.cache/bootc-dev/disks/lldb.raw +sudo podman run --pid=host --network=host --privileged --security-opt label=type:unconfined_t -v /var/lib/containers:/var/lib/containers -v ~/.cache/bootc-dev/disks:/output -v /dev:/dev localhost/bootc-lldb bootc install to-disk --via-loopback --generic-image --skip-fetch-check /output/lldb.raw + +# create a new VM in libvirt +set +e +virsh -c qemu:///system destroy bootc-lldb +virsh -c qemu:///system undefine --nvram bootc-lldb +set -e +sudo virt-install --name bootc-lldb --cpu host --vcpus 8 --memory 8192 --import --disk ~/.cache/bootc-dev/disks/lldb.raw --os-variant rhel9-unknown diff --git a/hack/lldb/etc/sudoers.d/wheel-nopasswd b/hack/lldb/etc/sudoers.d/wheel-nopasswd new file mode 100644 index 000000000..bc5308c61 --- /dev/null +++ b/hack/lldb/etc/sudoers.d/wheel-nopasswd @@ -0,0 +1,2 @@ +# Enable passwordless sudo for the wheel group +%wheel ALL=(ALL) NOPASSWD: ALL diff --git a/hack/lldb/etc/sysctl.conf b/hack/lldb/etc/sysctl.conf new file mode 100644 index 000000000..5baef18b5 --- /dev/null +++ b/hack/lldb/etc/sysctl.conf @@ -0,0 +1,2 @@ +net.ipv6.conf.all.disable_ipv6 = 1 +net.ipv6.conf.default.disable_ipv6 = 1 diff --git a/hack/lldb/etc/systemd/system/lldb-server.service b/hack/lldb/etc/systemd/system/lldb-server.service new file mode 100644 index 000000000..fe10a90d8 --- /dev/null +++ b/hack/lldb/etc/systemd/system/lldb-server.service @@ -0,0 +1,13 @@ +[Unit] +Description=LLDB Server +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/root +ExecStart=lldb-server platform --listen "*:1234" --server --min-gdbserver-port 31200 --max-gdbserver-port 31202 +Restart=on-failure + +[Install] +WantedBy=multi-user.target