metrics_process/implementation/windows.rs
1use std::mem::{size_of, MaybeUninit};
2use windows::{Win32::Foundation::*, Win32::System::ProcessStatus::*, Win32::System::Threading::*};
3
4use super::Metrics;
5
6/// Collect metrics.
7///
8/// Refer: / https://github.com/prometheus/client_golang/blob/c7aa2a5b843527449adb99ad113fe14ed15e4eb0/prometheus/process_collector_windows.go#L81-L116
9///
10// Copyright 2019 The Prometheus Authors
11// Licensed under the Apache License, Version 2.0 (the "License");
12// you may not use this file except in compliance with the License.
13// You may obtain a copy of the License at
14//
15// http://www.apache.org/licenses/LICENSE-2.0
16//
17// Unless required by applicable law or agreed to in writing, software
18// distributed under the License is distributed on an "AS IS" BASIS,
19// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20// See the License for the specific language governing permissions and
21// limitations under the License.
22pub fn collect() -> Metrics {
23 let mut metrics = Metrics::default();
24 unsafe {
25 let h = GetCurrentProcess();
26 let (start_time_seconds, cpu_seconds_total) = {
27 let mut creationtime = MaybeUninit::uninit();
28 let mut _exittime = MaybeUninit::uninit();
29 let mut kerneltime = MaybeUninit::uninit();
30 let mut usertime = MaybeUninit::uninit();
31 let ret = GetProcessTimes(
32 h,
33 creationtime.as_mut_ptr(),
34 _exittime.as_mut_ptr(),
35 kerneltime.as_mut_ptr(),
36 usertime.as_mut_ptr(),
37 );
38 if ret.is_ok() {
39 // `creationtime` and `_exittime` are points in time expressed as the amount of time that
40 // has elapsed since midnight on January 1, 1601 in 100 nanosecond time units.
41 let start_time_seconds =
42 filetime_to_unix_epoch_in_seconds(creationtime.assume_init());
43 // `kerneltime` and `usertime` are amounts of time in 100 nanosecond time units.
44 let cpu_seconds_total = {
45 let stime = filetime_to_seconds(kerneltime.assume_init());
46 let utime = filetime_to_seconds(usertime.assume_init());
47 stime + utime
48 };
49 (Some(start_time_seconds as u64), Some(cpu_seconds_total))
50 } else {
51 (None, None)
52 }
53 };
54 metrics.start_time_seconds = start_time_seconds;
55 metrics.cpu_seconds_total = cpu_seconds_total;
56
57 let (virtual_memory_bytes, resident_memory_bytes) = {
58 // We need to use PROCESS_MEMORY_COUNTERS_EX but GetProcessMemoryInfoEx is not provided
59 // thus we need to cast PROCESS_MEMORY_COUNTERS_EX into PROCESS_MEMORY_COUNTERS to use
60 // it with GetProcessMemoryInfo.
61 let memcounters = {
62 let m = &PROCESS_MEMORY_COUNTERS_EX::default();
63 m as *const _ as *mut PROCESS_MEMORY_COUNTERS
64 };
65 let cb = size_of::<PROCESS_MEMORY_COUNTERS_EX>();
66 let ret = GetProcessMemoryInfo(h, memcounters, cb as u32);
67 if ret.is_ok() {
68 let memcounters = memcounters as *const _ as *const PROCESS_MEMORY_COUNTERS_EX;
69 let &memcounters = &*memcounters;
70 (
71 Some(memcounters.PrivateUsage as u64),
72 Some(memcounters.WorkingSetSize as u64),
73 )
74 } else {
75 (None, None)
76 }
77 };
78 metrics.virtual_memory_bytes = virtual_memory_bytes;
79 metrics.resident_memory_bytes = resident_memory_bytes;
80
81 let open_fds = {
82 let mut handlecount = 0;
83 let ret = GetProcessHandleCount(h, &mut handlecount);
84 if ret.is_ok() {
85 Some(handlecount as u64)
86 } else {
87 None
88 }
89 };
90 metrics.open_fds = open_fds;
91 metrics.max_fds = Some(16 * 1024 * 1024); // Windows has a hard-coded max limit, not per-process.
92 }
93 metrics
94}
95
96/// Convert FILETIME to seconds.
97///
98/// FILETIME contains a 64-bit value representing the number of 100-nanosecond intervals.
99///
100/// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
101fn filetime_to_seconds(ft: FILETIME) -> f64 {
102 // 100-nanosecond intervals since January 1, 1601
103 let low = ft.dwLowDateTime as u64;
104 let high = ft.dwHighDateTime as u64;
105 let nsec = high.checked_shl(32).unwrap_or(0) + low;
106 // convert into nanoseconds
107 let nsec = nsec * 100;
108 // convert into seconds
109 nsec as f64 / 1e9
110}
111
112/// Convert FILETIME to Unix Epoch in seconds.
113///
114/// FILETIME contains a 64-bit value representing the number of 100-nanosecond intervals
115/// since January 1, 1601 (UTC).
116///
117/// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
118/// https://cs.opensource.google/go/x/sys/+/2296e014:windows/types_windows.go;l=792-800
119///
120// Copyright (c) 2009 The Go Authors. All rights reserved.
121//
122// Redistribution and use in source and binary forms, with or without
123// modification, are permitted provided that the following conditions are
124// met:
125//
126// * Redistributions of source code must retain the above copyright
127// notice, this list of conditions and the following disclaimer.
128// * Redistributions in binary form must reproduce the above
129// copyright notice, this list of conditions and the following disclaimer
130// in the documentation and/or other materials provided with the
131// distribution.
132// * Neither the name of Google Inc. nor the names of its
133// contributors may be used to endorse or promote products derived from
134// this software without specific prior written permission.
135//
136// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
137// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
138// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
139// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
140// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
141// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
142// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
143// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
144// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
145// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
146// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
147//
148fn filetime_to_unix_epoch_in_seconds(ft: FILETIME) -> f64 {
149 let low = ft.dwLowDateTime as u64;
150 let high = ft.dwHighDateTime as u64;
151 let nsec = high.checked_shl(32).unwrap_or(0) + low;
152 // change starting time to the epoch (00:00:00 UTC, January 1, 1970)
153 let nsec = nsec - 116444736000000000;
154 // convert into nanoseconds
155 let nsec = nsec * 100;
156 // convert into seconds
157 nsec as f64 / 1e9
158}