diff --git a/Cargo.lock b/Cargo.lock index a01d658d..c6b29bad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,6 +91,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -2672,7 +2681,7 @@ dependencies = [ "getrandom 0.2.16", "glob", "libc", - "nix", + "nix 0.30.1", "serde", "serde_json", "statrs", @@ -3030,6 +3039,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "cpp_demangle" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -3356,6 +3374,15 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + [[package]] name = "delay_map" version = "0.4.1" @@ -3787,6 +3814,26 @@ dependencies = [ "syn 2.0.110", ] +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -3977,6 +4024,18 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -4007,7 +4066,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "spin", + "spin 0.9.8", ] [[package]] @@ -5155,6 +5214,24 @@ dependencies = [ "rustversion", ] +[[package]] +name = "inferno" +version = "0.11.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +dependencies = [ + "ahash 0.8.12", + "indexmap 2.12.0", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + [[package]] name = "inherent" version = "1.0.13" @@ -5621,7 +5698,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin", + "spin 0.9.8", ] [[package]] @@ -6152,6 +6229,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nix" version = "0.30.1" @@ -6272,6 +6360,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -6952,6 +7050,30 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "pprof" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38a01da47675efa7673b032bf8efd8214f1917d89685e07e395ab125ea42b187" +dependencies = [ + "aligned-vec", + "backtrace", + "cfg-if", + "findshlibs", + "inferno", + "libc", + "log", + "nix 0.26.4", + "once_cell", + "protobuf", + "protobuf-codegen", + "smallvec", + "spin 0.10.0", + "symbolic-demangle", + "tempfile", + "thiserror 2.0.17", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -7183,6 +7305,57 @@ dependencies = [ "prost", ] +[[package]] +name = "protobuf" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-codegen" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace" +dependencies = [ + "anyhow", + "once_cell", + "protobuf", + "protobuf-parse", + "regex", + "tempfile", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-parse" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" +dependencies = [ + "anyhow", + "indexmap 2.12.0", + "log", + "protobuf", + "protobuf-support", + "tempfile", + "thiserror 1.0.69", + "which", +] + +[[package]] +name = "protobuf-support" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" +dependencies = [ + "thiserror 1.0.69", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -7244,6 +7417,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.9" @@ -10778,6 +10960,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.14" @@ -10920,7 +11111,11 @@ dependencies = [ "console-subscriber", "eyre", "futures", + "http-body-util", + "hyper 1.8.1", + "hyper-util", "jsonrpsee", + "pprof", "rayon", "reqwest", "reth-chainspec", @@ -12492,6 +12687,15 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -12738,6 +12942,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + [[package]] name = "stringprep" version = "0.1.5" @@ -12804,6 +13014,29 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "symbolic-common" +version = "12.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d8046c5674ab857104bc4559d505f4809b8060d57806e45d49737c97afeb60" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1accb6e5c4b0f682de907623912e616b44be1c9e725775155546669dbff720ec" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index ecd4ced4..b90e609b 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -106,6 +106,12 @@ tokio.workspace = true tracing.workspace = true console-subscriber = "0.5.0" +# pprof dependencies +pprof = { version = "0.15.0", features = ["flamegraph", "protobuf-codec"] } +hyper = { version = "1.5", features = ["full"] } +hyper-util = { version = "0.1", features = ["tokio"] } +http-body-util = "0.1" + [dev-dependencies] alloy-chains.workspace = true alloy-eips.workspace = true diff --git a/crates/node/src/add_ons/rollup.rs b/crates/node/src/add_ons/rollup.rs index 5c76d6ff..f9a717d5 100644 --- a/crates/node/src/add_ons/rollup.rs +++ b/crates/node/src/add_ons/rollup.rs @@ -1,4 +1,4 @@ -use crate::args::ScrollRollupNodeConfig; +use crate::{args::ScrollRollupNodeConfig, pprof::PprofConfig}; use reth_chainspec::NamedChain; use reth_network::NetworkProtocols; @@ -12,6 +12,7 @@ use rollup_node_chain_orchestrator::ChainOrchestratorHandle; use scroll_alloy_hardforks::ScrollHardforks; use scroll_wire::ScrollWireEvent; use tokio::sync::mpsc::UnboundedReceiver; +use tracing::{error, info}; /// Implementing the trait allows the type to return whether it is configured for dev chain. #[auto_impl::auto_impl(Arc)] @@ -59,6 +60,32 @@ impl RollupManagerAddOn { ChainConfig + ScrollHardforks + IsDevChain, N::Network: NetworkProtocols + FullNetwork, { + // Start pprof server if enabled + if self.config.pprof_args.enabled { + let addr = self.config.pprof_args.addr.parse().map_err(|e| { + eyre::eyre!("Invalid pprof address '{}': {}", self.config.pprof_args.addr, e) + })?; + + let pprof_config = PprofConfig::new(addr) + .with_default_duration(self.config.pprof_args.default_duration); + + match pprof_config.launch_server().await { + Ok(handle) => { + info!(target: "rollup_node::pprof", "pprof server started successfully"); + // Spawn the pprof server task + ctx.node.task_executor().spawn_critical("pprof_server", async move { + if let Err(e) = handle.await { + error!(target: "rollup_node::pprof", "pprof server error: {:?}", e); + } + }); + } + Err(e) => { + error!(target: "rollup_node::pprof", "Failed to start pprof server: {}", e); + return Err(e); + } + } + } + let (chain_orchestrator, handle) = self .config .build((&ctx).into(), self.scroll_wire_event, rpc.rpc_server_handles) diff --git a/crates/node/src/args.rs b/crates/node/src/args.rs index 4ceceb5a..e9da34ef 100644 --- a/crates/node/src/args.rs +++ b/crates/node/src/args.rs @@ -92,6 +92,9 @@ pub struct ScrollRollupNodeConfig { /// The gas price oracle args #[command(flatten)] pub gas_price_oracle_args: RollupNodeGasPriceOracleArgs, + /// The pprof server arguments + #[command(flatten)] + pub pprof_args: PprofArgs, /// The database connection (not parsed via CLI but hydrated after validation). #[arg(skip)] pub database: Option>, @@ -849,6 +852,40 @@ pub struct RollupNodeGasPriceOracleArgs { pub default_suggested_priority_fee: u64, } +/// The arguments for the pprof server. +#[derive(Debug, Clone, clap::Args)] +pub struct PprofArgs { + /// Enable the pprof HTTP server for performance profiling + #[arg(id = "pprof.enabled", long = "pprof.enabled", help = "Enable the pprof HTTP server")] + pub enabled: bool, + + /// The address to bind the pprof HTTP server to + #[arg( + id = "pprof.url", + long = "pprof.addr", + value_name = "PPROF_URL", + help = "Address to bind the pprof HTTP server (e.g., 0.0.0.0:6868)", + default_value = constants::DEFAULT_PPROF_URL + )] + pub addr: String, + + /// Default profiling duration in seconds + #[arg( + id = "pprof.default_duration", + value_name = "PPROF_DEFAULT_DURATION", + long = "pprof.default-duration", + help = "Default CPU profiling duration in seconds", + default_value_t = constants::DEFAULT_PPROF_DEFAULT_DURATION + )] + pub default_duration: u64, +} + +impl Default for PprofArgs { + fn default() -> Self { + Self { enabled: false, addr: "0.0.0.0:6868".to_string(), default_duration: 30 } + } +} + /// Returns the total difficulty constant for the given chain. const fn td_constant(chain: Option) -> U128 { match chain { @@ -935,6 +972,7 @@ mod tests { }, database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; let result = config.validate(); @@ -967,6 +1005,7 @@ mod tests { }, database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; let result = config.validate(); @@ -994,6 +1033,7 @@ mod tests { consensus_args: ConsensusArgs::noop(), database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; assert!(config.validate().is_ok()); @@ -1019,6 +1059,7 @@ mod tests { consensus_args: ConsensusArgs::noop(), database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; assert!(config.validate().is_ok()); @@ -1040,6 +1081,7 @@ mod tests { consensus_args: ConsensusArgs::noop(), database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; assert!(config.validate().is_ok()); diff --git a/crates/node/src/constants.rs b/crates/node/src/constants.rs index 8e8be6af..2a7bc0d5 100644 --- a/crates/node/src/constants.rs +++ b/crates/node/src/constants.rs @@ -70,3 +70,9 @@ pub(crate) const SCROLL_MAINNET_SIGNER: Address = /// The authorized signer address for Scroll sepolia. pub(crate) const SCROLL_SEPOLIA_SIGNER: Address = address!("0x687E0E85AD67ff71aC134CF61b65905b58Ab43b2"); + +/// The url for pprof +pub(crate) const DEFAULT_PPROF_URL: &str = "0.0.0.0:6868"; + +/// The default duration for pprof +pub(crate) const DEFAULT_PPROF_DEFAULT_DURATION: u64 = 30; diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 5017bfcb..8aa24a22 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -6,6 +6,7 @@ mod builder; pub mod constants; mod context; mod node; +pub mod pprof; #[cfg(feature = "test-utils")] pub mod test_utils; diff --git a/crates/node/src/pprof.rs b/crates/node/src/pprof.rs new file mode 100644 index 00000000..df5ef4bd --- /dev/null +++ b/crates/node/src/pprof.rs @@ -0,0 +1,258 @@ +//! pprof integration for performance profiling +//! +//! This module provides HTTP endpoints for CPU profiling using pprof-rs. +//! It exposes a simple HTTP server that can be used to collect profiling data. +//! +//! # Endpoints +//! - `/debug/pprof/profile?seconds=` - CPU profile for specified duration (default: 30s) +//! +//! # Example Usage +//! ```bash +//! # Collect CPU profile for 30 seconds +//! curl http://localhost:6868/debug/pprof/profile?seconds=30 -o cpu.pb +//! +//! # Collect CPU profile for 60 seconds +//! curl http://localhost:6868/debug/pprof/profile?seconds=60 -o cpu.pb +//! +//! # View with pprof (requires Go pprof tool) +//! go tool pprof -http=:8080 cpu.pb +//! +//! # Or convert to flamegraph +//! pprof -flame cpu.pb > flamegraph.svg +//! ``` + +use eyre::Result; +use http_body_util::Full; +use hyper::{ + body::{Bytes, Incoming}, + server::conn::http1, + service::service_fn, + Method, Request, Response, StatusCode, +}; +use hyper_util::rt::TokioIo; +use pprof::protos::Message; +use std::{net::SocketAddr, time::Duration}; +use tokio::net::TcpListener; +use tracing::{error, info, warn}; + +/// Configuration for the pprof HTTP server +#[derive(Debug, Clone)] +pub struct PprofConfig { + /// Address to bind the pprof HTTP server to + pub addr: SocketAddr, + /// Default profiling duration in seconds + pub default_duration: u64, +} + +impl Default for PprofConfig { + fn default() -> Self { + Self { addr: SocketAddr::from(([0, 0, 0, 0], 6868)), default_duration: 30 } + } +} + +impl PprofConfig { + /// Create a new pprof configuration with custom address + pub fn new(addr: SocketAddr) -> Self { + Self { addr, ..Default::default() } + } + + /// Set the default profiling duration + pub const fn with_default_duration(mut self, seconds: u64) -> Self { + self.default_duration = seconds; + self + } + + /// Start the pprof HTTP server + /// + /// This function spawns a background task that runs an HTTP server for pprof endpoints. + /// The server will run until the returned handle is dropped or the task is cancelled. + /// + /// # Returns + /// A `JoinHandle` that can be used to manage the server task + /// + /// # Example + /// ```no_run + /// use rollup_node::pprof::PprofConfig; + /// use std::net::SocketAddr; + /// + /// #[tokio::main] + /// async fn main() -> eyre::Result<()> { + /// let config = PprofConfig::new("127.0.0.1:6868".parse()?); + /// let handle = config.launch_server().await?; + /// + /// // Server runs in background + /// // ... + /// + /// // Wait for server to complete (or cancel it) + /// handle.await??; + /// Ok(()) + /// } + /// ``` + pub async fn launch_server(self) -> Result>> { + let listener = TcpListener::bind(self.addr).await?; + let addr = listener.local_addr()?; + + info!("Starting pprof server on http://{}", addr); + info!("CPU profile endpoint: http://{}/debug/pprof/profile?seconds=30", addr); + + let default_duration = self.default_duration; + let handle = tokio::spawn(async move { + loop { + let (stream, peer_addr) = match listener.accept().await { + Ok(conn) => conn, + Err(e) => { + error!("Failed to accept connection: {}", e); + continue; + } + }; + + let io = TokioIo::new(stream); + + tokio::spawn(async move { + let service = + service_fn(move |req| Self::handle_request(req, default_duration)); + + if let Err(err) = http1::Builder::new().serve_connection(io, service).await { + error!("Error serving connection from {}: {}", peer_addr, err); + } + }); + } + }); + + Ok(handle) + } + + /// Handle HTTP requests to pprof endpoints + async fn handle_request( + req: Request, + default_duration: u64, + ) -> Result>, hyper::Error> { + match (req.method(), req.uri().path()) { + (&Method::GET, "/debug/pprof/profile") => { + // Parse duration from query parameters + let duration = req + .uri() + .query() + .and_then(|q| { + q.split('&') + .find(|pair| pair.starts_with("seconds=")) + .and_then(|pair| pair.strip_prefix("seconds=")) + .and_then(|s| s.parse::().ok()) + }) + .unwrap_or(default_duration); + + info!("Starting CPU profile for {} seconds", duration); + Self::handle_cpu_profile(duration).await + } + _ => { + warn!("Not found: {} {}", req.method(), req.uri().path()); + Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Full::new(Bytes::from("Not Found"))) + .unwrap()) + } + } + } + + /// Handle CPU profiling requests + async fn handle_cpu_profile(duration_secs: u64) -> Result>, hyper::Error> { + // Validate duration + if duration_secs == 0 || duration_secs > 600 { + let error_msg = "Invalid duration: must be between 1 and 600 seconds"; + warn!("{}", error_msg); + return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Full::new(Bytes::from(error_msg))) + .unwrap()); + } + + info!("Collecting CPU profile for {} seconds...", duration_secs); + + // Start profiling + let guard = match pprof::ProfilerGuardBuilder::default().build() { + Ok(guard) => guard, + Err(e) => { + error!("Failed to start profiler: {}", e); + let error_msg = format!("Failed to start profiler: {}", e); + return Ok(Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Full::new(Bytes::from(error_msg))) + .unwrap()); + } + }; + + // Profile for the specified duration + tokio::time::sleep(Duration::from_secs(duration_secs)).await; + + // Generate report + match guard.report().build() { + Ok(report) => { + // Encode as protobuf + match report.pprof() { + Ok(profile) => { + // The profile object needs to be converted to bytes + let body = match profile.write_to_bytes() { + Ok(bytes) => bytes, + Err(e) => { + error!("Failed to encode profile: {}", e); + let error_msg = format!("Failed to encode profile: {}", e); + return Ok(Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Full::new(Bytes::from(error_msg))) + .unwrap()); + } + }; + + info!("Successfully collected CPU profile ({} bytes)", body.len()); + + Ok(Response::builder() + .status(StatusCode::OK) + .header("Content-Type", "application/octet-stream") + .header( + "Content-Disposition", + format!("attachment; filename=\"profile-{}.pb\"", duration_secs), + ) + .body(Full::new(Bytes::from(body))) + .unwrap()) + } + Err(e) => { + error!("Failed to generate pprof format: {}", e); + let error_msg = format!("Failed to generate pprof format: {}", e); + Ok(Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Full::new(Bytes::from(error_msg))) + .unwrap()) + } + } + } + Err(e) => { + error!("Failed to generate report: {}", e); + let error_msg = format!("Failed to generate report: {}", e); + Ok(Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Full::new(Bytes::from(error_msg))) + .unwrap()) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_config() { + let config = PprofConfig::default(); + assert_eq!(config.addr, "0.0.0.0:6868".parse::().unwrap()); + assert_eq!(config.default_duration, 30); + } + + #[test] + fn test_custom_config() { + let addr = "0.0.0.0:6868".parse::().unwrap(); + let config = PprofConfig::new(addr).with_default_duration(60); + assert_eq!(config.addr, addr); + assert_eq!(config.default_duration, 60); + } +} diff --git a/crates/node/src/test_utils/fixture.rs b/crates/node/src/test_utils/fixture.rs index 137875bf..f17618ee 100644 --- a/crates/node/src/test_utils/fixture.rs +++ b/crates/node/src/test_utils/fixture.rs @@ -5,8 +5,9 @@ use super::{ }; use crate::{ BlobProviderArgs, ChainOrchestratorArgs, ConsensusAlgorithm, ConsensusArgs, EngineDriverArgs, - L1ProviderArgs, RollupNodeDatabaseArgs, RollupNodeGasPriceOracleArgs, RollupNodeNetworkArgs, - RpcArgs, ScrollRollupNode, ScrollRollupNodeConfig, SequencerArgs, SignerArgs, + L1ProviderArgs, PprofArgs, RollupNodeDatabaseArgs, RollupNodeGasPriceOracleArgs, + RollupNodeNetworkArgs, RpcArgs, ScrollRollupNode, ScrollRollupNodeConfig, SequencerArgs, + SignerArgs, }; use alloy_eips::BlockNumberOrTag; @@ -250,6 +251,7 @@ impl TestFixtureBuilder { consensus_args: ConsensusArgs::noop(), database: None, rpc_args: RpcArgs { basic_enabled: true, admin_enabled: true }, + pprof_args: PprofArgs::default(), } } diff --git a/crates/node/src/test_utils/mod.rs b/crates/node/src/test_utils/mod.rs index 0411862f..62e1cf04 100644 --- a/crates/node/src/test_utils/mod.rs +++ b/crates/node/src/test_utils/mod.rs @@ -77,7 +77,7 @@ pub use network_helpers::{ // Legacy utilities - keep existing functions for backward compatibility use crate::{ BlobProviderArgs, ChainOrchestratorArgs, ConsensusArgs, EngineDriverArgs, L1ProviderArgs, - RollupNodeDatabaseArgs, RollupNodeNetworkArgs, RpcArgs, ScrollRollupNode, + PprofArgs, RollupNodeDatabaseArgs, RollupNodeNetworkArgs, RpcArgs, ScrollRollupNode, ScrollRollupNodeConfig, SequencerArgs, }; use alloy_primitives::Bytes; @@ -239,6 +239,7 @@ pub fn default_test_scroll_rollup_node_config() -> ScrollRollupNodeConfig { gas_price_oracle_args: crate::RollupNodeGasPriceOracleArgs::default(), consensus_args: ConsensusArgs::noop(), database: None, + pprof_args: PprofArgs::default(), rpc_args: RpcArgs { basic_enabled: true, admin_enabled: true }, } } @@ -279,6 +280,7 @@ pub fn default_sequencer_test_scroll_rollup_node_config() -> ScrollRollupNodeCon gas_price_oracle_args: crate::RollupNodeGasPriceOracleArgs::default(), consensus_args: ConsensusArgs::noop(), database: None, + pprof_args: PprofArgs::default(), rpc_args: RpcArgs { basic_enabled: true, admin_enabled: true }, } } diff --git a/crates/node/tests/sync.rs b/crates/node/tests/sync.rs index ee6f687d..dbd3f87a 100644 --- a/crates/node/tests/sync.rs +++ b/crates/node/tests/sync.rs @@ -13,8 +13,8 @@ use rollup_node::{ TestFixture, }, BlobProviderArgs, ChainOrchestratorArgs, ConsensusArgs, EngineDriverArgs, L1ProviderArgs, - RollupNodeDatabaseArgs, RollupNodeGasPriceOracleArgs, RollupNodeNetworkArgs, RpcArgs, - ScrollRollupNodeConfig, SequencerArgs, + PprofArgs, RollupNodeDatabaseArgs, RollupNodeGasPriceOracleArgs, RollupNodeNetworkArgs, + RpcArgs, ScrollRollupNodeConfig, SequencerArgs, }; use rollup_node_chain_orchestrator::ChainOrchestratorEvent; use rollup_node_primitives::BlockInfo; @@ -76,6 +76,7 @@ async fn test_should_consolidate_to_block_15k() -> eyre::Result<()> { consensus_args: ConsensusArgs::noop(), database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; let chain_spec = (*SCROLL_SEPOLIA).clone(); @@ -544,6 +545,7 @@ async fn test_chain_orchestrator_l1_reorg() -> eyre::Result<()> { consensus_args: ConsensusArgs::noop(), database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; // Create the chain spec for scroll dev with Feynman activated and a test genesis. diff --git a/crates/sequencer/tests/e2e.rs b/crates/sequencer/tests/e2e.rs index 1451293e..991d183f 100644 --- a/crates/sequencer/tests/e2e.rs +++ b/crates/sequencer/tests/e2e.rs @@ -10,8 +10,8 @@ use rollup_node::{ constants::SCROLL_GAS_LIMIT, test_utils::{default_test_scroll_rollup_node_config, setup_engine}, BlobProviderArgs, ChainOrchestratorArgs, ConsensusArgs, EngineDriverArgs, L1ProviderArgs, - RollupNodeDatabaseArgs, RollupNodeGasPriceOracleArgs, RollupNodeNetworkArgs, RpcArgs, - ScrollRollupNodeConfig, SequencerArgs, SignerArgs, + PprofArgs, RollupNodeDatabaseArgs, RollupNodeGasPriceOracleArgs, RollupNodeNetworkArgs, + RpcArgs, ScrollRollupNodeConfig, SequencerArgs, SignerArgs, }; use rollup_node_chain_orchestrator::ChainOrchestratorEvent; use rollup_node_primitives::{sig_encode_hash, BlockInfo, L1MessageEnvelope}; @@ -508,6 +508,7 @@ async fn can_sequence_blocks_with_private_key_file() -> eyre::Result<()> { consensus_args: ConsensusArgs::noop(), database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; let (nodes, _tasks, wallet) = @@ -609,6 +610,7 @@ async fn can_sequence_blocks_with_hex_key_file_without_prefix() -> eyre::Result< consensus_args: ConsensusArgs::noop(), database: None, rpc_args: RpcArgs::default(), + pprof_args: PprofArgs::default(), }; let (nodes, _tasks, wallet) =