mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 17:41:08 +00:00
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:
committed by
GitHub
parent
649bee1a1e
commit
a9c73113a8
@@ -336,6 +336,9 @@ check-web-wasm:
|
||||
- time cargo build --target=wasm32-unknown-unknown -p sc-telemetry
|
||||
# Note: the command below is a bit weird because several Cargo issues prevent us from compiling the node in a more straight-forward way.
|
||||
- time cargo +nightly build --manifest-path=bin/node/cli/Cargo.toml --no-default-features --features browser --target=wasm32-unknown-unknown -Z features=itarget
|
||||
# with-tracing must be explicitly activated, we run a test to ensure this works as expected in both cases
|
||||
- time cargo +nightly test --manifest-path primitives/tracing/Cargo.toml --no-default-features
|
||||
- time cargo +nightly test --manifest-path primitives/tracing/Cargo.toml --no-default-features --features=with-tracing
|
||||
- sccache -s
|
||||
|
||||
test-full-crypto-feature:
|
||||
|
||||
Generated
+14
-28
@@ -6024,27 +6024,6 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rental"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f"
|
||||
dependencies = [
|
||||
"rental-impl",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rental-impl"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "retain_mut"
|
||||
version = "0.1.1"
|
||||
@@ -7208,6 +7187,7 @@ dependencies = [
|
||||
"sp-runtime",
|
||||
"sp-session",
|
||||
"sp-state-machine",
|
||||
"sp-tracing",
|
||||
"sp-transaction-pool",
|
||||
"sp-trie",
|
||||
"sp-utils",
|
||||
@@ -7303,6 +7283,7 @@ dependencies = [
|
||||
"slog",
|
||||
"sp-tracing",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
@@ -8153,6 +8134,8 @@ dependencies = [
|
||||
"sp-tracing",
|
||||
"sp-trie",
|
||||
"sp-wasm-interface",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8296,6 +8279,7 @@ dependencies = [
|
||||
"sp-runtime-interface-test-wasm-deprecated",
|
||||
"sp-state-machine",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8432,8 +8416,10 @@ name = "sp-tracing"
|
||||
version = "2.0.0-rc6"
|
||||
dependencies = [
|
||||
"log",
|
||||
"rental",
|
||||
"parity-scale-codec",
|
||||
"sp-std",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
@@ -9388,9 +9374,9 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.18"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0aae59226cf195d8e74d4b34beae1859257efb4e5fed3f147d2dc2c7d372178"
|
||||
checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
@@ -9411,9 +9397,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.12"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2734b5a028fa697686f16c6d18c2c6a3c7e41513f9a213abb6754c4acb3c8d7"
|
||||
checksum = "5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
@@ -9521,9 +9507,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
|
||||
[[package]]
|
||||
name = "trybuild"
|
||||
version = "1.0.30"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe777c4e2060f44d83892be1189f96200be8ed3d99569d5c2d5ee26e62c0ea9"
|
||||
checksum = "b7d30fe369fd650072b352b1a9cb9587669de6b89be3b8225544012c1c45292d"
|
||||
dependencies = [
|
||||
"dissimilar",
|
||||
"glob",
|
||||
|
||||
@@ -41,7 +41,7 @@ hex-literal = "0.3.1"
|
||||
log = "0.4.8"
|
||||
rand = "0.7.2"
|
||||
structopt = { version = "0.3.8", optional = true }
|
||||
tracing = "0.1.18"
|
||||
tracing = "0.1.19"
|
||||
parking_lot = "0.10.0"
|
||||
|
||||
# primitives
|
||||
|
||||
@@ -88,6 +88,7 @@ sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
with-tracing = [ "frame-executive/with-tracing" ]
|
||||
std = [
|
||||
"sp-authority-discovery/std",
|
||||
"pallet-authority-discovery/std",
|
||||
|
||||
@@ -46,7 +46,7 @@ test-case = "0.3.3"
|
||||
sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" }
|
||||
sp-tracing = { version = "2.0.0-rc6", path = "../../primitives/tracing" }
|
||||
sc-tracing = { version = "2.0.0-rc6", path = "../tracing" }
|
||||
tracing = "0.1.18"
|
||||
tracing = "0.1.19"
|
||||
tracing-subscriber = "0.2.10"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -29,5 +29,7 @@ std = [
|
||||
"sp-io/std",
|
||||
"sp-sandbox/std",
|
||||
"sp-std/std",
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
"sp-allocator/std",
|
||||
]
|
||||
|
||||
@@ -17,10 +17,21 @@
|
||||
use wasm_builder_runner::WasmBuilder;
|
||||
|
||||
fn main() {
|
||||
// regular build
|
||||
WasmBuilder::new()
|
||||
.with_current_project()
|
||||
.with_wasm_builder_from_crates_or_path("2.0.0", "../../../utils/wasm-builder")
|
||||
.export_heap_base()
|
||||
.import_memory()
|
||||
.build()
|
||||
.build();
|
||||
|
||||
// and building with tracing activated
|
||||
WasmBuilder::new()
|
||||
.with_current_project()
|
||||
.with_wasm_builder_from_crates_or_path("2.0.0", "../../../utils/wasm-builder")
|
||||
.export_heap_base()
|
||||
.import_memory()
|
||||
.set_file_name("wasm_binary_with_tracing.rs")
|
||||
.append_to_rust_flags("--cfg feature=\\\"with-tracing\\\"")
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -254,11 +254,11 @@ sp_core::wasm_export_functions! {
|
||||
}
|
||||
|
||||
fn test_enter_span() -> u64 {
|
||||
wasm_tracing::enter_span("integration_test_span_target", "integration_test_span_name")
|
||||
wasm_tracing::enter_span(Default::default())
|
||||
}
|
||||
|
||||
fn test_exit_span(span_id: u64) {
|
||||
wasm_tracing::exit_span(span_id)
|
||||
wasm_tracing::exit(span_id)
|
||||
}
|
||||
|
||||
fn returns_mutable_static() -> u64 {
|
||||
|
||||
@@ -681,7 +681,7 @@ fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) {
|
||||
// Create subscriber with wasm_tracing disabled
|
||||
let test_subscriber = tracing_subscriber::fmt().finish().with(
|
||||
sc_tracing::ProfilingLayer::new_with_handler(
|
||||
Box::new(handler), "integration_test_span_target"
|
||||
Box::new(handler), "default"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -690,49 +690,9 @@ fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) {
|
||||
let mut ext = TestExternalities::default();
|
||||
let mut ext = ext.ext();
|
||||
|
||||
// Test tracing disabled
|
||||
assert!(!sp_tracing::wasm_tracing_enabled());
|
||||
|
||||
let span_id = call_in_wasm(
|
||||
"test_enter_span",
|
||||
&[],
|
||||
wasm_method,
|
||||
&mut ext,
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
0u64.encode(),
|
||||
span_id
|
||||
);
|
||||
// Repeat to check span id always 0 when deactivated
|
||||
let span_id = call_in_wasm(
|
||||
"test_enter_span",
|
||||
&[],
|
||||
wasm_method,
|
||||
&mut ext,
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
0u64.encode(),
|
||||
span_id
|
||||
);
|
||||
|
||||
call_in_wasm(
|
||||
"test_exit_span",
|
||||
&span_id.encode(),
|
||||
wasm_method,
|
||||
&mut ext,
|
||||
).unwrap();
|
||||
// Check span has not been recorded
|
||||
let len = traces.lock().unwrap().len();
|
||||
assert_eq!(len, 0);
|
||||
|
||||
// Test tracing enabled
|
||||
sp_tracing::set_wasm_tracing(true);
|
||||
|
||||
let span_id = call_in_wasm(
|
||||
"test_enter_span",
|
||||
&[],
|
||||
Default::default(),
|
||||
wasm_method,
|
||||
&mut ext,
|
||||
).unwrap();
|
||||
@@ -756,8 +716,7 @@ fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) {
|
||||
|
||||
let span_datum = traces.lock().unwrap().pop().unwrap();
|
||||
let values = span_datum.values;
|
||||
assert_eq!(span_datum.target, "integration_test_span_target");
|
||||
assert_eq!(span_datum.name, "integration_test_span_name");
|
||||
assert_eq!(span_datum.target, "default");
|
||||
assert_eq!(span_datum.name, "");
|
||||
assert_eq!(values.bool_values.get("wasm").unwrap(), &true);
|
||||
assert_eq!(values.bool_values.get("is_valid_trace").unwrap(), &true);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,8 @@ sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" }
|
||||
sc-offchain = { version = "2.0.0-rc6", path = "../offchain" }
|
||||
prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0-rc6"}
|
||||
sc-tracing = { version = "2.0.0-rc6", path = "../tracing" }
|
||||
tracing = "0.1.18"
|
||||
sp-tracing = { version = "2.0.0-rc6", path = "../../primitives/tracing" }
|
||||
tracing = "0.1.19"
|
||||
parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] }
|
||||
|
||||
[target.'cfg(not(target_os = "unknown"))'.dependencies]
|
||||
|
||||
@@ -19,7 +19,8 @@ rustc-hash = "1.1.0"
|
||||
serde = "1.0.101"
|
||||
serde_json = "1.0.41"
|
||||
slog = { version = "2.5.2", features = ["nested-values"] }
|
||||
tracing = "0.1.18"
|
||||
tracing = "0.1.19"
|
||||
tracing-core = "0.1.13"
|
||||
tracing-subscriber = "0.2.10"
|
||||
sp-tracing = { version = "2.0.0-rc6", path = "../../primitives/tracing" }
|
||||
sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" }
|
||||
|
||||
@@ -40,8 +40,7 @@ use tracing::{
|
||||
use tracing_subscriber::{CurrentSpan, layer::{Layer, Context}};
|
||||
|
||||
use sc_telemetry::{telemetry, SUBSTRATE_INFO};
|
||||
use sp_tracing::proxy::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER};
|
||||
|
||||
use sp_tracing::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER};
|
||||
const ZERO_DURATION: Duration = Duration::from_nanos(0);
|
||||
|
||||
/// Responsible for assigning ids to new spans, which are not re-used.
|
||||
@@ -275,12 +274,6 @@ impl<S: Subscriber> Layer<S> for ProfilingLayer {
|
||||
fn new_span(&self, attrs: &Attributes<'_>, id: &Id, _ctx: Context<S>) {
|
||||
let mut values = Values::default();
|
||||
attrs.record(&mut values);
|
||||
// If this is a wasm trace, check if target/level is enabled
|
||||
if let Some(wasm_target) = values.string_values.get(WASM_TARGET_KEY) {
|
||||
if !self.check_target(wasm_target, attrs.metadata().level()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
let span_datum = SpanDatum {
|
||||
id: id.clone(),
|
||||
parent_id: attrs.parent().cloned().or_else(|| self.current_span.id()),
|
||||
@@ -327,18 +320,13 @@ impl<S: Subscriber> Layer<S> for ProfilingLayer {
|
||||
fn on_exit(&self, span: &Id, _ctx: Context<S>) {
|
||||
self.current_span.exit();
|
||||
let end_time = Instant::now();
|
||||
let mut span_data = self.span_data.lock();
|
||||
if let Some(mut s) = span_data.get_mut(&span) {
|
||||
s.overall_time = end_time - s.start_time + s.overall_time;
|
||||
}
|
||||
}
|
||||
|
||||
fn on_close(&self, span: Id, _ctx: Context<S>) {
|
||||
let span_datum = {
|
||||
let mut span_data = self.span_data.lock();
|
||||
span_data.remove(&span)
|
||||
};
|
||||
|
||||
if let Some(mut span_datum) = span_datum {
|
||||
span_datum.overall_time += end_time - span_datum.start_time;
|
||||
if span_datum.name == WASM_TRACE_IDENTIFIER {
|
||||
span_datum.values.bool_values.insert("wasm".to_owned(), true);
|
||||
if let Some(n) = span_datum.values.string_values.remove(WASM_NAME_KEY) {
|
||||
@@ -355,6 +343,10 @@ impl<S: Subscriber> Layer<S> for ProfilingLayer {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn on_close(&self, span: Id, ctx: Context<S>) {
|
||||
self.on_exit(&span, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/// TraceHandler for sending span data to the logger
|
||||
|
||||
@@ -168,23 +168,28 @@ where
|
||||
Client::Api: TaggedTransactionQueue<Block>,
|
||||
sp_api::ApiErrorFor<Client, Block>: Send + std::fmt::Display,
|
||||
{
|
||||
sp_tracing::enter_span!("validate_transaction");
|
||||
let runtime_api = client.runtime_api();
|
||||
let has_v2 = sp_tracing::tracing_span! { "check_version";
|
||||
runtime_api
|
||||
.has_api_with::<dyn TaggedTransactionQueue<Block, Error=()>, _>(&at, |v| v >= 2)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
sp_tracing::within_span!(sp_tracing::Level::TRACE, "validate_transaction";
|
||||
{
|
||||
let runtime_api = client.runtime_api();
|
||||
let has_v2 = sp_tracing::within_span! { sp_tracing::Level::TRACE, "check_version";
|
||||
runtime_api
|
||||
.has_api_with::<dyn TaggedTransactionQueue<Block, Error=()>, _>(&at, |v| v >= 2)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
sp_tracing::enter_span!("runtime::validate_transaction");
|
||||
let res = if has_v2 {
|
||||
runtime_api.validate_transaction(&at, source, uxt)
|
||||
} else {
|
||||
#[allow(deprecated)] // old validate_transaction
|
||||
runtime_api.validate_transaction_before_version_2(&at, uxt)
|
||||
};
|
||||
let res = sp_tracing::within_span!(
|
||||
sp_tracing::Level::TRACE, "runtime::validate_transaction";
|
||||
{
|
||||
if has_v2 {
|
||||
runtime_api.validate_transaction(&at, source, uxt)
|
||||
} else {
|
||||
#[allow(deprecated)] // old validate_transaction
|
||||
runtime_api.validate_transaction_before_version_2(&at, uxt)
|
||||
}
|
||||
});
|
||||
|
||||
res.map_err(|e| Error::RuntimeApi(e.to_string()))
|
||||
res.map_err(|e| Error::RuntimeApi(e.to_string()))
|
||||
})
|
||||
}
|
||||
|
||||
impl<Client, Block> FullChainApi<Client, Block>
|
||||
|
||||
@@ -20,6 +20,7 @@ sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../pr
|
||||
sp-tracing = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/tracing" }
|
||||
sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" }
|
||||
sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" }
|
||||
sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.3.1"
|
||||
@@ -32,11 +33,15 @@ sp-version = { version = "2.0.0-rc6", path = "../../primitives/version" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
with-tracing = [
|
||||
"sp-tracing/with-tracing"
|
||||
]
|
||||
std = [
|
||||
"codec/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"serde",
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
"sp-tracing/std",
|
||||
"sp-std/std",
|
||||
|
||||
@@ -207,6 +207,8 @@ where
|
||||
{
|
||||
/// Start the execution of a particular block.
|
||||
pub fn initialize_block(header: &System::Header) {
|
||||
sp_io::init_tracing();
|
||||
sp_tracing::enter_span!(sp_tracing::Level::TRACE, "init_block");
|
||||
let digests = Self::extract_pre_digest(&header);
|
||||
Self::initialize_block_impl(
|
||||
header.number(),
|
||||
@@ -270,6 +272,7 @@ where
|
||||
}
|
||||
|
||||
fn initial_checks(block: &Block) {
|
||||
sp_tracing::enter_span!(sp_tracing::Level::TRACE, "initial_checks");
|
||||
let header = block.header();
|
||||
|
||||
// Check that `parent_hash` is correct.
|
||||
@@ -288,23 +291,28 @@ where
|
||||
|
||||
/// Actually execute all transitions for `block`.
|
||||
pub fn execute_block(block: Block) {
|
||||
Self::initialize_block(block.header());
|
||||
sp_io::init_tracing();
|
||||
sp_tracing::within_span! {
|
||||
sp_tracing::info_span!( "execute_block", ?block);
|
||||
{
|
||||
Self::initialize_block(block.header());
|
||||
|
||||
// any initial checks
|
||||
Self::initial_checks(&block);
|
||||
// any initial checks
|
||||
Self::initial_checks(&block);
|
||||
|
||||
let signature_batching = sp_runtime::SignatureBatching::start();
|
||||
let signature_batching = sp_runtime::SignatureBatching::start();
|
||||
|
||||
// execute extrinsics
|
||||
let (header, extrinsics) = block.deconstruct();
|
||||
Self::execute_extrinsics_with_book_keeping(extrinsics, *header.number());
|
||||
// execute extrinsics
|
||||
let (header, extrinsics) = block.deconstruct();
|
||||
Self::execute_extrinsics_with_book_keeping(extrinsics, *header.number());
|
||||
|
||||
if !signature_batching.verify() {
|
||||
panic!("Signature verification failed.");
|
||||
}
|
||||
if !signature_batching.verify() {
|
||||
panic!("Signature verification failed.");
|
||||
}
|
||||
|
||||
// any final checks
|
||||
Self::final_checks(&header);
|
||||
// any final checks
|
||||
Self::final_checks(&header);
|
||||
} };
|
||||
}
|
||||
|
||||
/// Execute given extrinsics and take care of post-extrinsics book-keeping.
|
||||
@@ -320,6 +328,8 @@ where
|
||||
/// Finalize the block - it is up the caller to ensure that all header fields are valid
|
||||
/// except state-root.
|
||||
pub fn finalize_block() -> System::Header {
|
||||
sp_io::init_tracing();
|
||||
sp_tracing::enter_span!( sp_tracing::Level::TRACE, "finalize_block" );
|
||||
<frame_system::Module<System>>::note_finished_extrinsics();
|
||||
let block_number = <frame_system::Module<System>>::block_number();
|
||||
<frame_system::Module<System> as OnFinalize<System::BlockNumber>>::on_finalize(block_number);
|
||||
@@ -335,6 +345,7 @@ where
|
||||
/// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt
|
||||
/// hashes.
|
||||
pub fn apply_extrinsic(uxt: Block::Extrinsic) -> ApplyExtrinsicResult {
|
||||
sp_io::init_tracing();
|
||||
let encoded = uxt.encode();
|
||||
let encoded_len = encoded.len();
|
||||
Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded))
|
||||
@@ -355,6 +366,10 @@ where
|
||||
encoded_len: usize,
|
||||
to_note: Option<Vec<u8>>,
|
||||
) -> ApplyExtrinsicResult {
|
||||
sp_tracing::enter_span!(
|
||||
sp_tracing::info_span!("apply_extrinsic",
|
||||
ext=?sp_core::hexdisplay::HexDisplay::from(&uxt.encode()))
|
||||
);
|
||||
// Verify that the signature is good.
|
||||
let xt = uxt.check(&Default::default())?;
|
||||
|
||||
@@ -377,6 +392,7 @@ where
|
||||
}
|
||||
|
||||
fn final_checks(header: &System::Header) {
|
||||
sp_tracing::enter_span!(sp_tracing::Level::TRACE, "final_checks");
|
||||
// remove temporaries
|
||||
let new_header = <frame_system::Module<System>>::finalize();
|
||||
|
||||
@@ -406,24 +422,32 @@ where
|
||||
source: TransactionSource,
|
||||
uxt: Block::Extrinsic,
|
||||
) -> TransactionValidity {
|
||||
use sp_tracing::tracing_span;
|
||||
sp_io::init_tracing();
|
||||
use sp_tracing::{enter_span, within_span};
|
||||
|
||||
sp_tracing::enter_span!("validate_transaction");
|
||||
enter_span!{ sp_tracing::Level::TRACE, "validate_transaction" };
|
||||
|
||||
let encoded_len = tracing_span!{ "using_encoded"; uxt.using_encoded(|d| d.len()) };
|
||||
let encoded_len = within_span!{ sp_tracing::Level::TRACE, "using_encoded";
|
||||
uxt.using_encoded(|d| d.len())
|
||||
};
|
||||
|
||||
let xt = tracing_span!{ "check"; uxt.check(&Default::default())? };
|
||||
let xt = within_span!{ sp_tracing::Level::TRACE, "check";
|
||||
uxt.check(&Default::default())
|
||||
}?;
|
||||
|
||||
let dispatch_info = tracing_span!{ "dispatch_info"; xt.get_dispatch_info() };
|
||||
let dispatch_info = within_span!{ sp_tracing::Level::TRACE, "dispatch_info";
|
||||
xt.get_dispatch_info()
|
||||
};
|
||||
|
||||
tracing_span! {
|
||||
"validate";
|
||||
within_span! {
|
||||
sp_tracing::Level::TRACE, "validate";
|
||||
xt.validate::<UnsignedValidator>(source, &dispatch_info, encoded_len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Start an offchain worker and generate extrinsics.
|
||||
pub fn offchain_worker(header: &System::Header) {
|
||||
sp_io::init_tracing();
|
||||
// We need to keep events available for offchain workers,
|
||||
// hence we initialize the block manually.
|
||||
// OffchainWorker RuntimeApi should skip initialization.
|
||||
|
||||
@@ -87,11 +87,11 @@
|
||||
//! native::print!("My struct: {:?}", x);
|
||||
//! ```
|
||||
|
||||
use sp_std::vec::Vec;
|
||||
use sp_std::fmt::{self, Debug};
|
||||
|
||||
pub use log::{info, debug, error, trace, warn};
|
||||
pub use crate::runtime_print as print;
|
||||
pub use sp_std::Writer;
|
||||
|
||||
/// Native-only logging.
|
||||
///
|
||||
@@ -132,9 +132,9 @@ macro_rules! runtime_print {
|
||||
($($arg:tt)+) => {
|
||||
{
|
||||
use core::fmt::Write;
|
||||
let mut w = $crate::debug::Writer::default();
|
||||
let mut w = $crate::sp_std::Writer::default();
|
||||
let _ = core::write!(&mut w, $($arg)+);
|
||||
w.print();
|
||||
sp_io::misc::print_utf8(&w.inner())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,24 +144,6 @@ pub fn debug(data: &impl Debug) {
|
||||
runtime_print!("{:?}", data);
|
||||
}
|
||||
|
||||
/// A target for `core::write!` macro - constructs a string in memory.
|
||||
#[derive(Default)]
|
||||
pub struct Writer(Vec<u8>);
|
||||
|
||||
impl fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.0.extend(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
/// Print the content of this `Writer` out.
|
||||
pub fn print(&self) {
|
||||
sp_io::misc::print_utf8(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime logger implementation - `log` crate backend.
|
||||
///
|
||||
/// The logger should be initialized if you want to display
|
||||
@@ -204,13 +186,13 @@ impl log::Log for RuntimeLogger {
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
use fmt::Write;
|
||||
let mut w = Writer::default();
|
||||
let mut w = sp_std::Writer::default();
|
||||
let _ = core::write!(&mut w, "{}", record.args());
|
||||
|
||||
sp_io::logging::log(
|
||||
record.level().into(),
|
||||
record.target(),
|
||||
&w.0,
|
||||
w.inner(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1275,7 +1275,7 @@ macro_rules! decl_module {
|
||||
for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
|
||||
{
|
||||
fn on_initialize(_block_number_not_used: <$trait_instance as $system::Trait>::BlockNumber) -> $return {
|
||||
$crate::sp_tracing::enter_span!("on_initialize");
|
||||
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_initialize"));
|
||||
{ $( $impl )* }
|
||||
}
|
||||
}
|
||||
@@ -1292,7 +1292,7 @@ macro_rules! decl_module {
|
||||
for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
|
||||
{
|
||||
fn on_initialize($param: $param_ty) -> $return {
|
||||
$crate::sp_tracing::enter_span!("on_initialize");
|
||||
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_initialize"));
|
||||
{ $( $impl )* }
|
||||
}
|
||||
}
|
||||
@@ -1319,7 +1319,7 @@ macro_rules! decl_module {
|
||||
for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
|
||||
{
|
||||
fn on_runtime_upgrade() -> $return {
|
||||
$crate::sp_tracing::enter_span!("on_runtime_upgrade");
|
||||
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_runtime_upgrade"));
|
||||
{ $( $impl )* }
|
||||
}
|
||||
}
|
||||
@@ -1375,7 +1375,7 @@ macro_rules! decl_module {
|
||||
for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
|
||||
{
|
||||
fn on_finalize(_block_number_not_used: <$trait_instance as $system::Trait>::BlockNumber) {
|
||||
$crate::sp_tracing::enter_span!("on_finalize");
|
||||
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_finalize"));
|
||||
{ $( $impl )* }
|
||||
}
|
||||
}
|
||||
@@ -1392,7 +1392,7 @@ macro_rules! decl_module {
|
||||
for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
|
||||
{
|
||||
fn on_finalize($param: $param_ty) {
|
||||
$crate::sp_tracing::enter_span!("on_finalize");
|
||||
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_finalize"));
|
||||
{ $( $impl )* }
|
||||
}
|
||||
}
|
||||
@@ -1465,7 +1465,7 @@ macro_rules! decl_module {
|
||||
$vis fn $name(
|
||||
$origin: $origin_ty $(, $param: $param_ty )*
|
||||
) -> $crate::dispatch::DispatchResult {
|
||||
$crate::sp_tracing::enter_span!(stringify!($name));
|
||||
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!(stringify!($name)));
|
||||
{ $( $impl )* }
|
||||
Ok(())
|
||||
}
|
||||
@@ -1484,7 +1484,7 @@ macro_rules! decl_module {
|
||||
) => {
|
||||
$(#[$fn_attr])*
|
||||
$vis fn $name($origin: $origin_ty $(, $param: $param_ty )* ) -> $result {
|
||||
$crate::sp_tracing::enter_span!(stringify!($name));
|
||||
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!(stringify!($name)));
|
||||
$( $impl )*
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../
|
||||
sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" }
|
||||
sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/core" }
|
||||
sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/std" }
|
||||
trybuild = "1.0.17"
|
||||
trybuild = "1.0.33"
|
||||
pretty_assertions = "0.6.1"
|
||||
rustversion = "1.0.0"
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//! Infinite precision unsigned integer for substrate runtime.
|
||||
|
||||
use num_traits::Zero;
|
||||
use sp_std::{cmp::Ordering, ops, prelude::*, cell::RefCell, convert::TryFrom};
|
||||
use sp_std::{cmp::Ordering, ops, prelude::*, vec, cell::RefCell, convert::TryFrom};
|
||||
|
||||
// A sensible value for this would be half of the dword size of the host machine. Since the
|
||||
// runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively
|
||||
|
||||
@@ -28,6 +28,8 @@ sp-tracing = { version = "2.0.0-rc6", default-features = false, path = "../traci
|
||||
log = { version = "0.4.8", optional = true }
|
||||
futures = { version = "0.3.1", features = ["thread-pool"], optional = true }
|
||||
parking_lot = { version = "0.10.0", optional = true }
|
||||
tracing = { version = "0.1.19", default-features = false }
|
||||
tracing-core = { version = "0.1.15", default-features = false}
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@@ -42,11 +44,18 @@ std = [
|
||||
"sp-runtime-interface/std",
|
||||
"sp-externalities",
|
||||
"sp-wasm-interface/std",
|
||||
"sp-tracing/std",
|
||||
"tracing/std",
|
||||
"tracing-core/std",
|
||||
"log",
|
||||
"futures",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
with-tracing = [
|
||||
"sp-tracing/with-tracing"
|
||||
]
|
||||
|
||||
# These two features are used for `no_std` builds for the environments which already provides
|
||||
# `#[panic_handler]`, `#[alloc_error_handler]` and `#[global_allocator]`.
|
||||
#
|
||||
|
||||
@@ -32,6 +32,9 @@ use sp_std::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_std::ops::Deref;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use tracing;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use sp_core::{
|
||||
crypto::Pair,
|
||||
@@ -52,6 +55,7 @@ use sp_core::{
|
||||
use sp_trie::{TrieConfiguration, trie_types::Layout};
|
||||
|
||||
use sp_runtime_interface::{runtime_interface, Pointer};
|
||||
use sp_runtime_interface::pass_by::PassBy;
|
||||
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
@@ -1004,55 +1008,156 @@ pub trait Logging {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
sp_externalities::decl_extension! {
|
||||
/// Extension to allow running traces in wasm via Proxy
|
||||
pub struct TracingProxyExt(sp_tracing::proxy::TracingProxy);
|
||||
#[derive(Encode, Decode)]
|
||||
/// Crossing is a helper wrapping any Encode-Decodeable type
|
||||
/// for transferring over the wasm barrier.
|
||||
pub struct Crossing<T: Encode + Decode>(T);
|
||||
|
||||
impl<T: Encode + Decode> PassBy for Crossing<T> {
|
||||
type PassBy = sp_runtime_interface::pass_by::Codec<Self>;
|
||||
}
|
||||
|
||||
/// Interface that provides functions for profiling the runtime.
|
||||
#[runtime_interface]
|
||||
impl<T: Encode + Decode> Crossing<T> {
|
||||
|
||||
/// Convert into the inner type
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
// useful for testing
|
||||
impl<T> core::default::Default for Crossing<T>
|
||||
where T: core::default::Default + Encode + Decode
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Interface to provide tracing facilities for wasm. Modelled after tokios `tracing`-crate
|
||||
/// interfaces. See `sp-tracing` for more information.
|
||||
#[runtime_interface(wasm_only, no_tracing)]
|
||||
pub trait WasmTracing {
|
||||
/// To create and enter a `tracing` span, using `sp_tracing::proxy`
|
||||
/// Returns 0 value to indicate that no further traces should be attempted
|
||||
fn enter_span(&mut self, target: &str, name: &str) -> u64 {
|
||||
if sp_tracing::wasm_tracing_enabled() {
|
||||
match self.extension::<TracingProxyExt>() {
|
||||
Some(proxy) => return proxy.enter_span(target, name),
|
||||
None => {
|
||||
if self.register_extension(TracingProxyExt(sp_tracing::proxy::TracingProxy::new())).is_ok() {
|
||||
if let Some(proxy) = self.extension::<TracingProxyExt>() {
|
||||
return proxy.enter_span(target, name);
|
||||
}
|
||||
} else {
|
||||
log::warn!(
|
||||
target: "tracing",
|
||||
"Unable to register extension: TracingProxyExt"
|
||||
);
|
||||
}
|
||||
}
|
||||
/// Whether the span described in `WasmMetadata` should be traced wasm-side
|
||||
/// On the host converts into a static Metadata and checks against the global `tracing` dispatcher.
|
||||
///
|
||||
/// When returning false the calling code should skip any tracing-related execution. In general
|
||||
/// within the same block execution this is not expected to change and it doesn't have to be
|
||||
/// checked more than once per metadata. This exists for optimisation purposes but is still not
|
||||
/// cheap as it will jump the wasm-native-barrier every time it is called. So an implementation might
|
||||
/// chose to cache the result for the execution of the entire block.
|
||||
fn enabled(&mut self, metadata: Crossing<sp_tracing::WasmMetadata>) -> bool {
|
||||
let metadata: &tracing_core::metadata::Metadata<'static> = (&metadata.into_inner()).into();
|
||||
tracing::dispatcher::get_default(|d| {
|
||||
d.enabled(metadata)
|
||||
})
|
||||
}
|
||||
|
||||
/// Open a new span with the given attributes. Return the u64 Id of the span.
|
||||
///
|
||||
/// On the native side this goes through the default `tracing` dispatcher to register the span
|
||||
/// and then calls `clone_span` with the ID to signal that we are keeping it around on the wasm-
|
||||
/// side even after the local span is dropped. The resulting ID is then handed over to the wasm-
|
||||
/// side.
|
||||
fn enter_span(&mut self, span: Crossing<sp_tracing::WasmEntryAttributes>) -> u64 {
|
||||
let span: tracing::Span = span.into_inner().into();
|
||||
match span.id() {
|
||||
Some(id) => tracing::dispatcher::get_default(|d| {
|
||||
// inform dispatch that we'll keep the ID around
|
||||
// then enter it immediately
|
||||
let final_id = d.clone_span(&id);
|
||||
d.enter(&final_id);
|
||||
final_id.into_u64()
|
||||
}),
|
||||
_ => {
|
||||
0
|
||||
}
|
||||
}
|
||||
log::debug!(
|
||||
target: "tracing",
|
||||
"Notify to runtime that tracing is disabled."
|
||||
);
|
||||
0
|
||||
}
|
||||
|
||||
/// Exit a `tracing` span, using `sp_tracing::proxy`
|
||||
fn exit_span(&mut self, id: u64) {
|
||||
if let Some(proxy) = self.extension::<TracingProxyExt>() {
|
||||
proxy.exit_span(id)
|
||||
} else {
|
||||
log::warn!(
|
||||
target: "tracing",
|
||||
"Unable to load extension: TracingProxyExt"
|
||||
);
|
||||
/// Emit the given event to the global tracer on the native side
|
||||
fn event(&mut self, event: Crossing<sp_tracing::WasmEntryAttributes>) {
|
||||
event.into_inner().emit();
|
||||
}
|
||||
|
||||
/// Signal that a given span-id has been exited. On native, this directly
|
||||
/// proxies the span to the global dispatcher.
|
||||
fn exit(&mut self, span: u64) {
|
||||
tracing::dispatcher::get_default(|d| {
|
||||
let id = tracing_core::span::Id::from_u64(span);
|
||||
d.exit(&id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature="std"), feature="with-tracing"))]
|
||||
mod tracing_setup {
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use tracing_core::{
|
||||
dispatcher::{Dispatch, set_global_default},
|
||||
span::{Id, Record, Attributes},
|
||||
Metadata, Event,
|
||||
};
|
||||
use super::{wasm_tracing, Crossing};
|
||||
|
||||
const TRACING_SET : AtomicBool = AtomicBool::new(false);
|
||||
|
||||
|
||||
/// The PassingTracingSubscriber implements `tracing_core::Subscriber`
|
||||
/// and pushes the information across the runtime interface to the host
|
||||
struct PassingTracingSubsciber;
|
||||
|
||||
impl tracing_core::Subscriber for PassingTracingSubsciber {
|
||||
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
|
||||
wasm_tracing::enabled(Crossing(metadata.into()))
|
||||
}
|
||||
fn new_span(&self, attrs: &Attributes<'_>) -> Id {
|
||||
Id::from_u64(wasm_tracing::enter_span(Crossing(attrs.into())))
|
||||
}
|
||||
fn enter(&self, span: &Id) {
|
||||
// Do nothing, we already entered the span previously
|
||||
}
|
||||
/// Not implemented! We do not support recording values later
|
||||
/// Will panic when used.
|
||||
fn record(&self, span: &Id, values: &Record<'_>) {
|
||||
unimplemented!{} // this usage is not supported
|
||||
}
|
||||
/// Not implemented! We do not support recording values later
|
||||
/// Will panic when used.
|
||||
fn record_follows_from(&self, span: &Id, follows: &Id) {
|
||||
unimplemented!{ } // this usage is not supported
|
||||
}
|
||||
fn event(&self, event: &Event<'_>) {
|
||||
wasm_tracing::event(Crossing(event.into()))
|
||||
}
|
||||
fn exit(&self, span: &Id) {
|
||||
wasm_tracing::exit(span.into_u64())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Initialize tracing of sp_tracing on wasm with `with-tracing` enabled.
|
||||
/// Can be called multiple times from within the same process and will only
|
||||
/// set the global bridging subscriber once.
|
||||
pub fn init_tracing() {
|
||||
if TRACING_SET.load(Ordering::Relaxed) == false {
|
||||
set_global_default(Dispatch::new(PassingTracingSubsciber {}))
|
||||
.expect("We only ever call this once");
|
||||
TRACING_SET.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(not(feature="std"), feature="with-tracing")))]
|
||||
mod tracing_setup {
|
||||
/// Initialize tracing of sp_tracing not necessary – noop. To enable build
|
||||
/// without std and with the `with-tracing`-feature.
|
||||
pub fn init_tracing() { }
|
||||
}
|
||||
|
||||
pub use tracing_setup::init_tracing;
|
||||
|
||||
/// Wasm-only interface that provides functions for interacting with the sandbox.
|
||||
#[runtime_interface(wasm_only)]
|
||||
pub trait Sandbox {
|
||||
|
||||
@@ -52,6 +52,7 @@ use crate::{ExtendedBalance, IdentifierT, StakedAssignment};
|
||||
use sp_arithmetic::traits::{Bounded, Zero};
|
||||
use sp_std::{
|
||||
collections::btree_map::{BTreeMap, Entry::*},
|
||||
vec,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
+12
-3
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
|
||||
#![cfg_attr(feature = "std",
|
||||
doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")]
|
||||
#![cfg_attr(not(feature = "std"),
|
||||
@@ -65,6 +64,30 @@ include!("../with_std.rs");
|
||||
#[cfg(not(feature = "std"))]
|
||||
include!("../without_std.rs");
|
||||
|
||||
|
||||
/// A target for `core::write!` macro - constructs a string in memory.
|
||||
#[derive(Default)]
|
||||
pub struct Writer(vec::Vec<u8>);
|
||||
|
||||
impl fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.0.extend(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
/// Access the content of this `Writer` e.g. for printout
|
||||
pub fn inner(&self) -> &vec::Vec<u8> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Convert into the content of this `Writer`
|
||||
pub fn into_inner(self) -> vec::Vec<u8> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Prelude of common useful imports.
|
||||
///
|
||||
/// This should include only things which are in the normal std prelude.
|
||||
|
||||
@@ -9,14 +9,33 @@ repository = "https://github.com/paritytech/substrate/"
|
||||
description = "Instrumentation primitives and macros for Substrate."
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
# let's default to wasm32
|
||||
default-target = "wasm32-unknown-unknown"
|
||||
# with the tracing enabled
|
||||
features = ["with-tracing"]
|
||||
# allowing for linux-gnu here, too, allows for `std` to show up as well
|
||||
targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
|
||||
|
||||
[dependencies]
|
||||
tracing = { version = "0.1.18", optional = true }
|
||||
rental = { version = "0.5.5", optional = true }
|
||||
sp-std = { version = "2.0.0-rc6", path = "../std", default-features = false}
|
||||
codec = { version = "1.3.1", package = "parity-scale-codec", default-features = false, features = ["derive"]}
|
||||
tracing = { version = "0.1.19", default-features = false }
|
||||
tracing-core = { version = "0.1.16", default-features = false }
|
||||
log = { version = "0.4.8", optional = true }
|
||||
tracing-subscriber = { version = "0.2.10", optional = true, features = ["tracing-log"] }
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = [ "tracing", "rental", "log", "tracing-subscriber" ]
|
||||
with-tracing = [
|
||||
"codec/derive",
|
||||
"codec/full",
|
||||
]
|
||||
std = [
|
||||
"with-tracing",
|
||||
"tracing/std",
|
||||
"tracing-core/std",
|
||||
"codec/std",
|
||||
"sp-std/std",
|
||||
"log",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
//! Substrate tracing primitives and macros.
|
||||
//!
|
||||
//! To trace functions or invidual code in Substrate, this crate provides [`tracing_span`]
|
||||
//! To trace functions or invidual code in Substrate, this crate provides [`within_span`]
|
||||
//! and [`enter_span`]. See the individual docs for how to use these macros.
|
||||
//!
|
||||
//! Note that to allow traces from wasm execution environment there are
|
||||
@@ -28,21 +28,80 @@
|
||||
//! Additionally, we have a const: `WASM_TRACE_IDENTIFIER`, which holds a span name used
|
||||
//! to signal that the 'actual' span name and target should be retrieved instead from
|
||||
//! the associated Fields mentioned above.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate rental;
|
||||
/// Tracing facilities and helpers.
|
||||
///
|
||||
/// This is modeled after the `tracing`/`tracing-core` interface and uses that more or
|
||||
/// less directly for the native side. Because of certain optimisations the these crates
|
||||
/// have done, the wasm implementation diverges slightly and is optimised for thtat use
|
||||
/// case (like being able to cross the wasm/native boundary via scale codecs).
|
||||
///
|
||||
/// One of said optimisations is that all macros will yield to a `noop` in non-std unless
|
||||
/// the `with-tracing` feature is explicitly activated. This allows you to just use the
|
||||
/// tracing wherever you deem fit and without any performance impact by default. Only if
|
||||
/// the specific `with-tracing`-feature is activated on this crate will it actually include
|
||||
/// the tracing code in the non-std environment.
|
||||
///
|
||||
/// Because of that optimisation, you should not use the `span!` and `span_*!` macros
|
||||
/// directly as they yield nothing without the feature present. Instead you should use
|
||||
/// `enter_span!` and `within_span!` – which would strip away even any parameter conversion
|
||||
/// you do within the span-definition (and thus optimise your performance). For your
|
||||
/// convineience you directly specify the `Level` and name of the span or use the full
|
||||
/// feature set of `span!`/`span_*!` on it:
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "fn wide span");
|
||||
/// {
|
||||
/// sp_tracing::enter_span!(sp_tracing::trace_span!("outer-span"));
|
||||
/// {
|
||||
/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "inner-span");
|
||||
/// // ..
|
||||
/// } // inner span exists here
|
||||
/// } // outer span exists here
|
||||
///
|
||||
/// sp_tracing::within_span! {
|
||||
/// sp_tracing::debug_span!("debug-span", you_can_pass="any params");
|
||||
/// 1 + 1;
|
||||
/// // some other complex code
|
||||
/// } // debug span ends here
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// # Setup
|
||||
///
|
||||
/// This project only provides the macros and facilities to manage tracing
|
||||
/// it doesn't implement the tracing subscriber or backend directly – that is
|
||||
/// up to the developer integrating it into a specific environment. In native
|
||||
/// this can and must be done through the regular `tracing`-facitilies, please
|
||||
/// see their documentation for details.
|
||||
///
|
||||
/// On the wasm-side we've adopted a similar approach of having a global
|
||||
/// `TracingSubscriber` that the macros call and that does the actual work
|
||||
/// of tracking. To provide your tracking, you must implement `TracingSubscriber`
|
||||
/// and call `set_tracing_subscriber` at the very beginning of your execution –
|
||||
/// the default subscriber is doing nothing, so any spans or events happening before
|
||||
/// will not be recorded!
|
||||
///
|
||||
|
||||
mod types;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[doc(hidden)]
|
||||
pub use tracing;
|
||||
use tracing;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod proxy;
|
||||
pub use tracing::{
|
||||
debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn, warn_span,
|
||||
span, event, Level, Span,
|
||||
};
|
||||
|
||||
pub use crate::types::{
|
||||
WasmMetadata, WasmEntryAttributes, WasmValuesSet, WasmValue, WasmFields, WasmLevel, WasmFieldName
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
/// Try to init a simple tracing subscriber with log compatibility layer.
|
||||
/// Ignores any error. Useful for testing.
|
||||
@@ -51,74 +110,127 @@ pub fn try_init_simple() {
|
||||
let _ = tracing_subscriber::fmt().with_writer(std::io::stderr).try_init();
|
||||
}
|
||||
|
||||
/// Flag to signal whether to run wasm tracing
|
||||
#[cfg(feature = "std")]
|
||||
static WASM_TRACING_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
pub use crate::types::{
|
||||
WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER
|
||||
};
|
||||
|
||||
|
||||
/// Runs given code within a tracing span, measuring it's execution time.
|
||||
///
|
||||
/// If tracing is not enabled, the code is still executed.
|
||||
/// If tracing is not enabled, the code is still executed. Pass in level and name or
|
||||
/// use any valid `sp_tracing::Span`followe by `;` and the code to execute,
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// sp_tracing::tracing_span! {
|
||||
/// sp_tracing::within_span! {
|
||||
/// sp_tracing::Level::TRACE,
|
||||
/// "test-span";
|
||||
/// 1 + 1;
|
||||
/// // some other complex code
|
||||
/// }
|
||||
///
|
||||
/// sp_tracing::within_span! {
|
||||
/// sp_tracing::span!(sp_tracing::Level::WARN, "warn-span", you_can_pass="any params");
|
||||
/// 1 + 1;
|
||||
/// // some other complex code
|
||||
/// }
|
||||
///
|
||||
/// sp_tracing::within_span! {
|
||||
/// sp_tracing::debug_span!("debug-span", you_can_pass="any params");
|
||||
/// 1 + 1;
|
||||
/// // some other complex code
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(any(feature = "std", feature = "with-tracing"))]
|
||||
#[macro_export]
|
||||
macro_rules! tracing_span {
|
||||
macro_rules! within_span {
|
||||
(
|
||||
$span:expr;
|
||||
$( $code:tt )*
|
||||
) => {
|
||||
$span.in_scope(||
|
||||
{
|
||||
$( $code )*
|
||||
}
|
||||
)
|
||||
};
|
||||
(
|
||||
$lvl:expr,
|
||||
$name:expr;
|
||||
$( $code:tt )*
|
||||
) => {
|
||||
{
|
||||
$crate::enter_span!($name);
|
||||
$( $code )*
|
||||
$crate::within_span!($crate::span!($crate::Level::TRACE, $name); $( $code )*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "std"), not(feature = "with-tracing")))]
|
||||
#[macro_export]
|
||||
macro_rules! within_span {
|
||||
(
|
||||
$span:stmt;
|
||||
$( $code:tt )*
|
||||
) => {
|
||||
$( $code )*
|
||||
};
|
||||
(
|
||||
$lvl:expr,
|
||||
$name:expr;
|
||||
$( $code:tt )*
|
||||
) => {
|
||||
$( $code )*
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Enter a span - noop for `no_std` without `with-tracing`
|
||||
#[cfg(all(not(feature = "std"), not(feature = "with-tracing")))]
|
||||
#[macro_export]
|
||||
macro_rules! enter_span {
|
||||
( $lvl:expr, $name:expr ) => ( );
|
||||
( $name:expr ) => ( ) // no-op
|
||||
}
|
||||
|
||||
/// Enter a span.
|
||||
///
|
||||
/// The span will be valid, until the scope is left.
|
||||
/// The span will be valid, until the scope is left. Use either level and name
|
||||
/// or pass in any valid `sp_tracing::Span` for extended usage. The span will
|
||||
/// be exited on drop – which is at the end of the block or to the next
|
||||
/// `enter_span!` calls, as this overwrites the local variable. For nested
|
||||
/// usage or to ensure the span closes at certain time either put it into a block
|
||||
/// or use `within_span!`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// sp_tracing::enter_span!("test-span");
|
||||
/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "test-span");
|
||||
/// // previous will be dropped here
|
||||
/// sp_tracing::enter_span!(
|
||||
/// sp_tracing::span!(sp_tracing::Level::DEBUG, "debug-span", params="value"));
|
||||
/// sp_tracing::enter_span!(sp_tracing::info_span!("info-span", params="value"));
|
||||
///
|
||||
/// {
|
||||
/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "outer-span");
|
||||
/// {
|
||||
/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "inner-span");
|
||||
/// // ..
|
||||
/// } // inner span exists here
|
||||
/// } // outer span exists here
|
||||
///
|
||||
/// ```
|
||||
#[cfg(any(feature = "std", feature = "with-tracing"))]
|
||||
#[macro_export]
|
||||
macro_rules! enter_span {
|
||||
( $name:expr ) => {
|
||||
let __tracing_span__ = $crate::if_tracing!(
|
||||
$crate::tracing::span!($crate::tracing::Level::TRACE, $name)
|
||||
);
|
||||
let __tracing_guard__ = $crate::if_tracing!(__tracing_span__.enter());
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the given code if the tracing dependency is enabled.
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! if_tracing {
|
||||
( $if:expr ) => {{ $if }}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[cfg(not(feature = "std"))]
|
||||
macro_rules! if_tracing {
|
||||
( $if:expr ) => {{}}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn wasm_tracing_enabled() -> bool {
|
||||
WASM_TRACING_ENABLED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn set_wasm_tracing(b: bool) {
|
||||
WASM_TRACING_ENABLED.store(b, Ordering::Relaxed)
|
||||
( $span:expr ) => {
|
||||
// Calling this twice in a row will overwrite (and drop) the earlier
|
||||
// that is a _documented feature_!
|
||||
let __within_span__ = $span;
|
||||
let __tracing_guard__ = __within_span__.enter();
|
||||
};
|
||||
( $lvl:expr, $name:expr ) => {
|
||||
$crate::enter_span!($crate::span!($crate::Level::TRACE, $name))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Proxy to allow entering tracing spans from wasm.
|
||||
//!
|
||||
//! Use `enter_span` and `exit_span` to surround the code that you wish to trace
|
||||
use rental;
|
||||
use tracing::info_span;
|
||||
|
||||
/// Used to identify a proxied WASM trace
|
||||
pub const WASM_TRACE_IDENTIFIER: &'static str = "WASM_TRACE";
|
||||
/// Used to extract the real `target` from the associated values of the span
|
||||
pub const WASM_TARGET_KEY: &'static str = "proxied_wasm_target";
|
||||
/// Used to extract the real `name` from the associated values of the span
|
||||
pub const WASM_NAME_KEY: &'static str = "proxied_wasm_name";
|
||||
|
||||
const MAX_SPANS_LEN: usize = 1000;
|
||||
|
||||
rental! {
|
||||
pub mod rent_span {
|
||||
#[rental]
|
||||
pub struct SpanAndGuard {
|
||||
span: Box<tracing::Span>,
|
||||
guard: tracing::span::Entered<'span>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Requires a tracing::Subscriber to process span traces,
|
||||
/// this is available when running with client (and relevant cli params).
|
||||
pub struct TracingProxy {
|
||||
next_id: u64,
|
||||
spans: Vec<(u64, rent_span::SpanAndGuard)>,
|
||||
}
|
||||
|
||||
impl Drop for TracingProxy {
|
||||
fn drop(&mut self) {
|
||||
if !self.spans.is_empty() {
|
||||
log::debug!(
|
||||
target: "tracing",
|
||||
"Dropping TracingProxy with {} un-exited spans, marking as not valid", self.spans.len()
|
||||
);
|
||||
while let Some((_, mut sg)) = self.spans.pop() {
|
||||
sg.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TracingProxy {
|
||||
pub fn new() -> TracingProxy {
|
||||
TracingProxy {
|
||||
next_id: 0,
|
||||
spans: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TracingProxy {
|
||||
/// Create and enter a `tracing` Span, returning the span id,
|
||||
/// which should be passed to `exit_span(id)` to signal that the span should exit.
|
||||
pub fn enter_span(&mut self, proxied_wasm_target: &str, proxied_wasm_name: &str) -> u64 {
|
||||
// The identifiers `proxied_wasm_target` and `proxied_wasm_name` must match their associated const,
|
||||
// WASM_TARGET_KEY and WASM_NAME_KEY.
|
||||
let span = info_span!(WASM_TRACE_IDENTIFIER, is_valid_trace = true, proxied_wasm_target, proxied_wasm_name);
|
||||
self.next_id += 1;
|
||||
let sg = rent_span::SpanAndGuard::new(
|
||||
Box::new(span),
|
||||
|span| span.enter(),
|
||||
);
|
||||
self.spans.push((self.next_id, sg));
|
||||
if self.spans.len() > MAX_SPANS_LEN {
|
||||
// This is to prevent unbounded growth of Vec and could mean one of the following:
|
||||
// 1. Too many nested spans, or MAX_SPANS_LEN is too low.
|
||||
// 2. Not correctly exiting spans due to misconfiguration / misuse
|
||||
log::warn!(
|
||||
target: "tracing",
|
||||
"TracingProxy MAX_SPANS_LEN exceeded, removing oldest span."
|
||||
);
|
||||
let mut sg = self.spans.remove(0).1;
|
||||
sg.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); });
|
||||
}
|
||||
self.next_id
|
||||
}
|
||||
|
||||
/// Exit a span by dropping it along with it's associated guard.
|
||||
pub fn exit_span(&mut self, id: u64) {
|
||||
if self.spans.last().map(|l| id > l.0).unwrap_or(true) {
|
||||
log::warn!(target: "tracing", "Span id not found in TracingProxy: {}", id);
|
||||
return;
|
||||
}
|
||||
let mut last_span = self.spans.pop().expect("Just checked that there is an element to pop; qed");
|
||||
while id < last_span.0 {
|
||||
log::warn!(
|
||||
target: "tracing",
|
||||
"TracingProxy Span ids not equal! id parameter given: {}, last span: {}",
|
||||
id,
|
||||
last_span.0,
|
||||
);
|
||||
last_span.1.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); });
|
||||
if let Some(s) = self.spans.pop() {
|
||||
last_span = s;
|
||||
} else {
|
||||
log::warn!(target: "tracing", "Span id not found in TracingProxy {}", id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn create_spans(proxy: &mut TracingProxy, qty: usize) -> Vec<u64> {
|
||||
let mut spans = Vec::new();
|
||||
for n in 0..qty {
|
||||
spans.push(proxy.enter_span("target", &format!("{}", n)));
|
||||
}
|
||||
spans
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_spans_len_respected() {
|
||||
let mut proxy = TracingProxy::new();
|
||||
let _spans = create_spans(&mut proxy, MAX_SPANS_LEN + 10);
|
||||
assert_eq!(proxy.spans.len(), MAX_SPANS_LEN);
|
||||
// ensure oldest spans removed
|
||||
assert_eq!(proxy.spans[0].0, 11);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handles_span_exit_scenarios() {
|
||||
let mut proxy = TracingProxy::new();
|
||||
let _spans = create_spans(&mut proxy, 10);
|
||||
assert_eq!(proxy.spans.len(), 10);
|
||||
// exit span normally
|
||||
proxy.exit_span(10);
|
||||
assert_eq!(proxy.spans.len(), 9);
|
||||
// skip and exit outer span without exiting inner, id: 8 instead of 9
|
||||
proxy.exit_span(8);
|
||||
// should have also removed the inner span that was lost
|
||||
assert_eq!(proxy.spans.len(), 7);
|
||||
// try to exit span not held
|
||||
proxy.exit_span(9);
|
||||
assert_eq!(proxy.spans.len(), 7);
|
||||
// exit all spans
|
||||
proxy.exit_span(1);
|
||||
assert_eq!(proxy.spans.len(), 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// Types for wasm based tracing. Loosly inspired by `tracing-core` but
|
||||
/// optimised for the specific use case.
|
||||
|
||||
use core::{format_args, fmt::Debug};
|
||||
use sp_std::{
|
||||
vec, vec::Vec,
|
||||
};
|
||||
use sp_std::Writer;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
/// The Tracing Level – the user can filter by this
|
||||
#[derive(Clone, Encode, Decode, Debug)]
|
||||
pub enum WasmLevel {
|
||||
/// This is a fatal errors
|
||||
ERROR,
|
||||
/// This is a warning you should be aware of
|
||||
WARN,
|
||||
/// Nice to now info
|
||||
INFO,
|
||||
/// Further information for debugging purposes
|
||||
DEBUG,
|
||||
/// The lowest level, keeping track of minute detail
|
||||
TRACE
|
||||
}
|
||||
|
||||
|
||||
impl From<&tracing_core::Level> for WasmLevel {
|
||||
fn from(l: &tracing_core::Level) -> WasmLevel {
|
||||
match l {
|
||||
&tracing_core::Level::ERROR => WasmLevel::ERROR,
|
||||
&tracing_core::Level::WARN => WasmLevel::WARN,
|
||||
&tracing_core::Level::INFO => WasmLevel::INFO,
|
||||
&tracing_core::Level::DEBUG => WasmLevel::DEBUG,
|
||||
&tracing_core::Level::TRACE => WasmLevel::TRACE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl core::default::Default for WasmLevel {
|
||||
fn default() -> Self {
|
||||
WasmLevel::TRACE
|
||||
}
|
||||
}
|
||||
|
||||
/// A paramter value provided to the span/event
|
||||
#[derive(Encode, Decode, Clone)]
|
||||
pub enum WasmValue {
|
||||
U8(u8),
|
||||
I8(i8),
|
||||
U32(u32),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
U64(u64),
|
||||
Bool(bool),
|
||||
Str(Vec<u8>),
|
||||
/// Debug or Display call, this is most-likely a print-able UTF8 String
|
||||
Formatted(Vec<u8>),
|
||||
/// SCALE CODEC encoded object – the name should allow the received to know
|
||||
/// how to decode this.
|
||||
Encoded(Vec<u8>),
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for WasmValue {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self {
|
||||
WasmValue::U8(ref i) => {
|
||||
f.write_fmt(format_args!("{}_u8", i))
|
||||
}
|
||||
WasmValue::I8(ref i) => {
|
||||
f.write_fmt(format_args!("{}_i8", i))
|
||||
}
|
||||
WasmValue::U32(ref i) => {
|
||||
f.write_fmt(format_args!("{}_u32", i))
|
||||
}
|
||||
WasmValue::I32(ref i) => {
|
||||
f.write_fmt(format_args!("{}_i32", i))
|
||||
}
|
||||
WasmValue::I64(ref i) => {
|
||||
f.write_fmt(format_args!("{}_i64", i))
|
||||
}
|
||||
WasmValue::U64(ref i) => {
|
||||
f.write_fmt(format_args!("{}_u64", i))
|
||||
}
|
||||
WasmValue::Bool(ref i) => {
|
||||
f.write_fmt(format_args!("{}_bool", i))
|
||||
}
|
||||
WasmValue::Formatted(ref i) | WasmValue::Str(ref i) => {
|
||||
if let Ok(v) = core::str::from_utf8(i) {
|
||||
f.write_fmt(format_args!("{}", v))
|
||||
} else {
|
||||
f.write_fmt(format_args!("{:?}", i))
|
||||
}
|
||||
}
|
||||
WasmValue::Encoded(ref v) => {
|
||||
f.write_str("Scale(")?;
|
||||
for byte in v {
|
||||
f.write_fmt(format_args!("{:02x}", byte))?;
|
||||
}
|
||||
f.write_str(")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for WasmValue {
|
||||
fn from(u: u8) -> WasmValue {
|
||||
WasmValue::U8(u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&i8> for WasmValue {
|
||||
fn from(inp: &i8) -> WasmValue {
|
||||
WasmValue::I8(inp.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for WasmValue {
|
||||
fn from(inp: &str) -> WasmValue {
|
||||
WasmValue::Str(inp.as_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&&str> for WasmValue {
|
||||
fn from(inp: &&str) -> WasmValue {
|
||||
WasmValue::Str((*inp).as_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for WasmValue {
|
||||
fn from(inp: bool) -> WasmValue {
|
||||
WasmValue::Bool(inp)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::fmt::Arguments<'_>> for WasmValue {
|
||||
fn from(inp: core::fmt::Arguments<'_>) -> WasmValue {
|
||||
let mut buf = Writer::default();
|
||||
core::fmt::write(&mut buf, inp).expect("Writing of arguments doesn't fail");
|
||||
WasmValue::Formatted(buf.into_inner())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i8> for WasmValue {
|
||||
fn from(u: i8) -> WasmValue {
|
||||
WasmValue::I8(u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for WasmValue {
|
||||
fn from(u: i32) -> WasmValue {
|
||||
WasmValue::I32(u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&i32> for WasmValue {
|
||||
fn from(u: &i32) -> WasmValue {
|
||||
WasmValue::I32(*u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for WasmValue {
|
||||
fn from(u: u32) -> WasmValue {
|
||||
WasmValue::U32(u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&u32> for WasmValue {
|
||||
fn from(u: &u32) -> WasmValue {
|
||||
WasmValue::U32(*u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for WasmValue {
|
||||
fn from(u: u64) -> WasmValue {
|
||||
WasmValue::U64(u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for WasmValue {
|
||||
fn from(u: i64) -> WasmValue {
|
||||
WasmValue::I64(u)
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of a field provided as the argument name when contstructing an
|
||||
/// `event!` or `span!`.
|
||||
/// Generally generated automaticaly via `stringify` from an `'static &str`.
|
||||
/// Likely print-able.
|
||||
#[derive(Encode, Decode, Clone)]
|
||||
pub struct WasmFieldName(Vec<u8>);
|
||||
|
||||
impl core::fmt::Debug for WasmFieldName {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
if let Ok(v) = core::str::from_utf8(&self.0) {
|
||||
f.write_fmt(format_args!("{}", v))
|
||||
} else {
|
||||
for byte in self.0.iter() {
|
||||
f.write_fmt(format_args!("{:02x}", byte))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for WasmFieldName {
|
||||
fn from(v: Vec<u8>) -> Self {
|
||||
WasmFieldName(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for WasmFieldName {
|
||||
fn from(v: &str) -> Self {
|
||||
WasmFieldName(v.as_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of `WasmFieldName`s in the order provided
|
||||
#[derive(Encode, Decode, Clone, Debug)]
|
||||
pub struct WasmFields(Vec<WasmFieldName>);
|
||||
|
||||
impl WasmFields {
|
||||
/// Iterate over the fields
|
||||
pub fn iter(&self) -> core::slice::Iter<'_, WasmFieldName> {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<WasmFieldName>> for WasmFields {
|
||||
fn from(v: Vec<WasmFieldName>) -> WasmFields {
|
||||
WasmFields(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<&str>> for WasmFields {
|
||||
fn from(v: Vec<&str>) -> WasmFields {
|
||||
WasmFields(v.into_iter().map(|v| v.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmFields {
|
||||
/// Create an empty entry
|
||||
pub fn empty() -> Self {
|
||||
WasmFields(Vec::with_capacity(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&tracing_core::field::FieldSet> for WasmFields {
|
||||
fn from(wm: &tracing_core::field::FieldSet) -> WasmFields {
|
||||
WasmFields(wm.iter().map(|s| s.name().into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of `WasmFieldName`s with the given `WasmValue` (if provided)
|
||||
/// in the order specified.
|
||||
#[derive(Encode, Decode, Clone)]
|
||||
pub struct WasmValuesSet(Vec<(WasmFieldName, Option<WasmValue>)>);
|
||||
|
||||
impl core::fmt::Debug for WasmValuesSet {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let mut wrt = f.debug_struct("");
|
||||
let mut non_str = false;
|
||||
for (f, v) in self.0.iter() {
|
||||
if let Ok(s) = core::str::from_utf8(&f.0) {
|
||||
match v {
|
||||
Some(ref i) => wrt.field(s, i),
|
||||
None => wrt.field(s, &(None as Option<WasmValue>)),
|
||||
};
|
||||
} else {
|
||||
non_str = true;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: replace with using `finish_non_exhaustive()` once stable
|
||||
// https://github.com/rust-lang/rust/issues/67364
|
||||
if non_str {
|
||||
wrt.field("..", &"..");
|
||||
}
|
||||
|
||||
wrt.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<Vec<(WasmFieldName, Option<WasmValue>)>> for WasmValuesSet {
|
||||
fn from(v: Vec<(WasmFieldName, Option<WasmValue>)>) -> Self {
|
||||
WasmValuesSet(v)
|
||||
}
|
||||
}
|
||||
impl From<Vec<(&&WasmFieldName, Option<WasmValue>)>> for WasmValuesSet {
|
||||
fn from(v: Vec<(&&WasmFieldName, Option<WasmValue>)>) -> Self {
|
||||
WasmValuesSet(v.into_iter().map(|(k, v)| ((**k).clone(), v)).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(&&str, Option<WasmValue>)>> for WasmValuesSet {
|
||||
fn from(v: Vec<(&&str, Option<WasmValue>)>) -> Self {
|
||||
WasmValuesSet(v.into_iter().map(|(k, v)| ((*k).into(), v)).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmValuesSet {
|
||||
/// Create an empty entry
|
||||
pub fn empty() -> Self {
|
||||
WasmValuesSet(Vec::with_capacity(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl tracing_core::field::Visit for WasmValuesSet {
|
||||
fn record_debug(&mut self, field: &tracing_core::field::Field, value: &dyn Debug) {
|
||||
self.0.push( (
|
||||
field.name().into(),
|
||||
Some(WasmValue::from(format_args!("{:?}", value)))
|
||||
))
|
||||
}
|
||||
fn record_i64(&mut self, field: &tracing_core::field::Field, value: i64) {
|
||||
self.0.push( (
|
||||
field.name().into(),
|
||||
Some(WasmValue::from(value))
|
||||
))
|
||||
}
|
||||
fn record_u64(&mut self, field: &tracing_core::field::Field, value: u64) {
|
||||
self.0.push( (
|
||||
field.name().into(),
|
||||
Some(WasmValue::from(value))
|
||||
))
|
||||
}
|
||||
fn record_bool(&mut self, field: &tracing_core::field::Field, value: bool) {
|
||||
self.0.push( (
|
||||
field.name().into(),
|
||||
Some(WasmValue::from(value))
|
||||
))
|
||||
}
|
||||
fn record_str(&mut self, field: &tracing_core::field::Field, value: &str) {
|
||||
self.0.push( (
|
||||
field.name().into(),
|
||||
Some(WasmValue::from(value))
|
||||
))
|
||||
}
|
||||
}
|
||||
/// Metadata provides generic information about the specifc location of the
|
||||
/// `span!` or `event!` call on the wasm-side.
|
||||
#[derive(Encode, Decode, Clone)]
|
||||
pub struct WasmMetadata {
|
||||
/// The name given to `event!`/`span!`, `&'static str` converted to bytes
|
||||
pub name: Vec<u8>,
|
||||
/// The given target to `event!`/`span!` – or module-name, `&'static str` converted to bytes
|
||||
pub target: Vec<u8>,
|
||||
/// The level of this entry
|
||||
pub level: WasmLevel,
|
||||
/// The file this was emitted from – useful for debugging; `&'static str` converted to bytes
|
||||
pub file: Vec<u8>,
|
||||
/// The specific line number in the file – useful for debugging
|
||||
pub line: u32,
|
||||
/// The module path; `&'static str` converted to bytes
|
||||
pub module_path: Vec<u8>,
|
||||
/// Whether this is a call to `span!` or `event!`
|
||||
pub is_span: bool,
|
||||
/// The list of fields specified in the call
|
||||
pub fields: WasmFields,
|
||||
}
|
||||
|
||||
impl From<&tracing_core::Metadata<'_>> for WasmMetadata {
|
||||
fn from(wm: &tracing_core::Metadata<'_>) -> WasmMetadata {
|
||||
WasmMetadata {
|
||||
name: wm.name().as_bytes().to_vec(),
|
||||
target: wm.target().as_bytes().to_vec(),
|
||||
level: wm.level().into(),
|
||||
file: wm.file().map(|f| f.as_bytes().to_vec()).unwrap_or_default(),
|
||||
line: wm.line().unwrap_or_default(),
|
||||
module_path: wm.module_path().map(|m| m.as_bytes().to_vec()).unwrap_or_default(),
|
||||
is_span: wm.is_span(),
|
||||
fields: wm.fields().into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for WasmMetadata {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("WasmMetadata")
|
||||
.field("name", &decode_field(&self.name))
|
||||
.field("target", &decode_field(&self.target))
|
||||
.field("level", &self.level)
|
||||
.field("file", &decode_field(&self.file))
|
||||
.field("line", &self.line)
|
||||
.field("module_path", &decode_field(&self.module_path))
|
||||
.field("is_span", &self.is_span)
|
||||
.field("fields", &self.fields)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::default::Default for WasmMetadata {
|
||||
fn default() -> Self {
|
||||
let target = "default".as_bytes().to_vec();
|
||||
WasmMetadata {
|
||||
target,
|
||||
name: Default::default(),
|
||||
level: Default::default(),
|
||||
file: Default::default(),
|
||||
line: Default::default(),
|
||||
module_path: Default::default(),
|
||||
is_span: true,
|
||||
fields: WasmFields::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn decode_field(field: &[u8]) -> &str {
|
||||
core::str::from_utf8(field).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Span or Event Attributes
|
||||
#[derive(Encode, Decode, Clone, Debug)]
|
||||
pub struct WasmEntryAttributes {
|
||||
/// the parent, if directly specified – otherwise assume most inner span
|
||||
pub parent_id: Option<u64>,
|
||||
/// the metadata of the location
|
||||
pub metadata: WasmMetadata,
|
||||
/// the Values provided
|
||||
pub fields: WasmValuesSet,
|
||||
}
|
||||
|
||||
impl From<&tracing_core::Event<'_>> for WasmEntryAttributes {
|
||||
fn from(evt: &tracing_core::Event<'_>) -> WasmEntryAttributes {
|
||||
let mut fields = WasmValuesSet(Vec::new());
|
||||
evt.record(&mut fields);
|
||||
WasmEntryAttributes {
|
||||
parent_id: evt.parent().map(|id| id.into_u64()),
|
||||
metadata: evt.metadata().into(),
|
||||
fields: fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&tracing_core::span::Attributes<'_>> for WasmEntryAttributes {
|
||||
fn from(attrs: &tracing_core::span::Attributes<'_>) -> WasmEntryAttributes {
|
||||
let mut fields = WasmValuesSet(Vec::new());
|
||||
attrs.record(&mut fields);
|
||||
WasmEntryAttributes {
|
||||
parent_id: attrs.parent().map(|id| id.into_u64()),
|
||||
metadata: attrs.metadata().into(),
|
||||
fields: fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::default::Default for WasmEntryAttributes {
|
||||
fn default() -> Self {
|
||||
WasmEntryAttributes {
|
||||
parent_id: None,
|
||||
metadata: Default::default(),
|
||||
fields: WasmValuesSet(vec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod std_features {
|
||||
|
||||
use tracing_core::callsite;
|
||||
use tracing;
|
||||
|
||||
/// Static entry use for wasm-originated metadata.
|
||||
pub struct WasmCallsite;
|
||||
impl callsite::Callsite for WasmCallsite {
|
||||
fn set_interest(&self, _: tracing_core::Interest) { unimplemented!() }
|
||||
fn metadata(&self) -> &tracing_core::Metadata { unimplemented!() }
|
||||
}
|
||||
static CALLSITE: WasmCallsite = WasmCallsite;
|
||||
/// The identifier we are using to inject the wasm events in the generic `tracing` system
|
||||
pub static WASM_TRACE_IDENTIFIER: &'static str = "wasm_tracing";
|
||||
/// The fieldname for the wasm-originated name
|
||||
pub static WASM_NAME_KEY: &'static str = "name";
|
||||
/// The fieldname for the wasm-originated target
|
||||
pub static WASM_TARGET_KEY: &'static str = "target";
|
||||
/// The the list of all static field names we construct from the given metadata
|
||||
pub static GENERIC_FIELDS: &'static [&'static str] = &[WASM_TARGET_KEY, WASM_NAME_KEY,
|
||||
"file", "line", "module_path", "params"];
|
||||
|
||||
// Implementation Note:
|
||||
// the original `tracing` crate generates these static metadata entries at every `span!` and
|
||||
// `event!` location to allow for highly optimised filtering. For us to allow level-based emitting
|
||||
// of wasm events we need these static metadata entries to inject into that system. We then provide
|
||||
// generic `From`-implementations picking the right metadata to refer to.
|
||||
|
||||
static SPAN_ERROR_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::ERROR, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::SPAN
|
||||
);
|
||||
|
||||
static SPAN_WARN_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::WARN, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::SPAN
|
||||
);
|
||||
static SPAN_INFO_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::INFO, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::SPAN
|
||||
);
|
||||
|
||||
static SPAN_DEBUG_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::DEBUG, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::SPAN
|
||||
);
|
||||
|
||||
static SPAN_TRACE_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::TRACE, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::SPAN
|
||||
);
|
||||
|
||||
static EVENT_ERROR_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::ERROR, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::EVENT
|
||||
);
|
||||
|
||||
static EVENT_WARN_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::WARN, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::EVENT
|
||||
);
|
||||
|
||||
static EVENT_INFO_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::INFO, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::EVENT
|
||||
);
|
||||
|
||||
static EVENT_DEBUG_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::DEBUG, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::EVENT
|
||||
);
|
||||
|
||||
static EVENT_TRACE_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new(
|
||||
WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::TRACE, None, None, None,
|
||||
tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)),
|
||||
tracing_core::metadata::Kind::EVENT
|
||||
);
|
||||
|
||||
// FIXME: this could be done a lot in 0.2 if they opt for using `Cow<str,'static>` instead
|
||||
// https://github.com/paritytech/substrate/issues/7134
|
||||
impl From<&crate::WasmMetadata> for &'static tracing_core::Metadata<'static> {
|
||||
fn from(wm: &crate::WasmMetadata) -> &'static tracing_core::Metadata<'static> {
|
||||
match (&wm.level, wm.is_span) {
|
||||
(&crate::WasmLevel::ERROR, true) => &SPAN_ERROR_METADATA,
|
||||
(&crate::WasmLevel::WARN, true) => &SPAN_WARN_METADATA,
|
||||
(&crate::WasmLevel::INFO, true) => &SPAN_INFO_METADATA,
|
||||
(&crate::WasmLevel::DEBUG, true) => &SPAN_DEBUG_METADATA,
|
||||
(&crate::WasmLevel::TRACE, true) => &SPAN_TRACE_METADATA,
|
||||
(&crate::WasmLevel::ERROR, false) => &EVENT_ERROR_METADATA,
|
||||
(&crate::WasmLevel::WARN, false) => &EVENT_WARN_METADATA,
|
||||
(&crate::WasmLevel::INFO, false) => &EVENT_INFO_METADATA,
|
||||
(&crate::WasmLevel::DEBUG, false) => &EVENT_DEBUG_METADATA,
|
||||
(&crate::WasmLevel::TRACE, false) => &EVENT_TRACE_METADATA,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::WasmEntryAttributes> for tracing::Span {
|
||||
fn from(a: crate::WasmEntryAttributes) -> tracing::Span {
|
||||
let name = std::str::from_utf8(&a.metadata.name).unwrap_or_default();
|
||||
let target = std::str::from_utf8(&a.metadata.target).unwrap_or_default();
|
||||
let file = std::str::from_utf8(&a.metadata.file).unwrap_or_default();
|
||||
let line = a.metadata.line;
|
||||
let module_path = std::str::from_utf8(&a.metadata.module_path).unwrap_or_default();
|
||||
let params = a.fields;
|
||||
let metadata : &tracing_core::metadata::Metadata<'static> = (&a.metadata).into();
|
||||
|
||||
tracing::span::Span::child_of(
|
||||
a.parent_id.map(|i|tracing_core::span::Id::from_u64(i)),
|
||||
&metadata,
|
||||
&tracing::valueset!{ metadata.fields(), target, name, file, line, module_path, ?params }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::WasmEntryAttributes {
|
||||
/// convert the given Attributes to an event and emit it using `tracing_core`.
|
||||
pub fn emit(self: crate::WasmEntryAttributes) {
|
||||
let name = std::str::from_utf8(&self.metadata.name).unwrap_or_default();
|
||||
let target = std::str::from_utf8(&self.metadata.target).unwrap_or_default();
|
||||
let file = std::str::from_utf8(&self.metadata.file).unwrap_or_default();
|
||||
let line = self.metadata.line;
|
||||
let module_path = std::str::from_utf8(&self.metadata.module_path).unwrap_or_default();
|
||||
let params = self.fields;
|
||||
let metadata : &tracing_core::metadata::Metadata<'static> = (&self.metadata).into();
|
||||
|
||||
tracing_core::Event::child_of(
|
||||
self.parent_id.map(|i|tracing_core::span::Id::from_u64(i)),
|
||||
&metadata,
|
||||
&tracing::valueset!{ metadata.fields(), target, name, file, line, module_path, ?params }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std_features::*;
|
||||
Reference in New Issue
Block a user