1use std::{
20 collections::HashMap,
21 sync::{
22 atomic::{AtomicU64, Ordering},
23 Arc,
24 },
25 time::Instant,
26};
27
28use codec::Encode;
29use parking_lot::Mutex;
30use tracing::{
31 dispatcher,
32 span::{Attributes, Id, Record},
33 Dispatch, Level, Subscriber,
34};
35
36use crate::{SpanDatum, TraceEvent, Values};
37use sc_client_api::BlockBackend;
38use sp_api::{Core, Metadata, ProvideRuntimeApi};
39use sp_blockchain::HeaderBackend;
40use sp_core::hexdisplay::HexDisplay;
41use sp_rpc::tracing::{BlockTrace, Span, TraceBlockResponse};
42use sp_runtime::{
43 generic::BlockId,
44 traits::{Block as BlockT, Header},
45};
46use sp_tracing::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER};
47
48const DEFAULT_TARGETS: &str = "pallet,frame,state";
50const TRACE_TARGET: &str = "block_trace";
51const REQUIRED_EVENT_FIELD: &str = "method";
53
54pub type TraceBlockResult<T> = Result<T, Error>;
56
57#[derive(Debug, thiserror::Error)]
59#[allow(missing_docs)]
60#[non_exhaustive]
61pub enum Error {
62 #[error("Invalid block Id: {0}")]
63 InvalidBlockId(#[from] sp_blockchain::Error),
64 #[error("Missing block component: {0}")]
65 MissingBlockComponent(String),
66 #[error("Dispatch error: {0}")]
67 Dispatch(String),
68}
69
70struct BlockSubscriber {
71 targets: Vec<(String, Level)>,
72 next_id: AtomicU64,
73 spans: Mutex<HashMap<Id, SpanDatum>>,
74 events: Mutex<Vec<TraceEvent>>,
75}
76
77impl BlockSubscriber {
78 fn new(targets: &str) -> Self {
79 let next_id = AtomicU64::new(1);
80 let mut targets: Vec<_> = targets.split(',').map(crate::parse_target).collect();
81 targets.push((WASM_TRACE_IDENTIFIER.to_owned(), Level::TRACE));
84 BlockSubscriber {
85 targets,
86 next_id,
87 spans: Mutex::new(HashMap::new()),
88 events: Mutex::new(Vec::new()),
89 }
90 }
91}
92
93impl Subscriber for BlockSubscriber {
94 fn enabled(&self, metadata: &tracing::Metadata<'_>) -> bool {
95 if !metadata.is_span() && metadata.fields().field(REQUIRED_EVENT_FIELD).is_none() {
96 return false
97 }
98 for (target, level) in &self.targets {
99 if metadata.level() <= level && metadata.target().starts_with(target) {
100 return true
101 }
102 }
103 false
104 }
105
106 fn new_span(&self, attrs: &Attributes<'_>) -> Id {
107 let id = Id::from_u64(self.next_id.fetch_add(1, Ordering::Relaxed));
108 let mut values = Values::default();
109 attrs.record(&mut values);
110 let parent_id = attrs.parent().cloned();
111 let span = SpanDatum {
112 id: id.clone(),
113 parent_id,
114 name: attrs.metadata().name().to_owned(),
115 target: attrs.metadata().target().to_owned(),
116 level: *attrs.metadata().level(),
117 line: attrs.metadata().line().unwrap_or(0),
118 start_time: Instant::now(),
119 values,
120 overall_time: Default::default(),
121 };
122
123 self.spans.lock().insert(id.clone(), span);
124 id
125 }
126
127 fn record(&self, span: &Id, values: &Record<'_>) {
128 let mut span_data = self.spans.lock();
129 if let Some(s) = span_data.get_mut(span) {
130 values.record(&mut s.values);
131 }
132 }
133
134 fn record_follows_from(&self, _span: &Id, _follows: &Id) {
135 unimplemented!("record_follows_from is not implemented");
137 }
138
139 fn event(&self, event: &tracing::Event<'_>) {
140 let mut values = crate::Values::default();
141 event.record(&mut values);
142 let parent_id = event.parent().cloned();
143 let trace_event = TraceEvent {
144 name: event.metadata().name().to_owned(),
145 target: event.metadata().target().to_owned(),
146 level: *event.metadata().level(),
147 values,
148 parent_id,
149 };
150 self.events.lock().push(trace_event);
151 }
152
153 fn enter(&self, _id: &Id) {}
154
155 fn exit(&self, _span: &Id) {}
156}
157
158pub struct BlockExecutor<Block: BlockT, Client> {
164 client: Arc<Client>,
165 block: Block::Hash,
166 targets: Option<String>,
167 storage_keys: Option<String>,
168 methods: Option<String>,
169}
170
171impl<Block, Client> BlockExecutor<Block, Client>
172where
173 Block: BlockT + 'static,
174 Client: HeaderBackend<Block>
175 + BlockBackend<Block>
176 + ProvideRuntimeApi<Block>
177 + Send
178 + Sync
179 + 'static,
180 Client::Api: Metadata<Block>,
181{
182 pub fn new(
184 client: Arc<Client>,
185 block: Block::Hash,
186 targets: Option<String>,
187 storage_keys: Option<String>,
188 methods: Option<String>,
189 ) -> Self {
190 Self { client, block, targets, storage_keys, methods }
191 }
192
193 pub fn trace_block(&self) -> TraceBlockResult<TraceBlockResponse> {
197 tracing::debug!(target: "state_tracing", "Tracing block: {}", self.block);
198 let mut header = self
200 .client
201 .header(self.block)
202 .map_err(Error::InvalidBlockId)?
203 .ok_or_else(|| Error::MissingBlockComponent("Header not found".to_string()))?;
204 let extrinsics = self
205 .client
206 .block_body(self.block)
207 .map_err(Error::InvalidBlockId)?
208 .ok_or_else(|| Error::MissingBlockComponent("Extrinsics not found".to_string()))?;
209 tracing::debug!(target: "state_tracing", "Found {} extrinsics", extrinsics.len());
210 let parent_hash = *header.parent_hash();
211 header.digest_mut().logs.retain(|d| d.as_seal().is_none());
214 let block = Block::new(header, extrinsics);
215
216 let targets = if let Some(t) = &self.targets { t } else { DEFAULT_TARGETS };
217 let block_subscriber = BlockSubscriber::new(targets);
218 let dispatch = Dispatch::new(block_subscriber);
219
220 {
221 let dispatcher_span = tracing::debug_span!(
222 target: "state_tracing",
223 "execute_block",
224 extrinsics_len = block.extrinsics().len(),
225 );
226 let _guard = dispatcher_span.enter();
227 if let Err(e) = dispatcher::with_default(&dispatch, || {
228 let span = tracing::info_span!(target: TRACE_TARGET, "trace_block");
229 let _enter = span.enter();
230 self.client.runtime_api().execute_block(parent_hash, block)
231 }) {
232 return Err(Error::Dispatch(format!(
233 "Failed to collect traces and execute block: {}",
234 e
235 )))
236 }
237 }
238
239 let block_subscriber = dispatch.downcast_ref::<BlockSubscriber>().ok_or_else(|| {
240 Error::Dispatch(
241 "Cannot downcast Dispatch to BlockSubscriber after tracing block".to_string(),
242 )
243 })?;
244 let spans: Vec<_> = block_subscriber
245 .spans
246 .lock()
247 .drain()
248 .filter_map(|(_, s)| patch_and_filter(s, targets))
250 .collect();
251 let events: Vec<_> = block_subscriber
252 .events
253 .lock()
254 .drain(..)
255 .filter(|e| {
256 self.storage_keys
257 .as_ref()
258 .map(|keys| event_values_filter(e, "key", keys))
259 .unwrap_or(false)
260 })
261 .filter(|e| {
262 self.methods
263 .as_ref()
264 .map(|methods| event_values_filter(e, "method", methods))
265 .unwrap_or(false)
266 })
267 .map(|s| s.into())
268 .collect();
269 tracing::debug!(target: "state_tracing", "Captured {} spans and {} events", spans.len(), events.len());
270
271 Ok(TraceBlockResponse::BlockTrace(BlockTrace {
272 block_hash: block_id_as_string(BlockId::<Block>::Hash(self.block)),
273 parent_hash: block_id_as_string(BlockId::<Block>::Hash(parent_hash)),
274 tracing_targets: targets.to_string(),
275 storage_keys: self.storage_keys.clone().unwrap_or_default(),
276 methods: self.methods.clone().unwrap_or_default(),
277 spans,
278 events,
279 }))
280 }
281}
282
283fn event_values_filter(event: &TraceEvent, filter_kind: &str, values: &str) -> bool {
284 event
285 .values
286 .string_values
287 .get(filter_kind)
288 .map(|value| check_target(values, value, &event.level))
289 .unwrap_or(false)
290}
291
292fn patch_and_filter(mut span: SpanDatum, targets: &str) -> Option<Span> {
301 if span.name == WASM_TRACE_IDENTIFIER {
302 span.values.bool_values.insert("wasm".to_owned(), true);
303 if let Some(n) = span.values.string_values.remove(WASM_NAME_KEY) {
304 span.name = n;
305 }
306 if let Some(t) = span.values.string_values.remove(WASM_TARGET_KEY) {
307 span.target = t;
308 }
309 if !check_target(targets, &span.target, &span.level) {
310 return None
311 }
312 }
313 Some(span.into())
314}
315
316fn check_target(targets: &str, target: &str, level: &Level) -> bool {
318 for (t, l) in targets.split(',').map(crate::parse_target) {
319 if target.starts_with(t.as_str()) && level <= &l {
320 return true
321 }
322 }
323 false
324}
325
326fn block_id_as_string<T: BlockT>(block_id: BlockId<T>) -> String {
327 match block_id {
328 BlockId::Hash(h) => HexDisplay::from(&h.encode()).to_string(),
329 BlockId::Number(n) => HexDisplay::from(&n.encode()).to_string(),
330 }
331}