Skip to content

Commit

Permalink
Merge pull request #139 from lialan/ali/repl
Browse files Browse the repository at this point in the history
Debugger REPL
  • Loading branch information
morganthomas committed Apr 6, 2024
2 parents bdb53de + b911609 commit 898d5ce
Show file tree
Hide file tree
Showing 9 changed files with 323 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ members = [
"range",
"static_data",
"util",
"verifier"
"verifier",
]

[workspace.dependencies]
Expand Down
2 changes: 2 additions & 0 deletions basic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ rand = "0.8.5"
rand_pcg = "0.3.1"
rand_seeder = "0.2.3"
tracing = "0.1.37"
reedline-repl-rs = "1.1.1"
valida-alu-u32 = { path = "../alu_u32" }
valida-assembler = { path = "../assembler" }
valida-bus = { path = "../bus" }
Expand Down Expand Up @@ -50,6 +51,7 @@ p3-merkle-tree = { workspace = true }
p3-poseidon = { workspace = true }
p3-symmetric = { workspace = true }


[dev-dependencies]
ciborium = "0.2.2"
p3-challenger = { workspace = true }
Expand Down
235 changes: 234 additions & 1 deletion basic/src/bin/valida.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use p3_baby_bear::BabyBear;
use p3_fri::{FriConfig, TwoAdicFriPcs, TwoAdicFriPcsConfig};
use valida_cpu::MachineWithCpuChip;
use valida_machine::{Machine, MachineProof, ProgramROM, StdinAdviceProvider};
use valida_memory::MachineWithMemoryChip;

use valida_program::MachineWithProgramChip;

Expand All @@ -27,9 +28,12 @@ use valida_machine::StarkConfigImpl;
use valida_machine::__internal::p3_commit::ExtensionMmcs;
use valida_output::MachineWithOutputChip;

use reedline_repl_rs::clap::{Arg, ArgMatches, Command};
use reedline_repl_rs::{Repl, Result};

#[derive(Parser)]
struct Args {
/// Command option either "run" or "prove" or "verify"
/// Command option either "run" or "prove" or "verify" or "interactive"
#[arg(name = "Action Option")]
action: String,

Expand All @@ -46,9 +50,238 @@ struct Args {
stack_height: u32,
}

struct Context<'a> {
machine_: BasicMachine<BabyBear>,
args_: &'a Args,
breakpoints_: Vec<u32>,
stopped_: bool,
last_fp_: u32,
recorded_current_fp_: u32,
last_fp_size_: u32,
}

impl Context<'_> {
fn new(args: &Args) -> Context {
let mut context = Context {
machine_: BasicMachine::<BabyBear>::default(),
args_: args.clone(),
breakpoints_: Vec::new(),
stopped_: false,
last_fp_: args.stack_height,
recorded_current_fp_: args.stack_height,
last_fp_size_: 0,
};

let rom = match ProgramROM::from_file(&args.program) {
Ok(contents) => contents,
Err(e) => panic!("Failure to load file: {}. {}", &args.program, e),
};

context.machine_.program_mut().set_program_rom(&rom);
context.machine_.cpu_mut().fp = args.stack_height;
context.machine_.cpu_mut().save_register_state();

context
}

fn step(&mut self) -> (bool, u32) {
// do not execute if already stopped
if self.stopped_ {
return (true, 0);
}
let state = self.machine_.step(&mut StdinAdviceProvider);
let pc = self.machine_.cpu().pc;
let fp = self.machine_.cpu().fp;

// check if fp is changed
if fp != self.recorded_current_fp_ {
self.last_fp_size_ = self.recorded_current_fp_ - fp;
self.last_fp_ = self.recorded_current_fp_;
} else if fp == self.last_fp_ {
self.last_fp_size_ = 0;
}
self.recorded_current_fp_ = fp;

(state, pc)
}
}

fn init_context(args: ArgMatches, context: &mut Context) -> Result<Option<String>> {
Ok(Some(String::from("created machine")))
}

fn status(args: ArgMatches, context: &mut Context) -> Result<Option<String>> {
// construct machine status
let mut status = String::new();
status.push_str("FP: ");
status.push_str(&context.machine_.cpu().fp.to_string());
status.push_str(", PC: ");
status.push_str(&context.machine_.cpu().pc.to_string());
status.push_str(if context.stopped_ {
", Stopped"
} else {
", Running"
});
Ok(Some(status))
}

fn show_frame(args: ArgMatches, context: &mut Context) -> Result<Option<String>> {
let size: i32 = match args.contains_id("size") {
true => args
.get_one::<String>("size")
.unwrap()
.parse::<i32>()
.unwrap(),
false => 6,
};
let mut frame = String::new();
let fp = context.machine_.cpu().fp as i32;
frame.push_str(format!("FP: {:x}\n", fp).as_str());
for i in 0..size {
let offset = i * -4;
let read_addr = (fp + offset) as u32;
let string_val = context.machine_.mem().examine(read_addr);
let frameslot_addr = format!("{}(fp)", offset);
let frameslot = format!("{:>7}", frameslot_addr);
let frame_str = format!("\n{} : {}", frameslot, string_val);
frame += &frame_str;
}

Ok(Some(frame))
}

fn last_frame(_: ArgMatches, context: &mut Context) -> Result<Option<String>> {
let mut frame = String::new();

let lfp = context.last_fp_;
let fp = context.machine_.cpu().fp as i32;
let last_size = context.last_fp_size_ as i32;
frame += &format!("Last FP : 0x{:x}, Frame size: {}\n", lfp, last_size).as_str();
frame += &format!("Current FP: 0x{:x}\n", fp).as_str();

// print last frame
for i in (-5..(last_size / 4) + 1).rev() {
let offset = (i * 4) as i32;
let read_addr = (fp + offset) as u32;
let string_val = context.machine_.mem().examine(read_addr);
let frameslot_addr = format!("{}(fp)", offset);
let frameslot = format!("0x{:<7x} | {:>7}", read_addr, frameslot_addr);
let frame_str = format!("\n{} : {}", frameslot, string_val);
frame += &frame_str;
}
Ok(Some(frame))
}

fn list_instrs(_: ArgMatches, context: &mut Context) -> Result<Option<String>> {
let pc = context.machine_.cpu().pc;

let program_rom = &context.machine_.program().program_rom;
let total_size = program_rom.0.len();

let mut formatted = String::new();
for i in 0..5 {
let cur_pc = pc + i;
if cur_pc >= total_size as u32 {
break;
}
let instruction = program_rom.get_instruction(cur_pc);
formatted.push_str(format!("{:?} : {:?}\n", cur_pc, instruction).as_str());
}
Ok(Some(formatted))
}

fn set_bp(args: ArgMatches, context: &mut Context) -> Result<Option<String>> {
let pc = args
.get_one::<String>("pc")
.unwrap()
.parse::<u32>()
.unwrap();
context.breakpoints_.push(pc);
let message = format!("Breakpoint set at pc: {}", pc);
Ok(Some(message))
}

fn run_until(args: ArgMatches, context: &mut Context) -> Result<Option<String>> {
let mut message = String::new();
loop {
let (stop, pc) = context.step();
if stop {
message.push_str("Execution stopped");
break;
}
if context.breakpoints_.contains(&pc) {
let bp_index = context.breakpoints_.iter().position(|&x| x == pc).unwrap();
message = format!("Execution stopped at breakpoint {}, PC: {}", bp_index, pc);
break;
}
}
Ok(Some(message))
}

fn step(args: ArgMatches, context: &mut Context) -> Result<Option<String>> {
let (stop, _) = context.step();
if stop {
context.stopped_ = true;
Ok(Some(String::from("Execution stopped")))
} else {
Ok(None)
}
}

fn repl_run(args: &Args) {
// instantiate repl
let mut repl = Repl::new(Context::new(args))
.with_name("REPL")
.with_version("v0.1.0")
.with_description("Valida VM REPL")
.with_banner("Start by using keywords")
.with_command(Command::new("x").about("read machine state"), status)
.with_command(
Command::new("s")
.arg(Arg::new("num_steps").required(false))
.about("step assembly"),
step,
)
.with_command(
Command::new("f")
.arg(Arg::new("size").required(false))
.about("show frame"),
show_frame,
)
.with_command(
Command::new("lf").about("show last frame and current frame"),
last_frame,
)
.with_command(
Command::new("b")
.arg(Arg::new("pc").required(false))
.about("set break point at"),
set_bp,
)
.with_command(
Command::new("r").about("run until stop or breakpoint"),
run_until,
)
.with_command(
Command::new("l").about("list instruction at current PC"),
list_instrs,
)
.with_command(
Command::new("reset").about("reset machine state!"),
init_context,
);

let _ = repl.run();
}

fn main() {
let args = Args::parse();

if args.action == "interactive" {
repl_run(&args);
return;
}

let mut machine = BasicMachine::<BabyBear>::default();
let rom = match ProgramROM::from_file(&args.program) {
Ok(contents) => contents,
Expand Down
1 change: 0 additions & 1 deletion basic/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#![no_std]
#![allow(unused)]

extern crate alloc;
Expand Down
76 changes: 57 additions & 19 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#![no_std]

extern crate alloc;

use alloc::format;
Expand Down Expand Up @@ -76,13 +74,15 @@ fn impl_machine(machine: &syn::DeriveInput) -> TokenStream {

let name = &machine.ident;
let run = run_method(machine, &instructions, &val, &static_data_chip);
let step = step_method(machine, &instructions, &val);
let prove = prove_method(&chips);
let verify = verify_method(&chips);

let (impl_generics, ty_generics, where_clause) = machine.generics.split_for_impl();

let stream = quote! {
impl #impl_generics Machine<#val> for #name #ty_generics #where_clause {
#step
#run
#prove
#verify
Expand Down Expand Up @@ -173,23 +173,25 @@ fn run_method(
#init_static_data

loop {
// Fetch
let pc = self.cpu().pc;
let instruction = program.get_instruction(pc);
let opcode = instruction.opcode;
let ops = instruction.operands;

// Execute
match opcode {
#opcode_arms
_ => panic!("Unrecognized opcode: {}", opcode),
};
self.read_word(pc as usize);

// A STOP instruction signals the end of the program
if opcode == <StopInstruction as Instruction<Self, #val>>::OPCODE {
break;
}
// Fetch
let pc = self.cpu().pc;
let instruction = program.get_instruction(pc);
let opcode = instruction.opcode;
let ops = instruction.operands;

// Execute
std::println!("trace: pc = {:?}, instruction = {:?}, ops = {:?}", pc, instruction, ops);
match opcode {
#opcode_arms
_ => panic!("Unrecognized opcode: {}", opcode),
};
self.read_word(pc as usize);

//let stop = self.step<Adv>(&program, &mut advice);
// A STOP instruction signals the end of the program
if opcode == <StopInstruction as Instruction<Self, #val>>::OPCODE {
break;
}
}

// Record padded STOP instructions
Expand All @@ -201,6 +203,42 @@ fn run_method(
}
}

fn step_method(machine: &syn::DeriveInput, instructions: &[&Field], val: &Ident) -> TokenStream2 {
// TODO: combine this with run
let name = &machine.ident;
let (_, ty_generics, _) = machine.generics.split_for_impl();

let opcode_arms = instructions
.iter()
.map(|inst| {
let ty = &inst.ty;
quote! {
// TODO: Self instead of #name #ty_generics?
<#ty as Instruction<#name #ty_generics, #val>>::OPCODE =>
#ty::execute_with_advice::<Adv>(self, ops, advice),
}
})
.collect::<TokenStream2>();

quote! {
fn step<Adv: ::valida_machine::AdviceProvider>(&mut self, advice: &mut Adv) -> bool {
let pc = self.cpu().pc;
let instruction = self.program.program_rom.get_instruction(pc);
let opcode = instruction.opcode;
let ops = instruction.operands;

std::println!("step: pc = {:?}, instruction = {:?}", pc, instruction);
match opcode {
#opcode_arms
_ => panic!("Unrecognized opcode: {}", opcode),
};
self.read_word(pc as usize);

opcode == <StopInstruction as Instruction<Self, #val>>::OPCODE
}
}
}

fn prove_method(chips: &[&Field]) -> TokenStream2 {
let num_chips = chips.len();
let chip_list = chips
Expand Down
Loading

0 comments on commit 898d5ce

Please sign in to comment.