Skip to content

Commit 4c70cc2

Browse files
committed
Allow explicit setting of max guest log level, check RUST_LOG to set guest log_level if not set
The guest log level impacts the amount of logs produced by the guest, its presently set by examining the value of max_log_level in the host. This may not be appropriate since logging in the guest is expensive and it may cause logs to be emitted from the guest but ignored in the host. This change introduces a function on an uninitializedsandbox to set the level explicitly, if this is not set the env var RUST_LOG is parsed to determine the log level so that logs are only produced if they are going to be consumed. If RUST_LOG is not set then the max guest log level is set to error. Signed-off-by: Simon Davies <[email protected]>
1 parent 42e66ce commit 4c70cc2

File tree

8 files changed

+125
-57
lines changed

8 files changed

+125
-57
lines changed

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extern crate mshv_ioctls3 as mshv_ioctls;
2626

2727
use std::fmt::{Debug, Formatter};
2828

29-
use log::error;
29+
use log::{error, LevelFilter};
3030
#[cfg(mshv2)]
3131
use mshv_bindings::hv_message;
3232
#[cfg(gdb)]
@@ -463,8 +463,14 @@ impl Hypervisor for HypervLinuxDriver {
463463
outb_hdl: OutBHandlerWrapper,
464464
mem_access_hdl: MemAccessHandlerWrapper,
465465
hv_handler: Option<HypervisorHandler>,
466+
max_guest_log_level: Option<LevelFilter>,
466467
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
467468
) -> Result<()> {
469+
let max_guest_log_level: u64 = match max_guest_log_level {
470+
Some(level) => level as u64,
471+
None => self.get_max_log_level().into(),
472+
};
473+
468474
let regs = StandardRegisters {
469475
rip: self.entrypoint,
470476
rsp: self.orig_rsp.absolute()?,
@@ -474,7 +480,7 @@ impl Hypervisor for HypervLinuxDriver {
474480
rcx: peb_addr.into(),
475481
rdx: seed,
476482
r8: page_size.into(),
477-
r9: self.get_max_log_level().into(),
483+
r9: max_guest_log_level,
478484

479485
..Default::default()
480486
};

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::fmt::{Debug, Formatter};
2020
use std::string::String;
2121

2222
use hyperlight_common::mem::PAGE_SIZE_USIZE;
23+
use log::LevelFilter;
2324
use tracing::{instrument, Span};
2425
use windows::Win32::System::Hypervisor::{
2526
WHvX64RegisterCr0, WHvX64RegisterCr3, WHvX64RegisterCr4, WHvX64RegisterCs, WHvX64RegisterEfer,
@@ -307,8 +308,14 @@ impl Hypervisor for HypervWindowsDriver {
307308
outb_hdl: OutBHandlerWrapper,
308309
mem_access_hdl: MemAccessHandlerWrapper,
309310
hv_handler: Option<HypervisorHandler>,
311+
max_guest_log_level: Option<LevelFilter>,
310312
#[cfg(gdb)] dbg_mem_access_hdl: DbgMemAccessHandlerWrapper,
311313
) -> Result<()> {
314+
let max_guest_log_level: u64 = match max_guest_log_level {
315+
Some(level) => level as u64,
316+
None => self.get_max_log_level().into(),
317+
};
318+
312319
let regs = WHvGeneralRegisters {
313320
rip: self.entrypoint,
314321
rsp: self.orig_rsp.absolute()?,
@@ -317,7 +324,7 @@ impl Hypervisor for HypervWindowsDriver {
317324
rcx: peb_address.into(),
318325
rdx: seed,
319326
r8: page_size.into(),
320-
r9: self.get_max_log_level().into(),
327+
r9: max_guest_log_level,
321328
rflags: 1 << 1, // eflags bit index 1 is reserved and always needs to be 1
322329

323330
..Default::default()

src/hyperlight_host/src/hypervisor/hypervisor_handler.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crossbeam::atomic::AtomicCell;
2828
use crossbeam_channel::{Receiver, Sender};
2929
#[cfg(target_os = "linux")]
3030
use libc::{pthread_kill, pthread_self, ESRCH};
31-
use log::{error, info};
31+
use log::{error, info, LevelFilter};
3232
use tracing::{instrument, Span};
3333
#[cfg(target_os = "linux")]
3434
use vmm_sys_util::signal::SIGRTMIN;
@@ -191,6 +191,7 @@ pub(crate) struct HvHandlerConfig {
191191
pub(crate) outb_handler: OutBHandlerWrapper,
192192
pub(crate) mem_access_handler: MemAccessHandlerWrapper,
193193
pub(crate) max_wait_for_cancellation: Duration,
194+
pub(crate) max_guest_log_level: Option<LevelFilter>,
194195
#[cfg(gdb)]
195196
pub(crate) dbg_mem_access_handler: DbgMemAccessHandlerWrapper,
196197
}
@@ -361,6 +362,7 @@ impl HypervisorHandler {
361362
configuration.outb_handler.clone(),
362363
configuration.mem_access_handler.clone(),
363364
Some(hv_handler_clone.clone()),
365+
configuration.max_guest_log_level,
364366
#[cfg(gdb)]
365367
configuration.dbg_mem_access_handler.clone(),
366368
);

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::sync::{Arc, Mutex};
2222
use kvm_bindings::{kvm_fpu, kvm_regs, kvm_userspace_memory_region, KVM_MEM_READONLY};
2323
use kvm_ioctls::Cap::UserMemory;
2424
use kvm_ioctls::{Kvm, VcpuExit, VcpuFd, VmFd};
25+
use log::LevelFilter;
2526
use tracing::{instrument, Span};
2627

2728
use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
@@ -406,8 +407,14 @@ impl Hypervisor for KVMDriver {
406407
outb_hdl: OutBHandlerWrapper,
407408
mem_access_hdl: MemAccessHandlerWrapper,
408409
hv_handler: Option<HypervisorHandler>,
410+
max_guest_log_level: Option<LevelFilter>,
409411
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
410412
) -> Result<()> {
413+
let max_guest_log_level: u64 = match max_guest_log_level {
414+
Some(level) => level as u64,
415+
None => self.get_max_log_level().into(),
416+
};
417+
411418
let regs = kvm_regs {
412419
rip: self.entrypoint,
413420
rsp: self.orig_rsp.absolute()?,
@@ -416,7 +423,7 @@ impl Hypervisor for KVMDriver {
416423
rcx: peb_addr.into(),
417424
rdx: seed,
418425
r8: page_size.into(),
419-
r9: self.get_max_log_level().into(),
426+
r9: max_guest_log_level,
420427

421428
..Default::default()
422429
};

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
use log::LevelFilter;
1718
use tracing::{instrument, Span};
1819

1920
use crate::error::HyperlightError::ExecutionCanceledByHost;
@@ -63,6 +64,7 @@ pub(crate) mod wrappers;
6364
pub(crate) mod crashdump;
6465

6566
use std::fmt::Debug;
67+
use std::str::FromStr;
6668
use std::sync::{Arc, Mutex};
6769

6870
#[cfg(gdb)]
@@ -130,6 +132,7 @@ pub(crate) trait Hypervisor: Debug + Sync + Send {
130132
outb_handle_fn: OutBHandlerWrapper,
131133
mem_access_fn: MemAccessHandlerWrapper,
132134
hv_handler: Option<HypervisorHandler>,
135+
guest_max_log_level: Option<LevelFilter>,
133136
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
134137
) -> Result<()>;
135138

@@ -191,7 +194,44 @@ pub(crate) trait Hypervisor: Debug + Sync + Send {
191194

192195
/// Get the logging level to pass to the guest entrypoint
193196
fn get_max_log_level(&self) -> u32 {
194-
log::max_level() as u32
197+
// Check to see if the RUST_LOG environment variable is set
198+
// and if so, parse it to get the log_level for hyperlight_guest
199+
// if that is not set get the log level for the hyperlight_host
200+
201+
// This is done as the guest will produce logs based on the log level returned here
202+
// producing those logs is expensive and we don't want to do it if the host is not
203+
// going to process them
204+
205+
let val = match std::env::var("RUST_LOG") {
206+
Ok(s) => s,
207+
Err(_) => String::new(),
208+
};
209+
210+
let level = if val.contains("hyperlight_guest") {
211+
val.split(',')
212+
.find(|s| s.contains("hyperlight_guest"))
213+
.unwrap_or("")
214+
.split('=')
215+
.nth(1)
216+
.unwrap_or("")
217+
} else {
218+
if val.contains("hyperlight_host") {
219+
val.split(',')
220+
.find(|s| s.contains("hyperlight_host"))
221+
.unwrap_or("")
222+
.split('=')
223+
.nth(1)
224+
.unwrap_or("")
225+
} else {
226+
// look for a value string that does not contain "="
227+
val.split(',').find(|s| !s.contains("=")).unwrap_or("")
228+
}
229+
};
230+
231+
log::info!("Determined guest log level: {}", level);
232+
// Convert the log level string to a LevelFilter
233+
// If no value is found, default to Error
234+
LevelFilter::from_str(level).unwrap_or(LevelFilter::Error) as u32
195235
}
196236

197237
/// get a mutable trait object from self
@@ -356,6 +396,7 @@ pub(crate) mod tests {
356396
max_wait_for_cancellation: Duration::from_millis(
357397
SandboxConfiguration::DEFAULT_MAX_WAIT_FOR_CANCELLATION as u64,
358398
),
399+
max_guest_log_level: None,
359400
};
360401

361402
let mut hv_handler = HypervisorHandler::new(hv_handler_config);

src/hyperlight_host/src/sandbox/uninitialized.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::path::Path;
2020
use std::sync::{Arc, Mutex};
2121
use std::time::Duration;
2222

23+
use log::LevelFilter;
2324
use tracing::{instrument, Span};
2425

2526
#[cfg(gdb)]
@@ -54,6 +55,7 @@ pub struct UninitializedSandbox {
5455
pub(crate) max_initialization_time: Duration,
5556
pub(crate) max_execution_time: Duration,
5657
pub(crate) max_wait_for_cancellation: Duration,
58+
pub(crate) max_guest_log_level: Option<LevelFilter>,
5759
#[cfg(gdb)]
5860
pub(crate) debug_info: Option<DebugInfo>,
5961
}
@@ -195,6 +197,7 @@ impl UninitializedSandbox {
195197
max_wait_for_cancellation: Duration::from_millis(
196198
sandbox_cfg.get_max_wait_for_cancellation() as u64,
197199
),
200+
max_guest_log_level: None,
198201
#[cfg(gdb)]
199202
debug_info,
200203
};
@@ -299,6 +302,13 @@ impl UninitializedSandbox {
299302
SandboxMemoryManager::load_guest_binary_into_memory(cfg, &mut exe_info, inprocess)
300303
}
301304
}
305+
306+
/// Set the max log level to be used by the guest.
307+
/// If this is not set then the log level will be determined by parsing the RUST_LOG environment variable.
308+
/// If the RUST_LOG environment variable is not set then the max log level will be set to `LevelFilter::Error`.
309+
pub fn set_max_guest_log_level(&mut self, log_level: LevelFilter) {
310+
self.max_guest_log_level = Some(log_level);
311+
}
302312
}
303313
// Check to see if the current version of Windows is supported
304314
// Hyperlight is only supported on Windows 11 and Windows Server 2022 and later

src/hyperlight_host/src/sandbox/uninitialized_evolve.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
use core::time::Duration;
1818
use std::sync::{Arc, Mutex};
1919

20+
use log::LevelFilter;
2021
use rand::Rng;
2122
use tracing::{instrument, Span};
2223

@@ -70,6 +71,7 @@ where
7071
u_sbox.max_initialization_time,
7172
u_sbox.max_execution_time,
7273
u_sbox.max_wait_for_cancellation,
74+
u_sbox.max_guest_log_level,
7375
#[cfg(gdb)]
7476
u_sbox.debug_info,
7577
)?;
@@ -106,6 +108,7 @@ fn hv_init(
106108
max_init_time: Duration,
107109
max_exec_time: Duration,
108110
max_wait_for_cancellation: Duration,
111+
max_guest_log_level: Option<LevelFilter>,
109112
#[cfg(gdb)] debug_info: Option<DebugInfo>,
110113
) -> Result<HypervisorHandler> {
111114
let outb_hdl = outb_handler_wrapper(hshm.clone(), host_funcs);
@@ -134,6 +137,7 @@ fn hv_init(
134137
max_init_time,
135138
max_exec_time,
136139
max_wait_for_cancellation,
140+
max_guest_log_level,
137141
};
138142
// Note: `dispatch_function_addr` is set by the Hyperlight guest library, and so it isn't in
139143
// shared memory at this point in time. We will set it after the execution of `hv_init`.

src/hyperlight_host/tests/integration_test.rs

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use hyperlight_host::sandbox::SandboxConfiguration;
2121
use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox;
2222
use hyperlight_host::sandbox_state::transition::Noop;
2323
use hyperlight_host::{GuestBinary, HyperlightError, MultiUseSandbox, UninitializedSandbox};
24+
use hyperlight_testing::simplelogger::{SimpleLogger, LOGGER};
2425
use hyperlight_testing::{c_simple_guest_as_string, simple_guest_as_string};
26+
use log::LevelFilter;
2527

2628
pub mod common; // pub to disable dead_code warning
2729
use crate::common::{new_uninit, new_uninit_rust};
@@ -471,69 +473,58 @@ fn recursive_stack_allocate_overflow() {
471473
#[test]
472474
#[ignore]
473475
fn log_message() {
474-
use hyperlight_testing::simplelogger::{SimpleLogger, LOGGER};
476+
// internal_dispatch_function does a log::trace! in debug mode, and we call it 6 times in `log_test_messages`
477+
let num_fixed_trace_log = if cfg!(debug_assertions) { 6 } else { 0 };
478+
479+
let mut tests = Vec::new();
480+
tests.push((LevelFilter::Trace, 5 + num_fixed_trace_log));
481+
tests.push((LevelFilter::Debug, 4));
482+
tests.push((LevelFilter::Info, 3));
483+
tests.push((LevelFilter::Warn, 2));
484+
tests.push((LevelFilter::Error, 1));
485+
tests.push((LevelFilter::Off, 0));
486+
475487
// init
476488
SimpleLogger::initialize_test_logger();
477489

478-
// internal_dispatch_function does a log::trace! in debug mode, and we call it 6 times in `log_test_messages`
479-
let num_fixed_trace_log = if cfg!(debug_assertions) { 6 } else { 0 };
490+
for test in tests {
491+
let (level, expected) = test;
480492

481-
// test trace level
482-
log::set_max_level(log::LevelFilter::Trace);
483-
LOGGER.clear_log_calls();
484-
assert_eq!(0, LOGGER.num_log_calls());
485-
log_test_messages();
486-
assert_eq!(5 + num_fixed_trace_log, LOGGER.num_log_calls());
487-
// The number of enabled calls is the number of times that the enabled function is called
488-
// with a target of "hyperlight_guest"
489-
// This should be the same as the number of log calls as all the log calls for the "hyperlight_guest" target should be filtered in
490-
// the guest
491-
assert_eq!(LOGGER.num_log_calls(), LOGGER.num_enabled_calls());
492-
493-
// test debug level
494-
log::set_max_level(log::LevelFilter::Debug);
495-
LOGGER.clear_log_calls();
496-
assert_eq!(0, LOGGER.num_log_calls());
497-
log_test_messages();
498-
assert_eq!(4, LOGGER.num_log_calls());
499-
assert_eq!(LOGGER.num_log_calls(), LOGGER.num_enabled_calls());
493+
// Test setting max log level via method on uninit sandbox
494+
log_test_messages(Some(level));
495+
assert_eq!(expected, LOGGER.num_log_calls());
500496

501-
// test info level
502-
log::set_max_level(log::LevelFilter::Info);
503-
LOGGER.clear_log_calls();
504-
assert_eq!(0, LOGGER.num_log_calls());
505-
log_test_messages();
506-
assert_eq!(3, LOGGER.num_log_calls());
507-
assert_eq!(LOGGER.num_log_calls(), LOGGER.num_enabled_calls());
497+
// Set the log level via env var
498+
std::env::set_var("RUST_LOG", format!("hyperlight_guest={}", level));
499+
log_test_messages(None);
500+
assert_eq!(expected, LOGGER.num_log_calls());
508501

509-
// test warn level
510-
log::set_max_level(log::LevelFilter::Warn);
511-
LOGGER.clear_log_calls();
512-
assert_eq!(0, LOGGER.num_log_calls());
513-
log_test_messages();
514-
assert_eq!(2, LOGGER.num_log_calls());
515-
assert_eq!(LOGGER.num_log_calls(), LOGGER.num_enabled_calls());
502+
std::env::set_var("RUST_LOG", format!("hyperlight_host={}", level));
503+
log_test_messages(None);
504+
assert_eq!(expected, LOGGER.num_log_calls());
516505

517-
// test error level
518-
log::set_max_level(log::LevelFilter::Error);
519-
LOGGER.clear_log_calls();
520-
assert_eq!(0, LOGGER.num_log_calls());
521-
log_test_messages();
506+
std::env::set_var("RUST_LOG", format!("{}", level));
507+
log_test_messages(None);
508+
assert_eq!(expected, LOGGER.num_log_calls());
509+
510+
std::env::remove_var("RUST_LOG");
511+
}
512+
513+
// Test that if no log level is set, the default is error
514+
log_test_messages(None);
522515
assert_eq!(1, LOGGER.num_log_calls());
523-
assert_eq!(LOGGER.num_log_calls(), LOGGER.num_enabled_calls());
516+
}
524517

525-
// test off level
526-
log::set_max_level(log::LevelFilter::Off);
518+
fn log_test_messages(levelfilter: Option<log::LevelFilter>) {
527519
LOGGER.clear_log_calls();
528520
assert_eq!(0, LOGGER.num_log_calls());
529-
log_test_messages();
530-
assert_eq!(0, LOGGER.num_log_calls());
531-
assert_eq!(LOGGER.num_log_calls(), LOGGER.num_enabled_calls());
532-
}
533-
534-
fn log_test_messages() {
535521
for level in log::LevelFilter::iter() {
536-
let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap();
522+
let mut sbox = new_uninit().unwrap();
523+
if let Some(levelfilter) = levelfilter {
524+
sbox.set_max_guest_log_level(levelfilter);
525+
}
526+
527+
let mut sbox1 = sbox.evolve(Noop::default()).unwrap();
537528

538529
let message = format!("Hello from log_message level {}", level as i32);
539530
sbox1

0 commit comments

Comments
 (0)