Unverified Commit 2458e0c0 authored by Andrew Jones's avatar Andrew Jones Committed by GitHub
Browse files

Implement `seal_debug_message` (#792)



* Implement `seal_debug_message`

* Update docs

* Fmt

* Fix debug_print macro

* review: use newline char
Co-authored-by: default avatarRobin Freyler <robin.freyler@gmail.com>

* Fix example

* Revert to newline string

* Fmt

* Single call to debug_print for debug_println!

* Add missing ReturnCode, still need to handle it

* Inline debug_println!

* If logging is disabled then subsequent calls will be a no-op

* Fmt

* Fix missing error match in experimental off-chain

* Add safety comment to debug_message

* Only re-export ink_prelude::format, and explain

* Satisfy clippy

* Encapsulate DEBUG_ENABLED global in module

* Move seal_denug_message to unstable module

* Update unstable and safety comments

* Add more comments about the required features to be enabled on the node runtime

* Add `ink-debug` feature, make debug messages a noop if not enabled

* Fmt

* Noop macro formatting

* Enable debug printing for std

* Comment formatting

* Encapsulate static variable inside the function

* Fmt

* Remove debug_assert!(true) for disabled macros
Co-authored-by: default avatarRobin Freyler <robin.freyler@gmail.com>
parent d4fd3f78
Pipeline #141392 passed with stages
in 28 minutes and 48 seconds
...@@ -91,6 +91,9 @@ define_error_codes! { ...@@ -91,6 +91,9 @@ define_error_codes! {
CodeNotFound = 7, CodeNotFound = 7,
/// The account that was called is either no contract (e.g. user account) or is a tombstone. /// The account that was called is either no contract (e.g. user account) or is a tombstone.
NotCallable = 8, NotCallable = 8,
/// The call to `seal_debug_message` had no effect because debug message
/// recording was disabled.
LoggingDisabled = 9,
} }
/// The raw return code returned by the host side. /// The raw return code returned by the host side.
...@@ -317,10 +320,10 @@ impl Engine { ...@@ -317,10 +320,10 @@ impl Engine {
unimplemented!("off-chain environment does not yet support `restore_to`"); unimplemented!("off-chain environment does not yet support `restore_to`");
} }
/// Prints the given contents to the console log. /// Records the given debug message and appends to stdout.
pub fn println(&mut self, content: &str) { pub fn debug_message(&mut self, message: &str) {
self.debug_info.record_println(String::from(content)); self.debug_info.record_debug_message(String::from(message));
println!("{}", content); print!("{}", message);
} }
/// Conduct the BLAKE-2 256-bit hash and place the result into `output`. /// Conduct the BLAKE-2 256-bit hash and place the result into `output`.
......
...@@ -33,41 +33,41 @@ pub struct EmittedEvent { ...@@ -33,41 +33,41 @@ pub struct EmittedEvent {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct RecordedPrintlns { pub struct RecordedDebugMessages {
printlns: Vec<String>, debug_messages: Vec<String>,
} }
impl RecordedPrintlns { impl RecordedDebugMessages {
// Creates a new `Engine instance. // Creates a new `Engine instance.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
printlns: Vec::new(), debug_messages: Vec::new(),
} }
} }
// Records a new println. // Records a new debug message.
pub fn record(&mut self, println: String) { pub fn record(&mut self, message: String) {
self.printlns.push(println); self.debug_messages.push(message);
} }
// Clears all recorded printlns. // Clears all recorded debug messages.
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.printlns.clear(); self.debug_messages.clear();
} }
} }
impl Default for RecordedPrintlns { impl Default for RecordedDebugMessages {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl IntoIterator for RecordedPrintlns { impl IntoIterator for RecordedDebugMessages {
type Item = String; type Item = String;
type IntoIter = std::vec::IntoIter<Self::Item>; type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.printlns.into_iter() self.debug_messages.into_iter()
} }
} }
...@@ -76,7 +76,7 @@ pub struct DebugInfo { ...@@ -76,7 +76,7 @@ pub struct DebugInfo {
/// Emitted events recorder. /// Emitted events recorder.
emitted_events: Vec<EmittedEvent>, emitted_events: Vec<EmittedEvent>,
/// Emitted print messages recorder. /// Emitted print messages recorder.
emitted_printlns: RecordedPrintlns, emitted_debug_messages: RecordedDebugMessages,
/// The total number of reads to the storage. /// The total number of reads to the storage.
count_reads: HashMap<AccountId, usize>, count_reads: HashMap<AccountId, usize>,
/// The total number of writes to the storage. /// The total number of writes to the storage.
...@@ -96,7 +96,7 @@ impl DebugInfo { ...@@ -96,7 +96,7 @@ impl DebugInfo {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
emitted_events: Vec::new(), emitted_events: Vec::new(),
emitted_printlns: RecordedPrintlns::new(), emitted_debug_messages: RecordedDebugMessages::new(),
count_reads: HashMap::new(), count_reads: HashMap::new(),
count_writes: HashMap::new(), count_writes: HashMap::new(),
cells_per_account: HashMap::new(), cells_per_account: HashMap::new(),
...@@ -108,7 +108,7 @@ impl DebugInfo { ...@@ -108,7 +108,7 @@ impl DebugInfo {
self.count_reads.clear(); self.count_reads.clear();
self.count_writes.clear(); self.count_writes.clear();
self.emitted_events.clear(); self.emitted_events.clear();
self.emitted_printlns.clear(); self.emitted_debug_messages.clear();
self.cells_per_account.clear(); self.cells_per_account.clear();
} }
...@@ -159,9 +159,9 @@ impl DebugInfo { ...@@ -159,9 +159,9 @@ impl DebugInfo {
.unwrap_or(None) .unwrap_or(None)
} }
/// Records a println. /// Records a debug message.
pub fn record_println(&mut self, println: String) { pub fn record_debug_message(&mut self, message: String) {
self.emitted_printlns.record(println); self.emitted_debug_messages.record(message);
} }
/// Records an event. /// Records an event.
...@@ -215,9 +215,9 @@ impl Engine { ...@@ -215,9 +215,9 @@ impl Engine {
self.exec_context.callee() self.exec_context.callee()
} }
/// Returns the contents of the past performed environmental `println` in order. /// Returns the contents of the past performed environmental `debug_message` in order.
pub fn get_recorded_printlns(&self) -> RecordedPrintlns { pub fn get_emitted_debug_messages(&self) -> RecordedDebugMessages {
self.debug_info.emitted_printlns.clone() self.debug_info.emitted_debug_messages.clone()
} }
/// Returns the recorded emitted events in order. /// Returns the recorded emitted events in order.
......
...@@ -115,10 +115,10 @@ fn transfer() { ...@@ -115,10 +115,10 @@ fn transfer() {
} }
#[test] #[test]
fn printlns() { fn debug_messages() {
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.println("foobar"); engine.debug_message("foobar");
let mut recorded = engine.get_recorded_printlns().into_iter(); let mut recorded = engine.get_emitted_debug_messages().into_iter();
assert_eq!(recorded.next(), Some("foobar".into())); assert_eq!(recorded.next(), Some("foobar".into()));
assert_eq!(recorded.next(), None); assert_eq!(recorded.next(), None);
} }
......
...@@ -62,4 +62,6 @@ std = [ ...@@ -62,4 +62,6 @@ std = [
"sha3", "sha3",
"blake2", "blake2",
] ]
# Enable contract debug messages via `debug_print!` and `debug_println!`.
ink-debug = []
ink-experimental-engine = ["ink_engine"] ink-experimental-engine = ["ink_engine"]
...@@ -553,10 +553,10 @@ where ...@@ -553,10 +553,10 @@ where
}) })
} }
/// Prints the given contents to the environmental log. /// Appends the given message to the debug message buffer.
pub fn debug_println(content: &str) { pub fn debug_message(message: &str) {
<EnvInstance as OnInstance>::on_instance(|instance| { <EnvInstance as OnInstance>::on_instance(|instance| {
EnvBackend::println(instance, content) EnvBackend::debug_message(instance, message)
}) })
} }
......
...@@ -119,8 +119,15 @@ pub trait EnvBackend { ...@@ -119,8 +119,15 @@ pub trait EnvBackend {
where where
R: scale::Encode; R: scale::Encode;
/// Prints the given contents to the console log. /// Emit a custom debug message.
fn println(&mut self, content: &str); ///
/// The message is appended to the debug buffer which is then supplied to the calling RPC
/// client. This buffer is also printed as a debug message to the node console if the
/// `debug` log level is enabled for the `runtime::contracts` target.
///
/// If debug message recording is disabled in the contracts pallet, which is always the case
/// when the code is executing on-chain, then this will have no effect.
fn debug_message(&mut self, content: &str);
/// Conducts the crypto hash of the given input and stores the result in `output`. /// Conducts the crypto hash of the given input and stores the result in `output`.
fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type) fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
......
...@@ -111,6 +111,7 @@ impl From<ext::Error> for crate::Error { ...@@ -111,6 +111,7 @@ impl From<ext::Error> for crate::Error {
ext::Error::NewContractNotFunded => Self::NewContractNotFunded, ext::Error::NewContractNotFunded => Self::NewContractNotFunded,
ext::Error::CodeNotFound => Self::CodeNotFound, ext::Error::CodeNotFound => Self::CodeNotFound,
ext::Error::NotCallable => Self::NotCallable, ext::Error::NotCallable => Self::NotCallable,
ext::Error::LoggingDisabled => Self::LoggingDisabled,
} }
} }
} }
...@@ -227,8 +228,8 @@ impl EnvBackend for EnvInstance { ...@@ -227,8 +228,8 @@ impl EnvBackend for EnvInstance {
) )
} }
fn println(&mut self, content: &str) { fn debug_message(&mut self, message: &str) {
self.engine.println(content) self.engine.debug_message(message)
} }
fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type) fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
......
...@@ -23,7 +23,7 @@ use crate::{ ...@@ -23,7 +23,7 @@ use crate::{
Result, Result,
}; };
use core::fmt::Debug; use core::fmt::Debug;
use ink_engine::test_api::RecordedPrintlns; use ink_engine::test_api::RecordedDebugMessages;
use std::panic::UnwindSafe; use std::panic::UnwindSafe;
/// Record for an emitted event. /// Record for an emitted event.
...@@ -129,10 +129,10 @@ where ...@@ -129,10 +129,10 @@ where
unimplemented!("off-chain environment does not yet support `set_block_entropy`"); unimplemented!("off-chain environment does not yet support `set_block_entropy`");
} }
/// Returns the contents of the past performed environmental `println` in order. /// Returns the contents of the past performed environmental debug messages in order.
pub fn recorded_printlns() -> RecordedPrintlns { pub fn recorded_debug_messages() -> RecordedDebugMessages {
<EnvInstance as OnInstance>::on_instance(|instance| { <EnvInstance as OnInstance>::on_instance(|instance| {
instance.engine.get_recorded_printlns() instance.engine.get_emitted_debug_messages()
}) })
} }
......
...@@ -14,53 +14,53 @@ ...@@ -14,53 +14,53 @@
use ink_prelude::string::String; use ink_prelude::string::String;
/// A debug console used to print console contents and store them. /// A debug buffer used to store debug messages and print them to stdout.
pub struct Console { pub struct DebugBuffer {
/// The buffer to store the already pasted contents. /// The buffer to store the emitted debug messages.
past_prints: Vec<String>, past_debug_messages: Vec<String>,
} }
impl Console { impl DebugBuffer {
/// Creates a new empty console. /// Creates a new empty console.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
past_prints: Vec::new(), past_debug_messages: Vec::new(),
} }
} }
/// Resets the console to uninitialized state. /// Resets the debug buffer to uninitialized state.
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.past_prints.clear(); self.past_debug_messages.clear();
} }
/// Prints the contents to the actual console and stores them. /// Prints the message to stdout and stores it.
pub fn println(&mut self, contents: &str) { pub fn debug_message(&mut self, message: &str) {
self.past_prints.push(contents.to_string()); self.past_debug_messages.push(message.to_string());
println!("{}", contents); print!("{}", message);
} }
/// Returns an iterator over the past console prints. /// Returns an iterator over the past debug messages.
pub fn past_prints(&self) -> PastPrints { pub fn past_debug_messages(&self) -> DebugMessages {
PastPrints::new(self) DebugMessages::new(self)
} }
} }
/// Iterator over the past prints to the console. /// Iterator over the past debug messages.
pub struct PastPrints<'a> { pub struct DebugMessages<'a> {
/// Iterator over the past printlns. /// Iterator over the past debug messages.
iter: core::slice::Iter<'a, String>, iter: core::slice::Iter<'a, String>,
} }
impl<'a> PastPrints<'a> { impl<'a> DebugMessages<'a> {
/// Creates a new iterator over the past console prints. /// Creates a new iterator over the past debug messages.
fn new(console: &'a Console) -> Self { fn new(console: &'a DebugBuffer) -> Self {
Self { Self {
iter: console.past_prints.iter(), iter: console.past_debug_messages.iter(),
} }
} }
} }
impl<'a> Iterator for PastPrints<'a> { impl<'a> Iterator for DebugMessages<'a> {
type Item = &'a str; type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
...@@ -68,13 +68,13 @@ impl<'a> Iterator for PastPrints<'a> { ...@@ -68,13 +68,13 @@ impl<'a> Iterator for PastPrints<'a> {
} }
} }
impl<'a> ExactSizeIterator for PastPrints<'a> { impl<'a> ExactSizeIterator for DebugMessages<'a> {
fn len(&self) -> usize { fn len(&self) -> usize {
self.iter.len() self.iter.len()
} }
} }
impl<'a> DoubleEndedIterator for PastPrints<'a> { impl<'a> DoubleEndedIterator for DebugMessages<'a> {
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back().map(AsRef::as_ref) self.iter.next_back().map(AsRef::as_ref)
} }
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
mod accounts; mod accounts;
mod block; mod block;
mod chain_spec; mod chain_spec;
mod console; mod debug_buf;
mod events; mod events;
mod exec_context; mod exec_context;
...@@ -30,9 +30,9 @@ pub use self::{ ...@@ -30,9 +30,9 @@ pub use self::{
}, },
block::Block, block::Block,
chain_spec::ChainSpec, chain_spec::ChainSpec,
console::{ debug_buf::{
Console, DebugBuffer,
PastPrints, DebugMessages,
}, },
events::{ events::{
EmittedEvent, EmittedEvent,
......
...@@ -175,8 +175,8 @@ impl EnvBackend for EnvInstance { ...@@ -175,8 +175,8 @@ impl EnvBackend for EnvInstance {
std::process::exit(flags.into_u32() as i32) std::process::exit(flags.into_u32() as i32)
} }
fn println(&mut self, content: &str) { fn debug_message(&mut self, message: &str) {
self.console.println(content) self.debug_buf.debug_message(message)
} }
fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type) fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
......
...@@ -28,8 +28,8 @@ pub use self::{ ...@@ -28,8 +28,8 @@ pub use self::{
call_data::CallData, call_data::CallData,
db::{ db::{
AccountError, AccountError,
DebugMessages,
EmittedEvent, EmittedEvent,
PastPrints,
}, },
typed_encoded::TypedEncodedError, typed_encoded::TypedEncodedError,
}; };
...@@ -40,7 +40,7 @@ use self::{ ...@@ -40,7 +40,7 @@ use self::{
AccountsDb, AccountsDb,
Block, Block,
ChainSpec, ChainSpec,
Console, DebugBuffer,
EmittedEventsRecorder, EmittedEventsRecorder,
ExecContext, ExecContext,
}, },
...@@ -84,8 +84,8 @@ pub struct EnvInstance { ...@@ -84,8 +84,8 @@ pub struct EnvInstance {
chain_spec: ChainSpec, chain_spec: ChainSpec,
/// The blocks of the chain. /// The blocks of the chain.
blocks: Vec<Block>, blocks: Vec<Block>,
/// The console to print debug contents. /// The debug buffer to collect debug messages and print them to stdout.
console: Console, debug_buf: DebugBuffer,
/// Handler for registered chain extensions. /// Handler for registered chain extensions.
chain_extension_handler: ChainExtensionHandler, chain_extension_handler: ChainExtensionHandler,
/// Emitted events recorder. /// Emitted events recorder.
...@@ -102,7 +102,7 @@ impl EnvInstance { ...@@ -102,7 +102,7 @@ impl EnvInstance {
exec_context: Vec::new(), exec_context: Vec::new(),
chain_spec: ChainSpec::uninitialized(), chain_spec: ChainSpec::uninitialized(),
blocks: Vec::new(), blocks: Vec::new(),
console: Console::new(), debug_buf: DebugBuffer::new(),
chain_extension_handler: ChainExtensionHandler::new(), chain_extension_handler: ChainExtensionHandler::new(),
emitted_events: EmittedEventsRecorder::new(), emitted_events: EmittedEventsRecorder::new(),
clear_storage_disabled: false, clear_storage_disabled: false,
...@@ -133,7 +133,7 @@ impl EnvInstance { ...@@ -133,7 +133,7 @@ impl EnvInstance {
self.exec_context.clear(); self.exec_context.clear();
self.chain_spec.reset(); self.chain_spec.reset();
self.blocks.clear(); self.blocks.clear();
self.console.reset(); self.debug_buf.reset();
self.chain_extension_handler.reset(); self.chain_extension_handler.reset();
self.emitted_events.reset(); self.emitted_events.reset();
self.clear_storage_disabled = false; self.clear_storage_disabled = false;
......
...@@ -215,8 +215,8 @@ where ...@@ -215,8 +215,8 @@ where
Ok(()) Ok(())
} }
/// Returns the contents of the past performed environmental `println` in order. /// Returns the contents of the past performed environmental debug messages in order.
pub fn recorded_printlns() -> impl Iterator<Item = String> { pub fn recorded_debug_messages() -> impl Iterator<Item = String> {
<EnvInstance as OnInstance>::on_instance(|instance| { <EnvInstance as OnInstance>::on_instance(|instance| {
// We return a clone of the recorded strings instead of // We return a clone of the recorded strings instead of
// references to them since this would require the whole `on_instance` // references to them since this would require the whole `on_instance`
...@@ -224,8 +224,8 @@ pub fn recorded_printlns() -> impl Iterator<Item = String> { ...@@ -224,8 +224,8 @@ pub fn recorded_printlns() -> impl Iterator<Item = String> {
// ultimately allow leaking those `'static` references to the outside // ultimately allow leaking those `'static` references to the outside
// and potentially lead to terrible bugs such as iterator invalidation. // and potentially lead to terrible bugs such as iterator invalidation.
instance instance
.console .debug_buf
.past_prints() .past_debug_messages()
.map(ToOwned::to_owned) .map(ToOwned::to_owned)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter() .into_iter()
......
...@@ -76,6 +76,9 @@ define_error_codes! { ...@@ -76,6 +76,9 @@ define_error_codes! {
CodeNotFound = 7, CodeNotFound = 7,
/// The account that was called is either no contract (e.g. user account) or is a tombstone. /// The account that was called is either no contract (e.g. user account) or is a tombstone.
NotCallable = 8, NotCallable = 8,