Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Space efficient custom_panic (take 2) #3952

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

LucasSte
Copy link

@LucasSte LucasSte commented Dec 5, 2024

Problem

Our implementation of custom_panic can consume up to 25kb in contracts. This happens because it relies on the format! macro and, consequently, on std::fmt::write. They include many more functions in the contract and utilize dynamic dispatch, a technique that hinders compiler and link side optimizations for size reduction.

Summary of Changes

I implemented a new custom_panic that functions independently with only primitive (and unsafe) operations. It needs the stabilization of fmt::Arguments::as_str, which is happening in Rust 1.84 (see rust-lang/rust#132511).

Size comparison

Take this simple contract as an example:

entrypoint!(process_instruction);

fn process_instruction(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    Ok(())
}

The binary size has whooping 17696 bytes (17kb).
The contract with an empty custom_panic function has 10336 bytes (10kb), so panic is consuming 7360 bytes.
The contract with my new implementation has 11504 bytes (11kb), so my implementation has 1168 bytes.

New error messages

The members of fmt::Arguments are all private, so I cannot build custom panic messages during runtime as Rust does (think about the error you get when you access an invalid index from a vector: we can only know the index and the vector length during execution time). These messages will be elided in the new panic implementation (see examples below). Error messages whose content is known at compile time will still be shown normally.

The formatting is also different. It is more efficient to call sol_log multiple times than to format a string.

Accessing an invalid index from a vector:

OLD:

Program log: panicked at src/lib.rs:21:13:\nindex out of bounds: the len is 44 but the index is 85034

NEW:

Program log: PANICKED AT:
Program log: src/lib.rs
Program log: Line: 21  Column: 13
Calling unwrap on a None:

OLD:

Program log: panicked at src/lib.rs:23:15:\ncalled `Option::unwrap()` on a `None` value

NEW:

Program log: PANICKED AT:
Program log: src/lib.rs
Program log: Line: 23  Column: 15
Program log: called `Option::unwrap()` on a `None` value

Alternative implementation

For a better log experience (in my opinion), I prefer the implementation in #3951. It is larger, but the messages are more organized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant