//! Stopped-process snapshot capture for the native kboxlike backend.
//!
//! This layer deliberately starts with metadata plus thread registers. Memory
//! byte dumping and restore replay are separate policy layers; this one answers
//! which stopped host tracees belong to the model and what mappings they expose.

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use super::dispatch::KboxlikeTraceeIdentity;
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use super::fd::{
    FdError, KboxlikeFdSystem, KboxlikeSocketShutdownState, ModelThreadId, ProcessId,
    ShadowBacking, ShadowKind, ShadowObject,
};
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use super::memory::{read_proc_maps_for_pid, KboxlikeMapEntry};
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use super::supervisor::{KboxlikeSyscallSupervisor, TraceeRegisterReader};
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use std::collections::{BTreeMap, BTreeSet};
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use std::fs;
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
use std::path::PathBuf;

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug)]
pub(crate) struct KboxlikeThreadSnapshot {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) regs: libc::user_regs_struct,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct KboxlikeThreadKernelStateSnapshot {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) clear_child_tid: Option<u64>,
    pub(crate) robust_list_head: Option<u64>,
    pub(crate) robust_list_len: u64,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeTraceeMapSnapshot {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) maps: Vec<KboxlikeMapEntry>,
    pub(crate) dump_candidate_bytes: u64,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum KboxlikeFdTargetKind {
    RegularPath,
    Device,
    Pipe,
    Socket,
    EventFd,
    EventPoll,
    TimerFd,
    Inotify,
    SignalFd,
    Memfd,
    AnonInode,
    Unknown,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeEpollInterestSnapshot {
    pub(crate) target_fd: i32,
    pub(crate) events: u32,
    pub(crate) data: u64,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeTimerFdSnapshot {
    pub(crate) clockid: i32,
    pub(crate) ticks: u64,
    pub(crate) settime_flags: i32,
    pub(crate) value_sec: i64,
    pub(crate) value_nsec: i64,
    pub(crate) interval_sec: i64,
    pub(crate) interval_nsec: i64,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeFdSnapshot {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) fd: i32,
    pub(crate) target: String,
    pub(crate) kind: KboxlikeFdTargetKind,
    pub(crate) object_identity: Option<KboxlikeFdObjectIdentity>,
    pub(crate) flags: Option<u64>,
    pub(crate) cloexec: bool,
    pub(crate) eventfd_count: Option<u64>,
    pub(crate) eventfd_semaphore: Option<bool>,
    pub(crate) timerfd: Option<KboxlikeTimerFdSnapshot>,
    pub(crate) epoll_interests: Vec<KboxlikeEpollInterestSnapshot>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct KboxlikeFdObjectIdentity {
    pub(crate) mnt_id: Option<u64>,
    pub(crate) ino: Option<u64>,
    pub(crate) eventfd_id: Option<u64>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeTraceeFdSnapshot {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) fds: Vec<KboxlikeFdSnapshot>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeFdShadowBackingPlan {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) fd: i32,
    pub(crate) target: String,
    pub(crate) cloexec: bool,
    pub(crate) backing: ShadowBacking,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeEpollInterestBackingPlan {
    pub(crate) target_fd: i32,
    pub(crate) events: u32,
    pub(crate) data: u64,
    pub(crate) target: Option<String>,
    pub(crate) target_kind: Option<KboxlikeFdTargetKind>,
    pub(crate) target_backing: Option<ShadowBacking>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeEpollShadowBackingPlan {
    pub(crate) fd_plan: KboxlikeFdShadowBackingPlan,
    pub(crate) interests: Vec<KboxlikeEpollInterestBackingPlan>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeFdQueuedDataPlan {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) receiver_fd: i32,
    pub(crate) injector_fd: i32,
    pub(crate) target: String,
    pub(crate) receiver_backing: ShadowBacking,
    pub(crate) injector_backing: ShadowBacking,
    pub(crate) bytes: Vec<u8>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeSocketIntOption {
    pub(crate) level: i32,
    pub(crate) name: i32,
    pub(crate) value: i32,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeFdSocketOptionsPlan {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) fd: i32,
    pub(crate) target: String,
    pub(crate) backing: ShadowBacking,
    pub(crate) int_options: Vec<KboxlikeSocketIntOption>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeFdSocketShutdownPlan {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) fd: i32,
    pub(crate) target: String,
    pub(crate) backing: ShadowBacking,
    pub(crate) shutdown: KboxlikeSocketShutdownState,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeUnixCredentials {
    pub(crate) pid: i32,
    pub(crate) uid: u32,
    pub(crate) gid: u32,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeFdScmRightsPlan {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) model_thread_id: ModelThreadId,
    pub(crate) receiver_fd: i32,
    pub(crate) injector_fd: i32,
    pub(crate) target: String,
    pub(crate) receiver_backing: ShadowBacking,
    pub(crate) injector_backing: ShadowBacking,
    pub(crate) data: Vec<u8>,
    pub(crate) passed_fds: Vec<i32>,
    pub(crate) passed_backings: Vec<ShadowBacking>,
    pub(crate) credentials: Option<KboxlikeUnixCredentials>,
    pub(crate) credential_identity: Option<KboxlikeTraceeIdentity>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, Default)]
pub(crate) struct KboxlikeStoppedTraceeSnapshot {
    pub(crate) threads: Vec<KboxlikeThreadSnapshot>,
    pub(crate) thread_kernel_states: Vec<KboxlikeThreadKernelStateSnapshot>,
    pub(crate) memory_maps: Vec<KboxlikeTraceeMapSnapshot>,
    pub(crate) fd_tables: Vec<KboxlikeTraceeFdSnapshot>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeMemoryDump {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) start: u64,
    pub(crate) bytes: Vec<u8>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeMemoryDumpFailure {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) start: u64,
    pub(crate) len: u64,
    pub(crate) bytes_read: usize,
    pub(crate) reason: &'static str,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct KboxlikeMemoryDumpCapture {
    pub(crate) dumps: Vec<KboxlikeMemoryDump>,
    pub(crate) failures: Vec<KboxlikeMemoryDumpFailure>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
impl KboxlikeMemoryDumpCapture {
    pub(crate) fn captured_bytes(&self) -> u64 {
        self.dumps.iter().map(|dump| dump.bytes.len() as u64).sum()
    }

    pub(crate) fn failed_bytes(&self) -> u64 {
        self.failures.iter().map(|failure| failure.len).sum()
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct KboxlikeSharedMemoryDump {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) start: u64,
    pub(crate) kind: KboxlikeSharedMappingKind,
    pub(crate) path: Option<String>,
    pub(crate) file_offset: u64,
    pub(crate) dev: String,
    pub(crate) inode: u64,
    pub(crate) bytes: Vec<u8>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeSharedMemoryDumpFailure {
    pub(crate) host_pid: i32,
    pub(crate) model_pid: ProcessId,
    pub(crate) start: u64,
    pub(crate) len: u64,
    pub(crate) kind: KboxlikeSharedMappingKind,
    pub(crate) path: Option<String>,
    pub(crate) file_offset: u64,
    pub(crate) dev: String,
    pub(crate) inode: u64,
    pub(crate) bytes_read: usize,
    pub(crate) reason: &'static str,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct KboxlikeSharedMemoryDumpCapture {
    pub(crate) dumps: Vec<KboxlikeSharedMemoryDump>,
    pub(crate) failures: Vec<KboxlikeSharedMemoryDumpFailure>,
    pub(crate) skipped_duplicate_extents: usize,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
impl KboxlikeSharedMemoryDumpCapture {
    pub(crate) fn captured_bytes(&self) -> u64 {
        self.dumps.iter().map(|dump| dump.bytes.len() as u64).sum()
    }

    pub(crate) fn failed_bytes(&self) -> u64 {
        self.failures.iter().map(|failure| failure.len).sum()
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeMemoryReadError {
    pub(crate) bytes_read: usize,
    pub(crate) reason: &'static str,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeMemoryRestorePlan {
    pub(crate) actions: Vec<KboxlikeMemoryRestoreAction>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum KboxlikeMemoryRestoreAction {
    SkipKernelSynthetic {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        label: String,
    },
    SkipInaccessible {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        prot: i32,
    },
    DumpAnonymousPrivate {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        prot: i32,
        label: Option<String>,
    },
    RemapCleanFile {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        prot: i32,
        path: PathBuf,
        file_offset: u64,
        dev: String,
        inode: u64,
    },
    DumpPrivateFileCow {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        prot: i32,
        path: PathBuf,
        file_offset: u64,
        dev: String,
        inode: u64,
        reason: &'static str,
    },
    RecreateSharedObject {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        prot: i32,
        kind: KboxlikeSharedMappingKind,
        path: Option<String>,
        file_offset: u64,
        dev: String,
        inode: u64,
    },
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum KboxlikeSharedMappingKind {
    Memfd,
    Device,
    File,
    Anonymous,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct KboxlikeMaterializedMemoryRestore {
    pub(crate) actions: Vec<KboxlikeMaterializedMemoryRestoreAction>,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum KboxlikeMaterializedMemoryRestoreAction {
    ByteBacked {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        prot: i32,
        bytes: Vec<u8>,
        source: KboxlikeByteBackedRestoreSource,
    },
    RemapCleanFile {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        prot: i32,
        path: PathBuf,
        file_offset: u64,
        dev: String,
        inode: u64,
    },
    RecreateSharedObject {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        prot: i32,
        kind: KboxlikeSharedMappingKind,
        path: Option<String>,
        file_offset: u64,
        dev: String,
        inode: u64,
    },
    SkipKernelSynthetic {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        label: String,
    },
    SkipInaccessible {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
        prot: i32,
    },
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum KboxlikeByteBackedRestoreSource {
    AnonymousPrivate {
        label: Option<String>,
    },
    PrivateFileCow {
        path: PathBuf,
        file_offset: u64,
        dev: String,
        inode: u64,
        reason: &'static str,
    },
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum KboxlikeRestoreMaterializeError {
    MissingDump {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: u64,
    },
    DuplicateDump {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
    },
    ReusedDump {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
    },
    DumpLengthMismatch {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        expected: u64,
        actual: usize,
    },
    UnusedDump {
        host_pid: i32,
        model_pid: ProcessId,
        start: u64,
        len: usize,
    },
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) trait TraceeMapsReader {
    fn maps_for_pid(&mut self, host_pid: i32) -> Result<Vec<KboxlikeMapEntry>, FdError>;
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) trait TraceeMemoryReader {
    fn read_exact(
        &mut self,
        host_pid: i32,
        addr: u64,
        len: usize,
    ) -> Result<Vec<u8>, KboxlikeMemoryReadError>;
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) trait TraceeFdTableReader {
    fn fd_table_for_tracee(
        &mut self,
        tracee: &KboxlikeTraceeIdentity,
    ) -> Result<KboxlikeTraceeFdSnapshot, FdError>;
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Debug, Default)]
pub(crate) struct ProcTraceeMapsReader;

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Debug, Default)]
pub(crate) struct ProcessVmTraceeMemoryReader;

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Debug, Default)]
pub(crate) struct ProcTraceeFdTableReader;

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[derive(Debug, Default)]
struct EmptyTraceeFdTableReader;

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
impl TraceeMapsReader for ProcTraceeMapsReader {
    fn maps_for_pid(&mut self, host_pid: i32) -> Result<Vec<KboxlikeMapEntry>, FdError> {
        read_proc_maps_for_pid(host_pid)
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
impl TraceeFdTableReader for ProcTraceeFdTableReader {
    fn fd_table_for_tracee(
        &mut self,
        tracee: &KboxlikeTraceeIdentity,
    ) -> Result<KboxlikeTraceeFdSnapshot, FdError> {
        read_proc_fd_table_for_tracee(tracee)
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
impl TraceeFdTableReader for EmptyTraceeFdTableReader {
    fn fd_table_for_tracee(
        &mut self,
        tracee: &KboxlikeTraceeIdentity,
    ) -> Result<KboxlikeTraceeFdSnapshot, FdError> {
        Ok(KboxlikeTraceeFdSnapshot {
            host_pid: tracee.host_pid,
            model_pid: tracee.model_pid,
            model_thread_id: tracee.model_thread_id,
            fds: Vec::new(),
        })
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
impl TraceeMemoryReader for ProcessVmTraceeMemoryReader {
    fn read_exact(
        &mut self,
        host_pid: i32,
        addr: u64,
        len: usize,
    ) -> Result<Vec<u8>, KboxlikeMemoryReadError> {
        process_vm_read_exact(host_pid, addr, len)
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn capture_stopped_tracees(
    supervisor: &KboxlikeSyscallSupervisor,
    regs: &mut impl TraceeRegisterReader,
) -> Result<KboxlikeStoppedTraceeSnapshot, FdError> {
    let mut maps = ProcTraceeMapsReader;
    let mut fds = ProcTraceeFdTableReader;
    capture_stopped_tracees_with_kernel_states_and_fds(
        supervisor.registered_tracee_identities(),
        supervisor.registered_thread_kernel_states(),
        regs,
        &mut maps,
        &mut fds,
    )
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn capture_stopped_tracees_with(
    tracees: Vec<KboxlikeTraceeIdentity>,
    regs: &mut impl TraceeRegisterReader,
    maps: &mut impl TraceeMapsReader,
) -> Result<KboxlikeStoppedTraceeSnapshot, FdError> {
    let mut fds = EmptyTraceeFdTableReader;
    capture_stopped_tracees_with_kernel_states_and_fds(tracees, Vec::new(), regs, maps, &mut fds)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn capture_stopped_tracees_with_kernel_states(
    tracees: Vec<KboxlikeTraceeIdentity>,
    kernel_states: Vec<KboxlikeThreadKernelStateSnapshot>,
    regs: &mut impl TraceeRegisterReader,
    maps: &mut impl TraceeMapsReader,
) -> Result<KboxlikeStoppedTraceeSnapshot, FdError> {
    let mut fds = EmptyTraceeFdTableReader;
    capture_stopped_tracees_with_kernel_states_and_fds(tracees, kernel_states, regs, maps, &mut fds)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn capture_stopped_tracees_with_kernel_states_and_fds(
    tracees: Vec<KboxlikeTraceeIdentity>,
    kernel_states: Vec<KboxlikeThreadKernelStateSnapshot>,
    regs: &mut impl TraceeRegisterReader,
    maps: &mut impl TraceeMapsReader,
    fd_tables: &mut impl TraceeFdTableReader,
) -> Result<KboxlikeStoppedTraceeSnapshot, FdError> {
    let mut threads = Vec::with_capacity(tracees.len());
    let mut thread_kernel_states = Vec::with_capacity(tracees.len());
    let mut memory_maps = Vec::with_capacity(tracees.len());
    let mut captured_fd_tables = Vec::with_capacity(tracees.len());
    let mut kernel_states_by_thread = BTreeMap::new();
    for state in kernel_states {
        if kernel_states_by_thread
            .insert(state.model_thread_id, state)
            .is_some()
        {
            return Err(FdError::TraceeInstallFailed(
                "duplicate thread kernel state snapshot",
            ));
        }
    }

    for tracee in tracees {
        let thread_regs = regs.thread_regs(tracee.host_pid)?;
        let entries = maps.maps_for_pid(tracee.host_pid)?;
        let fd_table = fd_tables.fd_table_for_tracee(&tracee)?;
        if fd_table.host_pid != tracee.host_pid
            || fd_table.model_pid != tracee.model_pid
            || fd_table.model_thread_id != tracee.model_thread_id
        {
            return Err(FdError::TraceeInstallFailed("fd table identity mismatch"));
        }
        let dump_candidate_bytes = entries
            .iter()
            .filter(|entry| entry.is_dump_candidate())
            .map(KboxlikeMapEntry::len)
            .sum();

        threads.push(KboxlikeThreadSnapshot {
            host_pid: tracee.host_pid,
            model_pid: tracee.model_pid,
            model_thread_id: tracee.model_thread_id,
            regs: thread_regs,
        });
        let kernel_state = kernel_states_by_thread
            .remove(&tracee.model_thread_id)
            .unwrap_or(KboxlikeThreadKernelStateSnapshot {
                host_pid: tracee.host_pid,
                model_pid: tracee.model_pid,
                model_thread_id: tracee.model_thread_id,
                ..KboxlikeThreadKernelStateSnapshot::default()
            });
        if kernel_state.host_pid != tracee.host_pid
            || kernel_state.model_pid != tracee.model_pid
            || kernel_state.model_thread_id != tracee.model_thread_id
        {
            return Err(FdError::TraceeInstallFailed(
                "thread kernel state identity mismatch",
            ));
        }
        thread_kernel_states.push(kernel_state);
        memory_maps.push(KboxlikeTraceeMapSnapshot {
            host_pid: tracee.host_pid,
            model_pid: tracee.model_pid,
            model_thread_id: tracee.model_thread_id,
            maps: entries,
            dump_candidate_bytes,
        });
        captured_fd_tables.push(fd_table);
    }

    Ok(KboxlikeStoppedTraceeSnapshot {
        threads,
        thread_kernel_states,
        memory_maps,
        fd_tables: captured_fd_tables,
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_proc_fd_table_for_tracee(
    tracee: &KboxlikeTraceeIdentity,
) -> Result<KboxlikeTraceeFdSnapshot, FdError> {
    let mut fds = Vec::new();
    let dir = format!("/proc/{}/fd", tracee.host_pid);
    let entries = fs::read_dir(dir)
        .map_err(|_| FdError::TraceeInstallFailed("failed to read proc fd directory"))?;

    for entry in entries.flatten() {
        let name = entry.file_name();
        let Some(name) = name.to_str() else {
            continue;
        };
        let Ok(fd) = name.parse::<i32>() else {
            continue;
        };
        if fd < 0 {
            continue;
        }

        let target = fs::read_link(entry.path())
            .map(|path| path.to_string_lossy().into_owned())
            .unwrap_or_else(|_| "<unreadable>".to_string());
        let fdinfo = read_proc_fdinfo(tracee.host_pid, fd);
        let flags = fdinfo.as_deref().and_then(parse_proc_fdinfo_flags);
        let object_identity = fdinfo
            .as_deref()
            .and_then(parse_proc_fdinfo_object_identity);
        let eventfd_count = fdinfo.as_deref().and_then(parse_proc_fdinfo_eventfd_count);
        let eventfd_semaphore = fdinfo
            .as_deref()
            .and_then(parse_proc_fdinfo_eventfd_semaphore);
        let timerfd = fdinfo.as_deref().and_then(parse_proc_fdinfo_timerfd);
        let epoll_interests = fdinfo
            .as_deref()
            .map(parse_proc_fdinfo_epoll_interests)
            .unwrap_or_default();
        fds.push(KboxlikeFdSnapshot {
            host_pid: tracee.host_pid,
            model_pid: tracee.model_pid,
            model_thread_id: tracee.model_thread_id,
            fd,
            kind: classify_fd_target(&target),
            target,
            cloexec: flags.is_some_and(|flags| flags & libc::O_CLOEXEC as u64 != 0),
            object_identity,
            flags,
            eventfd_count,
            eventfd_semaphore,
            timerfd,
            epoll_interests,
        });
    }

    fds.sort_by_key(|fd| fd.fd);
    Ok(KboxlikeTraceeFdSnapshot {
        host_pid: tracee.host_pid,
        model_pid: tracee.model_pid,
        model_thread_id: tracee.model_thread_id,
        fds,
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_proc_fdinfo(host_pid: i32, fd: i32) -> Option<String> {
    fs::read_to_string(format!("/proc/{host_pid}/fdinfo/{fd}")).ok()
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_proc_fdinfo_object_identity(fdinfo: &str) -> Option<KboxlikeFdObjectIdentity> {
    let mnt_id = parse_proc_fdinfo_decimal_field(fdinfo, "mnt_id:");
    let ino = parse_proc_fdinfo_decimal_field(fdinfo, "ino:");
    let eventfd_id = parse_proc_fdinfo_decimal_field(fdinfo, "eventfd-id:");
    if mnt_id.is_none() && ino.is_none() && eventfd_id.is_none() {
        return None;
    }
    Some(KboxlikeFdObjectIdentity {
        mnt_id,
        ino,
        eventfd_id,
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_proc_fdinfo_decimal_field(fdinfo: &str, prefix: &str) -> Option<u64> {
    fdinfo.lines().find_map(|line| {
        let value = line.strip_prefix(prefix)?.trim();
        value.parse::<u64>().ok()
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_proc_fdinfo_flags(fdinfo: &str) -> Option<u64> {
    fdinfo.lines().find_map(|line| {
        let value = line.strip_prefix("flags:")?.trim();
        u64::from_str_radix(value, 8).ok()
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_proc_fdinfo_eventfd_count(fdinfo: &str) -> Option<u64> {
    fdinfo.lines().find_map(|line| {
        let value = line.strip_prefix("eventfd-count:")?.trim();
        u64::from_str_radix(value, 16)
            .ok()
            .or_else(|| value.parse::<u64>().ok())
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_proc_fdinfo_eventfd_semaphore(fdinfo: &str) -> Option<bool> {
    fdinfo.lines().find_map(|line| {
        let value = line.strip_prefix("eventfd-semaphore:")?.trim();
        Some(value != "0")
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_proc_fdinfo_timerfd(fdinfo: &str) -> Option<KboxlikeTimerFdSnapshot> {
    let mut clockid = None;
    let mut ticks = None;
    let mut settime_flags = None;
    let mut it_value = None;
    let mut it_interval = None;

    for line in fdinfo.lines() {
        let line = line.trim();
        if let Some(value) = line.strip_prefix("clockid:") {
            clockid = value.trim().parse::<i32>().ok();
        } else if let Some(value) = line.strip_prefix("ticks:") {
            ticks = value.trim().parse::<u64>().ok();
        } else if let Some(value) = line.strip_prefix("settime flags:") {
            let value = value.trim();
            settime_flags = i32::from_str_radix(value, 16)
                .ok()
                .or_else(|| value.parse::<i32>().ok());
        } else if let Some(value) = line.strip_prefix("it_value:") {
            it_value = parse_timerfd_timespec(value.trim());
        } else if let Some(value) = line.strip_prefix("it_interval:") {
            it_interval = parse_timerfd_timespec(value.trim());
        }
    }

    let (value_sec, value_nsec) = it_value?;
    let (interval_sec, interval_nsec) = it_interval?;
    Some(KboxlikeTimerFdSnapshot {
        clockid: clockid?,
        ticks: ticks?,
        settime_flags: settime_flags?,
        value_sec,
        value_nsec,
        interval_sec,
        interval_nsec,
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_timerfd_timespec(value: &str) -> Option<(i64, i64)> {
    let value = value.strip_prefix('(')?.strip_suffix(')')?;
    let (sec, nsec) = value.split_once(',')?;
    Some((sec.trim().parse().ok()?, nsec.trim().parse().ok()?))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_proc_fdinfo_epoll_interests(fdinfo: &str) -> Vec<KboxlikeEpollInterestSnapshot> {
    fdinfo
        .lines()
        .filter_map(parse_proc_fdinfo_epoll_interest_line)
        .collect()
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn parse_proc_fdinfo_epoll_interest_line(line: &str) -> Option<KboxlikeEpollInterestSnapshot> {
    let line = line.trim_start();
    let rest = line.strip_prefix("tfd:")?.trim_start();
    let mut fields = rest.split_whitespace();
    let target_fd = fields.next()?.parse::<i32>().ok()?;
    let mut events = None;
    let mut data = None;
    while let Some(field) = fields.next() {
        match field {
            "events:" => {
                let value = fields.next()?;
                events = u32::from_str_radix(value, 16)
                    .ok()
                    .or_else(|| value.parse::<u32>().ok());
            }
            "data:" => {
                let value = fields.next()?;
                data = u64::from_str_radix(value, 16)
                    .ok()
                    .or_else(|| value.parse::<u64>().ok());
            }
            _ => {}
        }
    }
    Some(KboxlikeEpollInterestSnapshot {
        target_fd,
        events: events?,
        data: data?,
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn classify_fd_target(target: &str) -> KboxlikeFdTargetKind {
    if target.starts_with("pipe:[") {
        return KboxlikeFdTargetKind::Pipe;
    }
    if target.starts_with("socket:[") {
        return KboxlikeFdTargetKind::Socket;
    }
    if target.starts_with("/memfd:") || target.contains("/memfd:") {
        return KboxlikeFdTargetKind::Memfd;
    }
    if target.starts_with("/dev/") {
        return KboxlikeFdTargetKind::Device;
    }
    if target.starts_with('/') {
        return KboxlikeFdTargetKind::RegularPath;
    }
    if target == "anon_inode:[eventfd]" {
        return KboxlikeFdTargetKind::EventFd;
    }
    if target == "anon_inode:[eventpoll]" {
        return KboxlikeFdTargetKind::EventPoll;
    }
    if target == "anon_inode:[timerfd]" {
        return KboxlikeFdTargetKind::TimerFd;
    }
    if target == "anon_inode:inotify" || target == "anon_inode:[inotify]" {
        return KboxlikeFdTargetKind::Inotify;
    }
    if target == "anon_inode:[signalfd]" {
        return KboxlikeFdTargetKind::SignalFd;
    }
    if target.starts_with("anon_inode:") {
        return KboxlikeFdTargetKind::AnonInode;
    }
    KboxlikeFdTargetKind::Unknown
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_pipe_shadow_backings(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
) -> Vec<KboxlikeFdShadowBackingPlan> {
    let mut by_target = BTreeMap::<String, Vec<&KboxlikeFdSnapshot>>::new();
    for table in fd_tables {
        for fd in &table.fds {
            if fd.kind == KboxlikeFdTargetKind::Pipe {
                by_target.entry(fd.target.clone()).or_default().push(fd);
            }
        }
    }

    let mut plans = Vec::new();
    for (target, fds) in by_target {
        let Some(group) = pipe_shadow_group(&target) else {
            continue;
        };
        let read_ends = fds
            .iter()
            .copied()
            .filter(|fd| pipe_access_endpoint(fd.flags) == Some(0))
            .collect::<Vec<_>>();
        let write_ends = fds
            .iter()
            .copied()
            .filter(|fd| pipe_access_endpoint(fd.flags) == Some(1))
            .collect::<Vec<_>>();
        if read_ends.is_empty() || write_ends.is_empty() {
            continue;
        }

        for (fds, endpoint) in [(read_ends, 0u8), (write_ends, 1u8)] {
            for fd in fds {
                plans.push(KboxlikeFdShadowBackingPlan {
                    host_pid: fd.host_pid,
                    model_pid: fd.model_pid,
                    model_thread_id: fd.model_thread_id,
                    fd: fd.fd,
                    target: fd.target.clone(),
                    cloexec: fd.cloexec,
                    backing: ShadowBacking::HostPassthrough {
                        label: format!("pipe:{group}:{endpoint}"),
                    },
                });
            }
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_eventfd_shadow_backings(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
) -> Vec<KboxlikeFdShadowBackingPlan> {
    let mut plans = Vec::new();
    for table in fd_tables {
        for fd in &table.fds {
            if fd.kind != KboxlikeFdTargetKind::EventFd {
                continue;
            }
            let Some(count) = fd.eventfd_count else {
                continue;
            };
            let Ok(initval) = u32::try_from(count) else {
                continue;
            };
            let mut flags = 0;
            if fd
                .flags
                .is_some_and(|flags| flags & libc::O_NONBLOCK as u64 != 0)
            {
                flags |= libc::EFD_NONBLOCK;
            }
            if fd.eventfd_semaphore == Some(true) {
                flags |= libc::EFD_SEMAPHORE;
            }
            plans.push(KboxlikeFdShadowBackingPlan {
                host_pid: fd.host_pid,
                model_pid: fd.model_pid,
                model_thread_id: fd.model_thread_id,
                fd: fd.fd,
                target: fd.target.clone(),
                cloexec: fd.cloexec,
                backing: ShadowBacking::HostPassthrough {
                    label: format!("eventfd:init={initval},flags={flags}"),
                },
            });
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_timerfd_shadow_backings(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
) -> Vec<KboxlikeFdShadowBackingPlan> {
    let mut plans = Vec::new();
    for table in fd_tables {
        for fd in &table.fds {
            if fd.kind != KboxlikeFdTargetKind::TimerFd {
                continue;
            }
            let Some(timerfd) = &fd.timerfd else {
                continue;
            };
            let mut flags = 0;
            if fd
                .flags
                .is_some_and(|flags| flags & libc::O_NONBLOCK as u64 != 0)
            {
                flags |= libc::TFD_NONBLOCK;
            }
            plans.push(KboxlikeFdShadowBackingPlan {
                host_pid: fd.host_pid,
                model_pid: fd.model_pid,
                model_thread_id: fd.model_thread_id,
                fd: fd.fd,
                target: fd.target.clone(),
                cloexec: fd.cloexec,
                backing: ShadowBacking::HostPassthrough {
                    label: format!(
                        "timerfd:clockid={},flags={},value_sec={},value_nsec={},interval_sec={},interval_nsec={}",
                        timerfd.clockid,
                        flags,
                        timerfd.value_sec,
                        timerfd.value_nsec,
                        timerfd.interval_sec,
                        timerfd.interval_nsec
                    ),
                },
            });
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_socketpair_shadow_backings(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
) -> Vec<KboxlikeFdShadowBackingPlan> {
    let mut plans = Vec::new();
    for table in fd_tables {
        let mut socket_fds_by_target = BTreeMap::<String, Vec<&KboxlikeFdSnapshot>>::new();
        for fd in &table.fds {
            if fd.kind == KboxlikeFdTargetKind::Socket {
                socket_fds_by_target
                    .entry(fd.target.clone())
                    .or_default()
                    .push(fd);
            }
        }
        if socket_fds_by_target.len() != 2 {
            continue;
        }

        let Some(group) = socketpair_shadow_group(socket_fds_by_target.keys()) else {
            continue;
        };
        for (endpoint, (_target, fds)) in socket_fds_by_target.into_iter().enumerate() {
            for fd in fds {
                plans.push(KboxlikeFdShadowBackingPlan {
                    host_pid: fd.host_pid,
                    model_pid: fd.model_pid,
                    model_thread_id: fd.model_thread_id,
                    fd: fd.fd,
                    target: fd.target.clone(),
                    cloexec: fd.cloexec,
                    backing: ShadowBacking::HostPassthrough {
                        label: format!("socketpair:{group}:{endpoint}"),
                    },
                });
            }
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_live_socket_shadow_backings(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
) -> Vec<KboxlikeFdShadowBackingPlan> {
    let mut plans = Vec::new();
    for table in fd_tables {
        for fd in &table.fds {
            if fd.kind != KboxlikeFdTargetKind::Socket {
                continue;
            }
            plans.push(KboxlikeFdShadowBackingPlan {
                host_pid: fd.host_pid,
                model_pid: fd.model_pid,
                model_thread_id: fd.model_thread_id,
                fd: fd.fd,
                target: fd.target.clone(),
                cloexec: fd.cloexec,
                backing: ShadowBacking::HostPassthrough {
                    label: format!("livefd:{}:{}", fd.host_pid, fd.fd),
                },
            });
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_epoll_shadow_backings(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
    fd_system: &KboxlikeFdSystem,
) -> Vec<KboxlikeEpollShadowBackingPlan> {
    let mut plans = Vec::new();
    for table in fd_tables {
        for fd in &table.fds {
            if fd.kind != KboxlikeFdTargetKind::EventPoll {
                continue;
            }
            let interests = fd
                .epoll_interests
                .iter()
                .map(|interest| {
                    let target = table.fds.iter().find(|candidate| {
                        candidate.model_pid == fd.model_pid && candidate.fd == interest.target_fd
                    });
                    KboxlikeEpollInterestBackingPlan {
                        target_fd: interest.target_fd,
                        events: interest.events,
                        data: interest.data,
                        target: target.map(|target| target.target.clone()),
                        target_kind: target.map(|target| target.kind),
                        target_backing: fd_system
                            .shadow_backing(fd.model_pid, interest.target_fd)
                            .ok()
                            .flatten()
                            .cloned(),
                    }
                })
                .collect::<Vec<_>>();
            plans.push(KboxlikeEpollShadowBackingPlan {
                fd_plan: KboxlikeFdShadowBackingPlan {
                    host_pid: fd.host_pid,
                    model_pid: fd.model_pid,
                    model_thread_id: fd.model_thread_id,
                    fd: fd.fd,
                    target: fd.target.clone(),
                    cloexec: fd.cloexec,
                    backing: ShadowBacking::HostPassthrough {
                        label: format!("epoll:epoll{}_{}", fd.host_pid, fd.fd),
                    },
                },
                interests,
            });
        }
    }
    plans.sort_by_key(|plan| {
        (
            plan.fd_plan.model_pid,
            plan.fd_plan.model_thread_id,
            plan.fd_plan.fd,
        )
    });
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_fd_queued_data_replay(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
    fd_system: &KboxlikeFdSystem,
    max_bytes_per_fd: usize,
) -> Vec<KboxlikeFdQueuedDataPlan> {
    if max_bytes_per_fd == 0 {
        return Vec::new();
    }

    let mut plans = Vec::new();
    let mut captured_receivers = BTreeSet::<(ProcessId, String)>::new();
    for table in fd_tables {
        for fd in &table.fds {
            if !matches!(
                fd.kind,
                KboxlikeFdTargetKind::Pipe | KboxlikeFdTargetKind::Socket
            ) {
                continue;
            }
            if fd.kind == KboxlikeFdTargetKind::Pipe && pipe_access_endpoint(fd.flags) != Some(0) {
                continue;
            }

            let Some(receiver_backing) = fd_system
                .shadow_backing(fd.model_pid, fd.fd)
                .ok()
                .flatten()
                .cloned()
            else {
                continue;
            };
            let Some(injector_backing) = peer_stream_backing(&receiver_backing) else {
                continue;
            };
            let receiver_identity = shadow_backing_identity(&receiver_backing);
            if !captured_receivers.insert((fd.model_pid, receiver_identity)) {
                continue;
            }
            let Some(injector_fd) =
                find_fd_with_backing(table, fd_system, fd.model_pid, &injector_backing)
            else {
                continue;
            };
            let Some(bytes) =
                capture_queued_fd_bytes(fd.host_pid, fd.fd, fd.kind, max_bytes_per_fd)
            else {
                continue;
            };
            if bytes.is_empty() {
                continue;
            }

            plans.push(KboxlikeFdQueuedDataPlan {
                host_pid: fd.host_pid,
                model_pid: fd.model_pid,
                model_thread_id: fd.model_thread_id,
                receiver_fd: fd.fd,
                injector_fd,
                target: fd.target.clone(),
                receiver_backing,
                injector_backing,
                bytes,
            });
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.receiver_fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_fd_socket_options_replay(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
    fd_system: &KboxlikeFdSystem,
) -> Vec<KboxlikeFdSocketOptionsPlan> {
    let mut plans = Vec::new();
    let mut captured_endpoints = BTreeSet::<(ProcessId, String)>::new();
    for table in fd_tables {
        for fd in &table.fds {
            if fd.kind != KboxlikeFdTargetKind::Socket {
                continue;
            }
            let Some(backing) = fd_system
                .shadow_backing(fd.model_pid, fd.fd)
                .ok()
                .flatten()
                .cloned()
            else {
                continue;
            };
            if !is_socketpair_backing(&backing) {
                continue;
            }
            let backing_identity = shadow_backing_identity(&backing);
            if !captured_endpoints.insert((fd.model_pid, backing_identity)) {
                continue;
            }
            let Some(int_options) = capture_socket_int_options(fd.host_pid, fd.fd) else {
                continue;
            };
            if int_options.is_empty() {
                continue;
            }
            plans.push(KboxlikeFdSocketOptionsPlan {
                host_pid: fd.host_pid,
                model_pid: fd.model_pid,
                model_thread_id: fd.model_thread_id,
                fd: fd.fd,
                target: fd.target.clone(),
                backing,
                int_options,
            });
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_fd_socket_shutdown_replay(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
    fd_system: &KboxlikeFdSystem,
) -> Vec<KboxlikeFdSocketShutdownPlan> {
    let mut plans = Vec::new();
    let mut captured_endpoints = BTreeSet::<(ProcessId, String)>::new();
    for table in fd_tables {
        for fd in &table.fds {
            if fd.kind != KboxlikeFdTargetKind::Socket {
                continue;
            }
            let Some(backing) = fd_system
                .shadow_backing(fd.model_pid, fd.fd)
                .ok()
                .flatten()
                .cloned()
            else {
                continue;
            };
            if !is_socketpair_backing(&backing) {
                continue;
            }
            let backing_identity = shadow_backing_identity(&backing);
            if !captured_endpoints.insert((fd.model_pid, backing_identity)) {
                continue;
            }
            let Some(shutdown) = fd_system
                .socket_shutdown(fd.model_pid, fd.fd)
                .ok()
                .flatten()
            else {
                continue;
            };
            if shutdown.is_empty() {
                continue;
            }
            plans.push(KboxlikeFdSocketShutdownPlan {
                host_pid: fd.host_pid,
                model_pid: fd.model_pid,
                model_thread_id: fd.model_thread_id,
                fd: fd.fd,
                target: fd.target.clone(),
                backing,
                shutdown,
            });
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn plan_fd_scm_rights_replay(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
    fd_system: &KboxlikeFdSystem,
    max_data_bytes: usize,
    max_fds: usize,
) -> Vec<KboxlikeFdScmRightsPlan> {
    if max_data_bytes == 0 || max_fds == 0 {
        return Vec::new();
    }

    let mut plans = Vec::new();
    let mut captured_receivers = BTreeSet::<(ProcessId, String)>::new();
    for table in fd_tables {
        for fd in &table.fds {
            if fd.kind != KboxlikeFdTargetKind::Socket {
                continue;
            }
            let Some(receiver_backing) = fd_system
                .shadow_backing(fd.model_pid, fd.fd)
                .ok()
                .flatten()
                .cloned()
            else {
                continue;
            };
            if !is_socketpair_backing(&receiver_backing) {
                continue;
            }
            let receiver_identity = shadow_backing_identity(&receiver_backing);
            if !captured_receivers.insert((fd.model_pid, receiver_identity)) {
                continue;
            }
            let Some(injector_backing) = peer_stream_backing(&receiver_backing) else {
                continue;
            };
            let Some(injector_fd) =
                find_fd_with_backing(table, fd_system, fd.model_pid, &injector_backing)
            else {
                continue;
            };
            let Some((data, passed_fds, passed_backings, credentials)) = capture_scm_rights_message(
                table,
                fd_system,
                fd.host_pid,
                fd.model_pid,
                fd.fd,
                max_data_bytes,
                max_fds,
            ) else {
                continue;
            };
            if data.is_empty() || passed_fds.is_empty() {
                continue;
            }
            let credential_identity = credentials
                .as_ref()
                .and_then(|credentials| tracee_identity_for_host_pid(fd_tables, credentials.pid));
            plans.push(KboxlikeFdScmRightsPlan {
                host_pid: fd.host_pid,
                model_pid: fd.model_pid,
                model_thread_id: fd.model_thread_id,
                receiver_fd: fd.fd,
                injector_fd,
                target: fd.target.clone(),
                receiver_backing,
                injector_backing,
                data,
                passed_fds,
                passed_backings,
                credentials,
                credential_identity,
            });
        }
    }
    plans.sort_by_key(|plan| (plan.model_pid, plan.model_thread_id, plan.receiver_fd));
    plans
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn tracee_identity_for_host_pid(
    fd_tables: &[KboxlikeTraceeFdSnapshot],
    host_pid: i32,
) -> Option<KboxlikeTraceeIdentity> {
    fd_tables.iter().find_map(|table| {
        (table.host_pid == host_pid).then_some(KboxlikeTraceeIdentity {
            host_pid: table.host_pid,
            model_pid: table.model_pid,
            model_thread_id: table.model_thread_id,
        })
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn pipe_access_endpoint(flags: Option<u64>) -> Option<u8> {
    match flags? & libc::O_ACCMODE as u64 {
        mode if mode == libc::O_RDONLY as u64 => Some(0),
        mode if mode == libc::O_WRONLY as u64 => Some(1),
        _ => None,
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn pipe_shadow_group(target: &str) -> Option<String> {
    let inode = target.strip_prefix("pipe:[")?.strip_suffix(']')?;
    if inode.is_empty() || !inode.chars().all(|ch| ch.is_ascii_digit()) {
        return None;
    }
    Some(format!("pipe{inode}"))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn socketpair_shadow_group<'a>(targets: impl Iterator<Item = &'a String>) -> Option<String> {
    let mut inodes = Vec::new();
    for target in targets {
        let inode = target.strip_prefix("socket:[")?.strip_suffix(']')?;
        if inode.is_empty() || !inode.chars().all(|ch| ch.is_ascii_digit()) {
            return None;
        }
        inodes.push(inode.to_string());
    }
    if inodes.len() != 2 {
        return None;
    }
    Some(format!("socketpair{}_{}", inodes[0], inodes[1]))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn peer_stream_backing(backing: &ShadowBacking) -> Option<ShadowBacking> {
    let ShadowBacking::HostPassthrough { label } = backing else {
        return None;
    };
    let (prefix, rest) = label.split_once(':')?;
    if prefix != "pipe" && prefix != "socketpair" {
        return None;
    }
    let (group, endpoint) = rest.rsplit_once(':')?;
    let peer_endpoint = match endpoint {
        "0" => "1",
        "1" => "0",
        _ => return None,
    };
    Some(ShadowBacking::HostPassthrough {
        label: format!("{prefix}:{group}:{peer_endpoint}"),
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn is_socketpair_backing(backing: &ShadowBacking) -> bool {
    matches!(
        backing,
        ShadowBacking::HostPassthrough { label } if label.starts_with("socketpair:")
    )
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn find_fd_with_backing(
    table: &KboxlikeTraceeFdSnapshot,
    fd_system: &KboxlikeFdSystem,
    model_pid: ProcessId,
    backing: &ShadowBacking,
) -> Option<i32> {
    table
        .fds
        .iter()
        .filter(|fd| fd.model_pid == model_pid)
        .find(|fd| {
            fd_system
                .shadow_backing(model_pid, fd.fd)
                .ok()
                .flatten()
                .is_some_and(|candidate| candidate == backing)
        })
        .map(|fd| fd.fd)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn capture_queued_fd_bytes(
    host_pid: i32,
    fd: i32,
    kind: KboxlikeFdTargetKind,
    max_bytes: usize,
) -> Option<Vec<u8>> {
    let duplicated = duplicate_tracee_fd(host_pid, fd).ok()?;
    let available = queued_fd_bytes_available(duplicated.as_raw_fd()).ok()?;
    if available == 0 {
        return None;
    }
    let len = available.min(max_bytes);
    match kind {
        KboxlikeFdTargetKind::Pipe => read_queued_bytes(duplicated.as_raw_fd(), len).ok(),
        KboxlikeFdTargetKind::Socket => peek_socket_bytes(duplicated.as_raw_fd(), len).ok(),
        _ => None,
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn capture_socket_int_options(host_pid: i32, fd: i32) -> Option<Vec<KboxlikeSocketIntOption>> {
    let duplicated = duplicate_tracee_fd(host_pid, fd).ok()?;
    let mut options = Vec::new();
    for (level, name) in socket_int_options_to_capture() {
        if let Ok(value) = get_socket_int_option(duplicated.as_raw_fd(), level, name) {
            options.push(KboxlikeSocketIntOption { level, name, value });
        }
    }
    Some(options)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn capture_scm_rights_message(
    table: &KboxlikeTraceeFdSnapshot,
    fd_system: &KboxlikeFdSystem,
    host_pid: i32,
    model_pid: ProcessId,
    fd: i32,
    max_data_bytes: usize,
    max_fds: usize,
) -> Option<(
    Vec<u8>,
    Vec<i32>,
    Vec<ShadowBacking>,
    Option<KboxlikeUnixCredentials>,
)> {
    let duplicated = duplicate_tracee_fd(host_pid, fd).ok()?;
    let fd_bytes = max_fds.checked_mul(std::mem::size_of::<libc::c_int>())?;
    let cmsg_space = unsafe {
        libc::CMSG_SPACE(fd_bytes as libc::c_uint) as usize
            + libc::CMSG_SPACE(std::mem::size_of::<libc::ucred>() as libc::c_uint) as usize
    };
    let mut control = vec![0u8; cmsg_space];
    let mut data = vec![0u8; max_data_bytes];
    let mut iov = libc::iovec {
        iov_base: data.as_mut_ptr() as *mut libc::c_void,
        iov_len: data.len(),
    };
    let mut msg = libc::msghdr {
        msg_name: std::ptr::null_mut(),
        msg_namelen: 0,
        msg_iov: &mut iov,
        msg_iovlen: 1,
        msg_control: control.as_mut_ptr() as *mut libc::c_void,
        msg_controllen: control.len(),
        msg_flags: 0,
    };

    let read = unsafe {
        libc::recvmsg(
            duplicated.as_raw_fd(),
            &mut msg,
            libc::MSG_PEEK | libc::MSG_DONTWAIT,
        )
    };
    if read <= 0 {
        return None;
    }
    data.truncate(read as usize);
    let received = received_scm_rights_fds(&msg, max_fds);
    if received.is_empty() {
        return None;
    }
    let credentials = received_scm_credentials(&msg);
    map_received_fds_to_backings(table, fd_system, model_pid, received)
        .map(|(fds, backings)| (data, fds, backings, credentials))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn received_scm_rights_fds(msg: &libc::msghdr, max_fds: usize) -> Vec<OwnedFd> {
    let mut fds = Vec::new();
    let mut cmsg = unsafe { libc::CMSG_FIRSTHDR(msg) };
    while !cmsg.is_null() && fds.len() < max_fds {
        let header = unsafe { &*cmsg };
        if header.cmsg_level == libc::SOL_SOCKET && header.cmsg_type == libc::SCM_RIGHTS {
            let data_len = header.cmsg_len as usize - unsafe { libc::CMSG_LEN(0) as usize };
            let fd_count = data_len / std::mem::size_of::<libc::c_int>();
            let data = unsafe { libc::CMSG_DATA(cmsg) as *const libc::c_int };
            for index in 0..fd_count.min(max_fds - fds.len()) {
                let raw = unsafe { data.add(index).read_unaligned() };
                if raw >= 0 {
                    fds.push(unsafe { OwnedFd::from_raw_fd(raw) });
                }
            }
        }
        cmsg = unsafe { libc::CMSG_NXTHDR(msg, cmsg) };
    }
    fds
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn received_scm_credentials(msg: &libc::msghdr) -> Option<KboxlikeUnixCredentials> {
    let mut cmsg = unsafe { libc::CMSG_FIRSTHDR(msg) };
    while !cmsg.is_null() {
        let header = unsafe { &*cmsg };
        if header.cmsg_level == libc::SOL_SOCKET && header.cmsg_type == libc::SCM_CREDENTIALS {
            let data_len = header.cmsg_len as usize - unsafe { libc::CMSG_LEN(0) as usize };
            if data_len >= std::mem::size_of::<libc::ucred>() {
                let creds =
                    unsafe { (libc::CMSG_DATA(cmsg) as *const libc::ucred).read_unaligned() };
                return Some(KboxlikeUnixCredentials {
                    pid: creds.pid,
                    uid: creds.uid,
                    gid: creds.gid,
                });
            }
        }
        cmsg = unsafe { libc::CMSG_NXTHDR(msg, cmsg) };
    }
    None
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn map_received_fds_to_backings(
    table: &KboxlikeTraceeFdSnapshot,
    fd_system: &KboxlikeFdSystem,
    model_pid: ProcessId,
    received: Vec<OwnedFd>,
) -> Option<(Vec<i32>, Vec<ShadowBacking>)> {
    let mut fds = Vec::new();
    let mut backings = Vec::new();
    for received_fd in received {
        let target = fs::read_link(format!("/proc/self/fd/{}", received_fd.as_raw_fd()))
            .ok()?
            .to_string_lossy()
            .into_owned();
        let object_identity = read_proc_fdinfo(unsafe { libc::getpid() }, received_fd.as_raw_fd())
            .and_then(|fdinfo| parse_proc_fdinfo_object_identity(&fdinfo));
        let mut matches_by_backing = BTreeMap::<String, (i32, ShadowBacking)>::new();
        for (fd, backing) in table
            .fds
            .iter()
            .filter(|fd| fd.model_pid == model_pid && fd.target == target)
            .filter(|fd| {
                object_identity
                    .map(|identity| fd.object_identity == Some(identity))
                    .unwrap_or(true)
            })
            .filter_map(|fd| {
                fd_system
                    .shadow_backing(model_pid, fd.fd)
                    .ok()
                    .flatten()
                    .cloned()
                    .map(|backing| (fd.fd, backing))
            })
        {
            let identity = shadow_backing_identity(&backing);
            matches_by_backing
                .entry(identity)
                .and_modify(|(existing_fd, _)| {
                    *existing_fd = (*existing_fd).min(fd);
                })
                .or_insert((fd, backing));
        }
        if matches_by_backing.len() != 1 {
            return None;
        }
        let (_, (fd, backing)) = matches_by_backing
            .pop_first()
            .expect("checked unique backing");
        fds.push(fd);
        backings.push(backing);
    }
    Some((fds, backings))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn socket_int_options_to_capture() -> [(i32, i32); 1] {
    [(libc::SOL_SOCKET, libc::SO_PASSCRED)]
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn get_socket_int_option(fd: i32, level: i32, name: i32) -> Result<i32, FdError> {
    let mut value = 0 as libc::c_int;
    let mut len = std::mem::size_of::<libc::c_int>() as libc::socklen_t;
    let rc = unsafe {
        libc::getsockopt(
            fd,
            level,
            name,
            &mut value as *mut libc::c_int as *mut libc::c_void,
            &mut len,
        )
    };
    if rc < 0 || len as usize != std::mem::size_of::<libc::c_int>() {
        return Err(FdError::TraceeInstallFailed(
            "failed to capture socket option",
        ));
    }
    Ok(value)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn duplicate_tracee_fd(host_pid: i32, fd: i32) -> Result<OwnedFd, FdError> {
    let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, host_pid, 0) };
    if pidfd >= 0 {
        let pidfd = unsafe { OwnedFd::from_raw_fd(pidfd as i32) };
        let duplicated = unsafe {
            libc::syscall(
                libc::SYS_pidfd_getfd,
                pidfd.as_raw_fd(),
                fd,
                0 as libc::c_uint,
            )
        };
        if duplicated >= 0 {
            return Ok(unsafe { OwnedFd::from_raw_fd(duplicated as i32) });
        }
    }

    fs::File::open(format!("/proc/{host_pid}/fd/{fd}"))
        .map(OwnedFd::from)
        .map_err(|_| FdError::TraceeInstallFailed("failed to duplicate tracee fd"))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn queued_fd_bytes_available(fd: i32) -> Result<usize, FdError> {
    let mut available = 0 as libc::c_int;
    let rc = unsafe { libc::ioctl(fd, libc::FIONREAD, &mut available) };
    if rc < 0 {
        return Err(FdError::TraceeInstallFailed(
            "failed to query queued fd bytes",
        ));
    }
    usize::try_from(available).map_err(|_| FdError::TraceeInstallFailed("invalid queued fd bytes"))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_queued_bytes(fd: i32, len: usize) -> Result<Vec<u8>, FdError> {
    let mut bytes = vec![0u8; len];
    let mut read_total = 0usize;
    while read_total < len {
        let rc = unsafe {
            libc::read(
                fd,
                bytes[read_total..].as_mut_ptr() as *mut libc::c_void,
                len - read_total,
            )
        };
        if rc < 0 {
            return Err(FdError::TraceeInstallFailed(
                "failed to read queued pipe bytes",
            ));
        }
        if rc == 0 {
            bytes.truncate(read_total);
            return Ok(bytes);
        }
        read_total += rc as usize;
    }
    Ok(bytes)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn peek_socket_bytes(fd: i32, len: usize) -> Result<Vec<u8>, FdError> {
    let mut bytes = vec![0u8; len];
    let rc = unsafe {
        libc::recv(
            fd,
            bytes.as_mut_ptr() as *mut libc::c_void,
            bytes.len(),
            libc::MSG_PEEK | libc::MSG_DONTWAIT,
        )
    };
    if rc < 0 {
        return Err(FdError::TraceeInstallFailed(
            "failed to peek queued socket bytes",
        ));
    }
    bytes.truncate(rc as usize);
    Ok(bytes)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn apply_fd_shadow_backing_plans(
    fd_system: &mut KboxlikeFdSystem,
    plans: &[KboxlikeFdShadowBackingPlan],
) -> Result<usize, FdError> {
    let mut first_descriptor_by_backing = BTreeMap::<String, (ProcessId, i32)>::new();
    for plan in plans {
        let identity = shadow_backing_identity(&plan.backing);
        if let Some((source_pid, source_fd)) = first_descriptor_by_backing.get(&identity).copied() {
            fd_system.alias_fd_from(
                source_pid,
                source_fd,
                plan.model_pid,
                plan.fd,
                plan.cloexec,
            )?;
        } else {
            fd_system.insert_shadow_fd_with_backing(
                plan.model_pid,
                plan.fd,
                None,
                ShadowObject {
                    kind: ShadowKind::HostPassthrough,
                    supervisor_fd: plan.fd,
                },
                plan.backing.clone(),
                plan.cloexec,
            )?;
            first_descriptor_by_backing.insert(identity, (plan.model_pid, plan.fd));
        }
    }
    Ok(plans.len())
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn shadow_backing_identity(backing: &ShadowBacking) -> String {
    match backing {
        ShadowBacking::HostPassthrough { label } => format!("host:{label}"),
        ShadowBacking::GuestPath { path, writable } => format!("guest:{writable}:{path}"),
        ShadowBacking::HostPath { path, writable } => format!("hostpath:{writable}:{path}"),
        ShadowBacking::Ephemeral => "ephemeral".to_string(),
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn capture_dump_candidate_memory(
    snapshot: &KboxlikeStoppedTraceeSnapshot,
) -> KboxlikeMemoryDumpCapture {
    let mut reader = ProcessVmTraceeMemoryReader;
    capture_dump_candidate_memory_with(&snapshot.memory_maps, &mut reader)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn capture_dump_candidate_memory_with(
    memory_maps: &[KboxlikeTraceeMapSnapshot],
    reader: &mut impl TraceeMemoryReader,
) -> KboxlikeMemoryDumpCapture {
    let mut capture = KboxlikeMemoryDumpCapture::default();

    for tracee_maps in memory_maps {
        for entry in tracee_maps
            .maps
            .iter()
            .filter(|entry| entry.is_dump_candidate())
        {
            let Ok(len) = usize::try_from(entry.len()) else {
                capture.failures.push(KboxlikeMemoryDumpFailure {
                    host_pid: tracee_maps.host_pid,
                    model_pid: tracee_maps.model_pid,
                    start: entry.start,
                    len: entry.len(),
                    bytes_read: 0,
                    reason: "mapping length overflows usize",
                });
                continue;
            };

            match reader.read_exact(tracee_maps.host_pid, entry.start, len) {
                Ok(bytes) => capture.dumps.push(KboxlikeMemoryDump {
                    host_pid: tracee_maps.host_pid,
                    model_pid: tracee_maps.model_pid,
                    start: entry.start,
                    bytes,
                }),
                Err(err) => capture.failures.push(KboxlikeMemoryDumpFailure {
                    host_pid: tracee_maps.host_pid,
                    model_pid: tracee_maps.model_pid,
                    start: entry.start,
                    len: entry.len(),
                    bytes_read: err.bytes_read,
                    reason: err.reason,
                }),
            }
        }
    }

    capture
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn capture_shared_object_memory(
    plan: &KboxlikeMemoryRestorePlan,
) -> KboxlikeSharedMemoryDumpCapture {
    let mut reader = ProcessVmTraceeMemoryReader;
    capture_shared_object_memory_with(plan, &mut reader)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn capture_shared_object_memory_with(
    plan: &KboxlikeMemoryRestorePlan,
    reader: &mut impl TraceeMemoryReader,
) -> KboxlikeSharedMemoryDumpCapture {
    let mut capture = KboxlikeSharedMemoryDumpCapture::default();
    let mut seen_extents = BTreeSet::new();

    for action in &plan.actions {
        let KboxlikeMemoryRestoreAction::RecreateSharedObject {
            host_pid,
            model_pid,
            start,
            len,
            kind,
            path,
            file_offset,
            dev,
            inode,
            ..
        } = action
        else {
            continue;
        };

        let extent_key = (*kind, path.clone(), dev.clone(), *inode, *file_offset, *len);
        if !seen_extents.insert(extent_key) {
            capture.skipped_duplicate_extents += 1;
            continue;
        }

        let Ok(len_usize) = usize::try_from(*len) else {
            capture.failures.push(KboxlikeSharedMemoryDumpFailure {
                host_pid: *host_pid,
                model_pid: *model_pid,
                start: *start,
                len: *len,
                kind: *kind,
                path: path.clone(),
                file_offset: *file_offset,
                dev: dev.clone(),
                inode: *inode,
                bytes_read: 0,
                reason: "shared mapping length overflows usize",
            });
            continue;
        };

        match reader.read_exact(*host_pid, *start, len_usize) {
            Ok(bytes) => capture.dumps.push(KboxlikeSharedMemoryDump {
                host_pid: *host_pid,
                model_pid: *model_pid,
                start: *start,
                kind: *kind,
                path: path.clone(),
                file_offset: *file_offset,
                dev: dev.clone(),
                inode: *inode,
                bytes,
            }),
            Err(err) => capture.failures.push(KboxlikeSharedMemoryDumpFailure {
                host_pid: *host_pid,
                model_pid: *model_pid,
                start: *start,
                len: *len,
                kind: *kind,
                path: path.clone(),
                file_offset: *file_offset,
                dev: dev.clone(),
                inode: *inode,
                bytes_read: err.bytes_read,
                reason: err.reason,
            }),
        }
    }

    capture
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn classify_memory_restore_actions(
    memory_maps: &[KboxlikeTraceeMapSnapshot],
) -> KboxlikeMemoryRestorePlan {
    let actions = memory_maps
        .iter()
        .flat_map(|tracee_maps| {
            tracee_maps.maps.iter().map(|entry| {
                classify_memory_map_restore_action(
                    tracee_maps.host_pid,
                    tracee_maps.model_pid,
                    entry,
                )
            })
        })
        .collect();

    KboxlikeMemoryRestorePlan { actions }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn classify_memory_map_restore_action(
    host_pid: i32,
    model_pid: ProcessId,
    entry: &KboxlikeMapEntry,
) -> KboxlikeMemoryRestoreAction {
    if entry.path.as_deref() == Some("[vdso]") {
        return KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
            host_pid,
            model_pid,
            start: entry.start,
            len: entry.len(),
            prot: entry.perms.prot(),
            label: entry.path.clone(),
        };
    }

    if entry.is_kernel_synthetic() {
        return KboxlikeMemoryRestoreAction::SkipKernelSynthetic {
            host_pid,
            model_pid,
            start: entry.start,
            len: entry.len(),
            label: entry.path.clone().unwrap_or_default(),
        };
    }

    if !entry.perms.read {
        return KboxlikeMemoryRestoreAction::SkipInaccessible {
            host_pid,
            model_pid,
            start: entry.start,
            len: entry.len(),
            prot: entry.perms.prot(),
        };
    }

    let prot = entry.perms.prot();
    if entry.perms.shared {
        return KboxlikeMemoryRestoreAction::RecreateSharedObject {
            host_pid,
            model_pid,
            start: entry.start,
            len: entry.len(),
            prot,
            kind: shared_mapping_kind(entry),
            path: entry.path.clone(),
            file_offset: entry.file_offset,
            dev: entry.dev.clone(),
            inode: entry.inode,
        };
    }

    let Some(path) = entry.file_path() else {
        return KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
            host_pid,
            model_pid,
            start: entry.start,
            len: entry.len(),
            prot,
            label: entry.path.clone(),
        };
    };

    if entry
        .path
        .as_deref()
        .is_some_and(|path| path.ends_with(" (deleted)"))
    {
        return KboxlikeMemoryRestoreAction::DumpPrivateFileCow {
            host_pid,
            model_pid,
            start: entry.start,
            len: entry.len(),
            prot,
            path,
            file_offset: entry.file_offset,
            dev: entry.dev.clone(),
            inode: entry.inode,
            reason: "deleted file mapping cannot be reopened reliably",
        };
    }

    if entry.perms.write {
        return KboxlikeMemoryRestoreAction::DumpPrivateFileCow {
            host_pid,
            model_pid,
            start: entry.start,
            len: entry.len(),
            prot,
            path,
            file_offset: entry.file_offset,
            dev: entry.dev.clone(),
            inode: entry.inode,
            reason: "writable private file mapping may contain COW bytes",
        };
    }

    if !entry.perms.exec {
        return KboxlikeMemoryRestoreAction::DumpPrivateFileCow {
            host_pid,
            model_pid,
            start: entry.start,
            len: entry.len(),
            prot,
            path,
            file_offset: entry.file_offset,
            dev: entry.dev.clone(),
            inode: entry.inode,
            reason: "non-executable private file mapping may contain relocated COW bytes",
        };
    }

    KboxlikeMemoryRestoreAction::RemapCleanFile {
        host_pid,
        model_pid,
        start: entry.start,
        len: entry.len(),
        prot,
        path,
        file_offset: entry.file_offset,
        dev: entry.dev.clone(),
        inode: entry.inode,
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn shared_mapping_kind(entry: &KboxlikeMapEntry) -> KboxlikeSharedMappingKind {
    let Some(path) = entry.path.as_deref() else {
        return KboxlikeSharedMappingKind::Anonymous;
    };
    if path.contains("memfd:") {
        return KboxlikeSharedMappingKind::Memfd;
    }
    if path.starts_with("/dev/") {
        return KboxlikeSharedMappingKind::Device;
    }
    KboxlikeSharedMappingKind::File
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub(crate) fn materialize_memory_restore_actions(
    plan: &KboxlikeMemoryRestorePlan,
    dumps: &KboxlikeMemoryDumpCapture,
) -> Result<KboxlikeMaterializedMemoryRestore, KboxlikeRestoreMaterializeError> {
    let dump_by_start = index_memory_dumps(&dumps.dumps)?;
    let mut used_dump_starts = BTreeSet::new();
    let mut actions = Vec::with_capacity(plan.actions.len());

    for action in &plan.actions {
        actions.push(materialize_memory_restore_action(
            action,
            &dump_by_start,
            &mut used_dump_starts,
        )?);
    }

    for dump in &dumps.dumps {
        if !used_dump_starts.contains(&(dump.host_pid, dump.model_pid, dump.start)) {
            return Err(KboxlikeRestoreMaterializeError::UnusedDump {
                host_pid: dump.host_pid,
                model_pid: dump.model_pid,
                start: dump.start,
                len: dump.bytes.len(),
            });
        }
    }

    Ok(KboxlikeMaterializedMemoryRestore { actions })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn index_memory_dumps(
    dumps: &[KboxlikeMemoryDump],
) -> Result<BTreeMap<(i32, ProcessId, u64), &KboxlikeMemoryDump>, KboxlikeRestoreMaterializeError> {
    let mut dump_by_start = BTreeMap::new();
    for dump in dumps {
        let key = (dump.host_pid, dump.model_pid, dump.start);
        if dump_by_start.insert(key, dump).is_some() {
            return Err(KboxlikeRestoreMaterializeError::DuplicateDump {
                host_pid: dump.host_pid,
                model_pid: dump.model_pid,
                start: dump.start,
            });
        }
    }
    Ok(dump_by_start)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn materialize_memory_restore_action(
    action: &KboxlikeMemoryRestoreAction,
    dumps: &BTreeMap<(i32, ProcessId, u64), &KboxlikeMemoryDump>,
    used_dump_starts: &mut BTreeSet<(i32, ProcessId, u64)>,
) -> Result<KboxlikeMaterializedMemoryRestoreAction, KboxlikeRestoreMaterializeError> {
    match action {
        KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
            host_pid,
            model_pid,
            start,
            len,
            prot,
            label,
        } => {
            let mut bytes =
                take_matching_dump(dumps, used_dump_starts, *host_pid, *model_pid, *start, *len)?;
            if label.as_deref() == Some("[vdso]") {
                patch_vdso_syscall_fallbacks(&mut bytes);
            }
            Ok(KboxlikeMaterializedMemoryRestoreAction::ByteBacked {
                host_pid: *host_pid,
                model_pid: *model_pid,
                start: *start,
                prot: *prot,
                bytes,
                source: KboxlikeByteBackedRestoreSource::AnonymousPrivate {
                    label: label.clone(),
                },
            })
        }
        KboxlikeMemoryRestoreAction::DumpPrivateFileCow {
            host_pid,
            model_pid,
            start,
            len,
            prot,
            path,
            file_offset,
            dev,
            inode,
            reason,
        } => Ok(KboxlikeMaterializedMemoryRestoreAction::ByteBacked {
            host_pid: *host_pid,
            model_pid: *model_pid,
            start: *start,
            prot: *prot,
            bytes: take_matching_dump(
                dumps,
                used_dump_starts,
                *host_pid,
                *model_pid,
                *start,
                *len,
            )?,
            source: KboxlikeByteBackedRestoreSource::PrivateFileCow {
                path: path.clone(),
                file_offset: *file_offset,
                dev: dev.clone(),
                inode: *inode,
                reason,
            },
        }),
        KboxlikeMemoryRestoreAction::RemapCleanFile {
            host_pid,
            model_pid,
            start,
            len,
            prot,
            path,
            file_offset,
            dev,
            inode,
        } => Ok(KboxlikeMaterializedMemoryRestoreAction::RemapCleanFile {
            host_pid: *host_pid,
            model_pid: *model_pid,
            start: *start,
            len: *len,
            prot: *prot,
            path: path.clone(),
            file_offset: *file_offset,
            dev: dev.clone(),
            inode: *inode,
        }),
        KboxlikeMemoryRestoreAction::RecreateSharedObject {
            host_pid,
            model_pid,
            start,
            len,
            prot,
            kind,
            path,
            file_offset,
            dev,
            inode,
        } => Ok(
            KboxlikeMaterializedMemoryRestoreAction::RecreateSharedObject {
                host_pid: *host_pid,
                model_pid: *model_pid,
                start: *start,
                len: *len,
                prot: *prot,
                kind: *kind,
                path: path.clone(),
                file_offset: *file_offset,
                dev: dev.clone(),
                inode: *inode,
            },
        ),
        KboxlikeMemoryRestoreAction::SkipKernelSynthetic {
            host_pid,
            model_pid,
            start,
            len,
            label,
        } => Ok(
            KboxlikeMaterializedMemoryRestoreAction::SkipKernelSynthetic {
                host_pid: *host_pid,
                model_pid: *model_pid,
                start: *start,
                len: *len,
                label: label.clone(),
            },
        ),
        KboxlikeMemoryRestoreAction::SkipInaccessible {
            host_pid,
            model_pid,
            start,
            len,
            prot,
        } => Ok(KboxlikeMaterializedMemoryRestoreAction::SkipInaccessible {
            host_pid: *host_pid,
            model_pid: *model_pid,
            start: *start,
            len: *len,
            prot: *prot,
        }),
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn take_matching_dump(
    dumps: &BTreeMap<(i32, ProcessId, u64), &KboxlikeMemoryDump>,
    used_dump_starts: &mut BTreeSet<(i32, ProcessId, u64)>,
    host_pid: i32,
    model_pid: ProcessId,
    start: u64,
    len: u64,
) -> Result<Vec<u8>, KboxlikeRestoreMaterializeError> {
    let key = (host_pid, model_pid, start);
    let dump = dumps
        .get(&key)
        .ok_or(KboxlikeRestoreMaterializeError::MissingDump {
            host_pid,
            model_pid,
            start,
            len,
        })?;
    if dump.bytes.len() as u64 != len {
        return Err(KboxlikeRestoreMaterializeError::DumpLengthMismatch {
            host_pid,
            model_pid,
            start,
            expected: len,
            actual: dump.bytes.len(),
        });
    }
    if !used_dump_starts.insert(key) {
        return Err(KboxlikeRestoreMaterializeError::ReusedDump {
            host_pid,
            model_pid,
            start,
        });
    }
    Ok(dump.bytes.clone())
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn patch_vdso_syscall_fallbacks(bytes: &mut [u8]) -> usize {
    if bytes.len() < 0x40
        || bytes.get(0..4) != Some(b"\x7fELF")
        || bytes.get(4).copied() != Some(2)
        || bytes.get(5).copied() != Some(1)
    {
        return 0;
    }

    let Some(e_shoff) = read_elf_u64(bytes, 0x28).and_then(usize_from_u64) else {
        return 0;
    };
    let Some(e_shentsize) = read_elf_u16(bytes, 0x3a).map(usize::from) else {
        return 0;
    };
    let Some(e_shnum) = read_elf_u16(bytes, 0x3c).map(usize::from) else {
        return 0;
    };
    if e_shentsize < 64 {
        return 0;
    }

    let mut patched_offsets = BTreeSet::new();
    for section_index in 0..e_shnum {
        let Some(section) = elf_section_offset(bytes, e_shoff, e_shentsize, e_shnum, section_index)
        else {
            continue;
        };
        if read_elf_u32(bytes, section + 4) != Some(11) {
            continue;
        }

        let Some(strtab_index) = read_elf_u32(bytes, section + 40).and_then(usize_from_u32) else {
            continue;
        };
        let Some(strtab) = elf_section_offset(bytes, e_shoff, e_shentsize, e_shnum, strtab_index)
        else {
            continue;
        };
        let Some(strtab_offset) = read_elf_u64(bytes, strtab + 24).and_then(usize_from_u64) else {
            continue;
        };
        let Some(strtab_size) = read_elf_u64(bytes, strtab + 32).and_then(usize_from_u64) else {
            continue;
        };
        let Some(sym_offset) = read_elf_u64(bytes, section + 24).and_then(usize_from_u64) else {
            continue;
        };
        let Some(sym_size) = read_elf_u64(bytes, section + 32).and_then(usize_from_u64) else {
            continue;
        };
        let Some(sym_entsize) = read_elf_u64(bytes, section + 56).and_then(usize_from_u64) else {
            continue;
        };
        if sym_entsize < 24 {
            continue;
        }

        let Some(sym_end) = sym_offset.checked_add(sym_size) else {
            continue;
        };
        let mut sym = sym_offset;
        while sym
            .checked_add(24)
            .is_some_and(|end| end <= sym_end && end <= bytes.len())
        {
            let Some(name_offset) = read_elf_u32(bytes, sym).and_then(usize_from_u32) else {
                break;
            };
            let Some(name) = elf_string(bytes, strtab_offset, strtab_size, name_offset) else {
                sym += sym_entsize;
                continue;
            };
            let Some(syscall_number) = vdso_fallback_syscall_number(name) else {
                sym += sym_entsize;
                continue;
            };
            let Some(symbol_value) = read_elf_u64(bytes, sym + 8) else {
                sym += sym_entsize;
                continue;
            };
            if patched_offsets.insert(symbol_value) {
                write_vdso_syscall_stub(bytes, symbol_value, syscall_number);
            }
            sym += sym_entsize;
        }
    }

    patched_offsets.len()
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn vdso_fallback_syscall_number(name: &[u8]) -> Option<u32> {
    match name {
        b"__vdso_gettimeofday" | b"gettimeofday" => Some(96),
        b"__vdso_time" | b"time" => Some(201),
        b"__vdso_clock_gettime" | b"clock_gettime" => Some(228),
        b"__vdso_clock_getres" | b"clock_getres" => Some(229),
        b"__vdso_getcpu" | b"getcpu" => Some(309),
        _ => None,
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn write_vdso_syscall_stub(bytes: &mut [u8], offset: u64, syscall_number: u32) -> bool {
    let Some(offset) = usize_from_u64(offset) else {
        return false;
    };
    let Some(end) = offset.checked_add(8) else {
        return false;
    };
    if end > bytes.len() {
        return false;
    }
    bytes[offset] = 0xb8;
    bytes[offset + 1..offset + 5].copy_from_slice(&syscall_number.to_le_bytes());
    bytes[offset + 5] = 0x0f;
    bytes[offset + 6] = 0x05;
    bytes[offset + 7] = 0xc3;
    true
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn elf_section_offset(
    bytes: &[u8],
    e_shoff: usize,
    e_shentsize: usize,
    e_shnum: usize,
    index: usize,
) -> Option<usize> {
    if index >= e_shnum {
        return None;
    }
    let offset = e_shoff.checked_add(index.checked_mul(e_shentsize)?)?;
    offset.checked_add(64).filter(|end| *end <= bytes.len())?;
    Some(offset)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn elf_string<'a>(
    bytes: &'a [u8],
    strtab_offset: usize,
    strtab_size: usize,
    name_offset: usize,
) -> Option<&'a [u8]> {
    if name_offset >= strtab_size {
        return None;
    }
    let start = strtab_offset.checked_add(name_offset)?;
    let limit = strtab_offset.checked_add(strtab_size)?;
    if start >= bytes.len() || limit > bytes.len() {
        return None;
    }
    let rel_end = bytes[start..limit]
        .iter()
        .position(|byte| *byte == 0)
        .unwrap_or(limit - start);
    Some(&bytes[start..start + rel_end])
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_elf_u16(bytes: &[u8], offset: usize) -> Option<u16> {
    let raw = bytes.get(offset..offset.checked_add(2)?)?;
    Some(u16::from_le_bytes(raw.try_into().ok()?))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_elf_u32(bytes: &[u8], offset: usize) -> Option<u32> {
    let raw = bytes.get(offset..offset.checked_add(4)?)?;
    Some(u32::from_le_bytes(raw.try_into().ok()?))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_elf_u64(bytes: &[u8], offset: usize) -> Option<u64> {
    let raw = bytes.get(offset..offset.checked_add(8)?)?;
    Some(u64::from_le_bytes(raw.try_into().ok()?))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn usize_from_u32(value: u32) -> Option<usize> {
    usize::try_from(value).ok()
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn usize_from_u64(value: u64) -> Option<usize> {
    usize::try_from(value).ok()
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn process_vm_read_exact(
    host_pid: i32,
    addr: u64,
    len: usize,
) -> Result<Vec<u8>, KboxlikeMemoryReadError> {
    const CHUNK_LEN: usize = 1024 * 1024;

    if host_pid <= 0 {
        return Err(KboxlikeMemoryReadError {
            bytes_read: 0,
            reason: "invalid tracee pid",
        });
    }
    if len == 0 {
        return Ok(Vec::new());
    }

    let mut out = Vec::with_capacity(len);
    while out.len() < len {
        let offset = out.len();
        let chunk_len = (len - offset).min(CHUNK_LEN);
        let Some(remote_addr) = addr.checked_add(offset as u64) else {
            return Err(KboxlikeMemoryReadError {
                bytes_read: out.len(),
                reason: "tracee memory address overflows",
            });
        };
        let mut chunk = vec![0u8; chunk_len];
        let local = libc::iovec {
            iov_base: chunk.as_mut_ptr() as *mut libc::c_void,
            iov_len: chunk.len(),
        };
        let remote = libc::iovec {
            iov_base: remote_addr as *mut libc::c_void,
            iov_len: chunk.len(),
        };
        let read = unsafe { libc::process_vm_readv(host_pid, &local, 1, &remote, 1, 0) };
        if read < 0 {
            return Err(KboxlikeMemoryReadError {
                bytes_read: out.len(),
                reason: "process_vm_readv failed",
            });
        }
        if read == 0 {
            return Err(KboxlikeMemoryReadError {
                bytes_read: out.len(),
                reason: "process_vm_readv returned zero",
            });
        }
        let read = read as usize;
        out.extend_from_slice(&chunk[..read]);
        if read != chunk_len {
            return Err(KboxlikeMemoryReadError {
                bytes_read: out.len(),
                reason: "process_vm_readv partial mapping read",
            });
        }
    }

    Ok(out)
}

#[cfg(all(test, target_os = "linux", target_arch = "x86_64"))]
mod tests {
    use super::*;
    use std::collections::BTreeMap;
    use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};

    #[derive(Default)]
    struct FakeRegs {
        regs: BTreeMap<i32, libc::user_regs_struct>,
    }

    impl TraceeRegisterReader for FakeRegs {
        fn thread_regs(&mut self, host_pid: i32) -> Result<libc::user_regs_struct, FdError> {
            self.regs
                .get(&host_pid)
                .copied()
                .ok_or(FdError::TraceeInstallFailed("missing fake regs"))
        }
    }

    #[derive(Default)]
    struct FakeMaps {
        maps: BTreeMap<i32, Vec<KboxlikeMapEntry>>,
    }

    #[derive(Default)]
    struct FakeMemory {
        reads: BTreeMap<(i32, u64, usize), Result<Vec<u8>, KboxlikeMemoryReadError>>,
    }

    impl TraceeMapsReader for FakeMaps {
        fn maps_for_pid(&mut self, host_pid: i32) -> Result<Vec<KboxlikeMapEntry>, FdError> {
            self.maps
                .get(&host_pid)
                .cloned()
                .ok_or(FdError::TraceeInstallFailed("missing fake maps"))
        }
    }

    fn fake_regs(rip: u64) -> libc::user_regs_struct {
        let mut regs: libc::user_regs_struct = unsafe { std::mem::zeroed() };
        regs.rip = rip;
        regs
    }

    fn put_u16(bytes: &mut [u8], offset: usize, value: u16) {
        bytes[offset..offset + 2].copy_from_slice(&value.to_le_bytes());
    }

    fn put_u32(bytes: &mut [u8], offset: usize, value: u32) {
        bytes[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
    }

    fn put_u64(bytes: &mut [u8], offset: usize, value: u64) {
        bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
    }

    impl TraceeMemoryReader for FakeMemory {
        fn read_exact(
            &mut self,
            host_pid: i32,
            addr: u64,
            len: usize,
        ) -> Result<Vec<u8>, KboxlikeMemoryReadError> {
            self.reads
                .get(&(host_pid, addr, len))
                .cloned()
                .unwrap_or_else(|| {
                    Err(KboxlikeMemoryReadError {
                        bytes_read: 0,
                        reason: "missing fake memory",
                    })
                })
        }
    }

    #[test]
    fn capture_stopped_tracees_records_regs_and_dump_candidate_maps() {
        let tracees = vec![
            KboxlikeTraceeIdentity {
                host_pid: 2001,
                model_pid: 7,
                model_thread_id: 1,
            },
            KboxlikeTraceeIdentity {
                host_pid: 2002,
                model_pid: 7,
                model_thread_id: 2,
            },
            KboxlikeTraceeIdentity {
                host_pid: 3001,
                model_pid: 9,
                model_thread_id: 3,
            },
        ];
        let mut regs = FakeRegs::default();
        regs.regs.insert(2001, fake_regs(0x1000));
        regs.regs.insert(2002, fake_regs(0x2000));
        regs.regs.insert(3001, fake_regs(0x3000));
        let mut maps = FakeMaps::default();
        maps.maps.insert(
            2001,
            super::super::memory::parse_proc_maps(
                "1000-2000 r--p 00000000 00:00 0\n\
                 3000-4000 ---p 00000000 00:00 0\n\
                 7000-8000 r--p 00000000 00:00 0 [vvar]\n",
            ),
        );
        maps.maps.insert(
            2002,
            super::super::memory::parse_proc_maps("9000-b000 rw-p 00000000 00:00 0 [heap]\n"),
        );
        maps.maps.insert(3001, Vec::new());

        let snapshot = capture_stopped_tracees_with(tracees, &mut regs, &mut maps).unwrap();

        assert_eq!(snapshot.threads.len(), 3);
        assert_eq!(snapshot.threads[0].host_pid, 2001);
        assert_eq!(snapshot.threads[0].model_pid, 7);
        assert_eq!(snapshot.threads[0].model_thread_id, 1);
        assert_eq!(snapshot.threads[0].regs.rip, 0x1000);
        assert_eq!(snapshot.threads[2].host_pid, 3001);
        assert_eq!(snapshot.threads[2].model_pid, 9);
        assert_eq!(snapshot.threads[2].model_thread_id, 3);
        assert_eq!(snapshot.thread_kernel_states.len(), 3);
        assert_eq!(snapshot.thread_kernel_states[0].host_pid, 2001);
        assert_eq!(snapshot.thread_kernel_states[0].clear_child_tid, None);
        assert_eq!(snapshot.thread_kernel_states[1].robust_list_head, None);
        assert_eq!(snapshot.memory_maps[0].dump_candidate_bytes, 0x1000);
        assert_eq!(snapshot.memory_maps[1].dump_candidate_bytes, 0x2000);
        assert_eq!(snapshot.memory_maps[2].dump_candidate_bytes, 0);
        assert_eq!(snapshot.fd_tables.len(), 3);
        assert!(snapshot.fd_tables.iter().all(|table| table.fds.is_empty()));
    }

    #[test]
    fn fd_target_classifier_recognizes_chromium_kernel_objects() {
        assert_eq!(classify_fd_target("pipe:[123]"), KboxlikeFdTargetKind::Pipe);
        assert_eq!(
            classify_fd_target("socket:[456]"),
            KboxlikeFdTargetKind::Socket
        );
        assert_eq!(
            classify_fd_target("anon_inode:[eventfd]"),
            KboxlikeFdTargetKind::EventFd
        );
        assert_eq!(
            classify_fd_target("anon_inode:[eventpoll]"),
            KboxlikeFdTargetKind::EventPoll
        );
        assert_eq!(
            classify_fd_target("anon_inode:[timerfd]"),
            KboxlikeFdTargetKind::TimerFd
        );
        assert_eq!(
            classify_fd_target("/memfd:kboxlike (deleted)"),
            KboxlikeFdTargetKind::Memfd
        );
        assert_eq!(
            classify_fd_target("/dev/shm/chrome"),
            KboxlikeFdTargetKind::Device
        );
        assert_eq!(
            classify_fd_target("/usr/bin/chromium"),
            KboxlikeFdTargetKind::RegularPath
        );
    }

    fn fake_fd(fd: i32, target: &str, flags: u64) -> KboxlikeFdSnapshot {
        KboxlikeFdSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 1,
            fd,
            target: target.to_string(),
            kind: classify_fd_target(target),
            object_identity: None,
            flags: Some(flags),
            cloexec: flags & libc::O_CLOEXEC as u64 != 0,
            eventfd_count: None,
            eventfd_semaphore: None,
            timerfd: None,
            epoll_interests: Vec::new(),
        }
    }

    #[test]
    fn fdinfo_parser_reads_epoll_interest_records() {
        let fdinfo = "\
pos:\t0
flags:\t02000002
mnt_id:\t16
ino:\t1066
tfd:        5 events:       19 data:     7fff00000005  pos:0 ino:42a sdev:f
tfd:        3 events:       19 data:     7fff00000003  pos:0 ino:308bbad sdev:e
";

        assert_eq!(parse_proc_fdinfo_flags(fdinfo), Some(0o2000002));
        assert_eq!(
            parse_proc_fdinfo_object_identity(fdinfo),
            Some(KboxlikeFdObjectIdentity {
                mnt_id: Some(16),
                ino: Some(1066),
                eventfd_id: None,
            })
        );
        assert_eq!(
            parse_proc_fdinfo_epoll_interests(fdinfo),
            vec![
                KboxlikeEpollInterestSnapshot {
                    target_fd: 5,
                    events: 0x19,
                    data: 0x7fff00000005,
                },
                KboxlikeEpollInterestSnapshot {
                    target_fd: 3,
                    events: 0x19,
                    data: 0x7fff00000003,
                },
            ]
        );
    }

    #[test]
    fn eventfd_shadow_planner_uses_fdinfo_counter_and_flags() {
        let mut eventfd = fake_fd(
            8,
            "anon_inode:[eventfd]",
            (libc::O_CLOEXEC | libc::O_NONBLOCK) as u64,
        );
        eventfd.eventfd_count = Some(7);
        eventfd.eventfd_semaphore = Some(true);
        let fd_tables = vec![KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 1,
            fds: vec![eventfd],
        }];

        assert_eq!(
            plan_eventfd_shadow_backings(&fd_tables),
            vec![KboxlikeFdShadowBackingPlan {
                host_pid: 2001,
                model_pid: 7,
                model_thread_id: 1,
                fd: 8,
                target: "anon_inode:[eventfd]".to_string(),
                cloexec: true,
                backing: ShadowBacking::HostPassthrough {
                    label: format!(
                        "eventfd:init=7,flags={}",
                        libc::EFD_NONBLOCK | libc::EFD_SEMAPHORE
                    ),
                },
            }]
        );
    }

    #[test]
    fn fdinfo_parser_reads_timerfd_state() {
        let fdinfo = "\
pos:\t0
flags:\t02004002
mnt_id:\t16
ino:\t1066
clockid: 1
ticks: 2
settime flags: 00
it_value: (60, 987620682)
it_interval: (2, 123456789)
";

        assert_eq!(parse_proc_fdinfo_flags(fdinfo), Some(0o2004002));
        assert_eq!(
            parse_proc_fdinfo_object_identity(fdinfo),
            Some(KboxlikeFdObjectIdentity {
                mnt_id: Some(16),
                ino: Some(1066),
                eventfd_id: None,
            })
        );
        assert_eq!(
            parse_proc_fdinfo_timerfd(fdinfo),
            Some(KboxlikeTimerFdSnapshot {
                clockid: 1,
                ticks: 2,
                settime_flags: 0,
                value_sec: 60,
                value_nsec: 987_620_682,
                interval_sec: 2,
                interval_nsec: 123_456_789,
            })
        );
    }

    #[test]
    fn timerfd_shadow_planner_uses_fdinfo_timer_state_and_flags() {
        let mut timerfd = fake_fd(
            10,
            "anon_inode:[timerfd]",
            (libc::O_CLOEXEC | libc::O_NONBLOCK) as u64,
        );
        timerfd.timerfd = Some(KboxlikeTimerFdSnapshot {
            clockid: libc::CLOCK_MONOTONIC,
            ticks: 0,
            settime_flags: 0,
            value_sec: 60,
            value_nsec: 987_620_682,
            interval_sec: 2,
            interval_nsec: 123_456_789,
        });
        let fd_tables = vec![KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 1,
            fds: vec![timerfd],
        }];

        assert_eq!(
            plan_timerfd_shadow_backings(&fd_tables),
            vec![KboxlikeFdShadowBackingPlan {
                host_pid: 2001,
                model_pid: 7,
                model_thread_id: 1,
                fd: 10,
                target: "anon_inode:[timerfd]".to_string(),
                cloexec: true,
                backing: ShadowBacking::HostPassthrough {
                    label: format!(
                        "timerfd:clockid={},flags={},value_sec=60,value_nsec=987620682,interval_sec=2,interval_nsec=123456789",
                        libc::CLOCK_MONOTONIC,
                        libc::TFD_NONBLOCK
                    ),
                },
            }]
        );
    }

    #[test]
    fn epoll_shadow_planner_links_interests_to_watched_fd_backings() {
        let mut fd_system = KboxlikeFdSystem::new();
        let model_pid = fd_system.spawn_init();
        for fd in [3, 4, 8, 9] {
            fd_system
                .insert_plain_fd(model_pid, fd, Some(fd as i64), false)
                .unwrap();
        }

        let mut pipe_read = fake_fd(3, "pipe:[12345]", libc::O_CLOEXEC as u64);
        let mut pipe_write = fake_fd(4, "pipe:[12345]", (libc::O_CLOEXEC | libc::O_WRONLY) as u64);
        let mut eventfd = fake_fd(
            8,
            "anon_inode:[eventfd]",
            (libc::O_CLOEXEC | libc::O_NONBLOCK) as u64,
        );
        let mut epoll = fake_fd(9, "anon_inode:[eventpoll]", libc::O_CLOEXEC as u64);
        for fd in [&mut pipe_read, &mut pipe_write, &mut eventfd, &mut epoll] {
            fd.model_pid = model_pid;
        }
        eventfd.eventfd_count = Some(7);
        eventfd.eventfd_semaphore = Some(false);
        epoll.epoll_interests = vec![
            KboxlikeEpollInterestSnapshot {
                target_fd: 3,
                events: 0x19,
                data: 0x7fff00000003,
            },
            KboxlikeEpollInterestSnapshot {
                target_fd: 8,
                events: 0x19,
                data: 0x7fff00000008,
            },
        ];
        let fd_tables = vec![KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid,
            model_thread_id: 1,
            fds: vec![pipe_read, pipe_write, eventfd, epoll],
        }];

        let mut watched_fd_plans = plan_pipe_shadow_backings(&fd_tables);
        watched_fd_plans.extend(plan_eventfd_shadow_backings(&fd_tables));
        apply_fd_shadow_backing_plans(&mut fd_system, &watched_fd_plans).unwrap();

        let epoll_plans = plan_epoll_shadow_backings(&fd_tables, &fd_system);

        assert_eq!(epoll_plans.len(), 1);
        assert_eq!(
            epoll_plans[0].fd_plan.backing,
            ShadowBacking::HostPassthrough {
                label: "epoll:epoll2001_9".to_string(),
            }
        );
        assert_eq!(epoll_plans[0].interests.len(), 2);
        assert_eq!(
            epoll_plans[0].interests[0].target_backing,
            Some(ShadowBacking::HostPassthrough {
                label: "pipe:pipe12345:0".to_string(),
            })
        );
        assert_eq!(
            epoll_plans[0].interests[1].target_backing,
            Some(ShadowBacking::HostPassthrough {
                label: format!("eventfd:init=7,flags={}", libc::EFD_NONBLOCK),
            })
        );
        apply_fd_shadow_backing_plans(
            &mut fd_system,
            &epoll_plans
                .iter()
                .map(|plan| plan.fd_plan.clone())
                .collect::<Vec<_>>(),
        )
        .unwrap();
        assert!(matches!(
            fd_system.route_fd_operation(model_pid, 9, super::super::fd::FdOperation::Generic),
            Ok(super::super::fd::FdRoute::HostPassthrough { shadow })
                if shadow.kind == ShadowKind::HostPassthrough
        ));
    }

    #[test]
    fn pipe_shadow_planner_pairs_unique_read_and_write_ends() {
        let fd_tables = vec![KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 1,
            fds: vec![
                fake_fd(3, "pipe:[12345]", libc::O_CLOEXEC as u64),
                fake_fd(4, "pipe:[12345]", (libc::O_CLOEXEC | libc::O_WRONLY) as u64),
                fake_fd(5, "socket:[999]", libc::O_CLOEXEC as u64),
            ],
        }];

        let plans = plan_pipe_shadow_backings(&fd_tables);

        assert_eq!(
            plans,
            vec![
                KboxlikeFdShadowBackingPlan {
                    host_pid: 2001,
                    model_pid: 7,
                    model_thread_id: 1,
                    fd: 3,
                    target: "pipe:[12345]".to_string(),
                    cloexec: true,
                    backing: ShadowBacking::HostPassthrough {
                        label: "pipe:pipe12345:0".to_string(),
                    },
                },
                KboxlikeFdShadowBackingPlan {
                    host_pid: 2001,
                    model_pid: 7,
                    model_thread_id: 1,
                    fd: 4,
                    target: "pipe:[12345]".to_string(),
                    cloexec: true,
                    backing: ShadowBacking::HostPassthrough {
                        label: "pipe:pipe12345:1".to_string(),
                    },
                },
            ]
        );
    }

    #[test]
    fn pipe_shadow_planner_keeps_duplicate_endpoint_identity() {
        let fd_tables = vec![KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 1,
            fds: vec![
                fake_fd(3, "pipe:[12345]", libc::O_CLOEXEC as u64),
                fake_fd(4, "pipe:[12345]", libc::O_CLOEXEC as u64),
                fake_fd(5, "pipe:[12345]", (libc::O_CLOEXEC | libc::O_WRONLY) as u64),
            ],
        }];

        let plans = plan_pipe_shadow_backings(&fd_tables);

        assert_eq!(plans.len(), 3);
        assert_eq!(
            plans
                .iter()
                .map(|plan| (&plan.fd, &plan.backing))
                .collect::<Vec<_>>(),
            vec![
                (
                    &3,
                    &ShadowBacking::HostPassthrough {
                        label: "pipe:pipe12345:0".to_string()
                    }
                ),
                (
                    &4,
                    &ShadowBacking::HostPassthrough {
                        label: "pipe:pipe12345:0".to_string()
                    }
                ),
                (
                    &5,
                    &ShadowBacking::HostPassthrough {
                        label: "pipe:pipe12345:1".to_string()
                    }
                ),
            ]
        );
    }

    #[test]
    fn socketpair_shadow_planner_pairs_two_socket_endpoints() {
        let fd_tables = vec![KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 1,
            fds: vec![
                fake_fd(6, "socket:[111]", libc::O_CLOEXEC as u64),
                fake_fd(
                    7,
                    "socket:[222]",
                    (libc::O_CLOEXEC | libc::O_NONBLOCK) as u64,
                ),
                fake_fd(8, "anon_inode:[eventfd]", libc::O_CLOEXEC as u64),
            ],
        }];

        let plans = plan_socketpair_shadow_backings(&fd_tables);

        assert_eq!(
            plans,
            vec![
                KboxlikeFdShadowBackingPlan {
                    host_pid: 2001,
                    model_pid: 7,
                    model_thread_id: 1,
                    fd: 6,
                    target: "socket:[111]".to_string(),
                    cloexec: true,
                    backing: ShadowBacking::HostPassthrough {
                        label: "socketpair:socketpair111_222:0".to_string(),
                    },
                },
                KboxlikeFdShadowBackingPlan {
                    host_pid: 2001,
                    model_pid: 7,
                    model_thread_id: 1,
                    fd: 7,
                    target: "socket:[222]".to_string(),
                    cloexec: true,
                    backing: ShadowBacking::HostPassthrough {
                        label: "socketpair:socketpair111_222:1".to_string(),
                    },
                },
            ]
        );
    }

    #[test]
    fn socketpair_shadow_planner_preserves_duplicate_endpoint_identity() {
        let fd_tables = vec![KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 1,
            fds: vec![
                fake_fd(6, "socket:[111]", libc::O_CLOEXEC as u64),
                fake_fd(8, "socket:[111]", libc::O_CLOEXEC as u64),
                fake_fd(7, "socket:[222]", libc::O_CLOEXEC as u64),
            ],
        }];

        let plans = plan_socketpair_shadow_backings(&fd_tables);

        assert_eq!(plans.len(), 3);
        assert_eq!(
            plans
                .iter()
                .map(|plan| (&plan.fd, &plan.backing))
                .collect::<Vec<_>>(),
            vec![
                (
                    &6,
                    &ShadowBacking::HostPassthrough {
                        label: "socketpair:socketpair111_222:0".to_string()
                    }
                ),
                (
                    &7,
                    &ShadowBacking::HostPassthrough {
                        label: "socketpair:socketpair111_222:1".to_string()
                    }
                ),
                (
                    &8,
                    &ShadowBacking::HostPassthrough {
                        label: "socketpair:socketpair111_222:0".to_string()
                    }
                ),
            ]
        );
    }

    #[test]
    fn socketpair_shadow_planner_skips_ambiguous_socket_sets() {
        let fd_tables = vec![KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 1,
            fds: vec![
                fake_fd(6, "socket:[111]", libc::O_CLOEXEC as u64),
                fake_fd(7, "socket:[222]", libc::O_CLOEXEC as u64),
                fake_fd(8, "socket:[333]", libc::O_CLOEXEC as u64),
            ],
        }];

        assert!(plan_socketpair_shadow_backings(&fd_tables).is_empty());
    }

    #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
    #[test]
    fn scm_rights_mapping_accepts_duplicate_fd_aliases_with_same_backing() {
        let raw_received = unsafe { libc::eventfd(7, libc::EFD_CLOEXEC) };
        assert!(raw_received >= 0);
        let received = unsafe { OwnedFd::from_raw_fd(raw_received) };
        let target = fs::read_link(format!("/proc/self/fd/{}", received.as_raw_fd()))
            .unwrap()
            .to_string_lossy()
            .into_owned();
        let object_identity = read_proc_fdinfo(unsafe { libc::getpid() }, received.as_raw_fd())
            .and_then(|fdinfo| parse_proc_fdinfo_object_identity(&fdinfo));
        let mut fd_system = KboxlikeFdSystem::new();
        let model_pid = fd_system.spawn_init();
        let backing = ShadowBacking::HostPassthrough {
            label: "eventfd:init=7,flags=0".to_string(),
        };
        fd_system
            .insert_shadow_fd_with_backing(
                model_pid,
                8,
                None,
                ShadowObject {
                    kind: ShadowKind::HostPassthrough,
                    supervisor_fd: 8,
                },
                backing.clone(),
                false,
            )
            .unwrap();
        fd_system.dup_to(model_pid, 8, 9, false).unwrap();
        let mut fd8 = fake_fd(8, &target, libc::O_CLOEXEC as u64);
        fd8.model_pid = model_pid;
        fd8.object_identity = object_identity;
        let mut fd9 = fake_fd(9, &target, libc::O_CLOEXEC as u64);
        fd9.model_pid = model_pid;
        fd9.object_identity = object_identity;
        let table = KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid,
            model_thread_id: 1,
            fds: vec![fd8, fd9],
        };

        let mapped =
            map_received_fds_to_backings(&table, &fd_system, model_pid, vec![received]).unwrap();

        assert_eq!(mapped, (vec![8], vec![backing]));
    }

    #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
    #[test]
    fn scm_rights_mapping_uses_fdinfo_identity_for_same_target_different_backings() {
        let raw_received = unsafe { libc::eventfd(7, libc::EFD_CLOEXEC) };
        assert!(raw_received >= 0);
        let received = unsafe { OwnedFd::from_raw_fd(raw_received) };
        let target = fs::read_link(format!("/proc/self/fd/{}", received.as_raw_fd()))
            .unwrap()
            .to_string_lossy()
            .into_owned();
        let object_identity = read_proc_fdinfo(unsafe { libc::getpid() }, received.as_raw_fd())
            .and_then(|fdinfo| parse_proc_fdinfo_object_identity(&fdinfo))
            .unwrap();
        let mut fd_system = KboxlikeFdSystem::new();
        let model_pid = fd_system.spawn_init();
        let expected_backing = ShadowBacking::HostPassthrough {
            label: "eventfd:init=7,flags=0".to_string(),
        };
        for (fd, label) in [
            (8, "eventfd:init=7,flags=0"),
            (9, "eventfd:init=99,flags=0"),
        ] {
            fd_system
                .insert_shadow_fd_with_backing(
                    model_pid,
                    fd,
                    None,
                    ShadowObject {
                        kind: ShadowKind::HostPassthrough,
                        supervisor_fd: fd,
                    },
                    ShadowBacking::HostPassthrough {
                        label: label.to_string(),
                    },
                    false,
                )
                .unwrap();
        }
        let mut fd8 = fake_fd(8, &target, libc::O_CLOEXEC as u64);
        fd8.model_pid = model_pid;
        fd8.object_identity = Some(object_identity);
        let mut fd9 = fake_fd(9, &target, libc::O_CLOEXEC as u64);
        fd9.model_pid = model_pid;
        fd9.object_identity = Some(KboxlikeFdObjectIdentity {
            eventfd_id: object_identity.eventfd_id.map(|id| id + 1),
            ..object_identity
        });
        let table = KboxlikeTraceeFdSnapshot {
            host_pid: 2001,
            model_pid,
            model_thread_id: 1,
            fds: vec![fd8, fd9],
        };

        let mapped =
            map_received_fds_to_backings(&table, &fd_system, model_pid, vec![received]).unwrap();

        assert_eq!(mapped, (vec![8], vec![expected_backing]));
    }

    #[test]
    fn applies_fd_shadow_backing_plans_to_fd_model() {
        let mut fd_system = KboxlikeFdSystem::new();
        let model_pid = fd_system.spawn_init();
        fd_system
            .insert_plain_fd(model_pid, 3, Some(3), false)
            .unwrap();
        fd_system
            .insert_plain_fd(model_pid, 4, Some(4), false)
            .unwrap();
        let plans = vec![
            KboxlikeFdShadowBackingPlan {
                host_pid: 2001,
                model_pid,
                model_thread_id: 1,
                fd: 3,
                target: "pipe:[12345]".to_string(),
                cloexec: false,
                backing: ShadowBacking::HostPassthrough {
                    label: "pipe:pipe12345:0".to_string(),
                },
            },
            KboxlikeFdShadowBackingPlan {
                host_pid: 2001,
                model_pid,
                model_thread_id: 1,
                fd: 4,
                target: "pipe:[12345]".to_string(),
                cloexec: true,
                backing: ShadowBacking::HostPassthrough {
                    label: "pipe:pipe12345:1".to_string(),
                },
            },
        ];

        assert_eq!(
            apply_fd_shadow_backing_plans(&mut fd_system, &plans).unwrap(),
            2
        );

        assert_eq!(
            fd_system.shadow_backing(model_pid, 3).unwrap(),
            Some(&ShadowBacking::HostPassthrough {
                label: "pipe:pipe12345:0".to_string(),
            })
        );
        assert_eq!(
            fd_system.shadow_backing(model_pid, 4).unwrap(),
            Some(&ShadowBacking::HostPassthrough {
                label: "pipe:pipe12345:1".to_string(),
            })
        );
        assert!(!fd_system.descriptor(model_pid, 3).unwrap().cloexec);
        assert!(fd_system.descriptor(model_pid, 4).unwrap().cloexec);
        assert!(matches!(
            fd_system.route_fd_operation(model_pid, 3, super::super::fd::FdOperation::Read),
            Ok(super::super::fd::FdRoute::HostPassthrough { shadow })
                if shadow.kind == ShadowKind::HostPassthrough
        ));
    }

    #[test]
    fn applies_duplicate_pipe_endpoint_plans_as_shared_open_file() {
        let mut fd_system = KboxlikeFdSystem::new();
        let browser = fd_system.spawn_init();
        let helper = fd_system.fork(browser).unwrap();
        for (pid, fd) in [(browser, 3), (helper, 4), (browser, 5)] {
            fd_system
                .insert_plain_fd(pid, fd, Some(fd as i64), false)
                .unwrap();
        }
        let plans = vec![
            KboxlikeFdShadowBackingPlan {
                host_pid: 2001,
                model_pid: browser,
                model_thread_id: 1,
                fd: 3,
                target: "pipe:[12345]".to_string(),
                cloexec: false,
                backing: ShadowBacking::HostPassthrough {
                    label: "pipe:pipe12345:0".to_string(),
                },
            },
            KboxlikeFdShadowBackingPlan {
                host_pid: 2002,
                model_pid: helper,
                model_thread_id: 2,
                fd: 4,
                target: "pipe:[12345]".to_string(),
                cloexec: true,
                backing: ShadowBacking::HostPassthrough {
                    label: "pipe:pipe12345:0".to_string(),
                },
            },
            KboxlikeFdShadowBackingPlan {
                host_pid: 2001,
                model_pid: browser,
                model_thread_id: 1,
                fd: 5,
                target: "pipe:[12345]".to_string(),
                cloexec: false,
                backing: ShadowBacking::HostPassthrough {
                    label: "pipe:pipe12345:1".to_string(),
                },
            },
        ];

        assert_eq!(
            apply_fd_shadow_backing_plans(&mut fd_system, &plans).unwrap(),
            3
        );

        let first_read = fd_system.descriptor(browser, 3).unwrap();
        let duplicate_read = fd_system.descriptor(helper, 4).unwrap();
        let write_end = fd_system.descriptor(browser, 5).unwrap();
        assert_eq!(first_read.open_file, duplicate_read.open_file);
        assert_ne!(first_read.open_file, write_end.open_file);
        assert_eq!(first_read.ref_count, 2);
        assert!(duplicate_read.cloexec);
        assert_eq!(
            fd_system.shadow_backing(helper, 4).unwrap(),
            Some(&ShadowBacking::HostPassthrough {
                label: "pipe:pipe12345:0".to_string(),
            })
        );
    }

    #[test]
    fn capture_stopped_tracees_records_tracked_thread_kernel_state() {
        let tracees = vec![
            KboxlikeTraceeIdentity {
                host_pid: 2001,
                model_pid: 7,
                model_thread_id: 1,
            },
            KboxlikeTraceeIdentity {
                host_pid: 2002,
                model_pid: 7,
                model_thread_id: 2,
            },
        ];
        let kernel_states = vec![KboxlikeThreadKernelStateSnapshot {
            host_pid: 2002,
            model_pid: 7,
            model_thread_id: 2,
            clear_child_tid: Some(0x7000),
            robust_list_head: Some(0x8000),
            robust_list_len: 24,
        }];
        let mut regs = FakeRegs::default();
        regs.regs.insert(2001, fake_regs(0x1000));
        regs.regs.insert(2002, fake_regs(0x2000));
        let mut maps = FakeMaps::default();
        maps.maps.insert(2001, Vec::new());
        maps.maps.insert(2002, Vec::new());

        let snapshot = capture_stopped_tracees_with_kernel_states(
            tracees,
            kernel_states,
            &mut regs,
            &mut maps,
        )
        .unwrap();

        assert_eq!(snapshot.thread_kernel_states.len(), 2);
        assert_eq!(snapshot.thread_kernel_states[0].host_pid, 2001);
        assert_eq!(snapshot.thread_kernel_states[0].clear_child_tid, None);
        assert_eq!(snapshot.thread_kernel_states[1].host_pid, 2002);
        assert_eq!(
            snapshot.thread_kernel_states[1].clear_child_tid,
            Some(0x7000)
        );
        assert_eq!(
            snapshot.thread_kernel_states[1].robust_list_head,
            Some(0x8000)
        );
        assert_eq!(snapshot.thread_kernel_states[1].robust_list_len, 24);
    }

    #[test]
    fn capture_dump_candidate_memory_reads_only_candidate_mappings() {
        let memory_maps = vec![KboxlikeTraceeMapSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 7,
            maps: super::super::memory::parse_proc_maps(
                "1000-1004 r--p 00000000 00:00 0\n\
                 2000-2004 ---p 00000000 00:00 0\n\
                 3000-3004 r--p 00000000 00:00 0 [vvar]\n\
                 4000-4002 rw-p 00000000 00:00 0 [heap]\n\
                 5000-5003 r-xp 00000000 00:00 0 [vdso]\n",
            ),
            dump_candidate_bytes: 9,
        }];
        let mut memory = FakeMemory::default();
        memory.reads.insert((2001, 0x1000, 4), Ok(b"abcd".to_vec()));
        memory.reads.insert((2001, 0x4000, 2), Ok(b"ef".to_vec()));
        memory.reads.insert((2001, 0x5000, 3), Ok(b"ghi".to_vec()));

        let capture = capture_dump_candidate_memory_with(&memory_maps, &mut memory);

        assert!(capture.failures.is_empty());
        assert_eq!(
            capture.dumps,
            vec![
                KboxlikeMemoryDump {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    bytes: b"abcd".to_vec(),
                },
                KboxlikeMemoryDump {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x4000,
                    bytes: b"ef".to_vec(),
                },
                KboxlikeMemoryDump {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x5000,
                    bytes: b"ghi".to_vec(),
                },
            ]
        );
        assert_eq!(capture.captured_bytes(), 9);
        assert_eq!(capture.failed_bytes(), 0);
    }

    #[test]
    fn vdso_patcher_replaces_exported_entry_with_syscall_stub() {
        let mut bytes = vec![0x90; 0x220];
        bytes[0..4].copy_from_slice(b"\x7fELF");
        bytes[4] = 2;
        bytes[5] = 1;
        put_u64(&mut bytes, 0x28, 0x40);
        put_u16(&mut bytes, 0x3a, 64);
        put_u16(&mut bytes, 0x3c, 3);

        let dynsym_section = 0x40 + 64;
        put_u32(&mut bytes, dynsym_section + 4, 11);
        put_u64(&mut bytes, dynsym_section + 24, 0x100);
        put_u64(&mut bytes, dynsym_section + 32, 24);
        put_u32(&mut bytes, dynsym_section + 40, 2);
        put_u64(&mut bytes, dynsym_section + 56, 24);

        let strtab_section = 0x40 + 128;
        let names = b"\0__vdso_gettimeofday\0";
        put_u32(&mut bytes, strtab_section + 4, 3);
        put_u64(&mut bytes, strtab_section + 24, 0x180);
        put_u64(&mut bytes, strtab_section + 32, names.len() as u64);
        bytes[0x180..0x180 + names.len()].copy_from_slice(names);

        put_u32(&mut bytes, 0x100, 1);
        put_u64(&mut bytes, 0x108, 0x200);

        let patched = patch_vdso_syscall_fallbacks(&mut bytes);

        assert_eq!(patched, 1);
        assert_eq!(&bytes[0x200..0x208], &[0xb8, 96, 0, 0, 0, 0x0f, 0x05, 0xc3]);
    }

    #[test]
    fn capture_dump_candidate_memory_records_read_failures_without_stopping() {
        let memory_maps = vec![KboxlikeTraceeMapSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 7,
            maps: super::super::memory::parse_proc_maps(
                "1000-1004 r--p 00000000 00:00 0\n\
                 2000-2004 rw-p 00000000 00:00 0 [heap]\n",
            ),
            dump_candidate_bytes: 8,
        }];
        let mut memory = FakeMemory::default();
        memory.reads.insert((2001, 0x1000, 4), Ok(b"abcd".to_vec()));
        memory.reads.insert(
            (2001, 0x2000, 4),
            Err(KboxlikeMemoryReadError {
                bytes_read: 2,
                reason: "process_vm_readv partial mapping read",
            }),
        );

        let capture = capture_dump_candidate_memory_with(&memory_maps, &mut memory);

        assert_eq!(capture.dumps.len(), 1);
        assert_eq!(capture.captured_bytes(), 4);
        assert_eq!(capture.failed_bytes(), 4);
        assert_eq!(
            capture.failures,
            vec![KboxlikeMemoryDumpFailure {
                host_pid: 2001,
                model_pid: 7,
                start: 0x2000,
                len: 4,
                bytes_read: 2,
                reason: "process_vm_readv partial mapping read",
            }]
        );
    }

    #[test]
    fn capture_shared_object_memory_reads_one_copy_per_backing_extent() {
        let plan = KboxlikeMemoryRestorePlan {
            actions: vec![
                KboxlikeMemoryRestoreAction::RecreateSharedObject {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    len: 4,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    kind: KboxlikeSharedMappingKind::Memfd,
                    path: Some("/memfd:shared".to_owned()),
                    file_offset: 0,
                    dev: "00:01".to_owned(),
                    inode: 99,
                },
                KboxlikeMemoryRestoreAction::RecreateSharedObject {
                    host_pid: 2002,
                    model_pid: 8,
                    start: 0x5000,
                    len: 4,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    kind: KboxlikeSharedMappingKind::Memfd,
                    path: Some("/memfd:shared".to_owned()),
                    file_offset: 0,
                    dev: "00:01".to_owned(),
                    inode: 99,
                },
                KboxlikeMemoryRestoreAction::RecreateSharedObject {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x2000,
                    len: 2,
                    prot: libc::PROT_READ,
                    kind: KboxlikeSharedMappingKind::Memfd,
                    path: Some("/memfd:shared".to_owned()),
                    file_offset: 4,
                    dev: "00:01".to_owned(),
                    inode: 99,
                },
            ],
        };
        let mut memory = FakeMemory::default();
        memory.reads.insert((2001, 0x1000, 4), Ok(b"abcd".to_vec()));
        memory.reads.insert((2001, 0x2000, 2), Ok(b"ef".to_vec()));

        let capture = capture_shared_object_memory_with(&plan, &mut memory);

        assert!(capture.failures.is_empty());
        assert_eq!(capture.skipped_duplicate_extents, 1);
        assert_eq!(capture.captured_bytes(), 6);
        assert_eq!(
            capture.dumps,
            vec![
                KboxlikeSharedMemoryDump {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    kind: KboxlikeSharedMappingKind::Memfd,
                    path: Some("/memfd:shared".to_owned()),
                    file_offset: 0,
                    dev: "00:01".to_owned(),
                    inode: 99,
                    bytes: b"abcd".to_vec(),
                },
                KboxlikeSharedMemoryDump {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x2000,
                    kind: KboxlikeSharedMappingKind::Memfd,
                    path: Some("/memfd:shared".to_owned()),
                    file_offset: 4,
                    dev: "00:01".to_owned(),
                    inode: 99,
                    bytes: b"ef".to_vec(),
                },
            ]
        );
    }

    #[test]
    fn capture_shared_object_memory_records_read_failures() {
        let plan = KboxlikeMemoryRestorePlan {
            actions: vec![KboxlikeMemoryRestoreAction::RecreateSharedObject {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
                len: 4,
                prot: libc::PROT_READ,
                kind: KboxlikeSharedMappingKind::Memfd,
                path: Some("/memfd:shared".to_owned()),
                file_offset: 0,
                dev: "00:01".to_owned(),
                inode: 99,
            }],
        };
        let mut memory = FakeMemory::default();
        memory.reads.insert(
            (2001, 0x1000, 4),
            Err(KboxlikeMemoryReadError {
                bytes_read: 2,
                reason: "process_vm_readv partial mapping read",
            }),
        );

        let capture = capture_shared_object_memory_with(&plan, &mut memory);

        assert!(capture.dumps.is_empty());
        assert_eq!(capture.failed_bytes(), 4);
        assert_eq!(
            capture.failures,
            vec![KboxlikeSharedMemoryDumpFailure {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
                len: 4,
                kind: KboxlikeSharedMappingKind::Memfd,
                path: Some("/memfd:shared".to_owned()),
                file_offset: 0,
                dev: "00:01".to_owned(),
                inode: 99,
                bytes_read: 2,
                reason: "process_vm_readv partial mapping read",
            }]
        );
    }

    #[test]
    fn restore_classifier_splits_clean_files_cow_bytes_and_skips() {
        let memory_maps = vec![KboxlikeTraceeMapSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 7,
            maps: super::super::memory::parse_proc_maps(
                "1000-2000 r-xp 00000000 08:02 11 /usr/bin/chrome\n\
                 2000-3000 rw-p 00001000 08:02 11 /usr/bin/chrome\n\
                 3000-4000 rw-p 00000000 00:00 0 [heap]\n\
                 4000-5000 ---p 00000000 00:00 0\n\
                 5000-6000 r--p 00000000 00:00 0 [vvar]\n\
                 6000-7000 r--p 00000000 00:00 0 [vvar_vclock]\n\
                 7000-8000 r-xp 00000000 00:00 0 [vdso]\n\
                 8000-9000 --xp 00000000 00:00 0 [vsyscall]\n\
                 9000-a000 r--p 00002000 08:02 11 /usr/bin/chrome\n",
            ),
            dump_candidate_bytes: 0x4000,
        }];

        let plan = classify_memory_restore_actions(&memory_maps);

        assert_eq!(
            plan.actions,
            vec![
                KboxlikeMemoryRestoreAction::RemapCleanFile {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    len: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_EXEC,
                    path: PathBuf::from("/usr/bin/chrome"),
                    file_offset: 0,
                    dev: "08:02".to_owned(),
                    inode: 11,
                },
                KboxlikeMemoryRestoreAction::DumpPrivateFileCow {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x2000,
                    len: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    path: PathBuf::from("/usr/bin/chrome"),
                    file_offset: 0x1000,
                    dev: "08:02".to_owned(),
                    inode: 11,
                    reason: "writable private file mapping may contain COW bytes",
                },
                KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x3000,
                    len: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    label: Some("[heap]".to_owned()),
                },
                KboxlikeMemoryRestoreAction::SkipInaccessible {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x4000,
                    len: 0x1000,
                    prot: libc::PROT_NONE,
                },
                KboxlikeMemoryRestoreAction::SkipKernelSynthetic {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x5000,
                    len: 0x1000,
                    label: "[vvar]".to_owned(),
                },
                KboxlikeMemoryRestoreAction::SkipKernelSynthetic {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x6000,
                    len: 0x1000,
                    label: "[vvar_vclock]".to_owned(),
                },
                KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x7000,
                    len: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_EXEC,
                    label: Some("[vdso]".to_owned()),
                },
                KboxlikeMemoryRestoreAction::SkipKernelSynthetic {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x8000,
                    len: 0x1000,
                    label: "[vsyscall]".to_owned(),
                },
                KboxlikeMemoryRestoreAction::DumpPrivateFileCow {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x9000,
                    len: 0x1000,
                    prot: libc::PROT_READ,
                    path: PathBuf::from("/usr/bin/chrome"),
                    file_offset: 0x2000,
                    dev: "08:02".to_owned(),
                    inode: 11,
                    reason: "non-executable private file mapping may contain relocated COW bytes",
                },
            ]
        );
    }

    #[test]
    fn restore_classifier_keeps_shared_objects_out_of_plain_byte_dump() {
        let memory_maps = vec![KboxlikeTraceeMapSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 7,
            maps: super::super::memory::parse_proc_maps(
                "1000-2000 rw-s 00000000 00:01 99 /memfd:chromium-shared (deleted)\n\
                 2000-3000 r--s 00000000 00:05 10 /dev/shm/chrome\n\
                 3000-4000 rw-s 00000000 00:00 0\n",
            ),
            dump_candidate_bytes: 0x3000,
        }];

        let plan = classify_memory_restore_actions(&memory_maps);

        assert_eq!(
            plan.actions,
            vec![
                KboxlikeMemoryRestoreAction::RecreateSharedObject {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    len: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    kind: KboxlikeSharedMappingKind::Memfd,
                    path: Some("/memfd:chromium-shared (deleted)".to_owned()),
                    file_offset: 0,
                    dev: "00:01".to_owned(),
                    inode: 99,
                },
                KboxlikeMemoryRestoreAction::RecreateSharedObject {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x2000,
                    len: 0x1000,
                    prot: libc::PROT_READ,
                    kind: KboxlikeSharedMappingKind::Device,
                    path: Some("/dev/shm/chrome".to_owned()),
                    file_offset: 0,
                    dev: "00:05".to_owned(),
                    inode: 10,
                },
                KboxlikeMemoryRestoreAction::RecreateSharedObject {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x3000,
                    len: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    kind: KboxlikeSharedMappingKind::Anonymous,
                    path: None,
                    file_offset: 0,
                    dev: "00:00".to_owned(),
                    inode: 0,
                },
            ]
        );
    }

    #[test]
    fn restore_classifier_dumps_deleted_private_file_mappings() {
        let memory_maps = vec![KboxlikeTraceeMapSnapshot {
            host_pid: 2001,
            model_pid: 7,
            model_thread_id: 7,
            maps: super::super::memory::parse_proc_maps(
                "1000-2000 r--p 00002000 00:01 99 /tmp/chrome-cache (deleted)\n",
            ),
            dump_candidate_bytes: 0x1000,
        }];

        let plan = classify_memory_restore_actions(&memory_maps);

        assert_eq!(
            plan.actions,
            vec![KboxlikeMemoryRestoreAction::DumpPrivateFileCow {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
                len: 0x1000,
                prot: libc::PROT_READ,
                path: PathBuf::from("/tmp/chrome-cache"),
                file_offset: 0x2000,
                dev: "00:01".to_owned(),
                inode: 99,
                reason: "deleted file mapping cannot be reopened reliably",
            }]
        );
    }

    #[test]
    fn materializer_binds_byte_actions_to_process_specific_dumps() {
        let plan = KboxlikeMemoryRestorePlan {
            actions: vec![
                KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    len: 4,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    label: Some("[heap]".to_owned()),
                },
                KboxlikeMemoryRestoreAction::RemapCleanFile {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x2000,
                    len: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_EXEC,
                    path: PathBuf::from("/usr/bin/chrome"),
                    file_offset: 0,
                    dev: "08:02".to_owned(),
                    inode: 11,
                },
                KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
                    host_pid: 2002,
                    model_pid: 8,
                    start: 0x1000,
                    len: 2,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    label: Some("[heap]".to_owned()),
                },
            ],
        };
        let dumps = KboxlikeMemoryDumpCapture {
            dumps: vec![
                KboxlikeMemoryDump {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    bytes: b"abcd".to_vec(),
                },
                KboxlikeMemoryDump {
                    host_pid: 2002,
                    model_pid: 8,
                    start: 0x1000,
                    bytes: b"ef".to_vec(),
                },
            ],
            failures: Vec::new(),
        };

        let materialized = materialize_memory_restore_actions(&plan, &dumps).unwrap();

        assert_eq!(
            materialized.actions,
            vec![
                KboxlikeMaterializedMemoryRestoreAction::ByteBacked {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    bytes: b"abcd".to_vec(),
                    source: KboxlikeByteBackedRestoreSource::AnonymousPrivate {
                        label: Some("[heap]".to_owned()),
                    },
                },
                KboxlikeMaterializedMemoryRestoreAction::RemapCleanFile {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x2000,
                    len: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_EXEC,
                    path: PathBuf::from("/usr/bin/chrome"),
                    file_offset: 0,
                    dev: "08:02".to_owned(),
                    inode: 11,
                },
                KboxlikeMaterializedMemoryRestoreAction::ByteBacked {
                    host_pid: 2002,
                    model_pid: 8,
                    start: 0x1000,
                    prot: libc::PROT_READ | libc::PROT_WRITE,
                    bytes: b"ef".to_vec(),
                    source: KboxlikeByteBackedRestoreSource::AnonymousPrivate {
                        label: Some("[heap]".to_owned()),
                    },
                },
            ]
        );
    }

    #[test]
    fn materializer_rejects_missing_or_short_dumps() {
        let plan = KboxlikeMemoryRestorePlan {
            actions: vec![KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
                len: 4,
                prot: libc::PROT_READ,
                label: None,
            }],
        };

        assert_eq!(
            materialize_memory_restore_actions(&plan, &KboxlikeMemoryDumpCapture::default())
                .unwrap_err(),
            KboxlikeRestoreMaterializeError::MissingDump {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
                len: 4,
            }
        );

        let dumps = KboxlikeMemoryDumpCapture {
            dumps: vec![KboxlikeMemoryDump {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
                bytes: b"abc".to_vec(),
            }],
            failures: Vec::new(),
        };

        assert_eq!(
            materialize_memory_restore_actions(&plan, &dumps).unwrap_err(),
            KboxlikeRestoreMaterializeError::DumpLengthMismatch {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
                expected: 4,
                actual: 3,
            }
        );
    }

    #[test]
    fn materializer_rejects_duplicate_reused_or_unused_dumps() {
        let plan = KboxlikeMemoryRestorePlan {
            actions: vec![
                KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    len: 4,
                    prot: libc::PROT_READ,
                    label: None,
                },
                KboxlikeMemoryRestoreAction::DumpAnonymousPrivate {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    len: 4,
                    prot: libc::PROT_READ,
                    label: None,
                },
            ],
        };
        let dumps = KboxlikeMemoryDumpCapture {
            dumps: vec![KboxlikeMemoryDump {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
                bytes: b"abcd".to_vec(),
            }],
            failures: Vec::new(),
        };

        assert_eq!(
            materialize_memory_restore_actions(&plan, &dumps).unwrap_err(),
            KboxlikeRestoreMaterializeError::ReusedDump {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
            }
        );

        let duplicate_dumps = KboxlikeMemoryDumpCapture {
            dumps: vec![
                KboxlikeMemoryDump {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    bytes: b"abcd".to_vec(),
                },
                KboxlikeMemoryDump {
                    host_pid: 2001,
                    model_pid: 7,
                    start: 0x1000,
                    bytes: b"abcd".to_vec(),
                },
            ],
            failures: Vec::new(),
        };

        assert_eq!(
            materialize_memory_restore_actions(
                &KboxlikeMemoryRestorePlan {
                    actions: Vec::new()
                },
                &duplicate_dumps
            )
            .unwrap_err(),
            KboxlikeRestoreMaterializeError::DuplicateDump {
                host_pid: 2001,
                model_pid: 7,
                start: 0x1000,
            }
        );

        let unused_dumps = KboxlikeMemoryDumpCapture {
            dumps: vec![KboxlikeMemoryDump {
                host_pid: 2001,
                model_pid: 7,
                start: 0x3000,
                bytes: b"x".to_vec(),
            }],
            failures: Vec::new(),
        };

        assert_eq!(
            materialize_memory_restore_actions(
                &KboxlikeMemoryRestorePlan {
                    actions: Vec::new()
                },
                &unused_dumps
            )
            .unwrap_err(),
            KboxlikeRestoreMaterializeError::UnusedDump {
                host_pid: 2001,
                model_pid: 7,
                start: 0x3000,
                len: 1,
            }
        );
    }

    #[test]
    fn process_vm_memory_reader_rejects_invalid_tracee_pid() {
        assert_eq!(
            process_vm_read_exact(0, 0x1000, 4).unwrap_err(),
            KboxlikeMemoryReadError {
                bytes_read: 0,
                reason: "invalid tracee pid",
            }
        );
    }
}
