Add BPF test program instruction monitoring (#11984)
This commit is contained in:
parent
213196212e
commit
fab2d44abd
|
@ -35,7 +35,7 @@ fn rerun_if_changed(files: &[&str], directories: &[&str], excludes: &[&str]) {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let bpf_c = !env::var("CARGO_FEATURE_BPF_C").is_err();
|
||||
let bpf_c = env::var("CARGO_FEATURE_BPF_C").is_ok();
|
||||
if bpf_c {
|
||||
let install_dir =
|
||||
"OUT_DIR=../target/".to_string() + &env::var("PROFILE").unwrap() + &"/bpf".to_string();
|
||||
|
@ -52,7 +52,7 @@ fn main() {
|
|||
rerun_if_changed(&["c/makefile"], &["c/src", "../../sdk"], &["/target/"]);
|
||||
}
|
||||
|
||||
let bpf_rust = !env::var("CARGO_FEATURE_BPF_RUST").is_err();
|
||||
let bpf_rust = env::var("CARGO_FEATURE_BPF_RUST").is_ok();
|
||||
if bpf_rust {
|
||||
let install_dir =
|
||||
"target/".to_string() + &env::var("PROFILE").unwrap() + &"/bpf".to_string();
|
||||
|
@ -100,7 +100,7 @@ fn main() {
|
|||
.arg(&src)
|
||||
.arg(&install_dir)
|
||||
.status()
|
||||
.expect(&format!("Failed to cp {} to {}", src, install_dir))
|
||||
.unwrap_or_else(|_| panic!("Failed to cp {} to {}", src, install_dir))
|
||||
.success());
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
#[macro_use]
|
||||
extern crate solana_bpf_loader_program;
|
||||
|
||||
use solana_bpf_loader_program::{
|
||||
create_vm,
|
||||
serialization::{deserialize_parameters, serialize_parameters},
|
||||
};
|
||||
use solana_rbpf::InstructionMeter;
|
||||
use solana_runtime::{
|
||||
bank::Bank,
|
||||
bank_client::BankClient,
|
||||
|
@ -10,20 +15,20 @@ use solana_runtime::{
|
|||
loader_utils::load_program,
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
account::{Account, KeyedAccount},
|
||||
bpf_loader, bpf_loader_deprecated,
|
||||
client::SyncClient,
|
||||
clock::DEFAULT_SLOTS_PER_EPOCH,
|
||||
entrypoint::MAX_PERMITTED_DATA_INCREASE,
|
||||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||
entrypoint_native::{ComputeBudget, ComputeMeter, InvokeContext, Logger, ProcessInstruction},
|
||||
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::Keypair,
|
||||
signature::Signer,
|
||||
signature::{Keypair, Signer},
|
||||
sysvar::{clock, fees, rent, rewards, slot_hashes, stake_history},
|
||||
transaction::TransactionError,
|
||||
};
|
||||
use std::{env, fs::File, io::Read, path::PathBuf, sync::Arc};
|
||||
use std::{cell::RefCell, env, fs::File, io::Read, path::PathBuf, rc::Rc, sync::Arc};
|
||||
|
||||
/// BPF program file extension
|
||||
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
||||
|
@ -53,6 +58,42 @@ fn load_bpf_program(
|
|||
load_program(bank_client, payer_keypair, loader_id, elf)
|
||||
}
|
||||
|
||||
fn run_program(
|
||||
name: &str,
|
||||
program_id: &Pubkey,
|
||||
parameter_accounts: &[KeyedAccount],
|
||||
instruction_data: &[u8],
|
||||
) -> Result<u64, InstructionError> {
|
||||
let path = create_bpf_path(name);
|
||||
let mut file = File::open(path).unwrap();
|
||||
|
||||
let mut program_account = Account::default();
|
||||
file.read_to_end(&mut program_account.data).unwrap();
|
||||
|
||||
let mut invoke_context = MockInvokeContext::default();
|
||||
let (mut vm, heap_region) = create_vm(
|
||||
&bpf_loader::id(),
|
||||
&program_account.data,
|
||||
parameter_accounts,
|
||||
&mut invoke_context,
|
||||
)
|
||||
.unwrap();
|
||||
let mut parameter_bytes = serialize_parameters(
|
||||
&bpf_loader::id(),
|
||||
program_id,
|
||||
parameter_accounts,
|
||||
&instruction_data,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
SUCCESS,
|
||||
vm.execute_program(parameter_bytes.as_mut_slice(), &[], &[heap_region.clone()])
|
||||
.unwrap()
|
||||
);
|
||||
deserialize_parameters(&bpf_loader::id(), parameter_accounts, ¶meter_bytes).unwrap();
|
||||
Ok(vm.get_total_instruction_count())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
||||
fn test_program_bpf_sanity() {
|
||||
|
@ -506,3 +547,116 @@ fn test_program_bpf_invoke() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_instruction_count() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mut programs = Vec::new();
|
||||
#[cfg(feature = "bpf_c")]
|
||||
{
|
||||
programs.extend_from_slice(&[
|
||||
("bpf_to_bpf", 13),
|
||||
("multiple_static", 8),
|
||||
("noop", 1140),
|
||||
("noop++", 1140),
|
||||
("relative_call", 10),
|
||||
("struct_pass", 8),
|
||||
("struct_ret", 22),
|
||||
]);
|
||||
}
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
{
|
||||
programs.extend_from_slice(&[
|
||||
("solana_bpf_rust_128bit", 543),
|
||||
("solana_bpf_rust_alloc", 19082),
|
||||
("solana_bpf_rust_dep_crate", 2),
|
||||
("solana_bpf_rust_external_spend", 465),
|
||||
("solana_bpf_rust_iter", 723),
|
||||
("solana_bpf_rust_many_args", 231),
|
||||
("solana_bpf_rust_noop", 2209),
|
||||
("solana_bpf_rust_param_passing", 54),
|
||||
]);
|
||||
}
|
||||
|
||||
for program in programs.iter() {
|
||||
println!("Test program: {:?}", program.0);
|
||||
let program_id = Pubkey::new_rand();
|
||||
let key = Pubkey::new_rand();
|
||||
let mut account = RefCell::new(Account::default());
|
||||
let parameter_accounts = vec![KeyedAccount::new(&key, false, &mut account)];
|
||||
let count = run_program(program.0, &program_id, ¶meter_accounts[..], &[]).unwrap();
|
||||
println!(" {} : {:?} ({:?})", program.0, count, program.1,);
|
||||
assert!(count <= program.1);
|
||||
}
|
||||
}
|
||||
|
||||
// Mock InvokeContext
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct MockInvokeContext {
|
||||
pub key: Pubkey,
|
||||
pub logger: MockLogger,
|
||||
pub compute_meter: MockComputeMeter,
|
||||
}
|
||||
impl InvokeContext for MockInvokeContext {
|
||||
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
|
||||
Ok(())
|
||||
}
|
||||
fn pop(&mut self) {}
|
||||
fn verify_and_update(
|
||||
&mut self,
|
||||
_message: &Message,
|
||||
_instruction: &CompiledInstruction,
|
||||
_accounts: &[Rc<RefCell<Account>>],
|
||||
) -> Result<(), InstructionError> {
|
||||
Ok(())
|
||||
}
|
||||
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
|
||||
Ok(&self.key)
|
||||
}
|
||||
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
|
||||
&[]
|
||||
}
|
||||
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
|
||||
Rc::new(RefCell::new(self.logger.clone()))
|
||||
}
|
||||
fn is_cross_program_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn get_compute_budget(&self) -> ComputeBudget {
|
||||
ComputeBudget::default()
|
||||
}
|
||||
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
|
||||
Rc::new(RefCell::new(self.compute_meter.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
struct MockComputeMeter {}
|
||||
impl ComputeMeter for MockComputeMeter {
|
||||
fn consume(&mut self, _amount: u64) -> Result<(), InstructionError> {
|
||||
Ok(())
|
||||
}
|
||||
fn get_remaining(&self) -> u64 {
|
||||
u64::MAX
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default, Clone)]
|
||||
struct MockLogger {}
|
||||
impl Logger for MockLogger {
|
||||
fn log_enabled(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn log(&mut self, _message: &str) {
|
||||
// println!("{}", message);
|
||||
}
|
||||
}
|
||||
|
||||
struct TestInstructionMeter {}
|
||||
impl InstructionMeter for TestInstructionMeter {
|
||||
fn consume(&mut self, _amount: u64) {}
|
||||
fn get_remaining(&self) -> u64 {
|
||||
u64::MAX
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue