Skip to content

Commit 07be18f

Browse files
committed
New custom panic
1 parent b2a7a64 commit 07be18f

File tree

1 file changed

+86
-0
lines changed
  • sdk/program-entrypoint/src

1 file changed

+86
-0
lines changed

sdk/program-entrypoint/src/lib.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,92 @@ macro_rules! custom_panic_default {
279279
};
280280
}
281281

282+
/// This is a more space efficient implementation of custom panic.
283+
/// It is about 1kb in size and depends on the unstable feature `panic_info_message`,
284+
/// which is going to be stabilized in Rust 1.84.
285+
///
286+
/// It works just like the default custom panic, except that dynamic generated error
287+
/// messages are not shown. For instance, trying to access an index out of bounds of a vector
288+
/// would give us `index Y is out of bounds for length X`. As the length is only know at runtime
289+
/// time, this message is elided.
290+
///
291+
/// All messages known at compile time are correctly displayed, e.g. `called unwrap in a None`,
292+
/// file names, line and column numbers.
293+
#[macro_export]
294+
macro_rules! custom_panic_space_efficient {
295+
() => {
296+
#[cfg(all(not(feature = "custom-panic"), target_os = "solana"))]
297+
#[no_mangle]
298+
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
299+
#[inline(never)]
300+
unsafe fn write_num(mut num: u32, dst: *mut u8) -> usize {
301+
let mut buf: [u8; 10] = [0; 10];
302+
let init_ptr = buf.as_mut_ptr().add(11);
303+
let mut write_ptr = init_ptr;
304+
while num > 0 {
305+
write_ptr = write_ptr.sub(1);
306+
// SAFETY: The pointer will always be within *buf and *buf+10
307+
*write_ptr = (num % 10) as u8 + 48;
308+
num /= 10;
309+
}
310+
let len = init_ptr.offset_from(write_ptr) as usize;
311+
// SAFETY: The copy length will never be greater than 10.
312+
std::ptr::copy_nonoverlapping(write_ptr, dst, len);
313+
len + 1
314+
}
315+
316+
if let Some(loc) = info.location() {
317+
let filename = loc.file().as_bytes();
318+
// Array of 10 (line number) + 10 (column number)
319+
// + 6 (line string) + 8 bytes + 16 extra bytes
320+
let mut msg_line: [u8; 50] = [32; 50];
321+
322+
let dst = msg_line.as_mut_ptr();
323+
unsafe {
324+
let panic_str = "PANICKED AT:".as_bytes();
325+
// SAFETY: Both strings are well formed
326+
solana_program::syscalls::sol_log_(panic_str.as_ptr(), panic_str.len() as u64);
327+
solana_program::syscalls::sol_log_(filename.as_ptr(), filename.len() as u64);
328+
329+
let line = "Line: ".as_bytes(); // 6 bytes
330+
let src = line.as_ptr();
331+
// SAFETY: Destination is larger than the line string
332+
std::ptr::copy_nonoverlapping(src, dst, line.len());
333+
334+
let dst = dst.add(line.len());
335+
// SAFETY: write_num will never write more than 10 bytes
336+
let written_bytes = write_num(loc.line(), dst);
337+
let dst = dst.add(written_bytes);
338+
*dst = 32;
339+
340+
let dst = dst.add(1);
341+
let col = "Column: ".as_bytes(); // 8 bytes
342+
let src = col.as_ptr();
343+
// SAFETY: Destination is larger than 25 bytes
344+
std::ptr::copy_nonoverlapping(src, dst, col.len());
345+
346+
let dst = dst.add(col.len());
347+
// SAFETY: write_num will never write more than 10 bytes
348+
let written_bytes = write_num(loc.column(), dst);
349+
350+
let line_len = dst.add(written_bytes).offset_from(msg_line.as_ptr()) as usize;
351+
// SAFETY: The line length is properly offseted from number of
352+
// written bytes
353+
solana_program::syscalls::sol_log_(msg_line.as_ptr(), line_len as u64);
354+
}
355+
}
356+
357+
if let Some(Some(mm)) = info.message().map(|mes| mes.as_str()) {
358+
let mes = mm.as_bytes();
359+
// SAFETY: We assumed the panic message is well formed.
360+
unsafe {
361+
solana_program::syscalls::sol_log_(mes.as_ptr(), mes.len() as u64);
362+
}
363+
}
364+
}
365+
};
366+
}
367+
282368
/// The bump allocator used as the default rust heap when running programs.
283369
pub struct BumpAllocator {
284370
pub start: usize,

0 commit comments

Comments
 (0)