reverie/src/subscription.rs (196 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ use reverie_syscalls::Sysno; use bitflags::bitflags; use bitvec::bitvec; use bitvec::vec::BitVec; // The maximum number of syscalls to hold in our bitvec. // // FIXME: This should come from the `syscall` crate instead. const MAX_SYSCALLS: usize = 512; bitflags! { #[derive(Default)] struct Instructions: u32 { const CPUID = 1; const RDTSC = 2; } } /// A set of events to subscribe to. #[derive(Default, Clone, Eq, PartialEq)] pub struct Subscription { instructions: Instructions, // TODO: Use a BitArray with bitvec >=0.18 syscalls: BitVec, } impl Subscription { /// Don't receive any events. pub fn none() -> Self { Subscription { instructions: Instructions::empty(), syscalls: bitvec![0; MAX_SYSCALLS], } } /// Subscribe to all events. pub fn all() -> Self { Subscription { instructions: Instructions::CPUID | Instructions::RDTSC, syscalls: bitvec![1; MAX_SYSCALLS], } } /// Subscribe to all sycall events (but not instruction events). pub fn all_syscalls() -> Self { Subscription { instructions: Instructions::empty(), syscalls: bitvec![1; MAX_SYSCALLS], } } /// Enable interception of the `rdtsc` instruction. #[inline] pub fn rdtsc(&mut self) -> &mut Self { self.instructions.insert(Instructions::RDTSC); self } /// Enable interception of the `cpuid` instruction. #[inline] pub fn cpuid(&mut self) -> &mut Self { self.instructions.insert(Instructions::CPUID); self } /// Returns true if we're subscribed to RDTSC events. #[inline] pub fn has_rdtsc(&self) -> bool { self.instructions.contains(Instructions::RDTSC) } /// Returns true if we're subscribed to CPUID events. #[inline] pub fn has_cpuid(&self) -> bool { self.instructions.contains(Instructions::CPUID) } /// Enables or disables a single syscall. #[inline] pub fn set(&mut self, syscall: Sysno, enabled: bool) -> &mut Self { self.syscalls.set(syscall as i32 as usize, enabled); self } /// Enables a single syscall. #[inline] pub fn syscall(&mut self, syscall: Sysno) -> &mut Self { self.set(syscall, true) } /// Enables multiple syscalls. pub fn syscalls<I>(&mut self, syscalls: I) -> &mut Self where I: IntoIterator<Item = Sysno>, { for syscall in syscalls { self.syscall(syscall); } self } /// Disables a single syscall. #[inline] pub fn disable_syscall(&mut self, syscall: Sysno) -> &mut Self { self.set(syscall, false) } /// Disables multiple syscalls. pub fn disable_syscalls<I>(&mut self, syscalls: I) -> &mut Self where I: IntoIterator<Item = Sysno>, { for syscall in syscalls { self.disable_syscall(syscall); } self } /// Iterates over the set of syscalls that are enabled. pub fn iter_syscalls(&self) -> impl Iterator<Item = Sysno> + '_ { // With bitvec >=0.20, this becomes a lot simpler: //self.syscalls.iter_ones().filter_map(Sysno::new) self.syscalls.iter().enumerate().filter_map( |(i, is_set)| { if *is_set { Sysno::new(i) } else { None } }, ) } /// Iterates over the set of syscalls that are disabled. pub fn iter_disabled_syscalls(&self) -> impl Iterator<Item = Sysno> + '_ { // With bitvec >=0.20, this becomes a lot simpler: //self.syscalls.iter_zeros().filter_map(Sysno::new) self.syscalls.iter().enumerate().filter_map( |(i, is_set)| { if !*is_set { Sysno::new(i) } else { None } }, ) } } impl core::ops::BitOr for Subscription { type Output = Self; fn bitor(mut self, rhs: Self) -> Self::Output { self |= rhs; self } } impl core::ops::BitOrAssign for Subscription { fn bitor_assign(&mut self, rhs: Self) { self.instructions |= rhs.instructions; self.syscalls |= rhs.syscalls; } } impl core::ops::BitOrAssign<Sysno> for Subscription { fn bitor_assign(&mut self, syscall: Sysno) { self.syscalls.set(syscall as i32 as usize, true); } } impl Extend<Sysno> for Subscription { fn extend<I: IntoIterator<Item = Sysno>>(&mut self, iter: I) { for syscall in iter { *self |= syscall; } } } impl FromIterator<Sysno> for Subscription { fn from_iter<I: IntoIterator<Item = Sysno>>(iter: I) -> Self { let mut s = Self::none(); s.extend(iter); s } } impl core::fmt::Debug for Subscription { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { let syscalls: Vec<_> = self.iter_syscalls().collect(); f.debug_struct("Subscription") .field("instructions", &self.instructions) .field("syscalls", &syscalls) .finish() } } #[cfg(test)] mod tests { use super::*; #[test] fn smoke() { let mut s1: Subscription = [Sysno::open, Sysno::read, Sysno::write] .iter() .copied() .collect(); s1 |= Sysno::open; let mut s2 = Subscription::none(); s2 |= s1.clone(); s2 |= Sysno::open; assert_eq!( s1.iter_syscalls().collect::<Vec<_>>(), [Sysno::read, Sysno::write, Sysno::open,] ); assert_eq!( s2.iter_syscalls().collect::<Vec<_>>(), [Sysno::read, Sysno::write, Sysno::open,] ); } #[test] fn disabled_syscalls() { let mut sub = Subscription::all(); assert!(sub.iter_disabled_syscalls().next().is_none()); sub.set(Sysno::open, false); sub.set(Sysno::read, false); assert_eq!( sub.iter_disabled_syscalls().collect::<Vec<_>>(), [Sysno::read, Sysno::open] ); } #[test] fn compose() { let a = Subscription::from_iter([Sysno::open, Sysno::read]); let b = Subscription::from_iter([Sysno::read, Sysno::close]); let c = a | b; assert_eq!( c.iter_syscalls().collect::<Vec<_>>(), [Sysno::read, Sysno::open, Sysno::close] ); } }