1 use std::sync::atomic::{AtomicBool, Ordering}; 2 use std::sync::Arc; 3 use std::time::Duration; 4 5 /// A `SyncWaiter` is a handle to coordinate or execute some testing with respect to its 6 /// corresponding `Syncpoint`. 7 /// 8 /// A `SyncWaiter` corresponds to one `Syncpoint` and is created by its `wait_at`. A `SyncWaiter` 9 /// can only be waited at once, which is why both `wait_and_then` and `wait` consume the waiter. 10 pub struct SyncWaiter { 11 arrived: Arc<AtomicBool>, 12 proceed: Arc<AtomicBool>, 13 } 14 15 impl SyncWaiter { 16 /// Wait for the corresponding `Syncpoint` to be reached, then continue. wait(self)17 pub fn wait(self) { 18 self.wait_and_then(|| {}) 19 } 20 21 /// Wait for the corresponding `Syncpoint` to be reached, run the provided function, then 22 /// continue. This is useful for causing race conditions where a `Syncpoint` guarantees the 23 /// program under test has stopped at a location of interest, so the function provided to 24 /// `wait_and_then` is free to "race" with complete determinism. wait_and_then<U, F: FnOnce() -> U>(self, f: F) -> U25 pub fn wait_and_then<U, F: FnOnce() -> U>(self, f: F) -> U { 26 let resumption = self.pause(); 27 28 let res = f(); 29 30 resumption.resume(); 31 32 res 33 } 34 35 /// Wait for the corresponding `Syncpoint` to be reached, then return without permitting it to 36 /// continue. *If you do not resume from this `SyncWaiter` at some point you will likely 37 /// deadlock your test!* 38 #[must_use] pause(self) -> Self39 pub fn pause(self) -> Self { 40 while !self.arrived.load(Ordering::SeqCst) { 41 std::thread::sleep(Duration::from_millis(10)); 42 } 43 44 self 45 } 46 47 /// Resume this `SyncWaiter`, consuming it as can have no further effect. A `SyncWaiter` may be 48 /// resumed before it is reached, in which case this behaves similarly to having never called 49 /// `wait_at()` on the corresponding `Syncpoint`. resume(self)50 pub fn resume(self) { 51 self.proceed.store(true, Ordering::SeqCst); 52 } 53 } 54 55 /// A `Syncpoint` is a tool to coordinate testing at specific locations in Lucet. 56 /// 57 /// When `lock_testpoints` are compiled in, lucet-runtime will `check` unconditionally, where by 58 /// default this is functionally a no-op. For `Syncpoint`s a test has indicated interest in, with 59 /// `wait_at`, `check` becomes blocking until the test allows continuation through the 60 /// corresponding `SyncWaiter` that `wait_at` constructed. This allows tests to be written that 61 /// check race conditions in a deterministic manner: the runtime can execute "enter a guest", be 62 /// blocked at a Syncpoint just before guest entry, and a test that termination is correct in this 63 /// otherwise-unlikely circumstance can be performed. 64 pub struct Syncpoint { 65 arrived: Arc<AtomicBool>, 66 proceed: Arc<AtomicBool>, 67 } 68 69 impl Syncpoint { new() -> Self70 pub fn new() -> Self { 71 Self { 72 arrived: Arc::new(AtomicBool::new(false)), 73 proceed: Arc::new(AtomicBool::new(true)), 74 } 75 } 76 wait_at(&self) -> SyncWaiter77 pub fn wait_at(&self) -> SyncWaiter { 78 let arrived = Arc::clone(&self.arrived); 79 let proceed = Arc::clone(&self.proceed); 80 81 proceed.store(false, Ordering::SeqCst); 82 83 SyncWaiter { arrived, proceed } 84 } 85 check(&self)86 pub fn check(&self) { 87 self.arrived.store(true, Ordering::SeqCst); 88 89 while !self.proceed.load(Ordering::SeqCst) { 90 std::thread::sleep(Duration::from_millis(10)); 91 } 92 } 93 } 94 95 pub struct LockTestpoints { 96 pub instance_after_clearing_current_instance: Syncpoint, 97 pub instance_entering_guest_after_domain_change: Syncpoint, 98 pub instance_entering_guest_before_domain_change: Syncpoint, 99 pub instance_entering_hostcall_after_domain_change: Syncpoint, 100 pub instance_entering_hostcall_before_domain_change: Syncpoint, 101 pub instance_exiting_guest_after_domain_change: Syncpoint, 102 pub instance_exiting_guest_before_acquiring_terminable: Syncpoint, 103 pub instance_exiting_guest_without_terminable: Syncpoint, 104 pub instance_exiting_hostcall_after_domain_change: Syncpoint, 105 pub instance_exiting_hostcall_before_domain_change: Syncpoint, 106 pub kill_switch_after_acquiring_domain_lock: Syncpoint, 107 pub kill_switch_after_acquiring_termination: Syncpoint, 108 pub kill_switch_after_forbidden_termination: Syncpoint, 109 pub kill_switch_after_guest_alarm: Syncpoint, 110 pub kill_switch_after_releasing_domain: Syncpoint, 111 pub kill_switch_before_disabling_termination: Syncpoint, 112 pub kill_switch_before_guest_alarm: Syncpoint, 113 pub kill_switch_before_guest_termination: Syncpoint, 114 pub kill_switch_before_hostcall_termination: Syncpoint, 115 pub kill_switch_before_releasing_domain: Syncpoint, 116 pub kill_switch_before_terminated_termination: Syncpoint, 117 pub signal_handler_after_disabling_termination: Syncpoint, 118 pub signal_handler_after_unable_to_disable_termination: Syncpoint, 119 pub signal_handler_before_checking_alarm: Syncpoint, 120 pub signal_handler_before_disabling_termination: Syncpoint, 121 pub signal_handler_before_returning: Syncpoint, 122 } 123 124 impl LockTestpoints { new() -> Self125 pub fn new() -> Self { 126 LockTestpoints { 127 instance_after_clearing_current_instance: Syncpoint::new(), 128 instance_entering_guest_after_domain_change: Syncpoint::new(), 129 instance_entering_guest_before_domain_change: Syncpoint::new(), 130 instance_entering_hostcall_after_domain_change: Syncpoint::new(), 131 instance_entering_hostcall_before_domain_change: Syncpoint::new(), 132 instance_exiting_guest_after_domain_change: Syncpoint::new(), 133 instance_exiting_guest_before_acquiring_terminable: Syncpoint::new(), 134 instance_exiting_guest_without_terminable: Syncpoint::new(), 135 instance_exiting_hostcall_after_domain_change: Syncpoint::new(), 136 instance_exiting_hostcall_before_domain_change: Syncpoint::new(), 137 kill_switch_after_acquiring_domain_lock: Syncpoint::new(), 138 kill_switch_after_acquiring_termination: Syncpoint::new(), 139 kill_switch_after_forbidden_termination: Syncpoint::new(), 140 kill_switch_after_guest_alarm: Syncpoint::new(), 141 kill_switch_after_releasing_domain: Syncpoint::new(), 142 kill_switch_before_disabling_termination: Syncpoint::new(), 143 kill_switch_before_guest_alarm: Syncpoint::new(), 144 kill_switch_before_guest_termination: Syncpoint::new(), 145 kill_switch_before_hostcall_termination: Syncpoint::new(), 146 kill_switch_before_releasing_domain: Syncpoint::new(), 147 kill_switch_before_terminated_termination: Syncpoint::new(), 148 signal_handler_after_disabling_termination: Syncpoint::new(), 149 signal_handler_after_unable_to_disable_termination: Syncpoint::new(), 150 signal_handler_before_checking_alarm: Syncpoint::new(), 151 signal_handler_before_disabling_termination: Syncpoint::new(), 152 signal_handler_before_returning: Syncpoint::new(), 153 } 154 } 155 } 156