Add BPF test program instruction monitoring (#11984)

This commit is contained in:
Jack May 2020-09-01 17:25:55 -07:00 committed by GitHub
parent 213196212e
commit fab2d44abd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 163 additions and 9 deletions

View File

@ -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());
}

View File

@ -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, &parameter_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, &parameter_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
}
}