Skip to content

Commit 28cb140

Browse files
committed
[trace-host] handle a TraceBatch from the guest
- Parse the spans and events coming from the guest and create corresponding spans and events from the host that mimics a single call from host - Create a `TraceContext` that handles a call into a guest Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent 99d35a7 commit 28cb140

File tree

8 files changed

+509
-10
lines changed

8 files changed

+509
-10
lines changed

Cargo.lock

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/hyperlight_host/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ page_size = "0.6.0"
3535
termcolor = "1.2.0"
3636
bitflags = "2.9.4"
3737
log = "0.4.28"
38+
opentelemetry = { version = "0.30.0", optional = true }
3839
tracing = { version = "0.1.41", features = ["log"] }
3940
tracing-log = "0.2.0"
4041
tracing-core = "0.1.34"
42+
tracing-opentelemetry = { version = "0.31.0", optional = true }
4143
hyperlight-common = { workspace = true, default-features = true, features = [ "std" ] }
4244
hyperlight-guest-tracing = { workspace = true, default-features = true, optional = true }
4345
vmm-sys-util = "0.15.0"
@@ -97,7 +99,7 @@ tracing = "0.1.41"
9799
tracing-subscriber = {version = "0.3.20", features = ["std", "env-filter"]}
98100
tracing-opentelemetry = "0.31.0"
99101
opentelemetry = "0.30.0"
100-
opentelemetry-otlp = { version = "0.30.0", default-features = false, features = ["http-proto", "reqwest-blocking-client"] }
102+
opentelemetry-otlp = { version = "0.30.0", default-features = false, features = ["http-proto", "reqwest-blocking-client", "grpc-tonic"] }
101103
opentelemetry-semantic-conventions = "0.30"
102104
opentelemetry_sdk = { version = "0.30.0", features = ["rt-tokio"] }
103105
tokio = { version = "1.47.1", features = ["full"] }
@@ -131,7 +133,7 @@ executable_heap = []
131133
print_debug = []
132134
# Dumps the VM state to a file on unexpected errors or crashes. The path of the file will be printed on stdout and logged.
133135
crashdump = ["dep:chrono"]
134-
trace_guest = ["hyperlight-common/trace_guest", "hyperlight-guest-tracing/trace"]
136+
trace_guest = ["dep:opentelemetry", "dep:tracing-opentelemetry", "dep:hyperlight-guest-tracing", "hyperlight-common/trace_guest"]
135137
mem_profile = [ "trace_guest", "dep:framehop", "dep:fallible-iterator", "hyperlight-common/mem_profile" ]
136138
kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"]
137139
# This feature is deprecated in favor of mshv3

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ use mshv_bindings::{
5050
};
5151
use mshv_ioctls::{Mshv, VcpuFd, VmFd};
5252
use tracing::{Span, instrument};
53+
#[cfg(feature = "trace_guest")]
54+
use tracing_opentelemetry::OpenTelemetrySpanExt;
5355
#[cfg(crashdump)]
5456
use {super::crashdump, std::path::Path};
5557

@@ -320,7 +322,6 @@ pub(crate) struct HypervLinuxDriver {
320322
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
321323
#[cfg(crashdump)]
322324
rt_cfg: SandboxRuntimeConfig,
323-
#[allow(dead_code)]
324325
#[cfg(feature = "mem_profile")]
325326
trace_info: MemTraceInfo,
326327
}
@@ -733,7 +734,10 @@ impl Hypervisor for HypervLinuxDriver {
733734
}
734735

735736
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
736-
fn run(&mut self) -> Result<super::HyperlightExit> {
737+
fn run(
738+
&mut self,
739+
#[cfg(feature = "trace_guest")] tc: &mut crate::sandbox::trace::TraceContext,
740+
) -> Result<super::HyperlightExit> {
737741
const HALT_MESSAGE: hv_message_type = hv_message_type_HVMSG_X64_HALT;
738742
const IO_PORT_INTERCEPT_MESSAGE: hv_message_type =
739743
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
@@ -775,6 +779,9 @@ impl Hypervisor for HypervLinuxDriver {
775779
{
776780
Err(mshv_ioctls::MshvError::from(libc::EINTR))
777781
} else {
782+
#[cfg(feature = "trace_guest")]
783+
tc.setup_guest_trace(Span::current().context());
784+
778785
// Note: if a `InterruptHandle::kill()` called while this thread is **here**
779786
// Then the vcpu will run, but we will keep sending signals to this thread
780787
// to interrupt it until `running` is set to false. The `vcpu_fd::run()` call will
@@ -1140,6 +1147,17 @@ impl Hypervisor for HypervLinuxDriver {
11401147
Ok(X86_64Regs::from(self.vcpu_fd.get_regs()?))
11411148
}
11421149

1150+
#[cfg(feature = "trace_guest")]
1151+
fn handle_trace(&mut self, tc: &mut crate::sandbox::trace::TraceContext) -> Result<()> {
1152+
let regs = self.read_regs()?;
1153+
tc.handle_trace(
1154+
&regs,
1155+
self.mem_mgr.as_ref().ok_or_else(|| {
1156+
new_error!("Memory manager is not initialized before handling trace")
1157+
})?,
1158+
)
1159+
}
1160+
11431161
#[cfg(feature = "mem_profile")]
11441162
fn trace_info_mut(&mut self) -> &mut MemTraceInfo {
11451163
&mut self.trace_info

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use std::sync::{Arc, Mutex};
2222

2323
use log::LevelFilter;
2424
use tracing::{Span, instrument};
25+
#[cfg(feature = "trace_guest")]
26+
use tracing_opentelemetry::OpenTelemetrySpanExt;
2527
use windows::Win32::System::Hypervisor::{
2628
WHV_MEMORY_ACCESS_TYPE, WHV_PARTITION_HANDLE, WHV_REGISTER_VALUE, WHV_RUN_VP_EXIT_CONTEXT,
2729
WHV_RUN_VP_EXIT_REASON, WHV_X64_SEGMENT_REGISTER, WHV_X64_SEGMENT_REGISTER_0,
@@ -293,7 +295,6 @@ pub(crate) struct HypervWindowsDriver {
293295
#[cfg(crashdump)]
294296
rt_cfg: SandboxRuntimeConfig,
295297
#[cfg(feature = "mem_profile")]
296-
#[allow(dead_code)]
297298
trace_info: MemTraceInfo,
298299
}
299300
/* This does not automatically impl Send because the host
@@ -736,7 +737,10 @@ impl Hypervisor for HypervWindowsDriver {
736737
}
737738

738739
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
739-
fn run(&mut self) -> Result<super::HyperlightExit> {
740+
fn run(
741+
&mut self,
742+
#[cfg(feature = "trace_guest")] tc: &mut crate::sandbox::trace::TraceContext,
743+
) -> Result<super::HyperlightExit> {
740744
self.interrupt_handle.running.store(true, Ordering::Relaxed);
741745

742746
#[cfg(not(gdb))]
@@ -761,6 +765,9 @@ impl Hypervisor for HypervWindowsDriver {
761765
Reserved: Default::default(),
762766
}
763767
} else {
768+
#[cfg(feature = "trace_guest")]
769+
tc.setup_guest_trace(Span::current().context());
770+
764771
self.processor.run()?
765772
};
766773
self.interrupt_handle
@@ -1092,6 +1099,17 @@ impl Hypervisor for HypervWindowsDriver {
10921099
Ok(X86_64Regs::from(regs))
10931100
}
10941101

1102+
#[cfg(feature = "trace_guest")]
1103+
fn handle_trace(&mut self, tc: &mut crate::sandbox::trace::TraceContext) -> Result<()> {
1104+
let regs = self.read_regs()?;
1105+
tc.handle_trace(
1106+
&regs,
1107+
self.mem_mgr.as_ref().ok_or_else(|| {
1108+
new_error!("Memory manager is not initialized before handling trace")
1109+
})?,
1110+
)
1111+
}
1112+
10951113
#[cfg(feature = "mem_profile")]
10961114
fn trace_info_mut(&mut self) -> &mut MemTraceInfo {
10971115
&mut self.trace_info

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ use kvm_ioctls::Cap::UserMemory;
2424
use kvm_ioctls::{Kvm, VcpuExit, VcpuFd, VmFd};
2525
use log::LevelFilter;
2626
use tracing::{Span, instrument};
27+
#[cfg(feature = "trace_guest")]
28+
use tracing_opentelemetry::OpenTelemetrySpanExt;
2729
#[cfg(crashdump)]
2830
use {super::crashdump, std::path::Path};
2931

@@ -306,7 +308,6 @@ pub(crate) struct KVMDriver {
306308
#[cfg(crashdump)]
307309
rt_cfg: SandboxRuntimeConfig,
308310
#[cfg(feature = "mem_profile")]
309-
#[allow(dead_code)]
310311
trace_info: MemTraceInfo,
311312
}
312313

@@ -660,7 +661,10 @@ impl Hypervisor for KVMDriver {
660661
}
661662

662663
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
663-
fn run(&mut self) -> Result<HyperlightExit> {
664+
fn run(
665+
&mut self,
666+
#[cfg(feature = "trace_guest")] tc: &mut crate::sandbox::trace::TraceContext,
667+
) -> Result<HyperlightExit> {
664668
self.interrupt_handle
665669
.tid
666670
.store(unsafe { libc::pthread_self() as u64 }, Ordering::Relaxed);
@@ -693,6 +697,9 @@ impl Hypervisor for KVMDriver {
693697
{
694698
Err(kvm_ioctls::Error::new(libc::EINTR))
695699
} else {
700+
#[cfg(feature = "trace_guest")]
701+
tc.setup_guest_trace(Span::current().context());
702+
696703
// Note: if a `InterruptHandle::kill()` called while this thread is **here**
697704
// Then the vcpu will run, but we will keep sending signals to this thread
698705
// to interrupt it until `running` is set to false. The `vcpu_fd::run()` call will
@@ -1032,6 +1039,17 @@ impl Hypervisor for KVMDriver {
10321039
Ok(X86_64Regs::from(self.vcpu_fd.get_regs()?))
10331040
}
10341041

1042+
#[cfg(feature = "trace_guest")]
1043+
fn handle_trace(&mut self, tc: &mut crate::sandbox::trace::TraceContext) -> Result<()> {
1044+
let regs = self.read_regs()?;
1045+
tc.handle_trace(
1046+
&regs,
1047+
self.mem_mgr.as_ref().ok_or_else(|| {
1048+
new_error!("Memory manager is not initialized before handling trace")
1049+
})?,
1050+
)
1051+
}
1052+
10351053
#[cfg(feature = "mem_profile")]
10361054
fn trace_info_mut(&mut self) -> &mut MemTraceInfo {
10371055
&mut self.trace_info

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@ pub(crate) trait Hypervisor: Debug + Send {
171171
) -> Result<()>;
172172

173173
/// Run the vCPU
174-
fn run(&mut self) -> Result<HyperlightExit>;
174+
fn run(
175+
&mut self,
176+
#[cfg(feature = "trace_guest")] tc: &mut crate::sandbox::trace::TraceContext,
177+
) -> Result<HyperlightExit>;
175178

176179
/// Get InterruptHandle to underlying VM
177180
fn interrupt_handle(&self) -> Arc<dyn InterruptHandle>;
@@ -237,6 +240,9 @@ pub(crate) trait Hypervisor: Debug + Send {
237240
#[cfg(feature = "trace_guest")]
238241
fn read_regs(&self) -> Result<arch::X86_64Regs>;
239242

243+
#[cfg(feature = "trace_guest")]
244+
fn handle_trace(&mut self, tc: &mut crate::sandbox::trace::TraceContext) -> Result<()>;
245+
240246
/// Get a mutable reference of the trace info for the guest
241247
#[cfg(feature = "mem_profile")]
242248
fn trace_info_mut(&mut self) -> &mut MemTraceInfo;
@@ -276,8 +282,31 @@ impl VirtualCPU {
276282
hv: &mut dyn Hypervisor,
277283
#[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<MemMgrWrapper<HostSharedMemory>>>,
278284
) -> Result<()> {
285+
// Keeps the trace context and open spans
286+
#[cfg(feature = "trace_guest")]
287+
let mut tc = crate::sandbox::trace::TraceContext::new();
288+
279289
loop {
280-
match hv.run() {
290+
#[cfg(feature = "trace_guest")]
291+
let result = {
292+
let result = hv.run(&mut tc);
293+
// End current host trace by closing the current span that captures traces
294+
// happening when a guest exits and re-enters.
295+
tc.end_host_trace();
296+
297+
// Handle the guest trace data if any
298+
if let Err(e) = hv.handle_trace(&mut tc) {
299+
// If no trace data is available, we just log a message and continue
300+
// Is this the right thing to do?
301+
log::debug!("Error handling guest trace: {:?}", e);
302+
}
303+
304+
result
305+
};
306+
#[cfg(not(feature = "trace_guest"))]
307+
let result = hv.run();
308+
309+
match result {
281310
#[cfg(gdb)]
282311
Ok(HyperlightExit::Debug(stop_reason)) => {
283312
if let Err(e) = hv.handle_debug(dbg_mem_access_fn.clone(), stop_reason) {

0 commit comments

Comments
 (0)