Initial implementation of busywork library

Pattern-breaking sleep replacement that executes real, varied work on
every call. Randomized task selection across 7 categories (compute,
memory, filesystem, registry, winapi, network, crypto) with intensity
levels and jitter. Zero time objects in the library binary.
This commit is contained in:
PatchRequest
2026-06-04 12:12:32 +02:00
commit 5d5f15a7da
19 changed files with 1738 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/target
Generated
+394
View File
@@ -0,0 +1,394 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "bitflags"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "busywork"
version = "0.1.0"
dependencies = [
"bitflags",
"flate2",
"md-5",
"rand",
"sha2",
"windows",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "md-5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "simd-adler32"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core",
"windows-targets",
]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-strings",
"windows-targets",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zerocopy"
version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
+49
View File
@@ -0,0 +1,49 @@
[package]
name = "busywork"
version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8"
bitflags = "2"
sha2 = { version = "0.10", optional = true }
md-5 = { version = "0.10", optional = true }
flate2 = { version = "1", optional = true }
[dependencies.windows]
version = "0.58"
optional = true
[features]
default = [
"cat-compute",
"cat-memory",
"cat-filesystem",
"cat-registry",
"cat-winapi",
"cat-network",
"cat-crypto",
]
cat-compute = ["dep:sha2", "dep:md-5", "dep:flate2"]
cat-memory = []
cat-filesystem = []
cat-registry = [
"dep:windows",
"windows/Win32_System_Registry",
"windows/Win32_Foundation",
]
cat-winapi = [
"dep:windows",
"windows/Win32_UI_WindowsAndMessaging",
"windows/Win32_System_Diagnostics_ToolHelp",
"windows/Win32_System_SystemInformation",
"windows/Win32_System_DataExchange",
"windows/Win32_Foundation",
"windows/Win32_System_Threading",
]
cat-network = []
cat-crypto = [
"dep:windows",
"windows/Win32_Security_Cryptography",
"windows/Win32_Foundation",
]
+41
View File
@@ -0,0 +1,41 @@
use crate::categories::Categories;
use crate::dispatch;
use crate::intensity::Intensity;
pub struct BusyWork {
intensity: Intensity,
allow: Categories,
deny: Categories,
jitter: bool,
}
impl BusyWork {
pub fn new(intensity: Intensity) -> Self {
Self {
intensity,
allow: Categories::all(),
deny: Categories::empty(),
jitter: true,
}
}
pub fn allow(mut self, cats: Categories) -> Self {
self.allow = cats;
self
}
pub fn deny(mut self, cats: Categories) -> Self {
self.deny = cats;
self
}
pub fn jitter(mut self, enabled: bool) -> Self {
self.jitter = enabled;
self
}
pub fn run(&self) {
let effective = (self.allow & Categories::available()) & !self.deny;
dispatch::execute(self.intensity, effective, self.jitter);
}
}
+49
View File
@@ -0,0 +1,49 @@
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Categories: u32 {
const COMPUTE = 0b000_0001;
const MEMORY = 0b000_0010;
const FILESYSTEM = 0b000_0100;
const REGISTRY = 0b000_1000;
const WINAPI = 0b001_0000;
const NETWORK = 0b010_0000;
const CRYPTO = 0b100_0000;
}
}
impl Categories {
pub fn available() -> Self {
let mut cats = Self::empty();
#[cfg(feature = "cat-compute")]
{
cats |= Self::COMPUTE;
}
#[cfg(feature = "cat-memory")]
{
cats |= Self::MEMORY;
}
#[cfg(feature = "cat-filesystem")]
{
cats |= Self::FILESYSTEM;
}
#[cfg(feature = "cat-registry")]
{
cats |= Self::REGISTRY;
}
#[cfg(feature = "cat-winapi")]
{
cats |= Self::WINAPI;
}
#[cfg(feature = "cat-network")]
{
cats |= Self::NETWORK;
}
#[cfg(feature = "cat-crypto")]
{
cats |= Self::CRYPTO;
}
cats
}
}
+47
View File
@@ -0,0 +1,47 @@
use crate::categories::Categories;
use crate::intensity::Intensity;
use crate::jitter;
use crate::tasks::{self, TaskParams};
use rand::seq::SliceRandom;
pub fn execute(intensity: Intensity, effective: Categories, jitter_enabled: bool) {
let mut rng = rand::thread_rng();
let all = tasks::all_tasks();
let eligible: Vec<_> = all
.iter()
.filter(|t| effective.contains(t.category))
.collect();
if eligible.is_empty() {
return;
}
let base = intensity.base_params();
let count = if jitter_enabled {
jitter::apply(base.task_count, &mut rng)
} else {
base.task_count
};
for _ in 0..count {
let task = eligible.choose(&mut rng).unwrap();
let params = TaskParams {
iterations: if jitter_enabled {
jitter::apply(base.iteration_count, &mut rng)
} else {
base.iteration_count
},
buffer_size: if jitter_enabled {
jitter::apply(base.buffer_size, &mut rng)
} else {
base.buffer_size
},
call_depth: if jitter_enabled {
jitter::apply(base.call_depth, &mut rng)
} else {
base.call_depth
},
};
(task.func)(&params, &mut rng);
}
}
+45
View File
@@ -0,0 +1,45 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Intensity {
Low,
Medium,
High,
Ultra,
}
pub(crate) struct IntensityParams {
pub task_count: usize,
pub iteration_count: usize,
pub buffer_size: usize,
pub call_depth: usize,
}
impl Intensity {
pub(crate) fn base_params(&self) -> IntensityParams {
match self {
Intensity::Low => IntensityParams {
task_count: 2,
iteration_count: 50,
buffer_size: 1024,
call_depth: 2,
},
Intensity::Medium => IntensityParams {
task_count: 5,
iteration_count: 500,
buffer_size: 16384,
call_depth: 4,
},
Intensity::High => IntensityParams {
task_count: 10,
iteration_count: 5000,
buffer_size: 262144,
call_depth: 8,
},
Intensity::Ultra => IntensityParams {
task_count: 20,
iteration_count: 50000,
buffer_size: 1048576,
call_depth: 16,
},
}
}
}
+6
View File
@@ -0,0 +1,6 @@
use rand::Rng;
pub fn apply(base: usize, rng: &mut impl Rng) -> usize {
let factor: f64 = rng.gen_range(0.7..=1.3);
(base as f64 * factor).round().max(1.0) as usize
}
+18
View File
@@ -0,0 +1,18 @@
mod builder;
mod categories;
mod dispatch;
mod intensity;
mod jitter;
mod tasks;
pub use builder::BusyWork;
pub use categories::Categories;
pub use intensity::Intensity;
pub fn busywork(intensity: Intensity) {
BusyWork::new(intensity).run();
}
pub fn busywork_with(intensity: Intensity, categories: Categories) {
BusyWork::new(intensity).allow(categories).run();
}
+147
View File
@@ -0,0 +1,147 @@
use crate::categories::Categories;
use crate::tasks::{TaskDescriptor, TaskParams};
use flate2::write::{DeflateDecoder, DeflateEncoder};
use flate2::Compression;
use md5::Md5;
use rand::rngs::ThreadRng;
use rand::{Rng, RngCore};
use sha2::{Digest, Sha256};
use std::hint::black_box;
use std::io::Write;
pub fn register() -> Vec<TaskDescriptor> {
vec![
TaskDescriptor {
name: "hash_sha256_loop",
category: Categories::COMPUTE,
func: hash_sha256_loop,
},
TaskDescriptor {
name: "hash_md5_loop",
category: Categories::COMPUTE,
func: hash_md5_loop,
},
TaskDescriptor {
name: "prime_sieve",
category: Categories::COMPUTE,
func: prime_sieve,
},
TaskDescriptor {
name: "matrix_multiply",
category: Categories::COMPUTE,
func: matrix_multiply,
},
TaskDescriptor {
name: "sort_random_arrays",
category: Categories::COMPUTE,
func: sort_random_arrays,
},
TaskDescriptor {
name: "compress_decompress",
category: Categories::COMPUTE,
func: compress_decompress,
},
]
}
fn hash_sha256_loop(params: &TaskParams, rng: &mut ThreadRng) {
let mut data = vec![0u8; 64];
rng.fill_bytes(&mut data);
for _ in 0..params.iterations {
let mut hasher = Sha256::new();
hasher.update(&data);
let result = hasher.finalize();
data[..32].copy_from_slice(&result);
}
black_box(&data);
}
fn hash_md5_loop(params: &TaskParams, rng: &mut ThreadRng) {
let mut data = vec![0u8; 64];
rng.fill_bytes(&mut data);
for _ in 0..params.iterations {
let mut hasher = Md5::new();
hasher.update(&data);
let result = hasher.finalize();
data[..16].copy_from_slice(&result);
}
black_box(&data);
}
fn prime_sieve(params: &TaskParams, _rng: &mut ThreadRng) {
let limit = params.iterations.saturating_mul(100).min(10_000_000);
if limit < 2 {
return;
}
let mut sieve = vec![true; limit];
sieve[0] = false;
sieve[1] = false;
let sqrt_limit = (limit as f64).sqrt() as usize;
for i in 2..=sqrt_limit {
if sieve[i] {
let mut j = i * i;
while j < limit {
sieve[j] = false;
j += i;
}
}
}
let count = sieve.iter().filter(|&&b| b).count();
black_box(count);
}
fn matrix_multiply(params: &TaskParams, rng: &mut ThreadRng) {
let n = ((params.iterations as f64).sqrt() as usize).max(2).min(512);
let mut a = vec![0.0f64; n * n];
let mut b = vec![0.0f64; n * n];
for x in a.iter_mut() {
*x = rng.gen::<f64>();
}
for x in b.iter_mut() {
*x = rng.gen::<f64>();
}
let mut c = vec![0.0f64; n * n];
for i in 0..n {
for k in 0..n {
let a_ik = a[i * n + k];
for j in 0..n {
c[i * n + j] += a_ik * b[k * n + j];
}
}
}
black_box(&c);
}
fn sort_random_arrays(params: &TaskParams, rng: &mut ThreadRng) {
let size = (params.buffer_size / 8).max(1);
for _ in 0..params.call_depth {
let mut data: Vec<u64> = (0..size).map(|_| rng.gen()).collect();
data.sort_unstable();
black_box(&data);
}
}
fn compress_decompress(params: &TaskParams, rng: &mut ThreadRng) {
let size = params.buffer_size.min(65536);
let mut data = vec![0u8; size];
rng.fill_bytes(&mut data);
for _ in 0..params.call_depth {
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::fast());
if encoder.write_all(&data).is_err() {
continue;
}
let compressed = match encoder.finish() {
Ok(c) => c,
Err(_) => continue,
};
let mut decoder = DeflateDecoder::new(Vec::new());
if decoder.write_all(&compressed).is_err() {
continue;
}
let decompressed = match decoder.finish() {
Ok(d) => d,
Err(_) => continue,
};
black_box(&decompressed);
}
}
+73
View File
@@ -0,0 +1,73 @@
use crate::categories::Categories;
use crate::tasks::{TaskDescriptor, TaskParams};
use rand::rngs::ThreadRng;
use rand::RngCore;
use std::hint::black_box;
use windows::Win32::Security::Cryptography::*;
pub fn register() -> Vec<TaskDescriptor> {
vec![
TaskDescriptor {
name: "bcrypt_gen_random",
category: Categories::CRYPTO,
func: bcrypt_gen_random,
},
TaskDescriptor {
name: "bcrypt_hash",
category: Categories::CRYPTO,
func: bcrypt_hash,
},
]
}
fn bcrypt_gen_random(params: &TaskParams, _rng: &mut ThreadRng) {
let size = params.buffer_size.min(65536);
let mut buffer = vec![0u8; size];
for _ in 0..params.iterations.min(100) {
unsafe {
let _ = BCryptGenRandom(
BCRYPT_ALG_HANDLE::default(),
&mut buffer,
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
);
}
black_box(&buffer);
}
}
fn bcrypt_hash(params: &TaskParams, rng: &mut ThreadRng) {
let data_size = params.buffer_size.min(65536);
let mut data = vec![0u8; data_size];
rng.fill_bytes(&mut data);
unsafe {
let mut alg = BCRYPT_ALG_HANDLE::default();
let status = BCryptOpenAlgorithmProvider(
&mut alg,
windows::core::w!("SHA256"),
None,
BCRYPT_OPEN_ALGORITHM_PROVIDER_FLAGS(0),
);
if status.0 != 0 {
return;
}
for _ in 0..params.iterations.min(100) {
let mut hash_handle = BCRYPT_HASH_HANDLE::default();
let status = BCryptCreateHash(alg, &mut hash_handle, None, None, 0);
if status.0 != 0 {
continue;
}
let _ = BCryptHashData(hash_handle, &data, 0);
let mut output = vec![0u8; 32]; // SHA-256 output
let _ = BCryptFinishHash(hash_handle, &mut output, 0);
black_box(&output);
let _ = BCryptDestroyHash(hash_handle);
}
let _ = BCryptCloseAlgorithmProvider(alg, 0);
}
}
+96
View File
@@ -0,0 +1,96 @@
use crate::categories::Categories;
use crate::tasks::{TaskDescriptor, TaskParams};
use rand::rngs::ThreadRng;
use rand::seq::SliceRandom;
use std::hint::black_box;
pub fn register() -> Vec<TaskDescriptor> {
vec![
TaskDescriptor {
name: "enumerate_system_dir",
category: Categories::FILESYSTEM,
func: enumerate_system_dir,
},
TaskDescriptor {
name: "enumerate_temp_dir",
category: Categories::FILESYSTEM,
func: enumerate_temp_dir,
},
TaskDescriptor {
name: "stat_system_files",
category: Categories::FILESYSTEM,
func: stat_system_files,
},
TaskDescriptor {
name: "read_small_files",
category: Categories::FILESYSTEM,
func: read_small_files,
},
]
}
fn enumerate_system_dir(params: &TaskParams, _rng: &mut ThreadRng) {
let _ = (|| -> std::io::Result<()> {
let dir = std::fs::read_dir(r"C:\Windows\System32")?;
for (i, entry) in dir.enumerate() {
if i >= params.iterations {
break;
}
let entry = entry?;
let meta = entry.metadata()?;
black_box(meta.len());
}
Ok(())
})();
}
fn enumerate_temp_dir(params: &TaskParams, _rng: &mut ThreadRng) {
let _ = (|| -> std::io::Result<()> {
let dir = std::fs::read_dir(std::env::temp_dir())?;
for (i, entry) in dir.enumerate() {
if i >= params.iterations {
break;
}
let entry = entry?;
let meta = entry.metadata()?;
black_box(meta.len());
}
Ok(())
})();
}
fn stat_system_files(params: &TaskParams, rng: &mut ThreadRng) {
let paths = [
r"C:\Windows\explorer.exe",
r"C:\Windows\notepad.exe",
r"C:\Windows\System32\kernel32.dll",
r"C:\Windows\System32\ntdll.dll",
r"C:\Windows\System32\user32.dll",
r"C:\Windows\System32\advapi32.dll",
r"C:\Windows\System32\ws2_32.dll",
r"C:\Windows\System32\cmd.exe",
];
for _ in 0..params.iterations.min(200) {
if let Some(path) = paths.choose(rng) {
if let Ok(meta) = std::fs::metadata(path) {
black_box(meta.len());
}
}
}
}
fn read_small_files(params: &TaskParams, rng: &mut ThreadRng) {
let paths = [
r"C:\Windows\System32\drivers\etc\hosts",
r"C:\Windows\System32\drivers\etc\services",
r"C:\Windows\System32\drivers\etc\protocol",
r"C:\Windows\System32\drivers\etc\networks",
];
for _ in 0..params.call_depth {
if let Some(path) = paths.choose(rng) {
if let Ok(data) = std::fs::read(path) {
black_box(data.len());
}
}
}
}
+73
View File
@@ -0,0 +1,73 @@
use crate::categories::Categories;
use crate::tasks::{TaskDescriptor, TaskParams};
use rand::rngs::ThreadRng;
use rand::{Rng, RngCore};
use std::hint::black_box;
pub fn register() -> Vec<TaskDescriptor> {
vec![
TaskDescriptor {
name: "alloc_touch_free",
category: Categories::MEMORY,
func: alloc_touch_free,
},
TaskDescriptor {
name: "memcpy_chain",
category: Categories::MEMORY,
func: memcpy_chain,
},
TaskDescriptor {
name: "sort_random_memory",
category: Categories::MEMORY,
func: sort_random_memory,
},
TaskDescriptor {
name: "pattern_fill_verify",
category: Categories::MEMORY,
func: pattern_fill_verify,
},
]
}
fn alloc_touch_free(params: &TaskParams, rng: &mut ThreadRng) {
let size = params.buffer_size.min(1_048_576);
for _ in 0..params.iterations.min(100) {
let mut buf = vec![0u8; size];
for offset in (0..size).step_by(4096) {
buf[offset] = rng.gen();
}
black_box(&buf);
}
}
fn memcpy_chain(params: &TaskParams, rng: &mut ThreadRng) {
let size = params.buffer_size.min(1_048_576);
let mut src = vec![0u8; size];
let mut dst = vec![0u8; size];
rng.fill_bytes(&mut src);
for _ in 0..params.iterations.min(1000) {
dst.copy_from_slice(&src);
std::mem::swap(&mut src, &mut dst);
}
black_box(&src);
}
fn sort_random_memory(params: &TaskParams, rng: &mut ThreadRng) {
let size = (params.buffer_size / 8).max(1);
for _ in 0..params.call_depth {
let mut data: Vec<u64> = (0..size).map(|_| rng.gen()).collect();
data.sort_unstable();
black_box(&data);
}
}
fn pattern_fill_verify(params: &TaskParams, rng: &mut ThreadRng) {
let size = params.buffer_size.min(1_048_576);
let mut buf = vec![0u8; size];
for _ in 0..params.iterations.min(100) {
let pattern: u8 = rng.gen();
buf.fill(pattern);
let valid = buf.iter().all(|&b| b == pattern);
black_box(valid);
}
}
+51
View File
@@ -0,0 +1,51 @@
#[cfg(feature = "cat-compute")]
pub mod compute;
#[cfg(feature = "cat-crypto")]
pub mod crypto;
#[cfg(feature = "cat-filesystem")]
pub mod filesystem;
#[cfg(feature = "cat-memory")]
pub mod memory;
#[cfg(feature = "cat-network")]
pub mod network;
#[cfg(feature = "cat-registry")]
pub mod registry;
#[cfg(feature = "cat-winapi")]
pub mod winapi_tasks;
use crate::categories::Categories;
use rand::rngs::ThreadRng;
pub struct TaskParams {
pub iterations: usize,
pub buffer_size: usize,
pub call_depth: usize,
}
pub type TaskFn = fn(&TaskParams, &mut ThreadRng);
#[allow(dead_code)]
pub struct TaskDescriptor {
pub name: &'static str,
pub category: Categories,
pub func: TaskFn,
}
pub fn all_tasks() -> Vec<TaskDescriptor> {
let mut tasks = Vec::new();
#[cfg(feature = "cat-compute")]
tasks.extend(compute::register());
#[cfg(feature = "cat-memory")]
tasks.extend(memory::register());
#[cfg(feature = "cat-filesystem")]
tasks.extend(filesystem::register());
#[cfg(feature = "cat-registry")]
tasks.extend(registry::register());
#[cfg(feature = "cat-winapi")]
tasks.extend(winapi_tasks::register());
#[cfg(feature = "cat-network")]
tasks.extend(network::register());
#[cfg(feature = "cat-crypto")]
tasks.extend(crypto::register());
tasks
}
+115
View File
@@ -0,0 +1,115 @@
use crate::categories::Categories;
use crate::tasks::{TaskDescriptor, TaskParams};
use rand::rngs::ThreadRng;
use rand::seq::SliceRandom;
use std::hint::black_box;
use std::io::{Read, Write};
use std::net::{TcpStream, ToSocketAddrs, UdpSocket};
use std::os::windows::io::AsRawSocket;
#[link(name = "ws2_32")]
extern "system" {
fn setsockopt(s: usize, level: i32, optname: i32, optval: *const u8, optlen: i32) -> i32;
}
const SOL_SOCKET: i32 = 0xFFFF;
const SO_RCVTIMEO: i32 = 0x1006;
const SO_SNDTIMEO: i32 = 0x1005;
fn set_socket_timeouts(socket: &impl AsRawSocket, ms: u32) {
let raw = socket.as_raw_socket() as usize;
let val = ms.to_ne_bytes();
unsafe {
setsockopt(raw, SOL_SOCKET, SO_RCVTIMEO, val.as_ptr(), 4);
setsockopt(raw, SOL_SOCKET, SO_SNDTIMEO, val.as_ptr(), 4);
}
}
pub fn register() -> Vec<TaskDescriptor> {
vec![
TaskDescriptor {
name: "dns_lookups",
category: Categories::NETWORK,
func: dns_lookups,
},
TaskDescriptor {
name: "http_get",
category: Categories::NETWORK,
func: http_get,
},
TaskDescriptor {
name: "ntp_query",
category: Categories::NETWORK,
func: ntp_query,
},
]
}
fn dns_lookups(params: &TaskParams, rng: &mut ThreadRng) {
let hosts = [
"google.com:80",
"microsoft.com:80",
"cloudflare.com:80",
"github.com:80",
"amazon.com:80",
"apple.com:80",
"mozilla.org:80",
"wikipedia.org:80",
];
for _ in 0..params.iterations.min(50) {
if let Some(host) = hosts.choose(rng) {
let _ = host.to_socket_addrs().map(|addrs| {
for addr in addrs {
black_box(addr);
}
});
}
}
}
fn http_get(params: &TaskParams, rng: &mut ThreadRng) {
let targets: &[(&str, u16, &str)] = &[
("httpbin.org", 80, "/get"),
("ip-api.com", 80, "/json"),
("ifconfig.me", 80, "/ip"),
];
for _ in 0..params.call_depth.min(3) {
if let Some(&(host, port, path)) = targets.choose(rng) {
let addr = format!("{}:{}", host, port);
let stream = match TcpStream::connect(&*addr) {
Ok(s) => s,
Err(_) => continue,
};
set_socket_timeouts(&stream, 3000);
let request = format!(
"GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
path, host
);
let mut stream = stream;
if stream.write_all(request.as_bytes()).is_err() {
continue;
}
let mut response = vec![0u8; 4096];
let _ = stream.read(&mut response);
black_box(&response);
}
}
}
fn ntp_query(params: &TaskParams, _rng: &mut ThreadRng) {
for _ in 0..params.call_depth.min(3) {
let socket = match UdpSocket::bind("0.0.0.0:0") {
Ok(s) => s,
Err(_) => continue,
};
set_socket_timeouts(&socket, 3000);
let mut packet = [0u8; 48];
packet[0] = 0x1B; // NTP v3, client mode
if socket.send_to(&packet, "pool.ntp.org:123").is_err() {
continue;
}
let mut response = [0u8; 48];
let _ = socket.recv_from(&mut response);
black_box(&response);
}
}
+201
View File
@@ -0,0 +1,201 @@
use crate::categories::Categories;
use crate::tasks::{TaskDescriptor, TaskParams};
use rand::rngs::ThreadRng;
use std::hint::black_box;
use windows::Win32::System::Registry::*;
pub fn register() -> Vec<TaskDescriptor> {
vec![
TaskDescriptor {
name: "read_installed_software",
category: Categories::REGISTRY,
func: read_installed_software,
},
TaskDescriptor {
name: "read_system_info_reg",
category: Categories::REGISTRY,
func: read_system_info_reg,
},
TaskDescriptor {
name: "enumerate_services_reg",
category: Categories::REGISTRY,
func: enumerate_services_reg,
},
TaskDescriptor {
name: "read_timezone_info",
category: Categories::REGISTRY,
func: read_timezone_info,
},
]
}
unsafe fn enum_subkeys(root: HKEY, subkey_path: &[u16], max_keys: usize) {
let mut hkey = HKEY::default();
let result = RegOpenKeyExW(
root,
windows::core::PCWSTR(subkey_path.as_ptr()),
0,
KEY_READ,
&mut hkey,
);
if result.0 != 0 {
return;
}
let mut name_buf = [0u16; 256];
for i in 0..max_keys as u32 {
let mut name_len = name_buf.len() as u32;
let result = RegEnumKeyExW(
hkey,
i,
windows::core::PWSTR(name_buf.as_mut_ptr()),
&mut name_len,
None,
windows::core::PWSTR::null(),
None,
None,
);
if result.0 != 0 {
break;
}
black_box(&name_buf[..name_len as usize]);
}
let _ = RegCloseKey(hkey);
}
unsafe fn read_string_value(hkey: HKEY, value_name: &[u16]) {
let mut data_type = REG_VALUE_TYPE::default();
let mut buf = [0u8; 512];
let mut buf_len = buf.len() as u32;
let result = RegQueryValueExW(
hkey,
windows::core::PCWSTR(value_name.as_ptr()),
None,
Some(&mut data_type),
Some(buf.as_mut_ptr()),
Some(&mut buf_len),
);
if result.0 == 0 {
black_box(&buf[..buf_len as usize]);
}
}
fn read_installed_software(params: &TaskParams, _rng: &mut ThreadRng) {
const PATH: &[u16] = &{
let mut arr = [0u16; 60];
let bytes = b"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\0";
let mut i = 0;
while i < bytes.len() {
arr[i] = bytes[i] as u16;
i += 1;
}
arr
};
unsafe {
enum_subkeys(HKEY_LOCAL_MACHINE, PATH, params.iterations);
}
}
fn read_system_info_reg(params: &TaskParams, _rng: &mut ThreadRng) {
const PATH: &[u16] = &{
let mut arr = [0u16; 50];
let bytes = b"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\0";
let mut i = 0;
while i < bytes.len() {
arr[i] = bytes[i] as u16;
i += 1;
}
arr
};
unsafe {
let mut hkey = HKEY::default();
let result = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
windows::core::PCWSTR(PATH.as_ptr()),
0,
KEY_READ,
&mut hkey,
);
if result.0 != 0 {
return;
}
let value_names: &[&[u16]] = &[
&encode_wide_const::<20>(b"ProductName\0"),
&encode_wide_const::<20>(b"CurrentBuild\0"),
&encode_wide_const::<20>(b"EditionID\0"),
];
for _ in 0..params.call_depth {
for name in value_names {
read_string_value(hkey, name);
}
}
let _ = RegCloseKey(hkey);
}
}
fn enumerate_services_reg(params: &TaskParams, _rng: &mut ThreadRng) {
const PATH: &[u16] = &{
let mut arr = [0u16; 50];
let bytes = b"SYSTEM\\CurrentControlSet\\Services\0";
let mut i = 0;
while i < bytes.len() {
arr[i] = bytes[i] as u16;
i += 1;
}
arr
};
unsafe {
enum_subkeys(HKEY_LOCAL_MACHINE, PATH, params.iterations);
}
}
fn read_timezone_info(params: &TaskParams, _rng: &mut ThreadRng) {
const PATH: &[u16] = &{
let mut arr = [0u16; 60];
let bytes = b"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\0";
let mut i = 0;
while i < bytes.len() {
arr[i] = bytes[i] as u16;
i += 1;
}
arr
};
unsafe {
let mut hkey = HKEY::default();
let result = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
windows::core::PCWSTR(PATH.as_ptr()),
0,
KEY_READ,
&mut hkey,
);
if result.0 != 0 {
return;
}
let value_names: &[&[u16]] = &[
&encode_wide_const::<30>(b"TimeZoneKeyName\0"),
&encode_wide_const::<20>(b"StandardName\0"),
];
for _ in 0..params.call_depth {
for name in value_names {
read_string_value(hkey, name);
}
}
let _ = RegCloseKey(hkey);
}
}
const fn encode_wide_const<const N: usize>(bytes: &[u8]) -> [u16; N] {
let mut arr = [0u16; N];
let mut i = 0;
while i < bytes.len() && i < N {
arr[i] = bytes[i] as u16;
i += 1;
}
arr
}
+109
View File
@@ -0,0 +1,109 @@
use crate::categories::Categories;
use crate::tasks::{TaskDescriptor, TaskParams};
use rand::rngs::ThreadRng;
use std::hint::black_box;
use windows::Win32::Foundation::*;
use windows::Win32::System::Diagnostics::ToolHelp::*;
use windows::Win32::System::SystemInformation::*;
use windows::Win32::System::DataExchange::*;
use windows::Win32::UI::WindowsAndMessaging::*;
pub fn register() -> Vec<TaskDescriptor> {
vec![
TaskDescriptor {
name: "enumerate_windows",
category: Categories::WINAPI,
func: enumerate_windows,
},
TaskDescriptor {
name: "enumerate_processes",
category: Categories::WINAPI,
func: enumerate_processes,
},
TaskDescriptor {
name: "query_system_info",
category: Categories::WINAPI,
func: query_system_info,
},
TaskDescriptor {
name: "read_clipboard",
category: Categories::WINAPI,
func: read_clipboard,
},
]
}
unsafe extern "system" fn enum_window_callback(hwnd: HWND, lparam: LPARAM) -> BOOL {
let windows = &mut *(lparam.0 as *mut Vec<HWND>);
windows.push(hwnd);
BOOL(1)
}
fn enumerate_windows(params: &TaskParams, _rng: &mut ThreadRng) {
unsafe {
let mut windows: Vec<HWND> = Vec::new();
let ptr = &mut windows as *mut Vec<HWND> as isize;
let _ = EnumWindows(Some(enum_window_callback), LPARAM(ptr));
for hwnd in windows.iter().take(params.iterations) {
let mut text = [0u16; 256];
GetWindowTextW(*hwnd, &mut text);
black_box(&text);
}
}
}
fn enumerate_processes(params: &TaskParams, _rng: &mut ThreadRng) {
unsafe {
let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
let snapshot = match snapshot {
Ok(h) => h,
Err(_) => return,
};
let mut entry = PROCESSENTRY32W {
dwSize: std::mem::size_of::<PROCESSENTRY32W>() as u32,
..Default::default()
};
if Process32FirstW(snapshot, &mut entry).is_ok() {
for _ in 0..params.iterations {
black_box(entry.th32ProcessID);
black_box(&entry.szExeFile);
if Process32NextW(snapshot, &mut entry).is_err() {
break;
}
}
}
let _ = CloseHandle(snapshot);
}
}
fn query_system_info(params: &TaskParams, _rng: &mut ThreadRng) {
unsafe {
for _ in 0..params.call_depth {
let mut sys_info = SYSTEM_INFO::default();
GetSystemInfo(&mut sys_info);
black_box(sys_info.dwNumberOfProcessors);
let mut mem_status = MEMORYSTATUSEX {
dwLength: std::mem::size_of::<MEMORYSTATUSEX>() as u32,
..Default::default()
};
let _ = GlobalMemoryStatusEx(&mut mem_status);
black_box(mem_status.ullTotalPhys);
}
}
}
fn read_clipboard(params: &TaskParams, _rng: &mut ThreadRng) {
unsafe {
for _ in 0..params.call_depth {
if OpenClipboard(HWND::default()).is_ok() {
let _ = black_box(GetClipboardData(1)); // CF_TEXT
let _ = CloseClipboard();
}
}
}
}
+169
View File
@@ -0,0 +1,169 @@
use busywork::{busywork, busywork_with, BusyWork, Categories, Intensity};
use std::time::Instant;
fn measure<F: FnOnce()>(f: F) -> f64 {
let start = Instant::now();
f();
start.elapsed().as_secs_f64() * 1000.0
}
fn avg_ms<F: Fn()>(runs: usize, f: F) -> (f64, f64, f64) {
let mut times = Vec::with_capacity(runs);
for _ in 0..runs {
times.push(measure(|| f()));
}
times.sort_by(|a, b| a.partial_cmp(b).unwrap());
let min = times[0];
let max = times[times.len() - 1];
let avg = times.iter().sum::<f64>() / times.len() as f64;
(min, avg, max)
}
#[test]
fn bench_intensities() {
let runs = 5;
println!("\n{:=<70}", "");
println!(" INTENSITY BENCHMARKS ({} runs each, all categories)", runs);
println!("{:=<70}", "");
println!(
" {:10} {:>12} {:>12} {:>12}",
"Level", "Min (ms)", "Avg (ms)", "Max (ms)"
);
println!("{:-<70}", "");
for (name, intensity) in [
("Low", Intensity::Low),
("Medium", Intensity::Medium),
("High", Intensity::High),
("Ultra", Intensity::Ultra),
] {
let (min, avg, max) = avg_ms(runs, || busywork(intensity));
println!(
" {:10} {:>12.2} {:>12.2} {:>12.2}",
name, min, avg, max
);
}
println!("{:=<70}\n", "");
}
#[test]
fn bench_categories_medium() {
let runs = 5;
println!("\n{:=<70}", "");
println!(
" CATEGORY BENCHMARKS @ Medium ({} runs each, single category)",
runs
);
println!("{:=<70}", "");
println!(
" {:14} {:>12} {:>12} {:>12}",
"Category", "Min (ms)", "Avg (ms)", "Max (ms)"
);
println!("{:-<70}", "");
let cats: &[(&str, Categories)] = &[
("COMPUTE", Categories::COMPUTE),
("MEMORY", Categories::MEMORY),
("FILESYSTEM", Categories::FILESYSTEM),
("REGISTRY", Categories::REGISTRY),
("WINAPI", Categories::WINAPI),
("NETWORK", Categories::NETWORK),
("CRYPTO", Categories::CRYPTO),
];
for &(name, cat) in cats {
let (min, avg, max) = avg_ms(runs, || busywork_with(Intensity::Medium, cat));
println!(
" {:14} {:>12.2} {:>12.2} {:>12.2}",
name, min, avg, max
);
}
println!("{:=<70}\n", "");
}
#[test]
fn bench_categories_high() {
let runs = 3;
println!("\n{:=<70}", "");
println!(
" CATEGORY BENCHMARKS @ High ({} runs each, single category)",
runs
);
println!("{:=<70}", "");
println!(
" {:14} {:>12} {:>12} {:>12}",
"Category", "Min (ms)", "Avg (ms)", "Max (ms)"
);
println!("{:-<70}", "");
let cats: &[(&str, Categories)] = &[
("COMPUTE", Categories::COMPUTE),
("MEMORY", Categories::MEMORY),
("FILESYSTEM", Categories::FILESYSTEM),
("REGISTRY", Categories::REGISTRY),
("WINAPI", Categories::WINAPI),
("NETWORK", Categories::NETWORK),
("CRYPTO", Categories::CRYPTO),
];
for &(name, cat) in cats {
let (min, avg, max) = avg_ms(runs, || busywork_with(Intensity::High, cat));
println!(
" {:14} {:>12.2} {:>12.2} {:>12.2}",
name, min, avg, max
);
}
println!("{:=<70}\n", "");
}
#[test]
fn bench_jitter_variance() {
let runs = 10;
println!("\n{:=<70}", "");
println!(" JITTER VARIANCE (Medium, COMPUTE only, {} runs)", runs);
println!("{:=<70}", "");
let mut with_jitter = Vec::new();
let mut without_jitter = Vec::new();
for _ in 0..runs {
with_jitter.push(measure(|| {
BusyWork::new(Intensity::Medium)
.allow(Categories::COMPUTE)
.jitter(true)
.run();
}));
without_jitter.push(measure(|| {
BusyWork::new(Intensity::Medium)
.allow(Categories::COMPUTE)
.jitter(false)
.run();
}));
}
let variance = |times: &[f64]| -> f64 {
let avg = times.iter().sum::<f64>() / times.len() as f64;
let var = times.iter().map(|t| (t - avg).powi(2)).sum::<f64>() / times.len() as f64;
var.sqrt()
};
let jitter_avg = with_jitter.iter().sum::<f64>() / runs as f64;
let no_jitter_avg = without_jitter.iter().sum::<f64>() / runs as f64;
let jitter_std = variance(&with_jitter);
let no_jitter_std = variance(&without_jitter);
println!(
" {:20} {:>10} {:>10}",
"", "Avg (ms)", "StdDev"
);
println!("{:-<70}", "");
println!(
" {:20} {:>10.2} {:>10.2}",
"Jitter ON", jitter_avg, jitter_std
);
println!(
" {:20} {:>10.2} {:>10.2}",
"Jitter OFF", no_jitter_avg, no_jitter_std
);
println!("{:=<70}\n", "");
}
+54
View File
@@ -0,0 +1,54 @@
use busywork::{busywork, busywork_with, BusyWork, Categories, Intensity};
#[test]
fn low_intensity_no_panic() {
busywork(Intensity::Low);
}
#[test]
fn medium_intensity_no_panic() {
busywork(Intensity::Medium);
}
#[test]
fn high_compute_only() {
busywork_with(Intensity::High, Categories::COMPUTE);
}
#[test]
fn high_memory_only() {
busywork_with(Intensity::High, Categories::MEMORY);
}
#[test]
fn high_filesystem_only() {
busywork_with(Intensity::High, Categories::FILESYSTEM);
}
#[test]
fn builder_deny_network() {
BusyWork::new(Intensity::Medium)
.deny(Categories::NETWORK)
.run();
}
#[test]
fn builder_no_jitter() {
BusyWork::new(Intensity::Low)
.allow(Categories::COMPUTE | Categories::MEMORY)
.jitter(false)
.run();
}
#[test]
fn empty_categories_no_panic() {
busywork_with(Intensity::Ultra, Categories::empty());
}
#[test]
fn available_returns_all_compiled() {
let avail = Categories::available();
assert!(avail.contains(Categories::COMPUTE));
assert!(avail.contains(Categories::MEMORY));
assert!(avail.contains(Categories::FILESYSTEM));
}