Skip to content

Commit c3d65fc

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 1a702de commit c3d65fc

File tree

10 files changed

+156
-20
lines changed

10 files changed

+156
-20
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
# This feature is deprecated in favor of mshv3
131134
mshv2 = ["dep:mshv-bindings2", "dep:mshv-ioctls2"]

src/hyperlight_host/src/hypervisor/handlers.rs

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

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

21+
#[cfg(feature = "trace_guest")]
22+
use super::Hypervisor;
2123
use crate::{Result, new_error};
2224

2325
/// The trait representing custom logic to handle the case when
2426
/// a Hypervisor's virtual CPU (vCPU) informs Hyperlight the guest
2527
/// has initiated an outb operation.
2628
pub(crate) trait OutBHandlerCaller: Sync + Send {
2729
/// Function that gets called when an outb operation has occurred.
28-
fn call(&mut self, port: u16, payload: u32) -> Result<()>;
30+
fn call(
31+
&mut self,
32+
#[cfg(feature = "trace_guest")] hv: &mut dyn Hypervisor,
33+
port: u16,
34+
payload: u32,
35+
) -> Result<()>;
2936
}
3037

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

46+
#[cfg(feature = "trace_guest")]
47+
pub(crate) type OutBHandlerFunction =
48+
Box<dyn FnMut(&mut dyn Hypervisor, u16, u32) -> Result<()> + Send>;
49+
#[cfg(not(feature = "trace_guest"))]
3950
pub(crate) type OutBHandlerFunction = Box<dyn FnMut(u16, u32) -> Result<()> + Send>;
4051

4152
/// A `OutBHandler` implementation using a `OutBHandlerFunction`
@@ -52,12 +63,22 @@ impl From<OutBHandlerFunction> for OutBHandler {
5263

5364
impl OutBHandlerCaller for OutBHandler {
5465
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
55-
fn call(&mut self, port: u16, payload: u32) -> Result<()> {
66+
fn call(
67+
&mut self,
68+
#[cfg(feature = "trace_guest")] hv: &mut dyn Hypervisor,
69+
port: u16,
70+
payload: u32,
71+
) -> Result<()> {
5672
let mut func = self
5773
.0
5874
.try_lock()
5975
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
60-
func(port, payload)
76+
func(
77+
#[cfg(feature = "trace_guest")]
78+
hv,
79+
port,
80+
payload,
81+
)
6182
}
6283
}
6384

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 18 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};
@@ -311,6 +313,9 @@ pub(crate) struct HypervLinuxDriver {
311313
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
312314
#[cfg(crashdump)]
313315
rt_cfg: SandboxRuntimeConfig,
316+
#[cfg(feature = "trace_guest")]
317+
#[allow(dead_code)]
318+
trace_info: TraceInfo,
314319
}
315320

316321
impl HypervLinuxDriver {
@@ -322,6 +327,8 @@ impl HypervLinuxDriver {
322327
/// the underlying virtual CPU after this function returns. Call the
323328
/// `apply_registers` method to do that, or more likely call
324329
/// `initialise` to do it for you.
330+
#[allow(clippy::too_many_arguments)]
331+
// TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
325332
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
326333
pub(crate) fn new(
327334
mem_regions: Vec<MemoryRegion>,
@@ -331,6 +338,7 @@ impl HypervLinuxDriver {
331338
config: &SandboxConfiguration,
332339
#[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
333340
#[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
341+
#[cfg(feature = "trace_guest")] trace_info: TraceInfo,
334342
) -> Result<Self> {
335343
let mshv = Mshv::new()?;
336344
let pr = Default::default();
@@ -438,6 +446,8 @@ impl HypervLinuxDriver {
438446
gdb_conn,
439447
#[cfg(crashdump)]
440448
rt_cfg,
449+
#[cfg(feature = "trace_guest")]
450+
trace_info,
441451
};
442452

443453
// Send the interrupt handle to the GDB thread if debugging is enabled
@@ -668,7 +678,12 @@ impl Hypervisor for HypervLinuxDriver {
668678
outb_handle_fn
669679
.try_lock()
670680
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
671-
.call(port, val)?;
681+
.call(
682+
#[cfg(feature = "trace_guest")]
683+
self,
684+
port,
685+
val,
686+
)?;
672687

673688
// update rip
674689
self.vcpu_fd.set_reg(&[hv_register_assoc {
@@ -1164,6 +1179,8 @@ mod tests {
11641179
#[cfg(crashdump)]
11651180
guest_core_dump: true,
11661181
},
1182+
#[cfg(feature = "trace_guest")]
1183+
TraceInfo::new().unwrap(),
11671184
)
11681185
.unwrap();
11691186
}

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ use crate::hypervisor::fpu::FP_CONTROL_WORD_DEFAULT;
6060
use crate::hypervisor::wrappers::WHvGeneralRegisters;
6161
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
6262
use crate::mem::ptr::{GuestPtr, RawPtr};
63+
#[cfg(feature = "trace_guest")]
64+
use crate::sandbox::TraceInfo;
6365
#[cfg(crashdump)]
6466
use crate::sandbox::uninitialized::SandboxRuntimeConfig;
6567
use crate::{Result, debug, log_then_return, new_error};
@@ -283,6 +285,9 @@ pub(crate) struct HypervWindowsDriver {
283285
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
284286
#[cfg(crashdump)]
285287
rt_cfg: SandboxRuntimeConfig,
288+
#[cfg(feature = "trace_guest")]
289+
#[allow(dead_code)]
290+
trace_info: TraceInfo,
286291
}
287292
/* This does not automatically impl Send/Sync because the host
288293
* address of the shared memory region is a raw pointer, which are
@@ -294,6 +299,7 @@ unsafe impl Sync for HypervWindowsDriver {}
294299

295300
impl HypervWindowsDriver {
296301
#[allow(clippy::too_many_arguments)]
302+
// TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
297303
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
298304
pub(crate) fn new(
299305
mem_regions: Vec<MemoryRegion>,
@@ -304,6 +310,7 @@ impl HypervWindowsDriver {
304310
mmap_file_handle: HandleWrapper,
305311
#[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
306312
#[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
313+
#[cfg(feature = "trace_guest")] trace_info: TraceInfo,
307314
) -> Result<Self> {
308315
// create and setup hypervisor partition
309316
let mut partition = VMPartition::new(1)?;
@@ -354,6 +361,8 @@ impl HypervWindowsDriver {
354361
gdb_conn,
355362
#[cfg(crashdump)]
356363
rt_cfg,
364+
#[cfg(feature = "trace_guest")]
365+
trace_info,
357366
};
358367

359368
// Send the interrupt handle to the GDB thread if debugging is enabled
@@ -683,7 +692,12 @@ impl Hypervisor for HypervWindowsDriver {
683692
outb_handle_fn
684693
.try_lock()
685694
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
686-
.call(port, val)?;
695+
.call(
696+
#[cfg(feature = "trace_guest")]
697+
self,
698+
port,
699+
val,
700+
)?;
687701

688702
let mut regs = self.processor.get_regs()?;
689703
regs.rip = rip + instruction_length;

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 16 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};
@@ -298,12 +300,17 @@ pub(crate) struct KVMDriver {
298300
gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
299301
#[cfg(crashdump)]
300302
rt_cfg: SandboxRuntimeConfig,
303+
#[cfg(feature = "trace_guest")]
304+
#[allow(dead_code)]
305+
trace_info: TraceInfo,
301306
}
302307

303308
impl KVMDriver {
304309
/// Create a new instance of a `KVMDriver`, with only control registers
305310
/// set. Standard registers will not be set, and `initialise` must
306311
/// be called to do so.
312+
#[allow(clippy::too_many_arguments)]
313+
// TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
307314
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
308315
pub(crate) fn new(
309316
mem_regions: Vec<MemoryRegion>,
@@ -313,6 +320,7 @@ impl KVMDriver {
313320
config: &SandboxConfiguration,
314321
#[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
315322
#[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
323+
#[cfg(feature = "trace_guest")] trace_info: TraceInfo,
316324
) -> Result<Self> {
317325
let kvm = Kvm::new()?;
318326

@@ -380,6 +388,8 @@ impl KVMDriver {
380388
gdb_conn,
381389
#[cfg(crashdump)]
382390
rt_cfg,
391+
#[cfg(feature = "trace_guest")]
392+
trace_info,
383393
};
384394

385395
// Send the interrupt handle to the GDB thread if debugging is enabled
@@ -586,7 +596,12 @@ impl Hypervisor for KVMDriver {
586596
outb_handle_fn
587597
.try_lock()
588598
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
589-
.call(port, value)?;
599+
.call(
600+
#[cfg(feature = "trace_guest")]
601+
self,
602+
port,
603+
value,
604+
)?;
590605
}
591606

592607
Ok(())

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -503,10 +503,12 @@ pub(crate) mod tests {
503503

504504
use hyperlight_testing::dummy_guest_as_string;
505505

506-
use super::handlers::{MemAccessHandler, OutBHandler};
506+
use super::handlers::{MemAccessHandler, OutBHandler, OutBHandlerFunction};
507507
#[cfg(gdb)]
508508
use crate::hypervisor::DbgMemAccessHandlerCaller;
509509
use crate::mem::ptr::RawPtr;
510+
#[cfg(feature = "trace_guest")]
511+
use crate::sandbox::TraceInfo;
510512
use crate::sandbox::uninitialized::GuestBinary;
511513
#[cfg(any(crashdump, gdb))]
512514
use crate::sandbox::uninitialized::SandboxRuntimeConfig;
@@ -538,11 +540,6 @@ pub(crate) mod tests {
538540
return Ok(());
539541
}
540542

541-
let outb_handler: Arc<Mutex<OutBHandler>> = {
542-
let func: Box<dyn FnMut(u16, u32) -> Result<()> + Send> =
543-
Box::new(|_, _| -> Result<()> { Ok(()) });
544-
Arc::new(Mutex::new(OutBHandler::from(func)))
545-
};
546543
let mem_access_handler = {
547544
let func: Box<dyn FnMut() -> Result<()> + Send> = Box::new(|| -> Result<()> { Ok(()) });
548545
Arc::new(Mutex::new(MemAccessHandler::from(func)))
@@ -563,7 +560,17 @@ pub(crate) mod tests {
563560
&config,
564561
#[cfg(any(crashdump, gdb))]
565562
&rt_cfg,
563+
#[cfg(feature = "trace_guest")]
564+
TraceInfo::new()?,
566565
)?;
566+
let outb_handler: Arc<Mutex<OutBHandler>> = {
567+
#[cfg(feature = "trace_guest")]
568+
#[allow(clippy::type_complexity)]
569+
let func: OutBHandlerFunction = Box::new(|_, _, _| -> Result<()> { Ok(()) });
570+
#[cfg(not(feature = "trace_guest"))]
571+
let func: OutBHandlerFunction = Box::new(|_, _| -> Result<()> { Ok(()) });
572+
Arc::new(Mutex::new(OutBHandler::from(func)))
573+
};
567574
vm.initialise(
568575
RawPtr::from(0x230000),
569576
1234567890,

src/hyperlight_host/src/sandbox/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ pub mod snapshot;
4343
/// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm
4444
mod callable;
4545

46+
#[cfg(feature = "trace_guest")]
47+
use std::sync::{Arc, Mutex};
48+
4649
/// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm
4750
pub use callable::Callable;
4851
/// Re-export for `SandboxConfiguration` type
@@ -89,6 +92,35 @@ pub fn is_hypervisor_present() -> bool {
8992
hypervisor::get_available_hypervisor().is_some()
9093
}
9194

95+
#[cfg(feature = "trace_guest")]
96+
#[derive(Clone)]
97+
/// The information that trace collection requires in order to write
98+
/// an accurate trace.
99+
pub(crate) struct TraceInfo {
100+
/// The epoch against which trace events are timed; at least as
101+
/// early as the creation of the sandbox being traced.
102+
#[allow(dead_code)]
103+
pub epoch: std::time::Instant,
104+
/// The file to which the trace is being written
105+
#[allow(dead_code)]
106+
pub file: Arc<Mutex<std::fs::File>>,
107+
}
108+
#[cfg(feature = "trace_guest")]
109+
impl TraceInfo {
110+
/// Create a new TraceInfo by saving the current time as the epoch
111+
/// and generating a random filename.
112+
pub fn new() -> crate::Result<Self> {
113+
let mut path = std::env::current_dir()?;
114+
path.push("trace");
115+
path.push(uuid::Uuid::new_v4().to_string());
116+
path.set_extension("trace");
117+
Ok(Self {
118+
epoch: std::time::Instant::now(),
119+
file: Arc::new(Mutex::new(std::fs::File::create_new(path)?)),
120+
})
121+
}
122+
}
123+
92124
pub(crate) trait WrapperGetter {
93125
#[allow(dead_code)]
94126
fn get_mgr_wrapper(&self) -> &MemMgrWrapper<HostSharedMemory>;

0 commit comments

Comments
 (0)