From 4608b94bd89d444cbb02ed41fca2ca002cc0c5ca Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 19 Mar 2020 23:00:02 +0100 Subject: [PATCH 1/3] implement CLOCK_MONOTONIC on Linux --- src/machine.rs | 5 +++++ src/shims/time.rs | 24 ++++++++++++------------ tests/run-pass/clock.rs | 14 -------------- tests/run-pass/time.rs | 18 ++++++++++++++++++ 4 files changed, 35 insertions(+), 26 deletions(-) delete mode 100644 tests/run-pass/clock.rs create mode 100644 tests/run-pass/time.rs diff --git a/src/machine.rs b/src/machine.rs index 3a8e7fc90241..3cf00781338c 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -5,6 +5,7 @@ use std::borrow::Cow; use std::cell::RefCell; use std::num::NonZeroU64; use std::rc::Rc; +use std::time::Instant; use rand::rngs::StdRng; @@ -164,6 +165,9 @@ pub struct Evaluator<'tcx> { /// the call to `miri_start_panic` (the panic payload) when unwinding. /// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`. pub(crate) panic_payload: Option>, + + /// The "time anchor" for this machine's monotone clock (for `Instant` simulation). + pub(crate) time_anchor: Instant, } impl<'tcx> Evaluator<'tcx> { @@ -182,6 +186,7 @@ impl<'tcx> Evaluator<'tcx> { file_handler: Default::default(), dir_handler: Default::default(), panic_payload: None, + time_anchor: Instant::now(), } } } diff --git a/src/shims/time.rs b/src/shims/time.rs index 627478eaaab7..c8807dd6ea84 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -1,14 +1,9 @@ -use std::time::{Duration, SystemTime}; +use std::time::{Duration, SystemTime, Instant}; use crate::stacked_borrows::Tag; use crate::*; use helpers::immty_from_int_checked; -// Returns the time elapsed between now and the unix epoch as a `Duration`. -fn get_time<'tcx>() -> InterpResult<'tcx, Duration> { - system_time_to_duration(&SystemTime::now()) -} - /// Returns the time elapsed between the provided time and the unix epoch as a `Duration`. pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> { time.duration_since(SystemTime::UNIX_EPOCH) @@ -28,15 +23,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.check_no_isolation("clock_gettime")?; let clk_id = this.read_scalar(clk_id_op)?.to_i32()?; - if clk_id != this.eval_libc_i32("CLOCK_REALTIME")? { + let tp = this.deref_operand(tp_op)?; + + let duration = if clk_id == this.eval_libc_i32("CLOCK_REALTIME")? { + system_time_to_duration(&SystemTime::now())? + } else if clk_id == this.eval_libc_i32("CLOCK_MONOTONIC")? { + // Absolute time does not matter, only relative time does, so we can just + // use our own time anchor here. + Instant::now().duration_since(this.machine.time_anchor) + } else { let einval = this.eval_libc("EINVAL")?; this.set_last_error(einval)?; return Ok(-1); - } + }; - let tp = this.deref_operand(tp_op)?; - - let duration = get_time()?; let tv_sec = duration.as_secs(); let tv_nsec = duration.subsec_nanos(); @@ -68,7 +68,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let tv = this.deref_operand(tv_op)?; - let duration = get_time()?; + let duration = system_time_to_duration(&SystemTime::now())?; let tv_sec = duration.as_secs(); let tv_usec = duration.subsec_micros(); diff --git a/tests/run-pass/clock.rs b/tests/run-pass/clock.rs deleted file mode 100644 index b4c3fa08fdc6..000000000000 --- a/tests/run-pass/clock.rs +++ /dev/null @@ -1,14 +0,0 @@ -// ignore-windows: TODO clock shims are not implemented on Windows -// compile-flags: -Zmiri-disable-isolation - -use std::time::SystemTime; - -fn main() { - let now1 = SystemTime::now(); - - // Do some work to make time pass. - for _ in 0..10 { drop(vec![42]); } - - let now2 = SystemTime::now(); - assert!(now2 > now1); -} diff --git a/tests/run-pass/time.rs b/tests/run-pass/time.rs new file mode 100644 index 000000000000..bbe8b4011dfa --- /dev/null +++ b/tests/run-pass/time.rs @@ -0,0 +1,18 @@ +// ignore-windows: TODO clock shims are not implemented on Windows +// compile-flags: -Zmiri-disable-isolation + +use std::time::{SystemTime, Instant}; + +fn main() { + let now1 = SystemTime::now(); + // Do some work to make time pass. + for _ in 0..10 { drop(vec![42]); } + let now2 = SystemTime::now(); + assert!(now2 > now1); + + let now1 = Instant::now(); + // Do some work to make time pass. + for _ in 0..10 { drop(vec![42]); } + let now2 = Instant::now(); + assert!(now2 > now1); +} From 04c937e2813c5da2b003b272bff26b9d1f9ffca3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 20 Mar 2020 15:11:54 +0100 Subject: [PATCH 2/3] assert platform in time shims --- src/shims/time.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/shims/time.rs b/src/shims/time.rs index c8807dd6ea84..2d812208fba1 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -21,6 +21,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.check_no_isolation("clock_gettime")?; + this.assert_platform("linux"); let clk_id = this.read_scalar(clk_id_op)?.to_i32()?; let tp = this.deref_operand(tp_op)?; @@ -58,6 +59,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); this.check_no_isolation("gettimeofday")?; + this.assert_platform("macos"); + // Using tz is obsolete and should always be null let tz = this.read_scalar(tz_op)?.not_undef()?; if !this.is_null(tz)? { From f430e544561a430f267c9fbde20962cef4702332 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 20 Mar 2020 15:54:41 +0100 Subject: [PATCH 3/3] implement mach_absolute_time for macOS --- src/shims/foreign_items/posix/macos.rs | 4 ++++ src/shims/fs.rs | 12 ++++++------ src/shims/time.rs | 21 +++++++++++++++++---- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/shims/foreign_items/posix/macos.rs b/src/shims/foreign_items/posix/macos.rs index 34661fb2383c..0bb4710769d9 100644 --- a/src/shims/foreign_items/posix/macos.rs +++ b/src/shims/foreign_items/posix/macos.rs @@ -66,6 +66,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let result = this.gettimeofday(args[0], args[1])?; this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } + "mach_absolute_time" => { + let result = this.mach_absolute_time()?; + this.write_scalar(Scalar::from_uint(result, dest.layout.size), dest)?; + } // Other shims "pthread_attr_get_np" => { diff --git a/src/shims/fs.rs b/src/shims/fs.rs index cb429109a4a5..a5aae5ed90c3 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -574,8 +574,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx buf_op: OpTy<'tcx, Tag>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("stat")?; this.assert_platform("macos", "stat"); + this.check_no_isolation("stat")?; // `stat` always follows symlinks. this.macos_stat_or_lstat(true, path_op, buf_op) } @@ -587,8 +587,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx buf_op: OpTy<'tcx, Tag>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("lstat")?; this.assert_platform("macos", "lstat"); + this.check_no_isolation("lstat")?; this.macos_stat_or_lstat(false, path_op, buf_op) } @@ -599,8 +599,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("fstat")?; this.assert_platform("macos", "fstat"); + this.check_no_isolation("fstat")?; let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -621,8 +621,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("statx")?; this.assert_platform("linux", "statx"); + this.check_no_isolation("statx")?; let statxbuf_scalar = this.read_scalar(statxbuf_op)?.not_undef()?; let pathname_scalar = this.read_scalar(pathname_op)?.not_undef()?; @@ -880,8 +880,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("readdir64_r")?; this.assert_platform("linux", "readdir64_r"); + this.check_no_isolation("readdir64_r")?; let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; @@ -967,8 +967,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - this.check_no_isolation("readdir_r")?; this.assert_platform("macos", "readdir_r"); + this.check_no_isolation("readdir_r")?; let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; diff --git a/src/shims/time.rs b/src/shims/time.rs index 2d812208fba1..b270c9770f80 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -1,4 +1,5 @@ use std::time::{Duration, SystemTime, Instant}; +use std::convert::TryFrom; use crate::stacked_borrows::Tag; use crate::*; @@ -12,7 +13,6 @@ pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Du impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - // Foreign function used by linux fn clock_gettime( &mut self, clk_id_op: OpTy<'tcx, Tag>, @@ -20,8 +20,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + this.assert_platform("linux", "clock_gettime"); this.check_no_isolation("clock_gettime")?; - this.assert_platform("linux"); let clk_id = this.read_scalar(clk_id_op)?.to_i32()?; let tp = this.deref_operand(tp_op)?; @@ -50,7 +50,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - // Foreign function used by generic unix (in particular macOS) + fn gettimeofday( &mut self, tv_op: OpTy<'tcx, Tag>, @@ -58,8 +58,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + this.assert_platform("macos", "gettimeofday"); this.check_no_isolation("gettimeofday")?; - this.assert_platform("macos"); // Using tz is obsolete and should always be null let tz = this.read_scalar(tz_op)?.not_undef()?; @@ -84,4 +84,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } + + fn mach_absolute_time(&self) -> InterpResult<'tcx, u64> { + let this = self.eval_context_ref(); + + this.assert_platform("macos", "mach_absolute_time"); + this.check_no_isolation("mach_absolute_time")?; + + // This returns a u64, with time units determined dynamically by `mach_timebase_info`. + // We return plain nanoseconds. + let duration = Instant::now().duration_since(this.machine.time_anchor); + u64::try_from(duration.as_nanos()) + .map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported").into()) + } }