-
-
Notifications
You must be signed in to change notification settings - Fork 354
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix user and kernel times on Windows
- Loading branch information
1 parent
2e8ce30
commit 97116dc
Showing
4 changed files
with
115 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
#![cfg(windows)] | ||
#![warn(unsafe_op_in_unsafe_fn)] | ||
|
||
use std::{ | ||
mem, | ||
os::windows::{io::AsRawHandle, process::ChildExt}, | ||
process, ptr, | ||
}; | ||
|
||
use winapi::um::{ | ||
handleapi::CloseHandle, | ||
jobapi2::{AssignProcessToJobObject, CreateJobObjectW, QueryInformationJobObject}, | ||
processthreadsapi::ResumeThread, | ||
winnt::{JobObjectBasicAccountingInformation, HANDLE, JOBOBJECT_BASIC_ACCOUNTING_INFORMATION}, | ||
}; | ||
|
||
use crate::util::units::Second; | ||
|
||
const HUNDRED_NS_PER_MS: i64 = 10; | ||
|
||
pub struct CPUTimer { | ||
job_object: HANDLE, | ||
} | ||
|
||
impl CPUTimer { | ||
pub unsafe fn start_suspended_process(child: &process::Child) -> Self { | ||
// SAFETY: Creating a new job object is safe | ||
let job_object = unsafe { CreateJobObjectW(ptr::null_mut(), ptr::null_mut()) }; | ||
assert!(!job_object.is_null(), "CreateJobObjectW failed"); | ||
|
||
// SAFETY: The job object handle is valid | ||
let ret = unsafe { AssignProcessToJobObject(job_object, child.as_raw_handle()) }; | ||
assert!(ret != 0, "AssignProcessToJobObject failed"); | ||
|
||
// SAFETY: The main thread handle is valid | ||
unsafe { ResumeThread(child.main_thread_handle().as_raw_handle()) }; | ||
|
||
Self { job_object } | ||
} | ||
|
||
pub fn stop(&self) -> (Second, Second) { | ||
let mut job_object_info: JOBOBJECT_BASIC_ACCOUNTING_INFORMATION = unsafe { mem::zeroed() }; | ||
|
||
// SAFETY: A valid job object got created in `start_suspended_process` | ||
let res = unsafe { | ||
QueryInformationJobObject( | ||
self.job_object, | ||
JobObjectBasicAccountingInformation, | ||
ptr::addr_of_mut!(job_object_info).cast(), | ||
mem::size_of_val(&job_object_info) as u32, | ||
ptr::null_mut(), | ||
) | ||
}; | ||
|
||
assert!( | ||
job_object_info.ActiveProcesses == 0, | ||
"There shouldn't be active child processes left after the benchmarked process exited" | ||
); | ||
|
||
if res != 0 { | ||
// SAFETY: The `TotalUserTime` is "The total amount of user-mode execution time for | ||
// all active processes associated with the job, as well as all terminated processes no | ||
// longer associated with the job, in 100-nanosecond ticks." and is safe to extract | ||
let user: i64 = unsafe { job_object_info.TotalUserTime.QuadPart() } / HUNDRED_NS_PER_MS; | ||
|
||
// SAFETY: The `TotalKernelTime` is "The total amount of kernel-mode execution time | ||
// for all active processes associated with the job, as well as all terminated | ||
// processes no longer associated with the job, in 100-nanosecond ticks." and is safe | ||
// to extract | ||
let kernel: i64 = | ||
unsafe { job_object_info.TotalKernelTime.QuadPart() } / HUNDRED_NS_PER_MS; | ||
(user as f64 * 1e-6, kernel as f64 * 1e-6) | ||
} else { | ||
(0.0, 0.0) | ||
} | ||
} | ||
} | ||
|
||
impl Drop for CPUTimer { | ||
fn drop(self: &mut Self) { | ||
// SAFETY: A valid job object got created in `start_suspended_process` | ||
unsafe { CloseHandle(self.job_object) }; | ||
} | ||
} |