Skip to content

Commit

Permalink
feat(gcore): Stack-based logging (#4351)
Browse files Browse the repository at this point in the history
  • Loading branch information
ark0f authored Nov 26, 2024
1 parent de47744 commit 147bbbd
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 47 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ ark-ec = { version = "0.4.2", default-features = false }
ark-ff = { version = "0.4.2", default-features = false }
ark-scale = { version = "0.0.12", default-features = false }
sha2 = { version = "0.10.8", default-features = false }
arrayvec = { version = "0.7.4", default-features = false }

# Published deps
#
Expand Down
108 changes: 101 additions & 7 deletions gcore/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ impl AsRawPtr for ReservationId {}

/// Extensions for additional features.
pub mod ext {
#[cfg(any(feature = "debug", debug_assertions))]
use crate::errors::{Error, Result};
use crate::stack_buffer;
use core::{
fmt::{self, Write as _},
mem::MaybeUninit,
};
use gear_stack_buffer::MAX_BUFFER_SIZE;

/// Add a `data` string to the debug log.
///
Expand All @@ -50,16 +54,52 @@ pub mod ext {
///
/// #[no_mangle]
/// extern "C" fn handle() {
/// ext::debug("Hello, world!").expect("Unable to log");
/// ext::debug("Hello, world!");
/// }
/// ```
#[cfg(any(feature = "debug", debug_assertions))]
pub fn debug(data: &str) -> Result<()> {
let data_len = data.len().try_into().map_err(|_| Error::SyscallUsage)?;
pub fn debug(data: &str) {
unsafe { gsys::gr_debug(data.as_ptr(), data.len() as u32) }
}

/// Same as [`debug`] but uses a stack-allocated buffer.
///
/// Note: message size is limited to
/// [`MAX_BUFFER_SIZE`](crate::stack_buffer::MAX_BUFFER_SIZE).
/// Message is truncated if it exceeds maximum buffer size.
#[cfg(any(feature = "debug", debug_assertions))]
pub fn stack_debug(args: fmt::Arguments<'_>) {
struct StackFmtWriter<'a> {
buf: &'a mut [MaybeUninit<u8>],
pos: usize,
}

impl fmt::Write for StackFmtWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let upper_bound = (self.pos + s.len()).min(MAX_BUFFER_SIZE);
if let Some(buf) = self.buf.get_mut(self.pos..upper_bound) {
let buf = buf as *mut [MaybeUninit<u8>] as *mut [u8];
let s = &s.as_bytes()[..buf.len()];

// SAFETY: we only write to uninitialized memory
unsafe {
(*buf).copy_from_slice(s);
}

unsafe { gsys::gr_debug(data.as_ptr(), data_len) }
self.pos += buf.len();
}

Ok(())
Ok(())
}
}

stack_buffer::with_byte_buffer(MAX_BUFFER_SIZE, |buf| {
let mut writer = StackFmtWriter { buf, pos: 0 };
writer.write_fmt(args).expect("fmt failed");

// SAFETY: buffer was initialized via `write_fmt` and limited by `pos`
unsafe { gsys::gr_debug(writer.buf.as_ptr().cast(), writer.pos as u32) }
});
}

/// Panic
Expand Down Expand Up @@ -113,3 +153,57 @@ pub mod ext {
unsafe { gsys::gr_oom_panic() }
}
}

/// Add a debug message to the log.
///
/// Debug messages are available only if the program is compiled
/// in debug mode.
///
/// ```shell
/// cargo build --debug
/// cargo build --features=debug
/// ```
///
/// You can see the debug messages when running the program using the `gtest`
/// crate. To see these messages when executing the program on the node, you
/// should run the node with the `RUST_LOG="gwasm=debug"` environment variable.
///
/// Note: message size is limited to
/// [`MAX_BUFFER_SIZE`](crate::stack_buffer::MAX_BUFFER_SIZE).
/// Message is truncated if it exceeds maximum buffer size.
///
/// If you need bigger message size, consider using `gstd::heap_debug!()` macro.
///
/// # Examples
///
/// ```
/// use gcore::debug;
///
/// #[no_mangle]
/// extern "C" fn handle() {
/// debug!("String literal");
///
/// let value = 42;
/// debug!("{value}");
///
/// debug!("Formatted: value = {value}");
/// }
/// ```
#[cfg(any(feature = "debug", debug_assertions))]
#[macro_export]
macro_rules! debug {
($fmt:expr) => {
$crate::ext::stack_debug(format_args!($fmt))
};
($fmt:expr, $($args:tt)*) => {
$crate::ext::stack_debug(format_args!($fmt, $($args)*))
};
}

#[cfg(not(any(feature = "debug", debug_assertions)))]
#[allow(missing_docs)]
#[macro_export]
macro_rules! debug {
($fmt:expr) => {};
($fmt:expr, $($args:tt)*) => {};
}
2 changes: 1 addition & 1 deletion gstd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repository.workspace = true
rust-version.workspace = true

[dependencies]
arrayvec = { version = "0.7.4", default-features = false, optional = true }
arrayvec = { workspace = true, optional = true }
const_format = { version = "0.2.32", optional = true }
document-features = { version = "0.2.10", optional = true }
galloc.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions gstd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ pub use async_runtime::{handle_reply_with_hook, message_loop};
pub use common::errors;
pub use config::{Config, SYSTEM_RESERVE};
pub use gcore::{
ext, ActorId, BlockCount, BlockNumber, CodeId, EnvVars, Gas, GasMultiplier, MessageId, Percent,
Ss58Address, Value,
debug, ext, ActorId, BlockCount, BlockNumber, CodeId, EnvVars, Gas, GasMultiplier, MessageId,
Percent, Ss58Address, Value,
};
pub use gstd_codegen::{actor_id, async_init, async_main};
pub use prelude::*;
Expand Down
42 changes: 10 additions & 32 deletions gstd/src/macros/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,24 @@

/// Add a debug message to the log.
///
/// Note that debug messages are available only if the program is compiled
/// in debug mode.
///
/// ```shell
/// cargo build --debug
/// cargo build --features=debug
/// ```
///
/// You can see the debug messages when running the program using the `gtest`
/// crate. To see these messages when executing the program on the node, you
/// should run the node with the `RUST_LOG="gwasm=debug"` environment variable.
///
/// # Examples
///
/// ```
/// use gstd::debug;
///
/// #[no_mangle]
/// extern "C" fn handle() {
/// debug!("String literal");
///
/// let value = 42;
/// debug!("{value}");
///
/// debug!("Formatted: value = {value}");
/// }
/// ```
/// Same as [`gcore::debug`] but uses heap instead of stack for formatting.
#[cfg(any(feature = "debug", debug_assertions))]
#[macro_export]
macro_rules! debug {
($($arg:tt)*) => {
$crate::ext::debug(&$crate::format!($($arg)*)).unwrap()
macro_rules! heap_debug {
($fmt:expr) => {
$crate::ext::debug(&$crate::format!($fmt))
};
($fmt:expr, $($args:tt)*) => {
$crate::ext::debug(&$crate::format!($fmt, $($args)*))
};
}

#[cfg(not(any(feature = "debug", debug_assertions)))]
#[allow(missing_docs)]
#[macro_export]
macro_rules! debug {
($($arg:tt)*) => {};
macro_rules! heap_debug {
($fmt:expr) => {};
($fmt:expr, $($args:tt)*) => {};
}

/// Prints and returns the value of a given expression for quick and dirty
Expand Down
6 changes: 1 addition & 5 deletions stack-buffer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ extern "C" {
/// by the [`MAX_BUFFER_SIZE`] constant.
#[cfg(not(any(feature = "compile-alloca", target_arch = "wasm32")))]
unsafe extern "C" fn c_with_alloca(_size: usize, callback: Callback, data: *mut c_void) {
// Same as `MaybeUninit::uninit_array()`.
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
// safe because the type we are claiming to have initialized here is a
// bunch of `MaybeUninit`s, which do not require initialization.
let mut buffer = MaybeUninit::<[MaybeUninit<u8>; MAX_BUFFER_SIZE]>::uninit().assume_init();
let mut buffer = [MaybeUninit::uninit(); MAX_BUFFER_SIZE];
callback(buffer.as_mut_ptr(), data);
}

Expand Down

0 comments on commit 147bbbd

Please sign in to comment.