Skip to content

Commit ba778d1

Browse files
syntacticallydblnz
authored andcommitted
[hyperlight_host] Plumb a trace file descriptor around
This adds (unused) support for creating trace files for sandboxes and passing them around to relevant sandbox event handler code. This will be used for collecting debug trace and profiling information. Signed-off-by: Lucy Menon <[email protected]> Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent ba24f16 commit ba778d1

File tree

11 files changed

+222
-22
lines changed

11 files changed

+222
-22
lines changed

Cargo.lock

Lines changed: 10 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ anyhow = "1.0"
4444
metrics = "0.24.2"
4545
serde_json = "1.0"
4646
elfcore = "2.0"
47+
uuid = { version = "1.17.0", features = ["v4"] }
4748

4849
[target.'cfg(windows)'.dependencies]
4950
windows = { version = "0.61", features = [
@@ -79,6 +80,7 @@ mshv-ioctls3 = { package="mshv-ioctls", version = "=0.3.2", optional = true}
7980
[dev-dependencies]
8081
uuid = { version = "1.17.0", features = ["v4"] }
8182
signal-hook-registry = "1.4.5"
83+
envy = { version = "0.4.2" }
8284
serde = "1.0"
8385
proptest = "1.7.0"
8486
tempfile = "3.20.0"
@@ -126,6 +128,7 @@ executable_heap = []
126128
print_debug = []
127129
# Dumps the VM state to a file on unexpected errors or crashes. The path of the file will be printed on stdout and logged.
128130
crashdump = ["dep:chrono"]
131+
trace_guest = []
129132
kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"]
130133
mshv2 = ["dep:mshv-bindings2", "dep:mshv-ioctls2"]
131134
mshv3 = ["dep:mshv-bindings3", "dep:mshv-ioctls3"]

src/hyperlight_host/src/hypervisor/handlers.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,24 @@ use std::sync::{Arc, Mutex};
1818

1919
use tracing::{Span, instrument};
2020

21+
#[cfg(feature = "trace_guest")]
22+
use super::Hypervisor;
23+
#[cfg(feature = "trace_guest")]
24+
use crate::sandbox::TraceInfo;
2125
use crate::{Result, new_error};
2226

2327
/// The trait representing custom logic to handle the case when
2428
/// a Hypervisor's virtual CPU (vCPU) informs Hyperlight the guest
2529
/// has initiated an outb operation.
2630
pub(crate) trait OutBHandlerCaller: Sync + Send {
2731
/// Function that gets called when an outb operation has occurred.
28-
fn call(&mut self, port: u16, payload: u32) -> Result<()>;
32+
fn call(
33+
&mut self,
34+
#[cfg(feature = "trace_guest")] hv: &mut dyn Hypervisor,
35+
#[cfg(feature = "trace_guest")] trace_info: TraceInfo,
36+
port: u16,
37+
payload: u32,
38+
) -> Result<()>;
2939
}
3040

3141
/// A convenient type representing a common way `OutBHandler` implementations
@@ -36,6 +46,10 @@ pub(crate) trait OutBHandlerCaller: Sync + Send {
3646
/// a &mut self).
3747
pub(crate) type OutBHandlerWrapper = Arc<Mutex<dyn OutBHandlerCaller>>;
3848

49+
#[cfg(feature = "trace_guest")]
50+
pub(crate) type OutBHandlerFunction =
51+
Box<dyn FnMut(&mut dyn Hypervisor, TraceInfo, u16, u32) -> Result<()> + Send>;
52+
#[cfg(not(feature = "trace_guest"))]
3953
pub(crate) type OutBHandlerFunction = Box<dyn FnMut(u16, u32) -> Result<()> + Send>;
4054

4155
/// A `OutBHandler` implementation using a `OutBHandlerFunction`
@@ -52,12 +66,25 @@ impl From<OutBHandlerFunction> for OutBHandler {
5266

5367
impl OutBHandlerCaller for OutBHandler {
5468
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
55-
fn call(&mut self, port: u16, payload: u32) -> Result<()> {
69+
fn call(
70+
&mut self,
71+
#[cfg(feature = "trace_guest")] hv: &mut dyn Hypervisor,
72+
#[cfg(feature = "trace_guest")] trace_info: TraceInfo,
73+
port: u16,
74+
payload: u32,
75+
) -> Result<()> {
5676
let mut func = self
5777
.0
5878
.try_lock()
5979
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
60-
func(port, payload)
80+
func(
81+
#[cfg(feature = "trace_guest")]
82+
hv,
83+
#[cfg(feature = "trace_guest")]
84+
trace_info,
85+
port,
86+
payload,
87+
)
6188
}
6289
}
6390

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ use crate::HyperlightError;
7272
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
7373
use crate::mem::ptr::{GuestPtr, RawPtr};
7474
use crate::sandbox::SandboxConfiguration;
75+
#[cfg(feature = "trace_guest")]
76+
use crate::sandbox::TraceInfo;
7577
#[cfg(crashdump)]
7678
use crate::sandbox::uninitialized::SandboxRuntimeConfig;
7779
use crate::{Result, log_then_return, new_error};
@@ -310,6 +312,8 @@ pub(crate) struct HypervLinuxDriver {
310312
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
311313
#[cfg(crashdump)]
312314
rt_cfg: SandboxRuntimeConfig,
315+
#[cfg(feature = "trace_guest")]
316+
trace_info: TraceInfo,
313317
}
314318

315319
impl HypervLinuxDriver {
@@ -321,6 +325,8 @@ impl HypervLinuxDriver {
321325
/// the underlying virtual CPU after this function returns. Call the
322326
/// `apply_registers` method to do that, or more likely call
323327
/// `initialise` to do it for you.
328+
#[allow(clippy::too_many_arguments)]
329+
// TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
324330
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
325331
pub(crate) fn new(
326332
mem_regions: Vec<MemoryRegion>,
@@ -330,6 +336,7 @@ impl HypervLinuxDriver {
330336
config: &SandboxConfiguration,
331337
#[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
332338
#[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
339+
#[cfg(feature = "trace_guest")] trace_info: TraceInfo,
333340
) -> Result<Self> {
334341
let mshv = Mshv::new()?;
335342
let pr = Default::default();
@@ -436,6 +443,8 @@ impl HypervLinuxDriver {
436443
gdb_conn,
437444
#[cfg(crashdump)]
438445
rt_cfg,
446+
#[cfg(feature = "trace_guest")]
447+
trace_info,
439448
};
440449

441450
// Send the interrupt handle to the GDB thread if debugging is enabled
@@ -608,10 +617,19 @@ impl Hypervisor for HypervLinuxDriver {
608617
padded[..copy_len].copy_from_slice(&data[..copy_len]);
609618
let val = u32::from_le_bytes(padded);
610619

620+
#[cfg(feature = "trace_guest")]
621+
let trace_info = self.trace_info.clone();
611622
outb_handle_fn
612623
.try_lock()
613624
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
614-
.call(port, val)?;
625+
.call(
626+
#[cfg(feature = "trace_guest")]
627+
self,
628+
#[cfg(feature = "trace_guest")]
629+
trace_info,
630+
port,
631+
val,
632+
)?;
615633

616634
// update rip
617635
self.vcpu_fd.set_reg(&[hv_register_assoc {
@@ -1038,6 +1056,7 @@ impl Drop for HypervLinuxDriver {
10381056
#[cfg(test)]
10391057
mod tests {
10401058
use super::*;
1059+
use crate::mem::exe::DummyUnwindInfo;
10411060
use crate::mem::memory_region::MemoryRegionVecBuilder;
10421061
use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
10431062

@@ -1105,6 +1124,12 @@ mod tests {
11051124
#[cfg(crashdump)]
11061125
guest_core_dump: true,
11071126
},
1127+
#[cfg(feature = "trace_guest")]
1128+
TraceInfo::new(
1129+
#[cfg(feature = "unwind_guest")]
1130+
Arc::new(DummyUnwindInfo {}),
1131+
)
1132+
.unwrap(),
11081133
)
11091134
.unwrap();
11101135
}

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ use crate::hypervisor::fpu::FP_CONTROL_WORD_DEFAULT;
5757
use crate::hypervisor::wrappers::WHvGeneralRegisters;
5858
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
5959
use crate::mem::ptr::{GuestPtr, RawPtr};
60+
#[cfg(feature = "trace_guest")]
61+
use crate::sandbox::TraceInfo;
6062
#[cfg(crashdump)]
6163
use crate::sandbox::uninitialized::SandboxRuntimeConfig;
62-
use crate::{Result, debug, new_error};
64+
use crate::{Result, debug, log_then_return, new_error};
6365

6466
#[cfg(gdb)]
6567
mod debug {
@@ -280,6 +282,8 @@ pub(crate) struct HypervWindowsDriver {
280282
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
281283
#[cfg(crashdump)]
282284
rt_cfg: SandboxRuntimeConfig,
285+
#[cfg(feature = "trace_guest")]
286+
trace_info: TraceInfo,
283287
}
284288
/* This does not automatically impl Send/Sync because the host
285289
* address of the shared memory region is a raw pointer, which are
@@ -291,6 +295,7 @@ unsafe impl Sync for HypervWindowsDriver {}
291295

292296
impl HypervWindowsDriver {
293297
#[allow(clippy::too_many_arguments)]
298+
// TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
294299
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
295300
pub(crate) fn new(
296301
mem_regions: Vec<MemoryRegion>,
@@ -301,6 +306,7 @@ impl HypervWindowsDriver {
301306
mmap_file_handle: HandleWrapper,
302307
#[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
303308
#[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
309+
#[cfg(feature = "trace_guest")] trace_info: TraceInfo,
304310
) -> Result<Self> {
305311
// create and setup hypervisor partition
306312
let mut partition = VMPartition::new(1)?;
@@ -351,6 +357,8 @@ impl HypervWindowsDriver {
351357
gdb_conn,
352358
#[cfg(crashdump)]
353359
rt_cfg,
360+
#[cfg(feature = "trace_guest")]
361+
trace_info,
354362
};
355363

356364
// Send the interrupt handle to the GDB thread if debugging is enabled
@@ -656,10 +664,19 @@ impl Hypervisor for HypervWindowsDriver {
656664
padded[..copy_len].copy_from_slice(&data[..copy_len]);
657665
let val = u32::from_le_bytes(padded);
658666

667+
#[cfg(feature = "trace_guest")]
668+
let trace_info = self.trace_info.clone();
659669
outb_handle_fn
660670
.try_lock()
661671
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
662-
.call(port, val)?;
672+
.call(
673+
#[cfg(feature = "trace_guest")]
674+
self,
675+
#[cfg(feature = "trace_guest")]
676+
trace_info,
677+
port,
678+
val,
679+
)?;
663680

664681
let mut regs = self.processor.get_regs()?;
665682
regs.rip = rip + instruction_length;
@@ -1042,3 +1059,34 @@ impl InterruptHandle for WindowsInterruptHandle {
10421059
self.dropped.load(Ordering::Relaxed)
10431060
}
10441061
}
1062+
1063+
#[cfg(test)]
1064+
pub mod tests {
1065+
use std::sync::{Arc, Mutex};
1066+
1067+
use serial_test::serial;
1068+
1069+
use crate::Result;
1070+
use crate::hypervisor::handlers::{MemAccessHandler, OutBHandler, OutBHandlerFunction};
1071+
use crate::hypervisor::tests::test_initialise;
1072+
1073+
#[test]
1074+
#[serial]
1075+
fn test_init() {
1076+
let outb_handler = {
1077+
let func: OutBHandlerFunction = Box::new(
1078+
|#[cfg(feature = "trace_guest")] _,
1079+
#[cfg(feature = "trace_guest")] _,
1080+
_,
1081+
_|
1082+
-> Result<()> { Ok(()) },
1083+
);
1084+
Arc::new(Mutex::new(OutBHandler::from(func)))
1085+
};
1086+
let mem_access_handler = {
1087+
let func: Box<dyn FnMut() -> Result<()> + Send> = Box::new(|| -> Result<()> { Ok(()) });
1088+
Arc::new(Mutex::new(MemAccessHandler::from(func)))
1089+
};
1090+
test_initialise(outb_handler, mem_access_handler).unwrap();
1091+
}
1092+
}

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ use crate::HyperlightError;
4646
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
4747
use crate::mem::ptr::{GuestPtr, RawPtr};
4848
use crate::sandbox::SandboxConfiguration;
49+
#[cfg(feature = "trace_guest")]
50+
use crate::sandbox::TraceInfo;
4951
#[cfg(crashdump)]
5052
use crate::sandbox::uninitialized::SandboxRuntimeConfig;
5153
use crate::{Result, log_then_return, new_error};
@@ -297,12 +299,16 @@ pub(crate) struct KVMDriver {
297299
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
298300
#[cfg(crashdump)]
299301
rt_cfg: SandboxRuntimeConfig,
302+
#[cfg(feature = "trace_guest")]
303+
trace_info: TraceInfo,
300304
}
301305

302306
impl KVMDriver {
303307
/// Create a new instance of a `KVMDriver`, with only control registers
304308
/// set. Standard registers will not be set, and `initialise` must
305309
/// be called to do so.
310+
#[allow(clippy::too_many_arguments)]
311+
// TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
306312
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
307313
pub(crate) fn new(
308314
mem_regions: Vec<MemoryRegion>,
@@ -312,6 +318,7 @@ impl KVMDriver {
312318
config: &SandboxConfiguration,
313319
#[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
314320
#[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
321+
#[cfg(feature = "trace_guest")] trace_info: TraceInfo,
315322
) -> Result<Self> {
316323
let kvm = Kvm::new()?;
317324

@@ -390,6 +397,8 @@ impl KVMDriver {
390397
gdb_conn,
391398
#[cfg(crashdump)]
392399
rt_cfg,
400+
#[cfg(feature = "trace_guest")]
401+
trace_info,
393402
};
394403

395404
// Send the interrupt handle to the GDB thread if debugging is enabled
@@ -552,10 +561,19 @@ impl Hypervisor for KVMDriver {
552561
padded[..copy_len].copy_from_slice(&data[..copy_len]);
553562
let value = u32::from_le_bytes(padded);
554563

564+
#[cfg(feature = "trace_guest")]
565+
let trace_info = self.trace_info.clone();
555566
outb_handle_fn
556567
.try_lock()
557568
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
558-
.call(port, value)?;
569+
.call(
570+
#[cfg(feature = "trace_guest")]
571+
self,
572+
#[cfg(feature = "trace_guest")]
573+
trace_info,
574+
port,
575+
value,
576+
)?;
559577
}
560578

561579
Ok(())

0 commit comments

Comments
 (0)