Skip to content

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.

@mergify
Copy link

mergify bot commented Jan 6, 2025

⚠️ The sha of the head commit of this PR conflicts with #4306. Mergify cannot evaluate rules on this PR. ⚠️

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