1// Copyright 2019 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package prometheus
15
16import (
17	"syscall"
18	"unsafe"
19
20	"golang.org/x/sys/windows"
21)
22
23func canCollectProcess() bool {
24	return true
25}
26
27var (
28	modpsapi    = syscall.NewLazyDLL("psapi.dll")
29	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
30
31	procGetProcessMemoryInfo  = modpsapi.NewProc("GetProcessMemoryInfo")
32	procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount")
33)
34
35type processMemoryCounters struct {
36	// System interface description
37	// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/ns-psapi-process_memory_counters_ex
38
39	// Refer to the Golang internal implementation
40	// https://golang.org/src/internal/syscall/windows/psapi_windows.go
41	_                          uint32
42	PageFaultCount             uint32
43	PeakWorkingSetSize         uintptr
44	WorkingSetSize             uintptr
45	QuotaPeakPagedPoolUsage    uintptr
46	QuotaPagedPoolUsage        uintptr
47	QuotaPeakNonPagedPoolUsage uintptr
48	QuotaNonPagedPoolUsage     uintptr
49	PagefileUsage              uintptr
50	PeakPagefileUsage          uintptr
51	PrivateUsage               uintptr
52}
53
54func getProcessMemoryInfo(handle windows.Handle) (processMemoryCounters, error) {
55	mem := processMemoryCounters{}
56	r1, _, err := procGetProcessMemoryInfo.Call(
57		uintptr(handle),
58		uintptr(unsafe.Pointer(&mem)),
59		uintptr(unsafe.Sizeof(mem)),
60	)
61	if r1 != 1 {
62		return mem, err
63	} else {
64		return mem, nil
65	}
66}
67
68func getProcessHandleCount(handle windows.Handle) (uint32, error) {
69	var count uint32
70	r1, _, err := procGetProcessHandleCount.Call(
71		uintptr(handle),
72		uintptr(unsafe.Pointer(&count)),
73	)
74	if r1 != 1 {
75		return 0, err
76	} else {
77		return count, nil
78	}
79}
80
81func (c *processCollector) processCollect(ch chan<- Metric) {
82	h, err := windows.GetCurrentProcess()
83	if err != nil {
84		c.reportError(ch, nil, err)
85		return
86	}
87
88	var startTime, exitTime, kernelTime, userTime windows.Filetime
89	err = windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime)
90	if err != nil {
91		c.reportError(ch, nil, err)
92		return
93	}
94	ch <- MustNewConstMetric(c.startTime, GaugeValue, float64(startTime.Nanoseconds()/1e9))
95	ch <- MustNewConstMetric(c.cpuTotal, CounterValue, fileTimeToSeconds(kernelTime)+fileTimeToSeconds(userTime))
96
97	mem, err := getProcessMemoryInfo(h)
98	if err != nil {
99		c.reportError(ch, nil, err)
100		return
101	}
102	ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(mem.PrivateUsage))
103	ch <- MustNewConstMetric(c.rss, GaugeValue, float64(mem.WorkingSetSize))
104
105	handles, err := getProcessHandleCount(h)
106	if err != nil {
107		c.reportError(ch, nil, err)
108		return
109	}
110	ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(handles))
111	ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(16*1024*1024)) // Windows has a hard-coded max limit, not per-process.
112}
113
114func fileTimeToSeconds(ft windows.Filetime) float64 {
115	return float64(uint64(ft.HighDateTime)<<32+uint64(ft.LowDateTime)) / 1e7
116}
117