<commit_before>// The following definitions are correct for aarch64 and x86_64,
// but may be wrong for mips64

pub type c_long = i64;
pub type c_ulong = u64;
pub type mode_t = u32;
pub type off64_t = i64;
pub type socklen_t = u32;

s! {
    pub struct sigset_t {
        __val: [::c_ulong; 1],
    }

    pub struct sigaction {
        pub sa_flags: ::c_int,
        pub sa_sigaction: ::sighandler_t,
        pub sa_mask: ::sigset_t,
        pub sa_restorer: ::Option<extern fn()>,
    }

    pub struct rlimit64 {
        pub rlim_cur: ::c_ulonglong,
        pub rlim_max: ::c_ulonglong,
    }

    pub struct pthread_attr_t {
        pub flags: u32,
        pub stack_base: *mut ::c_void,
        pub stack_size: ::size_t,
        pub guard_size: ::size_t,
        pub sched_policy: i32,
        pub sched_priority: i32,
        __reserved: [::c_char; 16],
    }

    pub struct passwd {
        pub pw_name: *mut ::c_char,
        pub pw_passwd: *mut ::c_char,
        pub pw_uid: ::uid_t,
        pub pw_gid: ::gid_t,
        pub pw_gecos: *mut ::c_char,
        pub pw_dir: *mut ::c_char,
        pub pw_shell: *mut ::c_char,
    }

    pub struct statfs {
        pub f_type: u64,
        pub f_bsize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_fsid: ::__fsid_t,
        pub f_namelen: u64,
        pub f_frsize: u64,
        pub f_flags: u64,
        pub f_spare: [u64; 4],
    }

    pub struct sysinfo {
        pub uptime: ::c_long,
        pub loads: [::c_ulong; 3],
        pub totalram: ::c_ulong,
        pub freeram: ::c_ulong,
        pub sharedram: ::c_ulong,
        pub bufferram: ::c_ulong,
        pub totalswap: ::c_ulong,
        pub freeswap: ::c_ulong,
        pub procs: ::c_ushort,
        pub pad: ::c_ushort,
        pub totalhigh: ::c_ulong,
        pub freehigh: ::c_ulong,
        pub mem_unit: ::c_uint,
        pub _f: [::c_char; 0],
    }

    pub struct statfs64 {
        pub f_type: u64,
        pub f_bsize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        f_fsid: [u32; 2],
        pub f_namelen: u64,
        pub f_frsize: u64,
        pub f_flags: u64,
        pub f_spare: [u64; 4],
    }

    pub struct statvfs64 {
        pub f_bsize: ::c_ulong,
        pub f_frsize: ::c_ulong,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_favail: u64,
        pub f_fsid: ::c_ulong,
        pub f_flag: ::c_ulong,
        pub f_namemax: ::c_ulong,
        __f_spare: [::c_int; 6],
    }
}

s_no_extra_traits! {
    pub struct pthread_mutex_t {
        value: ::c_int,
        __reserved: [::c_char; 36],
    }

    pub struct pthread_cond_t {
        value: ::c_int,
        __reserved: [::c_char; 44],
    }

    pub struct pthread_rwlock_t {
        numLocks: ::c_int,
        writerThreadId: ::c_int,
        pendingReaders: ::c_int,
        pendingWriters: ::c_int,
        attr: i32,
        __reserved: [::c_char; 36],
    }

    pub struct sigset64_t {
        __bits: [::c_ulong; 1]
    }
}

cfg_if! {
    if #[cfg(feature = "extra_traits")] {
        impl PartialEq for pthread_mutex_t {
            fn eq(&self, other: &pthread_mutex_t) -> bool {
                self.value == other.value
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_mutex_t {}

        impl ::fmt::Debug for pthread_mutex_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_mutex_t")
                    .field("value", &self.value)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_mutex_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.value.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl PartialEq for pthread_cond_t {
            fn eq(&self, other: &pthread_cond_t) -> bool {
                self.value == other.value
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_cond_t {}

        impl ::fmt::Debug for pthread_cond_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_cond_t")
                    .field("value", &self.value)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_cond_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.value.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl PartialEq for pthread_rwlock_t {
            fn eq(&self, other: &pthread_rwlock_t) -> bool {
                self.numLocks == other.numLocks
                    && self.writerThreadId == other.writerThreadId
                    && self.pendingReaders == other.pendingReaders
                    && self.pendingWriters == other.pendingWriters
                    && self.attr == other.attr
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_rwlock_t {}

        impl ::fmt::Debug for pthread_rwlock_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_rwlock_t")
                    .field("numLocks", &self.numLocks)
                    .field("writerThreadId", &self.writerThreadId)
                    .field("pendingReaders", &self.pendingReaders)
                    .field("pendingWriters", &self.pendingWriters)
                    .field("attr", &self.attr)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_rwlock_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.numLocks.hash(state);
                self.writerThreadId.hash(state);
                self.pendingReaders.hash(state);
                self.pendingWriters.hash(state);
                self.attr.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl ::fmt::Debug for sigset64_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("sigset64_t")
                    .field("__bits", &self.__bits)
                    .finish()
            }
        }
    }
}

// These constants must be of the same type of sigaction.sa_flags
pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
pub const SA_NODEFER: ::c_int = 0x40000000;
pub const SA_ONSTACK: ::c_int = 0x08000000;
pub const SA_RESETHAND: ::c_int = 0x80000000;
pub const SA_RESTART: ::c_int = 0x10000000;
pub const SA_SIGINFO: ::c_int = 0x00000004;

pub const RTLD_GLOBAL: ::c_int = 0x00100;
pub const RTLD_NOW: ::c_int = 2;
pub const RTLD_DEFAULT: *mut ::c_void = 0i64 as *mut ::c_void;

pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
    value: 0,
    __reserved: [0; 36],
};
pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
    value: 0,
    __reserved: [0; 44],
};
pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
    numLocks: 0,
    writerThreadId: 0,
    pendingReaders: 0,
    pendingWriters: 0,
    attr: 0,
    __reserved: [0; 36],
};
pub const PTHREAD_STACK_MIN: ::size_t = 4096 * 4;
pub const CPU_SETSIZE: ::size_t = 1024;
pub const __CPU_BITS: ::size_t = 64;

pub const UT_LINESIZE: usize = 32;
pub const UT_NAMESIZE: usize = 32;
pub const UT_HOSTSIZE: usize = 256;

cfg_if! {
    if #[cfg(target_arch = "x86_64")] {
        mod x86_64;
        pub use self::x86_64::*;
    } else if #[cfg(target_arch = "aarch64")] {
        mod aarch64;
        pub use self::aarch64::*;
    } else {
        // Unknown target_arch
    }
}
<commit_msg>Add getauxval for 64-bit Android<commit_after>// The following definitions are correct for aarch64 and x86_64,
// but may be wrong for mips64

pub type c_long = i64;
pub type c_ulong = u64;
pub type mode_t = u32;
pub type off64_t = i64;
pub type socklen_t = u32;

s! {
    pub struct sigset_t {
        __val: [::c_ulong; 1],
    }

    pub struct sigaction {
        pub sa_flags: ::c_int,
        pub sa_sigaction: ::sighandler_t,
        pub sa_mask: ::sigset_t,
        pub sa_restorer: ::Option<extern fn()>,
    }

    pub struct rlimit64 {
        pub rlim_cur: ::c_ulonglong,
        pub rlim_max: ::c_ulonglong,
    }

    pub struct pthread_attr_t {
        pub flags: u32,
        pub stack_base: *mut ::c_void,
        pub stack_size: ::size_t,
        pub guard_size: ::size_t,
        pub sched_policy: i32,
        pub sched_priority: i32,
        __reserved: [::c_char; 16],
    }

    pub struct passwd {
        pub pw_name: *mut ::c_char,
        pub pw_passwd: *mut ::c_char,
        pub pw_uid: ::uid_t,
        pub pw_gid: ::gid_t,
        pub pw_gecos: *mut ::c_char,
        pub pw_dir: *mut ::c_char,
        pub pw_shell: *mut ::c_char,
    }

    pub struct statfs {
        pub f_type: u64,
        pub f_bsize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_fsid: ::__fsid_t,
        pub f_namelen: u64,
        pub f_frsize: u64,
        pub f_flags: u64,
        pub f_spare: [u64; 4],
    }

    pub struct sysinfo {
        pub uptime: ::c_long,
        pub loads: [::c_ulong; 3],
        pub totalram: ::c_ulong,
        pub freeram: ::c_ulong,
        pub sharedram: ::c_ulong,
        pub bufferram: ::c_ulong,
        pub totalswap: ::c_ulong,
        pub freeswap: ::c_ulong,
        pub procs: ::c_ushort,
        pub pad: ::c_ushort,
        pub totalhigh: ::c_ulong,
        pub freehigh: ::c_ulong,
        pub mem_unit: ::c_uint,
        pub _f: [::c_char; 0],
    }

    pub struct statfs64 {
        pub f_type: u64,
        pub f_bsize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        f_fsid: [u32; 2],
        pub f_namelen: u64,
        pub f_frsize: u64,
        pub f_flags: u64,
        pub f_spare: [u64; 4],
    }

    pub struct statvfs64 {
        pub f_bsize: ::c_ulong,
        pub f_frsize: ::c_ulong,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_favail: u64,
        pub f_fsid: ::c_ulong,
        pub f_flag: ::c_ulong,
        pub f_namemax: ::c_ulong,
        __f_spare: [::c_int; 6],
    }
}

s_no_extra_traits! {
    pub struct pthread_mutex_t {
        value: ::c_int,
        __reserved: [::c_char; 36],
    }

    pub struct pthread_cond_t {
        value: ::c_int,
        __reserved: [::c_char; 44],
    }

    pub struct pthread_rwlock_t {
        numLocks: ::c_int,
        writerThreadId: ::c_int,
        pendingReaders: ::c_int,
        pendingWriters: ::c_int,
        attr: i32,
        __reserved: [::c_char; 36],
    }

    pub struct sigset64_t {
        __bits: [::c_ulong; 1]
    }
}

cfg_if! {
    if #[cfg(feature = "extra_traits")] {
        impl PartialEq for pthread_mutex_t {
            fn eq(&self, other: &pthread_mutex_t) -> bool {
                self.value == other.value
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_mutex_t {}

        impl ::fmt::Debug for pthread_mutex_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_mutex_t")
                    .field("value", &self.value)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_mutex_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.value.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl PartialEq for pthread_cond_t {
            fn eq(&self, other: &pthread_cond_t) -> bool {
                self.value == other.value
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_cond_t {}

        impl ::fmt::Debug for pthread_cond_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_cond_t")
                    .field("value", &self.value)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_cond_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.value.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl PartialEq for pthread_rwlock_t {
            fn eq(&self, other: &pthread_rwlock_t) -> bool {
                self.numLocks == other.numLocks
                    && self.writerThreadId == other.writerThreadId
                    && self.pendingReaders == other.pendingReaders
                    && self.pendingWriters == other.pendingWriters
                    && self.attr == other.attr
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_rwlock_t {}

        impl ::fmt::Debug for pthread_rwlock_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_rwlock_t")
                    .field("numLocks", &self.numLocks)
                    .field("writerThreadId", &self.writerThreadId)
                    .field("pendingReaders", &self.pendingReaders)
                    .field("pendingWriters", &self.pendingWriters)
                    .field("attr", &self.attr)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_rwlock_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.numLocks.hash(state);
                self.writerThreadId.hash(state);
                self.pendingReaders.hash(state);
                self.pendingWriters.hash(state);
                self.attr.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl ::fmt::Debug for sigset64_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("sigset64_t")
                    .field("__bits", &self.__bits)
                    .finish()
            }
        }
    }
}

// These constants must be of the same type of sigaction.sa_flags
pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
pub const SA_NODEFER: ::c_int = 0x40000000;
pub const SA_ONSTACK: ::c_int = 0x08000000;
pub const SA_RESETHAND: ::c_int = 0x80000000;
pub const SA_RESTART: ::c_int = 0x10000000;
pub const SA_SIGINFO: ::c_int = 0x00000004;

pub const RTLD_GLOBAL: ::c_int = 0x00100;
pub const RTLD_NOW: ::c_int = 2;
pub const RTLD_DEFAULT: *mut ::c_void = 0i64 as *mut ::c_void;

// From NDK's linux/auxvec.h
pub const AT_NULL: ::c_ulong = 0;
pub const AT_IGNORE: ::c_ulong = 1;
pub const AT_EXECFD: ::c_ulong = 2;
pub const AT_PHDR: ::c_ulong = 3;
pub const AT_PHENT: ::c_ulong = 4;
pub const AT_PHNUM: ::c_ulong = 5;
pub const AT_PAGESZ: ::c_ulong = 6;
pub const AT_BASE: ::c_ulong = 7;
pub const AT_FLAGS: ::c_ulong = 8;
pub const AT_ENTRY: ::c_ulong = 9;
pub const AT_NOTELF: ::c_ulong = 10;
pub const AT_UID: ::c_ulong = 11;
pub const AT_EUID: ::c_ulong = 12;
pub const AT_GID: ::c_ulong = 13;
pub const AT_EGID: ::c_ulong = 14;
pub const AT_PLATFORM: ::c_ulong = 15;
pub const AT_HWCAP: ::c_ulong = 16;
pub const AT_CLKTCK: ::c_ulong = 17;
pub const AT_SECURE: ::c_ulong = 23;
pub const AT_BASE_PLATFORM: ::c_ulong = 24;
pub const AT_RANDOM: ::c_ulong = 25;
pub const AT_HWCAP2: ::c_ulong = 26;
pub const AT_EXECFN: ::c_ulong = 31;

pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
    value: 0,
    __reserved: [0; 36],
};
pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
    value: 0,
    __reserved: [0; 44],
};
pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
    numLocks: 0,
    writerThreadId: 0,
    pendingReaders: 0,
    pendingWriters: 0,
    attr: 0,
    __reserved: [0; 36],
};
pub const PTHREAD_STACK_MIN: ::size_t = 4096 * 4;
pub const CPU_SETSIZE: ::size_t = 1024;
pub const __CPU_BITS: ::size_t = 64;

pub const UT_LINESIZE: usize = 32;
pub const UT_NAMESIZE: usize = 32;
pub const UT_HOSTSIZE: usize = 256;

extern "C" {
    pub fn getauxval(type_: ::c_ulong) -> ::c_ulong;
}

cfg_if! {
    if #[cfg(target_arch = "x86_64")] {
        mod x86_64;
        pub use self::x86_64::*;
    } else if #[cfg(target_arch = "aarch64")] {
        mod aarch64;
        pub use self::aarch64::*;
    } else {
        // Unknown target_arch
    }
}
<|endoftext|>


<commit_before>/* diosix hypervisor code for handling hardware interrupts and software exceptions
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::scheduler;
use super::capsule;
use super::pcore;
use super::hardware;

/* platform-specific code must implement all this */
use platform;
use platform::irq::{IRQContext, IRQType, IRQCause, IRQSeverity, IRQ};
use platform::cpu::PrivilegeMode;
use platform::instructions::{self, EmulationResult};
use platform::syscalls;
use platform::timer;

/* hypervisor_irq_handler
   entry point for hardware interrupts and software exceptions, collectively known as IRQs.
   call down into platform-specific handlers
   => context = platform-specific context of the IRQ, which may be modified depending
   on the IRQ raised.
*/
#[no_mangle]
pub extern "C" fn hypervisor_irq_handler(mut context: IRQContext)
{
    /* if dispatch() returns an IRQ context then we need to handle it here
    at the high level. if it returns None, the platform-specific code handled it.
    note: the platform library should take care of hardware specfic things like
    catching illegal instructions that can be fixed up and handled transparently */
    if let Some(irq) = platform::irq::dispatch(context)
    {
        match irq.irq_type
        {
            IRQType::Exception => exception(irq, &mut context),
            IRQType::Interrupt => interrupt(irq, &mut context),
        };
    }
}

/* handle software exception */
fn exception(irq: IRQ, context: &mut IRQContext)
{
    match (irq.severity, irq.privilege_mode, irq.cause)
    {
        /* catch illegal instructions we may be able to emulate */
        (_, PrivilegeMode::Supervisor, IRQCause::IllegalInstruction) =>
        {
            match instructions::emulate(irq.privilege_mode, context)
            {
                EmulationResult::Success => (), /* nothing more to do, return */
                EmulationResult::Yield =>
                {
                    /* instruction was some kind of sleep or pause operation.
                    try to find something else to run in the meantime */
                    scheduler::ping();
                },

                /* if we can't handle the instruction,
                kill the capsule and force a context switch.
                TODO: is killing the whole capsule a little extreme? */
                _ => fatal_exception(&irq)
            }
        },

        /* catch environment calls from supervisor mode */
        (_, PrivilegeMode::Supervisor, IRQCause::SupervisorEnvironmentCall) =>
        {
            /* determine what we need to do from the platform code's decoding */
            if let Some(action) = syscalls::handler(context)
            {
                match action
                {
                    syscalls::Action::Terminate => if let Err(_e) = capsule::destroy_current()
                    {
                        hvalert!("BUG: Failed to terminate currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is dead */
                        scheduler::ping();
                    },

                    syscalls::Action::Restart => if let Err(_e) = capsule::restart_current()
                    {
                        hvalert!("BUG: Failed to restart currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is being replaced */
                        scheduler::ping();
                    },

                    syscalls::Action::TimerIRQAt(target) =>
                    {
                        /* mark this virtual core as awaiting a timer IRQ and
                        schedule a timer interrupt in anticipation */
                        pcore::PhysicalCore::set_virtualcore_timer_target(Some(target));
                        hardware::scheduler_timer_at(target);
                    },

                    /* stash debug output character into capsule's buffer */
                    syscalls::Action::OutputChar(character) => if let Some(capsule_id) = pcore::PhysicalCore::get_capsule_id()
                    {
                        if let Err(_e) = capsule::debug_write(capsule_id, character)
                        {
                            hvdebug!("Couldn't buffer debug byte {} from capsule {}: {:?}", character, capsule_id, _e);
                            syscalls::failed(context, syscalls::ActionResult::Failed);
                        }
                    },

                    _ => if let Some(c) = pcore::PhysicalCore::get_capsule_id()
                    {
                        hvalert!("Capsule {}: Unhandled syscall: {:x?} at 0x{:x}", c, action, irq.pc);
                    }
                    else
                    {
                        hvdebug!("Unhandled syscall: {:x?} at 0x{:x} in unknown capsule", action, irq.pc);
                    }
                }
            }
        },

        /* catch everything else, halting if fatal */
        (severity, privilege, cause) =>
        {
            /* if an unhandled fatal exception reaches us here from the supervisor or user mode,
            kill the capsule. if the hypervisor can't handle its own fatal exception, give up */
            match privilege
            {
                PrivilegeMode::Supervisor | PrivilegeMode::User => if severity == IRQSeverity::Fatal
                {
                    /* TODO: is it wise to blow away the whole capsule for a user exception?
                    the supervisor should really catch its user-level faults */
                    fatal_exception(&irq);
                },
                PrivilegeMode::Machine =>
                {
                    if severity == IRQSeverity::Fatal
                    {
                        hvalert!("Halting physical CPU core for {:?} at 0x{:x}, stack 0x{:x} integrity {:?}",
                            cause, irq.pc, irq.sp, pcore::PhysicalCore::integrity_check());
                        debughousekeeper!(); // flush the debug output
                        loop {}
                    }
                }
            }
        }
    }
}

/* handle hardware interrupt */
fn interrupt(irq: IRQ, _: &mut IRQContext)
{
    match irq.cause
    {
        IRQCause::MachineTimer =>
        {
            /* make a scheduling decision and raise any supervior-level timer IRQs*/
            scheduler::ping();
            check_supervisor_timer_irq();
        },
        _ => hvdebug!("Unhandled hardware interrupt: {:?}", irq.cause)
    }

    /* clear the interrupt condition */
    platform::irq::acknowledge(irq);
}

/* is the virtual core we're about to run awaiting a timer IRQ?
if so, and if its timer target value has been passed, generate a pending timer IRQ */
fn check_supervisor_timer_irq()
{
    if let Some(target) = pcore::PhysicalCore::get_virtualcore_timer_target()
    {
        match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
        {
            (Some(time), Some(freq)) =>
            {
                let current = time.to_exact(freq);
                if current >= target.to_exact(freq)
                {
                    /* create a pending timer IRQ for the supervisor kernel and clear the target */
                    timer::trigger_supervisor_irq();
                    pcore::PhysicalCore::set_virtualcore_timer_target(None);
                }
            },
            (_, _) => ()
        }
    }
}

/* kill the running capsule, alert the user, and then find something else to run.
   if the capsule is important enough to auto-restart-on-crash, try to revive it */
fn fatal_exception(irq: &IRQ)
{
    hvalert!("Terminating running capsule {} for {:?} at 0x{:x}, stack 0x{:x}",
        match pcore::PhysicalCore::this().get_virtualcore_id()
        {
            Some(id) => format!("{}.{}", id.capsuleid, id.vcoreid),
            None => format!("[unknown!]")
        }, irq.cause, irq.pc, irq.sp);

    let mut terminate = false; // when true, destroy the current capsule
    let mut reschedule = false; // when true, we must find another vcore to run

    match capsule::is_current_autorestart()
    {
        Some(true) =>
        {
            hvalert!("Restarting capsule due to auto-restart-on-crash flag");
            if let Err(err) = capsule::restart_current()
            {
                hvalert!("Can't restart capsule ({:?}), letting it die instead", err);
                terminate = true;
            }
            else
            {
                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        },
        Some(false) => terminate = true,
        None =>
        {
            hvalert!("BUG: fatal_exception() can't find the running capsule to kill");
            return;
        },
    }

    if terminate == true
    {
        match capsule::destroy_current()
        {
            Err(e) => hvalert!("BUG: Failed to kill running capsule ({:?})", e),
            _ =>
            {
                hvdebug!("Terminated running capsule");

                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        }
    }

    if reschedule == true
    {
        /* force a context switch to find another virtual core to run
        because this virtual core no long exists */
        scheduler::ping();
    }
}<commit_msg>[Minor] Fix comment typo<commit_after>/* diosix hypervisor code for handling hardware interrupts and software exceptions
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::scheduler;
use super::capsule;
use super::pcore;
use super::hardware;

/* platform-specific code must implement all this */
use platform;
use platform::irq::{IRQContext, IRQType, IRQCause, IRQSeverity, IRQ};
use platform::cpu::PrivilegeMode;
use platform::instructions::{self, EmulationResult};
use platform::syscalls;
use platform::timer;

/* hypervisor_irq_handler
   entry point for hardware interrupts and software exceptions, collectively known as IRQs.
   call down into platform-specific handlers
   => context = platform-specific context of the IRQ, which may be modified depending
   on the IRQ raised.
*/
#[no_mangle]
pub extern "C" fn hypervisor_irq_handler(mut context: IRQContext)
{
    /* if dispatch() returns an IRQ context then we need to handle it here
    at the high level. if it returns None, the platform-specific code handled it.
    note: the platform library should take care of hardware specfic things like
    catching illegal instructions that can be fixed up and handled transparently */
    if let Some(irq) = platform::irq::dispatch(context)
    {
        match irq.irq_type
        {
            IRQType::Exception => exception(irq, &mut context),
            IRQType::Interrupt => interrupt(irq, &mut context),
        };
    }
}

/* handle software exception */
fn exception(irq: IRQ, context: &mut IRQContext)
{
    match (irq.severity, irq.privilege_mode, irq.cause)
    {
        /* catch illegal instructions we may be able to emulate */
        (_, PrivilegeMode::Supervisor, IRQCause::IllegalInstruction) =>
        {
            match instructions::emulate(irq.privilege_mode, context)
            {
                EmulationResult::Success => (), /* nothing more to do, return */
                EmulationResult::Yield =>
                {
                    /* instruction was some kind of sleep or pause operation.
                    try to find something else to run in the meantime */
                    scheduler::ping();
                },

                /* if we can't handle the instruction,
                kill the capsule and force a context switch.
                TODO: is killing the whole capsule a little extreme? */
                _ => fatal_exception(&irq)
            }
        },

        /* catch environment calls from supervisor mode */
        (_, PrivilegeMode::Supervisor, IRQCause::SupervisorEnvironmentCall) =>
        {
            /* determine what we need to do from the platform code's decoding */
            if let Some(action) = syscalls::handler(context)
            {
                match action
                {
                    syscalls::Action::Terminate => if let Err(_e) = capsule::destroy_current()
                    {
                        hvalert!("BUG: Failed to terminate currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is dead */
                        scheduler::ping();
                    },

                    syscalls::Action::Restart => if let Err(_e) = capsule::restart_current()
                    {
                        hvalert!("BUG: Failed to restart currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is being replaced */
                        scheduler::ping();
                    },

                    syscalls::Action::TimerIRQAt(target) =>
                    {
                        /* mark this virtual core as awaiting a timer IRQ and
                        schedule a timer interrupt in anticipation */
                        pcore::PhysicalCore::set_virtualcore_timer_target(Some(target));
                        hardware::scheduler_timer_at(target);
                    },

                    /* stash debug output character into capsule's buffer */
                    syscalls::Action::OutputChar(character) => if let Some(capsule_id) = pcore::PhysicalCore::get_capsule_id()
                    {
                        if let Err(_e) = capsule::debug_write(capsule_id, character)
                        {
                            hvdebug!("Couldn't buffer debug byte {} from capsule {}: {:?}", character, capsule_id, _e);
                            syscalls::failed(context, syscalls::ActionResult::Failed);
                        }
                    },

                    _ => if let Some(c) = pcore::PhysicalCore::get_capsule_id()
                    {
                        hvalert!("Capsule {}: Unhandled syscall: {:x?} at 0x{:x}", c, action, irq.pc);
                    }
                    else
                    {
                        hvdebug!("Unhandled syscall: {:x?} at 0x{:x} in unknown capsule", action, irq.pc);
                    }
                }
            }
        },

        /* catch everything else, halting if fatal */
        (severity, privilege, cause) =>
        {
            /* if an unhandled fatal exception reaches us here from the supervisor or user mode,
            kill the capsule. if the hypervisor can't handle its own fatal exception, give up */
            match privilege
            {
                PrivilegeMode::Supervisor | PrivilegeMode::User => if severity == IRQSeverity::Fatal
                {
                    /* TODO: is it wise to blow away the whole capsule for a user exception?
                    the supervisor should really catch its user-level faults */
                    fatal_exception(&irq);
                },
                PrivilegeMode::Machine =>
                {
                    if severity == IRQSeverity::Fatal
                    {
                        hvalert!("Halting physical CPU core for {:?} at 0x{:x}, stack 0x{:x} integrity {:?}",
                            cause, irq.pc, irq.sp, pcore::PhysicalCore::integrity_check());
                        debughousekeeper!(); // flush the debug output
                        loop {}
                    }
                }
            }
        }
    }
}

/* handle hardware interrupt */
fn interrupt(irq: IRQ, _: &mut IRQContext)
{
    match irq.cause
    {
        IRQCause::MachineTimer =>
        {
            /* make a scheduling decision and raise any supervior-level timer IRQs*/
            scheduler::ping();
            check_supervisor_timer_irq();
        },
        _ => hvdebug!("Unhandled hardware interrupt: {:?}", irq.cause)
    }

    /* clear the interrupt condition */
    platform::irq::acknowledge(irq);
}

/* is the virtual core we're about to run awaiting a timer IRQ?
if so, and if its timer target value has been passed, generate a pending timer IRQ */
fn check_supervisor_timer_irq()
{
    if let Some(target) = pcore::PhysicalCore::get_virtualcore_timer_target()
    {
        match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
        {
            (Some(time), Some(freq)) =>
            {
                let current = time.to_exact(freq);
                if current >= target.to_exact(freq)
                {
                    /* create a pending timer IRQ for the supervisor kernel and clear the target */
                    timer::trigger_supervisor_irq();
                    pcore::PhysicalCore::set_virtualcore_timer_target(None);
                }
            },
            (_, _) => ()
        }
    }
}

/* kill the running capsule, alert the user, and then find something else to run.
   if the capsule is important enough to auto-restart-on-crash, try to revive it */
fn fatal_exception(irq: &IRQ)
{
    hvalert!("Terminating running capsule {} for {:?} at 0x{:x}, stack 0x{:x}",
        match pcore::PhysicalCore::this().get_virtualcore_id()
        {
            Some(id) => format!("{}.{}", id.capsuleid, id.vcoreid),
            None => format!("[unknown!]")
        }, irq.cause, irq.pc, irq.sp);

    let mut terminate = false; // when true, destroy the current capsule
    let mut reschedule = false; // when true, we must find another vcore to run

    match capsule::is_current_autorestart()
    {
        Some(true) =>
        {
            hvalert!("Restarting capsule due to auto-restart-on-crash flag");
            if let Err(err) = capsule::restart_current()
            {
                hvalert!("Can't restart capsule ({:?}), letting it die instead", err);
                terminate = true;
            }
            else
            {
                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        },
        Some(false) => terminate = true,
        None =>
        {
            hvalert!("BUG: fatal_exception() can't find the running capsule to kill");
            return;
        },
    }

    if terminate == true
    {
        match capsule::destroy_current()
        {
            Err(e) => hvalert!("BUG: Failed to kill running capsule ({:?})", e),
            _ =>
            {
                hvdebug!("Terminated running capsule");

                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        }
    }

    if reschedule == true
    {
        /* force a context switch to find another virtual core to run
        because this virtual core no longer exists */
        scheduler::ping();
    }
}<|endoftext|>


<commit_before>use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::ir::{Opcode, InstructionData, ValueDef};
use cranelift_codegen::ir::immediates::Offset32;
use cranelift_codegen::entity::SecondaryMap;

use crate::prelude::*;

pub(super) fn optimize_function(
    func: &mut Function,
    clif_comments: &mut crate::pretty_clif::CommentWriter,
    name: String, // FIXME remove
) {
    let mut stack_addr_insts = SecondaryMap::new();
    let mut stack_load_store_insts = SecondaryMap::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                // Record all stack_addr, stack_load and stack_store instructions.
                InstructionData::StackLoad {
                    opcode: Opcode::StackAddr,
                    stack_slot: _,
                    offset: _,
                } => {
                    stack_addr_insts[inst] = true;
                }
                InstructionData::StackLoad {
                    opcode: Opcode::StackLoad,
                    stack_slot: _,
                    offset: _,
                } => {
                    stack_load_store_insts[inst] = true;
                }
                InstructionData::StackStore {
                    opcode: Opcode::StackStore,
                    arg: _,
                    stack_slot: _,
                    offset: _,
                } => {
                    stack_load_store_insts[inst] = true;
                }

                // Turn load and store into stack_load and stack_store when possible.
                InstructionData::Load { opcode: Opcode::Load, arg: addr, flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUD: stack_load.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            let ty = cursor.func.dfg.ctrl_typevar(inst);
                            cursor.func.dfg.replace(inst).stack_load(ty, stack_slot, combined_offset);
                            stack_load_store_insts[inst] = true;
                        }
                    }
                }
                InstructionData::Store { opcode: Opcode::Store, args: [value, addr], flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUND: stack_store.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            cursor.func.dfg.replace(inst).stack_store(value, stack_slot, combined_offset);
                            stack_load_store_insts[inst] = true;
                        }
                    }
                }
                _ => {}
            }
        }
    }

    let mut used_stack_addr_insts = SecondaryMap::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            for &arg in cursor.func.dfg.inst_args(inst) {
                if let ValueDef::Result(arg_origin, 0) = cursor.func.dfg.value_def(arg) {
                    if cursor.func.dfg[arg_origin].opcode() == Opcode::StackAddr {
                        used_stack_addr_insts[arg_origin] = true;
                    }
                }
            }
        }
    }

    /*println!(
        "stack_addr: [{}] ([{}] used)\nstack_load/stack_store: [{}]",
        bool_secondary_map_to_string(&stack_addr_insts),
        bool_secondary_map_to_string(&used_stack_addr_insts),
        bool_secondary_map_to_string(&stack_load_store_insts),
    );*/

    for inst in used_stack_addr_insts.keys().filter(|&inst| used_stack_addr_insts[inst]) {
        assert!(stack_addr_insts[inst]);
    }

    // Replace all unused stack_addr instructions with nop.
    for inst in stack_addr_insts.keys() {
        if stack_addr_insts[inst] && !used_stack_addr_insts[inst] {
            func.dfg.detach_results(inst);
            func.dfg.replace(inst).nop();
            stack_addr_insts[inst] = false;
        }
    }

    //println!("stack_addr (after): [{}]", bool_secondary_map_to_string(&stack_addr_insts));

    let mut stack_slot_usage_map: SecondaryMap<StackSlot, HashSet<Inst>> = SecondaryMap::new();
    for inst in stack_load_store_insts.keys().filter(|&inst| stack_load_store_insts[inst]) {
        match func.dfg[inst] {
            InstructionData::StackLoad {
                opcode: Opcode::StackLoad,
                stack_slot,
                offset: _,
            } => {
                stack_slot_usage_map[stack_slot].insert(inst);
            }
            InstructionData::StackStore {
                opcode: Opcode::StackStore,
                arg: _,
                stack_slot,
                offset: _,
            } => {
                stack_slot_usage_map[stack_slot].insert(inst);
            }
            ref data => unreachable!("{:?}", data),
        }
    }
    for inst in stack_addr_insts.keys().filter(|&inst| stack_addr_insts[inst]) {
        match func.dfg[inst] {
            InstructionData::StackLoad {
                opcode: Opcode::StackAddr,
                stack_slot,
                offset: _,
            } => {
                stack_slot_usage_map[stack_slot].insert(inst);
            }
            ref data => unreachable!("{:?}", data),
        }
    }

    //println!("{:?}\n", stack_slot_usage_map);

    for (stack_slot, users) in stack_slot_usage_map.iter_mut() {
        let mut is_addr_leaked = false;
        let mut is_loaded = false;
        let mut is_stored = false;
        for &user in users.iter() {
            match func.dfg[user] {
                InstructionData::StackLoad {
                    opcode: Opcode::StackAddr,
                    stack_slot,
                    offset: _,
                } => {
                    is_addr_leaked = true;
                }
                InstructionData::StackLoad {
                    opcode: Opcode::StackLoad,
                    stack_slot,
                    offset: _,
                } => {
                    is_loaded = true;
                }
                InstructionData::StackStore {
                    opcode: Opcode::StackStore,
                    arg: _,
                    stack_slot,
                    offset: _,
                } => {
                    is_stored = true;
                }
                ref data => unreachable!("{:?}", data),
            }
        }

        if is_addr_leaked || (is_loaded && is_stored) {
            continue;
        }

        if is_loaded {
            println!("[{}] [BUG?] Reading uninitialized memory", name);
        } else {
            // Stored value never read; just remove reads.
            for &user in users.iter() {
                println!("[{}] Remove dead stack store {} of {}", name, user, stack_slot);
                func.dfg.replace(user).nop();
            }
        }
    }
}

fn try_get_stack_slot_and_offset_for_addr(func: &Function, addr: Value) -> Option<(StackSlot, Offset32)> {
    if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) {
        if let InstructionData::StackLoad {
            opcode: Opcode::StackAddr,
            stack_slot,
            offset,
        } = func.dfg[addr_inst] {
            return Some((stack_slot, offset));
        }
    }
    None
}

fn bool_secondary_map_to_string<E>(map: &SecondaryMap<E, bool>) -> String
    where E: cranelift_codegen::entity::EntityRef + std::fmt::Display,
{
    map
        .keys()
        .filter_map(|inst| {
            // EntitySet::keys returns all possible entities until the last entity inserted.
            if map[inst] {
                Some(format!("{}", inst))
            } else {
                None
            }
        })
        .collect::<Vec<String>>()
        .join(", ")
}
<commit_msg>Re-order some code<commit_after>use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::ir::{Opcode, InstructionData, ValueDef};
use cranelift_codegen::ir::immediates::Offset32;
use cranelift_codegen::entity::SecondaryMap;

use crate::prelude::*;

pub(super) fn optimize_function(
    func: &mut Function,
    clif_comments: &mut crate::pretty_clif::CommentWriter,
    name: String, // FIXME remove
) {
    // Turn load and store into stack_load and stack_store when possible.
    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::Load { opcode: Opcode::Load, arg: addr, flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUD: stack_load.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            let ty = cursor.func.dfg.ctrl_typevar(inst);
                            cursor.func.dfg.replace(inst).stack_load(ty, stack_slot, combined_offset);
                        }
                    }
                }
                InstructionData::Store { opcode: Opcode::Store, args: [value, addr], flags: _, offset } => {
                    if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() {
                        continue; // WORKAROUND: stack_store.i128 not yet implemented
                    }
                    if let Some((stack_slot, stack_addr_offset)) = try_get_stack_slot_and_offset_for_addr(cursor.func, addr) {
                        if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) {
                            cursor.func.dfg.replace(inst).stack_store(value, stack_slot, combined_offset);
                        }
                    }
                }
                _ => {}
            }
        }
    }

    // Record all stack_addr, stack_load and stack_store instructions. Also record all stack_addr
    // and stack_load insts whose result is used.
    let mut stack_addr_insts = SecondaryMap::new();
    let mut used_stack_addr_insts = SecondaryMap::new();
    let mut stack_load_insts = SecondaryMap::new();
    let mut used_stack_load_insts = SecondaryMap::new();
    let mut stack_store_insts = SecondaryMap::new();

    let mut cursor = FuncCursor::new(func);
    while let Some(_ebb) = cursor.next_ebb() {
        while let Some(inst) = cursor.next_inst() {
            match cursor.func.dfg[inst] {
                InstructionData::StackLoad {
                    opcode: Opcode::StackAddr,
                    stack_slot: _,
                    offset: _,
                } => {
                    stack_addr_insts[inst] = true;
                }
                InstructionData::StackLoad {
                    opcode: Opcode::StackLoad,
                    stack_slot: _,
                    offset: _,
                } => {
                    stack_load_insts[inst] = true;
                }
                InstructionData::StackStore {
                    opcode: Opcode::StackStore,
                    arg: _,
                    stack_slot: _,
                    offset: _,
                } => {
                    stack_store_insts[inst] = true;
                }
                _ => {}
            }

            for &arg in cursor.func.dfg.inst_args(inst) {
                if let ValueDef::Result(arg_origin, 0) = cursor.func.dfg.value_def(arg) {
                    match cursor.func.dfg[arg_origin].opcode() {
                        Opcode::StackAddr => used_stack_addr_insts[arg_origin] = true,
                        Opcode::StackLoad => used_stack_load_insts[arg_origin] = true,
                        _ => {}
                    }
                }
            }
        }
    }

    println!(
        "stack_addr: [{}] ([{}] used)\nstack_load: [{}] ([{}] used)\nstack_store: [{}]",
        bool_secondary_map_to_string(&stack_addr_insts),
        bool_secondary_map_to_string(&used_stack_addr_insts),
        bool_secondary_map_to_string(&stack_load_insts),
        bool_secondary_map_to_string(&used_stack_load_insts),
        bool_secondary_map_to_string(&stack_store_insts),
    );

    for inst in used_stack_addr_insts.keys().filter(|&inst| used_stack_addr_insts[inst]) {
        assert!(stack_addr_insts[inst]);
    }

    // Replace all unused stack_addr instructions with nop.
    for inst in stack_addr_insts.keys() {
        if stack_addr_insts[inst] && !used_stack_addr_insts[inst] {
            func.dfg.detach_results(inst);
            func.dfg.replace(inst).nop();
            stack_addr_insts[inst] = false;
        }
    }

    //println!("stack_addr (after): [{}]", bool_secondary_map_to_string(&stack_addr_insts));

    let mut stack_slot_usage_map: SecondaryMap<StackSlot, HashSet<Inst>> = SecondaryMap::new();
    for inst in stack_load_insts.keys().filter(|&inst| stack_load_insts[inst]) {
        match func.dfg[inst] {
            InstructionData::StackLoad {
                opcode: Opcode::StackLoad,
                stack_slot,
                offset: _,
            } => {
                stack_slot_usage_map[stack_slot].insert(inst);
            }
            ref data => unreachable!("{:?}", data),
        }
    }
    for inst in stack_store_insts.keys().filter(|&inst| stack_store_insts[inst]) {
        match func.dfg[inst] {
            InstructionData::StackStore {
                opcode: Opcode::StackStore,
                arg: _,
                stack_slot,
                offset: _,
            } => {
                stack_slot_usage_map[stack_slot].insert(inst);
            }
            ref data => unreachable!("{:?}", data),
        }
    }
    for inst in stack_addr_insts.keys().filter(|&inst| stack_addr_insts[inst]) {
        match func.dfg[inst] {
            InstructionData::StackLoad {
                opcode: Opcode::StackAddr,
                stack_slot,
                offset: _,
            } => {
                stack_slot_usage_map[stack_slot].insert(inst);
            }
            ref data => unreachable!("{:?}", data),
        }
    }

    println!("{:?}\n", stack_slot_usage_map);

    for (stack_slot, users) in stack_slot_usage_map.iter_mut() {
        let mut is_addr_leaked = false;
        let mut is_loaded = false;
        let mut is_stored = false;
        for &user in users.iter() {
            match func.dfg[user] {
                InstructionData::StackLoad {
                    opcode: Opcode::StackAddr,
                    stack_slot,
                    offset: _,
                } => {
                    is_addr_leaked = true;
                }
                InstructionData::StackLoad {
                    opcode: Opcode::StackLoad,
                    stack_slot,
                    offset: _,
                } => {
                    is_loaded = true;
                }
                InstructionData::StackStore {
                    opcode: Opcode::StackStore,
                    arg: _,
                    stack_slot,
                    offset: _,
                } => {
                    is_stored = true;
                }
                ref data => unreachable!("{:?}", data),
            }
        }

        if is_addr_leaked || (is_loaded && is_stored) {
            continue;
        }

        if is_loaded {
            println!("[{}] [BUG?] Reading uninitialized memory", name);
        } else {
            // Stored value never read; just remove reads.
            for &user in users.iter() {
                println!("[{}] Remove dead stack store {} of {}", name, user, stack_slot);
                func.dfg.replace(user).nop();
            }
        }
    }
}

fn try_get_stack_slot_and_offset_for_addr(func: &Function, addr: Value) -> Option<(StackSlot, Offset32)> {
    if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) {
        if let InstructionData::StackLoad {
            opcode: Opcode::StackAddr,
            stack_slot,
            offset,
        } = func.dfg[addr_inst] {
            return Some((stack_slot, offset));
        }
    }
    None
}

fn bool_secondary_map_to_string<E>(map: &SecondaryMap<E, bool>) -> String
    where E: cranelift_codegen::entity::EntityRef + std::fmt::Display,
{
    map
        .keys()
        .filter_map(|inst| {
            // EntitySet::keys returns all possible entities until the last entity inserted.
            if map[inst] {
                Some(format!("{}", inst))
            } else {
                None
            }
        })
        .collect::<Vec<String>>()
        .join(", ")
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};
use std::ptr;
use std::io;
use time::{Duration, SteadyTime};

use kernel32;
use winapi;

use error::{Error, Result};

use super::{HabExitStatus, ExitStatusExt, ShutdownMethod};

const STILL_ACTIVE: u32 = 259;

pub fn become_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    become_child_command(command, args)
}

/// Get process identifier of calling process.
pub fn current_pid() -> u32 {
    unsafe { kernel32::GetCurrentProcessId() as u32 }
}

/// Determines if a process is running with the given process identifier.
pub fn is_alive(pid: u32) -> bool {
    handle_from_pid(pid).is_some()
}

/// Executes a command as a child process and exits with the child's exit code.
///
/// Note that if successful, this function will not return.
///
/// # Failures
///
/// * If the child process cannot be created
fn become_child_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    debug!("Calling child process: ({:?}) {:?}",
           command.display(),
           &args);
    let status = try!(Command::new(command).args(&args).status());
    // Let's honor the exit codes from the child process we finished running
    process::exit(status.code().unwrap())
}

fn handle_from_pid(pid: u32) -> Option<winapi::HANDLE> {
    unsafe {
        let proc_handle = kernel32::OpenProcess(winapi::PROCESS_QUERY_LIMITED_INFORMATION |
                                                winapi::PROCESS_TERMINATE,
                                                winapi::FALSE,
                                                pid as winapi::DWORD);

        // we expect this to happen if the process died
        // before OpenProcess completes
        if proc_handle == ptr::null_mut() {
            return None;
        } else {
            return Some(proc_handle);
        }
    }
}

pub struct Child {
    handle: Option<winapi::HANDLE>,
    last_status: Option<u32>,
    pid: u32,
}

impl Child {
    // On windows we need the process handle to capture status
    // Here we will attempt to get the handle from the pid but if the
    // process dies before we can get it, we will just wait() on the
    // std::process::Child and cache the exit_status which we will return
    // when status is called.
    pub fn new(child: &mut process::Child) -> Result<Child> {
        let (win_handle, status) = match handle_from_pid(child.id()) {
            Some(handle) => (Some(handle), Ok(None)),
            _ => {
                (None,
                 {
                     match child.wait() {
                         Ok(exit) => Ok(Some(exit.code().unwrap() as u32)),
                         Err(e) => {
                             Err(format!("Failed to retrieve exit code for pid {} : {}",
                                         child.id(),
                                         e))
                         }
                     }
                 })
            }
        };

        match status {
            Ok(status) => {
                Ok(Child {
                       handle: win_handle,
                       last_status: status,
                       pid: child.id(),
                   })
            }
            Err(e) => Err(Error::GetHabChildFailed(e)),
        }
    }

    pub fn id(&self) -> u32 {
        self.pid
    }

    pub fn status(&mut self) -> Result<HabExitStatus> {
        if self.last_status.is_some() {
            return Ok(HabExitStatus { status: Some(self.last_status.unwrap()) });
        }

        let mut exit_status: u32 = 0;

        unsafe {
            let ret = kernel32::GetExitCodeProcess(self.handle.unwrap(),
                                                   &mut exit_status as winapi::LPDWORD);
            if ret == 0 {
                return Err(Error::GetExitCodeProcessFailed(format!("Failed to retrieve Exit \
                                                                    Code for pid {}: {}",
                                                                   self.pid,
                                                                   io::Error::last_os_error())));
            }
        }

        if exit_status == STILL_ACTIVE {
            return Ok(HabExitStatus { status: None });
        };

        Ok(HabExitStatus { status: Some(exit_status) })
    }

    pub fn kill(&mut self) -> Result<ShutdownMethod> {
        if self.last_status.is_some() {
            return Ok(ShutdownMethod::AlreadyExited);
        }

        let mut ret;
        unsafe {
            // Turn off ctrl-C handling for current process
            ret = kernel32::SetConsoleCtrlHandler(None, winapi::TRUE);
            if ret == 0 {
                debug!("Failed to call SetConsoleCtrlHandler on pid {}: {}",
                       self.pid,
                       io::Error::last_os_error());
            }

            if ret != 0 {
                // Send a ctrl-C
                ret = kernel32::GenerateConsoleCtrlEvent(0, 0);
                if ret == 0 {
                    debug!("Failed to send ctrl-c to pid {}: {}",
                           self.pid,
                           io::Error::last_os_error());
                }
            }
        }

        let stop_time = SteadyTime::now() + Duration::seconds(8);

        let result;
        loop {
            if ret == 0 || SteadyTime::now() > stop_time {
                unsafe {
                    ret = kernel32::TerminateProcess(self.handle.unwrap(), 1);
                    if ret == 0 {
                        result = Err(Error::TerminateProcessFailed(format!("Failed to call \
                                                                       terminate pid {}: {}",
                                                                      self.pid,
                                                                      io::Error::last_os_error())));
                    } else {
                        result = Ok(ShutdownMethod::Killed);
                    }
                    break;
                }
            }

            match self.status() {
                Ok(status) => {
                    if !status.no_status() {
                        result = Ok(ShutdownMethod::GracefulTermination);
                        break;
                    }
                }
                _ => {}
            }
        }

        // turn Ctrl-C handling back on for current process
        ret = unsafe { kernel32::SetConsoleCtrlHandler(None, winapi::FALSE) };
        if ret == 0 {
            debug!("Failed to call SetConsoleCtrlHandler on pid {}: {}",
                   self.pid,
                   io::Error::last_os_error());
        }

        result
    }
}

// Have to implement these due to our HANDLE field
unsafe impl Send for Child {}
unsafe impl Sync for Child {}

impl Drop for Child {
    fn drop(&mut self) {
        match self.handle {
            None => {}
            Some(handle) => unsafe {
                let _ = kernel32::CloseHandle(handle);
            },
        }
    }
}

impl ExitStatusExt for HabExitStatus {
    fn code(&self) -> Option<u32> {
        self.status
    }

    fn signal(&self) -> Option<u32> {
        None
    }
}

#[cfg(test)]
mod tests {
    use std::process::Command;
    use super::super::*;

    #[test]
    fn running_process_returns_no_exit_status() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("while($true) { Start-Sleep 1 }");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();

        assert!(hab_child.status().unwrap().no_status())
    }

    #[test]
    fn successfully_run_process_exits_zero() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("$a='b'");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.wait();

        assert_eq!(hab_child.status().unwrap().code(), Some(0))
    }

    #[test]
    fn terminated_process_returns_non_zero_exit() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("while($true) { Start-Sleep 1 }");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.kill();

        assert!(hab_child.status().unwrap().code() != Some(0))
    }

    #[test]
    fn process_that_exits_with_specific_code_has_same_exit_code() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("exit 5000");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.wait();

        assert_eq!(hab_child.status().unwrap().code(), Some(5000))
    }
}
<commit_msg>Fix file handle leak in `handle_from_pid()<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};
use std::ptr;
use std::io;
use time::{Duration, SteadyTime};

use kernel32;
use winapi;

use error::{Error, Result};

use super::{HabExitStatus, ExitStatusExt, ShutdownMethod};

const STILL_ACTIVE: u32 = 259;

pub fn become_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    become_child_command(command, args)
}

/// Get process identifier of calling process.
pub fn current_pid() -> u32 {
    unsafe { kernel32::GetCurrentProcessId() as u32 }
}

/// Determines if a process is running with the given process identifier.
pub fn is_alive(pid: u32) -> bool {
    match handle_from_pid(pid) {
        Some(handle) => {
            unsafe {
                let _ = kernel32::CloseHandle(handle);
            }
            true
        }
        None => false,
    }
}

/// Executes a command as a child process and exits with the child's exit code.
///
/// Note that if successful, this function will not return.
///
/// # Failures
///
/// * If the child process cannot be created
fn become_child_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    debug!("Calling child process: ({:?}) {:?}",
           command.display(),
           &args);
    let status = try!(Command::new(command).args(&args).status());
    // Let's honor the exit codes from the child process we finished running
    process::exit(status.code().unwrap())
}

fn handle_from_pid(pid: u32) -> Option<winapi::HANDLE> {
    unsafe {
        let proc_handle = kernel32::OpenProcess(winapi::PROCESS_QUERY_LIMITED_INFORMATION |
                                                winapi::PROCESS_TERMINATE,
                                                winapi::FALSE,
                                                pid as winapi::DWORD);

        // we expect this to happen if the process died
        // before OpenProcess completes
        if proc_handle == ptr::null_mut() {
            return None;
        } else {
            return Some(proc_handle);
        }
    }
}

pub struct Child {
    handle: Option<winapi::HANDLE>,
    last_status: Option<u32>,
    pid: u32,
}

impl Child {
    // On windows we need the process handle to capture status
    // Here we will attempt to get the handle from the pid but if the
    // process dies before we can get it, we will just wait() on the
    // std::process::Child and cache the exit_status which we will return
    // when status is called.
    pub fn new(child: &mut process::Child) -> Result<Child> {
        let (win_handle, status) = match handle_from_pid(child.id()) {
            Some(handle) => (Some(handle), Ok(None)),
            _ => {
                (None,
                 {
                     match child.wait() {
                         Ok(exit) => Ok(Some(exit.code().unwrap() as u32)),
                         Err(e) => {
                             Err(format!("Failed to retrieve exit code for pid {} : {}",
                                         child.id(),
                                         e))
                         }
                     }
                 })
            }
        };

        match status {
            Ok(status) => {
                Ok(Child {
                       handle: win_handle,
                       last_status: status,
                       pid: child.id(),
                   })
            }
            Err(e) => Err(Error::GetHabChildFailed(e)),
        }
    }

    pub fn id(&self) -> u32 {
        self.pid
    }

    pub fn status(&mut self) -> Result<HabExitStatus> {
        if self.last_status.is_some() {
            return Ok(HabExitStatus { status: Some(self.last_status.unwrap()) });
        }

        let mut exit_status: u32 = 0;

        unsafe {
            let ret = kernel32::GetExitCodeProcess(self.handle.unwrap(),
                                                   &mut exit_status as winapi::LPDWORD);
            if ret == 0 {
                return Err(Error::GetExitCodeProcessFailed(format!("Failed to retrieve Exit \
                                                                    Code for pid {}: {}",
                                                                   self.pid,
                                                                   io::Error::last_os_error())));
            }
        }

        if exit_status == STILL_ACTIVE {
            return Ok(HabExitStatus { status: None });
        };

        Ok(HabExitStatus { status: Some(exit_status) })
    }

    pub fn kill(&mut self) -> Result<ShutdownMethod> {
        if self.last_status.is_some() {
            return Ok(ShutdownMethod::AlreadyExited);
        }

        let mut ret;
        unsafe {
            // Turn off ctrl-C handling for current process
            ret = kernel32::SetConsoleCtrlHandler(None, winapi::TRUE);
            if ret == 0 {
                debug!("Failed to call SetConsoleCtrlHandler on pid {}: {}",
                       self.pid,
                       io::Error::last_os_error());
            }

            if ret != 0 {
                // Send a ctrl-C
                ret = kernel32::GenerateConsoleCtrlEvent(0, 0);
                if ret == 0 {
                    debug!("Failed to send ctrl-c to pid {}: {}",
                           self.pid,
                           io::Error::last_os_error());
                }
            }
        }

        let stop_time = SteadyTime::now() + Duration::seconds(8);

        let result;
        loop {
            if ret == 0 || SteadyTime::now() > stop_time {
                unsafe {
                    ret = kernel32::TerminateProcess(self.handle.unwrap(), 1);
                    if ret == 0 {
                        result = Err(Error::TerminateProcessFailed(format!("Failed to call \
                                                                       terminate pid {}: {}",
                                                                      self.pid,
                                                                      io::Error::last_os_error())));
                    } else {
                        result = Ok(ShutdownMethod::Killed);
                    }
                    break;
                }
            }

            match self.status() {
                Ok(status) => {
                    if !status.no_status() {
                        result = Ok(ShutdownMethod::GracefulTermination);
                        break;
                    }
                }
                _ => {}
            }
        }

        // turn Ctrl-C handling back on for current process
        ret = unsafe { kernel32::SetConsoleCtrlHandler(None, winapi::FALSE) };
        if ret == 0 {
            debug!("Failed to call SetConsoleCtrlHandler on pid {}: {}",
                   self.pid,
                   io::Error::last_os_error());
        }

        result
    }
}

// Have to implement these due to our HANDLE field
unsafe impl Send for Child {}
unsafe impl Sync for Child {}

impl Drop for Child {
    fn drop(&mut self) {
        match self.handle {
            None => {}
            Some(handle) => unsafe {
                let _ = kernel32::CloseHandle(handle);
            },
        }
    }
}

impl ExitStatusExt for HabExitStatus {
    fn code(&self) -> Option<u32> {
        self.status
    }

    fn signal(&self) -> Option<u32> {
        None
    }
}

#[cfg(test)]
mod tests {
    use std::process::Command;
    use super::super::*;

    #[test]
    fn running_process_returns_no_exit_status() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("while($true) { Start-Sleep 1 }");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();

        assert!(hab_child.status().unwrap().no_status())
    }

    #[test]
    fn successfully_run_process_exits_zero() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("$a='b'");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.wait();

        assert_eq!(hab_child.status().unwrap().code(), Some(0))
    }

    #[test]
    fn terminated_process_returns_non_zero_exit() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("while($true) { Start-Sleep 1 }");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.kill();

        assert!(hab_child.status().unwrap().code() != Some(0))
    }

    #[test]
    fn process_that_exits_with_specific_code_has_same_exit_code() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("exit 5000");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.wait();

        assert_eq!(hab_child.status().unwrap().code(), Some(5000))
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io::timer::sleep;
use std::time::duration::Duration;
use std::comm::Disconnected;

use std::fmt::Show;

use std::sync::{Arc, Mutex};

use std::task::TaskBuilder;

use super::replication::Committable;

pub trait Intercommunication < T: Committable + Send + Show > {
    fn new() -> Self;
    fn register(&mut self, host: String) -> Endpoint < T >;
    fn receive(&mut self) -> Option < Package < T > >;
    fn send(&mut self, recipient: String, package: Package < T >);
    fn is_debug(&self) -> bool;
}

pub struct DefaultIntercommunication < T: Committable + Send > {
    receiver: Receiver < Package < T > >,
    sender: Sender < Package < T > >,
    senders: HashMap < String, Sender < Package < T > > >,

    pub is_debug: bool,
}

pub struct Endpoint < T: Committable + Send > {
    pub host: String,
    pub tx: Sender < Package < T > >,
    pub rx: Receiver < Package < T > >,
}

#[deriving(Clone, Show)]
pub struct AppendLog < T: Committable + Send > {
    pub committed_offset: uint,
    pub node_list: Vec < String >,
    pub enqueue: Option < AppendLogEntry < T > >,
}

#[deriving(Clone, Show)]
pub struct AppendLogEntry < T: Committable + Send > {
    pub offset: uint,
    pub entry: T,
}

impl < T: Committable + Send + Show > Intercommunication < T > for DefaultIntercommunication < T > {
    fn new() -> DefaultIntercommunication < T > {
        let (tx, rx) = channel();

        DefaultIntercommunication {
            senders: HashMap::new(),
            sender: tx,
            receiver: rx,
            is_debug: false,
        }
    }

    fn register(&mut self, host: String) -> Endpoint < T > {
        let (tx, rx) = channel();

        self.senders.insert(host.clone(), tx);

        Endpoint {
            host: host,
            rx: rx,
            tx: self.sender.clone(),
        }
    }

    fn receive(&mut self) -> Option < Package < T > > {
        match self.receiver.try_recv() {
            Ok(package) => Some(package),
            _ => None,
        }
    }

    fn send(&mut self, recipient: String, package: Package < T >) {
        match self.senders.find(&recipient) {
            Some(tx) => {
                // be more careful at sending
                match tx.send_opt(package) {
                    Err(_) => (),
                    _ => (),
                }
            },
            None => (),
        }
    }

    fn is_debug(&self) -> bool {
        self.is_debug
    }
}

impl < T: Committable + Send > Endpoint < T > {
    pub fn send (&self, host: String, package: PackageDetails < T >) {
        self.tx.send(Pack(self.host.clone(), host, package));
    }

    pub fn listen_block_with_timeout(&self) -> Option < Package < T > > {
        for _ in range(0, 10u) {
            match self.listen() {
                Some(x) => return Some(x),
                _ => ()
            }

            sleep(Duration::milliseconds(2));
        }

        None
    }

    pub fn listen(&self) -> Option < Package < T > > {
        match self.rx.try_recv() {
            Ok(result) => Some(result),
            _ => None,
        }
    }
}

#[deriving(Show)]
pub enum PackageDetails < T: Committable + Send > {
    Ack,

    LeaderQuery,

    // LeaderQueryResponse(leader_host)
    LeaderQueryResponse(Option < String >),

    // AppendQuery(log)
    AppendQuery(AppendLog < T >),

    // Persisted(entry_offset)
    Persisted(uint),

    // RequestVote(term
    RequestVote(uint),

    // Vote(term)
    Vote(uint),
}

#[deriving(Show)]
pub enum Package < T: Committable + Send > {
    // Pack(from, to, package)
    Pack(String, String, PackageDetails < T >),
}

pub fn start < T: Committable + Send + Clone + Show, I: Intercommunication < T > + Send >(intercommunication: I) -> Sender < int > {
    let mutex = Arc::new(Mutex::new(intercommunication));
    let (exit_tx, exit_rx) = channel();

    TaskBuilder::new().named("intercommunication").spawn(proc() {
        loop {
            let mut intercommunication = mutex.lock();

            match intercommunication.receive() {
                Some(Pack(from, to, package)) => {
                    if intercommunication.is_debug() {
                        println!("sent package {} to host {} from {}", package, to, from);
                    }

                    intercommunication.send(to.clone(), Pack(from, to, package));
                },
                None => ()
            }

            match exit_rx.try_recv() {
                Ok(_) | Err(Disconnected) => break,
                _ => (),
            }

            intercommunication.cond.signal();

            sleep(Duration::milliseconds(2));
        }

        println!("intercommunication is dead");
    });

    exit_tx
}
<commit_msg>Try to ignore Err(Disconnected) on intercommunication task exit signal<commit_after>use std::collections::HashMap;
use std::io::timer::sleep;
use std::time::duration::Duration;
use std::comm::Disconnected;

use std::fmt::Show;

use std::sync::{Arc, Mutex};

use std::task::TaskBuilder;

use super::replication::Committable;

pub trait Intercommunication < T: Committable + Send + Show > {
    fn new() -> Self;
    fn register(&mut self, host: String) -> Endpoint < T >;
    fn receive(&mut self) -> Option < Package < T > >;
    fn send(&mut self, recipient: String, package: Package < T >);
    fn is_debug(&self) -> bool;
}

pub struct DefaultIntercommunication < T: Committable + Send > {
    receiver: Receiver < Package < T > >,
    sender: Sender < Package < T > >,
    senders: HashMap < String, Sender < Package < T > > >,

    pub is_debug: bool,
}

pub struct Endpoint < T: Committable + Send > {
    pub host: String,
    pub tx: Sender < Package < T > >,
    pub rx: Receiver < Package < T > >,
}

#[deriving(Clone, Show)]
pub struct AppendLog < T: Committable + Send > {
    pub committed_offset: uint,
    pub node_list: Vec < String >,
    pub enqueue: Option < AppendLogEntry < T > >,
}

#[deriving(Clone, Show)]
pub struct AppendLogEntry < T: Committable + Send > {
    pub offset: uint,
    pub entry: T,
}

impl < T: Committable + Send + Show > Intercommunication < T > for DefaultIntercommunication < T > {
    fn new() -> DefaultIntercommunication < T > {
        let (tx, rx) = channel();

        DefaultIntercommunication {
            senders: HashMap::new(),
            sender: tx,
            receiver: rx,
            is_debug: false,
        }
    }

    fn register(&mut self, host: String) -> Endpoint < T > {
        let (tx, rx) = channel();

        self.senders.insert(host.clone(), tx);

        Endpoint {
            host: host,
            rx: rx,
            tx: self.sender.clone(),
        }
    }

    fn receive(&mut self) -> Option < Package < T > > {
        match self.receiver.try_recv() {
            Ok(package) => Some(package),
            _ => None,
        }
    }

    fn send(&mut self, recipient: String, package: Package < T >) {
        match self.senders.find(&recipient) {
            Some(tx) => {
                // be more careful at sending
                match tx.send_opt(package) {
                    Err(_) => (),
                    _ => (),
                }
            },
            None => (),
        }
    }

    fn is_debug(&self) -> bool {
        self.is_debug
    }
}

impl < T: Committable + Send > Endpoint < T > {
    pub fn send (&self, host: String, package: PackageDetails < T >) {
        self.tx.send(Pack(self.host.clone(), host, package));
    }

    pub fn listen_block_with_timeout(&self) -> Option < Package < T > > {
        for _ in range(0, 10u) {
            match self.listen() {
                Some(x) => return Some(x),
                _ => ()
            }

            sleep(Duration::milliseconds(2));
        }

        None
    }

    pub fn listen(&self) -> Option < Package < T > > {
        match self.rx.try_recv() {
            Ok(result) => Some(result),
            _ => None,
        }
    }
}

#[deriving(Show)]
pub enum PackageDetails < T: Committable + Send > {
    Ack,

    LeaderQuery,

    // LeaderQueryResponse(leader_host)
    LeaderQueryResponse(Option < String >),

    // AppendQuery(log)
    AppendQuery(AppendLog < T >),

    // Persisted(entry_offset)
    Persisted(uint),

    // RequestVote(term
    RequestVote(uint),

    // Vote(term)
    Vote(uint),
}

#[deriving(Show)]
pub enum Package < T: Committable + Send > {
    // Pack(from, to, package)
    Pack(String, String, PackageDetails < T >),
}

pub fn start < T: Committable + Send + Clone + Show, I: Intercommunication < T > + Send >(intercommunication: I) -> Sender < int > {
    let mutex = Arc::new(Mutex::new(intercommunication));
    let (exit_tx, exit_rx) = channel();

    TaskBuilder::new().named("intercommunication").spawn(proc() {
        loop {
            let mut intercommunication = mutex.lock();

            match intercommunication.receive() {
                Some(Pack(from, to, package)) => {
                    if intercommunication.is_debug() {
                        println!("sent package {} to host {} from {}", package, to, from);
                    }

                    intercommunication.send(to.clone(), Pack(from, to, package));
                },
                None => ()
            }

            match exit_rx.try_recv() {
                Ok(_) => break,
                _ => (),
            }

            intercommunication.cond.signal();

            sleep(Duration::milliseconds(2));
        }

        println!("intercommunication task is dead");
    });

    exit_tx
}
<|endoftext|>


<commit_before>use std::u32;
use std::sync::Arc;
use parking_lot::RwLock;
use elements::{MemoryType, ResizableLimits};
use interpreter::{Error, UserError};
use interpreter::module::check_limits;

/// Linear memory page size.
pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536;
/// Maximal number of pages.
const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;

/// Linear memory instance.
pub struct MemoryInstance<E: UserError> {
	/// Memofy limits.
	limits: ResizableLimits,
	/// Linear memory buffer.
	buffer: RwLock<Vec<u8>>,
	/// Maximum buffer size.
	maximum_size: u32,
	/// Dummy to avoid compilation error.
	_dummy: ::std::marker::PhantomData<E>,
}

struct CheckedRegion<'a, B: 'a> where B: ::std::ops::Deref<Target=Vec<u8>> {
	buffer: &'a B,
	offset: usize,
	size: usize,
}

impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref<Target=Vec<u8>> {
	fn range(&self) -> ::std::ops::Range<usize> {
		self.offset..self.offset+self.size
	}

	fn slice(&self) -> &[u8] {
		&self.buffer[self.range()]
	}
}

impl<E> MemoryInstance<E> where E: UserError {
	/// Create new linear memory instance.
	pub fn new(memory_type: &MemoryType) -> Result<Arc<Self>, Error<E>> {
		check_limits(memory_type.limits())?;

		let maximum_size = match memory_type.limits().maximum() {
			Some(maximum_pages) if maximum_pages > LINEAR_MEMORY_MAX_PAGES =>
				return Err(Error::Memory(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES))),
			Some(maximum_pages) => maximum_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE),
			None => u32::MAX,
		};
		let initial_size = calculate_memory_size(0, memory_type.limits().initial(), maximum_size)
			.ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?;

		let memory = MemoryInstance {
			limits: memory_type.limits().clone(),
			buffer: RwLock::new(vec![0; initial_size as usize]),
			maximum_size: maximum_size,
			_dummy: Default::default(),
		};

		Ok(Arc::new(memory))
	}

	/// Return linear memory limits.
	pub fn limits(&self) -> &ResizableLimits {
		&self.limits
	}

	/// Return linear memory size (in pages).
	pub fn size(&self) -> u32 {
		self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
	}

	/// Get data at given offset.
	pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error<E>> {
		let buffer = self.buffer.read();
		let region = self.checked_region(&buffer, offset as usize, size)?;

		Ok(region.slice().to_vec())
	}

	/// Write memory slice into another slice
	pub fn get_into(&self, offset: u32, target: &mut [u8]) -> Result<(), Error<E>> {
		let buffer = self.buffer.read();
		let region = self.checked_region(&buffer, offset as usize, target.len())?;

		target.copy_from_slice(region.slice());

		Ok(())
	}

	/// Set data at given offset.
	pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error<E>> {
		let mut buffer = self.buffer.write();
		let range = self.checked_region(&buffer, offset as usize, value.len())?.range();

		buffer[range].copy_from_slice(value);

		Ok(())
	}

	/// Increases the size of the linear memory by given number of pages.
	/// Returns -1 if allocation fails or previous memory size, if succeeds.
	pub fn grow(&self, pages: u32) -> Result<u32, Error<E>> {
		let mut buffer = self.buffer.write();
		let old_size = buffer.len() as u32;
		match calculate_memory_size(old_size, pages, self.maximum_size) {
			None => Ok(u32::MAX),
			Some(new_size) => {
				buffer.resize(new_size as usize, 0);
				Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
			},
		}
	}

	fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error<E>>
		where B: ::std::ops::Deref<Target=Vec<u8>>
	{
		let end = offset.checked_add(size)
			.ok_or(Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;

		if end > buffer.len() {
			return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len())));
		}

		Ok(CheckedRegion {
			buffer: buffer,
			offset: offset,
			size: size,
		})
	}

	/// Copy memory region
	pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error<E>> {
		let buffer = self.buffer.write();

		let read_region = self.checked_region(&buffer, src_offset, len)?;
		let write_region = self.checked_region(&buffer, dst_offset, len)?;

		unsafe { ::std::ptr::copy(
			buffer[read_region.range()].as_ptr(),
			buffer[write_region.range()].as_ptr() as *mut _,
			len,
		)}

		Ok(())
	}

	/// Zero memory region
	pub fn zero(&self, offset: usize, len: usize) -> Result<(), Error<E>> {
		let mut buffer = self.buffer.write();

		let range = self.checked_region(&buffer, offset, len)?.range();
		for val in &mut buffer[range] { *val = 0 }
		Ok(())
	}
}

fn calculate_memory_size(old_size: u32, additional_pages: u32, maximum_size: u32) -> Option<u32> {
	additional_pages
		.checked_mul(LINEAR_MEMORY_PAGE_SIZE)
		.and_then(|size| size.checked_add(old_size))
		.and_then(|size| if size > maximum_size {
			None
		} else {
			Some(size)
		})
}

#[cfg(test)]
mod tests {

	use super::MemoryInstance;
	use elements::MemoryType;
	use interpreter::DummyUserError;

	#[test]
	fn get_into() {
		let mem = MemoryInstance::<DummyUserError>::new(&MemoryType::new(1, None)).expect("memory instance creation should not fail");
		mem.set(6, &[13, 17, 129]).expect("memory set should not fail");

		let mut data = [0u8; 2];
		mem.get_into(7, &mut data[..]).expect("get_into should not fail");

		assert_eq!(data, [17, 129]);
	}
}<commit_msg>Implement copy_nonoverlapping and clear.<commit_after>use std::u32;
use std::sync::Arc;
use std::ops::Range;
use std::cmp;
use parking_lot::RwLock;
use elements::{MemoryType, ResizableLimits};
use interpreter::{Error, UserError};
use interpreter::module::check_limits;

/// Linear memory page size.
pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536;
/// Maximal number of pages.
const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;

/// Linear memory instance.
pub struct MemoryInstance<E: UserError> {
	/// Memofy limits.
	limits: ResizableLimits,
	/// Linear memory buffer.
	buffer: RwLock<Vec<u8>>,
	/// Maximum buffer size.
	maximum_size: u32,
	/// Dummy to avoid compilation error.
	_dummy: ::std::marker::PhantomData<E>,
}

struct CheckedRegion<'a, B: 'a> where B: ::std::ops::Deref<Target=Vec<u8>> {
	buffer: &'a B,
	offset: usize,
	size: usize,
}

impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref<Target=Vec<u8>> {
	fn range(&self) -> Range<usize> {
		self.offset..self.offset+self.size
	}

	fn slice(&self) -> &[u8] {
		&self.buffer[self.range()]
	}

	fn intersects(&self, other: &Self) -> bool {
		let low = cmp::max(self.offset, other.offset);
		let high = cmp::min(self.offset + self.size, other.offset + other.size);

		low < high
	}
}

impl<E> MemoryInstance<E> where E: UserError {
	/// Create new linear memory instance.
	pub fn new(memory_type: &MemoryType) -> Result<Arc<Self>, Error<E>> {
		check_limits(memory_type.limits())?;

		let maximum_size = match memory_type.limits().maximum() {
			Some(maximum_pages) if maximum_pages > LINEAR_MEMORY_MAX_PAGES =>
				return Err(Error::Memory(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES))),
			Some(maximum_pages) => maximum_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE),
			None => u32::MAX,
		};
		let initial_size = calculate_memory_size(0, memory_type.limits().initial(), maximum_size)
			.ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?;

		let memory = MemoryInstance {
			limits: memory_type.limits().clone(),
			buffer: RwLock::new(vec![0; initial_size as usize]),
			maximum_size: maximum_size,
			_dummy: Default::default(),
		};

		Ok(Arc::new(memory))
	}

	/// Return linear memory limits.
	pub fn limits(&self) -> &ResizableLimits {
		&self.limits
	}

	/// Return linear memory size (in pages).
	pub fn size(&self) -> u32 {
		self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
	}

	/// Get data at given offset.
	pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error<E>> {
		let buffer = self.buffer.read();
		let region = self.checked_region(&buffer, offset as usize, size)?;

		Ok(region.slice().to_vec())
	}

	/// Write memory slice into another slice
	pub fn get_into(&self, offset: u32, target: &mut [u8]) -> Result<(), Error<E>> {
		let buffer = self.buffer.read();
		let region = self.checked_region(&buffer, offset as usize, target.len())?;

		target.copy_from_slice(region.slice());

		Ok(())
	}

	/// Set data at given offset.
	pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error<E>> {
		let mut buffer = self.buffer.write();
		let range = self.checked_region(&buffer, offset as usize, value.len())?.range();

		buffer[range].copy_from_slice(value);

		Ok(())
	}

	/// Increases the size of the linear memory by given number of pages.
	/// Returns -1 if allocation fails or previous memory size, if succeeds.
	pub fn grow(&self, pages: u32) -> Result<u32, Error<E>> {
		let mut buffer = self.buffer.write();
		let old_size = buffer.len() as u32;
		match calculate_memory_size(old_size, pages, self.maximum_size) {
			None => Ok(u32::MAX),
			Some(new_size) => {
				buffer.resize(new_size as usize, 0);
				Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
			},
		}
	}

	fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error<E>>
		where B: ::std::ops::Deref<Target=Vec<u8>>
	{
		let end = offset.checked_add(size)
			.ok_or(Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;

		if end > buffer.len() {
			return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len())));
		}

		Ok(CheckedRegion {
			buffer: buffer,
			offset: offset,
			size: size,
		})
	}

	/// Copy memory region. Semantically equivalent to `memmove`.
	pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error<E>> {
		let buffer = self.buffer.write();

		let read_region = self.checked_region(&buffer, src_offset, len)?;
		let write_region = self.checked_region(&buffer, dst_offset, len)?;

		unsafe { ::std::ptr::copy(
			buffer[read_region.range()].as_ptr(),
			buffer[write_region.range()].as_ptr() as *mut _,
			len,
		)}

		Ok(())
	}

	/// Copy memory region, non-overlapping version. Semantically equivalent to `memcpy`,
	/// but returns Error if source overlaping with destination.
	pub fn copy_nonoverlapping(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error<E>> {
		let buffer = self.buffer.write();

		let read_region = self.checked_region(&buffer, src_offset, len)?;
		let write_region = self.checked_region(&buffer, dst_offset, len)?;

		if read_region.intersects(&write_region) {
			return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions")))
		}

		unsafe { ::std::ptr::copy_nonoverlapping(
			buffer[read_region.range()].as_ptr(),
			buffer[write_region.range()].as_ptr() as *mut _,
			len,
		)}

		Ok(())
	}

	/// Clear memory region with a specified value. Semantically equivalent to `memset`.
	pub fn clear(&self, offset: usize, new_val: u8, len: usize) -> Result<(), Error<E>> {
		let mut buffer = self.buffer.write();

		let range = self.checked_region(&buffer, offset, len)?.range();
		for val in &mut buffer[range] { *val = new_val }
		Ok(())
	}

	/// Zero memory region
	pub fn zero(&self, offset: usize, len: usize) -> Result<(), Error<E>> {
		self.clear(offset, 0, len)
	}
}

fn calculate_memory_size(old_size: u32, additional_pages: u32, maximum_size: u32) -> Option<u32> {
	additional_pages
		.checked_mul(LINEAR_MEMORY_PAGE_SIZE)
		.and_then(|size| size.checked_add(old_size))
		.and_then(|size| if size > maximum_size {
			None
		} else {
			Some(size)
		})
}

#[cfg(test)]
mod tests {

	use super::MemoryInstance;
	use interpreter::{Error, DummyUserError};
	use elements::MemoryType;
	use std::sync::Arc;

	fn create_memory(initial_content: &[u8]) -> Arc<MemoryInstance<DummyUserError>> {
		let mem = MemoryInstance::new(&MemoryType::new(1, Some(1)))
			.expect("MemoryInstance created successfuly");
		mem.set(0, initial_content).expect("Successful initialize the memory");
		mem
	}

	#[test]
	fn copy_overlaps_1() {
		let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
		mem.copy(0, 4, 6).expect("Successfully copy the elements");
		let result = mem.get(0, 10).expect("Successfully retrieve the result");
		assert_eq!(result, &[0, 1, 2, 3, 0, 1, 2, 3, 4, 5]);
	}

	#[test]
	fn copy_overlaps_2() {
		let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
		mem.copy(4, 0, 6).expect("Successfully copy the elements");
		let result = mem.get(0, 10).expect("Successfully retrieve the result");
		assert_eq!(result, &[4, 5, 6, 7, 8, 9, 6, 7, 8, 9]);
	}

	#[test]
	fn copy_nonoverlapping() {
		let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
		mem.copy_nonoverlapping(0, 10, 10).expect("Successfully copy the elements");
		let result = mem.get(10, 10).expect("Successfully retrieve the result");
		assert_eq!(result, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
	}

	#[test]
	fn copy_nonoverlapping_overlaps_1() {
		let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
		let result = mem.copy_nonoverlapping(0, 4, 6);
		match result {
			Err(Error::Memory(_)) => {},
			_ => panic!("Expected Error::Memory(_) result, but got {:?}", result),
		}
	}

	#[test]
	fn copy_nonoverlapping_overlaps_2() {
		let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
		let result = mem.copy_nonoverlapping(4, 0, 6);
		match result {
			Err(Error::Memory(_)) => {},
			_ => panic!("Expected Error::Memory(_), but got {:?}", result),
		}
	}

	#[test]
	fn clear() {
		let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
		mem.clear(0, 0x4A, 10).expect("To successfully clear the memory");
		let result = mem.get(0, 10).expect("To successfully retrieve the result");
		assert_eq!(result, &[0x4A; 10]);
	}

	#[test]
	fn get_into() {
		let mem = MemoryInstance::<DummyUserError>::new(&MemoryType::new(1, None)).expect("memory instance creation should not fail");
		mem.set(6, &[13, 17, 129]).expect("memory set should not fail");

		let mut data = [0u8; 2];
		mem.get_into(7, &mut data[..]).expect("get_into should not fail");

		assert_eq!(data, [17, 129]);
	}
}
<|endoftext|>