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

feat(gstd): get panic message without panic_info_message #3527

Merged
merged 46 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
409e33b
feat(gstd): get panic message without `panic_info_message`
StackOverflowExcept1on Nov 27, 2023
bab3417
keep feature
StackOverflowExcept1on Nov 27, 2023
90dd7b8
[ci-skip] inline var
StackOverflowExcept1on Nov 27, 2023
b00d583
re-implement panic handler with arrayvec
StackOverflowExcept1on Nov 29, 2023
388492c
add common-handlers feature and document panic_handler
StackOverflowExcept1on Dec 1, 2023
76ba81b
fix fmt
StackOverflowExcept1on Dec 1, 2023
bdd77ae
add some notes
StackOverflowExcept1on Dec 1, 2023
89dbf0f
[skip-ci] use try_push_str bcz it doesn't encodes char as utf8
StackOverflowExcept1on Dec 1, 2023
4aa5f52
make binary x2.2 smaller on nightly
StackOverflowExcept1on Dec 8, 2023
61cb641
[skip-ci] remove core::
StackOverflowExcept1on Dec 8, 2023
e09cc5f
[skip-ci] use array syntax for MaybeUninit
StackOverflowExcept1on Dec 12, 2023
ee01ee3
Merge remote-tracking branch 'origin/master' into av/gstd-panic-msg
StackOverflowExcept1on Dec 13, 2023
b23ab7b
apply suggestion
StackOverflowExcept1on Dec 13, 2023
9b2b9ad
add safety comments
StackOverflowExcept1on Dec 13, 2023
66be25d
simulate write! macro with for loop
StackOverflowExcept1on Dec 13, 2023
14ab4d5
add extra space for single quotes
StackOverflowExcept1on Dec 13, 2023
8e354c4
trying to fix ci
StackOverflowExcept1on Dec 13, 2023
f7e459b
fix some clippy lint and use other approach for rust >=1.73
StackOverflowExcept1on Dec 15, 2023
9420d8f
remove most unsafe code
StackOverflowExcept1on Dec 26, 2023
c1608f0
completely get rid of unsafe code
StackOverflowExcept1on Dec 26, 2023
b960e3f
Merge remote-tracking branch 'origin/master' into av/gstd-panic-msg
StackOverflowExcept1on Dec 26, 2023
3297392
match result of get
StackOverflowExcept1on Dec 26, 2023
461b196
Merge remote-tracking branch 'origin/master' into av/gstd-panic-msg
StackOverflowExcept1on Jan 10, 2024
eab5a5d
split debug and panic handler
StackOverflowExcept1on Jan 11, 2024
d1bd563
Merge remote-tracking branch 'origin/master' into av/gstd-panic-msg
StackOverflowExcept1on Jan 11, 2024
6a4f82b
keep old msg format
StackOverflowExcept1on Jan 11, 2024
f14edfe
gr_debug is still needed for gtest
StackOverflowExcept1on Jan 12, 2024
e236195
[skip-ci] fix doc
StackOverflowExcept1on Jan 12, 2024
e703d8e
Merge remote-tracking branch 'origin/master' into av/gstd-panic-msg
StackOverflowExcept1on Jan 12, 2024
9d99503
simplify location/not location
StackOverflowExcept1on Jan 12, 2024
63a341b
nightly -> panic-info-message
StackOverflowExcept1on Jan 12, 2024
2036f5b
move cfg(feature = panic-handler) near the it's module
StackOverflowExcept1on Jan 12, 2024
fbeaefc
no info -> <unknown>
StackOverflowExcept1on Jan 13, 2024
4320ac0
[skip-ci] add dot
StackOverflowExcept1on Jan 13, 2024
ed77410
[skip-ci] move doc comment below
StackOverflowExcept1on Jan 13, 2024
c4a42f9
doc test -> #[test]
StackOverflowExcept1on Jan 14, 2024
2df8095
rework panic handler
StackOverflowExcept1on Jan 15, 2024
d093863
Merge remote-tracking branch 'origin/master' into av/gstd-panic-msg
StackOverflowExcept1on Jan 15, 2024
63a178b
fix ci
StackOverflowExcept1on Jan 15, 2024
9bb3525
fix assert_panicked
StackOverflowExcept1on Jan 15, 2024
f2759d1
[skip-ci] replace .starts_with with .contains in gtest::tests
StackOverflowExcept1on Jan 15, 2024
7049548
just use TRIMMED_MAX_LEN
StackOverflowExcept1on Jan 15, 2024
a9d9c3f
better docs
StackOverflowExcept1on Jan 15, 2024
ced5651
resolve some conversations
StackOverflowExcept1on Jan 15, 2024
20521e7
[skip-ci] add doc about `panic-location`
StackOverflowExcept1on Jan 16, 2024
d318147
Merge remote-tracking branch 'origin/master' into av/gstd-panic-msg
StackOverflowExcept1on Jan 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ gcore = { path = "gcore" }
gcli = { path = "gcli" }
gclient = { path = "gclient" }
gsdk = { path = "gsdk" }
gstd = { path = "gstd", features = [ "nightly" ] }
gstd = { path = "gstd", features = ["nightly"] }
gsys = { path = "gsys" }
gtest = { path = "gtest" }
gmeta = { path = "gmeta" }
Expand Down
14 changes: 10 additions & 4 deletions gstd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition.workspace = true
license.workspace = true

[dependencies]
arrayvec = { version = "0.7.4", default-features = false, optional = true }
document-features = { version = "0.2.7", optional = true }
galloc.workspace = true
gcore = { workspace = true, features = ["codec"] }
Expand All @@ -16,17 +17,22 @@ bs58 = { workspace = true, features = ["alloc"] }
hex = { workspace = true, features = ["alloc"] }
parity-scale-codec = { workspace = true, features = ["derive"] }
primitive-types = { workspace = true, features = ["scale-info"] }
rustversion = { version = "1.0", optional = true }
scale-info = { workspace = true, features = ["derive"] }
futures = { workspace = true, features = ["alloc"] }

static_assertions.workspace = true

[dev-dependencies]
rustversion = "1.0"
ark0f marked this conversation as resolved.
Show resolved Hide resolved

[features]
#! ## Default features

default = ["panic-handler"]
## When enabled, a panic handler is provided by this crate.
panic-handler = []
default = ["common-handlers"]
## When enabled, a panic handler and an OOM error handler are provided by this crate.
## Note that the OOM error handler is provided when "nightly" feature is enabled.
common-handlers = []

#! ## Nightly features

Expand All @@ -44,7 +50,7 @@ oom-handler = []

## Enables debug logging; this heavily impacts gas cost
## and is therefore disabled by default.
debug = ["galloc/debug", "gcore/debug"]
debug = ["galloc/debug", "gcore/debug", "arrayvec", "rustversion"]

#! [rust-66745]: https://github.com/rust-lang/rust/issues/66745
#! [rust-51540]: https://github.com/rust-lang/rust/issues/51540
Expand Down
227 changes: 193 additions & 34 deletions gstd/src/common/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,50 +25,209 @@
//! debug and non-debug mode, for programs built in `wasm32` architecture.
//! For `debug` mode it provides more extensive logging.

#[cfg(target_arch = "wasm32")]
#[cfg(feature = "oom-handler")]
#[alloc_error_handler]
pub fn oom(_: core::alloc::Layout) -> ! {
crate::ext::oom_panic()
}

#[cfg(feature = "panic-handler")]
mod panic_handler {
use crate::ext;
use core::panic::PanicInfo;
/// We currently support 2 panic handler modes:
/// - non-debug: it prints `no info`
/// - debug: it prints `'{message}', {location}`
/// - In nightly Rust, we use `#![feature(panic_info_message)]` and the
/// [`write!`] macro.
/// - In stable Rust, we need to modify the default panic handler message
/// format.
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
///
/// Default panic handler message format (according to <https://github.com/rust-lang/rust/pull/112849>):
/// - Rust <1.73: `panicked at '{message}', {location}`
/// - Rust >=1.73: `panicked at {location}:\n{message}`
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
breathx marked this conversation as resolved.
Show resolved Hide resolved
///
/// We parse the output of `impl Display for PanicInfo<'_>` and
/// then convert it to custom format: `'{message}', {location}`.
///
/// Here is a test to verify that the default panic handler message format
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
/// has not changed:
/// ```
/// use std::panic::{self, PanicInfo};
///
/// const MESSAGE: &str = "message";
///
/// #[rustversion::before(1.73)]
/// fn expected_format(panic_info: &PanicInfo) -> String {
/// let location = panic_info.location().unwrap();
/// format!("panicked at '{MESSAGE}', {location}")
/// }
///
/// #[rustversion::since(1.73)]
/// fn expected_format(panic_info: &PanicInfo) -> String {
/// let location = panic_info.location().unwrap();
/// format!("panicked at {location}:\n{MESSAGE}")
/// }
///
/// panic::set_hook(Box::new(|panic_info| {
/// assert_eq!(panic_info.to_string(), expected_format(panic_info));
/// }));
///
/// let result = panic::catch_unwind(|| {
/// panic!("{MESSAGE}");
/// });
/// assert!(result.is_err());
/// ```
pub mod panic_handler {
#[cfg(target_arch = "wasm32")]
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
mod internal {
use crate::ext;
use core::panic::PanicInfo;

#[cfg(not(feature = "debug"))]
#[panic_handler]
pub fn panic(_: &PanicInfo) -> ! {
ext::panic("no info")
}
/// Panic handler when debug feature is disabled.
#[cfg(not(feature = "debug"))]
#[panic_handler]
pub fn panic(_: &PanicInfo) -> ! {
ext::panic("no info")
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
}

#[cfg(feature = "debug")]
#[panic_handler]
pub fn panic(panic_info: &PanicInfo) -> ! {
use crate::prelude::format;
#[cfg(not(feature = "panic-messages"))]
let message = None::<&core::fmt::Arguments<'_>>;
#[cfg(feature = "debug")]
mod constants {
/// Max amount of bytes allowed to be thrown as string explanation
/// of the error.
pub const TRIMMED_MAX_LEN: usize = 1024; //TODO: do not duplicate `gear_core::str::TRIMMED_MAX_LEN`
/// This prefix is used to print debug message:
/// `debug!("panic occurred: {msg}")`.
pub const PANIC_OCCURRED: &str = "panic occurred: ";
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
/// This prefix is used by `impl Display for PanicInfo<'_>`.
#[cfg(not(feature = "panic-messages"))]
pub const PANICKED_AT: &str = "panicked at ";
}

#[cfg(feature = "debug")]
use constants::*;

/// Panic handler for nightly Rust.
#[cfg(feature = "debug")]
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(feature = "panic-messages")]
let message = panic_info.message();

let msg = match (message, panic_info.location()) {
(Some(msg), Some(loc)) => format!(
"'{:?}', {}:{}:{}",
msg,
loc.file(),
loc.line(),
loc.column()
),
(Some(msg), None) => format!("'{msg:?}'"),
(None, Some(loc)) => {
format!("{}:{}:{}", loc.file(), loc.line(), loc.column())
#[panic_handler]
pub fn panic(panic_info: &PanicInfo) -> ! {
use crate::prelude::{fmt::Write, mem::MaybeUninit, str};
use arrayvec::ArrayString;

let option = panic_info.message().zip(panic_info.location());
let (message, location) = unsafe { option.unwrap_unchecked() };
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved

fn itoa_u32(buffer: &mut [MaybeUninit<u8>; 10], mut n: u32) -> &str {
let mut idx = buffer.len();
loop {
idx -= 1;
unsafe { buffer.get_unchecked_mut(idx) }.write((n % 10) as u8 + b'0');
n /= 10;
if n == 0 {
break;
}
}
unsafe {
str::from_utf8_unchecked(
&*(buffer.get_unchecked(idx..) as *const [_] as *const _),
)
}
}

let mut debug_msg = ArrayString::<{ PANIC_OCCURRED.len() + TRIMMED_MAX_LEN }>::new();
let mut buffer = unsafe { MaybeUninit::uninit().assume_init() };
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved

let _ = debug_msg.try_push_str(PANIC_OCCURRED);
let _ = debug_msg.try_push_str("'");
let _ = write!(&mut debug_msg, "{message}");
let _ = debug_msg.try_push_str("', ");
let _ = debug_msg.try_push_str(location.file());
let _ = debug_msg.try_push_str(":");
let _ = debug_msg.try_push_str(itoa_u32(&mut buffer, location.line()));
let _ = debug_msg.try_push_str(":");
let _ = debug_msg.try_push_str(itoa_u32(&mut buffer, location.column()));
ark0f marked this conversation as resolved.
Show resolved Hide resolved

let _ = ext::debug(&debug_msg);

let msg = unsafe { debug_msg.get_unchecked(PANIC_OCCURRED.len()..) };
ext::panic(&msg)
}

/// Panic handler for stable Rust <1.73.
#[rustversion::before(1.73)]
#[cfg(feature = "debug")]
#[cfg(not(feature = "panic-messages"))]
#[panic_handler]
pub fn panic(panic_info: &PanicInfo) -> ! {
use crate::prelude::fmt::Write;
use arrayvec::ArrayString;

static_assertions::const_assert!(PANICKED_AT.len() == (PANIC_OCCURRED.len() - 4));

let mut debug_msg = ArrayString::<{ PANIC_OCCURRED.len() + TRIMMED_MAX_LEN }>::new();

let _ = debug_msg.try_push_str(&PANIC_OCCURRED[..4]);
let _ = write!(&mut debug_msg, "{panic_info}");

let src = (&PANIC_OCCURRED[4..]).as_bytes();
let dest = unsafe {
debug_msg
.as_bytes_mut()
.get_unchecked_mut(4..PANIC_OCCURRED.len())
};
dest.copy_from_slice(src);
ark0f marked this conversation as resolved.
Show resolved Hide resolved
ark0f marked this conversation as resolved.
Show resolved Hide resolved

let _ = ext::debug(&debug_msg);

let msg = unsafe { debug_msg.get_unchecked(PANIC_OCCURRED.len()..) };
ext::panic(&msg)
}

/// Panic handler for stable Rust >=1.73.
#[rustversion::since(1.73)]
#[cfg(feature = "debug")]
#[cfg(not(feature = "panic-messages"))]
#[panic_handler]
pub fn panic(panic_info: &PanicInfo) -> ! {
use crate::prelude::{fmt::Write, str};
use arrayvec::ArrayString;

let mut default_panic_msg =
ArrayString::<{ PANICKED_AT.len() + TRIMMED_MAX_LEN }>::new();
let _ = write!(&mut default_panic_msg, "{panic_info}");

fn parse_panic_msg(msg: &str) -> Option<(&str, &str)> {
const NEEDLE: [u8; 2] = *b":\n";

let msg_bytes = msg.as_bytes();
msg_bytes
.windows(NEEDLE.len())
.position(|window| NEEDLE.eq(window))
.map(|pos| unsafe {
(
str::from_utf8_unchecked(
msg_bytes.get_unchecked(PANICKED_AT.len()..pos),
),
str::from_utf8_unchecked(
msg_bytes.get_unchecked((pos + NEEDLE.len())..),
),
)
})
}
_ => ext::panic("no info"),
};

crate::debug!("panic occurred: {msg}");
ext::panic(&msg)
let option = parse_panic_msg(&default_panic_msg);
let (location, message) = unsafe { option.unwrap_unchecked() };
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved

let mut debug_msg = ArrayString::<{ PANIC_OCCURRED.len() + TRIMMED_MAX_LEN }>::new();

let _ = debug_msg.try_push_str(PANIC_OCCURRED);
let _ = debug_msg.try_push_str("'");
let _ = debug_msg.try_push_str(message);
let _ = debug_msg.try_push_str("', ");
let _ = debug_msg.try_push_str(location);

let _ = ext::debug(&debug_msg);

let msg = unsafe { debug_msg.get_unchecked(PANIC_OCCURRED.len()..) };
ext::panic(&msg)
}
}
}
#[cfg(feature = "panic-handler")]
pub use panic_handler::*;
2 changes: 1 addition & 1 deletion gstd/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
//! Common modules for each Gear smart contract.

pub mod errors;
#[cfg(target_arch = "wasm32")]
#[cfg(feature = "common-handlers")]
breathx marked this conversation as resolved.
Show resolved Hide resolved
mod handlers;
pub mod primitives;
2 changes: 1 addition & 1 deletion gstd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
#![no_std]
#![warn(missing_docs)]
#![cfg_attr(
all(target_arch = "wasm32", feature = "panic-messages",),
all(target_arch = "wasm32", feature = "panic-messages"),
feature(panic_info_message)
)]
#![cfg_attr(
Expand Down