reth_bench_compare/
compilation.rs1use crate::git::GitManager;
4use alloy_primitives::address;
5use alloy_provider::{Provider, ProviderBuilder};
6use eyre::{eyre, Result, WrapErr};
7use std::{fs, path::PathBuf, process::Command};
8use tracing::{debug, error, info, warn};
9
10#[derive(Debug)]
12pub(crate) struct CompilationManager {
13 repo_root: String,
14 output_dir: PathBuf,
15 git_manager: GitManager,
16 features: String,
17}
18
19impl CompilationManager {
20 pub(crate) const fn new(
22 repo_root: String,
23 output_dir: PathBuf,
24 git_manager: GitManager,
25 features: String,
26 ) -> Result<Self> {
27 Ok(Self { repo_root, output_dir, git_manager, features })
28 }
29
30 pub(crate) async fn detect_optimism_chain(&self, rpc_url: &str) -> Result<bool> {
32 info!("Detecting chain type from RPC endpoint...");
33
34 let url = rpc_url.parse().map_err(|e| eyre!("Invalid RPC URL '{}': {}", rpc_url, e))?;
36 let provider = ProviderBuilder::new().connect_http(url);
37
38 let is_optimism = !provider
40 .get_code_at(address!("0x420000000000000000000000000000000000000F"))
41 .await?
42 .is_empty();
43
44 if is_optimism {
45 info!("Detected Optimism chain");
46 } else {
47 info!("Detected Ethereum chain");
48 }
49
50 Ok(is_optimism)
51 }
52
53 pub(crate) fn get_cached_binary_path_for_commit(
55 &self,
56 commit: &str,
57 is_optimism: bool,
58 ) -> PathBuf {
59 let identifier = &commit[..8]; let binary_name = if is_optimism {
62 format!("op-reth_{}", identifier)
63 } else {
64 format!("reth_{}", identifier)
65 };
66
67 self.output_dir.join("bin").join(binary_name)
68 }
69
70 pub(crate) fn compile_reth(&self, commit: &str, is_optimism: bool) -> Result<()> {
72 let current_commit = self.git_manager.get_current_commit()?;
74 if current_commit != commit {
75 return Err(eyre!(
76 "Git commit mismatch! Expected: {}, but currently at: {}",
77 &commit[..8],
78 ¤t_commit[..8]
79 ));
80 }
81
82 let cached_path = self.get_cached_binary_path_for_commit(commit, is_optimism);
83
84 if cached_path.exists() {
86 info!("Using cached binary (commit: {})", &commit[..8]);
87 return Ok(());
88 }
89
90 info!("No cached binary found, compiling (commit: {})...", &commit[..8]);
91
92 let binary_name = if is_optimism { "op-reth" } else { "reth" };
93
94 info!(
95 "Compiling {} with profiling configuration (commit: {})...",
96 binary_name,
97 &commit[..8]
98 );
99
100 let mut cmd = Command::new("cargo");
101 cmd.arg("build").arg("--profile").arg("profiling");
102
103 cmd.arg("--features").arg(&self.features);
105 info!("Using features: {}", self.features);
106
107 if is_optimism {
109 cmd.arg("--bin")
110 .arg("op-reth")
111 .arg("--manifest-path")
112 .arg("crates/optimism/bin/Cargo.toml");
113 }
114
115 cmd.current_dir(&self.repo_root);
116
117 cmd.env("RUSTFLAGS", "-C target-cpu=native");
119
120 debug!("Executing cargo command: {:?}", cmd);
122
123 let output = cmd.output().wrap_err("Failed to execute cargo build command")?;
124
125 let stdout = String::from_utf8_lossy(&output.stdout);
127 let stderr = String::from_utf8_lossy(&output.stderr);
128
129 for line in stdout.lines() {
130 if !line.trim().is_empty() {
131 debug!("[CARGO] {}", line);
132 }
133 }
134
135 for line in stderr.lines() {
136 if !line.trim().is_empty() {
137 debug!("[CARGO] {}", line);
138 }
139 }
140
141 if !output.status.success() {
142 error!("Cargo build failed with exit code: {:?}", output.status.code());
144
145 if !stdout.trim().is_empty() {
146 error!("Cargo stdout:");
147 for line in stdout.lines() {
148 error!(" {}", line);
149 }
150 }
151
152 if !stderr.trim().is_empty() {
153 error!("Cargo stderr:");
154 for line in stderr.lines() {
155 error!(" {}", line);
156 }
157 }
158
159 return Err(eyre!("Compilation failed with exit code: {:?}", output.status.code()));
160 }
161
162 info!("{} compilation completed", binary_name);
163
164 let source_path =
166 PathBuf::from(&self.repo_root).join(format!("target/profiling/{}", binary_name));
167 if !source_path.exists() {
168 return Err(eyre!("Compiled binary not found at {:?}", source_path));
169 }
170
171 let bin_dir = self.output_dir.join("bin");
173 fs::create_dir_all(&bin_dir).wrap_err("Failed to create bin directory")?;
174
175 fs::copy(&source_path, &cached_path).wrap_err("Failed to copy binary to cache")?;
177
178 #[cfg(unix)]
180 {
181 use std::os::unix::fs::PermissionsExt;
182 let mut perms = fs::metadata(&cached_path)?.permissions();
183 perms.set_mode(0o755);
184 fs::set_permissions(&cached_path, perms)?;
185 }
186
187 info!("Cached compiled binary at: {:?}", cached_path);
188 Ok(())
189 }
190
191 pub(crate) fn is_reth_bench_available(&self) -> bool {
193 match Command::new("which").arg("reth-bench").output() {
194 Ok(output) => {
195 if output.status.success() {
196 let path = String::from_utf8_lossy(&output.stdout);
197 info!("Found reth-bench: {}", path.trim());
198 true
199 } else {
200 false
201 }
202 }
203 Err(_) => false,
204 }
205 }
206
207 pub(crate) fn is_samply_available(&self) -> bool {
209 match Command::new("which").arg("samply").output() {
210 Ok(output) => {
211 if output.status.success() {
212 let path = String::from_utf8_lossy(&output.stdout);
213 info!("Found samply: {}", path.trim());
214 true
215 } else {
216 false
217 }
218 }
219 Err(_) => false,
220 }
221 }
222
223 pub(crate) fn install_samply(&self) -> Result<()> {
225 info!("Installing samply via cargo...");
226
227 let mut cmd = Command::new("cargo");
228 cmd.args(["install", "--locked", "samply"]);
229
230 debug!("Executing cargo command: {:?}", cmd);
232
233 let output = cmd.output().wrap_err("Failed to execute cargo install samply command")?;
234
235 let stdout = String::from_utf8_lossy(&output.stdout);
237 let stderr = String::from_utf8_lossy(&output.stderr);
238
239 for line in stdout.lines() {
240 if !line.trim().is_empty() {
241 debug!("[CARGO-SAMPLY] {}", line);
242 }
243 }
244
245 for line in stderr.lines() {
246 if !line.trim().is_empty() {
247 debug!("[CARGO-SAMPLY] {}", line);
248 }
249 }
250
251 if !output.status.success() {
252 error!("Cargo install samply failed with exit code: {:?}", output.status.code());
254
255 if !stdout.trim().is_empty() {
256 error!("Cargo stdout:");
257 for line in stdout.lines() {
258 error!(" {}", line);
259 }
260 }
261
262 if !stderr.trim().is_empty() {
263 error!("Cargo stderr:");
264 for line in stderr.lines() {
265 error!(" {}", line);
266 }
267 }
268
269 return Err(eyre!(
270 "samply installation failed with exit code: {:?}",
271 output.status.code()
272 ));
273 }
274
275 info!("Samply installation completed");
276 Ok(())
277 }
278
279 pub(crate) fn ensure_samply_available(&self) -> Result<()> {
281 if self.is_samply_available() {
282 Ok(())
283 } else {
284 warn!("samply not found in PATH, installing...");
285 self.install_samply()
286 }
287 }
288
289 pub(crate) fn ensure_reth_bench_available(&self) -> Result<()> {
291 if self.is_reth_bench_available() {
292 Ok(())
293 } else {
294 warn!("reth-bench not found in PATH, compiling and installing...");
295 self.compile_reth_bench()
296 }
297 }
298
299 pub(crate) fn compile_reth_bench(&self) -> Result<()> {
301 info!("Compiling and installing reth-bench...");
302
303 let mut cmd = Command::new("make");
304 cmd.arg("install-reth-bench").current_dir(&self.repo_root);
305
306 debug!("Executing make command: {:?}", cmd);
308
309 let output = cmd.output().wrap_err("Failed to execute make install-reth-bench command")?;
310
311 let stdout = String::from_utf8_lossy(&output.stdout);
313 let stderr = String::from_utf8_lossy(&output.stderr);
314
315 for line in stdout.lines() {
316 if !line.trim().is_empty() {
317 debug!("[MAKE-BENCH] {}", line);
318 }
319 }
320
321 for line in stderr.lines() {
322 if !line.trim().is_empty() {
323 debug!("[MAKE-BENCH] {}", line);
324 }
325 }
326
327 if !output.status.success() {
328 error!("Make install-reth-bench failed with exit code: {:?}", output.status.code());
330
331 if !stdout.trim().is_empty() {
332 error!("Make stdout:");
333 for line in stdout.lines() {
334 error!(" {}", line);
335 }
336 }
337
338 if !stderr.trim().is_empty() {
339 error!("Make stderr:");
340 for line in stderr.lines() {
341 error!(" {}", line);
342 }
343 }
344
345 return Err(eyre!(
346 "reth-bench compilation failed with exit code: {:?}",
347 output.status.code()
348 ));
349 }
350
351 info!("Reth-bench compilation completed");
352 Ok(())
353 }
354}