Add a debug flag for uplc eval

This commit is contained in:
microproofs 2025-01-28 21:34:28 +07:00 committed by Lucas
parent f85d9460f6
commit c382e6fba8
13 changed files with 158 additions and 28 deletions

25
Cargo.lock generated vendored
View File

@ -75,6 +75,7 @@ dependencies = [
"rand", "rand",
"regex", "regex",
"serde_json", "serde_json",
"strum",
"thiserror 1.0.69", "thiserror 1.0.69",
"uplc", "uplc",
"xdg", "xdg",
@ -592,7 +593,7 @@ version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [ dependencies = [
"heck 0.5.0", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.91", "syn 2.0.91",
@ -1332,12 +1333,6 @@ version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@ -3162,21 +3157,24 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.24.1" version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
dependencies = [
"strum_macros",
]
[[package]] [[package]]
name = "strum_macros" name = "strum_macros"
version = "0.24.3" version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [ dependencies = [
"heck 0.4.1", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 1.0.109", "syn 2.0.91",
] ]
[[package]] [[package]]
@ -3613,7 +3611,6 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"strum", "strum",
"strum_macros",
"thiserror 1.0.69", "thiserror 1.0.69",
"walkdir", "walkdir",
] ]

View File

@ -61,9 +61,6 @@ pallas-traverse = "0.31.0"
[profile.dev.package.insta] [profile.dev.package.insta]
opt-level = 3 opt-level = 3
[profile.dev.package.similar]
opt-level = 3
# The profile that 'cargo dist' will build with # The profile that 'cargo dist' will build with
[profile.dist] [profile.dist]
inherits = "release" inherits = "release"

View File

@ -30,7 +30,7 @@ patricia_tree = "0.8.0"
petgraph = "0.6.3" petgraph = "0.6.3"
pretty = "0.12.3" pretty = "0.12.3"
serde = { version = "1.0.197", features = ["derive", "rc"] } serde = { version = "1.0.197", features = ["derive", "rc"] }
strum = "0.24.1" strum = { version = "0.26.3", features = ["derive"] }
thiserror = "1.0.39" thiserror = "1.0.39"
uplc = { path = '../uplc', version = "1.1.10" } uplc = { path = '../uplc', version = "1.1.10" }
vec1 = "1.10.1" vec1 = "1.10.1"

View File

@ -12,9 +12,11 @@ use crate::{
}, },
IdGenerator, IdGenerator,
}; };
use indexmap::IndexMap; use indexmap::IndexMap;
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use uplc::{ use uplc::{
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER}, builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
builtins::DefaultFunction, builtins::DefaultFunction,

View File

@ -45,6 +45,7 @@ pallas-traverse.workspace = true
rand = "0.8.5" rand = "0.8.5"
regex = "1.7.1" regex = "1.7.1"
serde_json = "1.0.94" serde_json = "1.0.94"
strum = { version = "0.26.3", features = ["derive"] }
thiserror = "1.0.39" thiserror = "1.0.39"
uplc = { path = '../uplc', version = "1.1.10" } uplc = { path = '../uplc', version = "1.1.10" }

View File

@ -1,9 +1,17 @@
use miette::IntoDiagnostic; use miette::IntoDiagnostic;
use pallas_primitives::conway::Language;
use serde_json::json; use serde_json::json;
use std::{path::PathBuf, process}; use std::{path::PathBuf, process};
use strum::IntoEnumIterator;
use uplc::{ use uplc::{
ast::{FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, ast::{FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term},
machine::cost_model::ExBudget, builtins::DefaultFunction,
machine::{
cost_model::{ExBudget, StepKind},
TERM_COUNT,
},
parser, parser,
}; };
@ -18,6 +26,9 @@ pub struct Args {
#[clap(short, long)] #[clap(short, long)]
cbor: bool, cbor: bool,
#[clap(short, long, default_value_t = false)]
debug: bool,
/// Arguments to pass to the UPLC program /// Arguments to pass to the UPLC program
args: Vec<String>, args: Vec<String>,
} }
@ -27,6 +38,7 @@ pub fn exec(
script, script,
flat, flat,
args, args,
debug,
cbor, cbor,
}: Args, }: Args,
) -> miette::Result<()> { ) -> miette::Result<()> {
@ -65,7 +77,11 @@ pub fn exec(
let program = Program::<NamedDeBruijn>::try_from(program).into_diagnostic()?; let program = Program::<NamedDeBruijn>::try_from(program).into_diagnostic()?;
let mut eval_result = program.eval(budget); let mut eval_result = if debug {
program.eval_debug(ExBudget::default(), &Language::PlutusV3)
} else {
program.eval(budget)
};
let cost = eval_result.cost(); let cost = eval_result.cost();
let logs = eval_result.logs(); let logs = eval_result.logs();
@ -85,6 +101,43 @@ pub fn exec(
serde_json::to_string_pretty(&output).into_diagnostic()? serde_json::to_string_pretty(&output).into_diagnostic()?
); );
if debug {
println!("---------------DEBUG------------------");
let costs = eval_result.debug_cost().unwrap();
let mut output = json!([]);
for step in StepKind::iter() {
if matches!(step, StepKind::StartUp) {
continue;
}
let i = step as usize * 2;
if costs[i + 1] != 0 || costs[i] != 0 {
output.as_array_mut().unwrap().push(json!({
"step": step.to_string(),
"cpu": costs[i+1],
"mem": costs[i],
}));
}
}
for fun in DefaultFunction::iter() {
let i = (fun as usize + TERM_COUNT) * 2;
if costs[i + 1] != 0 || costs[i] != 0 {
output.as_array_mut().unwrap().push(json!({
"fun": fun.to_string(),
"cpu": costs[i+1],
"mem": costs[i],
}));
}
}
println!(
"{}",
serde_json::to_string_pretty(&output).into_diagnostic()?
);
}
Ok(()) Ok(())
} }
Err(err) => { Err(err) => {

View File

@ -28,8 +28,7 @@ peg = "0.8.1"
pretty = "0.11.3" pretty = "0.11.3"
serde = { version = "1.0.152", features = ["derive"] } serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.94" serde_json = "1.0.94"
strum = "0.24.1" strum = { version = "0.26.3", features = ["derive"] }
strum_macros = "0.24.3"
thiserror = "1.0.39" thiserror = "1.0.39"
blst = "0.3.11" blst = "0.3.11"
once_cell = "1.18.0" once_cell = "1.18.0"

View File

@ -844,7 +844,7 @@ impl From<Term<FakeNamedDeBruijn>> for Term<NamedDeBruijn> {
impl Program<NamedDeBruijn> { impl Program<NamedDeBruijn> {
pub fn eval(self, initial_budget: ExBudget) -> EvalResult { pub fn eval(self, initial_budget: ExBudget) -> EvalResult {
let mut machine = Machine::new( let mut machine = Machine::new(
Language::PlutusV2, Language::PlutusV3,
CostModel::default(), CostModel::default(),
initial_budget, initial_budget,
200, 200,
@ -852,7 +852,13 @@ impl Program<NamedDeBruijn> {
let term = machine.run(self.term); let term = machine.run(self.term);
EvalResult::new(term, machine.ex_budget, initial_budget, machine.logs) EvalResult::new(
term,
machine.ex_budget,
initial_budget,
machine.logs,
machine.spend_counter.map(|i| i.into()),
)
} }
/// Evaluate a Program as a specific PlutusVersion /// Evaluate a Program as a specific PlutusVersion
@ -861,7 +867,13 @@ impl Program<NamedDeBruijn> {
let term = machine.run(self.term); let term = machine.run(self.term);
EvalResult::new(term, machine.ex_budget, initial_budget, machine.logs) EvalResult::new(
term,
machine.ex_budget,
initial_budget,
machine.logs,
machine.spend_counter.map(|i| i.into()),
)
} }
pub fn eval_as( pub fn eval_as(
@ -881,7 +893,32 @@ impl Program<NamedDeBruijn> {
let term = machine.run(self.term); let term = machine.run(self.term);
EvalResult::new(term, machine.ex_budget, budget, machine.logs) EvalResult::new(
term,
machine.ex_budget,
budget,
machine.logs,
machine.spend_counter.map(|i| i.into()),
)
}
pub fn eval_debug(self, initial_budget: ExBudget, version: &Language) -> EvalResult {
let mut machine = Machine::new_debug(
version.clone(),
CostModel::default(),
initial_budget,
200, //slippage
);
let term = machine.run(self.term);
EvalResult::new(
term,
machine.ex_budget,
initial_budget,
machine.logs,
machine.spend_counter.map(|i| i.into()),
)
} }
} }

View File

@ -1,7 +1,7 @@
use crate::ast::Term; use crate::ast::Term;
use pallas_codec::flat::de; use pallas_codec::flat::de;
use std::{fmt::Display, rc::Rc, str::FromStr}; use std::{fmt::Display, rc::Rc, str::FromStr};
use strum_macros::EnumIter; use strum::EnumIter;
/// All the possible builtin functions in Untyped Plutus Core. /// All the possible builtin functions in Untyped Plutus Core.
#[repr(u8)] #[repr(u8)]

View File

@ -42,11 +42,15 @@ enum Context {
NoFrame, NoFrame,
} }
pub const TERM_COUNT: usize = 9;
pub const BUILTIN_COUNT: usize = 87;
pub struct Machine { pub struct Machine {
costs: CostModel, costs: CostModel,
pub ex_budget: ExBudget, pub ex_budget: ExBudget,
slippage: u32, slippage: u32,
unbudgeted_steps: [u32; 10], unbudgeted_steps: [u32; 10],
pub spend_counter: Option<[i64; (TERM_COUNT + BUILTIN_COUNT) * 2]>,
pub logs: Vec<String>, pub logs: Vec<String>,
version: Language, version: Language,
} }
@ -63,6 +67,24 @@ impl Machine {
ex_budget: initial_budget, ex_budget: initial_budget,
slippage, slippage,
unbudgeted_steps: [0; 10], unbudgeted_steps: [0; 10],
spend_counter: None,
logs: vec![],
version,
}
}
pub fn new_debug(
version: Language,
costs: CostModel,
initial_budget: ExBudget,
slippage: u32,
) -> Machine {
Machine {
costs,
ex_budget: initial_budget,
slippage,
unbudgeted_steps: [0; 10],
spend_counter: Some([0; (TERM_COUNT + BUILTIN_COUNT) * 2]),
logs: vec![], logs: vec![],
version, version,
} }
@ -324,6 +346,13 @@ impl Machine {
self.spend_budget(cost)?; self.spend_budget(cost)?;
if let Some(counter) = &mut self.spend_counter {
let i = (runtime.fun as usize + TERM_COUNT) * 2;
counter[i] += cost.mem;
counter[i + 1] += cost.cpu;
}
runtime.call(&self.version, &mut self.logs) runtime.call(&self.version, &mut self.logs)
} }
@ -355,6 +384,11 @@ impl Machine {
self.spend_budget(unspent_step_budget)?; self.spend_budget(unspent_step_budget)?;
self.unbudgeted_steps[i] = 0; self.unbudgeted_steps[i] = 0;
if let Some(counter) = &mut self.spend_counter {
counter[i * 2] += unspent_step_budget.mem;
counter[i * 2 + 1] += unspent_step_budget.cpu;
}
} }
self.unbudgeted_steps[9] = 0; self.unbudgeted_steps[9] = 0;

View File

@ -4,6 +4,8 @@ use num_traits::Signed;
use pallas_primitives::conway::Language; use pallas_primitives::conway::Language;
use std::collections::HashMap; use std::collections::HashMap;
use strum::{Display, EnumIter};
macro_rules! hashmap { macro_rules! hashmap {
// map-like // map-like
($($k:expr => $v:expr),* $(,)?) => {{ ($($k:expr => $v:expr),* $(,)?) => {{
@ -5345,6 +5347,7 @@ pub struct TwoArgumentsQuadraticFunction {
} }
#[repr(u8)] #[repr(u8)]
#[derive(Debug, EnumIter, Display, Clone, Copy)]
pub enum StepKind { pub enum StepKind {
Constant = 0, Constant = 0,
Var = 1, Var = 1,

View File

@ -7,6 +7,7 @@ pub struct EvalResult {
remaining_budget: ExBudget, remaining_budget: ExBudget,
initial_budget: ExBudget, initial_budget: ExBudget,
logs: Vec<String>, logs: Vec<String>,
debug_cost: Option<Vec<i64>>,
} }
impl EvalResult { impl EvalResult {
@ -15,12 +16,14 @@ impl EvalResult {
remaining_budget: ExBudget, remaining_budget: ExBudget,
initial_budget: ExBudget, initial_budget: ExBudget,
logs: Vec<String>, logs: Vec<String>,
debug_cost: Option<Vec<i64>>,
) -> EvalResult { ) -> EvalResult {
EvalResult { EvalResult {
result, result,
remaining_budget, remaining_budget,
initial_budget, initial_budget,
logs, logs,
debug_cost,
} }
} }
@ -47,6 +50,10 @@ impl EvalResult {
} }
} }
pub fn debug_cost(&self) -> Option<Vec<i64>> {
self.debug_cost.clone()
}
#[allow(clippy::result_unit_err)] #[allow(clippy::result_unit_err)]
pub fn unwrap_constant(self) -> Result<Constant, ()> { pub fn unwrap_constant(self) -> Result<Constant, ()> {
match self.result { match self.result {

View File

@ -53,7 +53,7 @@ impl From<&Language> for BuiltinSemantics {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct BuiltinRuntime { pub struct BuiltinRuntime {
pub(super) args: Vec<Value>, pub(super) args: Vec<Value>,
fun: DefaultFunction, pub fun: DefaultFunction,
pub(super) forces: u32, pub(super) forces: u32,
} }