Skip to content

Gdb support for mshv guests #327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/dep_rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ jobs:
run: just run-rust-examples-linux ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}}

- name: Run Rust Gdb tests - linux
if: runner.os == 'Linux' && matrix.hypervisor == 'kvm'
if: runner.os == 'Linux'
env:
CARGO_TERM_COLOR: always
RUST_LOG: debug
run: just test-rust-gdb-debugging ${{ matrix.config }}
run: just test-rust-gdb-debugging ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}}

### Benchmarks ###
- name: Install github-cli (Linux mariner)
Expand Down
6 changes: 3 additions & 3 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ test-rust-feature-compilation-fail target=default-target:
{{ if os() == "linux" { "! cargo check -p hyperlight-host --no-default-features 2> /dev/null"} else { "" } }}

# Test rust gdb debugging
test-rust-gdb-debugging target=default-target: (build-rust target)
{{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --example guest-debugging --features gdb
{{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --features gdb -- test_gdb
test-rust-gdb-debugging target=default-target features="": (build-rust target)
{{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --example guest-debugging {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }}
{{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} -- test_gdb

test target=default-target: (test-rust target)

Expand Down
4 changes: 4 additions & 0 deletions docs/debugging-hyperlight.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ cargo test --package hyperlight-host --test integration_test --features print_de
To dump the details of the memory configuration, the virtual processors register state and the contents of the VM memory set the feature `crashdump` and run a debug build. This will result in a dump file being created in the temporary directory. The name and location of the dump file will be printed to the console and logged as an error message.

There are no tools at this time to analyze the dump file, but it can be useful for debugging.

## Debugging guests

For more information on how to debug the Hyperlight guests check the following [link](./how-to-debug-a-hyperlight-guest.md).
14 changes: 7 additions & 7 deletions docs/how-to-debug-a-hyperlight-guest.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# How to debug a Hyperlight **KVM** guest using gdb
# How to debug a Hyperlight guest using gdb on Linux

Hyperlight supports gdb debugging of a **KVM** guest running inside a Hyperlight sandbox.
When Hyperlight is compiled with the `gdb` feature enabled, a Hyperlight KVM sandbox can be configured
Hyperlight supports gdb debugging of a **KVM** or **MSHV** guest running inside a Hyperlight sandbox on Linux.
When Hyperlight is compiled with the `gdb` feature enabled, a Hyperlight sandbox can be configured
to start listening for a gdb connection.

## Supported features

The Hyperlight `gdb` feature enables **KVM** guest debugging:
- an entry point breakpoint is automatically set for the guest to stop
The Hyperlight `gdb` feature enables **KVM** and **MSHV** guest debugging to:
- stop at an entry point breakpoint which is automatically set by Hyperlight
- add and remove HW breakpoints (maximum 4 set breakpoints at a time)
- add and remove SW breakpoints
- read and write registers
Expand All @@ -18,7 +18,7 @@ The Hyperlight `gdb` feature enables **KVM** guest debugging:
## Expected behavior

Below is a list describing some cases of expected behavior from a gdb debug
session of a guest binary running inside a KVM Hyperlight sandbox.
session of a guest binary running inside a Hyperlight sandbox on Linux.

- when the `gdb` feature is enabled and a SandboxConfiguration is provided a
debug port, the created sandbox will wait for a gdb client to connect on the
Expand Down Expand Up @@ -154,7 +154,7 @@ is sent over the communication channel to the hypervisor handler for the sandbox
to resolve.

Below is a sequence diagram that shows the interaction between the entities
involved in the gdb debugging of a Hyperlight guest running inside a KVM sandbox.
involved in the gdb debugging of a Hyperlight guest running inside a **KVM** or **MSHV** sandbox.

```
┌───────────────────────────────────────────────────────────────────────────────────────────────┐
Expand Down
2 changes: 1 addition & 1 deletion src/hyperlight_host/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ fn main() -> Result<()> {
// Essentially the kvm and mshv features are ignored on windows as long as you use #[cfg(kvm)] and not #[cfg(feature = "kvm")].
// You should never use #[cfg(feature = "kvm")] or #[cfg(feature = "mshv")] in the codebase.
cfg_aliases::cfg_aliases! {
gdb: { all(feature = "gdb", debug_assertions, feature = "kvm", target_os = "linux") },
gdb: { all(feature = "gdb", debug_assertions, any(feature = "kvm", feature = "mshv2", feature = "mshv3"), target_os = "linux") },
kvm: { all(feature = "kvm", target_os = "linux") },
mshv: { all(any(feature = "mshv2", feature = "mshv3"), target_os = "linux") },
// inprocess feature is aliased with debug_assertions to make it only available in debug-builds.
Expand Down
11 changes: 8 additions & 3 deletions src/hyperlight_host/examples/guest-debugging/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ mod tests {

set pagination off
set logging file {out_file_path}
set logging enabled on
set logging on

break hyperlight_main
commands
Expand All @@ -118,7 +118,7 @@ mod tests {

continue

set logging enabled off
set logging off
quit
"
)
Expand All @@ -134,12 +134,17 @@ mod tests {
write_cmds_file(&cmd_file_path, &out_file_path)
.expect("Failed to write gdb commands to file");

#[cfg(mshv3)]
let features = "gdb,mshv3";
#[cfg(not(mshv3))]
let features = "gdb";

let mut guest_child = Command::new("cargo")
.arg("run")
.arg("--example")
.arg("guest-debugging")
.arg("--features")
.arg("gdb")
.arg(features)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
Expand Down
18 changes: 8 additions & 10 deletions src/hyperlight_host/src/hypervisor/gdb/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ limitations under the License.

use gdbstub::common::Signal;
use gdbstub::conn::ConnectionExt;
use gdbstub::stub::run_blocking::{self, WaitForStopReasonError};
use gdbstub::stub::{BaseStopReason, DisconnectReason, GdbStub, SingleThreadStopReason};
use gdbstub::stub::{
run_blocking, BaseStopReason, DisconnectReason, GdbStub, SingleThreadStopReason,
};
use libc::{pthread_kill, SIGRTMIN};

use super::x86_64_target::HyperlightSandboxTarget;
use super::{DebugResponse, GdbTargetError, VcpuStopReason};

pub struct GdbBlockingEventLoop;
struct GdbBlockingEventLoop;

impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
Expand Down Expand Up @@ -57,13 +58,10 @@ impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
signal: Signal(SIGRTMIN() as u8),
},
VcpuStopReason::Unknown => {
log::warn!("Unknown stop reason - resuming execution");
log::warn!("Unknown stop reason received");

target
.resume_vcpu()
.map_err(WaitForStopReasonError::Target)?;

continue;
// Marking as a SwBreak so the gdb inspect where/why it stopped
BaseStopReason::SwBreak(())
}
};

Expand Down Expand Up @@ -115,7 +113,7 @@ impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
}
}

pub fn event_loop_thread(
pub(crate) fn event_loop_thread(
debugger: GdbStub<HyperlightSandboxTarget, Box<dyn ConnectionExt<Error = std::io::Error>>>,
target: &mut HyperlightSandboxTarget,
) {
Expand Down
Loading