provide full Rust panic messages in BPF and add memory optimizations (#13455)

This commit is contained in:
Jack May 2020-11-09 13:40:26 -08:00 committed by GitHub
parent c644b05c54
commit 461ae40eea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 20 deletions

View File

@ -270,6 +270,65 @@ info!(&format!("Some varialbe: {:?}", variable));
The [debugging](debugging.md#logging) section has more information about working
with program logs.
## Panicking
Rust's `panic!`, `assert!`, and internal panic results are printed to the
[program logs](debugging.md#logging) by default.
```
INFO solana_runtime::message_processor] Finalized account CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ
INFO solana_runtime::message_processor] Call BPF program CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ
INFO solana_runtime::message_processor] Program log: Panicked at: 'assertion failed: `(left == right)`
left: `1`,
right: `2`', rust/panic/src/lib.rs:22:5
INFO solana_runtime::message_processor] BPF program consumed 5453 of 200000 units
INFO solana_runtime::message_processor] BPF program CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ failed: BPF program panicked
```
### Custom Panic Handler
Programs can override the default panic handler by providing their own
implementation.
First define the `custom-panic` feature in the program's `Cargo.toml`
```toml
[features]
default = ["custom-panic"]
custom-panic = []
```
Then provide a custom implementation of the panic handler:
```rust
#[cfg(all(feature = "custom-panic", target_arch = "bpf"))]
#[no_mangle]
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
solana_program::info!("program custom panic enabled");
solana_program::info!(&format!("{}", info));
}
```
In the above snippit, the default implementation is shown, but developers may
replace that with something that better suits their needs.
One of the side effects of supporting full panic messages by default is that
programs incur the cost of pulling in more of Rust's `libstd` implementation
into program's shared object. Typical programs will already be pulling in a
fair amount of `libstd` and may not notice much of an increase in the shared
object size. But programs that explicitly attempt to be very small by avoiding
`libstd` may take a significant impact (~25kb). To eliminate that impact,
programs can provide their own custom panic handler with an empty
implementation.
```rust
#[cfg(all(feature = "custom-panic", target_arch = "bpf"))]
#[no_mangle]
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
// Do nothing to save space
}
```
## Compute Budget
Use the system call

View File

@ -11,6 +11,10 @@ edition = "2018"
[dependencies]
solana-program = { path = "../../../../sdk/program", version = "1.5.0" }
[features]
default = ["custom-panic"]
custom-panic = []
[lib]
name = "solana_bpf_rust_panic"
crate-type = ["cdylib"]

View File

@ -1,8 +1,24 @@
//! @brief Example Rust-based BPF program that panics
extern crate solana_program;
#[cfg(all(feature = "custom-panic", target_arch = "bpf"))]
#[no_mangle]
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
panic!();
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
// Note: Full panic reporting is included here for testing purposes
solana_program::info!("program custom panic enabled");
solana_program::info!(&format!("{}", info));
}
extern crate solana_program;
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey,
};
entrypoint!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
assert_eq!(1, 2);
Ok(())
}

View File

@ -533,11 +533,7 @@ fn test_program_bpf_invoke() {
&[TEST_SUCCESS, bump_seed1, bump_seed2, bump_seed3],
account_metas.clone(),
);
let noop_instruction = Instruction::new(
noop_program_id,
&(),
vec![]
);
let noop_instruction = Instruction::new(noop_program_id, &(), vec![]);
let message = Message::new(&[instruction, noop_instruction], Some(&mint_pubkey));
let tx = Transaction::new(
&[
@ -763,8 +759,8 @@ fn assert_instruction_count() {
("multiple_static", 8),
("noop", 57),
("relative_call", 10),
("sanity", 1140),
("sanity++", 1140),
("sanity", 176),
("sanity++", 176),
("struct_pass", 8),
("struct_ret", 22),
]);
@ -772,16 +768,16 @@ fn assert_instruction_count() {
#[cfg(feature = "bpf_rust")]
{
programs.extend_from_slice(&[
("solana_bpf_rust_128bit", 543),
("solana_bpf_rust_alloc", 19082),
("solana_bpf_rust_128bit", 572),
("solana_bpf_rust_alloc", 12777),
("solana_bpf_rust_dep_crate", 2),
("solana_bpf_rust_external_spend", 538),
("solana_bpf_rust_iter", 723),
("solana_bpf_rust_many_args", 231),
("solana_bpf_rust_iter", 724),
("solana_bpf_rust_many_args", 237),
("solana_bpf_rust_noop", 488),
("solana_bpf_rust_param_passing", 46),
("solana_bpf_rust_param_passing", 48),
("solana_bpf_rust_ristretto", 19399),
("solana_bpf_rust_sanity", 1965),
("solana_bpf_rust_sanity", 894),
]);
}

View File

@ -41,7 +41,7 @@ use thiserror::Error as ThisError;
pub enum SyscallError {
#[error("{0}: {1:?}")]
InvalidString(Utf8Error, Vec<u8>),
#[error("BPF program called abort()!")]
#[error("BPF program panicked")]
Abort,
#[error("BPF program Panicked in {0} at {1}:{2}")]
Panic(String, u64, u64),

View File

@ -174,7 +174,7 @@ if [[ ! -e rust-bpf-$machine-$version.md || ! -e rust-bpf-$machine ]]; then
fi
# Install Rust-BPF Sysroot sources
version=v0.12
version=v0.13
if [[ ! -e rust-bpf-sysroot-$version.md || ! -e rust-bpf-sysroot ]]; then
(
set -e

View File

@ -45,13 +45,31 @@ pub const HEAP_LENGTH: usize = 32 * 1024;
#[macro_export]
macro_rules! entrypoint {
($process_instruction:ident) => {
#[cfg(all(not(feature = "custom-heap"), not(test)))]
/// A program can provide their own custom heap implementation by adding
/// a `custom-heap` feature to `Cargo.toml` and implementing their own
/// `global_allocator`.
#[cfg(all(not(feature = "custom-heap"), target_arch = "bpf"))]
#[global_allocator]
static A: $crate::entrypoint::BumpAllocator = $crate::entrypoint::BumpAllocator {
start: $crate::entrypoint::HEAP_START_ADDRESS,
len: $crate::entrypoint::HEAP_LENGTH,
};
/// A program can provide their own custom panic implementation by
/// adding a `custom-panic` feature to `Cargo.toml` and implementing
/// their own `custom_panic`.
///
/// A good way to reduce the final size of the program is to provide a
/// `custom_panic` implementation that does nothing. Doing so will cut
/// ~25kb from a noop program. That number goes down the more the
/// programs pulls in Rust's libstd for other purposes.
#[cfg(all(not(feature = "custom-panic"), target_arch = "bpf"))]
#[no_mangle]
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
// Full panic reporting
$crate::info!(&format!("{}", info));
}
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {