From ff1ff587d1eb48bcf06d0e0ff4efa758b0636781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Tue, 15 Nov 2022 15:21:11 +0100 Subject: [PATCH] Bump solana_rbpf to 0.2.36 (#28794) * Bumps solana_rbpf to v0.2.36 * Removes ThisInstructionMeter. * Removes one "unsafe" expression. * Removes redundant call to solana_rbpf::elf::register_bpf_function(). * Adjusts SyscallFunction and SyscallRegistry. * Inlines ProgramEnvironment into EbpfVm. * Refactors trait SyscallConsume into fn consume_compute_meter(). * Inlines ComputeMeter into InvokeContext. * Removes solana-metrics dependency from bpf_loader. * Replaces RBPF tracer functionality by the debugger. * Take compute_units_consumed from execute_program(). * Merges execute_program_interpreted() and execute_program_jit(). --- Cargo.lock | 33 ++- cli/Cargo.toml | 2 +- cli/src/program.rs | 9 +- program-runtime/Cargo.toml | 1 + program-runtime/src/invoke_context.rs | 72 ++--- program-test/src/lib.rs | 8 +- programs/bpf_loader/Cargo.toml | 3 +- programs/bpf_loader/src/allocator_bump.rs | 4 + programs/bpf_loader/src/lib.rs | 152 ++++------ programs/bpf_loader/src/syscalls/cpi.rs | 22 +- programs/bpf_loader/src/syscalls/logging.rs | 22 +- programs/bpf_loader/src/syscalls/mem_ops.rs | 2 +- programs/bpf_loader/src/syscalls/mod.rs | 307 +++++++------------- programs/bpf_loader/src/syscalls/sysvar.rs | 3 +- programs/sbf/Cargo.lock | 6 +- programs/sbf/Cargo.toml | 2 +- programs/sbf/benches/bpf_loader.rs | 80 ++--- programs/sbf/tests/programs.rs | 71 ++--- programs/zk-token-proof/src/lib.rs | 3 +- rbpf-cli/Cargo.toml | 2 +- rbpf-cli/src/main.rs | 78 ++--- 21 files changed, 354 insertions(+), 528 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fc61f82e8..a8e5863d67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1715,6 +1715,20 @@ dependencies = [ "tempfile", ] +[[package]] +name = "gdbstub" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c95766e0414f8bfc1d07055574c621b67739466d6ba516c4fef8e99d30d2e6" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "log", + "managed", + "num-traits", + "paste", +] + [[package]] name = "gen-headers" version = "1.15.0" @@ -2576,6 +2590,12 @@ dependencies = [ "libc", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "maplit" version = "1.0.2" @@ -3116,6 +3136,12 @@ dependencies = [ "windows-sys 0.32.0", ] +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + [[package]] name = "pbkdf2" version = "0.4.0" @@ -4747,7 +4773,6 @@ dependencies = [ "log", "rand 0.7.3", "solana-measure", - "solana-metrics", "solana-program-runtime", "solana-sdk 1.15.0", "solana-zk-token-sdk 1.15.0", @@ -5877,6 +5902,7 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-sdk 1.15.0", + "solana_rbpf", "thiserror", ] @@ -6822,12 +6848,13 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bcad04ab8dff83d39e5b039e419d03e83dafc6643401700b61acf0cc1589ff8" +checksum = "94fe0b05d69875dc1da1e646aec8284c680b9b3f5f521c8d8f958b2cbc108d0b" dependencies = [ "byteorder", "combine", + "gdbstub", "goblin", "hash32", "libc", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 30752b0651..c84d7b7ec4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -54,7 +54,7 @@ solana-tpu-client = { path = "../tpu-client", version = "=1.15.0" } solana-transaction-status = { path = "../transaction-status", version = "=1.15.0" } solana-version = { path = "../version", version = "=1.15.0" } solana-vote-program = { path = "../programs/vote", version = "=1.15.0" } -solana_rbpf = "=0.2.35" +solana_rbpf = "=0.2.36" spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] } thiserror = "1.0.31" tiny-bip39 = "0.8.2" diff --git a/cli/src/program.rs b/cli/src/program.rs index 8d60c5064a..50f5e732b4 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -10,7 +10,7 @@ use { clap::{App, AppSettings, Arg, ArgMatches, SubCommand}, log::*, solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}, - solana_bpf_loader_program::{syscalls::register_syscalls, ThisInstructionMeter}, + solana_bpf_loader_program::syscalls::register_syscalls, solana_clap_utils::{self, input_parsers::*, input_validators::*, keypair::*}, solana_cli_output::{ CliProgram, CliProgramAccountType, CliProgramAuthority, CliProgramBuffer, CliProgramId, @@ -2004,7 +2004,7 @@ fn read_and_verify_elf(program_location: &str) -> Result, Box::from_elf( + let executable = Executable::::from_elf( &program_data, Config { reject_broken_elfs: true, @@ -2014,9 +2014,8 @@ fn read_and_verify_elf(program_location: &str) -> Result, Box::from_executable(executable) - .map_err(|err| format!("ELF error: {}", err))?; + let _ = VerifiedExecutable::::from_executable(executable) + .map_err(|err| format!("ELF error: {}", err))?; Ok(program_data) } diff --git a/program-runtime/Cargo.toml b/program-runtime/Cargo.toml index aae3ff0cde..63d9ae51a5 100644 --- a/program-runtime/Cargo.toml +++ b/program-runtime/Cargo.toml @@ -26,6 +26,7 @@ solana-frozen-abi = { path = "../frozen-abi", version = "=1.15.0" } solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.15.0" } solana-measure = { path = "../measure", version = "=1.15.0" } solana-metrics = { path = "../metrics", version = "=1.15.0" } +solana_rbpf = "=0.2.36" solana-sdk = { path = "../sdk", version = "=1.15.0" } thiserror = "1.0" diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index 081547d45f..95895693c1 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -11,6 +11,7 @@ use { timings::{ExecuteDetailsTimings, ExecuteTimings}, }, solana_measure::measure::Measure, + solana_rbpf::vm::ContextObject, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, @@ -58,33 +59,20 @@ impl std::fmt::Debug for BuiltinProgram { } } -/// Compute meter -pub struct ComputeMeter { - remaining: u64, -} -impl ComputeMeter { - /// Consume compute units - pub fn consume(&mut self, amount: u64) -> Result<(), InstructionError> { - let exceeded = self.remaining < amount; - self.remaining = self.remaining.saturating_sub(amount); - if exceeded { - return Err(InstructionError::ComputationalBudgetExceeded); - } - Ok(()) +impl<'a> ContextObject for InvokeContext<'a> { + fn trace(&mut self, state: [u64; 12]) { + self.trace_log.push(state); } - /// Get the number of remaining compute units - pub fn get_remaining(&self) -> u64 { - self.remaining + + fn consume(&mut self, amount: u64) { + // 1 to 1 instruction to compute unit mapping + // ignore overflow, Ebpf will bail if exceeded + let mut compute_meter = self.compute_meter.borrow_mut(); + *compute_meter = compute_meter.saturating_sub(amount); } - /// Set compute units - /// - /// Only use for tests and benchmarks - pub fn mock_set_remaining(&mut self, remaining: u64) { - self.remaining = remaining; - } - /// Construct a new one with the given remaining units - pub fn new_ref(remaining: u64) -> Rc> { - Rc::new(RefCell::new(Self { remaining })) + + fn get_remaining(&self) -> u64 { + *self.compute_meter.borrow() } } @@ -116,10 +104,11 @@ pub struct InvokeContext<'a> { pre_accounts: Vec, builtin_programs: &'a [BuiltinProgram], pub sysvar_cache: Cow<'a, SysvarCache>, + pub trace_log: Vec<[u64; 12]>, log_collector: Option>>, compute_budget: ComputeBudget, current_compute_budget: ComputeBudget, - compute_meter: Rc>, + compute_meter: RefCell, accounts_data_meter: AccountsDataMeter, pub tx_executor_cache: Rc>, pub feature_set: Arc, @@ -150,10 +139,11 @@ impl<'a> InvokeContext<'a> { pre_accounts: Vec::new(), builtin_programs, sysvar_cache, + trace_log: Vec::new(), log_collector, current_compute_budget: compute_budget, compute_budget, - compute_meter: ComputeMeter::new_ref(compute_budget.compute_unit_limit), + compute_meter: RefCell::new(compute_budget.compute_unit_limit), accounts_data_meter: AccountsDataMeter::new(prev_accounts_data_len), tx_executor_cache, feature_set, @@ -746,7 +736,7 @@ impl<'a> InvokeContext<'a> { self.transaction_context .set_return_data(program_id, Vec::new())?; - let pre_remaining_units = self.compute_meter.borrow().get_remaining(); + let pre_remaining_units = self.get_remaining(); let result = if builtin_id == program_id { let logger = self.get_log_collector(); stable_log::program_invoke(&logger, &program_id, self.get_stack_height()); @@ -761,7 +751,7 @@ impl<'a> InvokeContext<'a> { } else { (entry.process_instruction)(first_instruction_account, self) }; - let post_remaining_units = self.compute_meter.borrow().get_remaining(); + let post_remaining_units = self.get_remaining(); *compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units); process_executable_chain_time.stop(); @@ -784,9 +774,22 @@ impl<'a> InvokeContext<'a> { self.log_collector.clone() } - /// Get this invocation's ComputeMeter - pub fn get_compute_meter(&self) -> Rc> { - self.compute_meter.clone() + /// Consume compute units + pub fn consume_checked(&self, amount: u64) -> Result<(), InstructionError> { + let mut compute_meter = self.compute_meter.borrow_mut(); + let exceeded = *compute_meter < amount; + *compute_meter = compute_meter.saturating_sub(amount); + if exceeded { + return Err(InstructionError::ComputationalBudgetExceeded); + } + Ok(()) + } + + /// Set compute units + /// + /// Only use for tests and benchmarks + pub fn mock_set_remaining(&self, remaining: u64) { + *self.compute_meter.borrow_mut() = remaining; } /// Get this invocation's AccountsDataMeter @@ -1138,10 +1141,7 @@ mod tests { compute_units_to_consume, desired_result, } => { - invoke_context - .get_compute_meter() - .borrow_mut() - .consume(compute_units_to_consume)?; + invoke_context.consume_checked(compute_units_to_consume)?; return desired_result; } MockInstruction::Resize { new_len } => instruction_context diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index b5dc801a76..7dd0a98708 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -35,7 +35,7 @@ use { instruction::{Instruction, InstructionError}, native_token::sol_to_lamports, poh_config::PohConfig, - program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR}, + program_error::{ProgramError, UNSUPPORTED_SYSVAR}, pubkey::Pubkey, rent::Rent, signature::{Keypair, Signer}, @@ -197,11 +197,7 @@ fn get_sysvar ) -> u64 { let invoke_context = get_invoke_context(); if invoke_context - .get_compute_meter() - .try_borrow_mut() - .map_err(|_| ACCOUNT_BORROW_FAILED) - .unwrap() - .consume(invoke_context.get_compute_budget().sysvar_base_cost + T::size_of() as u64) + .consume_checked(invoke_context.get_compute_budget().sysvar_base_cost + T::size_of() as u64) .is_err() { panic!("Exceeded compute budget"); diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index d95a4ab034..195b967766 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -15,11 +15,10 @@ byteorder = "1.4.3" libsecp256k1 = "0.6.0" log = "0.4.17" solana-measure = { path = "../../measure", version = "=1.15.0" } -solana-metrics = { path = "../../metrics", version = "=1.15.0" } solana-program-runtime = { path = "../../program-runtime", version = "=1.15.0" } solana-sdk = { path = "../../sdk", version = "=1.15.0" } solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=1.15.0" } -solana_rbpf = "=0.2.35" +solana_rbpf = "=0.2.36" thiserror = "1.0" [dev-dependencies] diff --git a/programs/bpf_loader/src/allocator_bump.rs b/programs/bpf_loader/src/allocator_bump.rs index 00819a9ae0..cd954d4b63 100644 --- a/programs/bpf_loader/src/allocator_bump.rs +++ b/programs/bpf_loader/src/allocator_bump.rs @@ -25,6 +25,10 @@ impl BpfAllocator { pos: 0, } } + + pub fn get_heap(&mut self) -> &mut [u8] { + self.heap.as_slice_mut() + } } impl Alloc for BpfAllocator { diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 75da03f64b..268fd2829d 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -15,14 +15,13 @@ use { serialization::{deserialize_parameters, serialize_parameters}, syscalls::SyscallError, }, - log::{log_enabled, trace, Level::Trace}, solana_measure::measure::Measure, solana_program_runtime::{ compute_budget::ComputeBudget, executor::{CreateMetrics, Executor}, executor_cache::TransactionExecutorCache, ic_logger_msg, ic_msg, - invoke_context::{ComputeMeter, InvokeContext}, + invoke_context::InvokeContext, log_collector::LogCollector, stable_log, sysvar_cache::get_sysvar_with_account_check, @@ -33,9 +32,8 @@ use { elf::Executable, error::{EbpfError, UserDefinedError}, memory_region::MemoryRegion, - static_analysis::Analysis, verifier::{RequisiteVerifier, VerifierError}, - vm::{Config, EbpfVm, InstructionMeter, ProgramResult, VerifiedExecutable}, + vm::{Config, ContextObject, EbpfVm, ProgramResult, VerifiedExecutable}, }, solana_sdk::{ bpf_loader, bpf_loader_deprecated, @@ -148,7 +146,7 @@ fn create_executor_from_bytes( enable_stack_frame_gaps: true, instruction_meter_checkpoint_distance: 10000, enable_instruction_meter: true, - enable_instruction_tracing: log_enabled!(Trace), + enable_instruction_tracing: false, enable_symbol_and_section_labels: false, reject_broken_elfs: reject_deployment_of_broken_elfs, noop_instruction_rate: 256, @@ -168,18 +166,17 @@ fn create_executor_from_bytes( // Warning, do not use `Config::default()` so that configuration here is explicit. }; let mut load_elf_time = Measure::start("load_elf_time"); - let executable = - Executable::::from_elf(programdata, config, syscall_registry) - .map_err(|err| { - ic_logger_msg!(log_collector, "{}", err); - InstructionError::InvalidAccountData - }); + let executable = Executable::::from_elf(programdata, config, syscall_registry) + .map_err(|err| { + ic_logger_msg!(log_collector, "{}", err); + InstructionError::InvalidAccountData + }); load_elf_time.stop(); create_executor_metrics.load_elf_us = load_elf_time.as_us(); let executable = executable?; let mut verify_code_time = Measure::start("verify_code_time"); let mut verified_executable = - VerifiedExecutable::::from_executable(executable) + VerifiedExecutable::::from_executable(executable) .map_err(|err| { ic_logger_msg!(log_collector, "{}", err); InstructionError::InvalidAccountData @@ -316,22 +313,20 @@ fn check_loader_id(id: &Pubkey) -> bool { /// Create the SBF virtual machine pub fn create_vm<'a, 'b>( - program: &'a VerifiedExecutable, + program: &'a VerifiedExecutable>, regions: Vec, orig_account_lengths: Vec, invoke_context: &'a mut InvokeContext<'b>, -) -> Result, EbpfError> { +) -> Result>, EbpfError> { let compute_budget = invoke_context.get_compute_budget(); let heap_size = compute_budget.heap_size.unwrap_or(HEAP_LENGTH); - let _ = invoke_context.get_compute_meter().borrow_mut().consume( + let _ = invoke_context.consume_checked( ((heap_size as u64).saturating_div(32_u64.saturating_mul(1024))) .saturating_sub(1) .saturating_mul(compute_budget.heap_cost), ); - let mut heap = + let heap = AlignedMemory::::zero_filled(compute_budget.heap_size.unwrap_or(HEAP_LENGTH)); - - let vm = EbpfVm::new(program, invoke_context, heap.as_slice_mut(), regions)?; let check_aligned = bpf_loader_deprecated::id() != invoke_context .transaction_context @@ -345,15 +340,22 @@ pub fn create_vm<'a, 'b>( let check_size = invoke_context .feature_set .is_active(&check_slice_translation_size::id()); + let allocator = Rc::new(RefCell::new(BpfAllocator::new(heap, MM_HEAP_START))); invoke_context .set_syscall_context( check_aligned, check_size, orig_account_lengths, - Rc::new(RefCell::new(BpfAllocator::new(heap, MM_HEAP_START))), + allocator.clone(), ) .map_err(SyscallError::InstructionError)?; - Ok(vm) + let result = EbpfVm::new( + program, + invoke_context, + allocator.borrow_mut().get_heap(), + regions, + ); + result } pub fn process_instruction( @@ -1366,29 +1368,9 @@ fn process_loader_instruction( Ok(()) } -/// Passed to the VM to enforce the compute budget -pub struct ThisInstructionMeter { - pub compute_meter: Rc>, -} -impl ThisInstructionMeter { - fn new(compute_meter: Rc>) -> Self { - Self { compute_meter } - } -} -impl InstructionMeter for ThisInstructionMeter { - fn consume(&mut self, amount: u64) { - // 1 to 1 instruction to compute unit mapping - // ignore error, Ebpf will bail if exceeded - let _ = self.compute_meter.borrow_mut().consume(amount); - } - fn get_remaining(&self) -> u64 { - self.compute_meter.borrow().get_remaining() - } -} - /// BPF Loader's Executor implementation pub struct BpfExecutor { - verified_executable: VerifiedExecutable, + verified_executable: VerifiedExecutable>, use_jit: bool, } @@ -1402,7 +1384,6 @@ impl Debug for BpfExecutor { impl Executor for BpfExecutor { fn execute(&self, invoke_context: &mut InvokeContext) -> Result<(), InstructionError> { let log_collector = invoke_context.get_log_collector(); - let compute_meter = invoke_context.get_compute_meter(); let stack_height = invoke_context.get_stack_height(); let transaction_context = &invoke_context.transaction_context; let instruction_context = transaction_context.get_current_instruction_context()?; @@ -1421,8 +1402,11 @@ impl Executor for BpfExecutor { let mut create_vm_time = Measure::start("create_vm"); let mut execute_time; let execution_result = { + let compute_meter_prev = invoke_context.get_remaining(); let mut vm = match create_vm( - &self.verified_executable, + // We dropped the lifetime tracking in the Executor by setting it to 'static, + // thus we need to reintroduce the correct lifetime of InvokeContext here again. + unsafe { std::mem::transmute(&self.verified_executable) }, regions, account_lengths, invoke_context, @@ -1437,33 +1421,15 @@ impl Executor for BpfExecutor { execute_time = Measure::start("execute"); stable_log::program_invoke(&log_collector, &program_id, stack_height); - let mut instruction_meter = ThisInstructionMeter::new(compute_meter.clone()); - let before = compute_meter.borrow().get_remaining(); - let result = if self.use_jit { - vm.execute_program_jit(&mut instruction_meter) - } else { - vm.execute_program_interpreted(&mut instruction_meter) - }; - let after = compute_meter.borrow().get_remaining(); + let (compute_units_consumed, result) = vm.execute_program(!self.use_jit); + drop(vm); ic_logger_msg!( log_collector, "Program {} consumed {} of {} compute units", &program_id, - before.saturating_sub(after), - before + compute_units_consumed, + compute_meter_prev ); - if log_enabled!(Trace) { - let mut trace_buffer = Vec::::new(); - let analysis = - Analysis::from_executable(self.verified_executable.get_executable()).unwrap(); - vm.get_program_environment() - .tracer - .write(&mut trace_buffer, &analysis) - .unwrap(); - let trace_string = String::from_utf8(trace_buffer).unwrap(); - trace!("SBF Program Instruction Trace:\n{}", trace_string); - } - drop(vm); let (_returned_from_program_id, return_data) = invoke_context.transaction_context.get_return_data(); if !return_data.is_empty() { @@ -1558,7 +1524,11 @@ mod tests { super::*, rand::Rng, solana_program_runtime::invoke_context::mock_process_instruction, - solana_rbpf::{ebpf::MM_INPUT_START, verifier::Verifier, vm::SyscallRegistry}, + solana_rbpf::{ + ebpf::MM_INPUT_START, + verifier::Verifier, + vm::{ContextObject, FunctionRegistry, SyscallRegistry}, + }, solana_sdk::{ account::{ create_account_shared_data_for_test as create_account_for_test, AccountSharedData, @@ -1574,10 +1544,11 @@ mod tests { std::{fs::File, io::Read, ops::Range}, }; - struct TestInstructionMeter { + struct TestContextObject { remaining: u64, } - impl InstructionMeter for TestInstructionMeter { + impl ContextObject for TestContextObject { + fn trace(&mut self, _state: [u64; 12]) {} fn consume(&mut self, amount: u64) { self.remaining = self.remaining.saturating_sub(amount); } @@ -1621,7 +1592,11 @@ mod tests { struct TautologyVerifier {} impl Verifier for TautologyVerifier { - fn verify(_prog: &[u8], _config: &Config) -> std::result::Result<(), VerifierError> { + fn verify( + _prog: &[u8], + _config: &Config, + _function_registry: &FunctionRegistry, + ) -> std::result::Result<(), VerifierError> { Ok(()) } } @@ -1638,16 +1613,8 @@ mod tests { let mut input_mem = [0x00]; let config = Config::default(); let syscall_registry = SyscallRegistry::default(); - let mut bpf_functions = std::collections::BTreeMap::::new(); - solana_rbpf::elf::register_bpf_function( - &config, - &mut bpf_functions, - &syscall_registry, - 0, - "entrypoint", - ) - .unwrap(); - let executable = Executable::::from_text_bytes( + let bpf_functions = std::collections::BTreeMap::::new(); + let executable = Executable::::from_text_bytes( program, config, syscall_registry, @@ -1655,16 +1622,18 @@ mod tests { ) .unwrap(); let verified_executable = - VerifiedExecutable::::from_executable( - executable, - ) - .unwrap(); + VerifiedExecutable::::from_executable(executable) + .unwrap(); let input_region = MemoryRegion::new_writable(&mut input_mem, MM_INPUT_START); - let mut vm = - EbpfVm::new(&verified_executable, &mut (), &mut [], vec![input_region]).unwrap(); - let mut instruction_meter = TestInstructionMeter { remaining: 10 }; - vm.execute_program_interpreted(&mut instruction_meter) - .unwrap(); + let mut context_object = TestContextObject { remaining: 10 }; + let mut vm = EbpfVm::new( + &verified_executable, + &mut context_object, + &mut [], + vec![input_region], + ) + .unwrap(); + vm.execute_program(true).1.unwrap(); } #[test] @@ -1673,7 +1642,7 @@ mod tests { let prog = &[ 0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, // first half of lddw ]; - RequisiteVerifier::verify(prog, &Config::default()).unwrap(); + RequisiteVerifier::verify(prog, &Config::default(), &FunctionRegistry::default()).unwrap(); } #[test] @@ -1878,10 +1847,7 @@ mod tests { None, Err(InstructionError::ProgramFailedToComplete), |first_instruction_account: IndexOfAccount, invoke_context: &mut InvokeContext| { - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(0); + invoke_context.mock_set_remaining(0); super::process_instruction(first_instruction_account, invoke_context) }, ); diff --git a/programs/bpf_loader/src/syscalls/cpi.rs b/programs/bpf_loader/src/syscalls/cpi.rs index 5a5c207e8a..43f81ce1ee 100644 --- a/programs/bpf_loader/src/syscalls/cpi.rs +++ b/programs/bpf_loader/src/syscalls/cpi.rs @@ -101,7 +101,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust { .feature_set .is_active(&feature_set::loosen_cpi_size_restriction::id()) { - invoke_context.get_compute_meter().consume( + consume_compute_meter( + invoke_context, (ix_data_len) .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), )?; @@ -175,7 +176,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust { invoke_context.get_check_aligned(), )?; - invoke_context.get_compute_meter().consume( + consume_compute_meter( + invoke_context, (data.len() as u64) .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), )?; @@ -392,7 +394,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedC { .feature_set .is_active(&feature_set::loosen_cpi_size_restriction::id()) { - invoke_context.get_compute_meter().consume( + consume_compute_meter( + invoke_context, (ix_data_len) .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), )?; @@ -471,7 +474,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedC { )?; let vm_data_addr = account_info.data_addr; - invoke_context.get_compute_meter().consume( + consume_compute_meter( + invoke_context, account_info .data_len .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), @@ -630,7 +634,8 @@ where .map_err(SyscallError::InstructionError)?; if callee_account.is_executable() { // Use the known account - invoke_context.get_compute_meter().consume( + consume_compute_meter( + invoke_context, (callee_account.get_data().len() as u64) .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), )?; @@ -862,9 +867,10 @@ fn cpi_common( signers_seeds_len: u64, memory_mapping: &mut MemoryMapping, ) -> Result { - invoke_context - .get_compute_meter() - .consume(invoke_context.get_compute_budget().invoke_units)?; + consume_compute_meter( + invoke_context, + invoke_context.get_compute_budget().invoke_units, + )?; // Translate and verify caller's data let instruction = S::translate_instruction(instruction_addr, memory_mapping, invoke_context)?; diff --git a/programs/bpf_loader/src/syscalls/logging.rs b/programs/bpf_loader/src/syscalls/logging.rs index 08444a8907..5af0b786f9 100644 --- a/programs/bpf_loader/src/syscalls/logging.rs +++ b/programs/bpf_loader/src/syscalls/logging.rs @@ -1,4 +1,4 @@ -use {super::*, crate::declare_syscall}; +use {super::*, crate::declare_syscall, solana_rbpf::vm::ContextObject}; declare_syscall!( /// Log a user's info message @@ -16,7 +16,7 @@ declare_syscall!( .get_compute_budget() .syscall_base_cost .max(len); - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; translate_string_and_do( memory_mapping, @@ -46,7 +46,7 @@ declare_syscall!( _memory_mapping: &mut MemoryMapping, ) -> Result { let cost = invoke_context.get_compute_budget().log_64_units; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; stable_log::program_log( &invoke_context.get_log_collector(), @@ -72,12 +72,12 @@ declare_syscall!( _memory_mapping: &mut MemoryMapping, ) -> Result { let cost = invoke_context.get_compute_budget().syscall_base_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; ic_logger_msg!( invoke_context.get_log_collector(), "Program consumption: {} units remaining", - invoke_context.get_compute_meter().borrow().get_remaining() + invoke_context.get_remaining(), ); Ok(0) } @@ -96,7 +96,7 @@ declare_syscall!( memory_mapping: &mut MemoryMapping, ) -> Result { let cost = invoke_context.get_compute_budget().log_pubkey_units; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let pubkey = translate_type::( memory_mapping, @@ -122,9 +122,7 @@ declare_syscall!( ) -> Result { let budget = invoke_context.get_compute_budget(); - invoke_context - .get_compute_meter() - .consume(budget.syscall_base_cost)?; + consume_compute_meter(invoke_context, budget.syscall_base_cost)?; let untranslated_fields = translate_slice::<&[u8]>( memory_mapping, @@ -134,12 +132,14 @@ declare_syscall!( invoke_context.get_check_size(), )?; - invoke_context.get_compute_meter().consume( + consume_compute_meter( + invoke_context, budget .syscall_base_cost .saturating_mul(untranslated_fields.len() as u64), )?; - invoke_context.get_compute_meter().consume( + consume_compute_meter( + invoke_context, untranslated_fields .iter() .fold(0, |total, e| total.saturating_add(e.len() as u64)), diff --git a/programs/bpf_loader/src/syscalls/mem_ops.rs b/programs/bpf_loader/src/syscalls/mem_ops.rs index 18cbf36483..fd90f9a8a5 100644 --- a/programs/bpf_loader/src/syscalls/mem_ops.rs +++ b/programs/bpf_loader/src/syscalls/mem_ops.rs @@ -5,7 +5,7 @@ fn mem_op_consume(invoke_context: &mut InvokeContext, n: u64) -> Result<(), Ebpf let cost = compute_budget .mem_op_base_cost .max(n.saturating_div(compute_budget.cpi_bytes_per_unit)); - invoke_context.get_compute_meter().consume(cost) + consume_compute_meter(invoke_context, cost) } declare_syscall!( diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index 9799e72d93..f9d7523763 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -13,10 +13,7 @@ pub use self::{ use { crate::BpfError, solana_program_runtime::{ - ic_logger_msg, ic_msg, - invoke_context::{ComputeMeter, InvokeContext}, - stable_log, - timings::ExecuteTimings, + ic_logger_msg, ic_msg, invoke_context::InvokeContext, stable_log, timings::ExecuteTimings, }, solana_rbpf::{ error::EbpfError, @@ -55,9 +52,7 @@ use { }, std::{ alloc::Layout, - cell::RefCell, mem::{align_of, size_of}, - rc::Rc, slice::from_raw_parts_mut, str::{from_utf8, Utf8Error}, sync::Arc, @@ -129,17 +124,11 @@ impl From for EbpfError { } } -trait SyscallConsume { - fn consume(&mut self, amount: u64) -> Result<(), EbpfError>; -} -impl SyscallConsume for Rc> { - fn consume(&mut self, amount: u64) -> Result<(), EbpfError> { - self.try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed)? - .consume(amount) - .map_err(SyscallError::InstructionError)?; - Ok(()) - } +fn consume_compute_meter(invoke_context: &InvokeContext, amount: u64) -> Result<(), EbpfError> { + invoke_context + .consume_checked(amount) + .map_err(SyscallError::InstructionError)?; + Ok(()) } macro_rules! register_feature_gated_syscall { @@ -152,10 +141,10 @@ macro_rules! register_feature_gated_syscall { }; } -pub fn register_syscalls( +pub fn register_syscalls<'a>( feature_set: &FeatureSet, disable_deploy_of_alloc_free_syscall: bool, -) -> Result { +) -> Result>, EbpfError> { let blake3_syscall_enabled = feature_set.is_active(&blake3_syscall_enabled::id()); let curve25519_syscall_enabled = feature_set.is_active(&curve25519_syscall_enabled::id()); let disable_fees_sysvar = feature_set.is_active(&disable_fees_sysvar::id()); @@ -460,7 +449,7 @@ declare_syscall!( _arg5: u64, memory_mapping: &mut MemoryMapping, ) -> Result { - invoke_context.get_compute_meter().consume(len)?; + consume_compute_meter(invoke_context, len)?; translate_string_and_do( memory_mapping, @@ -572,7 +561,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .create_program_address_units; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let (seeds, program_id) = translate_and_check_program_address_inputs( seeds_addr, @@ -616,7 +605,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .create_program_address_units; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let (seeds, program_id) = translate_and_check_program_address_inputs( seeds_addr, @@ -665,7 +654,7 @@ declare_syscall!( } } bump_seed[0] = bump_seed[0].saturating_sub(1); - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; } Ok(1) } @@ -694,9 +683,7 @@ declare_syscall!( return Err(SyscallError::TooManySlices.into()); } - invoke_context - .get_compute_meter() - .consume(compute_budget.sha256_base_cost)?; + consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?; let hash_result = translate_slice_mut::( memory_mapping, @@ -727,7 +714,7 @@ declare_syscall!( .sha256_byte_cost .saturating_mul((val.len() as u64).saturating_div(2)), ); - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; hasher.hash(bytes); } } @@ -759,9 +746,7 @@ declare_syscall!( return Err(SyscallError::TooManySlices.into()); } - invoke_context - .get_compute_meter() - .consume(compute_budget.sha256_base_cost)?; + consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?; let hash_result = translate_slice_mut::( memory_mapping, @@ -792,7 +777,7 @@ declare_syscall!( .sha256_byte_cost .saturating_mul((val.len() as u64).saturating_div(2)), ); - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; hasher.hash(bytes); } } @@ -814,7 +799,7 @@ declare_syscall!( memory_mapping: &mut MemoryMapping, ) -> Result { let cost = invoke_context.get_compute_budget().secp256k1_recover_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let hash = translate_slice::( memory_mapping, @@ -911,7 +896,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .curve25519_edwards_validate_point_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let point = translate_type::( memory_mapping, @@ -929,7 +914,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .curve25519_ristretto_validate_point_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let point = translate_type::( memory_mapping, @@ -971,7 +956,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .curve25519_edwards_add_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let left_point = translate_type::( memory_mapping, @@ -999,7 +984,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .curve25519_edwards_subtract_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let left_point = translate_type::( memory_mapping, @@ -1027,7 +1012,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .curve25519_edwards_multiply_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let scalar = translate_type::( memory_mapping, @@ -1059,7 +1044,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .curve25519_ristretto_add_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let left_point = translate_type::( memory_mapping, @@ -1087,7 +1072,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .curve25519_ristretto_subtract_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let left_point = translate_type::( memory_mapping, @@ -1117,7 +1102,7 @@ declare_syscall!( let cost = invoke_context .get_compute_budget() .curve25519_ristretto_multiply_cost; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let scalar = translate_type::( memory_mapping, @@ -1177,7 +1162,7 @@ declare_syscall!( .curve25519_edwards_msm_incremental_cost .saturating_mul(points_len.saturating_sub(1)), ); - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let scalars = translate_slice::( memory_mapping, @@ -1217,7 +1202,7 @@ declare_syscall!( .curve25519_ristretto_msm_incremental_cost .saturating_mul(points_len.saturating_sub(1)), ); - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let scalars = translate_slice::( memory_mapping, @@ -1277,9 +1262,7 @@ declare_syscall!( return Err(SyscallError::TooManySlices.into()); } - invoke_context - .get_compute_meter() - .consume(compute_budget.sha256_base_cost)?; + consume_compute_meter(invoke_context, compute_budget.sha256_base_cost)?; let hash_result = translate_slice_mut::( memory_mapping, @@ -1310,7 +1293,7 @@ declare_syscall!( .sha256_byte_cost .saturating_mul((val.len() as u64).saturating_div(2)), ); - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; hasher.hash(bytes); } } @@ -1345,7 +1328,7 @@ declare_syscall!( len / budget.cpi_bytes_per_unit + budget.syscall_base_cost } }; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; if len > MAX_RETURN_DATA as u64 { return Err(SyscallError::ReturnDataTooLarge(len, MAX_RETURN_DATA as u64).into()); @@ -1393,9 +1376,7 @@ declare_syscall!( ) -> Result { let budget = invoke_context.get_compute_budget(); - invoke_context - .get_compute_meter() - .consume(budget.syscall_base_cost)?; + consume_compute_meter(invoke_context, budget.syscall_base_cost)?; let (program_id, return_data) = invoke_context.transaction_context.get_return_data(); length = length.min(return_data.len() as u64); @@ -1413,7 +1394,7 @@ declare_syscall!( (length + size_of::() as u64) / budget.cpi_bytes_per_unit } }; - invoke_context.get_compute_meter().consume(cost)?; + consume_compute_meter(invoke_context, cost)?; let return_data_result = translate_slice_mut::( memory_mapping, @@ -1472,9 +1453,7 @@ declare_syscall!( ) -> Result { let budget = invoke_context.get_compute_budget(); - invoke_context - .get_compute_meter() - .consume(budget.syscall_base_cost)?; + consume_compute_meter(invoke_context, budget.syscall_base_cost)?; let stop_sibling_instruction_search_at_parent = invoke_context .feature_set .is_active(&stop_sibling_instruction_search_at_parent::id()); @@ -1626,9 +1605,7 @@ declare_syscall!( ) -> Result { let budget = invoke_context.get_compute_budget(); - invoke_context - .get_compute_meter() - .consume(budget.syscall_base_cost)?; + consume_compute_meter(invoke_context, budget.syscall_base_cost)?; Ok(invoke_context.get_stack_height() as u64) } @@ -1657,7 +1634,7 @@ mod tests { sysvar::{clock::Clock, epoch_schedule::EpochSchedule, rent::Rent}, transaction_context::TransactionContext, }, - std::{borrow::Cow, str::FromStr}, + std::{borrow::Cow, cell::RefCell, rc::Rc, str::FromStr}, }; macro_rules! assert_access_violation { @@ -1979,10 +1956,7 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(string.len() as u64 - 1); + invoke_context.mock_set_remaining(string.len() as u64 - 1); let mut result = ProgramResult::Ok(0); SyscallPanic::call( &mut invoke_context, @@ -2001,10 +1975,7 @@ mod tests { ), )); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(string.len() as u64); + invoke_context.mock_set_remaining(string.len() as u64); let mut result = ProgramResult::Ok(0); SyscallPanic::call( &mut invoke_context, @@ -2043,10 +2014,7 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(400 - 1); + invoke_context.mock_set_remaining(400 - 1); let mut result = ProgramResult::Ok(0); SyscallLog::call( &mut invoke_context, @@ -2122,10 +2090,7 @@ mod tests { ); let cost = invoke_context.get_compute_budget().log_64_units; - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(cost); + invoke_context.mock_set_remaining(cost); let config = Config::default(); let mut memory_mapping = MemoryMapping::new(vec![], &config).unwrap(); let mut result = ProgramResult::Ok(0); @@ -2189,10 +2154,7 @@ mod tests { ); assert_access_violation!(result, 0x100000001, 32); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(1); + invoke_context.mock_set_remaining(1); let mut result = ProgramResult::Ok(0); SyscallLogPubkey::call( &mut invoke_context, @@ -2211,10 +2173,7 @@ mod tests { ), )); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(cost); + invoke_context.mock_set_remaining(cost); let mut result = ProgramResult::Ok(0); SyscallLogPubkey::call( &mut invoke_context, @@ -2532,19 +2491,16 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining( - (invoke_context.get_compute_budget().sha256_base_cost - + invoke_context.get_compute_budget().mem_op_base_cost.max( - invoke_context - .get_compute_budget() - .sha256_byte_cost - .saturating_mul((bytes1.len() + bytes2.len()) as u64 / 2), - )) - * 4, - ); + invoke_context.mock_set_remaining( + (invoke_context.get_compute_budget().sha256_base_cost + + invoke_context.get_compute_budget().mem_op_base_cost.max( + invoke_context + .get_compute_budget() + .sha256_byte_cost + .saturating_mul((bytes1.len() + bytes2.len()) as u64 / 2), + )) + * 4, + ); let mut result = ProgramResult::Ok(0); SyscallSha256::call( @@ -2661,15 +2617,12 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining( - (invoke_context - .get_compute_budget() - .curve25519_edwards_validate_point_cost) - * 2, - ); + invoke_context.mock_set_remaining( + (invoke_context + .get_compute_budget() + .curve25519_edwards_validate_point_cost) + * 2, + ); let mut result = ProgramResult::Ok(0); SyscallCurvePointValidation::call( @@ -2761,15 +2714,12 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining( - (invoke_context - .get_compute_budget() - .curve25519_ristretto_validate_point_cost) - * 2, - ); + invoke_context.mock_set_remaining( + (invoke_context + .get_compute_budget() + .curve25519_ristretto_validate_point_cost) + * 2, + ); let mut result = ProgramResult::Ok(0); SyscallCurvePointValidation::call( @@ -2895,21 +2845,18 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining( - (invoke_context + invoke_context.mock_set_remaining( + (invoke_context + .get_compute_budget() + .curve25519_edwards_add_cost + + invoke_context .get_compute_budget() - .curve25519_edwards_add_cost - + invoke_context - .get_compute_budget() - .curve25519_edwards_subtract_cost - + invoke_context - .get_compute_budget() - .curve25519_edwards_multiply_cost) - * 2, - ); + .curve25519_edwards_subtract_cost + + invoke_context + .get_compute_budget() + .curve25519_edwards_multiply_cost) + * 2, + ); let mut result = ProgramResult::Ok(0); SyscallCurveGroupOps::call( @@ -3105,21 +3052,18 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining( - (invoke_context + invoke_context.mock_set_remaining( + (invoke_context + .get_compute_budget() + .curve25519_ristretto_add_cost + + invoke_context .get_compute_budget() - .curve25519_ristretto_add_cost - + invoke_context - .get_compute_budget() - .curve25519_ristretto_subtract_cost - + invoke_context - .get_compute_budget() - .curve25519_ristretto_multiply_cost) - * 2, - ); + .curve25519_ristretto_subtract_cost + + invoke_context + .get_compute_budget() + .curve25519_ristretto_multiply_cost) + * 2, + ); let mut result = ProgramResult::Ok(0); SyscallCurveGroupOps::call( @@ -3324,23 +3268,20 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining( - invoke_context + invoke_context.mock_set_remaining( + invoke_context + .get_compute_budget() + .curve25519_edwards_msm_base_cost + + invoke_context .get_compute_budget() - .curve25519_edwards_msm_base_cost - + invoke_context - .get_compute_budget() - .curve25519_edwards_msm_incremental_cost - + invoke_context - .get_compute_budget() - .curve25519_ristretto_msm_base_cost - + invoke_context - .get_compute_budget() - .curve25519_ristretto_msm_incremental_cost, - ); + .curve25519_edwards_msm_incremental_cost + + invoke_context + .get_compute_budget() + .curve25519_ristretto_msm_base_cost + + invoke_context + .get_compute_budget() + .curve25519_ristretto_msm_incremental_cost, + ); let mut result = ProgramResult::Ok(0); SyscallCurveMultiscalarMultiplication::call( @@ -3606,7 +3547,7 @@ mod tests { seeds: &[&[u8]], program_id: &Pubkey, overlap_outputs: bool, - syscall: SyscallFunction<&'a mut InvokeContext<'b>>, + syscall: SyscallFunction>, ) -> Result<(Pubkey, u8), EbpfError> { const SEEDS_VA: u64 = 0x100000000; const PROGRAM_ID_VA: u64 = 0x200000000; @@ -3874,10 +3815,7 @@ mod tests { ) .unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(syscall_base_cost); + invoke_context.mock_set_remaining(syscall_base_cost); let mut result = ProgramResult::Ok(0); SyscallGetProcessedSiblingInstruction::call( &mut invoke_context, @@ -3909,10 +3847,7 @@ mod tests { ); } - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(syscall_base_cost); + invoke_context.mock_set_remaining(syscall_base_cost); let mut result = ProgramResult::Ok(0); SyscallGetProcessedSiblingInstruction::call( &mut invoke_context, @@ -3926,10 +3861,7 @@ mod tests { ); assert_eq!(result.unwrap(), 0); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(syscall_base_cost); + invoke_context.mock_set_remaining(syscall_base_cost); let mut result = ProgramResult::Ok(0); SyscallGetProcessedSiblingInstruction::call( &mut invoke_context, @@ -4056,10 +3988,7 @@ mod tests { .unwrap(), create_program_address(&mut invoke_context, &[b"Talking"], &address).unwrap(), ); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(0); + invoke_context.mock_set_remaining(0); assert!(matches!( create_program_address(&mut invoke_context, &[b"", &[1]], &address), Err(EbpfError::UserError(error)) if error.downcast_ref::().unwrap() == &BpfError::SyscallError( @@ -4084,10 +4013,7 @@ mod tests { for _ in 0..1_000 { let address = Pubkey::new_unique(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(cost * max_tries); + invoke_context.mock_set_remaining(cost * max_tries); let (found_address, bump_seed) = try_find_program_address(&mut invoke_context, &[b"Lil'", b"Bits"], &address) .unwrap(); @@ -4103,21 +4029,12 @@ mod tests { } let seeds: &[&[u8]] = &[b""]; - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(cost * max_tries); + invoke_context.mock_set_remaining(cost * max_tries); let (_, bump_seed) = try_find_program_address(&mut invoke_context, seeds, &address).unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(cost * (max_tries - bump_seed as u64)); + invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64)); try_find_program_address(&mut invoke_context, seeds, &address).unwrap(); - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(cost * (max_tries - bump_seed as u64 - 1)); + invoke_context.mock_set_remaining(cost * (max_tries - bump_seed as u64 - 1)); assert!(matches!( try_find_program_address(&mut invoke_context, seeds, &address), Err(EbpfError::UserError(error)) if error.downcast_ref::().unwrap() == &BpfError::SyscallError( @@ -4126,10 +4043,7 @@ mod tests { )); let exceeded_seed = &[127; MAX_SEED_LEN + 1]; - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(cost * (max_tries - 1)); + invoke_context.mock_set_remaining(cost * (max_tries - 1)); assert!(matches!( try_find_program_address(&mut invoke_context, &[exceeded_seed], &address), Err(EbpfError::UserError(error)) if error.downcast_ref::().unwrap() == &BpfError::SyscallError( @@ -4155,10 +4069,7 @@ mod tests { &[16], &[17], ]; - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(cost * (max_tries - 1)); + invoke_context.mock_set_remaining(cost * (max_tries - 1)); assert!(matches!( try_find_program_address(&mut invoke_context, exceeded_seeds, &address), Err(EbpfError::UserError(error)) if error.downcast_ref::().unwrap() == &BpfError::SyscallError( diff --git a/programs/bpf_loader/src/syscalls/sysvar.rs b/programs/bpf_loader/src/syscalls/sysvar.rs index a1cf50a940..7cf4d440da 100644 --- a/programs/bpf_loader/src/syscalls/sysvar.rs +++ b/programs/bpf_loader/src/syscalls/sysvar.rs @@ -7,7 +7,8 @@ fn get_sysvar( memory_mapping: &mut MemoryMapping, invoke_context: &mut InvokeContext, ) -> Result { - invoke_context.get_compute_meter().consume( + consume_compute_meter( + invoke_context, invoke_context .get_compute_budget() .sysvar_base_cost diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 72777da3b9..6aabc9da17 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -4178,7 +4178,6 @@ dependencies = [ "libsecp256k1 0.6.0", "log", "solana-measure", - "solana-metrics", "solana-program-runtime", "solana-sdk 1.15.0", "solana-zk-token-sdk 1.15.0", @@ -4862,6 +4861,7 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-sdk 1.15.0", + "solana_rbpf", "thiserror", ] @@ -5992,9 +5992,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bcad04ab8dff83d39e5b039e419d03e83dafc6643401700b61acf0cc1589ff8" +checksum = "94fe0b05d69875dc1da1e646aec8284c680b9b3f5f521c8d8f958b2cbc108d0b" dependencies = [ "byteorder 1.4.3", "combine", diff --git a/programs/sbf/Cargo.toml b/programs/sbf/Cargo.toml index 929aed8045..5d4358a203 100644 --- a/programs/sbf/Cargo.toml +++ b/programs/sbf/Cargo.toml @@ -38,7 +38,7 @@ solana-sbf-rust-realloc = { path = "rust/realloc", version = "=1.15.0" } solana-sbf-rust-realloc-invoke = { path = "rust/realloc_invoke", version = "=1.15.0" } solana-sdk = { path = "../../sdk", version = "=1.15.0" } solana-transaction-status = { path = "../../transaction-status", version = "=1.15.0" } -solana_rbpf = "=0.2.35" +solana_rbpf = "=0.2.36" [dev-dependencies] solana-ledger = { path = "../../ledger", version = "=1.15.0" } diff --git a/programs/sbf/benches/bpf_loader.rs b/programs/sbf/benches/bpf_loader.rs index bafd18d2bd..5d722b0cab 100644 --- a/programs/sbf/benches/bpf_loader.rs +++ b/programs/sbf/benches/bpf_loader.rs @@ -9,16 +9,15 @@ use { byteorder::{ByteOrder, LittleEndian, WriteBytesExt}, solana_bpf_loader_program::{ create_vm, serialization::serialize_parameters, syscalls::register_syscalls, - ThisInstructionMeter, }, solana_measure::measure::Measure, - solana_program_runtime::invoke_context::with_mock_invoke_context, + solana_program_runtime::invoke_context::{with_mock_invoke_context, InvokeContext}, solana_rbpf::{ ebpf::MM_INPUT_START, elf::Executable, memory_region::MemoryRegion, verifier::RequisiteVerifier, - vm::{Config, InstructionMeter, SyscallRegistry, VerifiedExecutable}, + vm::{Config, ContextObject, SyscallRegistry, VerifiedExecutable}, }, solana_runtime::{ bank::Bank, @@ -82,7 +81,7 @@ fn bench_program_create_executable(bencher: &mut Bencher) { let elf = load_elf("bench_alu").unwrap(); bencher.iter(|| { - let _ = Executable::::from_elf( + let _ = Executable::::from_elf( &elf, Config::default(), SyscallRegistry::default(), @@ -103,11 +102,7 @@ fn bench_program_alu(bencher: &mut Bencher) { let elf = load_elf("bench_alu").unwrap(); let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 10000001, false, |invoke_context| { - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(std::i64::MAX as u64); - let executable = Executable::::from_elf( + let executable = Executable::::from_elf( &elf, Config::default(), register_syscalls(&invoke_context.feature_set, true).unwrap(), @@ -115,14 +110,10 @@ fn bench_program_alu(bencher: &mut Bencher) { .unwrap(); let mut verified_executable = - VerifiedExecutable::::from_executable( - executable, - ) - .unwrap(); + VerifiedExecutable::::from_executable(executable) + .unwrap(); verified_executable.jit_compile().unwrap(); - let compute_meter = invoke_context.get_compute_meter(); - let mut instruction_meter = ThisInstructionMeter { compute_meter }; let mut vm = create_vm( &verified_executable, vec![MemoryRegion::new_writable(&mut inner_iter, MM_INPUT_START)], @@ -132,11 +123,9 @@ fn bench_program_alu(bencher: &mut Bencher) { .unwrap(); println!("Interpreted:"); - assert_eq!( - SUCCESS, - vm.execute_program_interpreted(&mut instruction_meter) - .unwrap() - ); + vm.context_object.mock_set_remaining(std::i64::MAX as u64); + let (instructions, result) = vm.execute_program(true); + assert_eq!(SUCCESS, result.unwrap()); assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter)); assert_eq!( ARMSTRONG_EXPECTED, @@ -144,10 +133,9 @@ fn bench_program_alu(bencher: &mut Bencher) { ); bencher.iter(|| { - vm.execute_program_interpreted(&mut instruction_meter) - .unwrap(); + vm.context_object.mock_set_remaining(std::i64::MAX as u64); + vm.execute_program(true).1.unwrap(); }); - let instructions = vm.get_total_instruction_count(); let summary = bencher.bench(|_bencher| Ok(())).unwrap().unwrap(); println!(" {:?} instructions", instructions); println!(" {:?} ns/iter median", summary.median as u64); @@ -157,17 +145,17 @@ fn bench_program_alu(bencher: &mut Bencher) { println!("{{ \"type\": \"bench\", \"name\": \"bench_program_alu_interpreted_mips\", \"median\": {:?}, \"deviation\": 0 }}", mips); println!("JIT to native:"); - assert_eq!( - SUCCESS, - vm.execute_program_jit(&mut instruction_meter).unwrap() - ); + assert_eq!(SUCCESS, vm.execute_program(false).1.unwrap()); assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter)); assert_eq!( ARMSTRONG_EXPECTED, LittleEndian::read_u64(&inner_iter[mem::size_of::()..]) ); - bencher.iter(|| vm.execute_program_jit(&mut instruction_meter).unwrap()); + bencher.iter(|| { + vm.context_object.mock_set_remaining(std::i64::MAX as u64); + vm.execute_program(false).1.unwrap(); + }); let summary = bencher.bench(|_bencher| Ok(())).unwrap().unwrap(); println!(" {:?} instructions", instructions); println!(" {:?} ns/iter median", summary.median as u64); @@ -219,10 +207,7 @@ fn bench_create_vm(bencher: &mut Bencher) { let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 10000001, false, |invoke_context| { const BUDGET: u64 = 200_000; - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(BUDGET); + invoke_context.mock_set_remaining(BUDGET); // Serialize account data let (_serialized, regions, account_lengths) = serialize_parameters( @@ -235,7 +220,7 @@ fn bench_create_vm(bencher: &mut Bencher) { ) .unwrap(); - let executable = Executable::::from_elf( + let executable = Executable::::from_elf( &elf, Config::default(), register_syscalls(&invoke_context.feature_set, true).unwrap(), @@ -243,10 +228,8 @@ fn bench_create_vm(bencher: &mut Bencher) { .unwrap(); let verified_executable = - VerifiedExecutable::::from_executable( - executable, - ) - .unwrap(); + VerifiedExecutable::::from_executable(executable) + .unwrap(); bencher.iter(|| { let _ = create_vm( @@ -266,10 +249,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 10000001, true, |invoke_context| { const BUDGET: u64 = 200_000; - invoke_context - .get_compute_meter() - .borrow_mut() - .mock_set_remaining(BUDGET); + invoke_context.mock_set_remaining(BUDGET); // Serialize account data let (_serialized, regions, account_lengths) = serialize_parameters( @@ -282,7 +262,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { ) .unwrap(); - let executable = Executable::::from_elf( + let executable = Executable::::from_elf( &elf, Config::default(), register_syscalls(&invoke_context.feature_set, true).unwrap(), @@ -290,13 +270,9 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { .unwrap(); let verified_executable = - VerifiedExecutable::::from_executable( - executable, - ) - .unwrap(); + VerifiedExecutable::::from_executable(executable) + .unwrap(); - let compute_meter = invoke_context.get_compute_meter(); - let mut instruction_meter = ThisInstructionMeter { compute_meter }; let mut vm = create_vm( &verified_executable, regions, @@ -306,19 +282,19 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { .unwrap(); let mut measure = Measure::start("tune"); - let _ = vm.execute_program_interpreted(&mut instruction_meter); + let (instructions, _result) = vm.execute_program(true); measure.stop(); assert_eq!( 0, - instruction_meter.get_remaining(), + vm.context_object.get_remaining(), "Tuner must consume the whole budget" ); println!( "{:?} compute units took {:?} us ({:?} instructions)", - BUDGET - instruction_meter.get_remaining(), + BUDGET - vm.context_object.get_remaining(), measure.as_us(), - vm.get_total_instruction_count(), + instructions, ); }); } diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index d13e2ffb54..3d8684a08d 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -12,6 +12,7 @@ use { solana_ledger::token_balances::collect_token_balances, solana_program_runtime::{ compute_budget::{self, ComputeBudget}, + invoke_context::InvokeContext, timings::ExecuteTimings, }, solana_runtime::{ @@ -52,19 +53,16 @@ use { std::{collections::HashMap, str::FromStr}, }; use { - log::{log_enabled, trace, Level::Trace}, solana_bpf_loader_program::{ create_vm, serialization::{deserialize_parameters, serialize_parameters}, syscalls::register_syscalls, - ThisInstructionMeter, }, solana_program_runtime::invoke_context::with_mock_invoke_context, solana_rbpf::{ elf::Executable, - static_analysis::Analysis, verifier::RequisiteVerifier, - vm::{Config, Tracer, VerifiedExecutable}, + vm::{Config, VerifiedExecutable}, }, solana_runtime::{ bank::Bank, @@ -226,14 +224,12 @@ fn run_program(name: &str) -> u64 { file.read_to_end(&mut data).unwrap(); let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 0, false, |invoke_context| { - let compute_meter = invoke_context.get_compute_meter(); - let mut instruction_meter = ThisInstructionMeter { compute_meter }; let config = Config { enable_instruction_tracing: true, reject_broken_elfs: true, ..Config::default() }; - let executable = Executable::::from_elf( + let executable = Executable::::from_elf( &data, config, register_syscalls( @@ -246,10 +242,8 @@ fn run_program(name: &str) -> u64 { #[allow(unused_mut)] let mut verified_executable = - VerifiedExecutable::::from_executable( - executable, - ) - .unwrap(); + VerifiedExecutable::::from_executable(executable) + .unwrap(); let run_program_iterations = { #[cfg(target_arch = "x86_64")] @@ -262,7 +256,7 @@ fn run_program(name: &str) -> u64 { }; let mut instruction_count = 0; - let mut tracer = None; + let mut trace_log = None; for i in 0..run_program_iterations { let transaction_context = &mut invoke_context.transaction_context; let instruction_context = transaction_context @@ -293,53 +287,24 @@ fn run_program(name: &str) -> u64 { invoke_context, ) .unwrap(); - let result = if i == 0 { - vm.execute_program_interpreted(&mut instruction_meter) - } else { - vm.execute_program_jit(&mut instruction_meter) - }; + let (compute_units_consumed, result) = vm.execute_program(i == 0); assert_eq!(SUCCESS, result.unwrap()); if i == 1 { - assert_eq!(instruction_count, vm.get_total_instruction_count()); + assert_eq!(instruction_count, compute_units_consumed); } - instruction_count = vm.get_total_instruction_count(); + instruction_count = compute_units_consumed; if config.enable_instruction_tracing { - if i == 1 { - if !Tracer::compare( - tracer.as_ref().unwrap(), - &vm.get_program_environment().tracer, - ) { - let analysis = - Analysis::from_executable(verified_executable.get_executable()) - .unwrap(); - let stdout = std::io::stdout(); - println!("TRACE (interpreted):"); - tracer - .as_ref() - .unwrap() - .write(&mut stdout.lock(), &analysis) - .unwrap(); - println!("TRACE (jit):"); - vm.get_program_environment() - .tracer - .write(&mut stdout.lock(), &analysis) - .unwrap(); - assert!(false); - } else if log_enabled!(Trace) { - let analysis = - Analysis::from_executable(verified_executable.get_executable()) - .unwrap(); - let mut trace_buffer = Vec::::new(); - tracer - .as_ref() - .unwrap() - .write(&mut trace_buffer, &analysis) - .unwrap(); - let trace_string = String::from_utf8(trace_buffer).unwrap(); - trace!("SBF Program Instruction Trace:\n{}", trace_string); + if i == 0 { + trace_log = Some(vm.context_object.trace_log.clone()); + } else { + let interpreter = trace_log.as_ref().unwrap().as_slice(); + let mut jit = vm.context_object.trace_log.as_slice(); + if jit.len() > interpreter.len() { + jit = &jit[0..interpreter.len()]; } + assert_eq!(interpreter, jit); + trace_log = None; } - tracer = Some(vm.get_program_environment().tracer.clone()); } } assert!(match deserialize_parameters( diff --git a/programs/zk-token-proof/src/lib.rs b/programs/zk-token-proof/src/lib.rs index df92342ad4..7323f98b60 100644 --- a/programs/zk-token-proof/src/lib.rs +++ b/programs/zk-token-proof/src/lib.rs @@ -39,9 +39,8 @@ pub fn process_instruction( // Consume compute units since proof verification is an expensive operation { - let compute_meter = invoke_context.get_compute_meter(); // TODO: Tune the number of units consumed. The current value is just a rough estimate - compute_meter.borrow_mut().consume(100_000)?; + invoke_context.consume_checked(100_000)?; } let transaction_context = &invoke_context.transaction_context; diff --git a/rbpf-cli/Cargo.toml b/rbpf-cli/Cargo.toml index e17380177d..2d1fe81d1d 100644 --- a/rbpf-cli/Cargo.toml +++ b/rbpf-cli/Cargo.toml @@ -17,4 +17,4 @@ solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.15. solana-logger = { path = "../logger", version = "=1.15.0" } solana-program-runtime = { path = "../program-runtime", version = "=1.15.0" } solana-sdk = { path = "../sdk", version = "=1.15.0" } -solana_rbpf = "=0.2.35" +solana_rbpf = { version = "=0.2.36", features = ["debugger"] } diff --git a/rbpf-cli/src/main.rs b/rbpf-cli/src/main.rs index 80f09b606f..0859923105 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -4,15 +4,16 @@ use { serde_json::Result, solana_bpf_loader_program::{ create_vm, serialization::serialize_parameters, syscalls::register_syscalls, - ThisInstructionMeter, }, solana_program_runtime::invoke_context::{prepare_mock_invoke_context, InvokeContext}, solana_rbpf::{ assembler::assemble, + debugger, elf::Executable, + interpreter::Interpreter, static_analysis::Analysis, verifier::RequisiteVerifier, - vm::{Config, DynamicAnalysis, VerifiedExecutable}, + vm::{Config, VerifiedExecutable}, }, solana_sdk::{ account::AccountSharedData, bpf_loader, instruction::AccountMeta, pubkey::Pubkey, @@ -121,14 +122,15 @@ with input data, or BYTES is the number of 0-valued bytes to allocate for progra .help( "Method of execution to use, where 'cfg' generates Control Flow Graph \ of the program, 'disassembler' dumps disassembled code of the program, 'interpreter' runs \ -the program in the virtual machine's interpreter, and 'jit' precompiles the program to \ -native machine code before execting it in the virtual machine.", +the program in the virtual machine's interpreter, 'debugger' is the same as 'interpreter' \ +but hosts a GDB interface, and 'jit' precompiles the program to native machine code \ +before execting it in the virtual machine.", ) .short('u') .long("use") .takes_value(true) .value_name("VALUE") - .possible_values(["cfg", "disassembler", "interpreter", "jit"]) + .possible_values(["cfg", "disassembler", "interpreter", "debugger", "jit"]) .default_value("jit"), ) .arg( @@ -141,16 +143,12 @@ native machine code before execting it in the virtual machine.", .default_value(&std::i64::MAX.to_string()), ) .arg( - Arg::new("trace") - .help("Output trace to 'trace.out' file using tracing instrumentation") - .short('t') - .long("trace"), - ) - .arg( - Arg::new("profile") - .help("Output profile to 'profile.dot' file using tracing instrumentation") - .short('p') - .long("profile"), + Arg::new("port") + .help("Port to use for the connection with a remote debugger") + .long("port") + .takes_value(true) + .value_name("PORT") + .default_value("9001"), ) .arg( Arg::new("output_format") @@ -164,7 +162,6 @@ native machine code before execting it in the virtual machine.", .get_matches(); let config = Config { - enable_instruction_tracing: matches.is_present("trace") || matches.is_present("profile"), enable_symbol_and_section_labels: true, ..Config::default() }; @@ -242,8 +239,6 @@ native machine code before execting it in the virtual machine.", true, // should_cap_ix_accounts ) .unwrap(); - let compute_meter = invoke_context.get_compute_meter(); - let mut instruction_meter = ThisInstructionMeter { compute_meter }; let program = matches.value_of("PROGRAM").unwrap(); let mut file = File::open(Path::new(program)).unwrap(); @@ -254,10 +249,10 @@ native machine code before execting it in the virtual machine.", file.read_to_end(&mut contents).unwrap(); let syscall_registry = register_syscalls(&invoke_context.feature_set, true).unwrap(); let executable = if magic == [0x7f, 0x45, 0x4c, 0x46] { - Executable::::from_elf(&contents, config, syscall_registry) + Executable::::from_elf(&contents, config, syscall_registry) .map_err(|err| format!("Executable constructor failed: {:?}", err)) } else { - assemble::( + assemble::( std::str::from_utf8(contents.as_slice()).unwrap(), config, syscall_registry, @@ -266,7 +261,7 @@ native machine code before execting it in the virtual machine.", .unwrap(); let mut verified_executable = - VerifiedExecutable::::from_executable(executable) + VerifiedExecutable::::from_executable(executable) .map_err(|err| format!("Executable verifier failed: {:?}", err)) .unwrap(); @@ -298,33 +293,14 @@ native machine code before execting it in the virtual machine.", ) .unwrap(); let start_time = Instant::now(); - let result = if matches.value_of("use").unwrap() == "interpreter" { - vm.execute_program_interpreted(&mut instruction_meter) + let (instruction_count, result) = if matches.value_of("use").unwrap() == "debugger" { + let mut interpreter = Interpreter::new(&mut vm).unwrap(); + let port = matches.value_of("port").unwrap().parse::().unwrap(); + debugger::execute(&mut interpreter, port) } else { - vm.execute_program_jit(&mut instruction_meter) + vm.execute_program(matches.value_of("use").unwrap() == "interpreter") }; let duration = Instant::now() - start_time; - - if matches.is_present("trace") { - eprintln!("Trace is saved in trace.out"); - let mut file = File::create("trace.out").unwrap(); - vm.get_program_environment() - .tracer - .write(&mut file, analysis.analyze()) - .unwrap(); - } - if matches.is_present("profile") { - eprintln!("Profile is saved in profile.dot"); - let tracer = &vm.get_program_environment().tracer; - let analysis = analysis.analyze(); - let dynamic_analysis = DynamicAnalysis::new(tracer, analysis); - let mut file = File::create("profile.dot").unwrap(); - analysis - .visualize_graphically(&mut file, Some(&dynamic_analysis)) - .unwrap(); - } - - let instruction_count = vm.get_total_instruction_count(); drop(vm); let output = Output { @@ -374,20 +350,20 @@ impl Debug for Output { // Replace with std::lazy::Lazy when stabilized. // https://github.com/rust-lang/rust/issues/74465 -struct LazyAnalysis<'a> { - analysis: Option>, - executable: &'a Executable, +struct LazyAnalysis<'a, 'b> { + analysis: Option>>, + executable: &'a Executable>, } -impl<'a> LazyAnalysis<'a> { - fn new(executable: &'a Executable) -> Self { +impl<'a, 'b> LazyAnalysis<'a, 'b> { + fn new(executable: &'a Executable>) -> Self { Self { analysis: None, executable, } } - fn analyze(&mut self) -> &Analysis { + fn analyze(&mut self) -> &Analysis> { if let Some(ref analysis) = self.analysis { return analysis; }