mirror of
https://github.com/PatchRequest/BusyWork
synced 2026-06-09 16:04:04 +00:00
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:
@@ -0,0 +1 @@
|
||||
/target
|
||||
Generated
+394
@@ -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
@@ -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",
|
||||
]
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)(¶ms, &mut rng);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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", "");
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user