Skip to content

Commit d2b24d9

Browse files
authored
Merge pull request #6682 from rob-stacks/feat/blockreplay_profiler
Added blockreplay profiler
2 parents f9f144d + d764ed2 commit d2b24d9

File tree

4 files changed

+267
-7
lines changed

4 files changed

+267
-7
lines changed

Cargo.lock

Lines changed: 61 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

stackslib/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ developer-mode = ["clarity/developer-mode"]
8181
monitoring_prom = ["prometheus"]
8282
slog_json = ["stacks-common/slog_json", "clarity/slog_json", "pox-locking/slog_json"]
8383
testing = ["chrono", "stacks-common/testing", "clarity/testing"]
84+
profiler = ["perf-event2"]
85+
86+
[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies]
87+
perf-event2 = { version = "0.7.4", optional = true }
8488

8589
[target.'cfg(all(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"), not(any(target_os="windows"))))'.dependencies]
8690
sha2 = { version = "0.10", features = ["asm"] }

stackslib/src/net/api/blockreplay.rs

Lines changed: 160 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use stacks_common::types::net::PeerHost;
2222
use stacks_common::util::hash::Sha512Trunc256Sum;
2323
use stacks_common::util::secp256k1::MessageSignature;
2424
use stacks_common::util::serde_serializers::prefix_hex_codec;
25+
use url::form_urlencoded;
2526

2627
use crate::burnchains::Txid;
2728
use crate::chainstate::burn::db::sortdb::SortitionDB;
@@ -39,17 +40,122 @@ use crate::net::http::{
3940
use crate::net::httpcore::{RPCRequestHandler, StacksHttpResponse};
4041
use 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)]
43147
pub struct RPCNakamotoBlockReplayRequestHandler {
44148
pub block_id: Option<StacksBlockId>,
45149
pub auth: Option<String>,
150+
pub profiler: bool,
46151
}
47152

48153
impl 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

236358
impl 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

Comments
 (0)