-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
386fix #4076
386fix #4076
Changes from 3 commits
4c6940f
6efde55
77f45ba
af43fc0
930ea8e
4559ff9
ae0c16e
c271d08
cbd85c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,21 +35,32 @@ package win_perf_counters | |
import ( | ||
"fmt" | ||
"syscall" | ||
"time" | ||
"unsafe" | ||
|
||
"golang.org/x/sys/windows" | ||
) | ||
|
||
// Error codes | ||
const ( | ||
ERROR_SUCCESS = 0 | ||
ERROR_INVALID_FUNCTION = 1 | ||
ERROR_SUCCESS = 0 | ||
ERROR_FAILURE = 1 | ||
ERROR_INVALID_FUNCTION = 1 | ||
EPOCH_DIFFERENCE_MICROS int64 = 11644473600000000 | ||
) | ||
|
||
type ( | ||
HANDLE uintptr | ||
) | ||
|
||
type ( | ||
LPCTSTR uintptr | ||
) | ||
|
||
type ( | ||
LONGLONG int64 | ||
) | ||
|
||
// PDH error codes, which can be returned by all Pdh* functions. Taken from mingw-w64 pdhmsg.h | ||
const ( | ||
PDH_CSTATUS_VALID_DATA = 0x00000000 // The returned data is valid. | ||
|
@@ -156,76 +167,89 @@ const ( | |
PERF_DETAIL_STANDARD = 0x0000FFFF | ||
) | ||
|
||
const ( | ||
errnoERROR_IO_PENDING = 997 | ||
) | ||
|
||
var ( | ||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) | ||
) | ||
|
||
type ( | ||
PDH_HQUERY HANDLE // query handle | ||
PDH_HCOUNTER HANDLE // counter handle | ||
) | ||
|
||
// Union specialization for double values | ||
type PDH_FMT_COUNTERVALUE_DOUBLE struct { | ||
CStatus uint32 | ||
DoubleValue float64 | ||
type SYSTEMTIME struct { | ||
wYear uint16 | ||
wMonth uint16 | ||
wDayOfWeek uint16 | ||
wDay uint16 | ||
wHour uint16 | ||
wMinute uint16 | ||
wSecond uint16 | ||
wMilliseconds uint16 | ||
} | ||
|
||
// Union specialization for 64 bit integer values | ||
type PDH_FMT_COUNTERVALUE_LARGE struct { | ||
CStatus uint32 | ||
LargeValue int64 | ||
} | ||
|
||
// Union specialization for long values | ||
type PDH_FMT_COUNTERVALUE_LONG struct { | ||
CStatus uint32 | ||
LongValue int32 | ||
padding [4]byte | ||
} | ||
|
||
// Union specialization for double values, used by PdhGetFormattedCounterArrayDouble() | ||
type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct { | ||
SzName *uint16 // pointer to a string | ||
FmtValue PDH_FMT_COUNTERVALUE_DOUBLE | ||
} | ||
|
||
// Union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge() | ||
type PDH_FMT_COUNTERVALUE_ITEM_LARGE struct { | ||
SzName *uint16 // pointer to a string | ||
FmtValue PDH_FMT_COUNTERVALUE_LARGE | ||
} | ||
|
||
// Union specialization for long values, used by PdhGetFormattedCounterArrayLong() | ||
type PDH_FMT_COUNTERVALUE_ITEM_LONG struct { | ||
SzName *uint16 // pointer to a string | ||
FmtValue PDH_FMT_COUNTERVALUE_LONG | ||
type FILETIME struct { | ||
dwLowDateTime uint32 | ||
dwHighDateTime uint32 | ||
} | ||
|
||
var ( | ||
// Library | ||
libpdhDll *syscall.DLL | ||
libkrnDll *syscall.DLL | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you move all the kernel32 code to a new file: kernel32.go There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea. Done |
||
|
||
// Functions | ||
pdh_ConnectMachine *syscall.Proc | ||
pdh_AddCounterW *syscall.Proc | ||
pdh_AddEnglishCounterW *syscall.Proc | ||
pdh_CloseQuery *syscall.Proc | ||
pdh_CollectQueryData *syscall.Proc | ||
pdh_CollectQueryDataWithTime *syscall.Proc | ||
pdh_GetFormattedCounterValue *syscall.Proc | ||
pdh_GetFormattedCounterArrayW *syscall.Proc | ||
pdh_OpenQuery *syscall.Proc | ||
pdh_ValidatePathW *syscall.Proc | ||
|
||
krn_FileTimeToSystemTime *syscall.Proc | ||
krn_FileTimeToLocalFileTime *syscall.Proc | ||
krn_LocalFileTimeToFileTime *syscall.Proc | ||
krn_WideCharToMultiByte *syscall.Proc | ||
) | ||
|
||
func init() { | ||
// Library | ||
libpdhDll = syscall.MustLoadDLL("pdh.dll") | ||
libkrnDll = syscall.MustLoadDLL("Kernel32.dll") | ||
|
||
// Functions | ||
pdh_ConnectMachine = libpdhDll.MustFindProc("PdhConnectMachineW") | ||
pdh_AddCounterW = libpdhDll.MustFindProc("PdhAddCounterW") | ||
pdh_AddEnglishCounterW, _ = libpdhDll.FindProc("PdhAddEnglishCounterW") // XXX: only supported on versions > Vista. | ||
pdh_CloseQuery = libpdhDll.MustFindProc("PdhCloseQuery") | ||
pdh_CollectQueryData = libpdhDll.MustFindProc("PdhCollectQueryData") | ||
|
||
pdh_GetFormattedCounterValue = libpdhDll.MustFindProc("PdhGetFormattedCounterValue") | ||
pdh_GetFormattedCounterArrayW = libpdhDll.MustFindProc("PdhGetFormattedCounterArrayW") | ||
pdh_OpenQuery = libpdhDll.MustFindProc("PdhOpenQuery") | ||
pdh_ValidatePathW = libpdhDll.MustFindProc("PdhValidatePathW") | ||
|
||
krn_FileTimeToSystemTime = libkrnDll.MustFindProc("FileTimeToSystemTime") | ||
krn_FileTimeToLocalFileTime = libkrnDll.MustFindProc("FileTimeToLocalFileTime") | ||
krn_LocalFileTimeToFileTime = libkrnDll.MustFindProc("LocalFileTimeToFileTime") | ||
krn_WideCharToMultiByte = libkrnDll.MustFindProc("WideCharToMultiByte") | ||
} | ||
|
||
func PdhUseWinTimestamps() { | ||
pdh_CollectQueryDataWithTime = libpdhDll.MustFindProc("PdhCollectQueryDataWithTime") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do like above on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I learn something new everyday. Much cleaner. Thank-you |
||
} | ||
|
||
func PdhConnectMachine(szMachineName string) uint32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't seem to find where this is called from, is it used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, and I neglected to write a comment header stating this. In experimenting I had added the call, but ended up not requiring it (it was actually orders of magnitude slower doing the connect call and then querying for the counters. I've noted this in a comment header. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you instead remove all the calls that are not used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For sure. I can. I just seems a shame, the call works, and may be useful in the future. Perhaps there is an alternative place to put it? I'll remove it for now. At least it will be in my git history if I need to access it again. |
||
ptxt, _ := syscall.UTF16PtrFromString(szMachineName) | ||
ret, _, _ := pdh_ConnectMachine.Call(uintptr(unsafe.Pointer(ptxt))) | ||
return uint32(ret) | ||
} | ||
|
||
// Adds the specified counter to the query. This is the internationalized version. Preferably, use the | ||
|
@@ -329,6 +353,37 @@ func PdhCollectQueryData(hQuery PDH_HQUERY) uint32 { | |
return uint32(ret) | ||
} | ||
|
||
// Queries data from perfmon, retrieving the device/windows timestamp from the node it was collected on. | ||
// Converts the filetime structure to a GO time class and returns the native time. | ||
// | ||
func PdhCollectQueryDataWithTime(hQuery PDH_HQUERY) (uint32, time.Time) { | ||
var localFileTime FILETIME | ||
ret, _, _ := pdh_CollectQueryDataWithTime.Call(uintptr(hQuery), uintptr(unsafe.Pointer(&localFileTime))) | ||
|
||
if ret == ERROR_SUCCESS { | ||
var utcFileTime FILETIME | ||
ret, _, _ := krn_LocalFileTimeToFileTime.Call( | ||
uintptr(unsafe.Pointer(&localFileTime)), | ||
uintptr(unsafe.Pointer(&utcFileTime))) | ||
|
||
if ret == 0 { | ||
return uint32(ERROR_FAILURE), time.Now() | ||
} | ||
|
||
// First convert 100-ns intervals to microseconds, then adjust for the | ||
// epoch difference | ||
var totalMicroSeconds int64 | ||
totalMicroSeconds = ((int64(utcFileTime.dwHighDateTime) << 32) | int64(utcFileTime.dwLowDateTime)) / 10 | ||
totalMicroSeconds -= EPOCH_DIFFERENCE_MICROS | ||
|
||
retTime := time.Unix(0, totalMicroSeconds*1000) | ||
|
||
return uint32(ERROR_SUCCESS), retTime | ||
} | ||
|
||
return uint32(ret), time.Now() | ||
} | ||
|
||
// Formats the given hCounter using a 'double'. The result is set into the specialized union struct pValue. | ||
// This function does not directly translate to a Windows counterpart due to union specialization tricks. | ||
func PdhGetFormattedCounterValueDouble(hCounter PDH_HCOUNTER, lpdwType *uint32, pValue *PDH_FMT_COUNTERVALUE_DOUBLE) uint32 { | ||
|
@@ -414,11 +469,64 @@ func PdhValidatePath(path string) uint32 { | |
return uint32(ret) | ||
} | ||
|
||
func errnoErr(e syscall.Errno) error { | ||
switch e { | ||
case 0: | ||
return nil | ||
case errnoERROR_IO_PENDING: | ||
return errERROR_IO_PENDING | ||
} | ||
// TODO: add more here, after collecting data on the common | ||
// error values see on Windows. (perhaps when running | ||
// all.bat?) | ||
return e | ||
} | ||
|
||
// The windows native call for converting a 16-bit wide character string (UTF-16) to a null terminated string. | ||
// | ||
// Note: If you call the function and not pass in an out string, the return value will be the length of the | ||
// input string. | ||
// Example usage: | ||
// cc, err := WideCharToMultiByte(65001, 0, s, -1, nil, 0) | ||
// if err != nil { | ||
// fmt.Println("CONVERSION ERROR: ", err) | ||
// } | ||
// | ||
// fmt.Println("Length bytes: ", cc) | ||
// n, err := WideCharToMultiByte(65001, 0, s, 1<<29, &outStr[0], 1<<29) | ||
// if err != nil { | ||
// fmt.Println("CONVERSION ERROR: ", err) | ||
// } | ||
// fmt.Println("Converted bytes: ", n) | ||
// | ||
func WideCharToMultiByte(codePage uint32, dwFlags uint32, wchar *uint16, nwchar int32, str *byte, nstr int32) (nwrite int32, err error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is this called from? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not. It was one of those things I used to try and fix the win32 bug without padding the structures. The call itself works good and I thought it might be useful in the future. I've noted this in a comment, but I can remove it as well. |
||
r0, _, e1 := krn_WideCharToMultiByte.Call( | ||
uintptr(codePage), | ||
uintptr(dwFlags), | ||
uintptr(unsafe.Pointer(str)), | ||
uintptr(nstr), | ||
uintptr(unsafe.Pointer(wchar)), | ||
uintptr(nwchar), | ||
) | ||
|
||
nwrite = int32(r0) | ||
if nwrite == 0 { | ||
if e1 != nil { | ||
err = errnoErr(e1.(syscall.Errno)) | ||
} else { | ||
err = syscall.EINVAL | ||
} | ||
} | ||
|
||
return nwrite, err | ||
} | ||
|
||
func UTF16PtrToString(s *uint16) string { | ||
if s == nil { | ||
return "" | ||
} | ||
return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:]) | ||
|
||
return syscall.UTF16ToString((*[20]uint16)(unsafe.Pointer(s))[:]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO, this is not right. You have to cast pointer to large enough array, otherwise you could get out of bounds. |
||
} | ||
|
||
func PdhFormatError(msgId uint32) string { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright (c) 2010 The win Authors. All rights reserved. | ||
// | ||
// Redistribution and use in source and binary forms, with or without | ||
// modification, are permitted provided that the following conditions | ||
// are met: | ||
// 1. Redistributions of source code must retain the above copyright | ||
// notice, this list of conditions and the following disclaimer. | ||
// 2. Redistributions in binary form must reproduce the above copyright | ||
// notice, this list of conditions and the following disclaimer in the | ||
// documentation and/or other materials provided with the distribution. | ||
// 3. The names of the authors may not be used to endorse or promote products | ||
// derived from this software without specific prior written permission. | ||
// | ||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR | ||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
// | ||
// This is the official list of 'win' authors for copyright purposes. | ||
// | ||
// Alexander Neumann <an2048@googlemail.com> | ||
// Joseph Watson <jtwatson@linux-consulting.us> | ||
// Kevin Pors <krpors@gmail.com> | ||
|
||
// +build windows | ||
|
||
package win_perf_counters | ||
|
||
// Union specialization for double values | ||
type PDH_FMT_COUNTERVALUE_DOUBLE struct { | ||
CStatus uint32 | ||
padding uint32 // TODO: could well be broken on amd64 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change the type of padding to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
DoubleValue float64 | ||
} | ||
|
||
// Union specialization for 64 bit integer values | ||
type PDH_FMT_COUNTERVALUE_LARGE struct { | ||
CStatus uint32 | ||
padding uint32 // TODO: could well be broken on amd64 | ||
LargeValue int64 | ||
} | ||
|
||
// Union specialization for long values | ||
type PDH_FMT_COUNTERVALUE_LONG struct { | ||
CStatus uint32 | ||
LongValue int32 | ||
padding [4]byte | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't these all be the same size? This one is smaller than the rest by 32bits. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't actually change this one. It was already written this way, so my question is yours. It just looks like I changed it because I moved it to a new file, but amd64 is the same. I don't understand why it was done this way either but there is no need to access data in the struct after the padding so I was inclined not to worry about it... |
||
|
||
type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct { | ||
SzName *uint16 | ||
padding uint32 // TODO: could well be broken on amd64 | ||
FmtValue PDH_FMT_COUNTERVALUE_DOUBLE | ||
} | ||
|
||
// Union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge() | ||
type PDH_FMT_COUNTERVALUE_ITEM_LARGE struct { | ||
SzName *uint16 // pointer to a string | ||
padding uint32 // TODO: could well be broken on amd64 | ||
FmtValue PDH_FMT_COUNTERVALUE_LARGE | ||
} | ||
|
||
// Union specialization for long values, used by PdhGetFormattedCounterArrayLong() | ||
type PDH_FMT_COUNTERVALUE_ITEM_LONG struct { | ||
SzName *uint16 // pointer to a string | ||
padding uint32 // TODO: could well be broken on amd64 | ||
FmtValue PDH_FMT_COUNTERVALUE_LONG | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a more descriptive name would be
UsePerfCounterTime
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup. I like it. Changed