Skip to content

Commit 3e296e8

Browse files
committed
test(gdb): add integ test to verify gdb works on x86
The integ test opens gdb on a subprocess which connects to Firecracker, sets a breakpoint and continues until boot is complete. If the VM boots correctly and the breakpoint gets hit, then the test will succeed. This test currently only works on x86 as GDB requires a vmlinux image (we use a uImage on ARM). Signed-off-by: Riccardo Mancini <[email protected]>
1 parent fa2627a commit 3e296e8

File tree

2 files changed

+79
-1
lines changed

2 files changed

+79
-1
lines changed

tests/framework/microvm.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ def __init__(
287287

288288
self.help = MicrovmHelpers(self)
289289

290+
self.gdb_socket = None
291+
290292
def __repr__(self):
291293
return f"<Microvm id={self.id}>"
292294

@@ -1198,6 +1200,11 @@ def wait_for_ssh_up(self):
11981200
# run commands. The actual connection retry loop happens in SSHConnection._init_connection
11991201
_ = self.ssh_iface(0)
12001202

1203+
def enable_gdb(self):
1204+
"""Enables GDB debugging"""
1205+
self.gdb_socket = "gdb.socket"
1206+
self.api.machine_config.patch(gdb_socket_path=self.gdb_socket)
1207+
12011208

12021209
class MicroVMFactory:
12031210
"""MicroVM factory"""

tests/integration_tests/build/test_gdb.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,86 @@
22
# SPDX-License-Identifier: Apache-2.0
33
"""A test that ensures that firecracker builds with GDB feature enabled at integration time."""
44

5+
import os
56
import platform
7+
import pytest
8+
import signal
9+
import subprocess
10+
import tempfile
11+
from pathlib import Path
612

713
import host_tools.cargo_build as host
14+
from framework.defs import LOCAL_BUILD_PATH
15+
from framework.microvm import MicroVMFactory
816

917
MACHINE = platform.machine()
1018
TARGET = "{}-unknown-linux-musl".format(MACHINE)
19+
BUILD_PATH = LOCAL_BUILD_PATH / "gdb"
20+
21+
22+
def build_gdb():
23+
"""Builds Firecracker with GDB feature enabled"""
24+
25+
host.cargo(
26+
"build",
27+
f"--features gdb --target {TARGET} --all",
28+
env={"CARGO_TARGET_DIR": BUILD_PATH},
29+
)
1130

1231

1332
def test_gdb_compiles():
1433
"""Checks that Firecracker compiles with GDB enabled"""
1534

16-
host.cargo("build", f"--features gdb --target {TARGET}")
35+
build_gdb()
36+
37+
@pytest.mark.skipif(
38+
platform.machine() != "x86_64", reason="GDB requires a vmlinux but we ship a uImage for ARM in our CI"
39+
)
40+
def test_gdb_connects(guest_kernel_linux_6_1, rootfs):
41+
"""Checks that GDB works in a FC VM"""
42+
43+
build_gdb()
44+
45+
vmfcty = MicroVMFactory(BUILD_PATH / TARGET / "debug")
46+
kernel_dbg = guest_kernel_linux_6_1.parent / "debug" / guest_kernel_linux_6_1.name
47+
uvm = vmfcty.build(kernel_dbg, rootfs)
48+
uvm.spawn(validate_api=False)
49+
uvm.add_net_iface()
50+
uvm.basic_config()
51+
uvm.enable_gdb()
52+
53+
chroot_gdb_socket = Path(uvm.jailer.chroot_path(), uvm.gdb_socket)
54+
55+
gdb_commands = f"""
56+
target remote {chroot_gdb_socket}
57+
hbreak start_kernel
58+
# continue to start_kernel
59+
continue
60+
# continue boot until interrupted
61+
continue
62+
"""
63+
64+
with tempfile.NamedTemporaryFile(
65+
mode="w", suffix=".gdb", delete=False, prefix="fc_gdb_"
66+
) as f:
67+
f.write(gdb_commands)
68+
gdb_script = f.name
69+
70+
gdb_proc = subprocess.Popen(f"""
71+
until [ -S {chroot_gdb_socket} ]; do
72+
echo 'waiting for {chroot_gdb_socket}';
73+
sleep 1;
74+
done;
75+
gdb {kernel_dbg} -batch -x {gdb_script}
76+
""",
77+
shell=True,
78+
stdout=subprocess.PIPE,
79+
stderr=subprocess.PIPE,
80+
text=True
81+
)
82+
uvm.start()
83+
os.kill(uvm.firecracker_pid, signal.SIGKILL)
84+
gdb_proc.terminate()
85+
uvm.mark_killed()
86+
stdout, stderr = gdb_proc.communicate(timeout=10)
87+
assert "hit Breakpoint 1, start_kernel" in stdout, f"Breakpoint wasn't hit:\nstdout:\n{stdout}\n\nstderr:\n{stderr}"

0 commit comments

Comments
 (0)