Skip to content

Commit 6e52a86

Browse files
committed
feat(sandbox): add --gdb option to aumatically connect to GDB
This adds a sandbox option to setup GDB automatically and open it in a new tmux window. This makes it easier to use GDB with firecracker using common tooling. Signed-off-by: Riccardo Mancini <[email protected]>
1 parent 3e296e8 commit 6e52a86

File tree

2 files changed

+63
-16
lines changed

2 files changed

+63
-16
lines changed

tests/framework/microvm_helpers.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import platform
99
import subprocess
10+
import tempfile
1011
from pathlib import Path
1112

1213

@@ -245,3 +246,30 @@ def trace_cmd_guest(self, fns, cmd, port=4321):
245246
f"trace-cmd record -N {host_ip}:{port} -p function {' '.join(fns)} {cmd}"
246247
)
247248
return list(Path(".").glob("trace.*.dat"))
249+
250+
def tmux_gdb(self):
251+
"""Run GDB on a new tmux window"""
252+
chroot_gdb_socket = Path(self.vm.jailer.chroot_path(), self.vm.gdb_socket)
253+
254+
with tempfile.NamedTemporaryFile(
255+
mode="w", suffix=".gdb", delete=False, prefix="fc_gdb_"
256+
) as f:
257+
f.write(
258+
f"""
259+
target remote {chroot_gdb_socket}
260+
directory resources/linux
261+
hbreak start_kernel
262+
continue
263+
"""
264+
)
265+
gdb_script = f.name
266+
267+
self.tmux_neww(
268+
f"""
269+
until [ -S {chroot_gdb_socket} ]; do
270+
echo 'waiting for {chroot_gdb_socket}';
271+
sleep 1;
272+
done;
273+
gdb {self.vm.kernel_file} -x {gdb_script}
274+
"""
275+
)

tools/sandbox.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010

1111
import argparse
1212
import json
13+
import platform
1314
import re
1415
from pathlib import Path
1516

17+
import host_tools.cargo_build as build_tools
1618
from framework.artifacts import disks, kernels
17-
from framework.defs import DEFAULT_BINARY_DIR
19+
from framework.defs import DEFAULT_BINARY_DIR, LOCAL_BUILD_PATH
1820
from framework.microvm import MicroVMFactory
1921

2022
kernels = list(kernels("vmlinux-*"))
@@ -58,12 +60,31 @@ def parse_byte_size(param):
5860
parser.add_argument("--rootfs-size", type=parse_byte_size, default=1 * 2**30) # 1GB
5961
parser.add_argument("--binary-dir", help="Path to the firecracker binaries")
6062
parser.add_argument("--cpu-template-path", help="CPU template to use", type=Path)
63+
parser.add_argument(
64+
"--debug", action="store_true", default=False, help="Use debug kernel"
65+
)
66+
parser.add_argument(
67+
"--gdb", action="store_true", default=False, help="Connect to Firecracker guest GDB"
68+
)
6169
args = parser.parse_args()
6270
print(args)
6371

6472
binary_dir = None
6573
if args.binary_dir:
6674
binary_dir = Path(args.binary_dir).resolve()
75+
elif args.gdb:
76+
# Build Firecracker with GDB feature if needed
77+
print("Building Firecracker with GDB feature...")
78+
machine = platform.machine()
79+
target = f"{machine}-unknown-linux-musl"
80+
build_dir = LOCAL_BUILD_PATH / "gdb"
81+
build_tools.cargo(
82+
"build",
83+
f"--features gdb --target {target} --all",
84+
env={"CARGO_TARGET_DIR": build_dir},
85+
)
86+
print("Build complete!")
87+
binary_dir = build_dir / target / "debug"
6788
else:
6889
binary_dir = DEFAULT_BINARY_DIR
6990

@@ -72,28 +93,26 @@ def parse_byte_size(param):
7293
cpu_template = json.loads(args.cpu_template_path.read_text())
7394
vmfcty = MicroVMFactory(binary_dir)
7495

75-
print(f"uvm with kernel {args.kernel} ...")
76-
uvm = vmfcty.build(args.kernel, args.rootfs)
96+
if args.debug or args.gdb:
97+
kernel = args.kernel.parent / "debug" / args.kernel.name
98+
else:
99+
kernel = args.kernel
100+
101+
print(f"uvm with kernel {kernel} ...")
102+
uvm = vmfcty.build(kernel, args.rootfs)
77103
uvm.help.enable_console()
78104
uvm.help.resize_disk(uvm.rootfs_file, args.rootfs_size)
79-
uvm.spawn(log_show_level=True)
105+
uvm.spawn(log_show_level=True, validate_api=False)
80106
uvm.help.print_log()
81107
uvm.add_net_iface()
82108
uvm.basic_config(vcpu_count=args.vcpus, mem_size_mib=args.guest_mem_size // 2**20)
83109
if cpu_template is not None:
84110
uvm.api.cpu_config.put(**cpu_template)
85111
print(cpu_template)
112+
113+
if args.gdb:
114+
uvm.enable_gdb()
115+
uvm.help.tmux_gdb()
116+
86117
uvm.start()
87118
uvm.get_all_metrics()
88-
89-
kernel_dbg_dir = args.kernel.parent / "debug"
90-
kernel_dbg = kernel_dbg_dir / args.kernel.name
91-
print(f"uvm2 with kernel {kernel_dbg} ...")
92-
uvm2 = vmfcty.build(kernel_dbg, args.rootfs)
93-
uvm2.spawn()
94-
uvm2.add_net_iface()
95-
uvm2.basic_config(vcpu_count=args.vcpus, mem_size_mib=args.guest_mem_size // 2**20)
96-
uvm2.start()
97-
# trace-cmd needs this (DNS resolution?)
98-
uvm2.help.enable_ip_forwarding()
99-
files = uvm2.help.trace_cmd_guest(["-l", "read_msr"], cmd="sleep 5")

0 commit comments

Comments
 (0)