Rbpf v0.2.1 (#13732)

* Refactoring to match rbpf-v0.2.1 and add JIT compilation caching.

* Removes obsolete bpf-trace.py which has been replaced by the rbpf CLI tool.
This commit is contained in:
Alexander Meißner
2020-11-24 18:00:19 +01:00
committed by GitHub
parent 32cb8d1462
commit c833ede4af
11 changed files with 833 additions and 753 deletions

View File

@ -57,9 +57,9 @@ checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "ascii"
version = "0.7.1"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50"
checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e"
[[package]]
name = "assert_matches"
@ -289,12 +289,15 @@ dependencies = [
[[package]]
name = "combine"
version = "2.5.2"
version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1645a65a99c7c8d345761f4b75a6ffe5be3b3b27a93ee731fccc5050ba6be97c"
checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680"
dependencies = [
"ascii",
"byteorder 1.3.4",
"either",
"memchr",
"unreachable",
]
[[package]]
@ -1475,6 +1478,7 @@ dependencies = [
"rand_chacha",
"rand_core",
"rand_hc",
"rand_pcg",
]
[[package]]
@ -1505,6 +1509,15 @@ dependencies = [
"rand_core",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core",
]
[[package]]
name = "rayon"
version = "1.4.1"
@ -2347,9 +2360,9 @@ dependencies = [
[[package]]
name = "solana_rbpf"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09e18fa1e1b6cac20a5f2571882b7580a65ae685b1413fc498f975d07d4c968f"
checksum = "8fad65297e2bd080468cb57a97e6a62e9a92928a45960496338e997e8270fa00"
dependencies = [
"byteorder 1.3.4",
"combine",
@ -2805,6 +2818,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
dependencies = [
"void",
]
[[package]]
name = "untrusted"
version = "0.7.1"
@ -2828,6 +2850,12 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "walkdir"
version = "2.3.1"

View File

@ -27,7 +27,7 @@ solana-logger = { path = "../../logger", version = "1.5.0" }
solana-measure = { path = "../../measure", version = "1.5.0" }
solana-runtime = { path = "../../runtime", version = "1.5.0" }
solana-sdk = { path = "../../sdk", version = "1.5.0" }
solana_rbpf = "=0.2.0"
solana_rbpf = "=0.2.1"
[[bench]]
name = "bpf_loader"

View File

@ -6,9 +6,12 @@ extern crate test;
extern crate solana_bpf_loader_program;
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use solana_bpf_loader_program::ThisInstructionMeter;
use solana_bpf_loader_program::{
create_vm, serialization::serialize_parameters, syscalls::register_syscalls, BPFError,
ThisInstructionMeter,
};
use solana_measure::measure::Measure;
use solana_rbpf::vm::{Executable, InstructionMeter};
use solana_rbpf::vm::{Config, Executable, InstructionMeter};
use solana_runtime::{
bank::Bank,
bank_client::BankClient,
@ -72,7 +75,9 @@ fn bench_program_create_executable(bencher: &mut Bencher) {
let elf = load_elf("bench_alu").unwrap();
bencher.iter(|| {
let _ = Executable::<solana_bpf_loader_program::BPFError>::from_elf(&elf, None).unwrap();
let _ =
Executable::<BPFError, ThisInstructionMeter>::from_elf(&elf, None, Config::default())
.unwrap();
});
}
@ -89,11 +94,14 @@ fn bench_program_alu(bencher: &mut Bencher) {
let mut invoke_context = MockInvokeContext::default();
let elf = load_elf("bench_alu").unwrap();
let executable =
Executable::<solana_bpf_loader_program::BPFError>::from_elf(&elf, None).unwrap();
let mut executable =
Executable::<BPFError, ThisInstructionMeter>::from_elf(&elf, None, Config::default())
.unwrap();
executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap());
executable.jit_compile().unwrap();
let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter };
let mut vm = solana_bpf_loader_program::create_vm(
let mut vm = create_vm(
&loader_id,
executable.as_ref(),
&mut inner_iter,
@ -128,22 +136,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:");
vm.jit_compile().unwrap();
unsafe {
assert_eq!(
SUCCESS,
vm.execute_program_jit(&mut instruction_meter).unwrap()
);
}
assert_eq!(
SUCCESS,
vm.execute_program_jit(&mut instruction_meter).unwrap()
);
assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter));
assert_eq!(
ARMSTRONG_EXPECTED,
LittleEndian::read_u64(&inner_iter[mem::size_of::<u64>()..])
);
bencher.iter(|| unsafe {
vm.execute_program_jit(&mut instruction_meter).unwrap();
});
bencher.iter(|| vm.execute_program_jit(&mut instruction_meter).unwrap());
let summary = bencher.bench(|_bencher| {}).unwrap();
println!(" {:?} instructions", instructions);
println!(" {:?} ns/iter median", summary.median as u64);
@ -208,7 +211,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
let instruction_data = vec![0u8];
// Serialize account data
let serialized = solana_bpf_loader_program::serialization::serialize_parameters(
let mut serialized = serialize_parameters(
&bpf_loader::id(),
&solana_sdk::pubkey::new_rand(),
&keyed_accounts,
@ -217,14 +220,16 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
.unwrap();
let elf = load_elf("tuner").unwrap();
let executable =
Executable::<solana_bpf_loader_program::BPFError>::from_elf(&elf, None).unwrap();
let mut executable =
Executable::<BPFError, ThisInstructionMeter>::from_elf(&elf, None, Config::default())
.unwrap();
executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap());
let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter };
let mut vm = solana_bpf_loader_program::create_vm(
let mut vm = create_vm(
&loader_id,
executable.as_ref(),
&serialized,
&mut serialized,
&[],
&mut invoke_context,
)

View File

@ -6,9 +6,10 @@ extern crate solana_bpf_loader_program;
use solana_bpf_loader_program::{
create_vm,
serialization::{deserialize_parameters, serialize_parameters},
syscalls::register_syscalls,
ThisInstructionMeter,
};
use solana_rbpf::vm::Executable;
use solana_rbpf::vm::{Config, Executable, Tracer};
use solana_runtime::{
bank::Bank,
bank_client::BankClient,
@ -85,23 +86,62 @@ fn run_program(
let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter };
let executable = Executable::from_elf(&data, None).unwrap();
let mut vm = create_vm(
&loader_id,
executable.as_ref(),
&parameter_bytes,
parameter_accounts,
&mut invoke_context,
)
.unwrap();
let config = Config {
max_call_depth: 20,
stack_frame_size: 4096,
enable_instruction_meter: true,
enable_instruction_tracing: true,
};
let mut executable = Executable::from_elf(&data, None, config).unwrap();
executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap());
executable.jit_compile().unwrap();
assert_eq!(
SUCCESS,
vm.execute_program_interpreted(&mut instruction_meter)
.unwrap()
);
deserialize_parameters(&bpf_loader::id(), parameter_accounts, &parameter_bytes).unwrap();
Ok(vm.get_total_instruction_count())
let mut instruction_count = 0;
let mut tracer = None;
for i in 0..2 {
let mut parameter_bytes = parameter_bytes.clone();
let mut vm = create_vm(
&loader_id,
executable.as_ref(),
&mut parameter_bytes,
parameter_accounts,
&mut invoke_context,
)
.unwrap();
let result = if i == 0 {
vm.execute_program_interpreted(&mut instruction_meter)
} else {
vm.execute_program_jit(&mut instruction_meter)
};
assert_eq!(SUCCESS, result.unwrap());
deserialize_parameters(&bpf_loader::id(), parameter_accounts, &parameter_bytes).unwrap();
if i == 1 {
assert_eq!(instruction_count, vm.get_total_instruction_count());
}
instruction_count = vm.get_total_instruction_count();
if config.enable_instruction_tracing {
if i == 1 {
if !Tracer::compare(tracer.as_ref().unwrap(), vm.get_tracer()) {
let mut tracer_display = String::new();
tracer
.as_ref()
.unwrap()
.write(&mut tracer_display, vm.get_program())
.unwrap();
println!("TRACE (interpreted): {}", tracer_display);
let mut tracer_display = String::new();
vm.get_tracer()
.write(&mut tracer_display, vm.get_program())
.unwrap();
println!("TRACE (jit): {}", tracer_display);
assert!(false);
}
}
tracer = Some(vm.get_tracer().clone());
}
}
Ok(instruction_count)
}
fn process_transaction_and_record_inner(

View File

@ -16,7 +16,7 @@ num-derive = "0.3"
num-traits = "0.2"
solana-runtime = { path = "../../runtime", version = "1.5.0" }
solana-sdk = { path = "../../sdk", version = "1.5.0" }
solana_rbpf = "=0.2.0"
solana_rbpf = "=0.2.1"
thiserror = "1.0"
[dev-dependencies]

View File

@ -45,6 +45,8 @@ pub enum BPFLoaderError {
VirtualMachineCreationFailed = 0x0b9f_0001,
#[error("virtual machine failed to run the program to completion")]
VirtualMachineFailedToRunProgram = 0x0b9f_0002,
#[error("failed to compile program")]
JustInTimeCompilationFailed = 0x0b9f_0003,
}
impl<E> DecodeError<E> for BPFLoaderError {
fn type_of() -> &'static str {
@ -89,12 +91,24 @@ fn map_ebpf_error(
InstructionError::InvalidAccountData
}
const IS_JIT_ENABLED: bool = false;
pub fn create_and_cache_executor(
program: &KeyedAccount,
invoke_context: &mut dyn InvokeContext,
) -> Result<Arc<BPFExecutor>, InstructionError> {
let executable = Executable::from_elf(&program.try_account_ref()?.data, None)
.map_err(|e| map_ebpf_error(invoke_context, e))?;
let bpf_compute_budget = invoke_context.get_bpf_compute_budget();
let mut executable = Executable::<BPFError, ThisInstructionMeter>::from_elf(
&program.try_account_ref()?.data,
None,
Config {
max_call_depth: bpf_compute_budget.max_call_depth,
stack_frame_size: bpf_compute_budget.stack_frame_size,
enable_instruction_meter: true,
enable_instruction_tracing: false,
},
)
.map_err(|e| map_ebpf_error(invoke_context, e))?;
let (_, elf_bytes) = executable
.get_text_bytes()
.map_err(|e| map_ebpf_error(invoke_context, e))?;
@ -103,6 +117,12 @@ pub fn create_and_cache_executor(
!invoke_context.is_feature_active(&bpf_compute_budget_balancing::id()),
)
.map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e)))?;
let syscall_registry = syscalls::register_syscalls(invoke_context)
.map_err(|e| map_ebpf_error(invoke_context, e))?;
executable.set_syscall_registry(syscall_registry);
if IS_JIT_ENABLED && executable.jit_compile().is_err() {
return Err(BPFLoaderError::JustInTimeCompilationFailed.into());
}
let executor = Arc::new(BPFExecutor { executable });
invoke_context.add_executor(program.unsigned_key(), executor.clone());
Ok(executor)
@ -115,24 +135,21 @@ const DEFAULT_HEAP_SIZE: usize = 32 * 1024;
/// Create the BPF virtual machine
pub fn create_vm<'a>(
loader_id: &'a Pubkey,
executable: &'a dyn Executable<BPFError>,
parameter_bytes: &[u8],
executable: &'a dyn Executable<BPFError, ThisInstructionMeter>,
parameter_bytes: &mut [u8],
parameter_accounts: &'a [KeyedAccount<'a>],
invoke_context: &'a mut dyn InvokeContext,
) -> Result<EbpfVm<'a, BPFError, ThisInstructionMeter>, EbpfError<BPFError>> {
let heap = vec![0_u8; DEFAULT_HEAP_SIZE];
let heap_region = MemoryRegion::new_from_slice(&heap, MM_HEAP_START, true);
let bpf_compute_budget = invoke_context.get_bpf_compute_budget();
let mut vm = EbpfVm::new(
executable,
Config {
max_call_depth: bpf_compute_budget.max_call_depth,
stack_frame_size: bpf_compute_budget.stack_frame_size,
},
parameter_bytes,
&[heap_region],
let heap_region = MemoryRegion::new_from_slice(&heap, MM_HEAP_START, 0, true);
let mut vm = EbpfVm::new(executable, parameter_bytes, &[heap_region])?;
syscalls::bind_syscall_context_objects(
loader_id,
&mut vm,
parameter_accounts,
invoke_context,
heap,
)?;
syscalls::register_syscalls(loader_id, &mut vm, parameter_accounts, invoke_context, heap)?;
Ok(vm)
}
@ -219,7 +236,7 @@ impl InstructionMeter for ThisInstructionMeter {
/// BPF Loader's Executor implementation
pub struct BPFExecutor {
executable: Box<dyn Executable<BPFError>>,
executable: Box<dyn Executable<BPFError, ThisInstructionMeter>>,
}
// Well, implement Debug for solana_rbpf::vm::Executable in solana-rbpf...
@ -244,7 +261,7 @@ impl Executor for BPFExecutor {
let program = next_keyed_account(&mut keyed_accounts_iter)?;
let parameter_accounts = keyed_accounts_iter.as_slice();
let parameter_bytes = serialize_parameters(
let mut parameter_bytes = serialize_parameters(
program_id,
program.unsigned_key(),
parameter_accounts,
@ -255,7 +272,7 @@ impl Executor for BPFExecutor {
let mut vm = match create_vm(
program_id,
self.executable.as_ref(),
parameter_bytes.as_slice(),
&mut parameter_bytes,
&parameter_accounts,
invoke_context,
) {
@ -269,12 +286,8 @@ impl Executor for BPFExecutor {
stable_log::program_invoke(&logger, program.unsigned_key(), invoke_depth);
let mut instruction_meter = ThisInstructionMeter::new(compute_meter.clone());
let before = compute_meter.borrow().get_remaining();
const IS_JIT_ENABLED: bool = false;
let result = if IS_JIT_ENABLED {
if vm.jit_compile().is_err() {
return Err(BPFLoaderError::VirtualMachineCreationFailed.into());
}
unsafe { vm.execute_program_jit(&mut instruction_meter) }
vm.execute_program_jit(&mut instruction_meter)
} else {
vm.execute_program_interpreted(&mut instruction_meter)
};
@ -357,14 +370,14 @@ mod tests {
];
let input = &mut [0x00];
let executable = Executable::<BPFError>::from_text_bytes(program, None).unwrap();
let mut vm = EbpfVm::<BPFError, TestInstructionMeter>::new(
executable.as_ref(),
let executable = Executable::<BPFError, TestInstructionMeter>::from_text_bytes(
program,
None,
Config::default(),
input,
&[],
)
.unwrap();
let mut vm =
EbpfVm::<BPFError, TestInstructionMeter>::new(executable.as_ref(), input, &[]).unwrap();
let mut instruction_meter = TestInstructionMeter { remaining: 10 };
vm.execute_program_interpreted(&mut instruction_meter)
.unwrap();

File diff suppressed because it is too large Load Diff