go-tg-screenshot-bot
437 строк · 19.4 Кб
1// Copyright 2013 The win Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build windows
6
7package win
8
9import (
10"golang.org/x/sys/windows"
11"syscall"
12"unsafe"
13)
14
15// PDH error codes, which can be returned by all Pdh* functions. Taken from mingw-w64 pdhmsg.h
16const (
17PDH_CSTATUS_VALID_DATA = 0x00000000 // The returned data is valid.
18PDH_CSTATUS_NEW_DATA = 0x00000001 // The return data value is valid and different from the last sample.
19PDH_CSTATUS_NO_MACHINE = 0x800007D0 // Unable to connect to the specified computer, or the computer is offline.
20PDH_CSTATUS_NO_INSTANCE = 0x800007D1
21PDH_MORE_DATA = 0x800007D2 // The PdhGetFormattedCounterArray* function can return this if there's 'more data to be displayed'.
22PDH_CSTATUS_ITEM_NOT_VALIDATED = 0x800007D3
23PDH_RETRY = 0x800007D4
24PDH_NO_DATA = 0x800007D5 // The query does not currently contain any counters (for example, limited access)
25PDH_CALC_NEGATIVE_DENOMINATOR = 0x800007D6
26PDH_CALC_NEGATIVE_TIMEBASE = 0x800007D7
27PDH_CALC_NEGATIVE_VALUE = 0x800007D8
28PDH_DIALOG_CANCELLED = 0x800007D9
29PDH_END_OF_LOG_FILE = 0x800007DA
30PDH_ASYNC_QUERY_TIMEOUT = 0x800007DB
31PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE = 0x800007DC
32PDH_CSTATUS_NO_OBJECT = 0xC0000BB8
33PDH_CSTATUS_NO_COUNTER = 0xC0000BB9 // The specified counter could not be found.
34PDH_CSTATUS_INVALID_DATA = 0xC0000BBA // The counter was successfully found, but the data returned is not valid.
35PDH_MEMORY_ALLOCATION_FAILURE = 0xC0000BBB
36PDH_INVALID_HANDLE = 0xC0000BBC
37PDH_INVALID_ARGUMENT = 0xC0000BBD // Required argument is missing or incorrect.
38PDH_FUNCTION_NOT_FOUND = 0xC0000BBE
39PDH_CSTATUS_NO_COUNTERNAME = 0xC0000BBF
40PDH_CSTATUS_BAD_COUNTERNAME = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path.
41PDH_INVALID_BUFFER = 0xC0000BC1
42PDH_INSUFFICIENT_BUFFER = 0xC0000BC2
43PDH_CANNOT_CONNECT_MACHINE = 0xC0000BC3
44PDH_INVALID_PATH = 0xC0000BC4
45PDH_INVALID_INSTANCE = 0xC0000BC5
46PDH_INVALID_DATA = 0xC0000BC6 // specified counter does not contain valid data or a successful status code.
47PDH_NO_DIALOG_DATA = 0xC0000BC7
48PDH_CANNOT_READ_NAME_STRINGS = 0xC0000BC8
49PDH_LOG_FILE_CREATE_ERROR = 0xC0000BC9
50PDH_LOG_FILE_OPEN_ERROR = 0xC0000BCA
51PDH_LOG_TYPE_NOT_FOUND = 0xC0000BCB
52PDH_NO_MORE_DATA = 0xC0000BCC
53PDH_ENTRY_NOT_IN_LOG_FILE = 0xC0000BCD
54PDH_DATA_SOURCE_IS_LOG_FILE = 0xC0000BCE
55PDH_DATA_SOURCE_IS_REAL_TIME = 0xC0000BCF
56PDH_UNABLE_READ_LOG_HEADER = 0xC0000BD0
57PDH_FILE_NOT_FOUND = 0xC0000BD1
58PDH_FILE_ALREADY_EXISTS = 0xC0000BD2
59PDH_NOT_IMPLEMENTED = 0xC0000BD3
60PDH_STRING_NOT_FOUND = 0xC0000BD4
61PDH_UNABLE_MAP_NAME_FILES = 0x80000BD5
62PDH_UNKNOWN_LOG_FORMAT = 0xC0000BD6
63PDH_UNKNOWN_LOGSVC_COMMAND = 0xC0000BD7
64PDH_LOGSVC_QUERY_NOT_FOUND = 0xC0000BD8
65PDH_LOGSVC_NOT_OPENED = 0xC0000BD9
66PDH_WBEM_ERROR = 0xC0000BDA
67PDH_ACCESS_DENIED = 0xC0000BDB
68PDH_LOG_FILE_TOO_SMALL = 0xC0000BDC
69PDH_INVALID_DATASOURCE = 0xC0000BDD
70PDH_INVALID_SQLDB = 0xC0000BDE
71PDH_NO_COUNTERS = 0xC0000BDF
72PDH_SQL_ALLOC_FAILED = 0xC0000BE0
73PDH_SQL_ALLOCCON_FAILED = 0xC0000BE1
74PDH_SQL_EXEC_DIRECT_FAILED = 0xC0000BE2
75PDH_SQL_FETCH_FAILED = 0xC0000BE3
76PDH_SQL_ROWCOUNT_FAILED = 0xC0000BE4
77PDH_SQL_MORE_RESULTS_FAILED = 0xC0000BE5
78PDH_SQL_CONNECT_FAILED = 0xC0000BE6
79PDH_SQL_BIND_FAILED = 0xC0000BE7
80PDH_CANNOT_CONNECT_WMI_SERVER = 0xC0000BE8
81PDH_PLA_COLLECTION_ALREADY_RUNNING = 0xC0000BE9
82PDH_PLA_ERROR_SCHEDULE_OVERLAP = 0xC0000BEA
83PDH_PLA_COLLECTION_NOT_FOUND = 0xC0000BEB
84PDH_PLA_ERROR_SCHEDULE_ELAPSED = 0xC0000BEC
85PDH_PLA_ERROR_NOSTART = 0xC0000BED
86PDH_PLA_ERROR_ALREADY_EXISTS = 0xC0000BEE
87PDH_PLA_ERROR_TYPE_MISMATCH = 0xC0000BEF
88PDH_PLA_ERROR_FILEPATH = 0xC0000BF0
89PDH_PLA_SERVICE_ERROR = 0xC0000BF1
90PDH_PLA_VALIDATION_ERROR = 0xC0000BF2
91PDH_PLA_VALIDATION_WARNING = 0x80000BF3
92PDH_PLA_ERROR_NAME_TOO_LONG = 0xC0000BF4
93PDH_INVALID_SQL_LOG_FORMAT = 0xC0000BF5
94PDH_COUNTER_ALREADY_IN_QUERY = 0xC0000BF6
95PDH_BINARY_LOG_CORRUPT = 0xC0000BF7
96PDH_LOG_SAMPLE_TOO_SMALL = 0xC0000BF8
97PDH_OS_LATER_VERSION = 0xC0000BF9
98PDH_OS_EARLIER_VERSION = 0xC0000BFA
99PDH_INCORRECT_APPEND_TIME = 0xC0000BFB
100PDH_UNMATCHED_APPEND_COUNTER = 0xC0000BFC
101PDH_SQL_ALTER_DETAIL_FAILED = 0xC0000BFD
102PDH_QUERY_PERF_DATA_TIMEOUT = 0xC0000BFE
103)
104
105// Formatting options for GetFormattedCounterValue().
106const (
107PDH_FMT_RAW = 0x00000010
108PDH_FMT_ANSI = 0x00000020
109PDH_FMT_UNICODE = 0x00000040
110PDH_FMT_LONG = 0x00000100 // Return data as a long int.
111PDH_FMT_DOUBLE = 0x00000200 // Return data as a double precision floating point real.
112PDH_FMT_LARGE = 0x00000400 // Return data as a 64 bit integer.
113PDH_FMT_NOSCALE = 0x00001000 // can be OR-ed: Do not apply the counter's default scaling factor.
114PDH_FMT_1000 = 0x00002000 // can be OR-ed: multiply the actual value by 1,000.
115PDH_FMT_NODATA = 0x00004000 // can be OR-ed: unknown what this is for, MSDN says nothing.
116PDH_FMT_NOCAP100 = 0x00008000 // can be OR-ed: do not cap values > 100.
117PERF_DETAIL_COSTLY = 0x00010000
118PERF_DETAIL_STANDARD = 0x0000FFFF
119)
120
121type (
122PDH_HQUERY HANDLE // query handle
123PDH_HCOUNTER HANDLE // counter handle
124)
125
126// Union specialization for double values
127type PDH_FMT_COUNTERVALUE_DOUBLE struct {
128CStatus uint32
129DoubleValue float64
130}
131
132// Union specialization for 64 bit integer values
133type PDH_FMT_COUNTERVALUE_LARGE struct {
134CStatus uint32
135LargeValue int64
136}
137
138// Union specialization for long values
139type PDH_FMT_COUNTERVALUE_LONG struct {
140CStatus uint32
141LongValue int32
142padding [4]byte
143}
144
145// Union specialization for double values, used by PdhGetFormattedCounterArrayDouble()
146type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct {
147SzName *uint16 // pointer to a string
148FmtValue PDH_FMT_COUNTERVALUE_DOUBLE
149}
150
151// Union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge()
152type PDH_FMT_COUNTERVALUE_ITEM_LARGE struct {
153SzName *uint16 // pointer to a string
154FmtValue PDH_FMT_COUNTERVALUE_LARGE
155}
156
157// Union specialization for long values, used by PdhGetFormattedCounterArrayLong()
158type PDH_FMT_COUNTERVALUE_ITEM_LONG struct {
159SzName *uint16 // pointer to a string
160FmtValue PDH_FMT_COUNTERVALUE_LONG
161}
162
163var (
164// Library
165libpdhDll *windows.LazyDLL
166
167// Functions
168pdh_AddCounterW *windows.LazyProc
169pdh_AddEnglishCounterW *windows.LazyProc
170pdh_CloseQuery *windows.LazyProc
171pdh_CollectQueryData *windows.LazyProc
172pdh_GetFormattedCounterValue *windows.LazyProc
173pdh_GetFormattedCounterArrayW *windows.LazyProc
174pdh_OpenQuery *windows.LazyProc
175pdh_ValidatePathW *windows.LazyProc
176)
177
178func init() {
179// Library
180libpdhDll = windows.NewLazySystemDLL("pdh.dll")
181
182// Functions
183pdh_AddCounterW = libpdhDll.NewProc("PdhAddCounterW")
184pdh_AddEnglishCounterW = libpdhDll.NewProc("PdhAddEnglishCounterW")
185pdh_CloseQuery = libpdhDll.NewProc("PdhCloseQuery")
186pdh_CollectQueryData = libpdhDll.NewProc("PdhCollectQueryData")
187pdh_GetFormattedCounterValue = libpdhDll.NewProc("PdhGetFormattedCounterValue")
188pdh_GetFormattedCounterArrayW = libpdhDll.NewProc("PdhGetFormattedCounterArrayW")
189pdh_OpenQuery = libpdhDll.NewProc("PdhOpenQuery")
190pdh_ValidatePathW = libpdhDll.NewProc("PdhValidatePathW")
191}
192
193// Adds the specified counter to the query. This is the internationalized version. Preferably, use the
194// function PdhAddEnglishCounter instead. hQuery is the query handle, which has been fetched by PdhOpenQuery.
195// szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version).
196// dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value
197// later, call PdhGetCounterInfo() and access dwQueryUserData of the PDH_COUNTER_INFO structure.
198//
199// Examples of szFullCounterPath (in an English version of Windows):
200//
201// \\Processor(_Total)\\% Idle Time
202// \\Processor(_Total)\\% Processor Time
203// \\LogicalDisk(C:)\% Free Space
204//
205// To view all (internationalized...) counters on a system, there are three non-programmatic ways: perfmon utility,
206// the typeperf command, and the the registry editor. perfmon.exe is perhaps the easiest way, because it's basically a
207// full implemention of the pdh.dll API, except with a GUI and all that. The registry setting also provides an
208// interface to the available counters, and can be found at the following key:
209//
210// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage
211//
212// This registry key contains several values as follows:
213//
214// 1
215// 1847
216// 2
217// System
218// 4
219// Memory
220// 6
221// % Processor Time
222// ... many, many more
223//
224// Somehow, these numeric values can be used as szFullCounterPath too:
225//
226// \2\6 will correspond to \\System\% Processor Time
227//
228// The typeperf command may also be pretty easy. To find all performance counters, simply execute:
229//
230// typeperf -qx
231func PdhAddCounter(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 {
232ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
233ret, _, _ := pdh_AddCounterW.Call(
234uintptr(hQuery),
235uintptr(unsafe.Pointer(ptxt)),
236dwUserData,
237uintptr(unsafe.Pointer(phCounter)))
238
239return uint32(ret)
240}
241
242// Adds the specified language-neutral counter to the query. See the PdhAddCounter function. This function only exists on
243// Windows versions higher than Vista.
244func PdhAddEnglishCounter(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) uint32 {
245if pdh_AddEnglishCounterW.Find() != nil {
246return ERROR_INVALID_FUNCTION
247}
248
249ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
250ret, _, _ := pdh_AddEnglishCounterW.Call(
251uintptr(hQuery),
252uintptr(unsafe.Pointer(ptxt)),
253dwUserData,
254uintptr(unsafe.Pointer(phCounter)))
255
256return uint32(ret)
257}
258
259// Closes all counters contained in the specified query, closes all handles related to the query,
260// and frees all memory associated with the query.
261func PdhCloseQuery(hQuery PDH_HQUERY) uint32 {
262ret, _, _ := pdh_CloseQuery.Call(uintptr(hQuery))
263
264return uint32(ret)
265}
266
267// Collects the current raw data value for all counters in the specified query and updates the status
268// code of each counter. With some counters, this function needs to be repeatedly called before the value
269// of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code
270// requires at least two calls:
271//
272// var handle win.PDH_HQUERY
273// var counterHandle win.PDH_HCOUNTER
274// ret := win.PdhOpenQuery(0, 0, &handle)
275// ret = win.PdhAddEnglishCounter(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle)
276// var derp win.PDH_FMT_COUNTERVALUE_DOUBLE
277//
278// ret = win.PdhCollectQueryData(handle)
279// fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA
280// ret = win.PdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
281//
282// ret = win.PdhCollectQueryData(handle)
283// fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS
284// ret = win.PdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
285//
286// The PdhCollectQueryData will return an error in the first call because it needs two values for
287// displaying the correct data for the processor idle time. The second call will have a 0 return code.
288func PdhCollectQueryData(hQuery PDH_HQUERY) uint32 {
289ret, _, _ := pdh_CollectQueryData.Call(uintptr(hQuery))
290
291return uint32(ret)
292}
293
294// Formats the given hCounter using a 'double'. The result is set into the specialized union struct pValue.
295// This function does not directly translate to a Windows counterpart due to union specialization tricks.
296func PdhGetFormattedCounterValueDouble(hCounter PDH_HCOUNTER, lpdwType *uint32, pValue *PDH_FMT_COUNTERVALUE_DOUBLE) uint32 {
297ret, _, _ := pdh_GetFormattedCounterValue.Call(
298uintptr(hCounter),
299uintptr(PDH_FMT_DOUBLE),
300uintptr(unsafe.Pointer(lpdwType)),
301uintptr(unsafe.Pointer(pValue)))
302
303return uint32(ret)
304}
305
306// Formats the given hCounter using a large int (int64). The result is set into the specialized union struct pValue.
307// This function does not directly translate to a Windows counterpart due to union specialization tricks.
308func PdhGetFormattedCounterValueLarge(hCounter PDH_HCOUNTER, lpdwType *uint32, pValue *PDH_FMT_COUNTERVALUE_LARGE) uint32 {
309ret, _, _ := pdh_GetFormattedCounterValue.Call(
310uintptr(hCounter),
311uintptr(PDH_FMT_LARGE),
312uintptr(unsafe.Pointer(lpdwType)),
313uintptr(unsafe.Pointer(pValue)))
314
315return uint32(ret)
316}
317
318// Formats the given hCounter using a 'long'. The result is set into the specialized union struct pValue.
319// This function does not directly translate to a Windows counterpart due to union specialization tricks.
320//
321// BUG(krpors): Testing this function on multiple systems yielded inconsistent results. For instance,
322// the pValue.LongValue kept the value '192' on test system A, but on B this was '0', while the padding
323// bytes of the struct got the correct value. Until someone can figure out this behaviour, prefer to use
324// the Double or Large counterparts instead. These functions provide actually the same data, except in
325// a different, working format.
326func PdhGetFormattedCounterValueLong(hCounter PDH_HCOUNTER, lpdwType *uint32, pValue *PDH_FMT_COUNTERVALUE_LONG) uint32 {
327ret, _, _ := pdh_GetFormattedCounterValue.Call(
328uintptr(hCounter),
329uintptr(PDH_FMT_LONG),
330uintptr(unsafe.Pointer(lpdwType)),
331uintptr(unsafe.Pointer(pValue)))
332
333return uint32(ret)
334}
335
336// Returns an array of formatted counter values. Use this function when you want to format the counter values of a
337// counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE.
338// An example of how this function can be used:
339//
340// okPath := "\\Process(*)\\% Processor Time" // notice the wildcard * character
341//
342// // ommitted all necessary stuff ...
343//
344// var bufSize uint32
345// var bufCount uint32
346// var size uint32 = uint32(unsafe.Sizeof(win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
347// var emptyBuf [1]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
348//
349// for {
350// // collect
351// ret := win.PdhCollectQueryData(queryHandle)
352// if ret == win.ERROR_SUCCESS {
353// ret = win.PdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN.
354// if ret == win.PDH_MORE_DATA {
355// filledBuf := make([]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
356// ret = win.PdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &filledBuf[0])
357// for i := 0; i < int(bufCount); i++ {
358// c := filledBuf[i]
359// var s string = win.UTF16PtrToString(c.SzName)
360// fmt.Printf("Index %d -> %s, value %v\n", i, s, c.FmtValue.DoubleValue)
361// }
362//
363// filledBuf = nil
364// // Need to at least set bufSize to zero, because if not, the function will not
365// // return PDH_MORE_DATA and will not set the bufSize.
366// bufCount = 0
367// bufSize = 0
368// }
369//
370// time.Sleep(2000 * time.Millisecond)
371// }
372// }
373func PdhGetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *PDH_FMT_COUNTERVALUE_ITEM_DOUBLE) uint32 {
374ret, _, _ := pdh_GetFormattedCounterArrayW.Call(
375uintptr(hCounter),
376uintptr(PDH_FMT_DOUBLE),
377uintptr(unsafe.Pointer(lpdwBufferSize)),
378uintptr(unsafe.Pointer(lpdwBufferCount)),
379uintptr(unsafe.Pointer(itemBuffer)))
380
381return uint32(ret)
382}
383
384// Returns an array of formatted counter values. Use this function when you want to format the counter values of a
385// counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type PDH_FMT_COUNTERVALUE_ITEM_LARGE.
386// For an example usage, see PdhGetFormattedCounterArrayDouble.
387func PdhGetFormattedCounterArrayLarge(hCounter PDH_HCOUNTER, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *PDH_FMT_COUNTERVALUE_ITEM_LARGE) uint32 {
388ret, _, _ := pdh_GetFormattedCounterArrayW.Call(
389uintptr(hCounter),
390uintptr(PDH_FMT_LARGE),
391uintptr(unsafe.Pointer(lpdwBufferSize)),
392uintptr(unsafe.Pointer(lpdwBufferCount)),
393uintptr(unsafe.Pointer(itemBuffer)))
394
395return uint32(ret)
396}
397
398// Returns an array of formatted counter values. Use this function when you want to format the counter values of a
399// counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type PDH_FMT_COUNTERVALUE_ITEM_LONG.
400// For an example usage, see PdhGetFormattedCounterArrayDouble.
401//
402// BUG(krpors): See description of PdhGetFormattedCounterValueLong().
403func PdhGetFormattedCounterArrayLong(hCounter PDH_HCOUNTER, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *PDH_FMT_COUNTERVALUE_ITEM_LONG) uint32 {
404ret, _, _ := pdh_GetFormattedCounterArrayW.Call(
405uintptr(hCounter),
406uintptr(PDH_FMT_LONG),
407uintptr(unsafe.Pointer(lpdwBufferSize)),
408uintptr(unsafe.Pointer(lpdwBufferCount)),
409uintptr(unsafe.Pointer(itemBuffer)))
410
411return uint32(ret)
412}
413
414// Creates a new query that is used to manage the collection of performance data.
415// szDataSource is a null terminated string that specifies the name of the log file from which to
416// retrieve the performance data. If 0, performance data is collected from a real-time data source.
417// dwUserData is a user-defined value to associate with this query. To retrieve the user data later,
418// call PdhGetCounterInfo and access dwQueryUserData of the PDH_COUNTER_INFO structure. phQuery is
419// the handle to the query, and must be used in subsequent calls. This function returns a PDH_
420// constant error code, or ERROR_SUCCESS if the call succeeded.
421func PdhOpenQuery(szDataSource uintptr, dwUserData uintptr, phQuery *PDH_HQUERY) uint32 {
422ret, _, _ := pdh_OpenQuery.Call(
423szDataSource,
424dwUserData,
425uintptr(unsafe.Pointer(phQuery)))
426
427return uint32(ret)
428}
429
430// Validates a path. Will return ERROR_SUCCESS when ok, or PDH_CSTATUS_BAD_COUNTERNAME when the path is
431// erroneous.
432func PdhValidatePath(path string) uint32 {
433ptxt, _ := syscall.UTF16PtrFromString(path)
434ret, _, _ := pdh_ValidatePathW.Call(uintptr(unsafe.Pointer(ptxt)))
435
436return uint32(ret)
437}
438