From 64873baf3dbc9db665df0c670b325dfc974f0944 Mon Sep 17 00:00:00 2001 From: JoukoVirtanen Date: Thu, 14 May 2026 08:59:10 -0700 Subject: [PATCH 1/6] X-Smart-Branch-Parent: main From 347b1ff58411adb506fa354d73d971555a1f71a9 Mon Sep 17 00:00:00 2001 From: JoukoVirtanen Date: Thu, 14 May 2026 09:50:36 -0700 Subject: [PATCH 2/6] Memory leak should be fixed --- src/worker/script.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/worker/script.rs b/src/worker/script.rs index bcd1ac8..45b14e4 100644 --- a/src/worker/script.rs +++ b/src/worker/script.rs @@ -506,17 +506,19 @@ impl Worker for ScriptWorker { debug!("Distribution {:?}", d); let Dist::Exp { rate } = d else { todo!() }; - loop { - let worker = self.clone(); - thread::spawn(move || { - (worker.jit)(); - }); - - let interval: f64 = - thread_rng().sample(Exp::new(*rate).unwrap()); - debug!("Interval {}", interval); - thread::sleep(time::Duration::from_secs_f64(interval)); - } + thread::scope(|s| { + loop { + let worker = self.clone(); + s.spawn(move || { + (worker.jit)(); + }); + + let interval: f64 = + thread_rng().sample(Exp::new(*rate).unwrap()); + debug!("Interval {}", interval); + thread::sleep(time::Duration::from_secs_f64(interval)); + } + }); } None => { debug!("Single unit"); From 117c7679ac0713c9011c65355093e90f6095c939 Mon Sep 17 00:00:00 2001 From: JoukoVirtanen Date: Thu, 14 May 2026 10:02:21 -0700 Subject: [PATCH 3/6] Fixed syntax error --- src/worker/script.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker/script.rs b/src/worker/script.rs index 45b14e4..ad250b9 100644 --- a/src/worker/script.rs +++ b/src/worker/script.rs @@ -522,7 +522,7 @@ impl Worker for ScriptWorker { } None => { debug!("Single unit"); - (self.jit)() + (self.jit)(); } }; From 7edd6ae01604ad3dcb87bd847124b23b7d3e98a1 Mon Sep 17 00:00:00 2001 From: JoukoVirtanen Date: Fri, 15 May 2026 12:27:33 -0700 Subject: [PATCH 4/6] Fixed cstring leak --- Cargo.toml | 4 ++++ Containerfile | 7 +++++-- Makefile | 4 ++++ src/main.rs | 13 ++++++++++++- src/worker/script.rs | 22 +++++++++++++++++++++- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f28d28f..50931f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,11 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +dhat-heap = ["dep:dhat"] + [dependencies] +dhat = { version = "0.3", optional = true } fork = "0.1" nix = "0.26.2" core_affinity = "0.8.0" diff --git a/Containerfile b/Containerfile index 7c5a9ab..9fb6d06 100644 --- a/Containerfile +++ b/Containerfile @@ -27,12 +27,13 @@ RUN cargo fmt --check RUN cargo clippy -- -D warnings -RUN cargo build -r +ARG CARGO_FEATURES="" +RUN cargo build -r ${CARGO_FEATURES:+--features $CARGO_FEATURES} # Test will require stub binary to be available ENV PATH="${PATH}:/berserker:/berserker/target/release" -RUN cargo test +RUN cargo test ${CARGO_FEATURES:+--features $CARGO_FEATURES} FROM registry.fedoraproject.org/fedora:43 @@ -42,6 +43,8 @@ COPY --from=builder /berserker/target/release/berserker /usr/local/bin/berserker COPY --from=builder /berserker/workload.toml /etc/berserker/workload.toml COPY --from=builder /berserker/stub /usr/local/bin/stub +COPY --from=builder /berserker/profile-entrypoint.sh /usr/local/bin/profile-entrypoint.sh + ENV PATH="${PATH}:/usr/local/bin" ENTRYPOINT berserker diff --git a/Makefile b/Makefile index 679fb90..a0309e3 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,10 @@ all: docker build -t berserker-test -f Containerfile.test . docker run --privileged berserker-test +.PHONY: profile +profile: + docker build -t berserker:profile --build-arg CARGO_FEATURES=dhat-heap -f Containerfile . + .PHONY: build-network build-berserker-network: docker build -t berserker-network scripts/network diff --git a/src/main.rs b/src/main.rs index b9c731c..c6a06f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,10 @@ //! * Invoke a workload-specific logic via run_payload //! * Wait for all the workers to finish +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + #[macro_use] extern crate log; extern crate core_affinity; @@ -107,7 +111,14 @@ fn run_script(script_path: String) -> Vec<(i32, u64)> { Some((child, duration)) } Ok(Fork::Child) => { - worker.run_payload().unwrap(); + { + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::new_heap(); + worker.run_payload().unwrap(); + } + #[cfg(feature = "dhat-heap")] + std::process::exit(0); + #[allow(unreachable_code)] None } Err(e) => { diff --git a/src/worker/script.rs b/src/worker/script.rs index ad250b9..1a20acc 100644 --- a/src/worker/script.rs +++ b/src/worker/script.rs @@ -22,6 +22,7 @@ use llvm::execution_engine::*; use llvm::target::*; use llvm_sys::LLVMType; use llvm_sys::prelude::*; +use std::cell::RefCell; use std::ffi::{CStr, CString, c_void}; use std::mem; @@ -129,6 +130,11 @@ pub unsafe extern "C" fn task(name: *const i8, random: bool) -> u64 { 0 } +thread_local! { + static LAST_RANDOM_PATH: RefCell = + RefCell::new(CString::new("").unwrap()); +} + /// Return a randomly generated path. /// /// # Safety @@ -144,7 +150,10 @@ pub unsafe extern "C" fn random_path(base: *const i8) -> *const i8 { .map(char::from) .collect(); - CString::new(format!("{base}/{uniq}")).unwrap().into_raw() + LAST_RANDOM_PATH.with(|last| { + *last.borrow_mut() = CString::new(format!("{base}/{uniq}")).unwrap(); + last.borrow().as_ptr() + }) } #[derive(Debug, Clone)] @@ -507,6 +516,9 @@ impl Worker for ScriptWorker { let Dist::Exp { rate } = d else { todo!() }; thread::scope(|s| { + #[cfg(feature = "dhat-heap")] + let mut _dhat_counter: u64 = 0; + loop { let worker = self.clone(); s.spawn(move || { @@ -517,6 +529,14 @@ impl Worker for ScriptWorker { thread_rng().sample(Exp::new(*rate).unwrap()); debug!("Interval {}", interval); thread::sleep(time::Duration::from_secs_f64(interval)); + + #[cfg(feature = "dhat-heap")] + { + _dhat_counter += 1; + if _dhat_counter >= 1_000 { + break; + } + } } }); } From 952a43a856bd02ff2df0131adbda6f85aa4a2075 Mon Sep 17 00:00:00 2001 From: JoukoVirtanen Date: Fri, 15 May 2026 12:57:48 -0700 Subject: [PATCH 5/6] Removed heap profiling --- Cargo.toml | 4 ---- Containerfile | 7 ++----- Makefile | 4 ---- src/main.rs | 13 +------------ src/worker/script.rs | 11 ----------- 5 files changed, 3 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 50931f6..f28d28f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,7 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[features] -dhat-heap = ["dep:dhat"] - [dependencies] -dhat = { version = "0.3", optional = true } fork = "0.1" nix = "0.26.2" core_affinity = "0.8.0" diff --git a/Containerfile b/Containerfile index 9fb6d06..7c5a9ab 100644 --- a/Containerfile +++ b/Containerfile @@ -27,13 +27,12 @@ RUN cargo fmt --check RUN cargo clippy -- -D warnings -ARG CARGO_FEATURES="" -RUN cargo build -r ${CARGO_FEATURES:+--features $CARGO_FEATURES} +RUN cargo build -r # Test will require stub binary to be available ENV PATH="${PATH}:/berserker:/berserker/target/release" -RUN cargo test ${CARGO_FEATURES:+--features $CARGO_FEATURES} +RUN cargo test FROM registry.fedoraproject.org/fedora:43 @@ -43,8 +42,6 @@ COPY --from=builder /berserker/target/release/berserker /usr/local/bin/berserker COPY --from=builder /berserker/workload.toml /etc/berserker/workload.toml COPY --from=builder /berserker/stub /usr/local/bin/stub -COPY --from=builder /berserker/profile-entrypoint.sh /usr/local/bin/profile-entrypoint.sh - ENV PATH="${PATH}:/usr/local/bin" ENTRYPOINT berserker diff --git a/Makefile b/Makefile index a0309e3..679fb90 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,6 @@ all: docker build -t berserker-test -f Containerfile.test . docker run --privileged berserker-test -.PHONY: profile -profile: - docker build -t berserker:profile --build-arg CARGO_FEATURES=dhat-heap -f Containerfile . - .PHONY: build-network build-berserker-network: docker build -t berserker-network scripts/network diff --git a/src/main.rs b/src/main.rs index c6a06f0..b9c731c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,10 +13,6 @@ //! * Invoke a workload-specific logic via run_payload //! * Wait for all the workers to finish -#[cfg(feature = "dhat-heap")] -#[global_allocator] -static ALLOC: dhat::Alloc = dhat::Alloc; - #[macro_use] extern crate log; extern crate core_affinity; @@ -111,14 +107,7 @@ fn run_script(script_path: String) -> Vec<(i32, u64)> { Some((child, duration)) } Ok(Fork::Child) => { - { - #[cfg(feature = "dhat-heap")] - let _profiler = dhat::Profiler::new_heap(); - worker.run_payload().unwrap(); - } - #[cfg(feature = "dhat-heap")] - std::process::exit(0); - #[allow(unreachable_code)] + worker.run_payload().unwrap(); None } Err(e) => { diff --git a/src/worker/script.rs b/src/worker/script.rs index 1a20acc..d12bd4a 100644 --- a/src/worker/script.rs +++ b/src/worker/script.rs @@ -516,9 +516,6 @@ impl Worker for ScriptWorker { let Dist::Exp { rate } = d else { todo!() }; thread::scope(|s| { - #[cfg(feature = "dhat-heap")] - let mut _dhat_counter: u64 = 0; - loop { let worker = self.clone(); s.spawn(move || { @@ -529,14 +526,6 @@ impl Worker for ScriptWorker { thread_rng().sample(Exp::new(*rate).unwrap()); debug!("Interval {}", interval); thread::sleep(time::Duration::from_secs_f64(interval)); - - #[cfg(feature = "dhat-heap")] - { - _dhat_counter += 1; - if _dhat_counter >= 1_000 { - break; - } - } } }); } From 94acc4fceeed9bf6509e3e9fad99823c53104fb7 Mon Sep 17 00:00:00 2001 From: JoukoVirtanen Date: Fri, 15 May 2026 14:21:55 -0700 Subject: [PATCH 6/6] Set maximum number of concurrent threads --- src/worker/script.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/worker/script.rs b/src/worker/script.rs index d12bd4a..18463a6 100644 --- a/src/worker/script.rs +++ b/src/worker/script.rs @@ -8,6 +8,7 @@ use std::{ io::prelude::*, net::{Shutdown, TcpStream}, process::Command, + sync::Arc, thread, time, }; @@ -515,11 +516,31 @@ impl Worker for ScriptWorker { debug!("Distribution {:?}", d); let Dist::Exp { rate } = d else { todo!() }; + const MAX_CONCURRENT: usize = 16; + let semaphore = Arc::new(( + std::sync::Mutex::new(0usize), + std::sync::Condvar::new(), + )); + thread::scope(|s| { loop { + { + let (lock, cvar) = &*semaphore; + let mut count = cvar + .wait_while(lock.lock().unwrap(), |c| { + *c >= MAX_CONCURRENT + }) + .unwrap(); + *count += 1; + } + let worker = self.clone(); + let sem = Arc::clone(&semaphore); s.spawn(move || { (worker.jit)(); + let (lock, cvar) = &*sem; + *lock.lock().unwrap() -= 1; + cvar.notify_one(); }); let interval: f64 =