Route all loader messages to log collector (#10528)

This commit is contained in:
Jack May 2020-06-13 13:20:08 -07:00 committed by GitHub
parent e5a2c75fab
commit b6a9573748
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 240 additions and 230 deletions

2
Cargo.lock generated
View File

@ -3741,11 +3741,9 @@ dependencies = [
"bincode", "bincode",
"byteorder", "byteorder",
"jemalloc-sys", "jemalloc-sys",
"log 0.4.8",
"num-derive 0.3.0", "num-derive 0.3.0",
"num-traits", "num-traits",
"rand 0.7.3", "rand 0.7.3",
"solana-logger",
"solana-runtime", "solana-runtime",
"solana-sdk", "solana-sdk",
"solana_rbpf", "solana_rbpf",

View File

@ -47,6 +47,7 @@ use std::{
cmp::{max, min}, cmp::{max, min},
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
net::SocketAddr, net::SocketAddr,
rc::Rc,
str::FromStr, str::FromStr,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
@ -730,14 +731,18 @@ fn run_transaction_simulation(
let txs = &[transaction]; let txs = &[transaction];
let batch = bank.prepare_simulation_batch(txs); let batch = bank.prepare_simulation_batch(txs);
let log_collector = LogCollector::default(); let log_collector = Rc::new(LogCollector::default());
let (_loaded_accounts, executed, _retryable_transactions, _transaction_count, _signature_count) = let (_loaded_accounts, executed, _retryable_transactions, _transaction_count, _signature_count) = {
bank.load_and_execute_transactions( bank.load_and_execute_transactions(
&batch, &batch,
solana_sdk::clock::MAX_PROCESSING_AGE, solana_sdk::clock::MAX_PROCESSING_AGE,
Some(&log_collector), Some(log_collector.clone()),
); )
(executed[0].0.clone().map(|_| ()), log_collector.output()) };
(
executed[0].0.clone().map(|_| ()),
Rc::try_unwrap(log_collector).unwrap_or_default().into(),
)
} }
#[rpc] #[rpc]

View File

@ -1337,10 +1337,8 @@ dependencies = [
"bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-logger 1.3.0",
"solana-runtime 1.3.0", "solana-runtime 1.3.0",
"solana-sdk 1.3.0", "solana-sdk 1.3.0",
"solana_rbpf 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "solana_rbpf 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -6,7 +6,7 @@ use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use solana_rbpf::EbpfVm; use solana_rbpf::EbpfVm;
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
entrypoint_native::{InvokeContext, ProcessInstruction}, entrypoint_native::{InvokeContext, Logger, ProcessInstruction},
instruction::{CompiledInstruction, InstructionError}, instruction::{CompiledInstruction, InstructionError},
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
@ -135,6 +135,7 @@ fn bench_program_alu(bencher: &mut Bencher) {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct MockInvokeContext { pub struct MockInvokeContext {
key: Pubkey, key: Pubkey,
mock_logger: MockLogger,
} }
impl InvokeContext for MockInvokeContext { impl InvokeContext for MockInvokeContext {
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> { fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
@ -155,8 +156,19 @@ impl InvokeContext for MockInvokeContext {
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] { fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
&[] &[]
} }
fn log_enabled(&self) -> bool { fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
false Rc::new(RefCell::new(self.mock_logger.clone()))
}
}
#[derive(Debug, Default, Clone)]
pub struct MockLogger {
pub log: Rc<RefCell<Vec<String>>>,
}
impl Logger for MockLogger {
fn log_enabled(&self) -> bool {
true
}
fn log(&mut self, message: &str) {
self.log.borrow_mut().push(message.to_string());
} }
fn log(&mut self, _message: &str) {}
} }

View File

@ -12,10 +12,8 @@ edition = "2018"
bincode = "1.2.1" bincode = "1.2.1"
byteorder = "1.3.4" byteorder = "1.3.4"
jemalloc-sys = { version = "0.3.2", features = ["disable_initial_exec_tls"] } jemalloc-sys = { version = "0.3.2", features = ["disable_initial_exec_tls"] }
log = "0.4.8"
num-derive = { version = "0.3" } num-derive = { version = "0.3" }
num-traits = { version = "0.2" } num-traits = { version = "0.2" }
solana-logger = { path = "../../logger", version = "1.3.0" }
solana-runtime = { path = "../../runtime", version = "1.3.0" } solana-runtime = { path = "../../runtime", version = "1.3.0" }
solana-sdk = { path = "../../sdk", version = "1.3.0" } solana-sdk = { path = "../../sdk", version = "1.3.0" }
solana_rbpf = "=0.1.28" solana_rbpf = "=0.1.28"

View File

@ -5,7 +5,6 @@ pub mod syscalls;
use crate::{bpf_verifier::VerifierError, syscalls::SyscallError}; use crate::{bpf_verifier::VerifierError, syscalls::SyscallError};
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use log::*;
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
use solana_rbpf::{ use solana_rbpf::{
ebpf::{EbpfError, UserDefinedError}, ebpf::{EbpfError, UserDefinedError},
@ -156,18 +155,33 @@ pub fn deserialize_parameters(
Ok(()) Ok(())
} }
macro_rules! log{
($logger:ident, $message:expr) => {
if let Ok(mut logger) = $logger.try_borrow_mut() {
if logger.log_enabled() {
logger.log($message);
}
}
};
($logger:ident, $fmt:expr, $($arg:tt)*) => {
if let Ok(mut logger) = $logger.try_borrow_mut() {
logger.log(&format!($fmt, $($arg)*));
}
};
}
pub fn process_instruction( pub fn process_instruction(
program_id: &Pubkey, program_id: &Pubkey,
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
instruction_data: &[u8], instruction_data: &[u8],
invoke_context: &mut dyn InvokeContext, invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
solana_logger::setup_with_default("solana=info");
debug_assert!(bpf_loader::check_id(program_id)); debug_assert!(bpf_loader::check_id(program_id));
let logger = invoke_context.get_logger();
if keyed_accounts.is_empty() { if keyed_accounts.is_empty() {
warn!("No account keys"); log!(logger, "No account keys");
return Err(InstructionError::NotEnoughAccountKeys); return Err(InstructionError::NotEnoughAccountKeys);
} }
@ -187,22 +201,32 @@ pub fn process_instruction(
match create_vm(&program_account.data, &parameter_accounts, invoke_context) { match create_vm(&program_account.data, &parameter_accounts, invoke_context) {
Ok(info) => info, Ok(info) => info,
Err(e) => { Err(e) => {
warn!("Failed to create BPF VM: {}", e); log!(logger, "Failed to create BPF VM: {}", e);
return Err(BPFLoaderError::VirtualMachineCreationFailed.into()); return Err(BPFLoaderError::VirtualMachineCreationFailed.into());
} }
}; };
info!("Call BPF program {}", program.unsigned_key()); log!(logger, "Call BPF program {}", program.unsigned_key());
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) { match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
Ok(status) => { Ok(status) => {
if status != SUCCESS { if status != SUCCESS {
let error: InstructionError = status.into(); let error: InstructionError = status.into();
warn!("BPF program {} failed: {}", program.unsigned_key(), error); log!(
logger,
"BPF program {} failed: {}",
program.unsigned_key(),
error
);
return Err(error); return Err(error);
} }
} }
Err(error) => { Err(error) => {
warn!("BPF program {} failed: {}", program.unsigned_key(), error); log!(
logger,
"BPF program {} failed: {}",
program.unsigned_key(),
error
);
return match error { return match error {
EbpfError::UserError(BPFError::SyscallError( EbpfError::UserError(BPFError::SyscallError(
SyscallError::InstructionError(error), SyscallError::InstructionError(error),
@ -213,21 +237,25 @@ pub fn process_instruction(
} }
} }
deserialize_parameters(parameter_accounts, &parameter_bytes)?; deserialize_parameters(parameter_accounts, &parameter_bytes)?;
info!("BPF program {} success", program.unsigned_key()); log!(logger, "BPF program {} success", program.unsigned_key());
} else if !keyed_accounts.is_empty() { } else if !keyed_accounts.is_empty() {
match limited_deserialize(instruction_data)? { match limited_deserialize(instruction_data)? {
LoaderInstruction::Write { offset, bytes } => { LoaderInstruction::Write { offset, bytes } => {
let mut keyed_accounts_iter = keyed_accounts.iter(); let mut keyed_accounts_iter = keyed_accounts.iter();
let program = next_keyed_account(&mut keyed_accounts_iter)?; let program = next_keyed_account(&mut keyed_accounts_iter)?;
if program.signer_key().is_none() { if program.signer_key().is_none() {
warn!("key[0] did not sign the transaction"); log!(logger, "key[0] did not sign the transaction");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
let offset = offset as usize; let offset = offset as usize;
let len = bytes.len(); let len = bytes.len();
trace!("Write: offset={} length={}", offset, len);
if program.data_len()? < offset + len { if program.data_len()? < offset + len {
warn!("Write overflow: {} < {}", program.data_len()?, offset + len); log!(
logger,
"Write overflow: {} < {}",
program.data_len()?,
offset + len
);
return Err(InstructionError::AccountDataTooSmall); return Err(InstructionError::AccountDataTooSmall);
} }
program.try_account_ref_mut()?.data[offset..offset + len].copy_from_slice(&bytes); program.try_account_ref_mut()?.data[offset..offset + len].copy_from_slice(&bytes);
@ -237,17 +265,21 @@ pub fn process_instruction(
let program = next_keyed_account(&mut keyed_accounts_iter)?; let program = next_keyed_account(&mut keyed_accounts_iter)?;
if program.signer_key().is_none() { if program.signer_key().is_none() {
warn!("key[0] did not sign the transaction"); log!(logger, "key[0] did not sign the transaction");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
if let Err(e) = check_elf(&program.try_account_ref()?.data) { if let Err(e) = check_elf(&program.try_account_ref()?.data) {
warn!("{}", e); log!(logger, "{}", e);
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
program.try_account_ref_mut()?.executable = true; program.try_account_ref_mut()?.executable = true;
info!("Finalize: account {:?}", program.signer_key().unwrap()); log!(
logger,
"Finalized account {:?}",
program.signer_key().unwrap()
);
} }
} }
} }
@ -259,15 +291,18 @@ mod tests {
use super::*; use super::*;
use rand::Rng; use rand::Rng;
use solana_sdk::{ use solana_sdk::{
account::Account, entrypoint_native::ProcessInstruction, instruction::CompiledInstruction, account::Account,
message::Message, rent::Rent, entrypoint_native::{Logger, ProcessInstruction},
instruction::CompiledInstruction,
message::Message,
rent::Rent,
}; };
use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc}; use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct MockInvokeContext { pub struct MockInvokeContext {
key: Pubkey, key: Pubkey,
pub log: Vec<String>, mock_logger: MockLogger,
} }
impl InvokeContext for MockInvokeContext { impl InvokeContext for MockInvokeContext {
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> { fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
@ -288,12 +323,20 @@ mod tests {
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] { fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
&[] &[]
} }
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
Rc::new(RefCell::new(self.mock_logger.clone()))
}
}
#[derive(Debug, Default, Clone)]
pub struct MockLogger {
pub log: Rc<RefCell<Vec<String>>>,
}
impl Logger for MockLogger {
fn log_enabled(&self) -> bool { fn log_enabled(&self) -> bool {
true true
} }
fn log(&mut self, message: &str) { fn log(&mut self, message: &str) {
info!("[MockInvokeContext::log] {}", message); self.log.borrow_mut().push(message.to_string());
self.log.push(message.to_string());
} }
} }
@ -446,8 +489,6 @@ mod tests {
#[test] #[test]
fn test_bpf_loader_invoke_main() { fn test_bpf_loader_invoke_main() {
solana_logger::setup();
let program_id = Pubkey::new_rand(); let program_id = Pubkey::new_rand();
let program_key = Pubkey::new_rand(); let program_key = Pubkey::new_rand();
@ -560,7 +601,7 @@ mod tests {
let mut elf = Vec::new(); let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap(); file.read_to_end(&mut elf).unwrap();
info!("mangle the whole file"); // Mangle the whole file
fuzz( fuzz(
&elf, &elf,
1_000_000_000, 1_000_000_000,

View File

@ -1,6 +1,5 @@
use crate::{alloc, BPFError}; use crate::{alloc, BPFError};
use alloc::Alloc; use alloc::Alloc;
use log::*;
use solana_rbpf::{ use solana_rbpf::{
ebpf::{EbpfError, SyscallObject, ELF_INSN_DUMP_OFFSET, MM_HEAP_START}, ebpf::{EbpfError, SyscallObject, ELF_INSN_DUMP_OFFSET, MM_HEAP_START},
memory_region::{translate_addr, MemoryRegion}, memory_region::{translate_addr, MemoryRegion},
@ -13,7 +12,7 @@ use solana_sdk::{
account_info::AccountInfo, account_info::AccountInfo,
bpf_loader, bpf_loader,
entrypoint::SUCCESS, entrypoint::SUCCESS,
entrypoint_native::InvokeContext, entrypoint_native::{InvokeContext, Logger},
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
message::Message, message::Message,
program_error::ProgramError, program_error::ProgramError,
@ -37,7 +36,7 @@ pub enum SyscallError {
InvalidString(Utf8Error, Vec<u8>), InvalidString(Utf8Error, Vec<u8>),
#[error("BPF program called abort()!")] #[error("BPF program called abort()!")]
Abort, Abort,
#[error("BPF program Panicked at {0}, {1}:{2}")] #[error("BPF program Panicked in {0} at {1}:{2}")]
Panic(String, u64, u64), Panic(String, u64, u64),
#[error("cannot borrow invoke context")] #[error("cannot borrow invoke context")]
InvokeContextBorrowFailed, InvokeContextBorrowFailed,
@ -76,46 +75,41 @@ pub fn register_syscalls<'a>(
invoke_context: &'a mut dyn InvokeContext, invoke_context: &'a mut dyn InvokeContext,
) -> Result<MemoryRegion, EbpfError<BPFError>> { ) -> Result<MemoryRegion, EbpfError<BPFError>> {
// Syscall function common across languages // Syscall function common across languages
vm.register_syscall_ex("abort", syscall_abort)?; vm.register_syscall_ex("abort", syscall_abort)?;
vm.register_syscall_ex("sol_panic_", syscall_sol_panic)?;
{ vm.register_syscall_with_context_ex(
let invoke_context = Rc::new(RefCell::new(invoke_context)); "sol_log_",
Box::new(SyscallLog {
logger: invoke_context.get_logger(),
}),
)?;
vm.register_syscall_with_context_ex(
"sol_log_64_",
Box::new(SyscallLogU64 {
logger: invoke_context.get_logger(),
}),
)?;
vm.register_syscall_with_context_ex( // Cross-program invocation syscalls
"sol_panic_",
Box::new(SyscallPanic {
invoke_context: invoke_context.clone(),
}),
)?;
vm.register_syscall_with_context_ex(
"sol_log_",
Box::new(SyscallLog {
invoke_context: invoke_context.clone(),
}),
)?;
vm.register_syscall_with_context_ex(
"sol_log_64_",
Box::new(SyscallLogU64 {
invoke_context: invoke_context.clone(),
}),
)?;
// Cross-program invocation syscalls let invoke_context = Rc::new(RefCell::new(invoke_context));
vm.register_syscall_with_context_ex(
"sol_invoke_signed_c", vm.register_syscall_with_context_ex(
Box::new(SyscallProcessSolInstructionC { "sol_invoke_signed_c",
callers_keyed_accounts, Box::new(SyscallProcessSolInstructionC {
invoke_context: invoke_context.clone(), callers_keyed_accounts,
}), invoke_context: invoke_context.clone(),
)?; }),
vm.register_syscall_with_context_ex( )?;
"sol_invoke_signed_rust", vm.register_syscall_with_context_ex(
Box::new(SyscallProcessInstructionRust { "sol_invoke_signed_rust",
callers_keyed_accounts, Box::new(SyscallProcessInstructionRust {
invoke_context: invoke_context.clone(), callers_keyed_accounts,
}), invoke_context: invoke_context.clone(),
)?; }),
} )?;
// Memory allocator // Memory allocator
let heap = vec![0_u8; DEFAULT_HEAP_SIZE]; let heap = vec![0_u8; DEFAULT_HEAP_SIZE];
@ -232,49 +226,25 @@ pub fn syscall_abort(
/// Panic syscall function, called when the BPF program calls 'sol_panic_()` /// Panic syscall function, called when the BPF program calls 'sol_panic_()`
/// Causes the BPF program to be halted immediately /// Causes the BPF program to be halted immediately
pub struct SyscallPanic<'a> { pub fn syscall_sol_panic(
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>, file: u64,
} len: u64,
impl<'a> SyscallPanic<'a> { line: u64,
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>> { column: u64,
self.invoke_context _arg5: u64,
.try_borrow_mut() ro_regions: &[MemoryRegion],
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) _rw_regions: &[MemoryRegion],
} ) -> Result<u64, EbpfError<BPFError>> {
} translate_string_and_do(file, len, ro_regions, &mut |string: &str| {
impl<'a> SyscallObject<BPFError> for SyscallPanic<'a> { Err(SyscallError::Panic(string.to_string(), line, column).into())
fn call( })
&mut self,
file: u64,
len: u64,
line: u64,
column: u64,
_arg5: u64,
ro_regions: &[MemoryRegion],
_rw_regions: &[MemoryRegion],
) -> Result<u64, EbpfError<BPFError>> {
let mut invoke_context = self.get_context_mut()?;
translate_string_and_do(file, len, ro_regions, &mut |string: &str| {
if invoke_context.log_enabled() {
invoke_context.log(&format!("panicked at '{}' on {}:{}", string, line, column));
}
Err(SyscallError::Panic(string.to_string(), line, column).into())
})
}
} }
/// Log a user's info message /// Log a user's info message
pub struct SyscallLog<'a> { pub struct SyscallLog {
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>, logger: Rc<RefCell<dyn Logger>>,
} }
impl<'a> SyscallLog<'a> { impl SyscallObject<BPFError> for SyscallLog {
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>> {
self.invoke_context
.try_borrow_mut()
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
}
}
impl<'a> SyscallObject<BPFError> for SyscallLog<'a> {
fn call( fn call(
&mut self, &mut self,
addr: u64, addr: u64,
@ -285,10 +255,13 @@ impl<'a> SyscallObject<BPFError> for SyscallLog<'a> {
ro_regions: &[MemoryRegion], ro_regions: &[MemoryRegion],
_rw_regions: &[MemoryRegion], _rw_regions: &[MemoryRegion],
) -> Result<u64, EbpfError<BPFError>> { ) -> Result<u64, EbpfError<BPFError>> {
let mut invoke_context = self.get_context_mut()?; let mut logger = self
if invoke_context.log_enabled() { .logger
.try_borrow_mut()
.map_err(|_| SyscallError::InvokeContextBorrowFailed)?;
if logger.log_enabled() {
translate_string_and_do(addr, len, ro_regions, &mut |string: &str| { translate_string_and_do(addr, len, ro_regions, &mut |string: &str| {
invoke_context.log(string); logger.log(&format!("Program log: {}", string));
Ok(0) Ok(0)
})?; })?;
} }
@ -297,17 +270,10 @@ impl<'a> SyscallObject<BPFError> for SyscallLog<'a> {
} }
/// Log 5 64-bit values /// Log 5 64-bit values
pub struct SyscallLogU64<'a> { pub struct SyscallLogU64 {
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>, logger: Rc<RefCell<dyn Logger>>,
} }
impl<'a> SyscallLogU64<'a> { impl SyscallObject<BPFError> for SyscallLogU64 {
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>> {
self.invoke_context
.try_borrow_mut()
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
}
}
impl<'a> SyscallObject<BPFError> for SyscallLogU64<'a> {
fn call( fn call(
&mut self, &mut self,
arg1: u64, arg1: u64,
@ -318,10 +284,13 @@ impl<'a> SyscallObject<BPFError> for SyscallLogU64<'a> {
_ro_regions: &[MemoryRegion], _ro_regions: &[MemoryRegion],
_rw_regions: &[MemoryRegion], _rw_regions: &[MemoryRegion],
) -> Result<u64, EbpfError<BPFError>> { ) -> Result<u64, EbpfError<BPFError>> {
let mut invoke_context = self.get_context_mut()?; let mut logger = self
if invoke_context.log_enabled() { .logger
invoke_context.log(&format!( .try_borrow_mut()
"{:#x}, {:#x}, {:#x}, {:#x}, {:#x}", .map_err(|_| SyscallError::InvokeContextBorrowFailed)?;
if logger.log_enabled() {
logger.log(&format!(
"Program log: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}",
arg1, arg2, arg3, arg4, arg5 arg1, arg2, arg3, arg4, arg5
)); ));
} }
@ -863,8 +832,7 @@ fn call<'a>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::tests::MockInvokeContext; use crate::tests::MockLogger;
use solana_sdk::entrypoint_native::InvokeContext;
#[test] #[test]
fn test_translate() { fn test_translate() {
@ -988,38 +956,23 @@ mod tests {
#[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")] #[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")]
fn test_syscall_sol_panic() { fn test_syscall_sol_panic() {
let string = "Gaggablaghblagh!"; let string = "Gaggablaghblagh!";
let addr = string.as_ptr() as *const _ as u64;
let mut mock_invoke_context = MockInvokeContext::default(); let ro_region = MemoryRegion {
let result = { addr_host: addr,
let invoke_context: &mut dyn InvokeContext = &mut mock_invoke_context; addr_vm: 100,
let mut syscall_sol_panic = SyscallPanic { len: string.len() as u64,
invoke_context: Rc::new(RefCell::new(invoke_context)),
};
let addr = string.as_ptr() as *const _ as u64;
let ro_region = MemoryRegion {
addr_host: addr,
addr_vm: 100,
len: string.len() as u64,
};
let rw_region = MemoryRegion::default();
syscall_sol_panic.call(
100,
string.len() as u64,
42,
84,
0,
&[ro_region],
&[rw_region],
)
}; };
assert_eq!(mock_invoke_context.log.len(), 1); let rw_region = MemoryRegion::default();
assert_eq!( syscall_sol_panic(
mock_invoke_context.log[0], 100,
"panicked at \'Gaggablaghblagh!\' on 42:84" string.len() as u64,
); 42,
84,
result.unwrap(); 0,
&[ro_region],
&[rw_region],
)
.unwrap();
} }
#[test] #[test]
@ -1033,50 +986,47 @@ mod tests {
}]; }];
let rw_regions = &[MemoryRegion::default()]; let rw_regions = &[MemoryRegion::default()];
let mut mock_invoke_context = MockInvokeContext::default(); let log = Rc::new(RefCell::new(vec![]));
{ let mock_logger = MockLogger { log: log.clone() };
let invoke_context: &mut dyn InvokeContext = &mut mock_invoke_context; let logger: Rc<RefCell<dyn Logger>> = Rc::new(RefCell::new(mock_logger));
let mut syscall_sol_log = SyscallLog { let mut syscall_sol_log = SyscallLog { logger };
invoke_context: Rc::new(RefCell::new(invoke_context)),
};
syscall_sol_log syscall_sol_log
.call(100, string.len() as u64, 0, 0, 0, ro_regions, rw_regions) .call(100, string.len() as u64, 0, 0, 0, ro_regions, rw_regions)
.unwrap(); .unwrap();
syscall_sol_log syscall_sol_log
.call( .call(
100, 100,
string.len() as u64 * 2, // AccessViolation string.len() as u64 * 2, // AccessViolation
0, 0,
0, 0,
0, 0,
ro_regions, ro_regions,
rw_regions, rw_regions,
) )
.unwrap_err(); .unwrap_err();
}
assert_eq!(mock_invoke_context.log.len(), 1); assert_eq!(log.borrow().len(), 1);
assert_eq!(mock_invoke_context.log[0], "Gaggablaghblagh!"); assert_eq!(log.borrow()[0], "Program log: Gaggablaghblagh!");
} }
#[test] #[test]
fn test_syscall_sol_log_u64() { fn test_syscall_sol_log_u64() {
let mut mock_invoke_context = MockInvokeContext::default(); let log = Rc::new(RefCell::new(vec![]));
{ let mock_logger = MockLogger { log: log.clone() };
let invoke_context: &mut dyn InvokeContext = &mut mock_invoke_context; let mut syscall_sol_log_u64 = SyscallLogU64 {
let mut syscall_sol_log_u64 = SyscallLogU64 { logger: Rc::new(RefCell::new(mock_logger)),
invoke_context: Rc::new(RefCell::new(invoke_context)), };
}; let ro_regions = &[MemoryRegion::default()];
let rw_regions = &[MemoryRegion::default()];
let ro_regions = &[MemoryRegion::default()]; syscall_sol_log_u64
let rw_regions = &[MemoryRegion::default()]; .call(1, 2, 3, 4, 5, ro_regions, rw_regions)
syscall_sol_log_u64 .unwrap();
.call(1, 2, 3, 4, 5, ro_regions, rw_regions)
.unwrap(); assert_eq!(log.borrow().len(), 1);
} assert_eq!(log.borrow()[0], "Program log: 0x1, 0x2, 0x3, 0x4, 0x5");
assert_eq!(mock_invoke_context.log.len(), 1);
assert_eq!(mock_invoke_context.log[0], "0x1, 0x2, 0x3, 0x4, 0x5");
} }
#[test] #[test]

View File

@ -1348,7 +1348,7 @@ impl Bank {
&self, &self,
batch: &TransactionBatch, batch: &TransactionBatch,
max_age: usize, max_age: usize,
log_collector: Option<&LogCollector>, log_collector: Option<Rc<LogCollector>>,
) -> ( ) -> (
Vec<(Result<TransactionLoadResult>, Option<HashAgeKind>)>, Vec<(Result<TransactionLoadResult>, Option<HashAgeKind>)>,
Vec<TransactionProcessResult>, Vec<TransactionProcessResult>,
@ -1408,7 +1408,7 @@ impl Bank {
&loader_refcells, &loader_refcells,
&account_refcells, &account_refcells,
&self.rent_collector, &self.rent_collector,
log_collector, log_collector.clone(),
); );
Self::refcells_to_accounts( Self::refcells_to_accounts(

View File

@ -9,8 +9,9 @@ impl LogCollector {
pub fn log(&self, message: &str) { pub fn log(&self, message: &str) {
self.messages.borrow_mut().push(message.to_string()) self.messages.borrow_mut().push(message.to_string())
} }
}
pub fn output(self) -> Vec<String> { impl Into<Vec<String>> for LogCollector {
fn into(self) -> Vec<String> {
self.messages.into_inner() self.messages.into_inner()
} }
} }

View File

@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use solana_sdk::{ use solana_sdk::{
account::{create_keyed_readonly_accounts, Account, KeyedAccount}, account::{create_keyed_readonly_accounts, Account, KeyedAccount},
clock::Epoch, clock::Epoch,
entrypoint_native::{InvokeContext, ProcessInstruction}, entrypoint_native::{InvokeContext, Logger, ProcessInstruction},
instruction::{CompiledInstruction, InstructionError}, instruction::{CompiledInstruction, InstructionError},
message::Message, message::Message,
native_loader, native_loader,
@ -154,23 +154,21 @@ impl PreAccount {
} }
} }
#[derive(Default)] pub struct ThisInvokeContext {
pub struct ThisInvokeContext<'a> { program_ids: Vec<Pubkey>,
pub program_ids: Vec<Pubkey>, rent: Rent,
pub rent: Rent, pre_accounts: Vec<PreAccount>,
pub pre_accounts: Vec<PreAccount>, programs: Vec<(Pubkey, ProcessInstruction)>,
pub programs: Vec<(Pubkey, ProcessInstruction)>, logger: Rc<RefCell<dyn Logger>>,
pub log_collector: Option<&'a LogCollector>,
} }
impl ThisInvokeContext {
impl<'a> ThisInvokeContext<'a> {
const MAX_INVOCATION_DEPTH: usize = 5; const MAX_INVOCATION_DEPTH: usize = 5;
pub fn new( pub fn new(
program_id: &Pubkey, program_id: &Pubkey,
rent: Rent, rent: Rent,
pre_accounts: Vec<PreAccount>, pre_accounts: Vec<PreAccount>,
programs: Vec<(Pubkey, ProcessInstruction)>, programs: Vec<(Pubkey, ProcessInstruction)>,
log_collector: Option<&'a LogCollector>, log_collector: Option<Rc<LogCollector>>,
) -> Self { ) -> Self {
let mut program_ids = Vec::with_capacity(Self::MAX_INVOCATION_DEPTH); let mut program_ids = Vec::with_capacity(Self::MAX_INVOCATION_DEPTH);
program_ids.push(*program_id); program_ids.push(*program_id);
@ -179,12 +177,11 @@ impl<'a> ThisInvokeContext<'a> {
rent, rent,
pre_accounts, pre_accounts,
programs, programs,
log_collector, logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
} }
} }
} }
impl InvokeContext for ThisInvokeContext {
impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError> { fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError> {
if self.program_ids.len() >= Self::MAX_INVOCATION_DEPTH { if self.program_ids.len() >= Self::MAX_INVOCATION_DEPTH {
return Err(InstructionError::CallDepth); return Err(InstructionError::CallDepth);
@ -222,18 +219,23 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
.last() .last()
.ok_or(InstructionError::GenericError) .ok_or(InstructionError::GenericError)
} }
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] { fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
&self.programs &self.programs
} }
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
self.logger.clone()
}
}
pub struct ThisLogger {
log_collector: Option<Rc<LogCollector>>,
}
impl Logger for ThisLogger {
fn log_enabled(&self) -> bool { fn log_enabled(&self) -> bool {
log_enabled!(log::Level::Info) || self.log_collector.is_some() log_enabled!(log::Level::Info) || self.log_collector.is_some()
} }
fn log(&mut self, message: &str) { fn log(&mut self, message: &str) {
info!("{}", message); info!("{}", message);
if let Some(log_collector) = self.log_collector { if let Some(log_collector) = &self.log_collector {
log_collector.log(message); log_collector.log(message);
} }
} }
@ -502,14 +504,14 @@ impl MessageProcessor {
executable_accounts: &[(Pubkey, RefCell<Account>)], executable_accounts: &[(Pubkey, RefCell<Account>)],
accounts: &[Rc<RefCell<Account>>], accounts: &[Rc<RefCell<Account>>],
rent_collector: &RentCollector, rent_collector: &RentCollector,
log_collector: Option<&LogCollector>, log_collector: Option<Rc<LogCollector>>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let pre_accounts = Self::create_pre_accounts(message, instruction, accounts); let pre_accounts = Self::create_pre_accounts(message, instruction, accounts);
let mut invoke_context = ThisInvokeContext::new( let mut invoke_context = ThisInvokeContext::new(
instruction.program_id(&message.account_keys), instruction.program_id(&message.account_keys),
rent_collector.rent, rent_collector.rent,
pre_accounts, pre_accounts,
self.programs.clone(), self.programs.clone(), // get rid of clone
log_collector, log_collector,
); );
let keyed_accounts = let keyed_accounts =
@ -535,7 +537,7 @@ impl MessageProcessor {
loaders: &[Vec<(Pubkey, RefCell<Account>)>], loaders: &[Vec<(Pubkey, RefCell<Account>)>],
accounts: &[Rc<RefCell<Account>>], accounts: &[Rc<RefCell<Account>>],
rent_collector: &RentCollector, rent_collector: &RentCollector,
log_collector: Option<&LogCollector>, log_collector: Option<Rc<LogCollector>>,
) -> Result<(), TransactionError> { ) -> Result<(), TransactionError> {
for (instruction_index, instruction) in message.instructions.iter().enumerate() { for (instruction_index, instruction) in message.instructions.iter().enumerate() {
self.execute_instruction( self.execute_instruction(
@ -544,7 +546,7 @@ impl MessageProcessor {
&loaders[instruction_index], &loaders[instruction_index],
accounts, accounts,
rent_collector, rent_collector,
log_collector, log_collector.clone(),
) )
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?; .map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
} }

View File

@ -165,7 +165,12 @@ pub trait InvokeContext {
fn get_caller(&self) -> Result<&Pubkey, InstructionError>; fn get_caller(&self) -> Result<&Pubkey, InstructionError>;
/// Get a list of built-in programs /// Get a list of built-in programs
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)]; fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)];
/// Check if logging is enabled /// Get this invocation's logger
fn get_logger(&self) -> Rc<RefCell<dyn Logger>>;
}
/// Log messages
pub trait Logger {
fn log_enabled(&self) -> bool; fn log_enabled(&self) -> bool;
/// Log a message /// Log a message
fn log(&mut self, message: &str); fn log(&mut self, message: &str);