adding standard instruction logging utility

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2024-12-22 21:33:15 +01:00
parent 9c4b84ce33
commit 8ededb8612
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
4 changed files with 72 additions and 49 deletions

View file

@ -98,6 +98,27 @@ fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>>
writeln!(generated, " {name} = {id},")?;
}
writeln!(generated, "}}")?;
writeln!(generated, "impl {instr} {{")?;
writeln!(generated, " pub fn size(self) -> usize {{")?;
writeln!(generated, " match self {{")?;
let mut instrs = instructions().collect::<Vec<_>>();
instrs.sort_unstable_by_key(|&[.., ty, _]| iter_args(ty).map(arg_to_width).sum::<usize>());
for group in instrs.chunk_by(|[.., a, _], [.., b, _]| {
iter_args(a).map(arg_to_width).sum::<usize>()
== iter_args(b).map(arg_to_width).sum::<usize>()
}) {
let ty = group[0][2];
for &[_, name, ..] in group {
writeln!(generated, " | {instr}::{name}")?;
}
generated.pop();
let size = iter_args(ty).map(arg_to_width).sum::<usize>() + 1;
writeln!(generated, " => {size},")?;
}
writeln!(generated, " }}")?;
writeln!(generated, " }}")?;
writeln!(generated, "}}")?;
}
'_arg_kind: {

View file

@ -697,42 +697,7 @@ fn binary_prelude(to: &mut Vec<u8>) {
#[derive(Default)]
pub struct LoggedMem {
pub mem: hbvm::mem::HostMemory,
op_buf: Vec<hbbytecode::Oper>,
disp_buf: String,
prev_instr: Option<hbbytecode::Instr>,
}
impl LoggedMem {
unsafe fn display_instr<T>(&mut self, instr: hbbytecode::Instr, addr: hbvm::mem::Address) {
let novm: *const hbvm::Vm<Self, 0> = core::ptr::null();
let offset = core::ptr::addr_of!((*novm).memory) as usize;
let regs = unsafe {
&*core::ptr::addr_of!(
(*(((self as *mut _ as *mut u8).sub(offset)) as *const hbvm::Vm<Self, 0>))
.registers
)
};
let mut bytes = core::slice::from_raw_parts(
(addr.get() - 1) as *const u8,
core::mem::size_of::<T>() + 1,
);
use core::fmt::Write;
hbbytecode::parse_args(&mut bytes, instr, &mut self.op_buf).unwrap();
debug_assert!(bytes.is_empty());
self.disp_buf.clear();
write!(self.disp_buf, "{:<10}", format!("{instr:?}")).unwrap();
for (i, op) in self.op_buf.drain(..).enumerate() {
if i != 0 {
write!(self.disp_buf, ", ").unwrap();
}
write!(self.disp_buf, "{op:?}").unwrap();
if let hbbytecode::Oper::R(r) = op {
write!(self.disp_buf, "({})", regs[r as usize].0).unwrap()
}
}
log::trace!("read-typed: {:x}: {}", addr.get(), self.disp_buf);
}
logger: hbvm::mem::InstrLogger,
}
impl hbvm::mem::Memory for LoggedMem {
@ -765,20 +730,14 @@ impl hbvm::mem::Memory for LoggedMem {
}
unsafe fn prog_read<T: Copy + 'static>(&mut self, addr: hbvm::mem::Address) -> T {
if log::log_enabled!(log::Level::Trace) {
if core::any::TypeId::of::<u8>() == core::any::TypeId::of::<T>() {
if let Some(instr) = self.prev_instr {
self.display_instr::<()>(instr, addr);
}
self.prev_instr = hbbytecode::Instr::try_from(*(addr.get() as *const u8)).ok();
} else {
let instr = self.prev_instr.take().unwrap();
self.display_instr::<T>(instr, addr);
}
}
self.mem.prog_read(addr)
}
fn log_instr(&mut self, at: hbvm::mem::Address, regs: &[hbvm::value::Value]) {
log::trace!("read-typed: {:x}: {}", at.get(), unsafe {
self.logger.display_instr(at, regs)
});
}
}
struct AsHex<'a>(&'a [u8]);

View file

@ -4,7 +4,7 @@ pub mod softpaging;
pub(crate) mod addr;
use crate::utils::impl_display;
use crate::{utils::impl_display, value::Value};
pub use addr::Address;
/// Load-store memory access
@ -36,6 +36,48 @@ pub trait Memory {
/// # Safety
/// - Data read have to be valid
unsafe fn prog_read<T: Copy + 'static>(&mut self, addr: Address) -> T;
/// Log instruction to be executed
fn log_instr(&mut self, _at: Address, _regs: &[Value]) {}
}
#[derive(Default)]
pub struct InstrLogger {
#[cfg(debug_assertions)]
op_buf: alloc::vec::Vec<hbbytecode::Oper>,
#[cfg(debug_assertions)]
disp_buf: alloc::string::String,
}
impl InstrLogger {
/// # Safety
/// - `addr` needs to point to a valid instruction
#[cfg(debug_assertions)]
pub unsafe fn display_instr(&mut self, addr: Address, regs: &[Value]) -> &str {
let instr = hbbytecode::Instr::try_from(unsafe { *(addr.get() as *const u8) }).unwrap();
let mut bytes =
unsafe { core::slice::from_raw_parts(addr.get() as *const u8, instr.size()) };
use core::fmt::Write;
hbbytecode::parse_args(&mut bytes, instr, &mut self.op_buf).unwrap();
debug_assert!(bytes.is_empty());
self.disp_buf.clear();
write!(self.disp_buf, "{:<10}", alloc::format!("{instr:?}")).unwrap();
for (i, op) in self.op_buf.drain(..).enumerate() {
if i != 0 {
write!(self.disp_buf, ", ").unwrap();
}
write!(self.disp_buf, "{op:?}").unwrap();
if let hbbytecode::Oper::R(r) = op {
write!(self.disp_buf, "({})", regs[r as usize].0).unwrap()
}
}
&self.disp_buf
}
#[cfg(not(debug_assertions))]
pub unsafe fn display_instr(&mut self, addr: Address, regs: &[Value]) -> &str {
""
}
}
/// Unhandled load access trap

View file

@ -55,6 +55,7 @@ where
// - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU
// sorry 8 bit fans, HBVM won't run on your Speccy :(
unsafe {
self.memory.log_instr(self.pc, &self.registers);
match self
.memory
.prog_read::<u8>(self.pc as _)