From 4c6940fd4e32af36657d2db65fb806de280a55a0 Mon Sep 17 00:00:00 2001 From: "TECKCOMINCO\\sclosso3" Date: Wed, 18 Apr 2018 13:10:29 -0600 Subject: [PATCH 1/9] WIP: Fix for 32 bit, but now only 32bit compatible. Will need to figure out how to separate a 32bit build from a 64 bit build. Enhancement: Allow collecting of perfmon data from a remote node Enhancement: Allow collecting data using the timestamp from the source and don't re-timestamp --- plugins/inputs/win_perf_counters/pdh.go | 162 +++++++++++++++++- .../win_perf_counters/win_perf_counters.go | 37 ++-- 2 files changed, 181 insertions(+), 18 deletions(-) diff --git a/plugins/inputs/win_perf_counters/pdh.go b/plugins/inputs/win_perf_counters/pdh.go index a0b6935060393..caa6b2d039bad 100644 --- a/plugins/inputs/win_perf_counters/pdh.go +++ b/plugins/inputs/win_perf_counters/pdh.go @@ -42,14 +42,24 @@ import ( // 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,6 +166,14 @@ 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 @@ -164,12 +182,14 @@ type ( // Union specialization for double values type PDH_FMT_COUNTERVALUE_DOUBLE struct { CStatus uint32 + padding uint32 // TODO: could well be broken on amd64 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 } @@ -182,50 +202,91 @@ type PDH_FMT_COUNTERVALUE_LONG struct { // Union specialization for double values, used by PdhGetFormattedCounterArrayDouble() type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct { - SzName *uint16 // pointer to a string + 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 } +type SYSTEMTIME struct { + wYear uint16 + wMonth uint16 + wDayOfWeek uint16 + wDay uint16 + wHour uint16 + wMinute uint16 + wSecond uint16 + wMilliseconds uint16 +} + +type FILETIME struct { + dwLowDateTime uint32 + dwHighDateTime uint32 +} + var ( // Library libpdhDll *syscall.DLL + libkrnDll *syscall.DLL // Functions - pdh_AddCounterW *syscall.Proc - pdh_AddEnglishCounterW *syscall.Proc - pdh_CloseQuery *syscall.Proc - pdh_CollectQueryData *syscall.Proc + 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_CollectQueryDataWithTime = libpdhDll.MustFindProc("PdhCollectQueryDataWithTime") 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 PdhConnectMachine(szMachineName string) uint32 { + 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 +390,36 @@ func PdhCollectQueryData(hQuery PDH_HQUERY) uint32 { return uint32(ret) } +// Comment +// +// 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 +505,66 @@ 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 +} + +func WideCharToMultiByte(codePage uint32, dwFlags uint32, wchar *uint16, nwchar int32, str *byte, nstr int32) (nwrite int32, err error) { + 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:]) + + // var outStr [1 << 29]byte + + // 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) + + //return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:]) + return syscall.UTF16ToString((*[20]uint16)(unsafe.Pointer(s))[:]) } func PdhFormatError(msgId uint32) string { diff --git a/plugins/inputs/win_perf_counters/win_perf_counters.go b/plugins/inputs/win_perf_counters/win_perf_counters.go index 7e3991d199118..64a4cbbdad908 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "strings" + "time" "unsafe" "github.com/influxdata/telegraf" @@ -20,9 +21,10 @@ var sampleConfig = ` ## agent, it will not be gathered. ## Settings: # PrintValid = false # Print All matching performance counters - + [[inputs.win_perf_counters.object]] - # Processor usage, alternative to native, reports on a per core. + # Processor usage, alternative to native, reports on a per core. + NodeName = "localhost" ObjectName = "Processor" Instances = ["*"] Counters = [ @@ -37,7 +39,8 @@ var sampleConfig = ` # WarnOnMissing = false [[inputs.win_perf_counters.object]] - # Disk times and queues + # Disk times and queues + NodeName = "localhost" ObjectName = "LogicalDisk" Instances = ["*"] Counters = [ @@ -47,6 +50,7 @@ var sampleConfig = ` Measurement = "win_disk" [[inputs.win_perf_counters.object]] + NodeName = "localhost" ObjectName = "System" Counters = ["Context Switches/sec","System Calls/sec"] Instances = ["------"] @@ -54,7 +58,8 @@ var sampleConfig = ` [[inputs.win_perf_counters.object]] # Example query where the Instance portion must be removed to get data back, - # such as from the Memory object. + # such as from the Memory object. + NodeName = "localhost" ObjectName = "Memory" Counters = [ "Available Bytes", "Cache Faults/sec", "Demand Zero Faults/sec", @@ -79,6 +84,7 @@ type perfobject struct { Counters []string Instances []string Measurement string + NodeName string WarnOnMissing bool FailOnMissing bool IncludeTotal bool @@ -90,6 +96,7 @@ type item struct { counter string instance string measurement string + nodename string include_total bool handle PDH_HQUERY counterHandle PDH_HCOUNTER @@ -99,7 +106,7 @@ var sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec", " ", "_", "%", "Percent", `\`, "") func (m *Win_PerfCounters) AddItem(query string, objectName string, counter string, instance string, - measurement string, include_total bool) error { + measurement string, nodename string, include_total bool) error { var handle PDH_HQUERY var counterHandle PDH_HCOUNTER @@ -117,7 +124,7 @@ func (m *Win_PerfCounters) AddItem(query string, objectName string, counter stri return errors.New(PdhFormatError(ret)) } - newItem := &item{query, objectName, counter, instance, measurement, + newItem := &item{query, objectName, counter, instance, measurement, nodename, include_total, handle, counterHandle} m.itemCache = append(m.itemCache, newItem) @@ -140,15 +147,22 @@ func (m *Win_PerfCounters) ParseConfig() error { for _, counter := range PerfObject.Counters { for _, instance := range PerfObject.Instances { objectname := PerfObject.ObjectName + nodename := PerfObject.NodeName + + // ret := PdhConnectMachine(nodename) + // fmt.Println("connect ret", ret) + // if ret != ERROR_SUCCESS { + // fmt.Println("connect failed") + // } if instance == "------" { - query = "\\" + objectname + "\\" + counter + query = "\\\\" + nodename + "\\" + objectname + "\\" + counter } else { - query = "\\" + objectname + "(" + instance + ")\\" + counter + query = "\\\\" + nodename + "\\" + objectname + "(" + instance + ")\\" + counter } err := m.AddItem(query, objectname, counter, instance, - PerfObject.Measurement, PerfObject.IncludeTotal) + PerfObject.Measurement, PerfObject.NodeName, PerfObject.IncludeTotal) if err == nil { if m.PrintValid { @@ -195,6 +209,7 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { // For iterate over the known metrics and get the samples. for _, metric := range m.itemCache { // collect + //ret, timestamp := PdhCollectQueryDataWithTime(metric.handle) ret := PdhCollectQueryData(metric.handle) if ret == ERROR_SUCCESS { ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, @@ -236,6 +251,7 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { if s != "" { tags["instance"] = s } + tags["node"] = metric.nodename tags["objectname"] = metric.objectName fields[sanitizedChars.Replace(metric.counter)] = float32(c.FmtValue.DoubleValue) @@ -244,7 +260,8 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { if measurement == "" { measurement = "win_perf_counters" } - acc.AddFields(measurement, fields, tags) + + acc.AddFields(measurement, fields, tags, time.Now()) } } From 6efde556664577d9ba4dcd9783dfc3de2aaf586c Mon Sep 17 00:00:00 2001 From: srclossoon Date: Wed, 18 Apr 2018 15:35:54 -0600 Subject: [PATCH 2/9] Fix: UseWinTimestamps is now configurable Fix: 32 and 64 bit versions split out into separate files --- plugins/inputs/win_perf_counters/pdh.go | 146 +++++++----------- plugins/inputs/win_perf_counters/pdh_386.go | 74 +++++++++ plugins/inputs/win_perf_counters/pdh_amd64.go | 69 +++++++++ .../win_perf_counters/win_perf_counters.go | 31 ++-- 4 files changed, 215 insertions(+), 105 deletions(-) create mode 100644 plugins/inputs/win_perf_counters/pdh_386.go create mode 100644 plugins/inputs/win_perf_counters/pdh_amd64.go diff --git a/plugins/inputs/win_perf_counters/pdh.go b/plugins/inputs/win_perf_counters/pdh.go index caa6b2d039bad..c5155c279bf38 100644 --- a/plugins/inputs/win_perf_counters/pdh.go +++ b/plugins/inputs/win_perf_counters/pdh.go @@ -35,6 +35,7 @@ package win_perf_counters import ( "fmt" "syscall" + "time" "unsafe" "golang.org/x/sys/windows" @@ -179,48 +180,6 @@ type ( PDH_HCOUNTER HANDLE // counter handle ) -// Union specialization for double values -type PDH_FMT_COUNTERVALUE_DOUBLE struct { - CStatus uint32 - padding uint32 // TODO: could well be broken on amd64 - 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 -} - -// Union specialization for double values, used by PdhGetFormattedCounterArrayDouble() -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 -} - type SYSTEMTIME struct { wYear uint16 wMonth uint16 @@ -243,12 +202,12 @@ var ( libkrnDll *syscall.DLL // 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_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 @@ -271,7 +230,7 @@ func init() { pdh_AddEnglishCounterW, _ = libpdhDll.FindProc("PdhAddEnglishCounterW") // XXX: only supported on versions > Vista. pdh_CloseQuery = libpdhDll.MustFindProc("PdhCloseQuery") pdh_CollectQueryData = libpdhDll.MustFindProc("PdhCollectQueryData") - //pdh_CollectQueryDataWithTime = libpdhDll.MustFindProc("PdhCollectQueryDataWithTime") + pdh_GetFormattedCounterValue = libpdhDll.MustFindProc("PdhGetFormattedCounterValue") pdh_GetFormattedCounterArrayW = libpdhDll.MustFindProc("PdhGetFormattedCounterArrayW") pdh_OpenQuery = libpdhDll.MustFindProc("PdhOpenQuery") @@ -283,6 +242,10 @@ func init() { krn_WideCharToMultiByte = libkrnDll.MustFindProc("WideCharToMultiByte") } +func PdhUseWinTimestamps() { + pdh_CollectQueryDataWithTime = libpdhDll.MustFindProc("PdhCollectQueryDataWithTime") +} + func PdhConnectMachine(szMachineName string) uint32 { ptxt, _ := syscall.UTF16PtrFromString(szMachineName) ret, _, _ := pdh_ConnectMachine.Call(uintptr(unsafe.Pointer(ptxt))) @@ -390,35 +353,36 @@ func PdhCollectQueryData(hQuery PDH_HQUERY) uint32 { return uint32(ret) } -// Comment +// 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() -// } +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 + // 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) + retTime := time.Unix(0, totalMicroSeconds*1000) -// return uint32(ERROR_SUCCESS), retTime -// } + return uint32(ERROR_SUCCESS), retTime + } -// return uint32(ret), time.Now() -// } + 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. @@ -518,6 +482,23 @@ func errnoErr(e syscall.Errno) error { 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) { r0, _, e1 := krn_WideCharToMultiByte.Call( uintptr(codePage), @@ -545,25 +526,6 @@ func UTF16PtrToString(s *uint16) string { return "" } - // var outStr [1 << 29]byte - - // 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) - - //return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:]) return syscall.UTF16ToString((*[20]uint16)(unsafe.Pointer(s))[:]) } diff --git a/plugins/inputs/win_perf_counters/pdh_386.go b/plugins/inputs/win_perf_counters/pdh_386.go new file mode 100644 index 0000000000000..4181e24cff209 --- /dev/null +++ b/plugins/inputs/win_perf_counters/pdh_386.go @@ -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 +// Joseph Watson +// Kevin Pors + +// +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 + 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 +} + +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 +} diff --git a/plugins/inputs/win_perf_counters/pdh_amd64.go b/plugins/inputs/win_perf_counters/pdh_amd64.go new file mode 100644 index 0000000000000..9e4b07af79669 --- /dev/null +++ b/plugins/inputs/win_perf_counters/pdh_amd64.go @@ -0,0 +1,69 @@ +// 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 +// Joseph Watson +// Kevin Pors + +// +build windows + +package win_perf_counters + +// Union specialization for double values +type PDH_FMT_COUNTERVALUE_DOUBLE struct { + CStatus uint32 + DoubleValue float64 +} + +// 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 +} + +type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct { + SzName *uint16 + 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 +} diff --git a/plugins/inputs/win_perf_counters/win_perf_counters.go b/plugins/inputs/win_perf_counters/win_perf_counters.go index 64a4cbbdad908..29bb6d0496e00 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters.go @@ -73,6 +73,7 @@ var sampleConfig = ` type Win_PerfCounters struct { PrintValid bool PreVistaSupport bool + UseWinTimestamp bool Object []perfobject configParsed bool @@ -142,6 +143,10 @@ func (m *Win_PerfCounters) SampleConfig() string { func (m *Win_PerfCounters) ParseConfig() error { var query string + if m.UseWinTimestamp { + PdhUseWinTimestamps() + } + if len(m.Object) > 0 { for _, PerfObject := range m.Object { for _, counter := range PerfObject.Counters { @@ -149,12 +154,6 @@ func (m *Win_PerfCounters) ParseConfig() error { objectname := PerfObject.ObjectName nodename := PerfObject.NodeName - // ret := PdhConnectMachine(nodename) - // fmt.Println("connect ret", ret) - // if ret != ERROR_SUCCESS { - // fmt.Println("connect failed") - // } - if instance == "------" { query = "\\\\" + nodename + "\\" + objectname + "\\" + counter } else { @@ -209,18 +208,24 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { // For iterate over the known metrics and get the samples. for _, metric := range m.itemCache { // collect - //ret, timestamp := PdhCollectQueryDataWithTime(metric.handle) - ret := PdhCollectQueryData(metric.handle) + var ret uint32 = 0 + var timestamp time.Time + + if m.UseWinTimestamp { + ret, timestamp = PdhCollectQueryDataWithTime(metric.handle) + } else { + timestamp = time.Now() + ret = PdhCollectQueryData(metric.handle) + } + if ret == ERROR_SUCCESS { - ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, - &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN. + ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN. if ret == PDH_MORE_DATA { filledBuf := make([]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size) if len(filledBuf) == 0 { continue } - ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, - &bufSize, &bufCount, &filledBuf[0]) + ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, &bufCount, &filledBuf[0]) for i := 0; i < int(bufCount); i++ { c := filledBuf[i] var s string = UTF16PtrToString(c.SzName) @@ -261,7 +266,7 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { measurement = "win_perf_counters" } - acc.AddFields(measurement, fields, tags, time.Now()) + acc.AddFields(measurement, fields, tags, timestamp) } } From 77f45ba4217689b1fff6b3193c42e8f7f8e814c6 Mon Sep 17 00:00:00 2001 From: srclossoon Date: Wed, 18 Apr 2018 15:42:12 -0600 Subject: [PATCH 3/9] Updated readme.md --- plugins/inputs/win_perf_counters/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/inputs/win_perf_counters/README.md b/plugins/inputs/win_perf_counters/README.md index 8627d02fdf5f6..67e42b8e6e0cc 100644 --- a/plugins/inputs/win_perf_counters/README.md +++ b/plugins/inputs/win_perf_counters/README.md @@ -38,6 +38,12 @@ It is recommended NOT to use this on OSes starting with Vista and newer because Example for Windows Server 2003, this would be set to true: `PreVistaSupport=true` +#### UseWinTimestamps + +Bool, if set to `true` will request a timestamp along with the PerfCounter data. + +Note: This call may not be supported on all versions of windows. If you receive an error, set this value to false to disable the feature, and searching for this call entirely. + ### Object See Entry below. From af43fc0c01a44f64641388e946e00540cb1dc354 Mon Sep 17 00:00:00 2001 From: srclossoon Date: Thu, 26 Apr 2018 02:45:42 -0600 Subject: [PATCH 4/9] Changes made wrt https://github.com/influxdata/telegraf/pull/4076/files Renamed nodename to computer moved out kernel32 calls to their own library Added tests --- plugins/inputs/win_perf_counters/README.md | 35 +++++- plugins/inputs/win_perf_counters/kernel32.go | 115 ++++++++++++++++++ plugins/inputs/win_perf_counters/pdh.go | 80 ++---------- plugins/inputs/win_perf_counters/pdh_386.go | 6 +- .../win_perf_counters/win_perf_counters.go | 43 +++---- .../win_perf_counters_test.go | 49 ++++++++ 6 files changed, 232 insertions(+), 96 deletions(-) create mode 100644 plugins/inputs/win_perf_counters/kernel32.go diff --git a/plugins/inputs/win_perf_counters/README.md b/plugins/inputs/win_perf_counters/README.md index 67e42b8e6e0cc..d79f62e421a8d 100644 --- a/plugins/inputs/win_perf_counters/README.md +++ b/plugins/inputs/win_perf_counters/README.md @@ -38,7 +38,7 @@ It is recommended NOT to use this on OSes starting with Vista and newer because Example for Windows Server 2003, this would be set to true: `PreVistaSupport=true` -#### UseWinTimestamps +#### UsePerfCounterTime Bool, if set to `true` will request a timestamp along with the PerfCounter data. @@ -56,6 +56,16 @@ beneath the main win_perf_counters entry, `[[inputs.win_perf_counters]]`. Following this is 3 required key/value pairs and the three optional parameters and their usage. +#### NodeName +**Required** + +NodeName specifies the node to connect to for querying the performance counters. While "localhost" will work to retrieve data from a local host, it is recommended +to use the actual machine name as the data will be tagged with a "Node" tag with the value of this parameter as the value. + +Important: If you are requesting data from a remote node you must do the following: +1. Log into that node and ensure the user telegraf is running under is added to the "Performance Monitor Users" group. +2. Change the user telegraf is running under to be running as that user. You can use the account "NT AUTHORITY\NETWORK_SERVICE + #### ObjectName **Required** @@ -134,6 +144,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # Processor usage, alternative to native, reports on a per core. + NodeName = "localhost" ObjectName = "Processor" Instances = ["*"] Counters = ["% Idle Time", "% Interrupt Time", "% Privileged Time", "% User Time", "% Processor Time"] @@ -142,6 +153,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # Disk times and queues + NodeName = "localhost" ObjectName = "LogicalDisk" Instances = ["*"] Counters = ["% Idle Time", "% Disk Time","% Disk Read Time", "% Disk Write Time", "% User Time", "Current Disk Queue Length"] @@ -149,6 +161,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. #IncludeTotal=false #Set to true to include _Total instance when querying for all (*). [[inputs.win_perf_counters.object]] + NodeName = "localhost" ObjectName = "System" Counters = ["Context Switches/sec","System Calls/sec", "Processor Queue Length"] Instances = ["------"] @@ -157,6 +170,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # Example query where the Instance portion must be removed to get data back, such as from the Memory object. + NodeName = "localhost" ObjectName = "Memory" Counters = ["Available Bytes","Cache Faults/sec","Demand Zero Faults/sec","Page Faults/sec","Pages/sec","Transition Faults/sec","Pool Nonpaged Bytes","Pool Paged Bytes"] Instances = ["------"] # Use 6 x - to remove the Instance bit from the query. @@ -166,6 +180,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # more counters for the Network Interface Object can be found at # https://msdn.microsoft.com/en-us/library/ms803962.aspx + NodeName = "localhost" ObjectName = "Network Interface" Counters = ["Bytes Received/sec","Bytes Sent/sec","Packets Received/sec","Packets Sent/sec"] Instances = ["*"] # Use 6 x - to remove the Instance bit from the query. @@ -176,6 +191,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ### Active Directory Domain Controller ``` [[inputs.win_perf_counters.object]] + NodeName = "localhost" ObjectName = "DirectoryServices" Instances = ["*"] Counters = ["Base Searches/sec","Database adds/sec","Database deletes/sec","Database modifys/sec","Database recycles/sec","LDAP Client Sessions","LDAP Searches/sec","LDAP Writes/sec"] @@ -184,6 +200,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. #IncludeTotal=false #Set to true to include _Total instance when querying for all (*). [[inputs.win_perf_counters.object]] + NodeName = "localhost" ObjectName = "Security System-Wide Statistics" Instances = ["*"] Counters = ["NTLM Authentications","Kerberos Authentications","Digest Authentications"] @@ -191,6 +208,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. #IncludeTotal=false #Set to true to include _Total instance when querying for all (*). [[inputs.win_perf_counters.object]] + NodeName = "localhost" ObjectName = "Database" Instances = ["*"] Counters = ["Database Cache % Hit","Database Cache Page Fault Stalls/sec","Database Cache Page Faults/sec","Database Cache Size"] @@ -202,6 +220,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # AD, DFS N, Useful if the server hosts a DFS Namespace or is a Domain Controller + NodeName = "localhost" ObjectName = "DFS Namespace Service Referrals" Instances = ["*"] Counters = ["Requests Processed","Requests Failed","Avg. Response Time"] @@ -215,6 +234,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # AD, DFS R, Useful if the server hosts a DFS Replication folder or is a Domain Controller + NodeName = "localhost" ObjectName = "DFS Replication Service Volumes" Instances = ["*"] Counters = ["Data Lookups","Database Commits"] @@ -227,6 +247,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ### DNS Server + Domain Controllers ``` [[inputs.win_perf_counters.object]] + NodeName = "localhost" ObjectName = "DNS" Counters = ["Dynamic Update Received","Dynamic Update Rejected","Recursive Queries","Recursive Queries Failure","Secure Update Failure","Secure Update Received","TCP Query Received","TCP Response Sent","UDP Query Received","UDP Response Sent","Total Query Received","Total Response Sent"] Instances = ["------"] @@ -238,6 +259,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # HTTP Service request queues in the Kernel before being handed over to User Mode. + NodeName = "localhost" ObjectName = "HTTP Service Request Queues" Instances = ["*"] Counters = ["CurrentQueueSize","RejectedRequests"] @@ -246,6 +268,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # IIS, ASP.NET Applications + NodeName = "localhost" ObjectName = "ASP.NET Applications" Counters = ["Cache Total Entries","Cache Total Hit Ratio","Cache Total Turnover Rate","Output Cache Entries","Output Cache Hits","Output Cache Hit Ratio","Output Cache Turnover Rate","Compilations Total","Errors Total/Sec","Pipeline Instance Count","Requests Executing","Requests in Application Queue","Requests/Sec"] Instances = ["*"] @@ -254,6 +277,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # IIS, ASP.NET + NodeName = "localhost" ObjectName = "ASP.NET" Counters = ["Application Restarts","Request Wait Time","Requests Current","Requests Queued","Requests Rejected"] Instances = ["*"] @@ -262,6 +286,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # IIS, Web Service + NodeName = "localhost" ObjectName = "Web Service" Counters = ["Get Requests/sec","Post Requests/sec","Connection Attempts/sec","Current Connections","ISAPI Extension Requests/sec"] Instances = ["*"] @@ -270,6 +295,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # Web Service Cache / IIS + NodeName = "localhost" ObjectName = "Web Service Cache" Counters = ["URI Cache Hits %","Kernel: URI Cache Hits %","File Cache Hits %"] Instances = ["*"] @@ -282,6 +308,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # Process metrics, in this case for IIS only + NodeName = "localhost" ObjectName = "Process" Counters = ["% Processor Time","Handle Count","Private Bytes","Thread Count","Virtual Bytes","Working Set"] Instances = ["w3wp"] @@ -294,6 +321,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # .NET CLR Exceptions, in this case for IIS only + NodeName = "localhost" ObjectName = ".NET CLR Exceptions" Counters = ["# of Exceps Thrown / sec"] Instances = ["w3wp"] @@ -302,6 +330,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR Jit, in this case for IIS only + NodeName = "localhost" ObjectName = ".NET CLR Jit" Counters = ["% Time in Jit","IL Bytes Jitted / sec"] Instances = ["w3wp"] @@ -310,6 +339,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR Loading, in this case for IIS only + NodeName = "localhost" ObjectName = ".NET CLR Loading" Counters = ["% Time Loading"] Instances = ["w3wp"] @@ -318,6 +348,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR LocksAndThreads, in this case for IIS only + NodeName = "localhost" ObjectName = ".NET CLR LocksAndThreads" Counters = ["# of current logical Threads","# of current physical Threads","# of current recognized threads","# of total recognized threads","Queue Length / sec","Total # of Contentions","Current Queue Length"] Instances = ["w3wp"] @@ -326,6 +357,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR Memory, in this case for IIS only + NodeName = "localhost" ObjectName = ".NET CLR Memory" Counters = ["% Time in GC","# Bytes in all Heaps","# Gen 0 Collections","# Gen 1 Collections","# Gen 2 Collections","# Induced GC","Allocated Bytes/sec","Finalization Survivors","Gen 0 heap size","Gen 1 heap size","Gen 2 heap size","Large Object Heap size","# of Pinned Objects"] Instances = ["w3wp"] @@ -334,6 +366,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR Security, in this case for IIS only + NodeName = "localhost" ObjectName = ".NET CLR Security" Counters = ["% Time in RT checks","Stack Walk Depth","Total Runtime Checks"] Instances = ["w3wp"] diff --git a/plugins/inputs/win_perf_counters/kernel32.go b/plugins/inputs/win_perf_counters/kernel32.go new file mode 100644 index 0000000000000..4c1ddbfab0b01 --- /dev/null +++ b/plugins/inputs/win_perf_counters/kernel32.go @@ -0,0 +1,115 @@ +// 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 +// Joseph Watson +// Kevin Pors + +// +build windows + +package win_perf_counters + +import ( + "syscall" + "unsafe" +) + +type SYSTEMTIME struct { + wYear uint16 + wMonth uint16 + wDayOfWeek uint16 + wDay uint16 + wHour uint16 + wMinute uint16 + wSecond uint16 + wMilliseconds uint16 +} + +type FILETIME struct { + dwLowDateTime uint32 + dwHighDateTime uint32 +} + +var ( + // Library + libkrnDll *syscall.DLL + + // Functions + krn_FileTimeToSystemTime *syscall.Proc + krn_FileTimeToLocalFileTime *syscall.Proc + krn_LocalFileTimeToFileTime *syscall.Proc + krn_WideCharToMultiByte *syscall.Proc +) + +func init() { + libkrnDll = syscall.MustLoadDLL("Kernel32.dll") + + krn_FileTimeToSystemTime = libkrnDll.MustFindProc("FileTimeToSystemTime") + krn_FileTimeToLocalFileTime = libkrnDll.MustFindProc("FileTimeToLocalFileTime") + krn_LocalFileTimeToFileTime = libkrnDll.MustFindProc("LocalFileTimeToFileTime") + krn_WideCharToMultiByte = libkrnDll.MustFindProc("WideCharToMultiByte") +} + +// CURRENTLY UNUSED: But may be useful in the future +// +// 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) { + 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 +} diff --git a/plugins/inputs/win_perf_counters/pdh.go b/plugins/inputs/win_perf_counters/pdh.go index c5155c279bf38..cc515ec91fa36 100644 --- a/plugins/inputs/win_perf_counters/pdh.go +++ b/plugins/inputs/win_perf_counters/pdh.go @@ -180,26 +180,9 @@ type ( PDH_HCOUNTER HANDLE // counter handle ) -type SYSTEMTIME struct { - wYear uint16 - wMonth uint16 - wDayOfWeek uint16 - wDay uint16 - wHour uint16 - wMinute uint16 - wSecond uint16 - wMilliseconds uint16 -} - -type FILETIME struct { - dwLowDateTime uint32 - dwHighDateTime uint32 -} - var ( // Library libpdhDll *syscall.DLL - libkrnDll *syscall.DLL // Functions pdh_ConnectMachine *syscall.Proc @@ -212,17 +195,11 @@ var ( 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") @@ -230,22 +207,22 @@ func init() { pdh_AddEnglishCounterW, _ = libpdhDll.FindProc("PdhAddEnglishCounterW") // XXX: only supported on versions > Vista. pdh_CloseQuery = libpdhDll.MustFindProc("PdhCloseQuery") pdh_CollectQueryData = libpdhDll.MustFindProc("PdhCollectQueryData") + // Not supported on Windows XP for sure. May be other versions of windows. + pdh_CollectQueryDataWithTime, _ = libpdhDll.FindProc("PdhCollectQueryDataWithTime") 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") } +// Connects to the specified machine +// +// Originally used to connect to a computer for querying performnce counter data, but was unnecessary, and +// actually slower to query the data using the call. Still a useful call for the future? +// +// Currently unused. But may be useful in the future? +// func PdhConnectMachine(szMachineName string) uint32 { ptxt, _ := syscall.UTF16PtrFromString(szMachineName) ret, _, _ := pdh_ConnectMachine.Call(uintptr(unsafe.Pointer(ptxt))) @@ -482,45 +459,6 @@ func errnoErr(e syscall.Errno) error { 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) { - 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 "" diff --git a/plugins/inputs/win_perf_counters/pdh_386.go b/plugins/inputs/win_perf_counters/pdh_386.go index 4181e24cff209..1d1ee517b2f0a 100644 --- a/plugins/inputs/win_perf_counters/pdh_386.go +++ b/plugins/inputs/win_perf_counters/pdh_386.go @@ -55,20 +55,20 @@ type PDH_FMT_COUNTERVALUE_LONG struct { type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct { SzName *uint16 - padding uint32 // TODO: could well be broken on amd64 + padding [4]byte 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 + padding [4]byte 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 + padding [4]byte FmtValue PDH_FMT_COUNTERVALUE_LONG } diff --git a/plugins/inputs/win_perf_counters/win_perf_counters.go b/plugins/inputs/win_perf_counters/win_perf_counters.go index 29bb6d0496e00..fbb2894407e55 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters.go @@ -24,7 +24,7 @@ var sampleConfig = ` [[inputs.win_perf_counters.object]] # Processor usage, alternative to native, reports on a per core. - NodeName = "localhost" + Computer = "localhost" ObjectName = "Processor" Instances = ["*"] Counters = [ @@ -40,7 +40,7 @@ var sampleConfig = ` [[inputs.win_perf_counters.object]] # Disk times and queues - NodeName = "localhost" + Computer = "localhost" ObjectName = "LogicalDisk" Instances = ["*"] Counters = [ @@ -50,7 +50,7 @@ var sampleConfig = ` Measurement = "win_disk" [[inputs.win_perf_counters.object]] - NodeName = "localhost" + Computer = "localhost" ObjectName = "System" Counters = ["Context Switches/sec","System Calls/sec"] Instances = ["------"] @@ -59,7 +59,7 @@ var sampleConfig = ` [[inputs.win_perf_counters.object]] # Example query where the Instance portion must be removed to get data back, # such as from the Memory object. - NodeName = "localhost" + Computer = "localhost" ObjectName = "Memory" Counters = [ "Available Bytes", "Cache Faults/sec", "Demand Zero Faults/sec", @@ -71,21 +71,21 @@ var sampleConfig = ` ` type Win_PerfCounters struct { - PrintValid bool - PreVistaSupport bool - UseWinTimestamp bool - Object []perfobject + PrintValid bool + PreVistaSupport bool + UsePerfCounterTime bool + Object []perfobject configParsed bool itemCache []*item } type perfobject struct { + Computer string ObjectName string Counters []string Instances []string Measurement string - NodeName string WarnOnMissing bool FailOnMissing bool IncludeTotal bool @@ -143,25 +143,24 @@ func (m *Win_PerfCounters) SampleConfig() string { func (m *Win_PerfCounters) ParseConfig() error { var query string - if m.UseWinTimestamp { - PdhUseWinTimestamps() - } - if len(m.Object) > 0 { for _, PerfObject := range m.Object { for _, counter := range PerfObject.Counters { for _, instance := range PerfObject.Instances { + computer := "" + if len(PerfObject.Computer) > 0 { + computer = "\\\\" + PerfObject.Computer + } objectname := PerfObject.ObjectName - nodename := PerfObject.NodeName if instance == "------" { - query = "\\\\" + nodename + "\\" + objectname + "\\" + counter + query = computer + "\\" + objectname + "\\" + counter } else { - query = "\\\\" + nodename + "\\" + objectname + "(" + instance + ")\\" + counter + query = computer + "\\" + objectname + "(" + instance + ")\\" + counter } err := m.AddItem(query, objectname, counter, instance, - PerfObject.Measurement, PerfObject.NodeName, PerfObject.IncludeTotal) + PerfObject.Measurement, PerfObject.Computer, PerfObject.IncludeTotal) if err == nil { if m.PrintValid { @@ -208,10 +207,10 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { // For iterate over the known metrics and get the samples. for _, metric := range m.itemCache { // collect - var ret uint32 = 0 + var ret uint32 var timestamp time.Time - if m.UseWinTimestamp { + if m.UsePerfCounterTime { ret, timestamp = PdhCollectQueryDataWithTime(metric.handle) } else { timestamp = time.Now() @@ -219,13 +218,15 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { } if ret == ERROR_SUCCESS { - ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN. + ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, + &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN. if ret == PDH_MORE_DATA { filledBuf := make([]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size) if len(filledBuf) == 0 { continue } - ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, &bufCount, &filledBuf[0]) + ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, + &bufCount, &filledBuf[0]) for i := 0; i < int(bufCount); i++ { c := filledBuf[i] var s string = UTF16PtrToString(c.SzName) diff --git a/plugins/inputs/win_perf_counters/win_perf_counters_test.go b/plugins/inputs/win_perf_counters/win_perf_counters_test.go index a24cedae56470..e274b209549ce 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters_test.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters_test.go @@ -326,6 +326,55 @@ func TestWinPerfcountersConfigGet7(t *testing.T) { } } +func TestWinPerfcountersConfigGet8(t *testing.T) { + + var instances = make([]string, 1) + var counters = make([]string, 1) + var perfobjects = make([]perfobject, 1) + + computer := "localhost" + objectname := "Processor Information" + instances[0] = "_Total" + counters[0] = "% Processor Time" + + var measurement string = "test" + var warnonmissing bool = false + var failonmissing bool = true + var includetotal bool = false + + PerfObject := perfobject{ + Computer: computer, + ObjectName: objectname, + Instances: instances, + Counters: counters, + Measurement: measurement, + WarnOnMissing: warnonmissing, + FailOnMissing: failonmissing, + IncludeTotal: includetotal, + } + + perfobjects[0] = PerfObject + + m := Win_PerfCounters{PrintValid: false, Object: perfobjects} + + err := m.ParseConfig() + require.NoError(t, err) + + var parsedItems = m.GetParsedItemsForTesting() + + if len(parsedItems) == 1 { + require.NoError(t, nil) + } else if len(parsedItems) == 0 { + var errorstring1 string = "No results returned from the query: " + string(len(parsedItems)) + err2 := errors.New(errorstring1) + require.NoError(t, err2) + } else if len(parsedItems) > 1 { + var errorstring1 string = "Too many results returned from the query: " + string(len(parsedItems)) + err2 := errors.New(errorstring1) + require.NoError(t, err2) + } +} + func TestWinPerfcountersConfigError1(t *testing.T) { var instances = make([]string, 1) From 930ea8e65bfa62874f077dd1d32f914c80a9b2ed Mon Sep 17 00:00:00 2001 From: srclossoon Date: Thu, 26 Apr 2018 02:55:08 -0600 Subject: [PATCH 5/9] Updated readme from nodename to computer --- plugins/inputs/win_perf_counters/README.md | 46 +++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/plugins/inputs/win_perf_counters/README.md b/plugins/inputs/win_perf_counters/README.md index d79f62e421a8d..41399c46d6988 100644 --- a/plugins/inputs/win_perf_counters/README.md +++ b/plugins/inputs/win_perf_counters/README.md @@ -144,7 +144,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # Processor usage, alternative to native, reports on a per core. - NodeName = "localhost" + Computer = "localhost" ObjectName = "Processor" Instances = ["*"] Counters = ["% Idle Time", "% Interrupt Time", "% Privileged Time", "% User Time", "% Processor Time"] @@ -153,7 +153,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # Disk times and queues - NodeName = "localhost" + Computer = "localhost" ObjectName = "LogicalDisk" Instances = ["*"] Counters = ["% Idle Time", "% Disk Time","% Disk Read Time", "% Disk Write Time", "% User Time", "Current Disk Queue Length"] @@ -161,7 +161,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. #IncludeTotal=false #Set to true to include _Total instance when querying for all (*). [[inputs.win_perf_counters.object]] - NodeName = "localhost" + Computer = "localhost" ObjectName = "System" Counters = ["Context Switches/sec","System Calls/sec", "Processor Queue Length"] Instances = ["------"] @@ -170,7 +170,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # Example query where the Instance portion must be removed to get data back, such as from the Memory object. - NodeName = "localhost" + Computer = "localhost" ObjectName = "Memory" Counters = ["Available Bytes","Cache Faults/sec","Demand Zero Faults/sec","Page Faults/sec","Pages/sec","Transition Faults/sec","Pool Nonpaged Bytes","Pool Paged Bytes"] Instances = ["------"] # Use 6 x - to remove the Instance bit from the query. @@ -180,7 +180,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # more counters for the Network Interface Object can be found at # https://msdn.microsoft.com/en-us/library/ms803962.aspx - NodeName = "localhost" + Computer = "localhost" ObjectName = "Network Interface" Counters = ["Bytes Received/sec","Bytes Sent/sec","Packets Received/sec","Packets Sent/sec"] Instances = ["*"] # Use 6 x - to remove the Instance bit from the query. @@ -191,7 +191,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ### Active Directory Domain Controller ``` [[inputs.win_perf_counters.object]] - NodeName = "localhost" + Computer = "localhost" ObjectName = "DirectoryServices" Instances = ["*"] Counters = ["Base Searches/sec","Database adds/sec","Database deletes/sec","Database modifys/sec","Database recycles/sec","LDAP Client Sessions","LDAP Searches/sec","LDAP Writes/sec"] @@ -200,7 +200,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. #IncludeTotal=false #Set to true to include _Total instance when querying for all (*). [[inputs.win_perf_counters.object]] - NodeName = "localhost" + Computer = "localhost" ObjectName = "Security System-Wide Statistics" Instances = ["*"] Counters = ["NTLM Authentications","Kerberos Authentications","Digest Authentications"] @@ -208,7 +208,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. #IncludeTotal=false #Set to true to include _Total instance when querying for all (*). [[inputs.win_perf_counters.object]] - NodeName = "localhost" + Computer = "localhost" ObjectName = "Database" Instances = ["*"] Counters = ["Database Cache % Hit","Database Cache Page Fault Stalls/sec","Database Cache Page Faults/sec","Database Cache Size"] @@ -220,7 +220,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # AD, DFS N, Useful if the server hosts a DFS Namespace or is a Domain Controller - NodeName = "localhost" + Computer = "localhost" ObjectName = "DFS Namespace Service Referrals" Instances = ["*"] Counters = ["Requests Processed","Requests Failed","Avg. Response Time"] @@ -234,7 +234,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # AD, DFS R, Useful if the server hosts a DFS Replication folder or is a Domain Controller - NodeName = "localhost" + Computer = "localhost" ObjectName = "DFS Replication Service Volumes" Instances = ["*"] Counters = ["Data Lookups","Database Commits"] @@ -247,7 +247,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ### DNS Server + Domain Controllers ``` [[inputs.win_perf_counters.object]] - NodeName = "localhost" + Computer = "localhost" ObjectName = "DNS" Counters = ["Dynamic Update Received","Dynamic Update Rejected","Recursive Queries","Recursive Queries Failure","Secure Update Failure","Secure Update Received","TCP Query Received","TCP Response Sent","UDP Query Received","UDP Response Sent","Total Query Received","Total Response Sent"] Instances = ["------"] @@ -259,7 +259,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # HTTP Service request queues in the Kernel before being handed over to User Mode. - NodeName = "localhost" + Computer = "localhost" ObjectName = "HTTP Service Request Queues" Instances = ["*"] Counters = ["CurrentQueueSize","RejectedRequests"] @@ -268,7 +268,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # IIS, ASP.NET Applications - NodeName = "localhost" + Computer = "localhost" ObjectName = "ASP.NET Applications" Counters = ["Cache Total Entries","Cache Total Hit Ratio","Cache Total Turnover Rate","Output Cache Entries","Output Cache Hits","Output Cache Hit Ratio","Output Cache Turnover Rate","Compilations Total","Errors Total/Sec","Pipeline Instance Count","Requests Executing","Requests in Application Queue","Requests/Sec"] Instances = ["*"] @@ -277,7 +277,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # IIS, ASP.NET - NodeName = "localhost" + Computer = "localhost" ObjectName = "ASP.NET" Counters = ["Application Restarts","Request Wait Time","Requests Current","Requests Queued","Requests Rejected"] Instances = ["*"] @@ -286,7 +286,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # IIS, Web Service - NodeName = "localhost" + Computer = "localhost" ObjectName = "Web Service" Counters = ["Get Requests/sec","Post Requests/sec","Connection Attempts/sec","Current Connections","ISAPI Extension Requests/sec"] Instances = ["*"] @@ -295,7 +295,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # Web Service Cache / IIS - NodeName = "localhost" + Computer = "localhost" ObjectName = "Web Service Cache" Counters = ["URI Cache Hits %","Kernel: URI Cache Hits %","File Cache Hits %"] Instances = ["*"] @@ -308,7 +308,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # Process metrics, in this case for IIS only - NodeName = "localhost" + Computer = "localhost" ObjectName = "Process" Counters = ["% Processor Time","Handle Count","Private Bytes","Thread Count","Virtual Bytes","Working Set"] Instances = ["w3wp"] @@ -321,7 +321,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. ``` [[inputs.win_perf_counters.object]] # .NET CLR Exceptions, in this case for IIS only - NodeName = "localhost" + Computer = "localhost" ObjectName = ".NET CLR Exceptions" Counters = ["# of Exceps Thrown / sec"] Instances = ["w3wp"] @@ -330,7 +330,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR Jit, in this case for IIS only - NodeName = "localhost" + Computer = "localhost" ObjectName = ".NET CLR Jit" Counters = ["% Time in Jit","IL Bytes Jitted / sec"] Instances = ["w3wp"] @@ -339,7 +339,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR Loading, in this case for IIS only - NodeName = "localhost" + Computer = "localhost" ObjectName = ".NET CLR Loading" Counters = ["% Time Loading"] Instances = ["w3wp"] @@ -348,7 +348,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR LocksAndThreads, in this case for IIS only - NodeName = "localhost" + Computer = "localhost" ObjectName = ".NET CLR LocksAndThreads" Counters = ["# of current logical Threads","# of current physical Threads","# of current recognized threads","# of total recognized threads","Queue Length / sec","Total # of Contentions","Current Queue Length"] Instances = ["w3wp"] @@ -357,7 +357,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR Memory, in this case for IIS only - NodeName = "localhost" + Computer = "localhost" ObjectName = ".NET CLR Memory" Counters = ["% Time in GC","# Bytes in all Heaps","# Gen 0 Collections","# Gen 1 Collections","# Gen 2 Collections","# Induced GC","Allocated Bytes/sec","Finalization Survivors","Gen 0 heap size","Gen 1 heap size","Gen 2 heap size","Large Object Heap size","# of Pinned Objects"] Instances = ["w3wp"] @@ -366,7 +366,7 @@ if any of the combinations of ObjectName/Instances/Counters are invalid. [[inputs.win_perf_counters.object]] # .NET CLR Security, in this case for IIS only - NodeName = "localhost" + Computer = "localhost" ObjectName = ".NET CLR Security" Counters = ["% Time in RT checks","Stack Walk Depth","Total Runtime Checks"] Instances = ["w3wp"] From 4559ff9727604d5b6118375c9c20caf815c244de Mon Sep 17 00:00:00 2001 From: srclossoon Date: Thu, 26 Apr 2018 02:57:27 -0600 Subject: [PATCH 6/9] Oone more documentation update found and fixed --- plugins/inputs/win_perf_counters/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/win_perf_counters/README.md b/plugins/inputs/win_perf_counters/README.md index 41399c46d6988..4999a4800067f 100644 --- a/plugins/inputs/win_perf_counters/README.md +++ b/plugins/inputs/win_perf_counters/README.md @@ -56,10 +56,10 @@ beneath the main win_perf_counters entry, `[[inputs.win_perf_counters]]`. Following this is 3 required key/value pairs and the three optional parameters and their usage. -#### NodeName +#### Computer **Required** -NodeName specifies the node to connect to for querying the performance counters. While "localhost" will work to retrieve data from a local host, it is recommended +Computer specifies the computer to connect to for querying the performance counters. While "localhost" will work to retrieve data from a local host, it is recommended to use the actual machine name as the data will be tagged with a "Node" tag with the value of this parameter as the value. Important: If you are requesting data from a remote node you must do the following: From ae0c16e88ff4e3f5c8e4d8f50f1de4922bc30305 Mon Sep 17 00:00:00 2001 From: srclossoon Date: Thu, 26 Apr 2018 03:04:51 -0600 Subject: [PATCH 7/9] Fix: Moved item nodename declaration to computer. Made sure not to tag with "computer" if computer was not specified. (Backwards compatibility) --- .../win_perf_counters/win_perf_counters.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/plugins/inputs/win_perf_counters/win_perf_counters.go b/plugins/inputs/win_perf_counters/win_perf_counters.go index fbb2894407e55..5637684543a48 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters.go @@ -93,11 +93,11 @@ type perfobject struct { type item struct { query string + computer string objectName string counter string instance string measurement string - nodename string include_total bool handle PDH_HQUERY counterHandle PDH_HCOUNTER @@ -106,8 +106,8 @@ type item struct { var sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec", " ", "_", "%", "Percent", `\`, "") -func (m *Win_PerfCounters) AddItem(query string, objectName string, counter string, instance string, - measurement string, nodename string, include_total bool) error { +func (m *Win_PerfCounters) AddItem(query string, computer string, objectName string, counter string, instance string, + measurement string, include_total bool) error { var handle PDH_HQUERY var counterHandle PDH_HCOUNTER @@ -125,7 +125,7 @@ func (m *Win_PerfCounters) AddItem(query string, objectName string, counter stri return errors.New(PdhFormatError(ret)) } - newItem := &item{query, objectName, counter, instance, measurement, nodename, + newItem := &item{query, computer, objectName, counter, instance, measurement, include_total, handle, counterHandle} m.itemCache = append(m.itemCache, newItem) @@ -159,8 +159,8 @@ func (m *Win_PerfCounters) ParseConfig() error { query = computer + "\\" + objectname + "(" + instance + ")\\" + counter } - err := m.AddItem(query, objectname, counter, instance, - PerfObject.Measurement, PerfObject.Computer, PerfObject.IncludeTotal) + err := m.AddItem(query, computer, objectname, counter, instance, + PerfObject.Measurement, PerfObject.IncludeTotal) if err == nil { if m.PrintValid { @@ -257,7 +257,11 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { if s != "" { tags["instance"] = s } - tags["node"] = metric.nodename + + if len(metric.computer) > 0 { + tags["computer"] = metric.computer + } + tags["objectname"] = metric.objectName fields[sanitizedChars.Replace(metric.counter)] = float32(c.FmtValue.DoubleValue) From c271d0855f055ba0c8f0911b52201ffcc1bb3733 Mon Sep 17 00:00:00 2001 From: srclossoon Date: Thu, 26 Apr 2018 03:07:14 -0600 Subject: [PATCH 8/9] Revert: Line fix --- plugins/inputs/win_perf_counters/win_perf_counters.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/win_perf_counters/win_perf_counters.go b/plugins/inputs/win_perf_counters/win_perf_counters.go index 5637684543a48..7a98ec753ec79 100644 --- a/plugins/inputs/win_perf_counters/win_perf_counters.go +++ b/plugins/inputs/win_perf_counters/win_perf_counters.go @@ -225,8 +225,8 @@ func (m *Win_PerfCounters) Gather(acc telegraf.Accumulator) error { if len(filledBuf) == 0 { continue } - ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, &bufSize, - &bufCount, &filledBuf[0]) + ret = PdhGetFormattedCounterArrayDouble(metric.counterHandle, + &bufSize, &bufCount, &filledBuf[0]) for i := 0; i < int(bufCount); i++ { c := filledBuf[i] var s string = UTF16PtrToString(c.SzName) From cbd85c72e0100d060a4ac2ce9221fd07f26eb22e Mon Sep 17 00:00:00 2001 From: srclossoon Date: Sun, 29 Apr 2018 11:35:18 -0600 Subject: [PATCH 9/9] Update: Feedback from code review - Adding a computer name is optional - Removed unused code - Fixed another few instances of uint32 with [4]byte --- plugins/inputs/win_perf_counters/README.md | 2 +- plugins/inputs/win_perf_counters/kernel32.go | 42 -------------------- plugins/inputs/win_perf_counters/pdh.go | 14 ------- plugins/inputs/win_perf_counters/pdh_386.go | 4 +- 4 files changed, 3 insertions(+), 59 deletions(-) diff --git a/plugins/inputs/win_perf_counters/README.md b/plugins/inputs/win_perf_counters/README.md index 4999a4800067f..282443428bcb1 100644 --- a/plugins/inputs/win_perf_counters/README.md +++ b/plugins/inputs/win_perf_counters/README.md @@ -57,7 +57,7 @@ beneath the main win_perf_counters entry, `[[inputs.win_perf_counters]]`. Following this is 3 required key/value pairs and the three optional parameters and their usage. #### Computer -**Required** +**Optional** Computer specifies the computer to connect to for querying the performance counters. While "localhost" will work to retrieve data from a local host, it is recommended to use the actual machine name as the data will be tagged with a "Node" tag with the value of this parameter as the value. diff --git a/plugins/inputs/win_perf_counters/kernel32.go b/plugins/inputs/win_perf_counters/kernel32.go index 4c1ddbfab0b01..9cdadedc873bd 100644 --- a/plugins/inputs/win_perf_counters/kernel32.go +++ b/plugins/inputs/win_perf_counters/kernel32.go @@ -34,7 +34,6 @@ package win_perf_counters import ( "syscall" - "unsafe" ) type SYSTEMTIME struct { @@ -72,44 +71,3 @@ func init() { krn_LocalFileTimeToFileTime = libkrnDll.MustFindProc("LocalFileTimeToFileTime") krn_WideCharToMultiByte = libkrnDll.MustFindProc("WideCharToMultiByte") } - -// CURRENTLY UNUSED: But may be useful in the future -// -// 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) { - 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 -} diff --git a/plugins/inputs/win_perf_counters/pdh.go b/plugins/inputs/win_perf_counters/pdh.go index cc515ec91fa36..ea85c4c38cb5f 100644 --- a/plugins/inputs/win_perf_counters/pdh.go +++ b/plugins/inputs/win_perf_counters/pdh.go @@ -202,7 +202,6 @@ func init() { libpdhDll = syscall.MustLoadDLL("pdh.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") @@ -216,19 +215,6 @@ func init() { pdh_ValidatePathW = libpdhDll.MustFindProc("PdhValidatePathW") } -// Connects to the specified machine -// -// Originally used to connect to a computer for querying performnce counter data, but was unnecessary, and -// actually slower to query the data using the call. Still a useful call for the future? -// -// Currently unused. But may be useful in the future? -// -func PdhConnectMachine(szMachineName string) uint32 { - 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 // function PdhAddEnglishCounter instead. hQuery is the query handle, which has been fetched by PdhOpenQuery. // szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version). diff --git a/plugins/inputs/win_perf_counters/pdh_386.go b/plugins/inputs/win_perf_counters/pdh_386.go index 1d1ee517b2f0a..0a6c43457b77a 100644 --- a/plugins/inputs/win_perf_counters/pdh_386.go +++ b/plugins/inputs/win_perf_counters/pdh_386.go @@ -35,14 +35,14 @@ 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 + padding [4]byte 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 + padding [4]byte LargeValue int64 }