use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
use std::collections::BTreeMap;
use std::ffi::{CStr, CString, OsString};
use std::net::{Ipv4Addr, SocketAddrV4};
use std::ops::{Deref, DerefMut};
use std::os::unix::prelude::OsStringExt;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use atomic_refcell::AtomicRefCell;
use linux_api::signal::{siginfo_t, Signal};
use log::{debug, trace};
use logger::LogLevel;
use once_cell::unsync::OnceCell;
use rand::SeedableRng;
use rand_xoshiro::Xoshiro256PlusPlus;
use shadow_shim_helper_rs::emulated_time::EmulatedTime;
use shadow_shim_helper_rs::explicit_drop::ExplicitDropper;
use shadow_shim_helper_rs::rootedcell::cell::RootedCell;
use shadow_shim_helper_rs::rootedcell::rc::RootedRc;
use shadow_shim_helper_rs::rootedcell::refcell::RootedRefCell;
use shadow_shim_helper_rs::rootedcell::Root;
use shadow_shim_helper_rs::shim_shmem::{HostShmem, HostShmemProtected, ManagerShmem};
use shadow_shim_helper_rs::simulation_time::SimulationTime;
use shadow_shim_helper_rs::HostId;
use shadow_shmem::allocator::ShMemBlock;
use shadow_tsc::Tsc;
use vasi_sync::scmutex::SelfContainedMutexGuard;
use crate::core::configuration::{ProcessFinalState, QDiscMode};
use crate::core::sim_config::PcapConfig;
use crate::core::work::event::{Event, EventData};
use crate::core::work::event_queue::EventQueue;
use crate::core::work::task::TaskRef;
use crate::core::worker::Worker;
use crate::cshadow;
use crate::host::descriptor::socket::abstract_unix_ns::AbstractUnixNamespace;
use crate::host::descriptor::socket::inet::InetSocket;
use crate::host::futex_table::FutexTable;
use crate::host::network::interface::{FifoPacketPriority, NetworkInterface, PcapOptions};
use crate::host::network::namespace::NetworkNamespace;
use crate::host::process::Process;
use crate::host::thread::{Thread, ThreadId};
use crate::network::relay::{RateLimit, Relay};
use crate::network::router::Router;
use crate::network::PacketDevice;
use crate::utility;
#[cfg(feature = "perf_timers")]
use crate::utility::perf_timer::PerfTimer;
pub struct HostParameters {
pub id: HostId,
pub node_seed: u64,
pub hostname: CString,
pub node_id: u32,
pub ip_addr: libc::in_addr_t,
pub sim_end_time: EmulatedTime,
pub requested_bw_down_bits: u64,
pub requested_bw_up_bits: u64,
pub cpu_frequency: u64,
pub cpu_threshold: Option<SimulationTime>,
pub cpu_precision: Option<SimulationTime>,
pub log_level: LogLevel,
pub pcap_config: Option<PcapConfig>,
pub qdisc: QDiscMode,
pub init_sock_recv_buf_size: u64,
pub autotune_recv_buf: bool,
pub init_sock_send_buf_size: u64,
pub autotune_send_buf: bool,
pub native_tsc_frequency: u64,
pub model_unblocked_syscall_latency: bool,
pub max_unapplied_cpu_latency: SimulationTime,
pub unblocked_syscall_latency: SimulationTime,
pub unblocked_vdso_latency: SimulationTime,
pub strace_logging_options: Option<FmtOptions>,
pub shim_log_level: LogLevel,
pub use_new_tcp: bool,
pub use_mem_mapper: bool,
pub use_syscall_counters: bool,
}
use super::cpu::Cpu;
use super::process::ProcessId;
use super::syscall::formatter::FmtOptions;
#[derive(Debug, Clone)]
pub struct HostInfo {
pub id: HostId,
pub name: String,
pub default_ip: Ipv4Addr,
pub log_level: Option<log::LevelFilter>,
}
pub struct Host {
info: OnceCell<Arc<HostInfo>>,
root: Root,
event_queue: Arc<Mutex<EventQueue>>,
random: RefCell<Xoshiro256PlusPlus>,
router: RefCell<Router>,
relay_inet_out: Arc<Relay>,
relay_inet_in: Arc<Relay>,
relay_loopback: Arc<Relay>,
futex_table: RefCell<FutexTable>,
#[cfg(feature = "perf_timers")]
execution_timer: RefCell<PerfTimer>,
pub params: HostParameters,
cpu: RefCell<Cpu>,
net_ns: NetworkNamespace,
data_dir_path: PathBuf,
data_dir_path_cstring: CString,
thread_id_counter: Cell<libc::pid_t>,
event_id_counter: Cell<u64>,
packet_id_counter: Cell<u64>,
determinism_sequence_counter: Cell<u64>,
packet_priority_counter: Cell<FifoPacketPriority>,
processes: RefCell<BTreeMap<ProcessId, RootedRc<RootedRefCell<Process>>>>,
tsc: Tsc,
shim_shmem_lock:
RefCell<Option<UnsafeCell<SelfContainedMutexGuard<'static, HostShmemProtected>>>>,
shim_shmem: UnsafeCell<ShMemBlock<'static, HostShmem>>,
in_notify_socket_has_packets: RootedCell<bool>,
preload_paths: Arc<Vec<PathBuf>>,
}
impl crate::utility::IsSend for Host {}
impl std::fmt::Debug for Host {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Host")
.field("info", &self.info)
.finish_non_exhaustive()
}
}
impl Host {
pub fn new(
params: HostParameters,
host_root_path: &Path,
raw_cpu_freq_khz: u64,
manager_shmem: &ShMemBlock<ManagerShmem>,
preload_paths: Arc<Vec<PathBuf>>,
) -> Self {
#[cfg(feature = "perf_timers")]
let execution_timer = RefCell::new(PerfTimer::new());
let root = Root::new();
let random = RefCell::new(Xoshiro256PlusPlus::seed_from_u64(params.node_seed));
let cpu = RefCell::new(Cpu::new(
params.cpu_frequency,
raw_cpu_freq_khz,
params.cpu_threshold,
params.cpu_precision,
));
let data_dir_path = Self::make_data_dir_path(¶ms.hostname, host_root_path);
let data_dir_path_cstring = utility::pathbuf_to_nul_term_cstring(data_dir_path.clone());
let host_shmem = HostShmem::new(
params.id,
params.model_unblocked_syscall_latency,
params.max_unapplied_cpu_latency,
params.unblocked_syscall_latency,
params.unblocked_vdso_latency,
nix::unistd::getpid().as_raw(),
params.native_tsc_frequency,
params.shim_log_level,
manager_shmem,
);
let shim_shmem = UnsafeCell::new(shadow_shmem::allocator::shmalloc(host_shmem));
let thread_id_counter = Cell::new(1000);
let event_id_counter = Cell::new(0);
let packet_id_counter = Cell::new(0);
let determinism_sequence_counter = Cell::new(0);
let packet_priority_counter = Cell::new(1);
let tsc = Tsc::new(params.native_tsc_frequency);
std::fs::create_dir_all(&data_dir_path).unwrap();
let public_ip: Ipv4Addr = u32::from_be(params.ip_addr).into();
let pcap_options = params.pcap_config.as_ref().map(|x| PcapOptions {
path: data_dir_path.clone(),
capture_size_bytes: x.capture_size.try_into().unwrap(),
});
let net_ns = NetworkNamespace::new(params.id, public_ip, pcap_options, params.qdisc);
let router = Router::new(Ipv4Addr::UNSPECIFIED);
let relay_inet_out = Relay::new(
RateLimit::BytesPerSecond(params.requested_bw_up_bits / 8),
net_ns.internet.borrow().get_address(),
);
let relay_inet_in = Relay::new(
RateLimit::BytesPerSecond(params.requested_bw_down_bits / 8),
router.get_address(),
);
let relay_loopback = Relay::new(
RateLimit::Unlimited,
net_ns.localhost.borrow().get_address(),
);
let in_notify_socket_has_packets = RootedCell::new(&root, false);
let res = Self {
info: OnceCell::new(),
root,
event_queue: Arc::new(Mutex::new(EventQueue::new())),
params,
router: RefCell::new(router),
relay_inet_out: Arc::new(relay_inet_out),
relay_inet_in: Arc::new(relay_inet_in),
relay_loopback: Arc::new(relay_loopback),
futex_table: RefCell::new(FutexTable::new()),
random,
shim_shmem,
shim_shmem_lock: RefCell::new(None),
cpu,
net_ns,
data_dir_path,
data_dir_path_cstring,
thread_id_counter,
event_id_counter,
packet_id_counter,
packet_priority_counter,
determinism_sequence_counter,
tsc,
processes: RefCell::new(BTreeMap::new()),
#[cfg(feature = "perf_timers")]
execution_timer,
in_notify_socket_has_packets,
preload_paths,
};
res.stop_execution_timer();
debug!(
concat!(
"Setup host id '{:?}'",
" name '{name}'",
" with seed {seed},",
" {bw_up_kiBps} bwUpKiBps,",
" {bw_down_kiBps} bwDownKiBps,",
" {init_sock_send_buf_size} initSockSendBufSize,",
" {init_sock_recv_buf_size} initSockRecvBufSize, ",
" {cpu_frequency:?} cpuFrequency, ",
" {cpu_threshold:?} cpuThreshold, ",
" {cpu_precision:?} cpuPrecision"
),
res.id(),
name = res.info().name,
seed = res.params.node_seed,
bw_up_kiBps = res.bw_up_kiBps(),
bw_down_kiBps = res.bw_down_kiBps(),
init_sock_send_buf_size = res.params.init_sock_send_buf_size,
init_sock_recv_buf_size = res.params.init_sock_recv_buf_size,
cpu_frequency = res.params.cpu_frequency,
cpu_threshold = res.params.cpu_threshold,
cpu_precision = res.params.cpu_precision,
);
res
}
pub fn root(&self) -> &Root {
&self.root
}
fn make_data_dir_path(hostname: &CStr, host_root_path: &Path) -> PathBuf {
let hostname: OsString = { OsString::from_vec(hostname.to_bytes().to_vec()) };
let mut data_dir_path = PathBuf::new();
data_dir_path.push(host_root_path);
data_dir_path.push(&hostname);
data_dir_path
}
pub fn data_dir_path(&self) -> &Path {
&self.data_dir_path
}
pub fn add_application(
&self,
start_time: SimulationTime,
shutdown_time: Option<SimulationTime>,
shutdown_signal: nix::sys::signal::Signal,
plugin_name: CString,
plugin_path: CString,
argv: Vec<CString>,
envv: Vec<CString>,
pause_for_debugging: bool,
expected_final_state: ProcessFinalState,
) {
debug_assert!(shutdown_time.is_none() || shutdown_time.unwrap() > start_time);
let task = TaskRef::new(move |host| {
let envv = envv.clone();
let argv = argv.clone();
let process = Process::spawn(
host,
plugin_name.clone(),
&plugin_path,
argv,
envv,
pause_for_debugging,
host.params.strace_logging_options,
expected_final_state,
)
.unwrap_or_else(|e| panic!("Failed to initialize application {plugin_name:?}: {e:?}"));
let (process_id, thread_id) = {
let process = process.borrow(host.root());
(process.id(), process.thread_group_leader_id())
};
host.processes.borrow_mut().insert(process_id, process);
if let Some(shutdown_time) = shutdown_time {
let task = TaskRef::new(move |host| {
let Some(process) = host.process_borrow(process_id) else {
debug!("Can't send shutdown signal to process {process_id}; it no longer exists");
return;
};
let process = process.borrow(host.root());
let siginfo_t = siginfo_t::new_for_kill(
Signal::try_from(shutdown_signal as i32).unwrap(),
1,
0,
);
process.signal(host, None, &siginfo_t);
});
host.schedule_task_at_emulated_time(
task,
EmulatedTime::SIMULATION_START + shutdown_time,
);
}
host.resume(process_id, thread_id);
});
self.schedule_task_at_emulated_time(task, EmulatedTime::SIMULATION_START + start_time);
}
pub fn add_and_schedule_forked_process(
&self,
host: &Host,
process: RootedRc<RootedRefCell<Process>>,
) {
let (process_id, thread_id) = {
let process = process.borrow(&self.root);
(process.id(), process.thread_group_leader_id())
};
host.processes.borrow_mut().insert(process_id, process);
let task = TaskRef::new(move |host| {
host.resume(process_id, thread_id);
});
self.schedule_task_with_delay(task, SimulationTime::ZERO);
}
pub fn resume(&self, pid: ProcessId, tid: ThreadId) {
let Some(processrc) = self
.process_borrow(pid)
.map(|p| RootedRc::clone(&p, &self.root))
else {
trace!("{pid:?} doesn't exist");
return;
};
let processrc = ExplicitDropper::new(processrc, |p| {
p.explicit_drop_recursive(&self.root, self);
});
let died;
let is_orphan;
{
Worker::set_active_process(&processrc);
let process = processrc.borrow(self.root());
process.resume(self, tid);
Worker::clear_active_process();
let zombie_state = process.borrow_as_zombie();
if let Some(zombie) = zombie_state {
died = true;
is_orphan = zombie.reaper(self).is_none();
} else {
died = false;
is_orphan = false;
}
};
if !died {
return;
}
let mut orphaned_zombie_pids: Vec<ProcessId> = self
.processes
.borrow()
.iter()
.filter_map(|(other_pid, processrc)| {
let process = processrc.borrow(&self.root);
if process.parent_id() != pid {
return None;
}
process.set_parent_id(ProcessId::INIT);
let Some(z) = process.borrow_as_zombie() else {
return None;
};
if z.reaper(self).is_some() {
None
} else {
Some(*other_pid)
}
})
.collect();
debug_assert!(died);
if is_orphan {
orphaned_zombie_pids.push(pid);
}
let mut processes = self.processes.borrow_mut();
for pid in orphaned_zombie_pids {
trace!("Dropping orphan zombie process {pid:?}");
let processrc = processes.remove(&pid).unwrap();
RootedRc::explicit_drop_recursive(processrc, &self.root, self);
}
}
#[track_caller]
pub fn process_borrow(
&self,
id: ProcessId,
) -> Option<impl Deref<Target = RootedRc<RootedRefCell<Process>>> + '_> {
Ref::filter_map(self.processes.borrow(), |processes| processes.get(&id)).ok()
}
#[track_caller]
pub fn process_remove(&self, id: ProcessId) -> Option<RootedRc<RootedRefCell<Process>>> {
self.processes.borrow_mut().remove(&id)
}
#[track_caller]
pub fn processes_borrow(
&self,
) -> impl Deref<Target = BTreeMap<ProcessId, RootedRc<RootedRefCell<Process>>>> + '_ {
self.processes.borrow()
}
pub fn cpu_borrow(&self) -> impl Deref<Target = Cpu> + '_ {
self.cpu.borrow()
}
pub fn cpu_borrow_mut(&self) -> impl DerefMut<Target = Cpu> + '_ {
self.cpu.borrow_mut()
}
pub fn info(&self) -> &Arc<HostInfo> {
self.info.get_or_init(|| {
Arc::new(HostInfo {
id: self.id(),
name: self.params.hostname.to_str().unwrap().to_owned(),
default_ip: self.default_ip(),
log_level: self.log_level(),
})
})
}
pub fn id(&self) -> HostId {
self.params.id
}
pub fn name(&self) -> &str {
&self.info().name
}
pub fn default_ip(&self) -> Ipv4Addr {
self.net_ns.default_ip
}
pub fn abstract_unix_namespace(
&self,
) -> impl Deref<Target = Arc<AtomicRefCell<AbstractUnixNamespace>>> + '_ {
&self.net_ns.unix
}
pub fn log_level(&self) -> Option<log::LevelFilter> {
let level = self.params.log_level;
log_c2rust::c_to_rust_log_level(level).map(|l| l.to_level_filter())
}
#[track_caller]
pub fn upstream_router_borrow_mut(&self) -> impl DerefMut<Target = Router> + '_ {
self.router.borrow_mut()
}
#[track_caller]
pub fn network_namespace_borrow(&self) -> impl Deref<Target = NetworkNamespace> + '_ {
&self.net_ns
}
#[track_caller]
pub fn futextable_borrow(&self) -> impl Deref<Target = FutexTable> + '_ {
self.futex_table.borrow()
}
#[track_caller]
pub fn futextable_borrow_mut(&self) -> impl DerefMut<Target = FutexTable> + '_ {
self.futex_table.borrow_mut()
}
#[allow(non_snake_case)]
pub fn bw_up_kiBps(&self) -> u64 {
self.params.requested_bw_up_bits / (8 * 1024)
}
#[allow(non_snake_case)]
pub fn bw_down_kiBps(&self) -> u64 {
self.params.requested_bw_down_bits / (8 * 1024)
}
pub fn interface_borrow_mut(
&self,
addr: Ipv4Addr,
) -> Option<impl DerefMut<Target = NetworkInterface> + '_> {
self.net_ns.interface_borrow_mut(addr)
}
pub fn interface_borrow(
&self,
addr: Ipv4Addr,
) -> Option<impl Deref<Target = NetworkInterface> + '_> {
self.net_ns.interface_borrow(addr)
}
#[track_caller]
pub fn random_mut(&self) -> impl DerefMut<Target = Xoshiro256PlusPlus> + '_ {
self.random.borrow_mut()
}
pub fn get_new_event_id(&self) -> u64 {
let res = self.event_id_counter.get();
self.event_id_counter.set(res + 1);
res
}
pub fn get_new_thread_id(&self) -> ThreadId {
let res = self.thread_id_counter.get();
self.thread_id_counter.set(res + 1);
res.try_into().unwrap()
}
pub fn get_new_packet_id(&self) -> u64 {
let res = self.packet_id_counter.get();
self.packet_id_counter.set(res + 1);
res
}
pub fn get_next_deterministic_sequence_value(&self) -> u64 {
let res = self.determinism_sequence_counter.get();
self.determinism_sequence_counter.set(res + 1);
res
}
pub fn get_next_packet_priority(&self) -> FifoPacketPriority {
let res = self.packet_priority_counter.get();
self.packet_priority_counter
.set(res.checked_add(1).unwrap());
res
}
pub fn continue_execution_timer(&self) {
#[cfg(feature = "perf_timers")]
self.execution_timer.borrow_mut().start();
}
pub fn stop_execution_timer(&self) {
#[cfg(feature = "perf_timers")]
self.execution_timer.borrow_mut().stop();
}
pub fn schedule_task_at_emulated_time(&self, task: TaskRef, t: EmulatedTime) -> bool {
let event = Event::new_local(task, t, self);
self.push_local_event(event)
}
pub fn schedule_task_with_delay(&self, task: TaskRef, t: SimulationTime) -> bool {
self.schedule_task_at_emulated_time(task, Worker::current_time().unwrap() + t)
}
pub fn event_queue(&self) -> &Arc<Mutex<EventQueue>> {
&self.event_queue
}
pub fn push_local_event(&self, event: Event) -> bool {
if event.time() >= self.params.sim_end_time {
return false;
}
self.event_queue.lock().unwrap().push(event);
true
}
pub fn shutdown(&self) {
self.continue_execution_timer();
debug!("shutting down host {}", self.name());
self.net_ns.cleanup();
assert!(self.processes.borrow().is_empty());
self.stop_execution_timer();
#[cfg(feature = "perf_timers")]
debug!(
"host '{}' has been shut down, total execution time was {:?}",
self.name(),
self.execution_timer.borrow().elapsed()
);
}
pub fn free_all_applications(&self) {
trace!("start freeing applications for host '{}'", self.name());
let processes = std::mem::take(&mut *self.processes.borrow_mut());
for (_id, processrc) in processes.into_iter() {
let processrc = ExplicitDropper::new(processrc, |p| {
p.explicit_drop_recursive(self.root(), self);
});
Worker::set_active_process(&processrc);
let process = processrc.borrow(self.root());
process.stop(self);
Worker::clear_active_process();
process.set_parent_id(ProcessId::INIT);
}
trace!("done freeing application for host '{}'", self.name());
}
pub fn execute(&self, until: EmulatedTime) {
loop {
let mut event = {
let mut event_queue = self.event_queue.lock().unwrap();
match event_queue.next_event_time() {
Some(t) if t < until => {}
_ => break,
};
event_queue.pop().unwrap()
};
{
let mut cpu = self.cpu.borrow_mut();
cpu.update_time(event.time());
let cpu_delay = cpu.delay();
if cpu_delay > SimulationTime::ZERO {
trace!(
"event blocked on CPU, rescheduled for {:?} from now",
cpu_delay
);
event.set_time(event.time() + cpu_delay);
self.push_local_event(event);
continue;
}
}
Worker::set_current_time(event.time());
self.continue_execution_timer();
match event.data() {
EventData::Packet(data) => {
self.upstream_router_borrow_mut()
.route_incoming_packet(data.into());
self.notify_router_has_packets();
}
EventData::Local(data) => TaskRef::from(data).execute(self),
}
self.stop_execution_timer();
Worker::clear_current_time();
}
}
pub fn next_event_time(&self) -> Option<EmulatedTime> {
self.event_queue.lock().unwrap().next_event_time()
}
pub fn shim_shmem(&self) -> &ShMemBlock<'static, HostShmem> {
unsafe { &*self.shim_shmem.get() }
}
pub fn thread_cloned_rc(
&self,
virtual_tid: ThreadId,
) -> Option<RootedRc<RootedRefCell<Thread>>> {
for process in self.processes.borrow().values() {
let process = process.borrow(self.root());
if let Some(thread) = process.thread_borrow(virtual_tid) {
return Some(RootedRc::clone(&*thread, self.root()));
};
}
None
}
pub fn has_thread(&self, virtual_tid: ThreadId) -> bool {
for process in self.processes.borrow().values() {
let process = process.borrow(self.root());
if process.thread_borrow(virtual_tid).is_some() {
return true;
}
}
false
}
pub fn lock_shmem(&self) {
let shim_shmem: &'static ShMemBlock<HostShmem> =
unsafe { self.shim_shmem.get().as_ref().unwrap() };
let lock = shim_shmem.protected().lock();
let prev = self
.shim_shmem_lock
.borrow_mut()
.replace(UnsafeCell::new(lock));
assert!(prev.is_none());
}
pub fn unlock_shmem(&self) {
let prev = self.shim_shmem_lock.borrow_mut().take();
assert!(prev.is_some());
}
pub fn shim_shmem_lock_borrow(&self) -> Option<impl Deref<Target = HostShmemProtected> + '_> {
Ref::filter_map(self.shim_shmem_lock.borrow(), |l| {
l.as_ref().map(|l| {
let guard = unsafe { &*l.get() };
guard.deref()
})
})
.ok()
}
pub fn shim_shmem_lock_borrow_mut(
&self,
) -> Option<impl DerefMut<Target = HostShmemProtected> + '_> {
RefMut::filter_map(self.shim_shmem_lock.borrow_mut(), |l| {
l.as_ref().map(|l| {
let guard = unsafe { &mut *l.get() };
guard.deref_mut()
})
})
.ok()
}
pub fn tsc(&self) -> &Tsc {
&self.tsc
}
pub fn get_packet_device(&self, address: Ipv4Addr) -> Ref<dyn PacketDevice> {
if address == Ipv4Addr::LOCALHOST {
self.net_ns.localhost.borrow()
} else if address == self.default_ip() {
self.net_ns.internet.borrow()
} else {
self.router.borrow()
}
}
pub fn notify_router_has_packets(&self) {
self.relay_inet_in.notify(self);
}
pub fn notify_socket_has_packets(&self, addr: Ipv4Addr, socket: &InetSocket) {
if self.in_notify_socket_has_packets.replace(&self.root, true) {
panic!("Recursively calling host.notify_socket_has_packets()");
}
if let Some(iface) = self.interface_borrow(addr) {
iface.add_data_source(socket);
match addr {
Ipv4Addr::LOCALHOST => self.relay_loopback.notify(self),
_ => self.relay_inet_out.notify(self),
};
}
self.in_notify_socket_has_packets.set(&self.root, false);
}
pub fn process_session_id_of_group_id(&self, group_id: ProcessId) -> Option<ProcessId> {
let processes = self.processes.borrow();
for processrc in processes.values() {
let process = processrc.borrow(&self.root);
if process.group_id() == group_id {
return Some(process.session_id());
}
}
None
}
pub fn preload_paths(&self) -> &[PathBuf] {
&self.preload_paths
}
}
impl Drop for Host {
fn drop(&mut self) {
assert!(self.shim_shmem_lock.borrow().is_none());
}
}
mod export {
use std::{os::raw::c_char, time::Duration};
use libc::{in_addr_t, in_port_t};
use rand::{Rng, RngCore};
use shadow_shim_helper_rs::shim_shmem;
use super::*;
use crate::cshadow::{CEmulatedTime, CSimulationTime};
#[no_mangle]
pub unsafe extern "C-unwind" fn host_execute(hostrc: *const Host, until: CEmulatedTime) {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
let until = EmulatedTime::from_c_emutime(until).unwrap();
hostrc.execute(until)
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_nextEventTime(hostrc: *const Host) -> CEmulatedTime {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
EmulatedTime::to_c_emutime(hostrc.next_event_time())
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getNewPacketID(hostrc: *const Host) -> u64 {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.get_new_packet_id()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_freeAllApplications(hostrc: *const Host) {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.free_all_applications()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getID(hostrc: *const Host) -> HostId {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.id()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getTsc(host: *const Host) -> *const Tsc {
let hostrc = unsafe { host.as_ref().unwrap() };
hostrc.tsc()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getName(hostrc: *const Host) -> *const c_char {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.params.hostname.as_ptr()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getDefaultIP(hostrc: *const Host) -> in_addr_t {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
let ip = hostrc.default_ip();
u32::from(ip).to_be()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getNextPacketPriority(
hostrc: *const Host,
) -> FifoPacketPriority {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.get_next_packet_priority()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_autotuneReceiveBuffer(hostrc: *const Host) -> bool {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.params.autotune_recv_buf
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_autotuneSendBuffer(hostrc: *const Host) -> bool {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.params.autotune_send_buf
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getConfiguredRecvBufSize(hostrc: *const Host) -> u64 {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.params.init_sock_recv_buf_size
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getConfiguredSendBufSize(hostrc: *const Host) -> u64 {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.params.init_sock_send_buf_size
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getUpstreamRouter(hostrc: *const Host) -> *mut Router {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
&mut *hostrc.upstream_router_borrow_mut()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_get_bw_down_kiBps(hostrc: *const Host) -> u64 {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.bw_down_kiBps()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_get_bw_up_kiBps(hostrc: *const Host) -> u64 {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.bw_up_kiBps()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getDataPath(hostrc: *const Host) -> *const c_char {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.data_dir_path_cstring.as_ptr()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_disassociateInterface(
hostrc: *const Host,
protocol: cshadow::ProtocolType,
bind_ip: in_addr_t,
bind_port: in_port_t,
peer_ip: in_addr_t,
peer_port: in_port_t,
) {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
let bind_ip = Ipv4Addr::from(u32::from_be(bind_ip));
let peer_ip = Ipv4Addr::from(u32::from_be(peer_ip));
let bind_port = u16::from_be(bind_port);
let peer_port = u16::from_be(peer_port);
let bind_addr = SocketAddrV4::new(bind_ip, bind_port);
let peer_addr = SocketAddrV4::new(peer_ip, peer_port);
hostrc
.net_ns
.disassociate_interface(protocol, bind_addr, peer_addr);
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getRandomFreePort(
hostrc: *const Host,
protocol_type: cshadow::ProtocolType,
interface_ip: in_addr_t,
peer_ip: in_addr_t,
peer_port: in_port_t,
) -> in_port_t {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
let interface_ip = Ipv4Addr::from(u32::from_be(interface_ip));
let peer_addr = SocketAddrV4::new(
Ipv4Addr::from(u32::from_be(peer_ip)),
u16::from_be(peer_port),
);
hostrc
.net_ns
.get_random_free_port(
protocol_type,
interface_ip,
peer_addr,
hostrc.random.borrow_mut().deref_mut(),
)
.unwrap_or(0)
.to_be()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getFutexTable(hostrc: *const Host) -> *mut FutexTable {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
&mut *hostrc.futextable_borrow_mut()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getProcess(
host: *const Host,
virtual_pid: libc::pid_t,
) -> *const Process {
let host = unsafe { host.as_ref().unwrap() };
let virtual_pid = ProcessId::try_from(virtual_pid).unwrap();
host.process_borrow(virtual_pid)
.map(|x| std::ptr::from_ref(&*x.borrow(host.root())))
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getThread(
host: *const Host,
virtual_tid: libc::pid_t,
) -> *const Thread {
let host = unsafe { host.as_ref().unwrap() };
let tid = ThreadId::try_from(virtual_tid).unwrap();
for process in host.processes.borrow().values() {
let process = process.borrow(host.root());
if let Some(thread) = process.thread_borrow(tid) {
let thread = thread.borrow(host.root());
return std::ptr::from_ref(&*thread);
};
}
std::ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getShimShmemLock(
hostrc: *const Host,
) -> *mut shim_shmem::export::ShimShmemHostLock {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
let mut opt_lock = hostrc.shim_shmem_lock.borrow_mut();
let lock = opt_lock.as_mut().unwrap();
unsafe { lock.get().as_mut().unwrap().deref_mut() }
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_lockShimShmemLock(hostrc: *const Host) {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.lock_shmem()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_unlockShimShmemLock(hostrc: *const Host) {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.unlock_shmem()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_getNextDeterministicSequenceValue(
hostrc: *const Host,
) -> u64 {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
hostrc.get_next_deterministic_sequence_value()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_scheduleTaskAtEmulatedTime(
hostrc: *const Host,
task: *mut TaskRef,
time: CEmulatedTime,
) -> bool {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
let task = unsafe { task.as_ref().unwrap().clone() };
let time = EmulatedTime::from_c_emutime(time).unwrap();
hostrc.schedule_task_at_emulated_time(task, time)
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_scheduleTaskWithDelay(
hostrc: *const Host,
task: *mut TaskRef,
delay: CSimulationTime,
) -> bool {
let hostrc = unsafe { hostrc.as_ref().unwrap() };
let task = unsafe { task.as_ref().unwrap().clone() };
let delay = SimulationTime::from_c_simtime(delay).unwrap();
hostrc.schedule_task_with_delay(task, delay)
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_rngDouble(host: *const Host) -> f64 {
let host = unsafe { host.as_ref().unwrap() };
host.random_mut().gen()
}
#[no_mangle]
pub extern "C-unwind" fn host_rngNextNBytes(host: *const Host, buf: *mut u8, len: usize) {
let host = unsafe { host.as_ref().unwrap() };
let buf = unsafe { std::slice::from_raw_parts_mut(buf, len) };
host.random_mut().fill_bytes(buf);
}
#[no_mangle]
pub extern "C-unwind" fn host_paramsCpuFrequencyHz(host: *const Host) -> u64 {
let host = unsafe { host.as_ref().unwrap() };
host.params.cpu_frequency
}
#[no_mangle]
pub extern "C-unwind" fn host_addDelayNanos(host: *const Host, delay_nanos: u64) {
let host = unsafe { host.as_ref().unwrap() };
let delay = Duration::from_nanos(delay_nanos);
host.cpu.borrow_mut().add_delay(delay);
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_socketWantsToSend(
hostrc: *const Host,
socket: *const InetSocket,
addr: in_addr_t,
) {
let host = unsafe { hostrc.as_ref().unwrap() };
let socket = unsafe { socket.as_ref().unwrap() };
let addr = u32::from_be(addr).into();
host.notify_socket_has_packets(addr, socket);
}
#[no_mangle]
pub unsafe extern "C-unwind" fn host_continue(
host: *const Host,
pid: libc::pid_t,
tid: libc::pid_t,
) {
let host = unsafe { host.as_ref().unwrap() };
host.resume(pid.try_into().unwrap(), tid.try_into().unwrap())
}
}