Tracing for wasm with bridging to native (#6916)

* implement events handling, implement parent_id for spans & events

* add events to sp_io::storage

* update test

* add tests

* adjust limit

* let tracing crate handle parent_ids

* re-enable current-id tracking

* add test for threads with CurrentSpan

* fix log level

* remove redundant check for non wasm traces

* remove duplicate definition in test

* Adding conditional events API

* prefer explicit parent_id over current,

enhance test

* limit changes to client::tracing event implementation

* remove From impl due to fallback required on parent_id

* make tracing codecable

* replace with global tracing

* new tracing interface

* impl TracingSubscriber in client

* implement access to global TracingSubscriber from primitives

* span for wasm

* increment towards Wasm Tracing Subscriber implementation

* increment, remove sp-tracing from runtime-interface

* increment, it compiles

* attained original functionality with new mechanism

* implement remaining TracingSubscriber functions

* remove spans from decl_module

* add handling for encoded values

* Revert "replace with global tracing"

This reverts commit 8824a60deea54d9b437407a21c8ceaf6a1902ee5.

* Wasm Side Tracing

* tracing on wasm

* enable tracing wasm on node-runtime

* export all the macros in std

* tracing subscriber on wasm-side only

* pass spans and events over and record them

* reactivate previous code and  cleanup

* further cleaning up

* extend the span macros, activate through executive

* tracking the actual extrinsic, too

* style

* fixing tests

* spaces -> tabs

* attempting to reactivate params

* activate our tests in CI

* some passing

* tests passing

* with core lazy

* global tracer for wasm side with pass over

* fixing metadata referencing

* remove const_fn feature requirement

* reenable dispatch traces

* reset client tracing

* further cleaning up

* fixing runtime-test

* move tracing-build setup into runtime-test

* Merge DebugWriter from tracing and frame-support, move to sp-std

* remove dangling fixme

* Docs for tracing primitives

* cleaning up a bit more

* Wasm interface docs

* optimise docs.rs setup

* adding tracing flags to uncomment

* remove brace

* fixing imports

* fixing broken syntax

* add required modules

* nicer formatting

* better target management

* adding low level storage tracing events into frame

* add custom Debug impl for WasmMetadata

* cloning profiler

* adding info about cloning profiler

* using in-scope for within calls

* proper time tracing, cleaning up println

* allow to disable tracing on runtime_interface-macro

* disable tracing for wasm-tracing-interface

* simplify wasm-tracing-api

* update client to new interface

* fixing docs and tests for sp-tracing

* update integration tests

* re-activating enter_span

* dropping FIXME, it's documented

* fix formatting

* fix formatting

* fix imports

* more debug info

* inform wasm about it being disabled by returning 1

* only one tracer, but enabled multi-all support

* make trait pub again for tests

* Apply suggestions from code review

Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>

* fixing wasm doc tests for proper usage

* remove unnecessary import

* fixing formatting

* minor style fixes

* downgrading wabt

* update error message for UI

* Fix interface test

* next attempt to fix macros

* geee

* revert tracing on hashed for future PR

* remove local macros, use originals

* we are able to convert to static items

* implement more WasmValue types

* adding support to convert str, debug and encoded values

* more minor fixes

* revert unsafe 'static making

* fix indentation

* remove commented lines

* bump all them tracing versions

* cleaning up docs and info

* document new flag

* the new layered system handles span cloning better

* Apply suggestions from code review

Co-authored-by: David <dvdplm@gmail.com>

Co-authored-by: Matt Rutherford <mattrutherford@users.noreply.github.com>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
Co-authored-by: David <dvdplm@gmail.com>
This commit is contained in:
Benjamin Kampmann
2020-09-18 08:10:59 +02:00
committed by GitHub
parent 649bee1a1e
commit a9c73113a8
34 changed files with 1207 additions and 436 deletions
@@ -26,21 +26,59 @@
//! 3. The [`PassByEnum`](derive.PassByInner.html) derive macro for implementing `PassBy` with `Enum`.
//! 4. The [`PassByInner`](derive.PassByInner.html) derive macro for implementing `PassBy` with `Inner`.
use syn::{parse_macro_input, ItemTrait, DeriveInput};
use syn::{parse_macro_input, ItemTrait, DeriveInput, Result, Token};
use syn::parse::{Parse, ParseStream};
mod pass_by;
mod runtime_interface;
mod utils;
struct Options {
wasm_only: bool,
tracing: bool
}
impl Options {
fn unpack(self) -> (bool, bool) {
(self.wasm_only, self.tracing)
}
}
impl Default for Options {
fn default() -> Self {
Options { wasm_only: false, tracing: true }
}
}
impl Parse for Options {
fn parse(input: ParseStream) -> Result<Self> {
let mut res = Self::default();
while !input.is_empty() {
let lookahead = input.lookahead1();
if lookahead.peek(runtime_interface::keywords::wasm_only) {
let _ = input.parse::<runtime_interface::keywords::wasm_only>();
res.wasm_only = true;
} else if lookahead.peek(runtime_interface::keywords::no_tracing) {
let _ = input.parse::<runtime_interface::keywords::no_tracing>();
res.tracing = false;
} else if lookahead.peek(Token![,]) {
let _ = input.parse::<Token![,]>();
} else {
return Err(lookahead.error())
}
}
Ok(res)
}
}
#[proc_macro_attribute]
pub fn runtime_interface(
attrs: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let trait_def = parse_macro_input!(input as ItemTrait);
let wasm_only = parse_macro_input!(attrs as Option<runtime_interface::keywords::wasm_only>);
let (wasm_only, tracing) = parse_macro_input!(attrs as Options).unpack();
runtime_interface::runtime_interface_impl(trait_def, wasm_only.is_some())
runtime_interface::runtime_interface_impl(trait_def, wasm_only, tracing)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}
@@ -61,4 +99,4 @@ pub fn pass_by_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream
pub fn pass_by_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
pass_by::enum_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into()
}
}
@@ -46,7 +46,7 @@ use std::iter;
/// Generate one bare function per trait method. The name of the bare function is equal to the name
/// of the trait method.
pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool, tracing: bool) -> Result<TokenStream> {
let trait_name = &trait_def.ident;
let runtime_interface = get_runtime_interface(trait_def)?;
@@ -63,7 +63,7 @@ pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream
// earlier versions compatibility dispatch (only std variant)
let result: Result<TokenStream> = runtime_interface.all_versions().try_fold(token_stream?, |mut t, (version, method)|
{
t.extend(function_std_impl(trait_name, method, version, is_wasm_only)?);
t.extend(function_std_impl(trait_name, method, version, is_wasm_only, tracing)?);
Ok(t)
});
@@ -145,6 +145,7 @@ fn function_std_impl(
method: &TraitItemMethod,
version: u32,
is_wasm_only: bool,
tracing: bool,
) -> Result<TokenStream> {
let function_name = create_function_ident_with_version(&method.sig.ident, version);
let function_name_str = function_name.to_string();
@@ -168,13 +169,21 @@ fn function_std_impl(
let attrs = method.attrs.iter().filter(|a| !a.path.is_ident("version"));
// Don't make the function public accessible when this is a wasm only interface.
let call_to_trait = generate_call_to_trait(trait_name, method, version, is_wasm_only);
let call_to_trait = if !tracing {
call_to_trait
} else {
parse_quote!(
#crate_::sp_tracing::within_span! { #crate_::sp_tracing::trace_span!(#function_name_str);
#call_to_trait
}
)
};
Ok(
quote_spanned! { method.span() =>
#[cfg(feature = "std")]
#( #attrs )*
fn #function_name( #( #args, )* ) #return_value {
#crate_::sp_tracing::enter_span!(#function_name_str);
#call_to_trait
}
}
@@ -227,7 +227,6 @@ fn generate_host_function_implementation(
__function_context__: &mut dyn #crate_::sp_wasm_interface::FunctionContext,
args: &mut dyn Iterator<Item = #crate_::sp_wasm_interface::Value>,
) -> std::result::Result<Option<#crate_::sp_wasm_interface::Value>, String> {
#crate_::sp_tracing::enter_span!(#name);
#( #wasm_to_ffi_values )*
#( #ffi_to_host_values )*
#host_function_call
@@ -33,14 +33,20 @@ mod trait_decl_impl;
pub mod keywords {
// Custom keyword `wasm_only` that can be given as attribute to [`runtime_interface`].
syn::custom_keyword!(wasm_only);
// Disable tracing-macros added to the [`runtime_interface`] by specifying this optional entry
syn::custom_keyword!(no_tracing);
}
/// Implementation of the `runtime_interface` attribute.
///
/// It expects the trait definition the attribute was put above and if this should be an wasm only
/// interface.
pub fn runtime_interface_impl(trait_def: ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
let bare_functions = bare_function_interface::generate(&trait_def, is_wasm_only)?;
pub fn runtime_interface_impl(
trait_def: ItemTrait,
is_wasm_only: bool,
tracing: bool,
) -> Result<TokenStream> {
let bare_functions = bare_function_interface::generate(&trait_def, is_wasm_only, tracing)?;
let crate_include = generate_runtime_interface_include();
let mod_name = Ident::new(&trait_def.ident.to_string().to_snake_case(), Span::call_site());
let trait_decl_impl = trait_decl_impl::process(&trait_def, is_wasm_only)?;
@@ -284,6 +284,14 @@ pub use sp_std;
/// 1. The generated functions are not callable from the native side.
/// 2. The trait as shown above is not implemented for `Externalities` and is instead implemented
/// for `FunctionExecutor` (from `sp-wasm-interface`).
///
/// # Disable tracing
/// By addding `no_tracing` to the list of options you can prevent the wasm-side interface from
/// generating the default `sp-tracing`-calls. Note that this is rarely needed but only meant for
/// the case when that would create a circular dependency. You usually _do not_ want to add this
/// flag, as tracing doesn't cost you anything by default anyways (it is added as a no-op) but is
/// super useful for debugging later.
///
pub use sp_runtime_interface_proc_macro::runtime_interface;
#[doc(hidden)]
@@ -20,4 +20,5 @@ sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-ma
sp-runtime = { version = "2.0.0-rc6", path = "../../runtime" }
sp-core = { version = "2.0.0-rc6", path = "../../core" }
sp-io = { version = "2.0.0-rc6", path = "../../io" }
tracing = "0.1.18"
tracing = "0.1.19"
tracing-core = "0.1.15"
@@ -18,8 +18,6 @@
//! Integration tests for runtime interface primitives
#![cfg(test)]
#![cfg(test)]
use sp_runtime_interface::*;
use sp_runtime_interface_test_wasm::{wasm_binary_unwrap, test_api::HostFunctions};
@@ -157,14 +155,26 @@ fn test_versionining_with_new_host_works() {
#[test]
fn test_tracing() {
use tracing::span::Id as SpanId;
use std::fmt;
use tracing::{span::Id as SpanId};
use tracing_core::field::{Field, Visit};
#[derive(Clone)]
struct TracingSubscriber(Arc<Mutex<Inner>>);
struct FieldConsumer(&'static str, Option<String>);
impl Visit for FieldConsumer {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if field.name() == self.0 {
self.1 = Some(format!("{:?}", value))
}
}
}
#[derive(Default)]
struct Inner {
spans: HashSet<&'static str>,
spans: HashSet<String>,
}
impl tracing::subscriber::Subscriber for TracingSubscriber {
@@ -173,7 +183,9 @@ fn test_tracing() {
fn new_span(&self, span: &tracing::span::Attributes) -> tracing::Id {
let mut inner = self.0.lock().unwrap();
let id = SpanId::from_u64((inner.spans.len() + 1) as _);
inner.spans.insert(span.metadata().name());
let mut f = FieldConsumer("name", None);
span.record(&mut f);
inner.spans.insert(f.1.unwrap_or_else(||span.metadata().name().to_owned()));
id
}
@@ -196,5 +208,4 @@ fn test_tracing() {
let inner = subscriber.0.lock().unwrap();
assert!(inner.spans.contains("return_input_version_1"));
assert!(inner.spans.contains("ext_test_api_return_input_version_1"));
}
}