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