mirror of
https://github.com/urbit/ares.git
synced 2024-11-23 09:06:23 +03:00
Merge branch 'urcrypt' into puck
This commit is contained in:
commit
0daca39b1d
@ -7,15 +7,13 @@ use crate::jets::cold::Cold;
|
||||
use crate::jets::hot::Hot;
|
||||
use crate::jets::warm::Warm;
|
||||
use crate::jets::JetErr;
|
||||
use crate::jets::form::util::scow;
|
||||
use crate::jets::bits::util::rap;
|
||||
use crate::mem::unifying_equality;
|
||||
use crate::mem::NockStack;
|
||||
use crate::mug::met3_usize;
|
||||
use crate::newt::Newt;
|
||||
use crate::noun;
|
||||
use crate::noun::{Atom, Cell, DirectAtom, IndirectAtom, Noun, Slots, D, T};
|
||||
use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T};
|
||||
use crate::serf::TERMINATOR;
|
||||
use crate::trace::{write_nock_trace, TraceInfo, TraceStack};
|
||||
use ares_macros::tas;
|
||||
use assert_no_alloc::assert_no_alloc;
|
||||
use bitvec::prelude::{BitSlice, Lsb0};
|
||||
@ -23,11 +21,7 @@ use either::Either::*;
|
||||
use std::result;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::fs::File;
|
||||
use std::time::Instant;
|
||||
use json::object;
|
||||
use std::io::Write;
|
||||
|
||||
crate::gdb!();
|
||||
|
||||
@ -274,18 +268,6 @@ pub struct Context {
|
||||
pub trace_info: Option<TraceInfo>,
|
||||
}
|
||||
|
||||
pub struct TraceInfo {
|
||||
pub file: File,
|
||||
pub pid: u32,
|
||||
pub process_start: Instant,
|
||||
}
|
||||
|
||||
pub struct TraceStack {
|
||||
pub start: Instant,
|
||||
pub path: Noun,
|
||||
pub next: *const TraceStack,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Error {
|
||||
ScryBlocked(Noun), // path
|
||||
@ -325,9 +307,9 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
|
||||
// Setup stack for Nock computation
|
||||
unsafe {
|
||||
context.stack.frame_push(2);
|
||||
|
||||
// Bottom of mean stack
|
||||
*(context.stack.local_noun_pointer(0)) = D(0);
|
||||
|
||||
// Bottom of trace stack
|
||||
*(context.stack.local_noun_pointer(1) as *mut *const TraceStack) = std::ptr::null();
|
||||
|
||||
@ -352,8 +334,9 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
|
||||
let work: NockWork = *context.stack.top();
|
||||
match work {
|
||||
NockWork::Done => {
|
||||
let stack = &mut context.stack;
|
||||
write_trace(context);
|
||||
|
||||
let stack = &mut context.stack;
|
||||
debug_assertions(stack, orig_subject);
|
||||
debug_assertions(stack, subject);
|
||||
debug_assertions(stack, res);
|
||||
@ -370,62 +353,13 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
|
||||
break Ok(res);
|
||||
}
|
||||
NockWork::Ret => {
|
||||
let stack = &mut context.stack;
|
||||
write_trace(context);
|
||||
|
||||
let stack = &mut context.stack;
|
||||
debug_assertions(stack, orig_subject);
|
||||
debug_assertions(stack, subject);
|
||||
debug_assertions(stack, res);
|
||||
|
||||
// Write fast-hinted traces to file
|
||||
if let Some(ref mut info) = &mut context.trace_info {
|
||||
let mut trace_stack = *(stack.local_noun_pointer(1) as *const *const TraceStack);
|
||||
let now = Instant::now();
|
||||
assert_no_alloc::permit_alloc(|| {
|
||||
loop {
|
||||
if trace_stack.is_null() { break; }
|
||||
|
||||
let ts = (*trace_stack).start.saturating_duration_since(info.process_start).as_micros() as f64;
|
||||
let dur = now.saturating_duration_since((*trace_stack).start).as_micros() as f64;
|
||||
|
||||
// Don't write out traces less than 33us
|
||||
// (same threshhold used in vere)
|
||||
if dur < 33.0 {
|
||||
trace_stack = (*trace_stack).next;
|
||||
continue;
|
||||
}
|
||||
|
||||
let pc = path_to_cord(stack, (*trace_stack).path);
|
||||
let pclen = met3_usize(pc);
|
||||
if let Ok(pc_str) = std::str::from_utf8(&pc.as_bytes()[0..pclen]) {
|
||||
let obj = object!{
|
||||
cat: "nock",
|
||||
name: pc_str,
|
||||
ph: "X",
|
||||
pid: info.pid,
|
||||
tid: 2,
|
||||
ts: ts,
|
||||
dur: dur,
|
||||
};
|
||||
|
||||
if let Err(e) = obj.write(&mut info.file) {
|
||||
eprintln!("Error writing trace to file: {:?}", e);
|
||||
break;
|
||||
};
|
||||
|
||||
if let Err(e) = info.file.write(",\n".as_bytes()) {
|
||||
eprintln!("Error writing trace to file: {:?}", e);
|
||||
break;
|
||||
};
|
||||
|
||||
}
|
||||
trace_stack = (*trace_stack).next;
|
||||
}
|
||||
});
|
||||
if let Err(e) = info.file.sync_data() {
|
||||
eprintln!("Error syncing trace file: {:?}", e);
|
||||
};
|
||||
}
|
||||
|
||||
stack.preserve(&mut context.cache);
|
||||
stack.preserve(&mut context.cold);
|
||||
stack.preserve(&mut context.warm);
|
||||
@ -696,17 +630,12 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
|
||||
if kale.tail {
|
||||
stack.pop::<NockWork>();
|
||||
|
||||
if let Some(_info) = &context.trace_info {
|
||||
// We could trace on 2 as well, but 2 only comes from Hoon via
|
||||
// '.*', so we can assume it's never directly used to invoke
|
||||
// jetted code.
|
||||
if context.trace_info.is_some() {
|
||||
if let Some(path) = context.cold.matches(stack, &mut res) {
|
||||
// Push onto the tracing stack
|
||||
let trace_stack = *(stack.local_noun_pointer(1) as *const *const TraceStack);
|
||||
let new_trace_entry = stack.struct_alloc(1);
|
||||
*new_trace_entry = TraceStack {
|
||||
path,
|
||||
start: Instant::now(),
|
||||
next: trace_stack,
|
||||
};
|
||||
*(stack.local_noun_pointer(1) as *mut *const TraceStack) = new_trace_entry;
|
||||
append_trace(stack, path);
|
||||
};
|
||||
};
|
||||
|
||||
@ -726,20 +655,14 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
|
||||
*stack.push() = NockWork::Ret;
|
||||
push_formula(stack, formula, true)?;
|
||||
|
||||
if let Some(_info) = &context.trace_info {
|
||||
// We could trace on 2 as well, but 2 only comes from Hoon via
|
||||
// '.*', so we can assume it's never directly used to invoke
|
||||
// jetted code.
|
||||
if context.trace_info.is_some() {
|
||||
if let Some(path) = context.cold.matches(stack, &mut res) {
|
||||
// Push onto the tracing stack
|
||||
let trace_stack = *(stack.local_noun_pointer(1) as *const *const TraceStack);
|
||||
let new_trace_entry = stack.struct_alloc(1);
|
||||
*new_trace_entry = TraceStack {
|
||||
path,
|
||||
start: Instant::now(),
|
||||
next: trace_stack,
|
||||
};
|
||||
*(stack.local_noun_pointer(1) as *mut *const TraceStack) = new_trace_entry;
|
||||
append_trace(stack, path);
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
} else {
|
||||
// Axis into core must be atom
|
||||
@ -1172,7 +1095,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> result::Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exit(context: &mut Context, virtual_frame: *const u64, error: Error) -> Error {
|
||||
fn exit(context: &mut Context, virtual_frame: *const u64, error: Error) -> Error {
|
||||
unsafe {
|
||||
let stack = &mut context.stack;
|
||||
|
||||
@ -1326,81 +1249,30 @@ pub fn assert_all_normalized(res: Noun, path: Noun, depth: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_cord(stack: &mut NockStack, path: Noun) -> Atom {
|
||||
let mut cursor = path;
|
||||
let mut length = 0usize;
|
||||
|
||||
// count how much size we need
|
||||
loop {
|
||||
if let Ok(c) = cursor.as_cell() {
|
||||
unsafe {
|
||||
match c.head().as_either_atom_cell() {
|
||||
Left(a) => {
|
||||
length += 1;
|
||||
length += met3_usize(a);
|
||||
},
|
||||
Right(ch) => {
|
||||
if let Ok(nm) = ch.head().as_atom() {
|
||||
if let Ok(kv) = ch.tail().as_atom() {
|
||||
let kvt = scow(stack, DirectAtom::new_unchecked(tas!(b"ud")), kv).expect("scow should succeed in path_to_cord");
|
||||
let kvc = rap(stack, 3, kvt).expect("rap should succeed in path_to_cord");
|
||||
length += 1;
|
||||
length += met3_usize(nm);
|
||||
length += met3_usize(kvc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor = c.tail();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// reset cursor, then actually write the path
|
||||
cursor = path;
|
||||
let mut idx = 0;
|
||||
let (mut deres, buffer) = unsafe { IndirectAtom::new_raw_mut_bytes(stack, length) };
|
||||
let slash = (b"/")[0];
|
||||
|
||||
loop {
|
||||
if let Ok(c) = cursor.as_cell() {
|
||||
unsafe {
|
||||
match c.head().as_either_atom_cell() {
|
||||
Left(a) => {
|
||||
buffer[idx] = slash;
|
||||
idx += 1;
|
||||
let bytelen = met3_usize(a);
|
||||
buffer[idx..idx+bytelen].copy_from_slice(&a.as_bytes()[0..bytelen]);
|
||||
idx += bytelen;
|
||||
},
|
||||
Right(ch) => {
|
||||
if let Ok(nm) = ch.head().as_atom() {
|
||||
if let Ok(kv) = ch.tail().as_atom() {
|
||||
let kvt = scow(stack, DirectAtom::new_unchecked(tas!(b"ud")), kv).expect("scow should succeed in path_to_cord");
|
||||
let kvc = rap(stack, 3, kvt).expect("rap should succeed in path_to_cord");
|
||||
buffer[idx] = slash;
|
||||
idx += 1;
|
||||
let nmlen = met3_usize(nm);
|
||||
buffer[idx..idx+nmlen].copy_from_slice(&nm.as_bytes()[0..nmlen]);
|
||||
idx += nmlen;
|
||||
let kvclen = met3_usize(kvc);
|
||||
buffer[idx..idx+kvclen].copy_from_slice(&kvc.as_bytes()[0..kvclen]);
|
||||
idx += kvclen;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
cursor = c.tail();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/// Push onto the tracing stack
|
||||
fn append_trace(stack: &mut NockStack, path: Noun) {
|
||||
unsafe {
|
||||
deres.normalize_as_atom()
|
||||
let trace_stack = *(stack.local_noun_pointer(1) as *const *const TraceStack);
|
||||
let new_trace_entry = stack.struct_alloc(1);
|
||||
*new_trace_entry = TraceStack {
|
||||
path,
|
||||
start: Instant::now(),
|
||||
next: trace_stack,
|
||||
};
|
||||
*(stack.local_noun_pointer(1) as *mut *const TraceStack) = new_trace_entry;
|
||||
}
|
||||
}
|
||||
|
||||
/// Write fast-hinted traces to trace file
|
||||
unsafe fn write_trace(context: &mut Context) {
|
||||
if let Some(ref mut info) = &mut context.trace_info {
|
||||
let trace_stack = *(context.stack.local_noun_pointer(1) as *mut *const TraceStack);
|
||||
// Abort writing to trace file if we encountered an error. This should
|
||||
// result in a well-formed partial trace file.
|
||||
if let Err(e) = write_nock_trace(&mut context.stack, info, trace_stack) {
|
||||
eprintln!("\rserf: error writing nock trace to file: {:?}", e);
|
||||
context.trace_info = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1712,4 +1584,3 @@ mod hint {
|
||||
newt.slog(stack, 0u64, tank);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,15 +53,15 @@ pub enum JetErr {
|
||||
Fail(Error), // Error; do not retry
|
||||
}
|
||||
|
||||
impl From<noun::Error> for JetErr {
|
||||
fn from(_err: noun::Error) -> Self {
|
||||
Self::Fail(Error::Deterministic(D(0)))
|
||||
impl From<Error> for JetErr {
|
||||
fn from(err: Error) -> Self {
|
||||
Self::Fail(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for JetErr {
|
||||
fn from(e: Error) -> Self {
|
||||
Self::Fail(e)
|
||||
impl From<noun::Error> for JetErr {
|
||||
fn from(_err: noun::Error) -> Self {
|
||||
Self::Fail(Error::Deterministic(D(0)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,6 +355,14 @@ pub mod util {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_jet_size(context: &mut Context, jet: Jet, sam: Noun, siz: usize) {
|
||||
let sam = T(&mut context.stack, &[D(0), sam, D(0)]);
|
||||
let res = assert_no_alloc(|| jet(context, sam).unwrap());
|
||||
assert!(res.is_atom(), "jet result not atom");
|
||||
let res_siz = res.atom().unwrap().size();
|
||||
assert!(siz == res_siz, "got: {}, need: {}", res_siz, siz);
|
||||
}
|
||||
|
||||
pub fn assert_common_jet(
|
||||
context: &mut Context,
|
||||
jet: Jet,
|
||||
@ -386,5 +394,16 @@ pub mod util {
|
||||
let sam = T(&mut context.stack, &sam);
|
||||
assert_jet_err(context, jet, sam, err);
|
||||
}
|
||||
|
||||
pub fn assert_common_jet_size(
|
||||
context: &mut Context,
|
||||
jet: Jet,
|
||||
sam: &[fn(&mut NockStack) -> Noun],
|
||||
siz: usize,
|
||||
) {
|
||||
let sam: Vec<Noun> = sam.iter().map(|f| f(&mut context.stack)).collect();
|
||||
let sam = T(&mut context.stack, &sam);
|
||||
assert_jet_size(context, jet, sam, siz)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ pub fn jet_lsh(context: &mut Context, subject: Noun) -> Result {
|
||||
let (bloq, step) = bite(slot(arg, 2)?)?;
|
||||
let a = slot(arg, 3)?.as_atom()?;
|
||||
|
||||
util::lsh(&mut context.stack, bloq, step, a).map(|a| a.as_noun())
|
||||
util::lsh(&mut context.stack, bloq, step, a)
|
||||
}
|
||||
|
||||
pub fn jet_met(_context: &mut Context, subject: Noun) -> Result {
|
||||
@ -148,7 +148,6 @@ pub fn jet_rap(context: &mut Context, subject: Noun) -> Result {
|
||||
Ok(util::rap(&mut context.stack, bloq, original_list)?.as_noun())
|
||||
}
|
||||
|
||||
|
||||
pub fn jet_rep(context: &mut Context, subject: Noun) -> Result {
|
||||
let arg = slot(subject, 6)?;
|
||||
let (bloq, step) = bite(slot(arg, 2)?)?;
|
||||
@ -306,11 +305,10 @@ pub fn jet_mix(context: &mut Context, subject: Noun) -> Result {
|
||||
}
|
||||
|
||||
pub mod util {
|
||||
use crate::jets;
|
||||
use crate::jets::util::*;
|
||||
use crate::jets::JetErr;
|
||||
use crate::jets::{JetErr, Result};
|
||||
use crate::mem::NockStack;
|
||||
use crate::noun::{Atom, Cell, DirectAtom, IndirectAtom, D, Noun};
|
||||
use crate::noun::{Atom, Cell, DirectAtom, IndirectAtom, Noun, D};
|
||||
use std::cmp;
|
||||
use std::result;
|
||||
|
||||
@ -327,22 +325,17 @@ pub mod util {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lsh(
|
||||
stack: &mut NockStack,
|
||||
bloq: usize,
|
||||
step: usize,
|
||||
a: Atom,
|
||||
) -> result::Result<Atom, JetErr> {
|
||||
pub fn lsh(stack: &mut NockStack, bloq: usize, step: usize, a: Atom) -> Result {
|
||||
let len = met(bloq, a);
|
||||
if len == 0 {
|
||||
return Ok(D(0).as_atom()?);
|
||||
return Ok(D(0));
|
||||
}
|
||||
|
||||
let new_size = bits_to_word(checked_add(a.bit_size(), checked_left_shift(bloq, step)?)?)?;
|
||||
unsafe {
|
||||
let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, new_size);
|
||||
chop(bloq, 0, len, step, dest, a.as_bitslice())?;
|
||||
Ok(atom.normalize_as_atom())
|
||||
Ok(atom.normalize_as_atom().as_noun())
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,7 +351,7 @@ pub mod util {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rip(stack: &mut NockStack, bloq: usize, step: usize, atom: Atom) -> jets::Result {
|
||||
pub fn rip(stack: &mut NockStack, bloq: usize, step: usize, atom: Atom) -> Result {
|
||||
let len = (met(bloq, atom) + step - 1) / step;
|
||||
let mut list = D(0);
|
||||
for i in (0..len).rev() {
|
||||
@ -387,20 +380,24 @@ pub mod util {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rap(stack: &mut NockStack, bloq: usize, original_list: Noun) -> Result<Atom, JetErr> {
|
||||
pub fn rap(
|
||||
stack: &mut NockStack,
|
||||
bloq: usize,
|
||||
original_list: Noun,
|
||||
) -> result::Result<Atom, JetErr> {
|
||||
let mut len = 0usize;
|
||||
let mut list = original_list;
|
||||
loop {
|
||||
if unsafe { list.raw_equals(D(0)) } {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
let cell = list.as_cell()?;
|
||||
|
||||
|
||||
len = checked_add(len, met(bloq, cell.head().as_atom()?))?;
|
||||
list = cell.tail();
|
||||
}
|
||||
|
||||
|
||||
if len == 0 {
|
||||
Ok(Atom::new(stack, 0))
|
||||
} else {
|
||||
@ -409,21 +406,21 @@ pub mod util {
|
||||
IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, len)?);
|
||||
let mut pos = 0;
|
||||
let mut list = original_list;
|
||||
|
||||
|
||||
loop {
|
||||
if list.raw_equals(D(0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
let cell = list.as_cell()?;
|
||||
let atom = cell.head().as_atom()?;
|
||||
let step = met(bloq, atom);
|
||||
chop(bloq, 0, step, pos, new_slice, atom.as_bitslice())?;
|
||||
|
||||
|
||||
pos += step;
|
||||
list = cell.tail();
|
||||
}
|
||||
|
||||
|
||||
Ok(new_indirect.normalize_as_atom())
|
||||
}
|
||||
}
|
||||
|
@ -274,8 +274,8 @@ struct ColdMem {
|
||||
/// value: possible registered paths for core
|
||||
///
|
||||
/// Identical nock can exist in multiple places, so the outermost battery
|
||||
/// yield multiple paths. Instead of matching on the entire core in the Hamt
|
||||
/// (which would require iterating through every possible pait), we match
|
||||
/// yields multiple paths. Instead of matching on the entire core in the Hamt
|
||||
/// (which would require iterating through every possible pair), we match
|
||||
/// the outermost battery to a path, then compare the core to the registered
|
||||
/// cores for that path.
|
||||
battery_to_paths: Hamt<NounList>,
|
||||
@ -348,15 +348,16 @@ impl Cold {
|
||||
unsafe {
|
||||
let paths = (*(self.0)).battery_to_paths.lookup(stack, &mut battery)?;
|
||||
for path in paths {
|
||||
if let Some(batteries_list) = (*(self.0)).path_to_batteries.lookup(stack, &mut (*path)) {
|
||||
|
||||
if let Some(batteries_list) =
|
||||
(*(self.0)).path_to_batteries.lookup(stack, &mut (*path))
|
||||
{
|
||||
if let Some(_batt) = batteries_list.matches(stack, *core) {
|
||||
return Some(*path);
|
||||
}
|
||||
}
|
||||
};
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
/// register a core, return a boolean of whether we actually needed to register (false ->
|
||||
|
@ -9,12 +9,12 @@ crate::gdb!();
|
||||
|
||||
pub fn jet_flop(context: &mut Context, subject: Noun) -> Result {
|
||||
let sam = slot(subject, 6)?;
|
||||
Ok(util::flop(&mut context.stack, sam)?)
|
||||
util::flop(&mut context.stack, sam)
|
||||
}
|
||||
|
||||
pub fn jet_lent(_context: &mut Context, subject: Noun) -> Result {
|
||||
let tape = slot(subject, 6)?;
|
||||
util::lent(tape).map(|x| D(x as u64))
|
||||
let list = slot(subject, 6)?;
|
||||
util::lent(list).map(|x| D(x as u64))
|
||||
}
|
||||
|
||||
pub fn jet_zing(context: &mut Context, subject: Noun) -> Result {
|
||||
@ -26,14 +26,13 @@ pub fn jet_zing(context: &mut Context, subject: Noun) -> Result {
|
||||
|
||||
pub mod util {
|
||||
use crate::interpreter::Error;
|
||||
use crate::jets;
|
||||
use crate::jets::JetErr;
|
||||
use crate::jets::{JetErr, Result};
|
||||
use crate::mem::NockStack;
|
||||
use crate::noun::{Cell, Noun, D, T};
|
||||
use std::result::Result;
|
||||
use std::result;
|
||||
|
||||
/// Reverse order of list
|
||||
pub fn flop(stack: &mut NockStack, noun: Noun) -> Result<Noun, Error> {
|
||||
pub fn flop(stack: &mut NockStack, noun: Noun) -> Result {
|
||||
let mut list = noun;
|
||||
let mut tsil = D(0);
|
||||
loop {
|
||||
@ -49,7 +48,7 @@ pub mod util {
|
||||
Ok(tsil)
|
||||
}
|
||||
|
||||
pub fn lent(tape: Noun) -> Result<usize, JetErr> {
|
||||
pub fn lent(tape: Noun) -> result::Result<usize, JetErr> {
|
||||
let mut len = 0usize;
|
||||
let mut list = tape;
|
||||
loop {
|
||||
@ -68,50 +67,29 @@ pub mod util {
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn zing(stack: &mut NockStack, mut list: Noun) -> jets::Result {
|
||||
pub fn zing(stack: &mut NockStack, mut list: Noun) -> Result {
|
||||
unsafe {
|
||||
let (mut new_cell, mut new_memory) = Cell::new_raw_mut(stack);
|
||||
#[allow(unused_assignments)]
|
||||
let (mut cell, mut memory) = (new_cell, new_memory);
|
||||
let mut res: Noun = D(0);
|
||||
let mut flag = false;
|
||||
|
||||
loop {
|
||||
if list.raw_equals(D(0)) {
|
||||
break;
|
||||
}
|
||||
let mut dest = &mut res as *mut Noun;
|
||||
|
||||
while !list.raw_equals(D(0)) {
|
||||
let pair = list.as_cell()?;
|
||||
let mut sub_list = pair.head();
|
||||
|
||||
loop {
|
||||
if sub_list.raw_equals(D(0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
let elem = sub_list.as_cell()?;
|
||||
let head = elem.head();
|
||||
|
||||
if flag {
|
||||
(new_cell, new_memory) = Cell::new_raw_mut(stack);
|
||||
(*memory).tail = new_cell.as_noun();
|
||||
memory = new_memory;
|
||||
} else {
|
||||
(cell, memory) = Cell::new_raw_mut(stack);
|
||||
res = cell.as_noun();
|
||||
flag = true;
|
||||
}
|
||||
(*memory).head = head;
|
||||
|
||||
sub_list = elem.tail();
|
||||
}
|
||||
|
||||
let mut sublist = pair.head();
|
||||
list = pair.tail();
|
||||
|
||||
while !sublist.raw_equals(D(0)) {
|
||||
let it = sublist.as_cell()?;
|
||||
let i = it.head();
|
||||
sublist = it.tail();
|
||||
|
||||
let (new_cell, new_memory) = Cell::new_raw_mut(stack);
|
||||
(*new_memory).head = i;
|
||||
*dest = new_cell.as_noun();
|
||||
dest = &mut (*new_memory).tail;
|
||||
}
|
||||
}
|
||||
|
||||
if flag {
|
||||
(*memory).tail = D(0);
|
||||
}
|
||||
*dest = D(0);
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
@ -307,6 +307,7 @@ pub mod util {
|
||||
}
|
||||
}
|
||||
|
||||
/// Less than
|
||||
pub fn lth(stack: &mut NockStack, a: Atom, b: Atom) -> Noun {
|
||||
if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) {
|
||||
if a.data() < b.data() {
|
||||
|
@ -1,18 +1,17 @@
|
||||
/** Tree jets
|
||||
*/
|
||||
use crate::interpreter::{Context, Error};
|
||||
use crate::jets::bits;
|
||||
use crate::jets::math;
|
||||
use crate::jets::util::slot;
|
||||
use crate::jets::bits::util::*;
|
||||
use crate::jets::util::*;
|
||||
use crate::jets::{JetErr, Result};
|
||||
use crate::noun::{Noun, D};
|
||||
use crate::noun::{IndirectAtom, Noun, D};
|
||||
|
||||
crate::gdb!();
|
||||
|
||||
pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result {
|
||||
let arg = slot(subject, 6)?;
|
||||
let tom = arg.as_atom()?;
|
||||
let met = bits::util::met(0, tom);
|
||||
let met = met(0, tom);
|
||||
|
||||
unsafe {
|
||||
if met < 2 {
|
||||
@ -27,43 +26,53 @@ pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result {
|
||||
|
||||
pub fn jet_mas(context: &mut Context, subject: Noun) -> Result {
|
||||
let stack = &mut context.stack;
|
||||
let arg = slot(subject, 6)?;
|
||||
let tom = arg.as_atom()?;
|
||||
let met = bits::util::met(0, tom);
|
||||
let tom = slot(subject, 6)?.as_atom()?;
|
||||
let met = met(0, tom);
|
||||
|
||||
if met < 2 {
|
||||
Err(JetErr::Fail(Error::Deterministic(D(0))))
|
||||
} else {
|
||||
let c = bits::util::bex(stack, met - 1);
|
||||
let d = bits::util::bex(stack, met - 2);
|
||||
let e = math::util::sub(stack, tom, c)?;
|
||||
|
||||
Ok(bits::util::con(stack, e, d).as_noun())
|
||||
let out_bits = met - 1;
|
||||
let out_words = (out_bits + 63) >> 6;
|
||||
let (mut indirect_out, out_bs) =
|
||||
unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) };
|
||||
out_bs.set(met - 2, true); // Set MSB
|
||||
if met > 2 {
|
||||
out_bs[0..(met - 2)].copy_from_bitslice(&tom.as_bitslice()[0..(met - 2)]);
|
||||
};
|
||||
unsafe { Ok(indirect_out.normalize_as_atom().as_noun()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jet_peg(context: &mut Context, subject: Noun) -> Result {
|
||||
let stack = &mut context.stack;
|
||||
let arg = slot(subject, 6)?;
|
||||
let a = slot(arg, 2)?;
|
||||
let b = slot(arg, 3)?;
|
||||
let a = slot(arg, 2)?.as_atom()?;
|
||||
let b = slot(arg, 3)?.as_atom()?;
|
||||
|
||||
unsafe {
|
||||
if a.raw_equals(D(0)) {
|
||||
if a.as_noun().raw_equals(D(0)) {
|
||||
return Err(JetErr::Fail(Error::Deterministic(D(0))));
|
||||
}
|
||||
// XX: Import jet mistmatch from Vere
|
||||
if b.raw_equals(D(0)) {
|
||||
return Err(JetErr::Fail(Error::Deterministic(D(0))));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let c = bits::util::met(0, b.as_atom()?);
|
||||
let d = c - 1;
|
||||
let e = bits::util::lsh(stack, 0, d, D(1).as_atom()?)?;
|
||||
let f = math::util::sub(stack, b.as_atom()?, e)?;
|
||||
let g = bits::util::lsh(stack, 0, d, a.as_atom()?)?;
|
||||
Ok(math::util::add(stack, f, g).as_noun())
|
||||
if b.as_noun().raw_equals(D(0)) {
|
||||
return Err(JetErr::Fail(Error::Deterministic(D(0))));
|
||||
};
|
||||
}
|
||||
|
||||
let a_bits = met(0, a);
|
||||
let b_bits = met(0, b);
|
||||
let out_bits = a_bits + b_bits - 1;
|
||||
|
||||
let out_words = (out_bits + 63) >> 6; // bits to 8-byte words
|
||||
|
||||
let (mut indirect_out, out_bs) =
|
||||
unsafe { IndirectAtom::new_raw_mut_bitslice(stack, out_words) };
|
||||
|
||||
out_bs[0..b_bits - 1].copy_from_bitslice(&b.as_bitslice()[0..b_bits - 1]);
|
||||
out_bs[b_bits - 1..out_bits].copy_from_bitslice(&a.as_bitslice()[0..a_bits]);
|
||||
|
||||
unsafe { Ok(indirect_out.normalize_as_atom().as_noun()) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -92,10 +101,18 @@ mod tests {
|
||||
D(0x4000000000000000)
|
||||
}
|
||||
|
||||
fn atom_64(stack: &mut NockStack) -> Noun {
|
||||
A(stack, &ubig!(_0x8000000000000000))
|
||||
}
|
||||
|
||||
fn atom_65(stack: &mut NockStack) -> Noun {
|
||||
A(stack, &ubig!(_0x10000000000000000))
|
||||
}
|
||||
|
||||
fn atom_66(stack: &mut NockStack) -> Noun {
|
||||
A(stack, &ubig!(_0x20000000000000000))
|
||||
}
|
||||
|
||||
fn pos_2(_stack: &mut NockStack) -> Noun {
|
||||
D(2)
|
||||
}
|
||||
@ -127,10 +144,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_mas() {
|
||||
let c = &mut init_context();
|
||||
let a63 = atom_63(&mut c.stack);
|
||||
let a64 = atom_64(&mut c.stack);
|
||||
let a65 = atom_65(&mut c.stack);
|
||||
let a66 = atom_66(&mut c.stack);
|
||||
|
||||
// Test invalid input
|
||||
assert_jet_err(c, jet_mas, D(0), JetErr::Fail(Error::Deterministic(D(0))));
|
||||
assert_jet_err(c, jet_mas, D(1), JetErr::Fail(Error::Deterministic(D(0))));
|
||||
|
||||
// Test direct
|
||||
assert_jet(c, jet_mas, D(2), D(1));
|
||||
assert_jet(c, jet_mas, D(3), D(1));
|
||||
assert_jet(c, jet_mas, D(4), D(2));
|
||||
@ -138,6 +161,16 @@ mod tests {
|
||||
assert_jet(c, jet_mas, D(6), D(2));
|
||||
assert_jet(c, jet_mas, D(7), D(3));
|
||||
assert_jet(c, jet_mas, D(8), D(4));
|
||||
|
||||
// Test indirect
|
||||
assert_jet(c, jet_mas, a64, a63);
|
||||
assert_jet(c, jet_mas, a65, a64);
|
||||
assert_jet(c, jet_mas, a66, a65);
|
||||
|
||||
// Test allocation
|
||||
assert_jet_size(c, jet_mas, D(2), 1_usize);
|
||||
assert_jet_size(c, jet_mas, a65, 1_usize);
|
||||
assert_jet_size(c, jet_mas, a66, 2_usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -174,5 +207,10 @@ mod tests {
|
||||
&[atom_65, atom_65],
|
||||
ubig!(_0x100000000000000000000000000000000),
|
||||
);
|
||||
|
||||
// Test allocation
|
||||
assert_common_jet_size(c, jet_peg, &[atom_65, atom_1], 2_usize);
|
||||
assert_common_jet_size(c, jet_peg, &[atom_1, atom_65], 2_usize);
|
||||
assert_common_jet_size(c, jet_peg, &[atom_65, atom_65], 3_usize);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ extern crate num_derive;
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
pub mod hamt;
|
||||
pub mod interpreter;
|
||||
pub mod jets;
|
||||
pub mod mem;
|
||||
@ -11,9 +12,9 @@ pub mod newt;
|
||||
pub mod noun;
|
||||
pub mod serf;
|
||||
//pub mod bytecode;
|
||||
pub mod hamt;
|
||||
pub mod serialization;
|
||||
pub mod snapshot;
|
||||
pub mod trace;
|
||||
|
||||
/** Introduce useful functions for debugging
|
||||
*
|
||||
|
@ -104,4 +104,3 @@ fn main() -> io::Result<()> {
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ const FORWARDING_MASK: u64 = CELL_MASK;
|
||||
/** Loobeans */
|
||||
pub const YES: Noun = D(0);
|
||||
pub const NO: Noun = D(1);
|
||||
pub const NONE: Noun = unsafe { DirectAtom::new_unchecked(tas!(b"mormagic")).as_noun() };
|
||||
pub const NONE: Noun = unsafe { DirectAtom::new_unchecked(tas!(b"MORMAGIC")).as_noun() };
|
||||
|
||||
#[cfg(feature = "check_acyclic")]
|
||||
#[macro_export]
|
||||
@ -464,6 +464,25 @@ impl IndirectAtom {
|
||||
UBig::from_le_bytes_stack(stack, self.as_bytes())
|
||||
}
|
||||
|
||||
pub unsafe fn as_u64(self) -> Result<u64> {
|
||||
if self.size() == 1 {
|
||||
Ok(*(self.data_pointer()))
|
||||
} else {
|
||||
Err(Error::NotRepresentable)
|
||||
}
|
||||
}
|
||||
|
||||
/** Produce a SoftFloat-compatible ordered pair of 64-bit words */
|
||||
pub fn as_u64_pair(self) -> Result<[u64; 2]> {
|
||||
if self.size() <= 2 {
|
||||
let u128_array = &mut [0u64; 2];
|
||||
u128_array.copy_from_slice(&(self.as_slice()[0..2]));
|
||||
Ok(*u128_array)
|
||||
} else {
|
||||
Err(Error::NotRepresentable)
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure that the size does not contain any trailing 0 words */
|
||||
pub unsafe fn normalize(&mut self) -> &Self {
|
||||
let mut index = self.size() - 1;
|
||||
@ -738,6 +757,26 @@ impl Atom {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_u64(self) -> Result<u64> {
|
||||
if self.is_direct() {
|
||||
Ok(unsafe { self.direct.data() })
|
||||
} else {
|
||||
unsafe { self.indirect.as_u64() }
|
||||
}
|
||||
}
|
||||
|
||||
/** Produce a SoftFloat-compatible ordered pair of 64-bit words */
|
||||
pub unsafe fn as_u64_pair(self) -> Result<[u64; 2]> {
|
||||
if self.is_direct() {
|
||||
let u128_array = &mut [0u64; 2];
|
||||
u128_array[0] = self.as_direct()?.data();
|
||||
u128_array[1] = 0x0_u64;
|
||||
Ok(*u128_array)
|
||||
} else {
|
||||
unsafe { self.indirect.as_u64_pair() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bitslice(&self) -> &BitSlice<u64, Lsb0> {
|
||||
if self.is_indirect() {
|
||||
unsafe { self.indirect.as_bitslice() }
|
||||
|
@ -1,30 +1,28 @@
|
||||
use crate::hamt::Hamt;
|
||||
use crate::interpreter;
|
||||
use crate::interpreter::{inc, interpret, Error, TraceInfo};
|
||||
use crate::interpreter::{inc, interpret, Error};
|
||||
use crate::jets::cold::Cold;
|
||||
use crate::jets::hot::Hot;
|
||||
use crate::jets::list::util::{lent, zing};
|
||||
use crate::jets::nock::util::mook;
|
||||
use crate::jets::warm::Warm;
|
||||
use crate::mem::NockStack;
|
||||
use crate::mug::mug_u32;
|
||||
use crate::mug::*;
|
||||
use crate::newt::Newt;
|
||||
use crate::noun::{Cell, Noun, Slots, D, T};
|
||||
use crate::noun::{Atom, Cell, DirectAtom, Noun, Slots, D, T};
|
||||
use crate::snapshot::double_jam::DoubleJam;
|
||||
use crate::snapshot::Snapshot;
|
||||
use crate::trace::*;
|
||||
use ares_macros::tas;
|
||||
use signal_hook;
|
||||
use signal_hook::consts::SIGINT;
|
||||
use std::fs::create_dir_all;
|
||||
use std::io::{self, Write};
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::result::Result;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::time::Instant;
|
||||
use std::fs::File;
|
||||
use json::object;
|
||||
|
||||
crate::gdb!();
|
||||
|
||||
@ -195,88 +193,26 @@ pub fn serf() -> io::Result<()> {
|
||||
snap_path.push("chk");
|
||||
create_dir_all(&snap_path)?;
|
||||
|
||||
let wag: u32 = std::env::args().nth(4).ok_or(io::Error::new(io::ErrorKind::Other, "no flag bitmap"))?.parse().or(Err(io::Error::new(io::ErrorKind::Other, "flag bitmap is not integer")))?;
|
||||
let wag: u32 = std::env::args()
|
||||
.nth(4)
|
||||
.ok_or(io::Error::new(io::ErrorKind::Other, "no flag bitmap"))?
|
||||
.parse()
|
||||
.or(Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"flag bitmap is not integer",
|
||||
)))?;
|
||||
|
||||
let trace_info =
|
||||
if wag & FLAG_TRACE != 0 {
|
||||
let mut trace_dir_path = pier_path.clone();
|
||||
trace_dir_path.push(".urb");
|
||||
trace_dir_path.push("put");
|
||||
trace_dir_path.push("trace");
|
||||
create_dir_all(&trace_dir_path)?;
|
||||
|
||||
let mut trace_idx = 0u32;
|
||||
loop {
|
||||
let mut trace_path = trace_dir_path.clone();
|
||||
trace_path.push(format!("{}.json", trace_idx));
|
||||
if trace_path.exists() {
|
||||
trace_idx += 1;
|
||||
} else {
|
||||
let mut file = File::create(trace_path)?;
|
||||
let process_start = Instant::now();
|
||||
let pid = std::process::id();
|
||||
|
||||
file.write("[ ".as_bytes())?;
|
||||
|
||||
// write metadata to trace file
|
||||
|
||||
(object!{
|
||||
name: "process_name",
|
||||
ph: "M",
|
||||
pid: pid,
|
||||
args: object! { name: "urbit", },
|
||||
}).write(&mut file)?;
|
||||
file.write(",\n".as_bytes())?;
|
||||
|
||||
(object!{
|
||||
name: "thread_name",
|
||||
ph: "M",
|
||||
pid: pid,
|
||||
tid: 1,
|
||||
args: object!{ name: "Event Processing", },
|
||||
}).write(&mut file)?;
|
||||
file.write(",\n".as_bytes())?;
|
||||
|
||||
(object!{
|
||||
name: "thread_sort_index",
|
||||
ph: "M",
|
||||
pid: pid,
|
||||
tid: 1,
|
||||
args: object!{ sort_index: 1, },
|
||||
}).write(&mut file)?;
|
||||
file.write(",\n".as_bytes())?;
|
||||
|
||||
(object!{
|
||||
name: "thread_name",
|
||||
ph: "M",
|
||||
pid: pid,
|
||||
tid: 2,
|
||||
args: object!{ name: "Nock", },
|
||||
}).write(&mut file)?;
|
||||
file.write(",\n".as_bytes())?;
|
||||
|
||||
(object!{
|
||||
name: "thread_sort_index",
|
||||
ph: "M",
|
||||
pid: pid,
|
||||
tid: 2,
|
||||
args: object!{ sort_index: 2, },
|
||||
}).write(&mut file)?;
|
||||
file.write(",\n".as_bytes())?;
|
||||
|
||||
file.sync_data()?;
|
||||
|
||||
break Some(TraceInfo {
|
||||
file,
|
||||
pid,
|
||||
process_start,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut trace_info = if wag & FLAG_TRACE != 0 {
|
||||
create_trace_file(pier_path).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(ref mut info) = trace_info.as_mut() {
|
||||
if let Err(e) = write_metadata(info) {
|
||||
eprintln!("\rError initializing trace file: {:?}", e);
|
||||
trace_info = None;
|
||||
}
|
||||
}
|
||||
|
||||
let mut context = Context::new(&snap_path, trace_info);
|
||||
context.ripe();
|
||||
@ -307,9 +243,8 @@ pub fn serf() -> io::Result<()> {
|
||||
context.live();
|
||||
}
|
||||
tas!(b"peek") => {
|
||||
let sam = slot(writ, 7)?;
|
||||
let res =
|
||||
slam(&mut context, PEEK_AXIS, sam).expect("peek error handling unimplemented");
|
||||
let ovo = slot(writ, 7)?;
|
||||
let res = peek(&mut context, ovo);
|
||||
context.peek_done(res);
|
||||
}
|
||||
tas!(b"play") => {
|
||||
@ -354,6 +289,20 @@ fn slam(context: &mut Context, axis: u64, ovo: Noun) -> Result<Noun, Error> {
|
||||
interpret(&mut context.nock_context, sub, fol)
|
||||
}
|
||||
|
||||
fn peek(context: &mut Context, ovo: Noun) -> Noun {
|
||||
if context.nock_context.trace_info.is_some() {
|
||||
// XX: way too many cases in the input to pull the actual vane, care, and path out
|
||||
let trace_name = "peek";
|
||||
let start = Instant::now();
|
||||
let slam_res = slam(context, PEEK_AXIS, ovo);
|
||||
write_serf_trace_safe(&mut context.nock_context.trace_info, trace_name, start);
|
||||
|
||||
slam_res.expect("peek error handling unimplemented")
|
||||
} else {
|
||||
slam(context, PEEK_AXIS, ovo).expect("peek error handling unimplemented")
|
||||
}
|
||||
}
|
||||
|
||||
fn goof(context: &mut Context, traces: Noun) -> Noun {
|
||||
let trace = zing(&mut context.nock_context.stack, traces).unwrap();
|
||||
let tone = Cell::new(&mut context.nock_context.stack, D(2), trace);
|
||||
@ -366,9 +315,25 @@ fn goof(context: &mut Context, traces: Noun) -> Noun {
|
||||
T(&mut context.nock_context.stack, &[D(tas!(b"exit")), tang])
|
||||
}
|
||||
|
||||
/** Run slam, process stack trace to tang if error */
|
||||
fn soft(context: &mut Context, ovo: Noun) -> Result<Noun, Noun> {
|
||||
match slam(context, POKE_AXIS, ovo) {
|
||||
/** Run slam; process stack trace to tang if error.
|
||||
* Generate tracing events, if JSON tracing enabled.
|
||||
*/
|
||||
fn soft(context: &mut Context, ovo: Noun, trace_name: Option<String>) -> Result<Noun, Noun> {
|
||||
let slam_res = if context.nock_context.trace_info.is_some() {
|
||||
let start = Instant::now();
|
||||
let slam_res = slam(context, POKE_AXIS, ovo);
|
||||
write_serf_trace_safe(
|
||||
&mut context.nock_context.trace_info,
|
||||
trace_name.as_ref().unwrap(),
|
||||
start,
|
||||
);
|
||||
|
||||
slam_res
|
||||
} else {
|
||||
slam(context, POKE_AXIS, ovo)
|
||||
};
|
||||
|
||||
match slam_res {
|
||||
Ok(res) => Ok(res),
|
||||
Err(error) => match error {
|
||||
Error::Deterministic(trace) | Error::NonDeterministic(trace) => {
|
||||
@ -385,7 +350,18 @@ fn play_life(context: &mut Context, eve: Noun) {
|
||||
let stack = &mut context.nock_context.stack;
|
||||
let sub = T(stack, &[D(0), D(3)]);
|
||||
let lyf = T(stack, &[D(2), sub, D(0), D(2)]);
|
||||
match interpret(&mut context.nock_context, eve, lyf) {
|
||||
let res = if context.nock_context.trace_info.is_some() {
|
||||
let trace_name = "boot";
|
||||
let start = Instant::now();
|
||||
let boot_res = interpret(&mut context.nock_context, eve, lyf);
|
||||
write_serf_trace_safe(&mut context.nock_context.trace_info, trace_name, start);
|
||||
|
||||
boot_res
|
||||
} else {
|
||||
interpret(&mut context.nock_context, eve, lyf)
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(gat) => {
|
||||
let eved = lent(eve).expect("serf: play: boot event number failure") as u64;
|
||||
let arvo = slot(gat, 7).expect("serf: play: lifecycle didn't return initial Arvo");
|
||||
@ -399,7 +375,7 @@ fn play_life(context: &mut Context, eve: Noun) {
|
||||
context.play_bail(goof);
|
||||
}
|
||||
Error::ScryBlocked(_) | Error::ScryCrashed(_) => {
|
||||
panic!("serf: soft: .^ invalid outside of virtual Nock")
|
||||
panic!("serf: play: .^ invalid outside of virtual Nock")
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -409,7 +385,13 @@ fn play_list(context: &mut Context, mut lit: Noun) {
|
||||
let mut eve = context.event_num;
|
||||
while let Ok(cell) = lit.as_cell() {
|
||||
let ovo = cell.head();
|
||||
match soft(context, ovo) {
|
||||
let trace_name = if context.nock_context.trace_info.is_some() {
|
||||
Some(format!("play [{}]", eve))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match soft(context, ovo, trace_name) {
|
||||
Ok(res) => {
|
||||
let arvo = res
|
||||
.as_cell()
|
||||
@ -429,7 +411,21 @@ fn play_list(context: &mut Context, mut lit: Noun) {
|
||||
}
|
||||
|
||||
fn work(context: &mut Context, job: Noun) {
|
||||
match soft(context, job) {
|
||||
let trace_name = if context.nock_context.trace_info.is_some() {
|
||||
// XX: good luck making this safe AND rust idiomatic!
|
||||
let wire = job.slot(6).expect("serf: work: job missing wire");
|
||||
let vent = job
|
||||
.slot(14)
|
||||
.expect("serf: work: job missing event tag")
|
||||
.as_atom()
|
||||
.expect("serf: work: event tag not atom");
|
||||
|
||||
Some(work_trace_name(&mut context.nock_context.stack, wire, vent))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match soft(context, job, trace_name) {
|
||||
Ok(res) => {
|
||||
let cell = res.as_cell().expect("serf: work: +slam returned atom");
|
||||
let fec = cell.head();
|
||||
@ -452,21 +448,31 @@ fn work_swap(context: &mut Context, job: Noun, goof: Noun) {
|
||||
clear_interrupt();
|
||||
|
||||
let stack = &mut context.nock_context.stack;
|
||||
// crud = [+(now) [%$ %arvo ~] [%crud goof ovo]]
|
||||
// crud ovo = [+(now) [%$ %arvo ~] [%crud goof ovo]]
|
||||
let job_cell = job.as_cell().expect("serf: work: job not a cell");
|
||||
let job_now = job_cell.head().as_atom().expect("serf: work: now not atom");
|
||||
let now = inc(stack, job_now).as_noun();
|
||||
let wire = T(stack, &[D(0), D(tas!(b"arvo")), D(0)]);
|
||||
let crud = T(stack, &[now, wire, D(tas!(b"crud")), goof, job_cell.tail()]);
|
||||
let crud = DirectAtom::new_panic(tas!(b"crud"));
|
||||
let ovo = T(stack, &[now, wire, crud.as_noun(), goof, job_cell.tail()]);
|
||||
let trace_name = if context.nock_context.trace_info.is_some() {
|
||||
Some(work_trace_name(
|
||||
&mut context.nock_context.stack,
|
||||
wire,
|
||||
crud.as_atom(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match soft(context, crud) {
|
||||
match soft(context, ovo, trace_name) {
|
||||
Ok(res) => {
|
||||
let cell = res.as_cell().expect("serf: work: crud +slam returned atom");
|
||||
let fec = cell.head();
|
||||
let eve = context.event_num;
|
||||
|
||||
context.event_update(eve + 1, cell.tail());
|
||||
context.work_swap(crud, fec);
|
||||
context.work_swap(ovo, fec);
|
||||
}
|
||||
Err(goof_crud) => {
|
||||
work_bail(context, &[goof_crud, goof]);
|
||||
@ -481,6 +487,31 @@ fn work_bail(context: &mut Context, goofs: &[Noun]) {
|
||||
context.work_bail(lud);
|
||||
}
|
||||
|
||||
fn work_trace_name(stack: &mut NockStack, wire: Noun, vent: Atom) -> String {
|
||||
let wpc = path_to_cord(stack, wire);
|
||||
let wpc_len = met3_usize(wpc);
|
||||
let wpc_bytes = &wpc.as_bytes()[0..wpc_len];
|
||||
let wpc_str = match std::str::from_utf8(wpc_bytes) {
|
||||
Ok(valid) => valid,
|
||||
Err(error) => {
|
||||
let (valid, _) = wpc_bytes.split_at(error.valid_up_to());
|
||||
unsafe { std::str::from_utf8_unchecked(valid) }
|
||||
}
|
||||
};
|
||||
|
||||
let vc_len = met3_usize(vent);
|
||||
let vc_bytes = &vent.as_bytes()[0..vc_len];
|
||||
let vc_str = match std::str::from_utf8(vc_bytes) {
|
||||
Ok(valid) => valid,
|
||||
Err(error) => {
|
||||
let (valid, _) = vc_bytes.split_at(error.valid_up_to());
|
||||
unsafe { std::str::from_utf8_unchecked(valid) }
|
||||
}
|
||||
};
|
||||
|
||||
format!("work [{} {}]", wpc_str, vc_str)
|
||||
}
|
||||
|
||||
fn slot(noun: Noun, axis: u64) -> io::Result<Noun> {
|
||||
noun.slot(axis)
|
||||
.map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis"))
|
||||
|
257
rust/ares/src/trace.rs
Normal file
257
rust/ares/src/trace.rs
Normal file
@ -0,0 +1,257 @@
|
||||
use crate::jets::bits::util::rap;
|
||||
use crate::jets::form::util::scow;
|
||||
use crate::mem::NockStack;
|
||||
use crate::mug::met3_usize;
|
||||
use crate::noun::{Atom, DirectAtom, IndirectAtom, Noun};
|
||||
use ares_macros::tas;
|
||||
use either::Either::*;
|
||||
use json::object;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::{Error, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::result::Result;
|
||||
use std::time::Instant;
|
||||
|
||||
crate::gdb!();
|
||||
|
||||
pub struct TraceInfo {
|
||||
pub file: File,
|
||||
pub pid: u32,
|
||||
pub process_start: Instant,
|
||||
}
|
||||
|
||||
pub struct TraceStack {
|
||||
pub start: Instant,
|
||||
pub path: Noun,
|
||||
pub next: *const TraceStack,
|
||||
}
|
||||
|
||||
pub fn create_trace_file(pier_path: PathBuf) -> Result<TraceInfo, Error> {
|
||||
let mut trace_dir_path = pier_path.clone();
|
||||
trace_dir_path.push(".urb");
|
||||
trace_dir_path.push("put");
|
||||
trace_dir_path.push("trace");
|
||||
create_dir_all(&trace_dir_path)?;
|
||||
|
||||
let trace_path: PathBuf;
|
||||
let mut trace_idx = 0u32;
|
||||
loop {
|
||||
let mut prospective_path = trace_dir_path.clone();
|
||||
prospective_path.push(format!("{}.json", trace_idx));
|
||||
|
||||
if prospective_path.exists() {
|
||||
trace_idx += 1;
|
||||
} else {
|
||||
trace_path = prospective_path.clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let file = File::create(trace_path)?;
|
||||
let process_start = Instant::now();
|
||||
let pid = std::process::id();
|
||||
|
||||
Ok(TraceInfo {
|
||||
file,
|
||||
pid,
|
||||
process_start,
|
||||
})
|
||||
}
|
||||
|
||||
/// Write metadata to trace file
|
||||
pub fn write_metadata(info: &mut TraceInfo) -> Result<(), Error> {
|
||||
info.file.write_all("[ ".as_bytes())?;
|
||||
|
||||
(object! {
|
||||
name: "process_name",
|
||||
ph: "M",
|
||||
pid: info.pid,
|
||||
args: object! { name: "urbit", },
|
||||
})
|
||||
.write(&mut info.file)?;
|
||||
info.file.write_all(",\n".as_bytes())?;
|
||||
|
||||
(object! {
|
||||
name: "thread_name",
|
||||
ph: "M",
|
||||
pid: info.pid,
|
||||
tid: 1,
|
||||
args: object!{ name: "Event Processing", },
|
||||
})
|
||||
.write(&mut info.file)?;
|
||||
info.file.write_all(",\n".as_bytes())?;
|
||||
|
||||
(object! {
|
||||
name: "thread_sort_index",
|
||||
ph: "M",
|
||||
pid: info.pid,
|
||||
tid: 1,
|
||||
args: object!{ sort_index: 1, },
|
||||
})
|
||||
.write(&mut info.file)?;
|
||||
info.file.write_all(",\n".as_bytes())?;
|
||||
|
||||
info.file.sync_data()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Abort writing to trace file if an error is encountered.
|
||||
///
|
||||
/// This should result in a well-formed partial trace file.
|
||||
pub fn write_serf_trace_safe(info: &mut Option<TraceInfo>, name: &str, start: Instant) {
|
||||
if let Err(e) = write_serf_trace(info.as_mut().unwrap(), name, start) {
|
||||
eprintln!("\rserf: error writing event trace to file: {:?}", e);
|
||||
*info = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_serf_trace(info: &mut TraceInfo, name: &str, start: Instant) -> Result<(), Error> {
|
||||
let ts = start
|
||||
.saturating_duration_since(info.process_start)
|
||||
.as_micros() as f64;
|
||||
let dur = Instant::now().saturating_duration_since(start).as_micros() as f64;
|
||||
|
||||
assert_no_alloc::permit_alloc(|| {
|
||||
let obj = object! {
|
||||
cat: "event",
|
||||
name: name,
|
||||
ph: "X",
|
||||
pid: info.pid,
|
||||
tid: 1,
|
||||
ts: ts,
|
||||
dur: dur,
|
||||
};
|
||||
obj.write(&mut info.file)
|
||||
})?;
|
||||
info.file.write_all(",\n".as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn write_nock_trace(
|
||||
stack: &mut NockStack,
|
||||
info: &mut TraceInfo,
|
||||
mut trace_stack: *const TraceStack,
|
||||
) -> Result<(), Error> {
|
||||
let now = Instant::now();
|
||||
|
||||
while !trace_stack.is_null() {
|
||||
let ts = (*trace_stack)
|
||||
.start
|
||||
.saturating_duration_since(info.process_start)
|
||||
.as_micros() as f64;
|
||||
let dur = now
|
||||
.saturating_duration_since((*trace_stack).start)
|
||||
.as_micros() as f64;
|
||||
|
||||
// Don't write out traces less than 33us
|
||||
// (same threshhold used in vere)
|
||||
if dur < 33.0 {
|
||||
trace_stack = (*trace_stack).next;
|
||||
continue;
|
||||
}
|
||||
|
||||
let pc = path_to_cord(stack, (*trace_stack).path);
|
||||
let pc_len = met3_usize(pc);
|
||||
let pc_bytes = &pc.as_bytes()[0..pc_len];
|
||||
let pc_str = match std::str::from_utf8(pc_bytes) {
|
||||
Ok(valid) => valid,
|
||||
Err(error) => {
|
||||
let (valid, _) = pc_bytes.split_at(error.valid_up_to());
|
||||
unsafe { std::str::from_utf8_unchecked(valid) }
|
||||
}
|
||||
};
|
||||
|
||||
assert_no_alloc::permit_alloc(|| {
|
||||
let obj = object! {
|
||||
cat: "nock",
|
||||
name: pc_str,
|
||||
ph: "X",
|
||||
pid: info.pid,
|
||||
tid: 1,
|
||||
ts: ts,
|
||||
dur: dur,
|
||||
};
|
||||
obj.write(&mut info.file)
|
||||
})?;
|
||||
info.file.write_all(",\n".as_bytes())?;
|
||||
|
||||
trace_stack = (*trace_stack).next;
|
||||
}
|
||||
|
||||
info.file.sync_data()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// XX: Need Rust string interpolation helper that doesn't allocate
|
||||
pub fn path_to_cord(stack: &mut NockStack, path: Noun) -> Atom {
|
||||
let mut cursor = path;
|
||||
let mut length = 0usize;
|
||||
|
||||
// count how much size we need
|
||||
while let Ok(c) = cursor.as_cell() {
|
||||
unsafe {
|
||||
match c.head().as_either_atom_cell() {
|
||||
Left(a) => {
|
||||
length += 1;
|
||||
length += met3_usize(a);
|
||||
}
|
||||
Right(ch) => {
|
||||
if let Ok(nm) = ch.head().as_atom() {
|
||||
if let Ok(kv) = ch.tail().as_atom() {
|
||||
let kvt = scow(stack, DirectAtom::new_unchecked(tas!(b"ud")), kv)
|
||||
.expect("scow should succeed in path_to_cord");
|
||||
let kvc =
|
||||
rap(stack, 3, kvt).expect("rap should succeed in path_to_cord");
|
||||
length += 1;
|
||||
length += met3_usize(nm);
|
||||
length += met3_usize(kvc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor = c.tail();
|
||||
}
|
||||
|
||||
// reset cursor, then actually write the path
|
||||
cursor = path;
|
||||
let mut idx = 0;
|
||||
let (mut deres, buffer) = unsafe { IndirectAtom::new_raw_mut_bytes(stack, length) };
|
||||
let slash = (b"/")[0];
|
||||
|
||||
while let Ok(c) = cursor.as_cell() {
|
||||
unsafe {
|
||||
match c.head().as_either_atom_cell() {
|
||||
Left(a) => {
|
||||
buffer[idx] = slash;
|
||||
idx += 1;
|
||||
let bytelen = met3_usize(a);
|
||||
buffer[idx..idx + bytelen].copy_from_slice(&a.as_bytes()[0..bytelen]);
|
||||
idx += bytelen;
|
||||
}
|
||||
Right(ch) => {
|
||||
if let Ok(nm) = ch.head().as_atom() {
|
||||
if let Ok(kv) = ch.tail().as_atom() {
|
||||
let kvt = scow(stack, DirectAtom::new_unchecked(tas!(b"ud")), kv)
|
||||
.expect("scow should succeed in path_to_cord");
|
||||
let kvc =
|
||||
rap(stack, 3, kvt).expect("rap should succeed in path_to_cord");
|
||||
buffer[idx] = slash;
|
||||
idx += 1;
|
||||
let nmlen = met3_usize(nm);
|
||||
buffer[idx..idx + nmlen].copy_from_slice(&nm.as_bytes()[0..nmlen]);
|
||||
idx += nmlen;
|
||||
let kvclen = met3_usize(kvc);
|
||||
buffer[idx..idx + kvclen].copy_from_slice(&kvc.as_bytes()[0..kvclen]);
|
||||
idx += kvclen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor = c.tail();
|
||||
}
|
||||
|
||||
unsafe { deres.normalize_as_atom() }
|
||||
}
|
Loading…
Reference in New Issue
Block a user