perf: batch MinHook enable calls for 10x faster test startup
Browse filesReplace per-hook MinHook::enable_hook() with queue_enable_hook(), then
call apply_queued() once after all hooks are installed. This batches
~90 VirtualProtect syscalls into a single pass per code page.
Results (17 tests):
j=4: ~35s → ~3s
j=16: ~25s → ~1.8s
The VirtualProtect calls were the entire startup bottleneck — each one
flushes the instruction cache and modifies page tables, and they were
serialized across all hooks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-wormkit/src/hook.rs
CHANGED
|
@@ -384,8 +384,8 @@ pub unsafe fn install(
|
|
| 384 |
let trampoline = MinHook::create_hook(target, detour)
|
| 385 |
.map_err(|e| format!("MinHook create_hook failed for {name}: {e}"))?;
|
| 386 |
|
| 387 |
-
MinHook::
|
| 388 |
-
.map_err(|e| format!("MinHook
|
| 389 |
|
| 390 |
let _ = log_line(&format!(
|
| 391 |
" [REPLACE] {name}: target 0x{:08X}, trampoline 0x{:08X}",
|
|
|
|
| 384 |
let trampoline = MinHook::create_hook(target, detour)
|
| 385 |
.map_err(|e| format!("MinHook create_hook failed for {name}: {e}"))?;
|
| 386 |
|
| 387 |
+
MinHook::queue_enable_hook(target)
|
| 388 |
+
.map_err(|e| format!("MinHook queue_enable_hook failed for {name}: {e}"))?;
|
| 389 |
|
| 390 |
let _ = log_line(&format!(
|
| 391 |
" [REPLACE] {name}: target 0x{:08X}, trampoline 0x{:08X}",
|
crates/openwa-wormkit/src/lib.rs
CHANGED
|
@@ -82,6 +82,13 @@ fn run() -> Result<(), String> {
|
|
| 82 |
|
| 83 |
replacements::install_all()?;
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
let _ = log_line("=== All replacements installed ===");
|
| 86 |
|
| 87 |
// Signal the launcher that all hooks are installed and the main thread
|
|
|
|
| 82 |
|
| 83 |
replacements::install_all()?;
|
| 84 |
|
| 85 |
+
// All hooks were queued during install_all() — now enable them in one
|
| 86 |
+
// batched VirtualProtect pass instead of one syscall per hook.
|
| 87 |
+
unsafe {
|
| 88 |
+
minhook::MinHook::apply_queued()
|
| 89 |
+
.map_err(|e| format!("MinHook apply_queued failed: {e}"))?;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
let _ = log_line("=== All replacements installed ===");
|
| 93 |
|
| 94 |
// Signal the launcher that all hooks are installed and the main thread
|
crates/openwa-wormkit/src/replacements/file_isolation.rs
CHANGED
|
@@ -163,8 +163,8 @@ pub fn install() -> Result<(), String> {
|
|
| 163 |
let target = addr as *mut c_void;
|
| 164 |
let trampoline = minhook::MinHook::create_hook(target, hook_create_file_a as *mut c_void)
|
| 165 |
.map_err(|e| format!("MinHook create_hook failed for CreateFileA: {e}"))?;
|
| 166 |
-
minhook::MinHook::
|
| 167 |
-
.map_err(|e| format!("MinHook
|
| 168 |
|
| 169 |
ORIG_CREATE_FILE_A.store(trampoline as u32, Ordering::Relaxed);
|
| 170 |
|
|
|
|
| 163 |
let target = addr as *mut c_void;
|
| 164 |
let trampoline = minhook::MinHook::create_hook(target, hook_create_file_a as *mut c_void)
|
| 165 |
.map_err(|e| format!("MinHook create_hook failed for CreateFileA: {e}"))?;
|
| 166 |
+
minhook::MinHook::queue_enable_hook(target)
|
| 167 |
+
.map_err(|e| format!("MinHook queue_enable_hook failed for CreateFileA: {e}"))?;
|
| 168 |
|
| 169 |
ORIG_CREATE_FILE_A.store(trampoline as u32, Ordering::Relaxed);
|
| 170 |
|
crates/openwa-wormkit/src/replacements/headless.rs
CHANGED
|
@@ -99,7 +99,7 @@ pub fn install() -> Result<(), String> {
|
|
| 99 |
if let Some(addr) = proc {
|
| 100 |
let target = addr as *mut core::ffi::c_void;
|
| 101 |
if let Ok(trampoline) = minhook::MinHook::create_hook(target, hook_fn) {
|
| 102 |
-
let _ = minhook::MinHook::
|
| 103 |
let fn_name = std::str::from_utf8(&name[..name.len() - 1]).unwrap_or("?");
|
| 104 |
let _ = log_line(&format!(
|
| 105 |
"[Headless] user32!{fn_name} hooked at 0x{:08X}, trampoline 0x{:08X}",
|
|
@@ -130,7 +130,7 @@ pub fn install() -> Result<(), String> {
|
|
| 130 |
target,
|
| 131 |
hook_create_semaphore_a as *mut core::ffi::c_void,
|
| 132 |
) {
|
| 133 |
-
let _ = minhook::MinHook::
|
| 134 |
ORIG_CREATE_SEMAPHORE_A
|
| 135 |
.store(trampoline as u32, core::sync::atomic::Ordering::Relaxed);
|
| 136 |
let _ = log_line(&format!(
|
|
|
|
| 99 |
if let Some(addr) = proc {
|
| 100 |
let target = addr as *mut core::ffi::c_void;
|
| 101 |
if let Ok(trampoline) = minhook::MinHook::create_hook(target, hook_fn) {
|
| 102 |
+
let _ = minhook::MinHook::queue_enable_hook(target);
|
| 103 |
let fn_name = std::str::from_utf8(&name[..name.len() - 1]).unwrap_or("?");
|
| 104 |
let _ = log_line(&format!(
|
| 105 |
"[Headless] user32!{fn_name} hooked at 0x{:08X}, trampoline 0x{:08X}",
|
|
|
|
| 130 |
target,
|
| 131 |
hook_create_semaphore_a as *mut core::ffi::c_void,
|
| 132 |
) {
|
| 133 |
+
let _ = minhook::MinHook::queue_enable_hook(target);
|
| 134 |
ORIG_CREATE_SEMAPHORE_A
|
| 135 |
.store(trampoline as u32, core::sync::atomic::Ordering::Relaxed);
|
| 136 |
let _ = log_line(&format!(
|