@@ -22,6 +22,7 @@ use stacks_common::types::net::PeerHost;
2222use stacks_common:: util:: hash:: Sha512Trunc256Sum ;
2323use stacks_common:: util:: secp256k1:: MessageSignature ;
2424use stacks_common:: util:: serde_serializers:: prefix_hex_codec;
25+ use url:: form_urlencoded;
2526
2627use crate :: burnchains:: Txid ;
2728use crate :: chainstate:: burn:: db:: sortdb:: SortitionDB ;
@@ -39,17 +40,122 @@ use crate::net::http::{
3940use crate :: net:: httpcore:: { RPCRequestHandler , StacksHttpResponse } ;
4041use crate :: net:: { Error as NetError , StacksHttpRequest , StacksNodeState } ;
4142
43+ #[ cfg( all( feature = "profiler" , target_os = "linux" , target_arch = "x86_64" ) ) ]
44+ struct BlockReplayProfiler {
45+ perf_event_cpu_instructions : Option < perf_event:: Counter > ,
46+ perf_event_cpu_cycles : Option < perf_event:: Counter > ,
47+ perf_event_cpu_ref_cycles : Option < perf_event:: Counter > ,
48+ }
49+
50+ #[ cfg( not( all( feature = "profiler" , target_os = "linux" , target_arch = "x86_64" ) ) ) ]
51+ struct BlockReplayProfiler ( ) ;
52+
53+ #[ derive( Default ) ]
54+ struct BlockReplayProfilerResult {
55+ cpu_instructions : Option < u64 > ,
56+ cpu_cycles : Option < u64 > ,
57+ cpu_ref_cycles : Option < u64 > ,
58+ }
59+
60+ #[ cfg( all( feature = "profiler" , target_os = "linux" , target_arch = "x86_64" ) ) ]
61+ impl BlockReplayProfiler {
62+ fn new ( ) -> Self {
63+ let mut perf_event_cpu_instructions: Option < perf_event:: Counter > = None ;
64+ let mut perf_event_cpu_cycles: Option < perf_event:: Counter > = None ;
65+ let mut perf_event_cpu_ref_cycles: Option < perf_event:: Counter > = None ;
66+
67+ if let Ok ( mut perf_event_cpu_instructions_result) =
68+ perf_event:: Builder :: new ( perf_event:: events:: Hardware :: INSTRUCTIONS ) . build ( )
69+ {
70+ if perf_event_cpu_instructions_result. enable ( ) . is_ok ( ) {
71+ perf_event_cpu_instructions = Some ( perf_event_cpu_instructions_result) ;
72+ }
73+ }
74+
75+ if let Ok ( mut perf_event_cpu_cycles_result) =
76+ perf_event:: Builder :: new ( perf_event:: events:: Hardware :: CPU_CYCLES ) . build ( )
77+ {
78+ if perf_event_cpu_cycles_result. enable ( ) . is_ok ( ) {
79+ perf_event_cpu_cycles = Some ( perf_event_cpu_cycles_result) ;
80+ }
81+ }
82+
83+ if let Ok ( mut perf_event_cpu_ref_cycles_result) =
84+ perf_event:: Builder :: new ( perf_event:: events:: Hardware :: REF_CPU_CYCLES ) . build ( )
85+ {
86+ if perf_event_cpu_ref_cycles_result. enable ( ) . is_ok ( ) {
87+ perf_event_cpu_ref_cycles = Some ( perf_event_cpu_ref_cycles_result) ;
88+ }
89+ }
90+
91+ Self {
92+ perf_event_cpu_instructions,
93+ perf_event_cpu_cycles,
94+ perf_event_cpu_ref_cycles,
95+ }
96+ }
97+
98+ fn collect ( self ) -> BlockReplayProfilerResult {
99+ let mut cpu_instructions: Option < u64 > = None ;
100+ let mut cpu_cycles: Option < u64 > = None ;
101+ let mut cpu_ref_cycles: Option < u64 > = None ;
102+
103+ if let Some ( mut perf_event_cpu_instructions) = self . perf_event_cpu_instructions {
104+ if perf_event_cpu_instructions. disable ( ) . is_ok ( ) {
105+ if let Ok ( value) = perf_event_cpu_instructions. read ( ) {
106+ cpu_instructions = Some ( value) ;
107+ }
108+ }
109+ }
110+
111+ if let Some ( mut perf_event_cpu_cycles) = self . perf_event_cpu_cycles {
112+ if perf_event_cpu_cycles. disable ( ) . is_ok ( ) {
113+ if let Ok ( value) = perf_event_cpu_cycles. read ( ) {
114+ cpu_cycles = Some ( value) ;
115+ }
116+ }
117+ }
118+
119+ if let Some ( mut perf_event_cpu_ref_cycles) = self . perf_event_cpu_ref_cycles {
120+ if perf_event_cpu_ref_cycles. disable ( ) . is_ok ( ) {
121+ if let Ok ( value) = perf_event_cpu_ref_cycles. read ( ) {
122+ cpu_ref_cycles = Some ( value) ;
123+ }
124+ }
125+ }
126+
127+ BlockReplayProfilerResult {
128+ cpu_instructions,
129+ cpu_cycles,
130+ cpu_ref_cycles,
131+ }
132+ }
133+ }
134+
135+ #[ cfg( not( all( feature = "profiler" , target_os = "linux" , target_arch = "x86_64" ) ) ) ]
136+ impl BlockReplayProfiler {
137+ fn new ( ) -> Self {
138+ warn ! ( "BlockReplay Profiler is not available in this build." ) ;
139+ Self { }
140+ }
141+ fn collect ( self ) -> BlockReplayProfilerResult {
142+ BlockReplayProfilerResult :: default ( )
143+ }
144+ }
145+
42146#[ derive( Clone ) ]
43147pub struct RPCNakamotoBlockReplayRequestHandler {
44148 pub block_id : Option < StacksBlockId > ,
45149 pub auth : Option < String > ,
150+ pub profiler : bool ,
46151}
47152
48153impl RPCNakamotoBlockReplayRequestHandler {
49154 pub fn new ( auth : Option < String > ) -> Self {
50155 Self {
51156 block_id : None ,
52157 auth,
158+ profiler : false ,
53159 }
54160 }
55161
@@ -160,16 +266,28 @@ impl RPCNakamotoBlockReplayRequestHandler {
160266 for ( i, tx) in block. txs . iter ( ) . enumerate ( ) {
161267 let tx_len = tx. tx_len ( ) ;
162268
269+ let mut profiler: Option < BlockReplayProfiler > = None ;
270+ let mut profiler_result = BlockReplayProfilerResult :: default ( ) ;
271+
272+ if self . profiler {
273+ profiler = Some ( BlockReplayProfiler :: new ( ) ) ;
274+ }
275+
163276 let tx_result = builder. try_mine_tx_with_len (
164277 & mut tenure_tx,
165278 tx,
166279 tx_len,
167280 & BlockLimitFunction :: NO_LIMIT_HIT ,
168281 None ,
169282 ) ;
283+
284+ if let Some ( profiler) = profiler {
285+ profiler_result = profiler. collect ( ) ;
286+ }
287+
170288 let err = match tx_result {
171289 TransactionResult :: Success ( tx_result) => {
172- txs_receipts. push ( tx_result. receipt ) ;
290+ txs_receipts. push ( ( tx_result. receipt , profiler_result ) ) ;
173291 Ok ( ( ) )
174292 }
175293 _ => Err ( format ! ( "Problematic tx {i}" ) ) ,
@@ -194,8 +312,8 @@ impl RPCNakamotoBlockReplayRequestHandler {
194312 let mut rpc_replayed_block =
195313 RPCReplayedBlock :: from_block ( block, block_fees, tenure_id, parent_block_id) ;
196314
197- for receipt in & txs_receipts {
198- let transaction = RPCReplayedBlockTransaction :: from_receipt ( receipt) ;
315+ for ( receipt, profiler_result ) in & txs_receipts {
316+ let transaction = RPCReplayedBlockTransaction :: from_receipt ( receipt, & profiler_result ) ;
199317 rpc_replayed_block. transactions . push ( transaction) ;
200318 }
201319
@@ -231,10 +349,17 @@ pub struct RPCReplayedBlockTransaction {
231349 pub post_condition_aborted : bool ,
232350 /// optional vm error
233351 pub vm_error : Option < String > ,
352+ /// profiling data based on linux perf_events
353+ pub cpu_instructions : Option < u64 > ,
354+ pub cpu_cycles : Option < u64 > ,
355+ pub cpu_ref_cycles : Option < u64 > ,
234356}
235357
236358impl RPCReplayedBlockTransaction {
237- pub fn from_receipt ( receipt : & StacksTransactionReceipt ) -> Self {
359+ fn from_receipt (
360+ receipt : & StacksTransactionReceipt ,
361+ profiler_result : & BlockReplayProfilerResult ,
362+ ) -> Self {
238363 let events = receipt
239364 . events
240365 . iter ( )
@@ -269,6 +394,9 @@ impl RPCReplayedBlockTransaction {
269394 events,
270395 post_condition_aborted : receipt. post_condition_aborted ,
271396 vm_error : receipt. vm_error . clone ( ) ,
397+ cpu_instructions : profiler_result. cpu_instructions ,
398+ cpu_cycles : profiler_result. cpu_cycles ,
399+ cpu_ref_cycles : profiler_result. cpu_ref_cycles ,
272400 }
273401 }
274402}
@@ -382,6 +510,17 @@ impl HttpRequest for RPCNakamotoBlockReplayRequestHandler {
382510
383511 self . block_id = Some ( block_id) ;
384512
513+ if let Some ( query_string) = query {
514+ for ( key, value) in form_urlencoded:: parse ( query_string. as_bytes ( ) ) {
515+ if key == "profiler" {
516+ if value == "1" {
517+ self . profiler = true ;
518+ }
519+ break ;
520+ }
521+ }
522+ }
523+
385524 Ok ( HttpRequestContents :: new ( ) . query_string ( query) )
386525 }
387526}
@@ -446,6 +585,23 @@ impl StacksHttpRequest {
446585 )
447586 . expect ( "FATAL: failed to construct request from infallible data" )
448587 }
588+
589+ pub fn new_block_replay_with_profiler (
590+ host : PeerHost ,
591+ block_id : & StacksBlockId ,
592+ profiler : bool ,
593+ ) -> StacksHttpRequest {
594+ StacksHttpRequest :: new_for_peer (
595+ host,
596+ "GET" . into ( ) ,
597+ format ! ( "/v3/blocks/replay/{block_id}" ) ,
598+ HttpRequestContents :: new ( ) . query_arg (
599+ "profiler" . into ( ) ,
600+ if profiler { "1" . into ( ) } else { "0" . into ( ) } ,
601+ ) ,
602+ )
603+ . expect ( "FATAL: failed to construct request from infallible data" )
604+ }
449605}
450606
451607/// Decode the HTTP response
0 commit comments