Add caching. (#134)

* Allow a module to have a different signature registry than the process-specific

* Add core ability to build compiled code caches

* Remove timing printouts

* Serialize/Deserialize memories to reduce copies

* Work more on api

* Relocate local functions relatively before external functions

* Fix incorrect definition in test

* merge errors caused by merge

* Fix emscripten compile

* Fix review comments
This commit is contained in:
Lachlan Sneff
2019-02-06 16:26:45 -08:00
committed by GitHub
parent 2f2f86a4de
commit 8fe9b7eac2
34 changed files with 1768 additions and 291 deletions

View File

@ -9,3 +9,77 @@ pub use self::unix::*;
#[cfg(windows)]
pub use self::windows::*;
#[cfg(feature = "cache")]
use serde::{
de::{self, SeqAccess, Visitor},
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
#[cfg(feature = "cache")]
use serde_bytes::Bytes;
#[cfg(feature = "cache")]
use std::fmt;
#[cfg(feature = "cache")]
impl Serialize for Memory {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
assert!(self.protection().is_readable());
let mut state = serializer.serialize_struct("Memory", 2)?;
state.serialize_field("protection", &self.protection())?;
state.serialize_field("data", &Bytes::new(unsafe { self.as_slice() }))?;
state.end()
}
}
#[cfg(feature = "cache")]
impl<'de> Deserialize<'de> for Memory {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MemoryVisitor;
impl<'de> Visitor<'de> for MemoryVisitor {
type Value = Memory;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "struct Memory")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Memory, V::Error>
where
V: SeqAccess<'de>,
{
let original_protection = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let bytes: Bytes = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let mut memory = Memory::with_size_protect(bytes.len(), Protect::ReadWrite)
.expect("Could not create a memory");
unsafe {
memory.as_slice_mut().copy_from_slice(&*bytes);
if memory.protection() != original_protection {
memory
.protect(.., original_protection)
.expect("Could not protect memory as its original protection");
}
}
Ok(memory)
}
}
deserializer.deserialize_struct("Memory", &["protection", "data"], MemoryVisitor)
}
}

View File

@ -2,7 +2,7 @@ use errno;
use nix::libc;
use page_size;
use std::ops::{Bound, RangeBounds};
use std::{ptr, slice};
use std::{fs::File, os::unix::io::IntoRawFd, path::Path, ptr, rc::Rc, slice};
unsafe impl Send for Memory {}
unsafe impl Sync for Memory {}
@ -11,14 +11,86 @@ unsafe impl Sync for Memory {}
pub struct Memory {
ptr: *mut u8,
size: usize,
protection: Protect,
fd: Option<Rc<RawFd>>,
}
impl Memory {
pub fn from_file_path<P>(path: P, protection: Protect) -> Result<Self, String>
where
P: AsRef<Path>,
{
let file = File::open(path).map_err(|e| e.to_string())?;
let file_len = file.metadata().map_err(|e| e.to_string())?.len();
let raw_fd = RawFd::from_file(file);
let ptr = unsafe {
libc::mmap(
ptr::null_mut(),
file_len as usize,
protection.to_protect_const() as i32,
libc::MAP_PRIVATE,
raw_fd.0,
0,
)
};
if ptr == -1 as _ {
Err(errno::errno().to_string())
} else {
Ok(Self {
ptr: ptr as *mut u8,
size: file_len as usize,
protection,
fd: Some(Rc::new(raw_fd)),
})
}
}
pub fn with_size_protect(size: usize, protection: Protect) -> Result<Self, String> {
if size == 0 {
return Ok(Self {
ptr: ptr::null_mut(),
size: 0,
protection,
fd: None,
});
}
let size = round_up_to_page_size(size, page_size::get());
let ptr = unsafe {
libc::mmap(
ptr::null_mut(),
size,
protection.to_protect_const() as i32,
libc::MAP_PRIVATE | libc::MAP_ANON,
-1,
0,
)
};
if ptr == -1 as _ {
Err(errno::errno().to_string())
} else {
Ok(Self {
ptr: ptr as *mut u8,
size,
protection,
fd: None,
})
}
}
pub fn with_size(size: usize) -> Result<Self, String> {
if size == 0 {
return Ok(Self {
ptr: ptr::null_mut(),
size: 0,
protection: Protect::None,
fd: None,
});
}
@ -41,6 +113,8 @@ impl Memory {
Ok(Self {
ptr: ptr as *mut u8,
size,
protection: Protect::None,
fd: None,
})
}
}
@ -48,9 +122,9 @@ impl Memory {
pub unsafe fn protect(
&mut self,
range: impl RangeBounds<usize>,
protect: Protect,
protection: Protect,
) -> Result<(), String> {
let protect = protect.to_protect_const();
let protect = protection.to_protect_const();
let range_start = match range.start_bound() {
Bound::Included(start) => *start,
@ -75,10 +149,32 @@ impl Memory {
if success == -1 {
Err(errno::errno().to_string())
} else {
self.protection = protection;
Ok(())
}
}
pub fn split_at(mut self, offset: usize) -> (Memory, Memory) {
let page_size = page_size::get();
if offset % page_size == 0 {
let second_ptr = unsafe { self.ptr.add(offset) };
let second_size = self.size - offset;
self.size = offset;
let second = Memory {
ptr: second_ptr,
size: second_size,
protection: self.protection,
fd: self.fd.clone(),
};
(self, second)
} else {
panic!("offset must be multiple of page size: {}", offset)
}
}
pub fn size(&self) -> usize {
self.size
}
@ -91,6 +187,10 @@ impl Memory {
slice::from_raw_parts_mut(self.ptr, self.size)
}
pub fn protection(&self) -> Protect {
self.protection
}
pub fn as_ptr(&self) -> *mut u8 {
self.ptr
}
@ -105,6 +205,7 @@ impl Drop for Memory {
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum Protect {
@ -123,6 +224,41 @@ impl Protect {
Protect::ReadExec => 1 | 4,
}
}
pub fn is_readable(self) -> bool {
match self {
Protect::Read | Protect::ReadWrite | Protect::ReadExec => true,
_ => false,
}
}
pub fn is_writable(self) -> bool {
match self {
Protect::ReadWrite => true,
_ => false,
}
}
}
#[derive(Debug)]
struct RawFd(i32);
impl RawFd {
fn from_file(f: File) -> Self {
RawFd(f.into_raw_fd())
}
}
impl Drop for RawFd {
fn drop(&mut self) {
let success = unsafe { libc::close(self.0) };
assert_eq!(
success,
0,
"failed to close mmapped file descriptor: {}",
errno::errno()
);
}
}
/// Round `size` up to the nearest multiple of `page_size`.

View File

@ -14,6 +14,7 @@ unsafe impl Sync for Memory {}
pub struct Memory {
ptr: *mut u8,
size: usize,
protection: Protect,
}
impl Memory {
@ -22,6 +23,7 @@ impl Memory {
return Ok(Self {
ptr: ptr::null_mut(),
size: 0,
protection: Protect::None,
});
}
@ -35,6 +37,7 @@ impl Memory {
Ok(Self {
ptr: ptr as *mut u8,
size,
protection: Protect::None,
})
}
}
@ -71,10 +74,31 @@ impl Memory {
if ptr.is_null() {
Err("unable to protect memory".to_string())
} else {
self.protection = protection;
Ok(())
}
}
pub fn split_at(mut self, offset: usize) -> (Memory, Memory) {
let page_size = page_size::get();
if offset % page_size == 0 {
let second_ptr = unsafe { self.ptr.add(offset) };
let second_size = self.size - offset;
self.size = offset;
let second = Memory {
ptr: second_ptr,
size: second_size,
protection: self.protection,
};
(self, second)
} else {
panic!("offset must be multiple of page size: {}", offset)
}
}
pub fn size(&self) -> usize {
self.size
}
@ -87,6 +111,10 @@ impl Memory {
slice::from_raw_parts_mut(self.ptr, self.size)
}
pub fn protection(&self) -> Protect {
self.protection
}
pub fn as_ptr(&self) -> *mut u8 {
self.ptr
}
@ -101,6 +129,7 @@ impl Drop for Memory {
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum Protect {
@ -119,6 +148,20 @@ impl Protect {
Protect::ReadExec => PAGE_EXECUTE_READ,
}
}
pub fn is_readable(self) -> bool {
match self {
Protect::Read | Protect::ReadWrite | Protect::ReadExec => true,
_ => false,
}
}
pub fn is_writable(self) -> bool {
match self {
Protect::ReadWrite => true,
_ => false,
}
}
}
/// Round `size` up to the nearest multiple of `page_size`.