Add BPF test program instruction monitoring (#11984)
This commit is contained in:
@ -35,7 +35,7 @@ fn rerun_if_changed(files: &[&str], directories: &[&str], excludes: &[&str]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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 {
|
if bpf_c {
|
||||||
let install_dir =
|
let install_dir =
|
||||||
"OUT_DIR=../target/".to_string() + &env::var("PROFILE").unwrap() + &"/bpf".to_string();
|
"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/"]);
|
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 {
|
if bpf_rust {
|
||||||
let install_dir =
|
let install_dir =
|
||||||
"target/".to_string() + &env::var("PROFILE").unwrap() + &"/bpf".to_string();
|
"target/".to_string() + &env::var("PROFILE").unwrap() + &"/bpf".to_string();
|
||||||
@ -100,7 +100,7 @@ fn main() {
|
|||||||
.arg(&src)
|
.arg(&src)
|
||||||
.arg(&install_dir)
|
.arg(&install_dir)
|
||||||
.status()
|
.status()
|
||||||
.expect(&format!("Failed to cp {} to {}", src, install_dir))
|
.unwrap_or_else(|_| panic!("Failed to cp {} to {}", src, install_dir))
|
||||||
.success());
|
.success());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate solana_bpf_loader_program;
|
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::{
|
use solana_runtime::{
|
||||||
bank::Bank,
|
bank::Bank,
|
||||||
bank_client::BankClient,
|
bank_client::BankClient,
|
||||||
@ -10,20 +15,20 @@ use solana_runtime::{
|
|||||||
loader_utils::load_program,
|
loader_utils::load_program,
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::{Account, KeyedAccount},
|
||||||
bpf_loader, bpf_loader_deprecated,
|
bpf_loader, bpf_loader_deprecated,
|
||||||
client::SyncClient,
|
client::SyncClient,
|
||||||
clock::DEFAULT_SLOTS_PER_EPOCH,
|
clock::DEFAULT_SLOTS_PER_EPOCH,
|
||||||
entrypoint::MAX_PERMITTED_DATA_INCREASE,
|
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||||
instruction::{AccountMeta, Instruction, InstructionError},
|
entrypoint_native::{ComputeBudget, ComputeMeter, InvokeContext, Logger, ProcessInstruction},
|
||||||
|
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::Keypair,
|
signature::{Keypair, Signer},
|
||||||
signature::Signer,
|
|
||||||
sysvar::{clock, fees, rent, rewards, slot_hashes, stake_history},
|
sysvar::{clock, fees, rent, rewards, slot_hashes, stake_history},
|
||||||
transaction::TransactionError,
|
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
|
/// BPF program file extension
|
||||||
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
||||||
@ -53,6 +58,42 @@ fn load_bpf_program(
|
|||||||
load_program(bank_client, payer_keypair, loader_id, elf)
|
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]
|
#[test]
|
||||||
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
||||||
fn test_program_bpf_sanity() {
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user