chore: Fix linter findings for `revive:exported` in `plugins/inputs/win*` (#16427)

This commit is contained in:
Paweł Żak 2025-01-27 19:34:59 +01:00 committed by GitHub
parent b074f0893d
commit 25de05a8fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1012 additions and 1067 deletions

View File

@ -1,15 +1,12 @@
//go:build windows //go:build windows
// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog package win_eventlog
// Event is the event entry representation // event is the event entry representation
// Only the most common elements are processed, human-readable data is rendered in Message // Only the most common elements are processed, human-readable data is rendered in Message
// More info on schema, if there will be need to add more: // More info on schema, if there will be need to add more:
// https://docs.microsoft.com/en-us/windows/win32/wes/eventschema-elements // https://docs.microsoft.com/en-us/windows/win32/wes/eventschema-elements
type Event struct { type event struct {
Source provider `xml:"System>Provider"` Source provider `xml:"System>Provider"`
EventID int `xml:"System>EventID"` EventID int `xml:"System>EventID"`
Version int `xml:"System>Version"` Version int `xml:"System>Version"`
@ -44,7 +41,7 @@ type eventData struct {
InnerXML []byte `xml:",innerxml"` InnerXML []byte `xml:",innerxml"`
} }
// provider is the Event provider information // provider is the event provider information
type provider struct { type provider struct {
Name string `xml:"Name,attr"` Name string `xml:"Name,attr"`
} }
@ -55,24 +52,24 @@ type correlation struct {
RelatedActivityID string `xml:"RelatedActivityID,attr"` RelatedActivityID string `xml:"RelatedActivityID,attr"`
} }
// execution Info for Event // execution Info for event
type execution struct { type execution struct {
ProcessID uint32 `xml:"ProcessID,attr"` ProcessID uint32 `xml:"ProcessID,attr"`
ThreadID uint32 `xml:"ThreadID,attr"` ThreadID uint32 `xml:"ThreadID,attr"`
ProcessName string ProcessName string
} }
// security Data for Event // security Data for event
type security struct { type security struct {
UserID string `xml:"UserID,attr"` UserID string `xml:"UserID,attr"`
} }
// timeCreated field for Event // timeCreated field for event
type timeCreated struct { type timeCreated struct {
SystemTime string `xml:"SystemTime,attr"` SystemTime string `xml:"SystemTime,attr"`
} }
// renderingInfo is provided for events forwarded by Windows Event Collector // renderingInfo is provided for events forwarded by Windows event Collector
// see https://learn.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtformatmessage#parameters // see https://learn.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtformatmessage#parameters
type renderingInfo struct { type renderingInfo struct {
Message string `xml:"Message"` Message string `xml:"Message"`

View File

@ -1,41 +1,36 @@
//go:build windows //go:build windows
// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog package win_eventlog
import "syscall" import "syscall"
// Event log error codes. // event log error codes.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
const ( const (
//revive:disable:var-naming errInsufficientBuffer syscall.Errno = 122
ERROR_INSUFFICIENT_BUFFER syscall.Errno = 122 errNoMoreItems syscall.Errno = 259
ERROR_NO_MORE_ITEMS syscall.Errno = 259 errInvalidOperation syscall.Errno = 4317
ERROR_INVALID_OPERATION syscall.Errno = 4317
//revive:enable:var-naming
) )
// EvtSubscribeFlag defines the possible values that specify when to start subscribing to events. // evtSubscribeFlag defines the possible values that specify when to start subscribing to events.
type EvtSubscribeFlag uint32 type evtSubscribeFlag uint32
// EVT_SUBSCRIBE_FLAGS enumeration // EVT_SUBSCRIBE_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385588(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/aa385588(v=vs.85).aspx
const ( const (
EvtSubscribeToFutureEvents EvtSubscribeFlag = 1 evtSubscribeToFutureEvents evtSubscribeFlag = 1
EvtSubscribeStartAtOldestRecord EvtSubscribeFlag = 2 evtSubscribeStartAtOldestRecord evtSubscribeFlag = 2
EvtSubscribeStartAfterBookmark EvtSubscribeFlag = 3 evtSubscribeStartAfterBookmark evtSubscribeFlag = 3
) )
// EvtRenderFlag uint32 // evtRenderFlag uint32
type EvtRenderFlag uint32 type evtRenderFlag uint32
// EVT_RENDER_FLAGS enumeration // EVT_RENDER_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385563(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/aa385563(v=vs.85).aspx
const ( const (
// Render the event as an XML string. For details on the contents of the XML string, see the Event schema. // Render the event as an XML string. For details on the contents of the XML string, see the event schema.
EvtRenderEventXML EvtRenderFlag = 1 evtRenderEventXML evtRenderFlag = 1
// Render bookmark // Render bookmark
EvtRenderBookmark EvtRenderFlag = 2 evtRenderBookmark evtRenderFlag = 2
) )

View File

@ -1,8 +1,5 @@
//go:build windows //go:build windows
// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog package win_eventlog
import ( import (
@ -18,8 +15,8 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
// DecodeUTF16 to UTF8 bytes // decodeUTF16 to UTF8 bytes
func DecodeUTF16(b []byte) ([]byte, error) { func decodeUTF16(b []byte) ([]byte, error) {
if len(b)%2 != 0 { if len(b)%2 != 0 {
return nil, errors.New("must have even length byte slice") return nil, errors.New("must have even length byte slice")
} }
@ -41,9 +38,9 @@ func DecodeUTF16(b []byte) ([]byte, error) {
return ret.Bytes(), nil return ret.Bytes(), nil
} }
// GetFromSnapProcess finds information about process by the given pid // getFromSnapProcess finds information about process by the given pid
// Returns process name // Returns process name
func GetFromSnapProcess(pid uint32) (string, error) { func getFromSnapProcess(pid uint32) (string, error) {
snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, pid) snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, pid)
if err != nil { if err != nil {
return "", err return "", err
@ -74,8 +71,8 @@ type xmlnode struct {
Nodes []xmlnode `xml:",any"` Nodes []xmlnode `xml:",any"`
} }
// EventField for unique rendering // eventField for unique rendering
type EventField struct { type eventField struct {
Name string Name string
Value string Value string
} }
@ -88,11 +85,11 @@ func (n *xmlnode) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
return d.DecodeElement((*node)(n), &start) return d.DecodeElement((*node)(n), &start)
} }
// UnrollXMLFields extracts fields from xml data // unrollXMLFields extracts fields from xml data
func UnrollXMLFields(data []byte, fieldsUsage map[string]int, separator string) ([]EventField, map[string]int) { func unrollXMLFields(data []byte, fieldsUsage map[string]int, separator string) ([]eventField, map[string]int) {
buf := bytes.NewBuffer(data) buf := bytes.NewBuffer(data)
dec := xml.NewDecoder(buf) dec := xml.NewDecoder(buf)
var fields []EventField var fields []eventField
for { for {
var node xmlnode var node xmlnode
err := dec.Decode(&node) err := dec.Decode(&node)
@ -106,7 +103,7 @@ func UnrollXMLFields(data []byte, fieldsUsage map[string]int, separator string)
if len(innerText) > 0 { if len(innerText) > 0 {
valueName := strings.Join(parents, separator) valueName := strings.Join(parents, separator)
fieldsUsage[valueName]++ fieldsUsage[valueName]++
field := EventField{Name: valueName, Value: innerText} field := eventField{Name: valueName, Value: innerText}
fields = append(fields, field) fields = append(fields, field)
} }
return true return true
@ -132,18 +129,17 @@ func walkXML(nodes []xmlnode, parents []string, separator string, f func(xmlnode
} }
} }
// UniqueFieldNames forms unique field names // uniqueFieldNames forms unique field names by adding _<num> if there are several of them
// by adding _<num> if there are several of them func uniqueFieldNames(fields []eventField, fieldsUsage map[string]int, separator string) []eventField {
func UniqueFieldNames(fields []EventField, fieldsUsage map[string]int, separator string) []EventField {
var fieldsCounter = make(map[string]int, len(fields)) var fieldsCounter = make(map[string]int, len(fields))
fieldsUnique := make([]EventField, 0, len(fields)) fieldsUnique := make([]eventField, 0, len(fields))
for _, field := range fields { for _, field := range fields {
fieldName := field.Name fieldName := field.Name
if fieldsUsage[field.Name] > 1 { if fieldsUsage[field.Name] > 1 {
fieldsCounter[field.Name]++ fieldsCounter[field.Name]++
fieldName = fmt.Sprint(field.Name, separator, fieldsCounter[field.Name]) fieldName = fmt.Sprint(field.Name, separator, fieldsCounter[field.Name])
} }
fieldsUnique = append(fieldsUnique, EventField{ fieldsUnique = append(fieldsUnique, eventField{
Name: fieldName, Name: fieldName,
Value: field.Value, Value: field.Value,
}) })

View File

@ -1,8 +1,5 @@
//go:build windows //go:build windows
// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog package win_eventlog
import ( import (
@ -52,13 +49,13 @@ func TestDecodeUTF16(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := DecodeUTF16(tt.args.b) got, err := decodeUTF16(tt.args.b)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("DecodeUTF16() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("decodeUTF16() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("DecodeUTF16() = %v, want %v", got, tt.want) t.Errorf("decodeUTF16() = %v, want %v", got, tt.want)
} }
}) })
} }
@ -110,7 +107,7 @@ func TestUnrollXMLFields(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args args args args
want1 []EventField want1 []eventField
want2 map[string]int want2 map[string]int
}{ }{
{ {
@ -128,7 +125,7 @@ func TestUnrollXMLFields(t *testing.T) {
data: container.EventData.InnerXML, data: container.EventData.InnerXML,
fieldsUsage: map[string]int{}, fieldsUsage: map[string]int{},
}, },
want1: []EventField{ want1: []eventField{
{Name: "Data", Value: "2120-07-26T15:24:25Z"}, {Name: "Data", Value: "2120-07-26T15:24:25Z"},
{Name: "Data", Value: "RulesEngine"}, {Name: "Data", Value: "RulesEngine"},
{Name: "Data_Engine", Value: "RulesEngine"}, {Name: "Data_Engine", Value: "RulesEngine"},
@ -141,7 +138,7 @@ func TestUnrollXMLFields(t *testing.T) {
data: container.UserData.InnerXML, data: container.UserData.InnerXML,
fieldsUsage: map[string]int{}, fieldsUsage: map[string]int{},
}, },
want1: []EventField{ want1: []eventField{
{Name: "CbsPackageChangeState_IntendedPackageState", Value: "5111"}, {Name: "CbsPackageChangeState_IntendedPackageState", Value: "5111"},
{Name: "CbsPackageChangeState_ErrorCode_Code", Value: "0x0"}, {Name: "CbsPackageChangeState_ErrorCode_Code", Value: "0x0"},
}, },
@ -153,7 +150,7 @@ func TestUnrollXMLFields(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, got1 := UnrollXMLFields(tt.args.data, tt.args.fieldsUsage, "_") got, got1 := unrollXMLFields(tt.args.data, tt.args.fieldsUsage, "_")
if !reflect.DeepEqual(got, tt.want1) { if !reflect.DeepEqual(got, tt.want1) {
t.Errorf("ExtractFields() got = %v, want %v", got, tt.want1) t.Errorf("ExtractFields() got = %v, want %v", got, tt.want1)
} }
@ -166,25 +163,25 @@ func TestUnrollXMLFields(t *testing.T) {
func TestUniqueFieldNames(t *testing.T) { func TestUniqueFieldNames(t *testing.T) {
type args struct { type args struct {
fields []EventField fields []eventField
fieldsUsage map[string]int fieldsUsage map[string]int
} }
tests := []struct { tests := []struct {
name string name string
args args args args
want []EventField want []eventField
}{ }{
{ {
name: "Unique values", name: "Unique values",
args: args{ args: args{
fields: []EventField{ fields: []eventField{
{Name: "Data", Value: "2120-07-26T15:24:25Z"}, {Name: "Data", Value: "2120-07-26T15:24:25Z"},
{Name: "Data", Value: "RulesEngine"}, {Name: "Data", Value: "RulesEngine"},
{Name: "Engine", Value: "RulesEngine"}, {Name: "Engine", Value: "RulesEngine"},
}, },
fieldsUsage: map[string]int{"Data": 2, "Engine": 1}, fieldsUsage: map[string]int{"Data": 2, "Engine": 1},
}, },
want: []EventField{ want: []eventField{
{Name: "Data_1", Value: "2120-07-26T15:24:25Z"}, {Name: "Data_1", Value: "2120-07-26T15:24:25Z"},
{Name: "Data_2", Value: "RulesEngine"}, {Name: "Data_2", Value: "RulesEngine"},
{Name: "Engine", Value: "RulesEngine"}, {Name: "Engine", Value: "RulesEngine"},
@ -193,7 +190,7 @@ func TestUniqueFieldNames(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := UniqueFieldNames(tt.args.fields, tt.args.fieldsUsage, "_"); !reflect.DeepEqual(got, tt.want) { if got := uniqueFieldNames(tt.args.fields, tt.args.fieldsUsage, "_"); !reflect.DeepEqual(got, tt.want) {
t.Errorf("PrintFields() = %v, want %v", got, tt.want) t.Errorf("PrintFields() = %v, want %v", got, tt.want)
} }
}) })

View File

@ -1,9 +1,6 @@
//go:generate ../../../tools/readme_config_includer/generator //go:generate ../../../tools/readme_config_includer/generator
//go:build windows //go:build windows
// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog package win_eventlog
import ( import (
@ -28,7 +25,8 @@ import (
//go:embed sample.conf //go:embed sample.conf
var sampleConfig string var sampleConfig string
// WinEventLog config const bufferSize = 1 << 14
type WinEventLog struct { type WinEventLog struct {
Locale uint32 `toml:"locale"` Locale uint32 `toml:"locale"`
EventlogName string `toml:"eventlog_name"` EventlogName string `toml:"eventlog_name"`
@ -46,16 +44,14 @@ type WinEventLog struct {
ExcludeEmpty []string `toml:"exclude_empty"` ExcludeEmpty []string `toml:"exclude_empty"`
Log telegraf.Logger `toml:"-"` Log telegraf.Logger `toml:"-"`
subscription EvtHandle subscription evtHandle
subscriptionFlag EvtSubscribeFlag subscriptionFlag evtSubscribeFlag
bookmark EvtHandle bookmark evtHandle
tagFilter filter.Filter tagFilter filter.Filter
fieldFilter filter.Filter fieldFilter filter.Filter
fieldEmptyFilter filter.Filter fieldEmptyFilter filter.Filter
} }
const bufferSize = 1 << 14
func (*WinEventLog) SampleConfig() string { func (*WinEventLog) SampleConfig() string {
return sampleConfig return sampleConfig
} }
@ -66,16 +62,16 @@ func (w *WinEventLog) Init() error {
w.BatchSize = 5 w.BatchSize = 5
} }
w.subscriptionFlag = EvtSubscribeToFutureEvents w.subscriptionFlag = evtSubscribeToFutureEvents
if w.FromBeginning { if w.FromBeginning {
w.subscriptionFlag = EvtSubscribeStartAtOldestRecord w.subscriptionFlag = evtSubscribeStartAtOldestRecord
} }
if w.Query == "" { if w.Query == "" {
w.Query = "*" w.Query = "*"
} }
bookmark, err := _EvtCreateBookmark(nil) bookmark, err := evtCreateBookmark(nil)
if err != nil { if err != nil {
return err return err
} }
@ -96,7 +92,7 @@ func (w *WinEventLog) Init() error {
return nil return nil
} }
func (w *WinEventLog) Start(_ telegraf.Accumulator) error { func (w *WinEventLog) Start(telegraf.Accumulator) error {
subscription, err := w.evtSubscribe() subscription, err := w.evtSubscribe()
if err != nil { if err != nil {
return fmt.Errorf("subscription of Windows Event Log failed: %w", err) return fmt.Errorf("subscription of Windows Event Log failed: %w", err)
@ -107,11 +103,6 @@ func (w *WinEventLog) Start(_ telegraf.Accumulator) error {
return nil return nil
} }
func (w *WinEventLog) Stop() {
//nolint:errcheck // ending the subscription, error can be ignored
_ = _EvtClose(w.subscription)
}
func (w *WinEventLog) GetState() interface{} { func (w *WinEventLog) GetState() interface{} {
bookmarkXML, err := renderBookmark(w.bookmark) bookmarkXML, err := renderBookmark(w.bookmark)
if err != nil { if err != nil {
@ -132,22 +123,21 @@ func (w *WinEventLog) SetState(state interface{}) error {
return fmt.Errorf("conversion to pointer failed: %w", err) return fmt.Errorf("conversion to pointer failed: %w", err)
} }
bookmark, err := _EvtCreateBookmark(ptr) bookmark, err := evtCreateBookmark(ptr)
if err != nil { if err != nil {
return fmt.Errorf("creating bookmark failed: %w", err) return fmt.Errorf("creating bookmark failed: %w", err)
} }
w.bookmark = bookmark w.bookmark = bookmark
w.subscriptionFlag = EvtSubscribeStartAfterBookmark w.subscriptionFlag = evtSubscribeStartAfterBookmark
return nil return nil
} }
// Gather Windows Event Log entries
func (w *WinEventLog) Gather(acc telegraf.Accumulator) error { func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
for { for {
events, err := w.fetchEvents(w.subscription) events, err := w.fetchEvents(w.subscription)
if err != nil { if err != nil {
if errors.Is(err, ERROR_NO_MORE_ITEMS) { if errors.Is(err, errNoMoreItems) {
break break
} }
w.Log.Errorf("Error getting events: %v", err) w.Log.Errorf("Error getting events: %v", err)
@ -163,7 +153,7 @@ func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
event := events[i] event := events[i]
evt := reflect.ValueOf(&event).Elem() evt := reflect.ValueOf(&event).Elem()
timeStamp := time.Now() timeStamp := time.Now()
// Walk through all fields of Event struct to process System tags or fields // Walk through all fields of event struct to process System tags or fields
for i := 0; i < evt.NumField(); i++ { for i := 0; i < evt.NumField(); i++ {
fieldName := evt.Type().Field(i).Name fieldName := evt.Type().Field(i).Name
fieldType := evt.Field(i).Type().String() fieldType := evt.Field(i).Type().String()
@ -179,7 +169,7 @@ func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
fieldName = "ProcessID" fieldName = "ProcessID"
// Look up Process Name from pid // Look up Process Name from pid
if should, _ := w.shouldProcessField("ProcessName"); should { if should, _ := w.shouldProcessField("ProcessName"); should {
processName, err := GetFromSnapProcess(fieldValue) processName, err := getFromSnapProcess(fieldValue)
if err == nil { if err == nil {
computedValues["ProcessName"] = processName computedValues["ProcessName"] = processName
} }
@ -250,18 +240,18 @@ func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
} }
// Unroll additional XML // Unroll additional XML
var xmlFields []EventField var xmlFields []eventField
if w.ProcessUserData { if w.ProcessUserData {
fieldsUserData, xmlFieldsUsage := UnrollXMLFields(event.UserData.InnerXML, fieldsUsage, w.Separator) fieldsUserData, xmlFieldsUsage := unrollXMLFields(event.UserData.InnerXML, fieldsUsage, w.Separator)
xmlFields = append(xmlFields, fieldsUserData...) xmlFields = append(xmlFields, fieldsUserData...)
fieldsUsage = xmlFieldsUsage fieldsUsage = xmlFieldsUsage
} }
if w.ProcessEventData { if w.ProcessEventData {
fieldsEventData, xmlFieldsUsage := UnrollXMLFields(event.EventData.InnerXML, fieldsUsage, w.Separator) fieldsEventData, xmlFieldsUsage := unrollXMLFields(event.EventData.InnerXML, fieldsUsage, w.Separator)
xmlFields = append(xmlFields, fieldsEventData...) xmlFields = append(xmlFields, fieldsEventData...)
fieldsUsage = xmlFieldsUsage fieldsUsage = xmlFieldsUsage
} }
uniqueXMLFields := UniqueFieldNames(xmlFields, fieldsUsage, w.Separator) uniqueXMLFields := uniqueFieldNames(xmlFields, fieldsUsage, w.Separator)
for _, xmlField := range uniqueXMLFields { for _, xmlField := range uniqueXMLFields {
should, where := w.shouldProcessField(xmlField.Name) should, where := w.shouldProcessField(xmlField.Name)
if !should { if !should {
@ -282,6 +272,11 @@ func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
return nil return nil
} }
func (w *WinEventLog) Stop() {
//nolint:errcheck // ending the subscription, error can be ignored
_ = evtClose(w.subscription)
}
func (w *WinEventLog) shouldProcessField(field string) (should bool, list string) { func (w *WinEventLog) shouldProcessField(field string) (should bool, list string) {
if w.tagFilter != nil && w.tagFilter.Match(field) { if w.tagFilter != nil && w.tagFilter.Match(field) {
return true, "tags" return true, "tags"
@ -311,7 +306,7 @@ func (w *WinEventLog) shouldExcludeEmptyField(field, fieldType string, fieldValu
return false return false
} }
func (w *WinEventLog) evtSubscribe() (EvtHandle, error) { func (w *WinEventLog) evtSubscribe() (evtHandle, error) {
sigEvent, err := windows.CreateEvent(nil, 0, 0, nil) sigEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil { if err != nil {
return 0, err return 0, err
@ -328,11 +323,11 @@ func (w *WinEventLog) evtSubscribe() (EvtHandle, error) {
return 0, err return 0, err
} }
var bookmark EvtHandle var bookmark evtHandle
if w.subscriptionFlag == EvtSubscribeStartAfterBookmark { if w.subscriptionFlag == evtSubscribeStartAfterBookmark {
bookmark = w.bookmark bookmark = w.bookmark
} }
subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr, bookmark, 0, 0, w.subscriptionFlag) subsHandle, err := evtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr, bookmark, 0, 0, w.subscriptionFlag)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -340,13 +335,13 @@ func (w *WinEventLog) evtSubscribe() (EvtHandle, error) {
return subsHandle, nil return subsHandle, nil
} }
func (w *WinEventLog) fetchEventHandles(subsHandle EvtHandle) ([]EvtHandle, error) { func (w *WinEventLog) fetchEventHandles(subsHandle evtHandle) ([]evtHandle, error) {
var evtReturned uint32 var evtReturned uint32
eventHandles := make([]EvtHandle, w.BatchSize) eventHandles := make([]evtHandle, w.BatchSize)
if err := _EvtNext(subsHandle, w.BatchSize, &eventHandles[0], 0, 0, &evtReturned); err != nil { if err := evtNext(subsHandle, w.BatchSize, &eventHandles[0], 0, 0, &evtReturned); err != nil {
if errors.Is(err, ERROR_INVALID_OPERATION) && evtReturned == 0 { if errors.Is(err, errInvalidOperation) && evtReturned == 0 {
return nil, ERROR_NO_MORE_ITEMS return nil, errNoMoreItems
} }
return nil, err return nil, err
} }
@ -354,8 +349,8 @@ func (w *WinEventLog) fetchEventHandles(subsHandle EvtHandle) ([]EvtHandle, erro
return eventHandles[:evtReturned], nil return eventHandles[:evtReturned], nil
} }
func (w *WinEventLog) fetchEvents(subsHandle EvtHandle) ([]Event, error) { func (w *WinEventLog) fetchEvents(subsHandle evtHandle) ([]event, error) {
var events []Event var events []event
eventHandles, err := w.fetchEventHandles(subsHandle) eventHandles, err := w.fetchEventHandles(subsHandle)
if err != nil { if err != nil {
@ -370,27 +365,27 @@ func (w *WinEventLog) fetchEvents(subsHandle EvtHandle) ([]Event, error) {
if event, err := w.renderEvent(eventHandle); err == nil { if event, err := w.renderEvent(eventHandle); err == nil {
events = append(events, event) events = append(events, event)
} }
if err := _EvtUpdateBookmark(w.bookmark, eventHandle); err != nil && evterr == nil { if err := evtUpdateBookmark(w.bookmark, eventHandle); err != nil && evterr == nil {
evterr = err evterr = err
} }
if err := _EvtClose(eventHandle); err != nil && evterr == nil { if err := evtClose(eventHandle); err != nil && evterr == nil {
evterr = err evterr = err
} }
} }
return events, evterr return events, evterr
} }
func renderBookmark(bookmark EvtHandle) (string, error) { func renderBookmark(bookmark evtHandle) (string, error) {
var bufferUsed, propertyCount uint32 var bufferUsed, propertyCount uint32
buf := make([]byte, bufferSize) buf := make([]byte, bufferSize)
err := _EvtRender(0, bookmark, EvtRenderBookmark, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount) err := evtRender(0, bookmark, evtRenderBookmark, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount)
if err != nil { if err != nil {
return "", err return "", err
} }
x, err := DecodeUTF16(buf[:bufferUsed]) x, err := decodeUTF16(buf[:bufferUsed])
if err != nil { if err != nil {
return "", err return "", err
} }
@ -400,17 +395,17 @@ func renderBookmark(bookmark EvtHandle) (string, error) {
return string(x), err return string(x), err
} }
func (w *WinEventLog) renderEvent(eventHandle EvtHandle) (Event, error) { func (w *WinEventLog) renderEvent(eventHandle evtHandle) (event, error) {
var bufferUsed, propertyCount uint32 var bufferUsed, propertyCount uint32
buf := make([]byte, bufferSize) buf := make([]byte, bufferSize)
event := Event{} event := event{}
err := _EvtRender(0, eventHandle, EvtRenderEventXML, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount) err := evtRender(0, eventHandle, evtRenderEventXML, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount)
if err != nil { if err != nil {
return event, err return event, err
} }
eventXML, err := DecodeUTF16(buf[:bufferUsed]) eventXML, err := decodeUTF16(buf[:bufferUsed])
if err != nil { if err != nil {
return event, err return event, err
} }
@ -434,19 +429,19 @@ func (w *WinEventLog) renderEvent(eventHandle EvtHandle) (Event, error) {
return w.renderRemoteMessage(event) return w.renderRemoteMessage(event)
} }
func (w *WinEventLog) renderLocalMessage(event Event, eventHandle EvtHandle) (Event, error) { func (w *WinEventLog) renderLocalMessage(event event, eventHandle evtHandle) (event, error) {
publisherHandle, err := openPublisherMetadata(0, event.Source.Name, w.Locale) publisherHandle, err := openPublisherMetadata(0, event.Source.Name, w.Locale)
if err != nil { if err != nil {
return event, nil return event, nil
} }
defer _EvtClose(publisherHandle) //nolint:errcheck // Ignore error returned during Close defer evtClose(publisherHandle) //nolint:errcheck // Ignore error returned during Close
// Populating text values // Populating text values
keywords, err := formatEventString(EvtFormatMessageKeyword, eventHandle, publisherHandle) keywords, err := formatEventString(evtFormatMessageKeyword, eventHandle, publisherHandle)
if err == nil { if err == nil {
event.Keywords = keywords event.Keywords = keywords
} }
message, err := formatEventString(EvtFormatMessageEvent, eventHandle, publisherHandle) message, err := formatEventString(evtFormatMessageEvent, eventHandle, publisherHandle)
if err == nil { if err == nil {
if w.OnlyFirstLineOfMessage { if w.OnlyFirstLineOfMessage {
scanner := bufio.NewScanner(strings.NewReader(message)) scanner := bufio.NewScanner(strings.NewReader(message))
@ -455,22 +450,22 @@ func (w *WinEventLog) renderLocalMessage(event Event, eventHandle EvtHandle) (Ev
} }
event.Message = message event.Message = message
} }
level, err := formatEventString(EvtFormatMessageLevel, eventHandle, publisherHandle) level, err := formatEventString(evtFormatMessageLevel, eventHandle, publisherHandle)
if err == nil { if err == nil {
event.LevelText = level event.LevelText = level
} }
task, err := formatEventString(EvtFormatMessageTask, eventHandle, publisherHandle) task, err := formatEventString(evtFormatMessageTask, eventHandle, publisherHandle)
if err == nil { if err == nil {
event.TaskText = task event.TaskText = task
} }
opcode, err := formatEventString(EvtFormatMessageOpcode, eventHandle, publisherHandle) opcode, err := formatEventString(evtFormatMessageOpcode, eventHandle, publisherHandle)
if err == nil { if err == nil {
event.OpcodeText = opcode event.OpcodeText = opcode
} }
return event, nil return event, nil
} }
func (w *WinEventLog) renderRemoteMessage(event Event) (Event, error) { func (w *WinEventLog) renderRemoteMessage(event event) (event, error) {
// Populating text values from RenderingInfo part of the XML // Populating text values from RenderingInfo part of the XML
if len(event.RenderingInfo.Keywords) > 0 { if len(event.RenderingInfo.Keywords) > 0 {
event.Keywords = strings.Join(event.RenderingInfo.Keywords, ",") event.Keywords = strings.Join(event.RenderingInfo.Keywords, ",")
@ -496,11 +491,11 @@ func (w *WinEventLog) renderRemoteMessage(event Event) (Event, error) {
return event, nil return event, nil
} }
func formatEventString(messageFlag EvtFormatMessageFlag, eventHandle, publisherHandle EvtHandle) (string, error) { func formatEventString(messageFlag evtFormatMessageFlag, eventHandle, publisherHandle evtHandle) (string, error) {
var bufferUsed uint32 var bufferUsed uint32
err := _EvtFormatMessage(publisherHandle, eventHandle, 0, 0, 0, messageFlag, err := evtFormatMessage(publisherHandle, eventHandle, 0, 0, 0, messageFlag,
0, nil, &bufferUsed) 0, nil, &bufferUsed)
if err != nil && !errors.Is(err, ERROR_INSUFFICIENT_BUFFER) { if err != nil && !errors.Is(err, errInsufficientBuffer) {
return "", err return "", err
} }
@ -513,20 +508,20 @@ func formatEventString(messageFlag EvtFormatMessageFlag, eventHandle, publisherH
buffer := make([]byte, bufferUsed) buffer := make([]byte, bufferUsed)
bufferUsed = 0 bufferUsed = 0
err = _EvtFormatMessage(publisherHandle, eventHandle, 0, 0, 0, messageFlag, err = evtFormatMessage(publisherHandle, eventHandle, 0, 0, 0, messageFlag,
uint32(len(buffer)/2), &buffer[0], &bufferUsed) uint32(len(buffer)/2), &buffer[0], &bufferUsed)
bufferUsed *= 2 bufferUsed *= 2
if err != nil { if err != nil {
return "", err return "", err
} }
result, err := DecodeUTF16(buffer[:bufferUsed]) result, err := decodeUTF16(buffer[:bufferUsed])
if err != nil { if err != nil {
return "", err return "", err
} }
var out string var out string
if messageFlag == EvtFormatMessageKeyword { if messageFlag == evtFormatMessageKeyword {
// Keywords are returned as array of a zero-terminated strings // Keywords are returned as array of a zero-terminated strings
splitZero := func(c rune) bool { return c == '\x00' } splitZero := func(c rune) bool { return c == '\x00' }
eventKeywords := strings.FieldsFunc(string(result), splitZero) eventKeywords := strings.FieldsFunc(string(result), splitZero)
@ -540,18 +535,14 @@ func formatEventString(messageFlag EvtFormatMessageFlag, eventHandle, publisherH
} }
// openPublisherMetadata opens a handle to the publisher's metadata. Close must // openPublisherMetadata opens a handle to the publisher's metadata. Close must
// be called on returned EvtHandle when finished with the handle. // be called on returned evtHandle when finished with the handle.
func openPublisherMetadata( func openPublisherMetadata(session evtHandle, publisherName string, lang uint32) (evtHandle, error) {
session EvtHandle,
publisherName string,
lang uint32,
) (EvtHandle, error) {
p, err := syscall.UTF16PtrFromString(publisherName) p, err := syscall.UTF16PtrFromString(publisherName)
if err != nil { if err != nil {
return 0, err return 0, err
} }
h, err := _EvtOpenPublisherMetadata(session, p, nil, lang, 0) h, err := evtOpenPublisherMetadata(session, p, nil, lang, 0)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -17,14 +17,14 @@ type WinEventLog struct {
Log telegraf.Logger `toml:"-"` Log telegraf.Logger `toml:"-"`
} }
func (*WinEventLog) SampleConfig() string { return sampleConfig }
func (w *WinEventLog) Init() error { func (w *WinEventLog) Init() error {
w.Log.Warn("current platform is not supported") w.Log.Warn("Current platform is not supported")
return nil return nil
} }
func (*WinEventLog) SampleConfig() string { return sampleConfig }
func (*WinEventLog) Gather(_ telegraf.Accumulator) error { return nil } func (*WinEventLog) Gather(telegraf.Accumulator) error { return nil }
func (*WinEventLog) Start(_ telegraf.Accumulator) error { return nil }
func (*WinEventLog) Stop() {}
func init() { func init() {
inputs.Add("win_eventlog", func() telegraf.Input { inputs.Add("win_eventlog", func() telegraf.Input {

View File

@ -1,8 +1,5 @@
//go:build windows //go:build windows
// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog package win_eventlog
import ( import (

View File

@ -1,8 +1,5 @@
//go:build windows //go:build windows
// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog package win_eventlog
import ( import (
@ -14,50 +11,45 @@ import (
var _ unsafe.Pointer var _ unsafe.Pointer
// EvtHandle uintptr // evtHandle uintptr
type EvtHandle uintptr type evtHandle uintptr
// Do the interface allocations only once for common // Do the interface allocations only once for common errno values.
// Errno values.
const ( const (
//revive:disable-next-line:var-naming errnoErrorIOPending = 997
errnoERROR_IO_PENDING = 997
) )
var ( var (
//revive:disable-next-line:var-naming errErrorIOPending error = syscall.Errno(errnoErrorIOPending)
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
) )
// EvtFormatMessageFlag defines the values that specify the message string from // evtFormatMessageFlag defines the values that specify the message string from the event to format.
// the event to format. type evtFormatMessageFlag uint32
type EvtFormatMessageFlag uint32
// EVT_FORMAT_MESSAGE_FLAGS enumeration // EVT_FORMAT_MESSAGE_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385525(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/aa385525(v=vs.85).aspx
const ( const (
// EvtFormatMessageEvent - Format the event's message string. // evtFormatMessageEvent - Format the event's message string.
EvtFormatMessageEvent EvtFormatMessageFlag = iota + 1 evtFormatMessageEvent evtFormatMessageFlag = iota + 1
// EvtFormatMessageLevel - Format the message string of the level specified in the event. // evtFormatMessageLevel - Format the message string of the level specified in the event.
EvtFormatMessageLevel evtFormatMessageLevel
// EvtFormatMessageTask - Format the message string of the task specified in the event. // evtFormatMessageTask - Format the message string of the task specified in the event.
EvtFormatMessageTask evtFormatMessageTask
// EvtFormatMessageOpcode - Format the message string of the task specified in the event. // evtFormatMessageOpcode - Format the message string of the task specified in the event.
EvtFormatMessageOpcode evtFormatMessageOpcode
// EvtFormatMessageKeyword - Format the message string of the keywords specified in the event. If the // evtFormatMessageKeyword - Format the message string of the keywords specified in the event. If the
// event specifies multiple keywords, the formatted string is a list of null-terminated strings. // event specifies multiple keywords, the formatted string is a list of null-terminated strings.
// Increment through the strings until your pointer points past the end of the used buffer. // Increment through the strings until your pointer points past the end of the used buffer.
EvtFormatMessageKeyword evtFormatMessageKeyword
) )
// errnoErr returns common boxed Errno values, to prevent // errnoErr returns common boxed Errno values, to prevent allocations at runtime.
// allocations at runtime.
func errnoErr(e syscall.Errno) error { func errnoErr(e syscall.Errno) error {
switch e { switch e {
case 0: case 0:
return nil return nil
case errnoERROR_IO_PENDING: case errnoErrorIOPending:
return errERROR_IO_PENDING return errErrorIOPending
} }
return e return e
@ -77,16 +69,16 @@ var (
) )
//nolint:revive //argument-limit conditionally more arguments allowed //nolint:revive //argument-limit conditionally more arguments allowed
func _EvtSubscribe( func evtSubscribe(
session EvtHandle, session evtHandle,
signalEvent uintptr, signalEvent uintptr,
channelPath *uint16, channelPath *uint16,
query *uint16, query *uint16,
bookmark EvtHandle, bookmark evtHandle,
context uintptr, context uintptr,
callback syscall.Handle, callback syscall.Handle,
flags EvtSubscribeFlag, flags evtSubscribeFlag,
) (EvtHandle, error) { ) (evtHandle, error) {
r0, _, e1 := syscall.SyscallN( r0, _, e1 := syscall.SyscallN(
procEvtSubscribe.Addr(), procEvtSubscribe.Addr(),
uintptr(session), uintptr(session),
@ -100,7 +92,7 @@ func _EvtSubscribe(
) )
var err error var err error
handle := EvtHandle(r0) handle := evtHandle(r0)
if handle == 0 { if handle == 0 {
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
@ -112,10 +104,10 @@ func _EvtSubscribe(
} }
//nolint:revive //argument-limit conditionally more arguments allowed //nolint:revive //argument-limit conditionally more arguments allowed
func _EvtRender( func evtRender(
context EvtHandle, context evtHandle,
fragment EvtHandle, fragment evtHandle,
flags EvtRenderFlag, flags evtRenderFlag,
bufferSize uint32, bufferSize uint32,
buffer *byte, buffer *byte,
bufferUsed *uint32, bufferUsed *uint32,
@ -143,7 +135,7 @@ func _EvtRender(
return err return err
} }
func _EvtClose(object EvtHandle) error { func evtClose(object evtHandle) error {
r1, _, e1 := syscall.SyscallN(procEvtClose.Addr(), uintptr(object)) r1, _, e1 := syscall.SyscallN(procEvtClose.Addr(), uintptr(object))
var err error var err error
if r1 == 0 { if r1 == 0 {
@ -156,7 +148,7 @@ func _EvtClose(object EvtHandle) error {
return err return err
} }
func _EvtNext(resultSet EvtHandle, eventArraySize uint32, eventArray *EvtHandle, timeout, flags uint32, numReturned *uint32) error { func evtNext(resultSet evtHandle, eventArraySize uint32, eventArray *evtHandle, timeout, flags uint32, numReturned *uint32) error {
r1, _, e1 := syscall.SyscallN( r1, _, e1 := syscall.SyscallN(
procEvtNext.Addr(), procEvtNext.Addr(),
uintptr(resultSet), uintptr(resultSet),
@ -179,13 +171,13 @@ func _EvtNext(resultSet EvtHandle, eventArraySize uint32, eventArray *EvtHandle,
} }
//nolint:revive //argument-limit conditionally more arguments allowed //nolint:revive //argument-limit conditionally more arguments allowed
func _EvtFormatMessage( func evtFormatMessage(
publisherMetadata EvtHandle, publisherMetadata evtHandle,
event EvtHandle, event evtHandle,
messageID uint32, messageID uint32,
valueCount uint32, valueCount uint32,
values uintptr, values uintptr,
flags EvtFormatMessageFlag, flags evtFormatMessageFlag,
bufferSize uint32, bufferSize uint32,
buffer *byte, buffer *byte,
bufferUsed *uint32, bufferUsed *uint32,
@ -214,7 +206,7 @@ func _EvtFormatMessage(
return err return err
} }
func _EvtOpenPublisherMetadata(session EvtHandle, publisherIdentity, logFilePath *uint16, locale, flags uint32) (EvtHandle, error) { func evtOpenPublisherMetadata(session evtHandle, publisherIdentity, logFilePath *uint16, locale, flags uint32) (evtHandle, error) {
r0, _, e1 := syscall.SyscallN( r0, _, e1 := syscall.SyscallN(
procEvtOpenPublisherMetadata.Addr(), procEvtOpenPublisherMetadata.Addr(),
uintptr(session), uintptr(session),
@ -225,7 +217,7 @@ func _EvtOpenPublisherMetadata(session EvtHandle, publisherIdentity, logFilePath
) )
var err error var err error
handle := EvtHandle(r0) handle := evtHandle(r0)
if handle == 0 { if handle == 0 {
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
@ -236,10 +228,10 @@ func _EvtOpenPublisherMetadata(session EvtHandle, publisherIdentity, logFilePath
return handle, err return handle, err
} }
func _EvtCreateBookmark(bookmarkXML *uint16) (EvtHandle, error) { func evtCreateBookmark(bookmarkXML *uint16) (evtHandle, error) {
//nolint:gosec // G103: Valid use of unsafe call to pass bookmarkXML //nolint:gosec // G103: Valid use of unsafe call to pass bookmarkXML
r0, _, e1 := syscall.SyscallN(procEvtCreateBookmark.Addr(), uintptr(unsafe.Pointer(bookmarkXML))) r0, _, e1 := syscall.SyscallN(procEvtCreateBookmark.Addr(), uintptr(unsafe.Pointer(bookmarkXML)))
handle := EvtHandle(r0) handle := evtHandle(r0)
if handle != 0 { if handle != 0 {
return handle, nil return handle, nil
} }
@ -249,7 +241,7 @@ func _EvtCreateBookmark(bookmarkXML *uint16) (EvtHandle, error) {
return handle, syscall.EINVAL return handle, syscall.EINVAL
} }
func _EvtUpdateBookmark(bookmark, event EvtHandle) error { func evtUpdateBookmark(bookmark, event evtHandle) error {
r0, _, e1 := syscall.SyscallN(procEvtUpdateBookmark.Addr(), uintptr(bookmark), uintptr(event)) r0, _, e1 := syscall.SyscallN(procEvtUpdateBookmark.Addr(), uintptr(bookmark), uintptr(event))
if r0 != 0 { if r0 != 0 {
return nil return nil

View File

@ -44,215 +44,214 @@ import (
// Error codes // Error codes
const ( const (
ErrorSuccess = 0 errorSuccess = 0
ErrorFailure = 1 errorFailure = 1
ErrorInvalidFunction = 1 errorInvalidFunction = 1
EpochDifferenceMicros int64 = 11644473600000000 epochDifferenceMicros int64 = 11644473600000000
) )
type ( type (
HANDLE uintptr handle uintptr
) )
// PDH error codes, which can be returned by all Pdh* functions. Taken from mingw-w64 pdhmsg.h // PDH error codes, which can be returned by all Pdh* functions. Taken from mingw-w64 pdhmsg.h
const ( const (
PdhCstatusValidData = 0x00000000 // The returned data is valid. pdhCstatusValidData = 0x00000000 // The returned data is valid.
PdhCstatusNewData = 0x00000001 // The return data value is valid and different from the last sample. pdhCstatusNewData = 0x00000001 // The return data value is valid and different from the last sample.
PdhCstatusNoMachine = 0x800007D0 // Unable to connect to the specified computer, or the computer is offline. pdhCstatusNoMachine = 0x800007D0 // Unable to connect to the specified computer, or the computer is offline.
PdhCstatusNoInstance = 0x800007D1 pdhCstatusNoInstance = 0x800007D1
PdhMoreData = 0x800007D2 // The PdhGetFormattedCounterArray* function can return this if there's 'more data to be displayed'. pdhMoreData = 0x800007D2 // The pdhGetFormattedCounterArray* function can return this if there's 'more data to be displayed'.
PdhCstatusItemNotValidated = 0x800007D3 pdhCstatusItemNotValidated = 0x800007D3
PdhRetry = 0x800007D4 pdhRetry = 0x800007D4
PdhNoData = 0x800007D5 // The query does not currently contain any counters (for example, limited access) pdhNoData = 0x800007D5 // The query does not currently contain any counters (for example, limited access)
PdhCalcNegativeDenominator = 0x800007D6 pdhCalcNegativeDenominator = 0x800007D6
PdhCalcNegativeTimebase = 0x800007D7 pdhCalcNegativeTimebase = 0x800007D7
PdhCalcNegativeValue = 0x800007D8 pdhCalcNegativeValue = 0x800007D8
PdhDialogCancelled = 0x800007D9 pdhDialogCancelled = 0x800007D9
PdhEndOfLogFile = 0x800007DA pdhEndOfLogFile = 0x800007DA
PdhAsyncQueryTimeout = 0x800007DB pdhAsyncQueryTimeout = 0x800007DB
PdhCannotSetDefaultRealtimeDatasource = 0x800007DC pdhCannotSetDefaultRealtimeDatasource = 0x800007DC
PdhCstatusNoObject = 0xC0000BB8 pdhCstatusNoObject = 0xC0000BB8
PdhCstatusNoCounter = 0xC0000BB9 // The specified counter could not be found. pdhCstatusNoCounter = 0xC0000BB9 // The specified counter could not be found.
PdhCstatusInvalidData = 0xC0000BBA // The counter was successfully found, but the data returned is not valid. pdhCstatusInvalidData = 0xC0000BBA // The counter was successfully found, but the data returned is not valid.
PdhMemoryAllocationFailure = 0xC0000BBB pdhMemoryAllocationFailure = 0xC0000BBB
PdhInvalidHandle = 0xC0000BBC pdhInvalidHandle = 0xC0000BBC
PdhInvalidArgument = 0xC0000BBD // Required argument is missing or incorrect. pdhInvalidArgument = 0xC0000BBD // Required argument is missing or incorrect.
PdhFunctionNotFound = 0xC0000BBE pdhFunctionNotFound = 0xC0000BBE
PdhCstatusNoCountername = 0xC0000BBF pdhCstatusNoCountername = 0xC0000BBF
PdhCstatusBadCountername = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path. pdhCstatusBadCountername = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path.
PdhInvalidBuffer = 0xC0000BC1 pdhInvalidBuffer = 0xC0000BC1
PdhInsufficientBuffer = 0xC0000BC2 pdhInsufficientBuffer = 0xC0000BC2
PdhCannotConnectMachine = 0xC0000BC3 pdhCannotConnectMachine = 0xC0000BC3
PdhInvalidPath = 0xC0000BC4 pdhInvalidPath = 0xC0000BC4
PdhInvalidInstance = 0xC0000BC5 pdhInvalidInstance = 0xC0000BC5
PdhInvalidData = 0xC0000BC6 // specified counter does not contain valid data or a successful status code. pdhInvalidData = 0xC0000BC6 // specified counter does not contain valid data or a successful status code.
PdhNoDialogData = 0xC0000BC7 pdhNoDialogData = 0xC0000BC7
PdhCannotReadNameStrings = 0xC0000BC8 pdhCannotReadNameStrings = 0xC0000BC8
PdhLogFileCreateError = 0xC0000BC9 pdhLogFileCreateError = 0xC0000BC9
PdhLogFileOpenError = 0xC0000BCA pdhLogFileOpenError = 0xC0000BCA
PdhLogTypeNotFound = 0xC0000BCB pdhLogTypeNotFound = 0xC0000BCB
PdhNoMoreData = 0xC0000BCC pdhNoMoreData = 0xC0000BCC
PdhEntryNotInLogFile = 0xC0000BCD pdhEntryNotInLogFile = 0xC0000BCD
PdhDataSourceIsLogFile = 0xC0000BCE pdhDataSourceIsLogFile = 0xC0000BCE
PdhDataSourceIsRealTime = 0xC0000BCF pdhDataSourceIsRealTime = 0xC0000BCF
PdhUnableReadLogHeader = 0xC0000BD0 pdhUnableReadLogHeader = 0xC0000BD0
PdhFileNotFound = 0xC0000BD1 pdhFileNotFound = 0xC0000BD1
PdhFileAlreadyExists = 0xC0000BD2 pdhFileAlreadyExists = 0xC0000BD2
PdhNotImplemented = 0xC0000BD3 pdhNotImplemented = 0xC0000BD3
PdhStringNotFound = 0xC0000BD4 pdhStringNotFound = 0xC0000BD4
PdhUnableMapNameFiles = 0x80000BD5 pdhUnableMapNameFiles = 0x80000BD5
PdhUnknownLogFormat = 0xC0000BD6 pdhUnknownLogFormat = 0xC0000BD6
PdhUnknownLogsvcCommand = 0xC0000BD7 pdhUnknownLogsvcCommand = 0xC0000BD7
PdhLogsvcQueryNotFound = 0xC0000BD8 pdhLogsvcQueryNotFound = 0xC0000BD8
PdhLogsvcNotOpened = 0xC0000BD9 pdhLogsvcNotOpened = 0xC0000BD9
PdhWbemError = 0xC0000BDA pdhWbemError = 0xC0000BDA
PdhAccessDenied = 0xC0000BDB pdhAccessDenied = 0xC0000BDB
PdhLogFileTooSmall = 0xC0000BDC pdhLogFileTooSmall = 0xC0000BDC
PdhInvalidDatasource = 0xC0000BDD pdhInvalidDatasource = 0xC0000BDD
PdhInvalidSqldb = 0xC0000BDE pdhInvalidSqldb = 0xC0000BDE
PdhNoCounters = 0xC0000BDF pdhNoCounters = 0xC0000BDF
PdhSQLAllocFailed = 0xC0000BE0 pdhSQLAllocFailed = 0xC0000BE0
PdhSQLAllocconFailed = 0xC0000BE1 pdhSQLAllocconFailed = 0xC0000BE1
PdhSQLExecDirectFailed = 0xC0000BE2 pdhSQLExecDirectFailed = 0xC0000BE2
PdhSQLFetchFailed = 0xC0000BE3 pdhSQLFetchFailed = 0xC0000BE3
PdhSQLRowcountFailed = 0xC0000BE4 pdhSQLRowcountFailed = 0xC0000BE4
PdhSQLMoreResultsFailed = 0xC0000BE5 pdhSQLMoreResultsFailed = 0xC0000BE5
PdhSQLConnectFailed = 0xC0000BE6 pdhSQLConnectFailed = 0xC0000BE6
PdhSQLBindFailed = 0xC0000BE7 pdhSQLBindFailed = 0xC0000BE7
PdhCannotConnectWmiServer = 0xC0000BE8 pdhCannotConnectWmiServer = 0xC0000BE8
PdhPlaCollectionAlreadyRunning = 0xC0000BE9 pdhPlaCollectionAlreadyRunning = 0xC0000BE9
PdhPlaErrorScheduleOverlap = 0xC0000BEA pdhPlaErrorScheduleOverlap = 0xC0000BEA
PdhPlaCollectionNotFound = 0xC0000BEB pdhPlaCollectionNotFound = 0xC0000BEB
PdhPlaErrorScheduleElapsed = 0xC0000BEC pdhPlaErrorScheduleElapsed = 0xC0000BEC
PdhPlaErrorNostart = 0xC0000BED pdhPlaErrorNostart = 0xC0000BED
PdhPlaErrorAlreadyExists = 0xC0000BEE pdhPlaErrorAlreadyExists = 0xC0000BEE
PdhPlaErrorTypeMismatch = 0xC0000BEF pdhPlaErrorTypeMismatch = 0xC0000BEF
PdhPlaErrorFilepath = 0xC0000BF0 pdhPlaErrorFilepath = 0xC0000BF0
PdhPlaServiceError = 0xC0000BF1 pdhPlaServiceError = 0xC0000BF1
PdhPlaValidationError = 0xC0000BF2 pdhPlaValidationError = 0xC0000BF2
PdhPlaValidationWarning = 0x80000BF3 pdhPlaValidationWarning = 0x80000BF3
PdhPlaErrorNameTooLong = 0xC0000BF4 pdhPlaErrorNameTooLong = 0xC0000BF4
PdhInvalidSQLLogFormat = 0xC0000BF5 pdhInvalidSQLLogFormat = 0xC0000BF5
PdhCounterAlreadyInQuery = 0xC0000BF6 pdhCounterAlreadyInQuery = 0xC0000BF6
PdhBinaryLogCorrupt = 0xC0000BF7 pdhBinaryLogCorrupt = 0xC0000BF7
PdhLogSampleTooSmall = 0xC0000BF8 pdhLogSampleTooSmall = 0xC0000BF8
PdhOsLaterVersion = 0xC0000BF9 pdhOsLaterVersion = 0xC0000BF9
PdhOsEarlierVersion = 0xC0000BFA pdhOsEarlierVersion = 0xC0000BFA
PdhIncorrectAppendTime = 0xC0000BFB pdhIncorrectAppendTime = 0xC0000BFB
PdhUnmatchedAppendCounter = 0xC0000BFC pdhUnmatchedAppendCounter = 0xC0000BFC
PdhSQLAlterDetailFailed = 0xC0000BFD pdhSQLAlterDetailFailed = 0xC0000BFD
PdhQueryPerfDataTimeout = 0xC0000BFE pdhQueryPerfDataTimeout = 0xC0000BFE
) )
var PDHErrors = map[uint32]string{ var pdhErrors = map[uint32]string{
PdhCstatusValidData: "PDH_CSTATUS_VALID_DATA", pdhCstatusValidData: "PDH_CSTATUS_VALID_DATA",
PdhCstatusNewData: "PDH_CSTATUS_NEW_DATA", pdhCstatusNewData: "PDH_CSTATUS_NEW_DATA",
PdhCstatusNoMachine: "PDH_CSTATUS_NO_MACHINE", pdhCstatusNoMachine: "PDH_CSTATUS_NO_MACHINE",
PdhCstatusNoInstance: "PDH_CSTATUS_NO_INSTANCE", pdhCstatusNoInstance: "PDH_CSTATUS_NO_INSTANCE",
PdhMoreData: "PDH_MORE_DATA", pdhMoreData: "PDH_MORE_DATA",
PdhCstatusItemNotValidated: "PDH_CSTATUS_ITEM_NOT_VALIDATED", pdhCstatusItemNotValidated: "PDH_CSTATUS_ITEM_NOT_VALIDATED",
PdhRetry: "PDH_RETRY", pdhRetry: "PDH_RETRY",
PdhNoData: "PDH_NO_DATA", pdhNoData: "PDH_NO_DATA",
PdhCalcNegativeDenominator: "PDH_CALC_NEGATIVE_DENOMINATOR", pdhCalcNegativeDenominator: "PDH_CALC_NEGATIVE_DENOMINATOR",
PdhCalcNegativeTimebase: "PDH_CALC_NEGATIVE_TIMEBASE", pdhCalcNegativeTimebase: "PDH_CALC_NEGATIVE_TIMEBASE",
PdhCalcNegativeValue: "PDH_CALC_NEGATIVE_VALUE", pdhCalcNegativeValue: "PDH_CALC_NEGATIVE_VALUE",
PdhDialogCancelled: "PDH_DIALOG_CANCELLED", pdhDialogCancelled: "PDH_DIALOG_CANCELLED",
PdhEndOfLogFile: "PDH_END_OF_LOG_FILE", pdhEndOfLogFile: "PDH_END_OF_LOG_FILE",
PdhAsyncQueryTimeout: "PDH_ASYNC_QUERY_TIMEOUT", pdhAsyncQueryTimeout: "PDH_ASYNC_QUERY_TIMEOUT",
PdhCannotSetDefaultRealtimeDatasource: "PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE", pdhCannotSetDefaultRealtimeDatasource: "PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE",
PdhCstatusNoObject: "PDH_CSTATUS_NO_OBJECT", pdhCstatusNoObject: "PDH_CSTATUS_NO_OBJECT",
PdhCstatusNoCounter: "PDH_CSTATUS_NO_COUNTER", pdhCstatusNoCounter: "PDH_CSTATUS_NO_COUNTER",
PdhCstatusInvalidData: "PDH_CSTATUS_INVALID_DATA", pdhCstatusInvalidData: "PDH_CSTATUS_INVALID_DATA",
PdhMemoryAllocationFailure: "PDH_MEMORY_ALLOCATION_FAILURE", pdhMemoryAllocationFailure: "PDH_MEMORY_ALLOCATION_FAILURE",
PdhInvalidHandle: "PDH_INVALID_HANDLE", pdhInvalidHandle: "PDH_INVALID_HANDLE",
PdhInvalidArgument: "PDH_INVALID_ARGUMENT", pdhInvalidArgument: "PDH_INVALID_ARGUMENT",
PdhFunctionNotFound: "PDH_FUNCTION_NOT_FOUND", pdhFunctionNotFound: "PDH_FUNCTION_NOT_FOUND",
PdhCstatusNoCountername: "PDH_CSTATUS_NO_COUNTERNAME", pdhCstatusNoCountername: "PDH_CSTATUS_NO_COUNTERNAME",
PdhCstatusBadCountername: "PDH_CSTATUS_BAD_COUNTERNAME", pdhCstatusBadCountername: "PDH_CSTATUS_BAD_COUNTERNAME",
PdhInvalidBuffer: "PDH_INVALID_BUFFER", pdhInvalidBuffer: "PDH_INVALID_BUFFER",
PdhInsufficientBuffer: "PDH_INSUFFICIENT_BUFFER", pdhInsufficientBuffer: "PDH_INSUFFICIENT_BUFFER",
PdhCannotConnectMachine: "PDH_CANNOT_CONNECT_MACHINE", pdhCannotConnectMachine: "PDH_CANNOT_CONNECT_MACHINE",
PdhInvalidPath: "PDH_INVALID_PATH", pdhInvalidPath: "PDH_INVALID_PATH",
PdhInvalidInstance: "PDH_INVALID_INSTANCE", pdhInvalidInstance: "PDH_INVALID_INSTANCE",
PdhInvalidData: "PDH_INVALID_DATA", pdhInvalidData: "PDH_INVALID_DATA",
PdhNoDialogData: "PDH_NO_DIALOG_DATA", pdhNoDialogData: "PDH_NO_DIALOG_DATA",
PdhCannotReadNameStrings: "PDH_CANNOT_READ_NAME_STRINGS", pdhCannotReadNameStrings: "PDH_CANNOT_READ_NAME_STRINGS",
PdhLogFileCreateError: "PDH_LOG_FILE_CREATE_ERROR", pdhLogFileCreateError: "PDH_LOG_FILE_CREATE_ERROR",
PdhLogFileOpenError: "PDH_LOG_FILE_OPEN_ERROR", pdhLogFileOpenError: "PDH_LOG_FILE_OPEN_ERROR",
PdhLogTypeNotFound: "PDH_LOG_TYPE_NOT_FOUND", pdhLogTypeNotFound: "PDH_LOG_TYPE_NOT_FOUND",
PdhNoMoreData: "PDH_NO_MORE_DATA", pdhNoMoreData: "PDH_NO_MORE_DATA",
PdhEntryNotInLogFile: "PDH_ENTRY_NOT_IN_LOG_FILE", pdhEntryNotInLogFile: "PDH_ENTRY_NOT_IN_LOG_FILE",
PdhDataSourceIsLogFile: "PDH_DATA_SOURCE_IS_LOG_FILE", pdhDataSourceIsLogFile: "PDH_DATA_SOURCE_IS_LOG_FILE",
PdhDataSourceIsRealTime: "PDH_DATA_SOURCE_IS_REAL_TIME", pdhDataSourceIsRealTime: "PDH_DATA_SOURCE_IS_REAL_TIME",
PdhUnableReadLogHeader: "PDH_UNABLE_READ_LOG_HEADER", pdhUnableReadLogHeader: "PDH_UNABLE_READ_LOG_HEADER",
PdhFileNotFound: "PDH_FILE_NOT_FOUND", pdhFileNotFound: "PDH_FILE_NOT_FOUND",
PdhFileAlreadyExists: "PDH_FILE_ALREADY_EXISTS", pdhFileAlreadyExists: "PDH_FILE_ALREADY_EXISTS",
PdhNotImplemented: "PDH_NOT_IMPLEMENTED", pdhNotImplemented: "PDH_NOT_IMPLEMENTED",
PdhStringNotFound: "PDH_STRING_NOT_FOUND", pdhStringNotFound: "PDH_STRING_NOT_FOUND",
PdhUnableMapNameFiles: "PDH_UNABLE_MAP_NAME_FILES", pdhUnableMapNameFiles: "PDH_UNABLE_MAP_NAME_FILES",
PdhUnknownLogFormat: "PDH_UNKNOWN_LOG_FORMAT", pdhUnknownLogFormat: "PDH_UNKNOWN_LOG_FORMAT",
PdhUnknownLogsvcCommand: "PDH_UNKNOWN_LOGSVC_COMMAND", pdhUnknownLogsvcCommand: "PDH_UNKNOWN_LOGSVC_COMMAND",
PdhLogsvcQueryNotFound: "PDH_LOGSVC_QUERY_NOT_FOUND", pdhLogsvcQueryNotFound: "PDH_LOGSVC_QUERY_NOT_FOUND",
PdhLogsvcNotOpened: "PDH_LOGSVC_NOT_OPENED", pdhLogsvcNotOpened: "PDH_LOGSVC_NOT_OPENED",
PdhWbemError: "PDH_WBEM_ERROR", pdhWbemError: "PDH_WBEM_ERROR",
PdhAccessDenied: "PDH_ACCESS_DENIED", pdhAccessDenied: "PDH_ACCESS_DENIED",
PdhLogFileTooSmall: "PDH_LOG_FILE_TOO_SMALL", pdhLogFileTooSmall: "PDH_LOG_FILE_TOO_SMALL",
PdhInvalidDatasource: "PDH_INVALID_DATASOURCE", pdhInvalidDatasource: "PDH_INVALID_DATASOURCE",
PdhInvalidSqldb: "PDH_INVALID_SQLDB", pdhInvalidSqldb: "PDH_INVALID_SQLDB",
PdhNoCounters: "PDH_NO_COUNTERS", pdhNoCounters: "PDH_NO_COUNTERS",
PdhSQLAllocFailed: "PDH_SQL_ALLOC_FAILED", pdhSQLAllocFailed: "PDH_SQL_ALLOC_FAILED",
PdhSQLAllocconFailed: "PDH_SQL_ALLOCCON_FAILED", pdhSQLAllocconFailed: "PDH_SQL_ALLOCCON_FAILED",
PdhSQLExecDirectFailed: "PDH_SQL_EXEC_DIRECT_FAILED", pdhSQLExecDirectFailed: "PDH_SQL_EXEC_DIRECT_FAILED",
PdhSQLFetchFailed: "PDH_SQL_FETCH_FAILED", pdhSQLFetchFailed: "PDH_SQL_FETCH_FAILED",
PdhSQLRowcountFailed: "PDH_SQL_ROWCOUNT_FAILED", pdhSQLRowcountFailed: "PDH_SQL_ROWCOUNT_FAILED",
PdhSQLMoreResultsFailed: "PDH_SQL_MORE_RESULTS_FAILED", pdhSQLMoreResultsFailed: "PDH_SQL_MORE_RESULTS_FAILED",
PdhSQLConnectFailed: "PDH_SQL_CONNECT_FAILED", pdhSQLConnectFailed: "PDH_SQL_CONNECT_FAILED",
PdhSQLBindFailed: "PDH_SQL_BIND_FAILED", pdhSQLBindFailed: "PDH_SQL_BIND_FAILED",
PdhCannotConnectWmiServer: "PDH_CANNOT_CONNECT_WMI_SERVER", pdhCannotConnectWmiServer: "PDH_CANNOT_CONNECT_WMI_SERVER",
PdhPlaCollectionAlreadyRunning: "PDH_PLA_COLLECTION_ALREADY_RUNNING", pdhPlaCollectionAlreadyRunning: "PDH_PLA_COLLECTION_ALREADY_RUNNING",
PdhPlaErrorScheduleOverlap: "PDH_PLA_ERROR_SCHEDULE_OVERLAP", pdhPlaErrorScheduleOverlap: "PDH_PLA_ERROR_SCHEDULE_OVERLAP",
PdhPlaCollectionNotFound: "PDH_PLA_COLLECTION_NOT_FOUND", pdhPlaCollectionNotFound: "PDH_PLA_COLLECTION_NOT_FOUND",
PdhPlaErrorScheduleElapsed: "PDH_PLA_ERROR_SCHEDULE_ELAPSED", pdhPlaErrorScheduleElapsed: "PDH_PLA_ERROR_SCHEDULE_ELAPSED",
PdhPlaErrorNostart: "PDH_PLA_ERROR_NOSTART", pdhPlaErrorNostart: "PDH_PLA_ERROR_NOSTART",
PdhPlaErrorAlreadyExists: "PDH_PLA_ERROR_ALREADY_EXISTS", pdhPlaErrorAlreadyExists: "PDH_PLA_ERROR_ALREADY_EXISTS",
PdhPlaErrorTypeMismatch: "PDH_PLA_ERROR_TYPE_MISMATCH", pdhPlaErrorTypeMismatch: "PDH_PLA_ERROR_TYPE_MISMATCH",
PdhPlaErrorFilepath: "PDH_PLA_ERROR_FILEPATH", pdhPlaErrorFilepath: "PDH_PLA_ERROR_FILEPATH",
PdhPlaServiceError: "PDH_PLA_SERVICE_ERROR", pdhPlaServiceError: "PDH_PLA_SERVICE_ERROR",
PdhPlaValidationError: "PDH_PLA_VALIDATION_ERROR", pdhPlaValidationError: "PDH_PLA_VALIDATION_ERROR",
PdhPlaValidationWarning: "PDH_PLA_VALIDATION_WARNING", pdhPlaValidationWarning: "PDH_PLA_VALIDATION_WARNING",
PdhPlaErrorNameTooLong: "PDH_PLA_ERROR_NAME_TOO_LONG", pdhPlaErrorNameTooLong: "PDH_PLA_ERROR_NAME_TOO_LONG",
PdhInvalidSQLLogFormat: "PDH_INVALID_SQL_LOG_FORMAT", pdhInvalidSQLLogFormat: "PDH_INVALID_SQL_LOG_FORMAT",
PdhCounterAlreadyInQuery: "PDH_COUNTER_ALREADY_IN_QUERY", pdhCounterAlreadyInQuery: "PDH_COUNTER_ALREADY_IN_QUERY",
PdhBinaryLogCorrupt: "PDH_BINARY_LOG_CORRUPT", pdhBinaryLogCorrupt: "PDH_BINARY_LOG_CORRUPT",
PdhLogSampleTooSmall: "PDH_LOG_SAMPLE_TOO_SMALL", pdhLogSampleTooSmall: "PDH_LOG_SAMPLE_TOO_SMALL",
PdhOsLaterVersion: "PDH_OS_LATER_VERSION", pdhOsLaterVersion: "PDH_OS_LATER_VERSION",
PdhOsEarlierVersion: "PDH_OS_EARLIER_VERSION", pdhOsEarlierVersion: "PDH_OS_EARLIER_VERSION",
PdhIncorrectAppendTime: "PDH_INCORRECT_APPEND_TIME", pdhIncorrectAppendTime: "PDH_INCORRECT_APPEND_TIME",
PdhUnmatchedAppendCounter: "PDH_UNMATCHED_APPEND_COUNTER", pdhUnmatchedAppendCounter: "PDH_UNMATCHED_APPEND_COUNTER",
PdhSQLAlterDetailFailed: "PDH_SQL_ALTER_DETAIL_FAILED", pdhSQLAlterDetailFailed: "PDH_SQL_ALTER_DETAIL_FAILED",
PdhQueryPerfDataTimeout: "PDH_QUERY_PERF_DATA_TIMEOUT", pdhQueryPerfDataTimeout: "PDH_QUERY_PERF_DATA_TIMEOUT",
} }
// Formatting options for GetFormattedCounterValue(). // Formatting options for GetFormattedCounterValue().
const ( const (
PdhFmtRaw = 0x00000010 pdhFmtRaw = 0x00000010
PdhFmtAnsi = 0x00000020 pdhFmtAnsi = 0x00000020
PdhFmtUnicode = 0x00000040 pdhFmtUnicode = 0x00000040
PdhFmtLong = 0x00000100 // Return data as a long int. pdhFmtLong = 0x00000100 // Return data as a long int.
PdhFmtDouble = 0x00000200 // Return data as a double precision floating point real. pdhFmtDouble = 0x00000200 // Return data as a double precision floating point real.
PdhFmtLarge = 0x00000400 // Return data as a 64 bit integer. pdhFmtLarge = 0x00000400 // Return data as a 64 bit integer.
PdhFmtNoscale = 0x00001000 // can be OR-ed: Do not apply the counter's default scaling factor. pdhFmtNoscale = 0x00001000 // can be OR-ed: Do not apply the counter's default scaling factor.
PdhFmt1000 = 0x00002000 // can be OR-ed: multiply the actual value by 1,000. pdhFmt1000 = 0x00002000 // can be OR-ed: multiply the actual value by 1,000.
PdhFmtNodata = 0x00004000 // can be OR-ed: unknown what this is for, MSDN says nothing. pdhFmtNodata = 0x00004000 // can be OR-ed: unknown what this is for, MSDN says nothing.
PdhFmtNocap100 = 0x00008000 // can be OR-ed: do not cap values > 100. pdhFmtNocap100 = 0x00008000 // can be OR-ed: do not cap values > 100.
PerfDetailCostly = 0x00010000 perfDetailCostly = 0x00010000
PerfDetailStandard = 0x0000FFFF perfDetailStandard = 0x0000FFFF
) )
type ( type (
pdhQueryHandle HANDLE // query handle pdhQueryHandle handle // query handle
pdhCounterHandle HANDLE // counter handle pdhCounterHandle handle // counter handle
) )
var ( var (
@ -260,19 +259,18 @@ var (
libPdhDll *syscall.DLL libPdhDll *syscall.DLL
// Functions // Functions
pdhAddCounterW *syscall.Proc pdhAddCounterWProc *syscall.Proc
pdhAddEnglishCounterW *syscall.Proc pdhAddEnglishCounterWProc *syscall.Proc
pdhCloseQuery *syscall.Proc pdhCloseQueryProc *syscall.Proc
pdhCollectQueryData *syscall.Proc pdhCollectQueryDataProc *syscall.Proc
pdhCollectQueryDataWithTime *syscall.Proc pdhCollectQueryDataWithTimeProc *syscall.Proc
pdhGetFormattedCounterValue *syscall.Proc pdhGetFormattedCounterValueProc *syscall.Proc
pdhGetFormattedCounterArrayW *syscall.Proc pdhGetFormattedCounterArrayWProc *syscall.Proc
pdhOpenQuery *syscall.Proc pdhOpenQueryProc *syscall.Proc
pdhValidatePathW *syscall.Proc pdhExpandWildCardPathWProc *syscall.Proc
pdhExpandWildCardPathW *syscall.Proc pdhGetCounterInfoWProc *syscall.Proc
pdhGetCounterInfoW *syscall.Proc pdhGetRawCounterValueProc *syscall.Proc
pdhGetRawCounterValue *syscall.Proc pdhGetRawCounterArrayWProc *syscall.Proc
pdhGetRawCounterArrayW *syscall.Proc
) )
func init() { func init() {
@ -280,26 +278,25 @@ func init() {
libPdhDll = syscall.MustLoadDLL("pdh.dll") libPdhDll = syscall.MustLoadDLL("pdh.dll")
// Functions // Functions
pdhAddCounterW = libPdhDll.MustFindProc("PdhAddCounterW") pdhAddCounterWProc = libPdhDll.MustFindProc("PdhAddCounterW")
pdhAddEnglishCounterW, _ = libPdhDll.FindProc("PdhAddEnglishCounterW") // XXX: only supported on versions > Vista. pdhAddEnglishCounterWProc, _ = libPdhDll.FindProc("PdhAddEnglishCounterW") // XXX: only supported on versions > Vista.
pdhCloseQuery = libPdhDll.MustFindProc("PdhCloseQuery") pdhCloseQueryProc = libPdhDll.MustFindProc("PdhCloseQuery")
pdhCollectQueryData = libPdhDll.MustFindProc("PdhCollectQueryData") pdhCollectQueryDataProc = libPdhDll.MustFindProc("PdhCollectQueryData")
pdhCollectQueryDataWithTime, _ = libPdhDll.FindProc("PdhCollectQueryDataWithTime") pdhCollectQueryDataWithTimeProc, _ = libPdhDll.FindProc("PdhCollectQueryDataWithTime")
pdhGetFormattedCounterValue = libPdhDll.MustFindProc("PdhGetFormattedCounterValue") pdhGetFormattedCounterValueProc = libPdhDll.MustFindProc("PdhGetFormattedCounterValue")
pdhGetFormattedCounterArrayW = libPdhDll.MustFindProc("PdhGetFormattedCounterArrayW") pdhGetFormattedCounterArrayWProc = libPdhDll.MustFindProc("PdhGetFormattedCounterArrayW")
pdhOpenQuery = libPdhDll.MustFindProc("PdhOpenQuery") pdhOpenQueryProc = libPdhDll.MustFindProc("PdhOpenQuery")
pdhValidatePathW = libPdhDll.MustFindProc("PdhValidatePathW") pdhExpandWildCardPathWProc = libPdhDll.MustFindProc("PdhExpandWildCardPathW")
pdhExpandWildCardPathW = libPdhDll.MustFindProc("PdhExpandWildCardPathW") pdhGetCounterInfoWProc = libPdhDll.MustFindProc("PdhGetCounterInfoW")
pdhGetCounterInfoW = libPdhDll.MustFindProc("PdhGetCounterInfoW") pdhGetRawCounterValueProc = libPdhDll.MustFindProc("PdhGetRawCounterValue")
pdhGetRawCounterValue = libPdhDll.MustFindProc("PdhGetRawCounterValue") pdhGetRawCounterArrayWProc = libPdhDll.MustFindProc("PdhGetRawCounterArrayW")
pdhGetRawCounterArrayW = libPdhDll.MustFindProc("PdhGetRawCounterArrayW")
} }
// PdhAddCounter adds the specified counter to the query. This is the internationalized version. Preferably, use the // pdhAddCounter 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. // 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). // szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version).
// dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value // dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value
// later, call PdhGetCounterInfo() and access dwQueryUserData of the pdhCounterInfo structure. // later, call pdhGetCounterInfo() and access dwQueryUserData of the pdhCounterInfo structure.
// //
// Examples of szFullCounterPath (in an English version of Windows): // Examples of szFullCounterPath (in an English version of Windows):
// //
@ -333,9 +330,9 @@ func init() {
// The typeperf command may also be pretty easy. To find all performance counters, simply execute: // The typeperf command may also be pretty easy. To find all performance counters, simply execute:
// //
// typeperf -qx // typeperf -qx
func PdhAddCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 { func pdhAddCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 {
ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath) ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
ret, _, _ := pdhAddCounterW.Call( ret, _, _ := pdhAddCounterWProc.Call(
uintptr(hQuery), uintptr(hQuery),
uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
dwUserData, dwUserData,
@ -344,21 +341,21 @@ func PdhAddCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData u
return uint32(ret) return uint32(ret)
} }
// PdhAddEnglishCounterSupported returns true if PdhAddEnglishCounterW Win API function was found in pdh.dll. // pdhAddEnglishCounterSupported returns true if PdhAddEnglishCounterW Win API function was found in pdh.dll.
// PdhAddEnglishCounterW function is not supported on pre-Windows Vista systems // PdhAddEnglishCounterW function is not supported on pre-Windows Vista systems
func PdhAddEnglishCounterSupported() bool { func pdhAddEnglishCounterSupported() bool {
return pdhAddEnglishCounterW != nil return pdhAddEnglishCounterWProc != nil
} }
// PdhAddEnglishCounter adds the specified language-neutral counter to the query. See the PdhAddCounter function. This function only exists on // pdhAddEnglishCounter adds the specified language-neutral counter to the query. See the pdhAddCounter function. This function only exists on
// Windows versions higher than Vista. // Windows versions higher than Vista.
func PdhAddEnglishCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 { func pdhAddEnglishCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 {
if pdhAddEnglishCounterW == nil { if pdhAddEnglishCounterWProc == nil {
return ErrorInvalidFunction return errorInvalidFunction
} }
ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath) ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
ret, _, _ := pdhAddEnglishCounterW.Call( ret, _, _ := pdhAddEnglishCounterWProc.Call(
uintptr(hQuery), uintptr(hQuery),
uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
dwUserData, dwUserData,
@ -367,85 +364,85 @@ func PdhAddEnglishCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUse
return uint32(ret) return uint32(ret)
} }
// PdhCloseQuery closes all counters contained in the specified query, closes all handles related to the query, // pdhCloseQuery closes all counters contained in the specified query, closes all handles related to the query,
// and frees all memory associated with the query. // and frees all memory associated with the query.
func PdhCloseQuery(hQuery pdhQueryHandle) uint32 { func pdhCloseQuery(hQuery pdhQueryHandle) uint32 {
ret, _, _ := pdhCloseQuery.Call(uintptr(hQuery)) ret, _, _ := pdhCloseQueryProc.Call(uintptr(hQuery))
return uint32(ret) return uint32(ret)
} }
// PdhCollectQueryData collects the current raw data value for all counters in the specified query and updates the status // pdhCollectQueryData collects the current raw data value for all counters in the specified query and updates the status
// code of each counter. With some counters, this function needs to be repeatedly called before the value // code of each counter. With some counters, this function needs to be repeatedly called before the value
// of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code // of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code
// requires at least two calls: // requires at least two calls:
// //
// var handle win.PDH_HQUERY // var handle win.PDH_HQUERY
// var counterHandle win.PDH_HCOUNTER // var counterHandle win.PDH_HCOUNTER
// ret := win.PdhOpenQuery(0, 0, &handle) // ret := win.pdhOpenQuery(0, 0, &handle)
// ret = win.PdhAddEnglishCounter(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle) // ret = win.pdhAddEnglishCounter(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle)
// var derp win.PDH_FMT_COUNTERVALUE_DOUBLE // var derp win.PDH_FMT_COUNTERVALUE_DOUBLE
// //
// ret = win.PdhCollectQueryData(handle) // ret = win.pdhCollectQueryData(handle)
// fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA // fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA
// ret = win.PdhGetFormattedCounterValueDouble(counterHandle, 0, &derp) // ret = win.pdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
// //
// ret = win.PdhCollectQueryData(handle) // ret = win.pdhCollectQueryData(handle)
// fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS // fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS
// ret = win.PdhGetFormattedCounterValueDouble(counterHandle, 0, &derp) // ret = win.pdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
// //
// The PdhCollectQueryData will return an error in the first call because it needs two values for // The pdhCollectQueryData will return an error in the first call because it needs two values for
// displaying the correct data for the processor idle time. The second call will have a 0 return code. // displaying the correct data for the processor idle time. The second call will have a 0 return code.
func PdhCollectQueryData(hQuery pdhQueryHandle) uint32 { func pdhCollectQueryData(hQuery pdhQueryHandle) uint32 {
ret, _, _ := pdhCollectQueryData.Call(uintptr(hQuery)) ret, _, _ := pdhCollectQueryDataProc.Call(uintptr(hQuery))
return uint32(ret) return uint32(ret)
} }
// PdhCollectQueryDataWithTime queries data from perfmon, retrieving the device/windows timestamp from the node it was collected on. // pdhCollectQueryDataWithTime 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. // Converts the filetime structure to a GO time class and returns the native time.
func PdhCollectQueryDataWithTime(hQuery pdhQueryHandle) (uint32, time.Time) { func pdhCollectQueryDataWithTime(hQuery pdhQueryHandle) (uint32, time.Time) {
var localFileTime fileTime var localFileTime fileTime
//nolint:gosec // G103: Valid use of unsafe call to pass localFileTime //nolint:gosec // G103: Valid use of unsafe call to pass localFileTime
ret, _, _ := pdhCollectQueryDataWithTime.Call(uintptr(hQuery), uintptr(unsafe.Pointer(&localFileTime))) ret, _, _ := pdhCollectQueryDataWithTimeProc.Call(uintptr(hQuery), uintptr(unsafe.Pointer(&localFileTime)))
if ret == ErrorSuccess { if ret == errorSuccess {
var utcFileTime fileTime var utcFileTime fileTime
ret, _, _ := kernelLocalFileTimeToFileTime.Call( ret, _, _ := kernelLocalFileTimeToFileTime.Call(
uintptr(unsafe.Pointer(&localFileTime)), //nolint:gosec // G103: Valid use of unsafe call to pass localFileTime uintptr(unsafe.Pointer(&localFileTime)), //nolint:gosec // G103: Valid use of unsafe call to pass localFileTime
uintptr(unsafe.Pointer(&utcFileTime))) //nolint:gosec // G103: Valid use of unsafe call to pass utcFileTime uintptr(unsafe.Pointer(&utcFileTime))) //nolint:gosec // G103: Valid use of unsafe call to pass utcFileTime
if ret == 0 { if ret == 0 {
return uint32(ErrorFailure), time.Now() return uint32(errorFailure), time.Now()
} }
// First convert 100-ns intervals to microseconds, then adjust for the // First convert 100-ns intervals to microseconds, then adjust for the
// epoch difference // epoch difference
var totalMicroSeconds int64 var totalMicroSeconds int64
totalMicroSeconds = ((int64(utcFileTime.dwHighDateTime) << 32) | int64(utcFileTime.dwLowDateTime)) / 10 totalMicroSeconds = ((int64(utcFileTime.dwHighDateTime) << 32) | int64(utcFileTime.dwLowDateTime)) / 10
totalMicroSeconds -= EpochDifferenceMicros totalMicroSeconds -= epochDifferenceMicros
retTime := time.Unix(0, totalMicroSeconds*1000) retTime := time.Unix(0, totalMicroSeconds*1000)
return uint32(ErrorSuccess), retTime return uint32(errorSuccess), retTime
} }
return uint32(ret), time.Now() return uint32(ret), time.Now()
} }
// PdhGetFormattedCounterValueDouble formats the given hCounter using a 'double'. The result is set into the specialized union struct pValue. // pdhGetFormattedCounterValueDouble 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. // This function does not directly translate to a Windows counterpart due to union specialization tricks.
func PdhGetFormattedCounterValueDouble(hCounter pdhCounterHandle, lpdwType *uint32, pValue *pdhFmtCountervalueDouble) uint32 { func pdhGetFormattedCounterValueDouble(hCounter pdhCounterHandle, lpdwType *uint32, pValue *pdhFmtCountervalueDouble) uint32 {
ret, _, _ := pdhGetFormattedCounterValue.Call( ret, _, _ := pdhGetFormattedCounterValueProc.Call(
uintptr(hCounter), uintptr(hCounter),
uintptr(PdhFmtDouble|PdhFmtNocap100), uintptr(pdhFmtDouble|pdhFmtNocap100),
uintptr(unsafe.Pointer(lpdwType)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwType uintptr(unsafe.Pointer(lpdwType)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwType
uintptr(unsafe.Pointer(pValue))) //nolint:gosec // G103: Valid use of unsafe call to pass pValue uintptr(unsafe.Pointer(pValue))) //nolint:gosec // G103: Valid use of unsafe call to pass pValue
return uint32(ret) return uint32(ret)
} }
// PdhGetFormattedCounterArrayDouble returns an array of formatted counter values. Use this function when you want to format the counter values of a // pdhGetFormattedCounterArrayDouble returns an array of formatted counter values. Use this function when you want to format the counter values of a
// counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type pdhFmtCountervalueItemDouble. // counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type pdhFmtCountervalueItemDouble.
// An example of how this function can be used: // An example of how this function can be used:
// //
@ -460,15 +457,15 @@ func PdhGetFormattedCounterValueDouble(hCounter pdhCounterHandle, lpdwType *uint
// //
// for { // for {
// // collect // // collect
// ret := win.PdhCollectQueryData(queryHandle) // ret := win.pdhCollectQueryData(queryHandle)
// if ret == win.ERROR_SUCCESS { // if ret == win.ERROR_SUCCESS {
// ret = win.PdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN. // ret = win.pdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN.
// if ret == win.PDH_MORE_DATA { // if ret == win.PDH_MORE_DATA {
// filledBuf := make([]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size) // filledBuf := make([]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
// ret = win.PdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &filledBuf[0]) // ret = win.pdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &filledBuf[0])
// for i := 0; i < int(bufCount); i++ { // for i := 0; i < int(bufCount); i++ {
// c := filledBuf[i] // c := filledBuf[i]
// var s string = win.UTF16PtrToString(c.SzName) // var s string = win.utf16PtrToString(c.SzName)
// fmt.Printf("Index %d -> %s, value %v\n", i, s, c.FmtValue.DoubleValue) // fmt.Printf("Index %d -> %s, value %v\n", i, s, c.FmtValue.DoubleValue)
// } // }
// //
@ -482,10 +479,10 @@ func PdhGetFormattedCounterValueDouble(hCounter pdhCounterHandle, lpdwType *uint
// time.Sleep(2000 * time.Millisecond) // time.Sleep(2000 * time.Millisecond)
// } // }
// } // }
func PdhGetFormattedCounterArrayDouble(hCounter pdhCounterHandle, lpdwBufferSize, lpdwBufferCount *uint32, itemBuffer *byte) uint32 { func pdhGetFormattedCounterArrayDouble(hCounter pdhCounterHandle, lpdwBufferSize, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
ret, _, _ := pdhGetFormattedCounterArrayW.Call( ret, _, _ := pdhGetFormattedCounterArrayWProc.Call(
uintptr(hCounter), uintptr(hCounter),
uintptr(PdhFmtDouble|PdhFmtNocap100), uintptr(pdhFmtDouble|pdhFmtNocap100),
uintptr(unsafe.Pointer(lpdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferSize uintptr(unsafe.Pointer(lpdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferSize
uintptr(unsafe.Pointer(lpdwBufferCount)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferCount uintptr(unsafe.Pointer(lpdwBufferCount)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferCount
uintptr(unsafe.Pointer(itemBuffer))) //nolint:gosec // G103: Valid use of unsafe call to pass itemBuffer uintptr(unsafe.Pointer(itemBuffer))) //nolint:gosec // G103: Valid use of unsafe call to pass itemBuffer
@ -493,15 +490,15 @@ func PdhGetFormattedCounterArrayDouble(hCounter pdhCounterHandle, lpdwBufferSize
return uint32(ret) return uint32(ret)
} }
// PdhOpenQuery creates a new query that is used to manage the collection of performance data. // pdhOpenQuery creates a new query that is used to manage the collection of performance data.
// szDataSource is a null terminated string that specifies the name of the log file from which to // szDataSource is a null terminated string that specifies the name of the log file from which to
// retrieve the performance data. If 0, performance data is collected from a real-time data source. // retrieve the performance data. If 0, performance data is collected from a real-time data source.
// dwUserData is a user-defined value to associate with this query. To retrieve the user data later, // dwUserData is a user-defined value to associate with this query. To retrieve the user data later,
// call PdhGetCounterInfo and access dwQueryUserData of the pdhCounterInfo structure. phQuery is // call pdhGetCounterInfo and access dwQueryUserData of the pdhCounterInfo structure. phQuery is
// the handle to the query, and must be used in subsequent calls. This function returns a PDH_ // the handle to the query, and must be used in subsequent calls. This function returns a PDH_
// constant error code, or ErrorSuccess if the call succeeded. // constant error code, or errorSuccess if the call succeeded.
func PdhOpenQuery(szDataSource, dwUserData uintptr, phQuery *pdhQueryHandle) uint32 { func pdhOpenQuery(szDataSource, dwUserData uintptr, phQuery *pdhQueryHandle) uint32 {
ret, _, _ := pdhOpenQuery.Call( ret, _, _ := pdhOpenQueryProc.Call(
szDataSource, szDataSource,
dwUserData, dwUserData,
uintptr(unsafe.Pointer(phQuery))) //nolint:gosec // G103: Valid use of unsafe call to pass phQuery uintptr(unsafe.Pointer(phQuery))) //nolint:gosec // G103: Valid use of unsafe call to pass phQuery
@ -509,7 +506,7 @@ func PdhOpenQuery(szDataSource, dwUserData uintptr, phQuery *pdhQueryHandle) uin
return uint32(ret) return uint32(ret)
} }
// PdhExpandWildCardPath examines the specified computer or log file and returns those counter paths that match the given counter path // pdhExpandWildCardPath examines the specified computer or log file and returns those counter paths that match the given counter path
// which contains wildcard characters. The general counter path format is as follows: // which contains wildcard characters. The general counter path format is as follows:
// //
// \\computer\object(parent/instance#index)\counter // \\computer\object(parent/instance#index)\counter
@ -542,10 +539,10 @@ func PdhOpenQuery(szDataSource, dwUserData uintptr, phQuery *pdhQueryHandle) uin
// If a wildcard character is specified in the counter name, all counters of the specified object are returned. // If a wildcard character is specified in the counter name, all counters of the specified object are returned.
// //
// Partial counter path string matches (for example, "pro*") are supported. // Partial counter path string matches (for example, "pro*") are supported.
func PdhExpandWildCardPath(szWildCardPath string, mszExpandedPathList *uint16, pcchPathListLength *uint32) uint32 { func pdhExpandWildCardPath(szWildCardPath string, mszExpandedPathList *uint16, pcchPathListLength *uint32) uint32 {
ptxt, _ := syscall.UTF16PtrFromString(szWildCardPath) ptxt, _ := syscall.UTF16PtrFromString(szWildCardPath)
flags := uint32(0) // expand instances and counters flags := uint32(0) // expand instances and counters
ret, _, _ := pdhExpandWildCardPathW.Call( ret, _, _ := pdhExpandWildCardPathWProc.Call(
0, // search counters on local computer 0, // search counters on local computer
uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
uintptr(unsafe.Pointer(mszExpandedPathList)), //nolint:gosec // G103: Valid use of unsafe call to pass mszExpandedPathList uintptr(unsafe.Pointer(mszExpandedPathList)), //nolint:gosec // G103: Valid use of unsafe call to pass mszExpandedPathList
@ -555,27 +552,19 @@ func PdhExpandWildCardPath(szWildCardPath string, mszExpandedPathList *uint16, p
return uint32(ret) return uint32(ret)
} }
// PdhValidatePath validates a path. Will return ErrorSuccess when ok, or PdhCstatusBadCountername when the path is erroneous. func pdhFormatError(msgID uint32) string {
func PdhValidatePath(path string) uint32 {
ptxt, _ := syscall.UTF16PtrFromString(path)
ret, _, _ := pdhValidatePathW.Call(uintptr(unsafe.Pointer(ptxt))) //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
return uint32(ret)
}
func PdhFormatError(msgID uint32) string {
var flags uint32 = windows.FORMAT_MESSAGE_FROM_HMODULE | windows.FORMAT_MESSAGE_ARGUMENT_ARRAY | windows.FORMAT_MESSAGE_IGNORE_INSERTS var flags uint32 = windows.FORMAT_MESSAGE_FROM_HMODULE | windows.FORMAT_MESSAGE_ARGUMENT_ARRAY | windows.FORMAT_MESSAGE_IGNORE_INSERTS
buf := make([]uint16, 300) buf := make([]uint16, 300)
_, err := windows.FormatMessage(flags, uintptr(libPdhDll.Handle), msgID, 0, buf, nil) _, err := windows.FormatMessage(flags, uintptr(libPdhDll.Handle), msgID, 0, buf, nil)
if err == nil { if err == nil {
return UTF16PtrToString(&buf[0]) return utf16PtrToString(&buf[0])
} }
return fmt.Sprintf("(pdhErr=%d) %s", msgID, err.Error()) return fmt.Sprintf("(pdhErr=%d) %s", msgID, err.Error())
} }
// PdhGetCounterInfo retrieves information about a counter, such as data size, counter type, path, and user-supplied data values // pdhGetCounterInfo retrieves information about a counter, such as data size, counter type, path, and user-supplied data values
// hCounter [in] // hCounter [in]
// Handle of the counter from which you want to retrieve information. The PdhAddCounter function returns this handle. // Handle of the counter from which you want to retrieve information. The pdhAddCounter function returns this handle.
// //
// bRetrieveExplainText [in] // bRetrieveExplainText [in]
// Determines whether explain text is retrieved. If you set this parameter to TRUE, the explain text for the counter is retrieved. // Determines whether explain text is retrieved. If you set this parameter to TRUE, the explain text for the counter is retrieved.
@ -590,8 +579,8 @@ func PdhFormatError(msgID uint32) string {
// Caller-allocated buffer that receives a pdhCounterInfo structure. // Caller-allocated buffer that receives a pdhCounterInfo structure.
// The structure is variable-length, because the string data is appended to the end of the fixed-format portion of the structure. // The structure is variable-length, because the string data is appended to the end of the fixed-format portion of the structure.
// This is done so that all data is returned in a single buffer allocated by the caller. Set to NULL if pdwBufferSize is zero. // This is done so that all data is returned in a single buffer allocated by the caller. Set to NULL if pdwBufferSize is zero.
func PdhGetCounterInfo(hCounter pdhCounterHandle, bRetrieveExplainText int, pdwBufferSize *uint32, lpBuffer *byte) uint32 { func pdhGetCounterInfo(hCounter pdhCounterHandle, bRetrieveExplainText int, pdwBufferSize *uint32, lpBuffer *byte) uint32 {
ret, _, _ := pdhGetCounterInfoW.Call( ret, _, _ := pdhGetCounterInfoWProc.Call(
uintptr(hCounter), uintptr(hCounter),
uintptr(bRetrieveExplainText), uintptr(bRetrieveExplainText),
uintptr(unsafe.Pointer(pdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass pdwBufferSize uintptr(unsafe.Pointer(pdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass pdwBufferSize
@ -600,12 +589,12 @@ func PdhGetCounterInfo(hCounter pdhCounterHandle, bRetrieveExplainText int, pdwB
return uint32(ret) return uint32(ret)
} }
// PdhGetRawCounterValue returns the current raw value of the counter. // pdhGetRawCounterValue returns the current raw value of the counter.
// If the specified counter instance does not exist, this function will return ErrorSuccess // If the specified counter instance does not exist, this function will return errorSuccess
// and the CStatus member of the pdhRawCounter structure will contain PdhCstatusNoInstance. // and the CStatus member of the pdhRawCounter structure will contain PdhCstatusNoInstance.
// //
// hCounter [in] // hCounter [in]
// Handle of the counter from which to retrieve the current raw value. The PdhAddCounter function returns this handle. // Handle of the counter from which to retrieve the current raw value. The pdhAddCounter function returns this handle.
// //
// lpdwType [out] // lpdwType [out]
// Receives the counter type. For a list of counter types, see the Counter Types section of the Windows Server 2003 Deployment Kit. // Receives the counter type. For a list of counter types, see the Counter Types section of the Windows Server 2003 Deployment Kit.
@ -613,8 +602,8 @@ func PdhGetCounterInfo(hCounter pdhCounterHandle, bRetrieveExplainText int, pdwB
// //
// pValue [out] // pValue [out]
// A pdhRawCounter structure that receives the counter value. // A pdhRawCounter structure that receives the counter value.
func PdhGetRawCounterValue(hCounter pdhCounterHandle, lpdwType *uint32, pValue *pdhRawCounter) uint32 { func pdhGetRawCounterValue(hCounter pdhCounterHandle, lpdwType *uint32, pValue *pdhRawCounter) uint32 {
ret, _, _ := pdhGetRawCounterValue.Call( ret, _, _ := pdhGetRawCounterValueProc.Call(
uintptr(hCounter), uintptr(hCounter),
uintptr(unsafe.Pointer(lpdwType)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwType uintptr(unsafe.Pointer(lpdwType)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwType
uintptr(unsafe.Pointer(pValue))) //nolint:gosec // G103: Valid use of unsafe call to pass pValue uintptr(unsafe.Pointer(pValue))) //nolint:gosec // G103: Valid use of unsafe call to pass pValue
@ -622,10 +611,10 @@ func PdhGetRawCounterValue(hCounter pdhCounterHandle, lpdwType *uint32, pValue *
return uint32(ret) return uint32(ret)
} }
// PdhGetRawCounterArray returns an array of raw values from the specified counter. Use this function when you want to retrieve the raw counter values // pdhGetRawCounterArray returns an array of raw values from the specified counter. Use this function when you want to retrieve the raw counter values
// of a counter that contains a wildcard character for the instance name. // of a counter that contains a wildcard character for the instance name.
// hCounter // hCounter
// Handle of the counter for whose current raw instance values you want to retrieve. The PdhAddCounter function returns this handle. // Handle of the counter for whose current raw instance values you want to retrieve. The pdhAddCounter function returns this handle.
// //
// lpdwBufferSize // lpdwBufferSize
// Size of the ItemBuffer buffer, in bytes. If zero on input, the function returns PdhMoreData and sets this parameter to the required buffer size. // Size of the ItemBuffer buffer, in bytes. If zero on input, the function returns PdhMoreData and sets this parameter to the required buffer size.
@ -638,8 +627,8 @@ func PdhGetRawCounterValue(hCounter pdhCounterHandle, lpdwType *uint32, pValue *
// ItemBuffer // ItemBuffer
// Caller-allocated buffer that receives the array of pdhRawCounterItem structures; the structures contain the raw instance counter values. // Caller-allocated buffer that receives the array of pdhRawCounterItem structures; the structures contain the raw instance counter values.
// Set to NULL if lpdwBufferSize is zero. // Set to NULL if lpdwBufferSize is zero.
func PdhGetRawCounterArray(hCounter pdhCounterHandle, lpdwBufferSize, lpdwBufferCount *uint32, itemBuffer *byte) uint32 { func pdhGetRawCounterArray(hCounter pdhCounterHandle, lpdwBufferSize, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
ret, _, _ := pdhGetRawCounterArrayW.Call( ret, _, _ := pdhGetRawCounterArrayWProc.Call(
uintptr(hCounter), uintptr(hCounter),
uintptr(unsafe.Pointer(lpdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferSize uintptr(unsafe.Pointer(lpdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferSize
uintptr(unsafe.Pointer(lpdwBufferCount)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferCount uintptr(unsafe.Pointer(lpdwBufferCount)), //nolint:gosec // G103: Valid use of unsafe call to pass lpdwBufferCount

View File

@ -52,13 +52,6 @@ type pdhFmtCountervalueItemDouble struct {
FmtValue pdhFmtCountervalueDouble FmtValue pdhFmtCountervalueDouble
} }
// pdhFmtCountervalueItemLong is a union specialization for long values, used by PdhGetFormattedCounterArrayLong()
type PdhFmtCountervalueItemLong struct {
SzName *uint16 // pointer to a string
padding [4]byte //nolint:unused // Memory reservation
FmtValue pdhFmtCountervalueLong
}
// pdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path. // pdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
type pdhCounterInfo struct { type pdhCounterInfo struct {
//Size of the structure, including the appended strings, in bytes. //Size of the structure, including the appended strings, in bytes.
@ -78,9 +71,9 @@ type pdhCounterInfo struct {
LScale int32 LScale int32
//Default scale factor as suggested by the counter's provider. //Default scale factor as suggested by the counter's provider.
LDefaultScale int32 LDefaultScale int32
//The value passed in the dwUserData parameter when calling PdhAddCounter. //The value passed in the dwUserData parameter when calling pdhAddCounter.
DwUserData *uint32 DwUserData *uint32
//The value passed in the dwUserData parameter when calling PdhOpenQuery. //The value passed in the dwUserData parameter when calling pdhOpenQuery.
DwQueryUserData *uint32 DwQueryUserData *uint32
//Null-terminated string that specifies the full counter path. The string follows this structure in memory. //Null-terminated string that specifies the full counter path. The string follows this structure in memory.
SzFullPath *uint16 // pointer to a string SzFullPath *uint16 // pointer to a string

View File

@ -38,7 +38,7 @@ type pdhFmtCountervalueDouble struct {
DoubleValue float64 DoubleValue float64
} }
// pdhFmtCountervalueItemDouble is a union specialization for double values, used by PdhGetFormattedCounterArrayDouble // pdhFmtCountervalueItemDouble is a union specialization for double values, used by pdhGetFormattedCounterArrayDouble
type pdhFmtCountervalueItemDouble struct { type pdhFmtCountervalueItemDouble struct {
SzName *uint16 SzName *uint16
FmtValue pdhFmtCountervalueDouble FmtValue pdhFmtCountervalueDouble
@ -63,9 +63,9 @@ type pdhCounterInfo struct {
LScale int32 LScale int32
// Default scale factor as suggested by the counter's provider. // Default scale factor as suggested by the counter's provider.
LDefaultScale int32 LDefaultScale int32
// The value passed in the dwUserData parameter when calling PdhAddCounter. // The value passed in the dwUserData parameter when calling pdhAddCounter.
DwUserData *uint32 DwUserData *uint32
// The value passed in the dwUserData parameter when calling PdhOpenQuery. // The value passed in the dwUserData parameter when calling pdhOpenQuery.
DwQueryUserData *uint32 DwQueryUserData *uint32
// Null-terminated string that specifies the full counter path. The string follows this structure in memory. // Null-terminated string that specifies the full counter path. The string follows this structure in memory.
SzFullPath *uint16 // pointer to a string SzFullPath *uint16 // pointer to a string

View File

@ -62,9 +62,9 @@ type pdhCounterInfo struct {
LScale int32 LScale int32
//Default scale factor as suggested by the counter's provider. //Default scale factor as suggested by the counter's provider.
LDefaultScale int32 LDefaultScale int32
//The value passed in the dwUserData parameter when calling PdhAddCounter. //The value passed in the dwUserData parameter when calling pdhAddCounter.
DwUserData *uint32 DwUserData *uint32
//The value passed in the dwUserData parameter when calling PdhOpenQuery. //The value passed in the dwUserData parameter when calling pdhOpenQuery.
DwQueryUserData *uint32 DwQueryUserData *uint32
//Null-terminated string that specifies the full counter path. The string follows this structure in memory. //Null-terminated string that specifies the full counter path. The string follows this structure in memory.
SzFullPath *uint16 // pointer to a string SzFullPath *uint16 // pointer to a string

View File

@ -17,36 +17,36 @@ var errBufferLimitReached = errors.New("buffer limit reached")
// counterValue is abstraction for pdhFmtCountervalueItemDouble // counterValue is abstraction for pdhFmtCountervalueItemDouble
type counterValue struct { type counterValue struct {
InstanceName string instanceName string
Value interface{} value interface{}
} }
// PerformanceQuery provides wrappers around Windows performance counters API for easy usage in GO // performanceQuery provides wrappers around Windows performance counters API for easy usage in GO
// //
//nolint:interfacebloat // conditionally allow to contain more methods //nolint:interfacebloat // conditionally allow to contain more methods
type PerformanceQuery interface { type performanceQuery interface {
Open() error open() error
Close() error close() error
AddCounterToQuery(counterPath string) (pdhCounterHandle, error) addCounterToQuery(counterPath string) (pdhCounterHandle, error)
AddEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error) addEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error)
GetCounterPath(counterHandle pdhCounterHandle) (string, error) getCounterPath(counterHandle pdhCounterHandle) (string, error)
ExpandWildCardPath(counterPath string) ([]string, error) expandWildCardPath(counterPath string) ([]string, error)
GetFormattedCounterValueDouble(hCounter pdhCounterHandle) (float64, error) getFormattedCounterValueDouble(hCounter pdhCounterHandle) (float64, error)
GetRawCounterValue(hCounter pdhCounterHandle) (int64, error) getRawCounterValue(hCounter pdhCounterHandle) (int64, error)
GetFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error) getFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error)
GetRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error) getRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error)
CollectData() error collectData() error
CollectDataWithTime() (time.Time, error) collectDataWithTime() (time.Time, error)
IsVistaOrNewer() bool isVistaOrNewer() bool
} }
type PerformanceQueryCreator interface { type performanceQueryCreator interface {
NewPerformanceQuery(string, uint32) PerformanceQuery newPerformanceQuery(string, uint32) performanceQuery
} }
// pdhError represents error returned from Performance Counters API // pdhError represents error returned from Performance Counters API
type pdhError struct { type pdhError struct {
ErrorCode uint32 errorCode uint32
errorText string errorText string
} }
@ -54,14 +54,14 @@ func (m *pdhError) Error() string {
return m.errorText return m.errorText
} }
func NewPdhError(code uint32) error { func newPdhError(code uint32) error {
return &pdhError{ return &pdhError{
ErrorCode: code, errorCode: code,
errorText: PdhFormatError(code), errorText: pdhFormatError(code),
} }
} }
// performanceQueryImpl is implementation of PerformanceQuery interface, which calls phd.dll functions // performanceQueryImpl is implementation of performanceQuery interface, which calls phd.dll functions
type performanceQueryImpl struct { type performanceQueryImpl struct {
maxBufferSize uint32 maxBufferSize uint32
query pdhQueryHandle query pdhQueryHandle
@ -69,75 +69,75 @@ type performanceQueryImpl struct {
type performanceQueryCreatorImpl struct{} type performanceQueryCreatorImpl struct{}
func (performanceQueryCreatorImpl) NewPerformanceQuery(_ string, maxBufferSize uint32) PerformanceQuery { func (performanceQueryCreatorImpl) newPerformanceQuery(_ string, maxBufferSize uint32) performanceQuery {
return &performanceQueryImpl{maxBufferSize: maxBufferSize} return &performanceQueryImpl{maxBufferSize: maxBufferSize}
} }
// Open creates a new counterPath that is used to manage the collection of performance data. // open creates a new counterPath that is used to manage the collection of performance data.
// It returns counterPath handle used for subsequent calls for adding counters and querying data // It returns counterPath handle used for subsequent calls for adding counters and querying data
func (m *performanceQueryImpl) Open() error { func (m *performanceQueryImpl) open() error {
if m.query != 0 { if m.query != 0 {
err := m.Close() err := m.close()
if err != nil { if err != nil {
return err return err
} }
} }
var handle pdhQueryHandle var handle pdhQueryHandle
if ret := PdhOpenQuery(0, 0, &handle); ret != ErrorSuccess { if ret := pdhOpenQuery(0, 0, &handle); ret != errorSuccess {
return NewPdhError(ret) return newPdhError(ret)
} }
m.query = handle m.query = handle
return nil return nil
} }
// Close closes the counterPath, releases associated counter handles and frees resources // close closes the counterPath, releases associated counter handles and frees resources
func (m *performanceQueryImpl) Close() error { func (m *performanceQueryImpl) close() error {
if m.query == 0 { if m.query == 0 {
return errors.New("uninitialized query") return errors.New("uninitialized query")
} }
if ret := PdhCloseQuery(m.query); ret != ErrorSuccess { if ret := pdhCloseQuery(m.query); ret != errorSuccess {
return NewPdhError(ret) return newPdhError(ret)
} }
m.query = 0 m.query = 0
return nil return nil
} }
func (m *performanceQueryImpl) AddCounterToQuery(counterPath string) (pdhCounterHandle, error) { func (m *performanceQueryImpl) addCounterToQuery(counterPath string) (pdhCounterHandle, error) {
var counterHandle pdhCounterHandle var counterHandle pdhCounterHandle
if m.query == 0 { if m.query == 0 {
return 0, errors.New("uninitialized query") return 0, errors.New("uninitialized query")
} }
if ret := PdhAddCounter(m.query, counterPath, 0, &counterHandle); ret != ErrorSuccess { if ret := pdhAddCounter(m.query, counterPath, 0, &counterHandle); ret != errorSuccess {
return 0, NewPdhError(ret) return 0, newPdhError(ret)
} }
return counterHandle, nil return counterHandle, nil
} }
func (m *performanceQueryImpl) AddEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error) { func (m *performanceQueryImpl) addEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error) {
var counterHandle pdhCounterHandle var counterHandle pdhCounterHandle
if m.query == 0 { if m.query == 0 {
return 0, errors.New("uninitialized query") return 0, errors.New("uninitialized query")
} }
if ret := PdhAddEnglishCounter(m.query, counterPath, 0, &counterHandle); ret != ErrorSuccess { if ret := pdhAddEnglishCounter(m.query, counterPath, 0, &counterHandle); ret != errorSuccess {
return 0, NewPdhError(ret) return 0, newPdhError(ret)
} }
return counterHandle, nil return counterHandle, nil
} }
// GetCounterPath return counter information for given handle // getCounterPath returns counter information for given handle
func (m *performanceQueryImpl) GetCounterPath(counterHandle pdhCounterHandle) (string, error) { func (m *performanceQueryImpl) getCounterPath(counterHandle pdhCounterHandle) (string, error) {
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 { for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
buf := make([]byte, buflen) buf := make([]byte, buflen)
// Get the info with the current buffer size // Get the info with the current buffer size
size := buflen size := buflen
ret := PdhGetCounterInfo(counterHandle, 0, &size, &buf[0]) ret := pdhGetCounterInfo(counterHandle, 0, &size, &buf[0])
if ret == ErrorSuccess { if ret == errorSuccess {
ci := (*pdhCounterInfo)(unsafe.Pointer(&buf[0])) //nolint:gosec // G103: Valid use of unsafe call to create PDH_COUNTER_INFO ci := (*pdhCounterInfo)(unsafe.Pointer(&buf[0])) //nolint:gosec // G103: Valid use of unsafe call to create PDH_COUNTER_INFO
return UTF16PtrToString(ci.SzFullPath), nil return utf16PtrToString(ci.SzFullPath), nil
} }
// Use the size as a hint if it exceeds the current buffer size // Use the size as a hint if it exceeds the current buffer size
@ -146,24 +146,24 @@ func (m *performanceQueryImpl) GetCounterPath(counterHandle pdhCounterHandle) (s
} }
// We got a non-recoverable error so exit here // We got a non-recoverable error so exit here
if ret != PdhMoreData { if ret != pdhMoreData {
return "", NewPdhError(ret) return "", newPdhError(ret)
} }
} }
return "", errBufferLimitReached return "", errBufferLimitReached
} }
// ExpandWildCardPath examines local computer and returns those counter paths that match the given counter path which contains wildcard characters. // expandWildCardPath examines local computer and returns those counter paths that match the given counter path which contains wildcard characters.
func (m *performanceQueryImpl) ExpandWildCardPath(counterPath string) ([]string, error) { func (m *performanceQueryImpl) expandWildCardPath(counterPath string) ([]string, error) {
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 { for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
buf := make([]uint16, buflen) buf := make([]uint16, buflen)
// Get the info with the current buffer size // Get the info with the current buffer size
size := buflen size := buflen
ret := PdhExpandWildCardPath(counterPath, &buf[0], &size) ret := pdhExpandWildCardPath(counterPath, &buf[0], &size)
if ret == ErrorSuccess { if ret == errorSuccess {
return UTF16ToStringArray(buf), nil return utf16ToStringArray(buf), nil
} }
// Use the size as a hint if it exceeds the current buffer size // Use the size as a hint if it exceeds the current buffer size
@ -172,43 +172,43 @@ func (m *performanceQueryImpl) ExpandWildCardPath(counterPath string) ([]string,
} }
// We got a non-recoverable error so exit here // We got a non-recoverable error so exit here
if ret != PdhMoreData { if ret != pdhMoreData {
return nil, NewPdhError(ret) return nil, newPdhError(ret)
} }
} }
return nil, errBufferLimitReached return nil, errBufferLimitReached
} }
// GetFormattedCounterValueDouble computes a displayable value for the specified counter // getFormattedCounterValueDouble computes a displayable value for the specified counter
func (*performanceQueryImpl) GetFormattedCounterValueDouble(hCounter pdhCounterHandle) (float64, error) { func (*performanceQueryImpl) getFormattedCounterValueDouble(hCounter pdhCounterHandle) (float64, error) {
var counterType uint32 var counterType uint32
var value pdhFmtCountervalueDouble var value pdhFmtCountervalueDouble
if ret := PdhGetFormattedCounterValueDouble(hCounter, &counterType, &value); ret != ErrorSuccess { if ret := pdhGetFormattedCounterValueDouble(hCounter, &counterType, &value); ret != errorSuccess {
return 0, NewPdhError(ret) return 0, newPdhError(ret)
} }
if value.CStatus == PdhCstatusValidData || value.CStatus == PdhCstatusNewData { if value.CStatus == pdhCstatusValidData || value.CStatus == pdhCstatusNewData {
return value.DoubleValue, nil return value.DoubleValue, nil
} }
return 0, NewPdhError(value.CStatus) return 0, newPdhError(value.CStatus)
} }
func (m *performanceQueryImpl) GetFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error) { func (m *performanceQueryImpl) getFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error) {
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 { for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
buf := make([]byte, buflen) buf := make([]byte, buflen)
// Get the info with the current buffer size // Get the info with the current buffer size
var itemCount uint32 var itemCount uint32
size := buflen size := buflen
ret := PdhGetFormattedCounterArrayDouble(hCounter, &size, &itemCount, &buf[0]) ret := pdhGetFormattedCounterArrayDouble(hCounter, &size, &itemCount, &buf[0])
if ret == ErrorSuccess { if ret == errorSuccess {
//nolint:gosec // G103: Valid use of unsafe call to create PDH_FMT_COUNTERVALUE_ITEM_DOUBLE //nolint:gosec // G103: Valid use of unsafe call to create PDH_FMT_COUNTERVALUE_ITEM_DOUBLE
items := (*[1 << 20]pdhFmtCountervalueItemDouble)(unsafe.Pointer(&buf[0]))[:itemCount] items := (*[1 << 20]pdhFmtCountervalueItemDouble)(unsafe.Pointer(&buf[0]))[:itemCount]
values := make([]counterValue, 0, itemCount) values := make([]counterValue, 0, itemCount)
for _, item := range items { for _, item := range items {
if item.FmtValue.CStatus == PdhCstatusValidData || item.FmtValue.CStatus == PdhCstatusNewData { if item.FmtValue.CStatus == pdhCstatusValidData || item.FmtValue.CStatus == pdhCstatusNewData {
val := counterValue{UTF16PtrToString(item.SzName), item.FmtValue.DoubleValue} val := counterValue{utf16PtrToString(item.SzName), item.FmtValue.DoubleValue}
values = append(values, val) values = append(values, val)
} }
} }
@ -221,29 +221,29 @@ func (m *performanceQueryImpl) GetFormattedCounterArrayDouble(hCounter pdhCounte
} }
// We got a non-recoverable error so exit here // We got a non-recoverable error so exit here
if ret != PdhMoreData { if ret != pdhMoreData {
return nil, NewPdhError(ret) return nil, newPdhError(ret)
} }
} }
return nil, errBufferLimitReached return nil, errBufferLimitReached
} }
func (m *performanceQueryImpl) GetRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error) { func (m *performanceQueryImpl) getRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error) {
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 { for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
buf := make([]byte, buflen) buf := make([]byte, buflen)
// Get the info with the current buffer size // Get the info with the current buffer size
var itemCount uint32 var itemCount uint32
size := buflen size := buflen
ret := PdhGetRawCounterArray(hCounter, &size, &itemCount, &buf[0]) ret := pdhGetRawCounterArray(hCounter, &size, &itemCount, &buf[0])
if ret == ErrorSuccess { if ret == errorSuccess {
//nolint:gosec // G103: Valid use of unsafe call to create PDH_RAW_COUNTER_ITEM //nolint:gosec // G103: Valid use of unsafe call to create PDH_RAW_COUNTER_ITEM
items := (*[1 << 20]pdhRawCounterItem)(unsafe.Pointer(&buf[0]))[:itemCount] items := (*[1 << 20]pdhRawCounterItem)(unsafe.Pointer(&buf[0]))[:itemCount]
values := make([]counterValue, 0, itemCount) values := make([]counterValue, 0, itemCount)
for _, item := range items { for _, item := range items {
if item.RawValue.CStatus == PdhCstatusValidData || item.RawValue.CStatus == PdhCstatusNewData { if item.RawValue.CStatus == pdhCstatusValidData || item.RawValue.CStatus == pdhCstatusNewData {
val := counterValue{UTF16PtrToString(item.SzName), item.RawValue.FirstValue} val := counterValue{utf16PtrToString(item.SzName), item.RawValue.FirstValue}
values = append(values, val) values = append(values, val)
} }
} }
@ -256,42 +256,42 @@ func (m *performanceQueryImpl) GetRawCounterArray(hCounter pdhCounterHandle) ([]
} }
// We got a non-recoverable error so exit here // We got a non-recoverable error so exit here
if ret != PdhMoreData { if ret != pdhMoreData {
return nil, NewPdhError(ret) return nil, newPdhError(ret)
} }
} }
return nil, errBufferLimitReached return nil, errBufferLimitReached
} }
func (m *performanceQueryImpl) CollectData() error { func (m *performanceQueryImpl) collectData() error {
var ret uint32 var ret uint32
if m.query == 0 { if m.query == 0 {
return errors.New("uninitialized query") return errors.New("uninitialized query")
} }
if ret = PdhCollectQueryData(m.query); ret != ErrorSuccess { if ret = pdhCollectQueryData(m.query); ret != errorSuccess {
return NewPdhError(ret) return newPdhError(ret)
} }
return nil return nil
} }
func (m *performanceQueryImpl) CollectDataWithTime() (time.Time, error) { func (m *performanceQueryImpl) collectDataWithTime() (time.Time, error) {
if m.query == 0 { if m.query == 0 {
return time.Now(), errors.New("uninitialized query") return time.Now(), errors.New("uninitialized query")
} }
ret, mtime := PdhCollectQueryDataWithTime(m.query) ret, mtime := pdhCollectQueryDataWithTime(m.query)
if ret != ErrorSuccess { if ret != errorSuccess {
return time.Now(), NewPdhError(ret) return time.Now(), newPdhError(ret)
} }
return mtime, nil return mtime, nil
} }
func (*performanceQueryImpl) IsVistaOrNewer() bool { func (*performanceQueryImpl) isVistaOrNewer() bool {
return PdhAddEnglishCounterSupported() return pdhAddEnglishCounterSupported()
} }
func (m *performanceQueryImpl) GetRawCounterValue(hCounter pdhCounterHandle) (int64, error) { func (m *performanceQueryImpl) getRawCounterValue(hCounter pdhCounterHandle) (int64, error) {
if m.query == 0 { if m.query == 0 {
return 0, errors.New("uninitialised query") return 0, errors.New("uninitialised query")
} }
@ -300,17 +300,17 @@ func (m *performanceQueryImpl) GetRawCounterValue(hCounter pdhCounterHandle) (in
var value pdhRawCounter var value pdhRawCounter
var ret uint32 var ret uint32
if ret = PdhGetRawCounterValue(hCounter, &counterType, &value); ret == ErrorSuccess { if ret = pdhGetRawCounterValue(hCounter, &counterType, &value); ret == errorSuccess {
if value.CStatus == PdhCstatusValidData || value.CStatus == PdhCstatusNewData { if value.CStatus == pdhCstatusValidData || value.CStatus == pdhCstatusNewData {
return value.FirstValue, nil return value.FirstValue, nil
} }
return 0, NewPdhError(value.CStatus) return 0, newPdhError(value.CStatus)
} }
return 0, NewPdhError(ret) return 0, newPdhError(ret)
} }
// UTF16PtrToString converts Windows API LPTSTR (pointer to string) to go string // utf16PtrToString converts Windows API LPTSTR (pointer to string) to go string
func UTF16PtrToString(s *uint16) string { func utf16PtrToString(s *uint16) string {
if s == nil { if s == nil {
return "" return ""
} }
@ -318,16 +318,16 @@ func UTF16PtrToString(s *uint16) string {
return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:]) return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:])
} }
// UTF16ToStringArray converts list of Windows API NULL terminated strings to go string array // utf16ToStringArray converts list of Windows API NULL terminated strings to go string array
func UTF16ToStringArray(buf []uint16) []string { func utf16ToStringArray(buf []uint16) []string {
var strings []string var strings []string
nextLineStart := 0 nextLineStart := 0
stringLine := UTF16PtrToString(&buf[0]) stringLine := utf16PtrToString(&buf[0])
for stringLine != "" { for stringLine != "" {
strings = append(strings, stringLine) strings = append(strings, stringLine)
nextLineStart += len([]rune(stringLine)) + 1 nextLineStart += len([]rune(stringLine)) + 1
remainingBuf := buf[nextLineStart:] remainingBuf := buf[nextLineStart:]
stringLine = UTF16PtrToString(&remainingBuf[0]) stringLine = utf16PtrToString(&remainingBuf[0])
} }
return strings return strings
} }

View File

@ -21,51 +21,56 @@ import (
//go:embed sample.conf //go:embed sample.conf
var sampleConfig string var sampleConfig string
var defaultMaxBufferSize = config.Size(100 * 1024 * 1024) var (
defaultMaxBufferSize = config.Size(100 * 1024 * 1024)
sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec", " ", "_", "%", "Percent", `\`, "")
)
const emptyInstance = "------"
type WinPerfCounters struct { type WinPerfCounters struct {
PrintValid bool `toml:"PrintValid"` PrintValid bool `toml:"PrintValid"`
PreVistaSupport bool `toml:"PreVistaSupport" deprecated:"1.7.0;1.35.0;determined dynamically"` PreVistaSupport bool `toml:"PreVistaSupport" deprecated:"1.7.0;1.35.0;determined dynamically"`
UsePerfCounterTime bool UsePerfCounterTime bool `toml:"UsePerfCounterTime"`
Object []perfObject Object []perfObject `toml:"object"`
CountersRefreshInterval config.Duration CountersRefreshInterval config.Duration `toml:"CountersRefreshInterval"`
UseWildcardsExpansion bool UseWildcardsExpansion bool `toml:"UseWildcardsExpansion"`
LocalizeWildcardsExpansion bool LocalizeWildcardsExpansion bool `toml:"LocalizeWildcardsExpansion"`
IgnoredErrors []string `toml:"IgnoredErrors"` IgnoredErrors []string `toml:"IgnoredErrors"`
MaxBufferSize config.Size MaxBufferSize config.Size `toml:"MaxBufferSize"`
Sources []string Sources []string `toml:"Sources"`
Log telegraf.Logger Log telegraf.Logger `toml:"-"`
lastRefreshed time.Time lastRefreshed time.Time
queryCreator PerformanceQueryCreator queryCreator performanceQueryCreator
hostCounters map[string]*hostCountersInfo hostCounters map[string]*hostCountersInfo
// cached os.Hostname() // cached os.Hostname()
cachedHostname string cachedHostname string
} }
type perfObject struct {
Sources []string `toml:"Sources"`
ObjectName string `toml:"ObjectName"`
Counters []string `toml:"Counters"`
Instances []string `toml:"Instances"`
Measurement string `toml:"Measurement"`
WarnOnMissing bool `toml:"WarnOnMissing"`
FailOnMissing bool `toml:"FailOnMissing"`
IncludeTotal bool `toml:"IncludeTotal"`
UseRawValues bool `toml:"UseRawValues"`
}
type hostCountersInfo struct { type hostCountersInfo struct {
// computer name used as key and for printing // computer name used as key and for printing
computer string computer string
// computer name used in tag // computer name used in tag
tag string tag string
counters []*counter counters []*counter
query PerformanceQuery query performanceQuery
timestamp time.Time timestamp time.Time
} }
type perfObject struct {
Sources []string
ObjectName string
Counters []string
Instances []string
Measurement string
WarnOnMissing bool
FailOnMissing bool
IncludeTotal bool
UseRawValues bool
}
type counter struct { type counter struct {
counterPath string counterPath string
computer string computer string
@ -86,8 +91,103 @@ type instanceGrouping struct {
type fieldGrouping map[instanceGrouping]map[string]interface{} type fieldGrouping map[instanceGrouping]map[string]interface{}
var sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec", func (*WinPerfCounters) SampleConfig() string {
" ", "_", "%", "Percent", `\`, "") return sampleConfig
}
func (m *WinPerfCounters) Init() error {
// Check the buffer size
if m.MaxBufferSize < config.Size(initialBufferSize) {
return fmt.Errorf("maximum buffer size should at least be %d", 2*initialBufferSize)
}
if m.MaxBufferSize > math.MaxUint32 {
return fmt.Errorf("maximum buffer size should be smaller than %d", uint32(math.MaxUint32))
}
if m.UseWildcardsExpansion && !m.LocalizeWildcardsExpansion {
// Counters must not have wildcards with this option
found := false
wildcards := []string{"*", "?"}
for _, object := range m.Object {
for _, wildcard := range wildcards {
if strings.Contains(object.ObjectName, wildcard) {
found = true
m.Log.Errorf("Object: %s, contains wildcard %s", object.ObjectName, wildcard)
}
}
for _, counter := range object.Counters {
for _, wildcard := range wildcards {
if strings.Contains(counter, wildcard) {
found = true
m.Log.Errorf("Object: %s, counter: %s contains wildcard %s", object.ObjectName, counter, wildcard)
}
}
}
}
if found {
return errors.New("wildcards can't be used with LocalizeWildcardsExpansion=false")
}
}
return nil
}
func (m *WinPerfCounters) Gather(acc telegraf.Accumulator) error {
// Parse the config once
var err error
if m.lastRefreshed.IsZero() || (m.CountersRefreshInterval > 0 && m.lastRefreshed.Add(time.Duration(m.CountersRefreshInterval)).Before(time.Now())) {
if err := m.cleanQueries(); err != nil {
return err
}
if err := m.parseConfig(); err != nil {
return err
}
for _, hostCounterSet := range m.hostCounters {
// some counters need two data samples before computing a value
if err = hostCounterSet.query.collectData(); err != nil {
return m.checkError(err)
}
}
m.lastRefreshed = time.Now()
// minimum time between collecting two samples
time.Sleep(time.Second)
}
for _, hostCounterSet := range m.hostCounters {
if m.UsePerfCounterTime && hostCounterSet.query.isVistaOrNewer() {
hostCounterSet.timestamp, err = hostCounterSet.query.collectDataWithTime()
if err != nil {
return err
}
} else {
hostCounterSet.timestamp = time.Now()
if err := hostCounterSet.query.collectData(); err != nil {
return err
}
}
}
var wg sync.WaitGroup
// iterate over computers
for _, hostCounterInfo := range m.hostCounters {
wg.Add(1)
go func(hostInfo *hostCountersInfo) {
m.Log.Debugf("Gathering from %s", hostInfo.computer)
start := time.Now()
err := m.gatherComputerCounters(hostInfo, acc)
m.Log.Debugf("Gathering from %s finished in %v", hostInfo.computer, time.Since(start))
if err != nil && m.checkError(err) != nil {
acc.AddError(fmt.Errorf("error during collecting data on host %q: %w", hostInfo.computer, err))
}
wg.Done()
}(hostCounterInfo)
}
wg.Wait()
return nil
}
// extractCounterInfoFromCounterPath gets object name, instance name (if available) and counter name from counter path // extractCounterInfoFromCounterPath gets object name, instance name (if available) and counter name from counter path
// General Counter path pattern is: \\computer\object(parent/instance#index)\counter // General Counter path pattern is: \\computer\object(parent/instance#index)\counter
@ -153,10 +253,6 @@ func extractCounterInfoFromCounterPath(counterPath string) (computer string, obj
return computer, object, instance, counter, nil return computer, object, instance, counter, nil
} }
func (*WinPerfCounters) SampleConfig() string {
return sampleConfig
}
func (m *WinPerfCounters) hostname() string { func (m *WinPerfCounters) hostname() string {
if m.cachedHostname != "" { if m.cachedHostname != "" {
return m.cachedHostname return m.cachedHostname
@ -195,7 +291,7 @@ func newCounter(
} }
//nolint:revive //argument-limit conditionally more arguments allowed //nolint:revive //argument-limit conditionally more arguments allowed
func (m *WinPerfCounters) AddItem(counterPath, computer, objectName, instance, counterName, measurement string, includeTotal bool, useRawValue bool) error { func (m *WinPerfCounters) addItem(counterPath, computer, objectName, instance, counterName, measurement string, includeTotal bool, useRawValue bool) error {
origCounterPath := counterPath origCounterPath := counterPath
var err error var err error
var counterHandle pdhCounterHandle var counterHandle pdhCounterHandle
@ -211,20 +307,20 @@ func (m *WinPerfCounters) AddItem(counterPath, computer, objectName, instance, c
if !ok { if !ok {
hostCounter = &hostCountersInfo{computer: computer, tag: sourceTag} hostCounter = &hostCountersInfo{computer: computer, tag: sourceTag}
m.hostCounters[computer] = hostCounter m.hostCounters[computer] = hostCounter
hostCounter.query = m.queryCreator.NewPerformanceQuery(computer, uint32(m.MaxBufferSize)) hostCounter.query = m.queryCreator.newPerformanceQuery(computer, uint32(m.MaxBufferSize))
if err := hostCounter.query.Open(); err != nil { if err := hostCounter.query.open(); err != nil {
return err return err
} }
hostCounter.counters = make([]*counter, 0) hostCounter.counters = make([]*counter, 0)
} }
if !hostCounter.query.IsVistaOrNewer() { if !hostCounter.query.isVistaOrNewer() {
counterHandle, err = hostCounter.query.AddCounterToQuery(counterPath) counterHandle, err = hostCounter.query.addCounterToQuery(counterPath)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
counterHandle, err = hostCounter.query.AddEnglishCounterToQuery(counterPath) counterHandle, err = hostCounter.query.addEnglishCounterToQuery(counterPath)
if err != nil { if err != nil {
return err return err
} }
@ -232,11 +328,11 @@ func (m *WinPerfCounters) AddItem(counterPath, computer, objectName, instance, c
if m.UseWildcardsExpansion { if m.UseWildcardsExpansion {
origInstance := instance origInstance := instance
counterPath, err = hostCounter.query.GetCounterPath(counterHandle) counterPath, err = hostCounter.query.getCounterPath(counterHandle)
if err != nil { if err != nil {
return err return err
} }
counters, err := hostCounter.query.ExpandWildCardPath(counterPath) counters, err := hostCounter.query.expandWildCardPath(counterPath)
if err != nil { if err != nil {
return err return err
} }
@ -247,7 +343,7 @@ func (m *WinPerfCounters) AddItem(counterPath, computer, objectName, instance, c
} }
for _, counterPath := range counters { for _, counterPath := range counters {
_, err := hostCounter.query.AddCounterToQuery(counterPath) _, err := hostCounter.query.addCounterToQuery(counterPath)
if err != nil { if err != nil {
return err return err
} }
@ -261,7 +357,7 @@ func (m *WinPerfCounters) AddItem(counterPath, computer, objectName, instance, c
if !m.LocalizeWildcardsExpansion { if !m.LocalizeWildcardsExpansion {
// On localized installations of Windows, Telegraf // On localized installations of Windows, Telegraf
// should return English metrics, but // should return English metrics, but
// ExpandWildCardPath returns localized counters. Undo // expandWildCardPath returns localized counters. Undo
// that by using the original object and counter // that by using the original object and counter
// names, along with the expanded instance. // names, along with the expanded instance.
@ -272,7 +368,7 @@ func (m *WinPerfCounters) AddItem(counterPath, computer, objectName, instance, c
newInstance = instance newInstance = instance
} }
counterPath = formatPath(computer, origObjectName, newInstance, origCounterName) counterPath = formatPath(computer, origObjectName, newInstance, origCounterName)
counterHandle, err = hostCounter.query.AddEnglishCounterToQuery(counterPath) counterHandle, err = hostCounter.query.addEnglishCounterToQuery(counterPath)
if err != nil { if err != nil {
return err return err
} }
@ -287,7 +383,7 @@ func (m *WinPerfCounters) AddItem(counterPath, computer, objectName, instance, c
useRawValue, useRawValue,
) )
} else { } else {
counterHandle, err = hostCounter.query.AddCounterToQuery(counterPath) counterHandle, err = hostCounter.query.addCounterToQuery(counterPath)
if err != nil { if err != nil {
return err return err
} }
@ -335,8 +431,6 @@ func (m *WinPerfCounters) AddItem(counterPath, computer, objectName, instance, c
return nil return nil
} }
const emptyInstance = "------"
func formatPath(computer, objectName, instance, counter string) string { func formatPath(computer, objectName, instance, counter string) string {
path := "" path := ""
if instance == emptyInstance { if instance == emptyInstance {
@ -350,7 +444,7 @@ func formatPath(computer, objectName, instance, counter string) string {
return path return path
} }
func (m *WinPerfCounters) ParseConfig() error { func (m *WinPerfCounters) parseConfig() error {
var counterPath string var counterPath string
if len(m.Sources) == 0 { if len(m.Sources) == 0 {
@ -380,7 +474,7 @@ func (m *WinPerfCounters) ParseConfig() error {
objectName := PerfObject.ObjectName objectName := PerfObject.ObjectName
counterPath = formatPath(computer, objectName, instance, counter) counterPath = formatPath(computer, objectName, instance, counter)
err := m.AddItem(counterPath, computer, objectName, instance, counter, err := m.addItem(counterPath, computer, objectName, instance, counter,
PerfObject.Measurement, PerfObject.IncludeTotal, PerfObject.UseRawValues) PerfObject.Measurement, PerfObject.IncludeTotal, PerfObject.UseRawValues)
if err != nil { if err != nil {
if PerfObject.FailOnMissing || PerfObject.WarnOnMissing { if PerfObject.FailOnMissing || PerfObject.WarnOnMissing {
@ -402,7 +496,7 @@ func (m *WinPerfCounters) checkError(err error) error {
var pdhErr *pdhError var pdhErr *pdhError
if errors.As(err, &pdhErr) { if errors.As(err, &pdhErr) {
for _, ignoredErrors := range m.IgnoredErrors { for _, ignoredErrors := range m.IgnoredErrors {
if PDHErrors[pdhErr.ErrorCode] == ignoredErrors { if pdhErrors[pdhErr.errorCode] == ignoredErrors {
return nil return nil
} }
} }
@ -412,62 +506,6 @@ func (m *WinPerfCounters) checkError(err error) error {
return err return err
} }
func (m *WinPerfCounters) Gather(acc telegraf.Accumulator) error {
// Parse the config once
var err error
if m.lastRefreshed.IsZero() || (m.CountersRefreshInterval > 0 && m.lastRefreshed.Add(time.Duration(m.CountersRefreshInterval)).Before(time.Now())) {
if err := m.cleanQueries(); err != nil {
return err
}
if err := m.ParseConfig(); err != nil {
return err
}
for _, hostCounterSet := range m.hostCounters {
// some counters need two data samples before computing a value
if err = hostCounterSet.query.CollectData(); err != nil {
return m.checkError(err)
}
}
m.lastRefreshed = time.Now()
// minimum time between collecting two samples
time.Sleep(time.Second)
}
for _, hostCounterSet := range m.hostCounters {
if m.UsePerfCounterTime && hostCounterSet.query.IsVistaOrNewer() {
hostCounterSet.timestamp, err = hostCounterSet.query.CollectDataWithTime()
if err != nil {
return err
}
} else {
hostCounterSet.timestamp = time.Now()
if err := hostCounterSet.query.CollectData(); err != nil {
return err
}
}
}
var wg sync.WaitGroup
// iterate over computers
for _, hostCounterInfo := range m.hostCounters {
wg.Add(1)
go func(hostInfo *hostCountersInfo) {
m.Log.Debugf("Gathering from %s", hostInfo.computer)
start := time.Now()
err := m.gatherComputerCounters(hostInfo, acc)
m.Log.Debugf("Gathering from %s finished in %v", hostInfo.computer, time.Since(start))
if err != nil && m.checkError(err) != nil {
acc.AddError(fmt.Errorf("error during collecting data on host %q: %w", hostInfo.computer, err))
}
wg.Done()
}(hostCounterInfo)
}
wg.Wait()
return nil
}
func (m *WinPerfCounters) gatherComputerCounters(hostCounterInfo *hostCountersInfo, acc telegraf.Accumulator) error { func (m *WinPerfCounters) gatherComputerCounters(hostCounterInfo *hostCountersInfo, acc telegraf.Accumulator) error {
var value interface{} var value interface{}
var err error var err error
@ -477,9 +515,9 @@ func (m *WinPerfCounters) gatherComputerCounters(hostCounterInfo *hostCountersIn
// collect // collect
if m.UseWildcardsExpansion { if m.UseWildcardsExpansion {
if metric.useRawValue { if metric.useRawValue {
value, err = hostCounterInfo.query.GetRawCounterValue(metric.counterHandle) value, err = hostCounterInfo.query.getRawCounterValue(metric.counterHandle)
} else { } else {
value, err = hostCounterInfo.query.GetFormattedCounterValueDouble(metric.counterHandle) value, err = hostCounterInfo.query.getFormattedCounterValueDouble(metric.counterHandle)
} }
if err != nil { if err != nil {
// ignore invalid data as some counters from process instances returns this sometimes // ignore invalid data as some counters from process instances returns this sometimes
@ -493,9 +531,9 @@ func (m *WinPerfCounters) gatherComputerCounters(hostCounterInfo *hostCountersIn
} else { } else {
var counterValues []counterValue var counterValues []counterValue
if metric.useRawValue { if metric.useRawValue {
counterValues, err = hostCounterInfo.query.GetRawCounterArray(metric.counterHandle) counterValues, err = hostCounterInfo.query.getRawCounterArray(metric.counterHandle)
} else { } else {
counterValues, err = hostCounterInfo.query.GetFormattedCounterArrayDouble(metric.counterHandle) counterValues, err = hostCounterInfo.query.getFormattedCounterArrayDouble(metric.counterHandle)
} }
if err != nil { if err != nil {
// ignore invalid data as some counters from process instances returns this sometimes // ignore invalid data as some counters from process instances returns this sometimes
@ -506,14 +544,14 @@ func (m *WinPerfCounters) gatherComputerCounters(hostCounterInfo *hostCountersIn
continue continue
} }
for _, cValue := range counterValues { for _, cValue := range counterValues {
if strings.Contains(metric.instance, "#") && strings.HasPrefix(metric.instance, cValue.InstanceName) { if strings.Contains(metric.instance, "#") && strings.HasPrefix(metric.instance, cValue.instanceName) {
// If you are using a multiple instance identifier such as "w3wp#1" // If you are using a multiple instance identifier such as "w3wp#1"
// phd.dll returns only the first 2 characters of the identifier. // phd.dll returns only the first 2 characters of the identifier.
cValue.InstanceName = metric.instance cValue.instanceName = metric.instance
} }
if shouldIncludeMetric(metric, cValue) { if shouldIncludeMetric(metric, cValue) {
addCounterMeasurement(metric, cValue.InstanceName, cValue.Value, collectedFields) addCounterMeasurement(metric, cValue.instanceName, cValue.value, collectedFields)
} }
} }
} }
@ -535,7 +573,7 @@ func (m *WinPerfCounters) gatherComputerCounters(hostCounterInfo *hostCountersIn
func (m *WinPerfCounters) cleanQueries() error { func (m *WinPerfCounters) cleanQueries() error {
for _, hostCounterInfo := range m.hostCounters { for _, hostCounterInfo := range m.hostCounters {
if err := hostCounterInfo.query.Close(); err != nil { if err := hostCounterInfo.query.close(); err != nil {
return err return err
} }
} }
@ -548,11 +586,11 @@ func shouldIncludeMetric(metric *counter, cValue counterValue) bool {
// If IncludeTotal is set, include all. // If IncludeTotal is set, include all.
return true return true
} }
if metric.instance == "*" && !strings.Contains(cValue.InstanceName, "_Total") { if metric.instance == "*" && !strings.Contains(cValue.instanceName, "_Total") {
// Catch if set to * and that it is not a '*_Total*' instance. // Catch if set to * and that it is not a '*_Total*' instance.
return true return true
} }
if metric.instance == cValue.InstanceName { if metric.instance == cValue.instanceName {
// Catch if we set it to total or some form of it // Catch if we set it to total or some form of it
return true return true
} }
@ -572,55 +610,17 @@ func addCounterMeasurement(metric *counter, instanceName string, value interface
func isKnownCounterDataError(err error) bool { func isKnownCounterDataError(err error) bool {
var pdhErr *pdhError var pdhErr *pdhError
if errors.As(err, &pdhErr) && (pdhErr.ErrorCode == PdhInvalidData || if errors.As(err, &pdhErr) && (pdhErr.errorCode == pdhInvalidData ||
pdhErr.ErrorCode == PdhCalcNegativeDenominator || pdhErr.errorCode == pdhCalcNegativeDenominator ||
pdhErr.ErrorCode == PdhCalcNegativeValue || pdhErr.errorCode == pdhCalcNegativeValue ||
pdhErr.ErrorCode == PdhCstatusInvalidData || pdhErr.errorCode == pdhCstatusInvalidData ||
pdhErr.ErrorCode == PdhCstatusNoInstance || pdhErr.errorCode == pdhCstatusNoInstance ||
pdhErr.ErrorCode == PdhNoData) { pdhErr.errorCode == pdhNoData) {
return true return true
} }
return false return false
} }
func (m *WinPerfCounters) Init() error {
// Check the buffer size
if m.MaxBufferSize < config.Size(initialBufferSize) {
return fmt.Errorf("maximum buffer size should at least be %d", 2*initialBufferSize)
}
if m.MaxBufferSize > math.MaxUint32 {
return fmt.Errorf("maximum buffer size should be smaller than %d", uint32(math.MaxUint32))
}
if m.UseWildcardsExpansion && !m.LocalizeWildcardsExpansion {
// Counters must not have wildcards with this option
found := false
wildcards := []string{"*", "?"}
for _, object := range m.Object {
for _, wildcard := range wildcards {
if strings.Contains(object.ObjectName, wildcard) {
found = true
m.Log.Errorf("Object: %s, contains wildcard %s", object.ObjectName, wildcard)
}
}
for _, counter := range object.Counters {
for _, wildcard := range wildcards {
if strings.Contains(counter, wildcard) {
found = true
m.Log.Errorf("Object: %s, counter: %s contains wildcard %s", object.ObjectName, counter, wildcard)
}
}
}
}
if found {
return errors.New("wildcards can't be used with LocalizeWildcardsExpansion=false")
}
}
return nil
}
func init() { func init() {
inputs.Add("win_perf_counters", func() telegraf.Input { inputs.Add("win_perf_counters", func() telegraf.Input {
return &WinPerfCounters{ return &WinPerfCounters{

View File

@ -19,97 +19,97 @@ func TestWinPerformanceQueryImplIntegration(t *testing.T) {
} }
query := &performanceQueryImpl{maxBufferSize: uint32(defaultMaxBufferSize)} query := &performanceQueryImpl{maxBufferSize: uint32(defaultMaxBufferSize)}
err := query.Close() err := query.close()
require.Error(t, err, "uninitialized query must return errors") require.Error(t, err, "uninitialized query must return errors")
_, err = query.AddCounterToQuery("") _, err = query.addCounterToQuery("")
require.Error(t, err, "uninitialized query must return errors") require.Error(t, err, "uninitialized query must return errors")
require.ErrorContains(t, err, "uninitialized") require.ErrorContains(t, err, "uninitialized")
_, err = query.AddEnglishCounterToQuery("") _, err = query.addEnglishCounterToQuery("")
require.Error(t, err, "uninitialized query must return errors") require.Error(t, err, "uninitialized query must return errors")
require.ErrorContains(t, err, "uninitialized") require.ErrorContains(t, err, "uninitialized")
err = query.CollectData() err = query.collectData()
require.Error(t, err, "uninitialized query must return errors") require.Error(t, err, "uninitialized query must return errors")
require.ErrorContains(t, err, "uninitialized") require.ErrorContains(t, err, "uninitialized")
require.NoError(t, query.Open()) require.NoError(t, query.open())
counterPath := "\\Processor Information(_Total)\\% Processor Time" counterPath := "\\Processor Information(_Total)\\% Processor Time"
hCounter, err := query.AddCounterToQuery(counterPath) hCounter, err := query.addCounterToQuery(counterPath)
require.NoError(t, err) require.NoError(t, err)
require.NotEqual(t, 0, hCounter) require.NotEqual(t, 0, hCounter)
require.NoError(t, query.Close()) require.NoError(t, query.close())
require.NoError(t, query.Open()) require.NoError(t, query.open())
hCounter, err = query.AddEnglishCounterToQuery(counterPath) hCounter, err = query.addEnglishCounterToQuery(counterPath)
require.NoError(t, err) require.NoError(t, err)
require.NotEqual(t, 0, hCounter) require.NotEqual(t, 0, hCounter)
cp, err := query.GetCounterPath(hCounter) cp, err := query.getCounterPath(hCounter)
require.NoError(t, err) require.NoError(t, err)
require.True(t, strings.HasSuffix(cp, counterPath)) require.True(t, strings.HasSuffix(cp, counterPath))
require.NoError(t, query.CollectData()) require.NoError(t, query.collectData())
time.Sleep(time.Second) time.Sleep(time.Second)
require.NoError(t, query.CollectData()) require.NoError(t, query.collectData())
fcounter, err := query.GetFormattedCounterValueDouble(hCounter) fcounter, err := query.getFormattedCounterValueDouble(hCounter)
require.NoError(t, err) require.NoError(t, err)
require.Greater(t, fcounter, float64(0)) require.Greater(t, fcounter, float64(0))
rcounter, err := query.GetRawCounterValue(hCounter) rcounter, err := query.getRawCounterValue(hCounter)
require.NoError(t, err) require.NoError(t, err)
require.Greater(t, rcounter, int64(10000000)) require.Greater(t, rcounter, int64(10000000))
now := time.Now() now := time.Now()
mtime, err := query.CollectDataWithTime() mtime, err := query.collectDataWithTime()
require.NoError(t, err) require.NoError(t, err)
require.Less(t, mtime.Sub(now), time.Second) require.Less(t, mtime.Sub(now), time.Second)
counterPath = "\\Process(*)\\% Processor Time" counterPath = "\\Process(*)\\% Processor Time"
paths, err := query.ExpandWildCardPath(counterPath) paths, err := query.expandWildCardPath(counterPath)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, paths) require.NotNil(t, paths)
require.Greater(t, len(paths), 1) require.Greater(t, len(paths), 1)
counterPath = "\\Process(_Total)\\*" counterPath = "\\Process(_Total)\\*"
paths, err = query.ExpandWildCardPath(counterPath) paths, err = query.expandWildCardPath(counterPath)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, paths) require.NotNil(t, paths)
require.Greater(t, len(paths), 1) require.Greater(t, len(paths), 1)
require.NoError(t, query.Open()) require.NoError(t, query.open())
counterPath = "\\Process(*)\\% Processor Time" counterPath = "\\Process(*)\\% Processor Time"
hCounter, err = query.AddEnglishCounterToQuery(counterPath) hCounter, err = query.addEnglishCounterToQuery(counterPath)
require.NoError(t, err) require.NoError(t, err)
require.NotEqual(t, 0, hCounter) require.NotEqual(t, 0, hCounter)
require.NoError(t, query.CollectData()) require.NoError(t, query.collectData())
time.Sleep(time.Second) time.Sleep(time.Second)
require.NoError(t, query.CollectData()) require.NoError(t, query.collectData())
farr, err := query.GetFormattedCounterArrayDouble(hCounter) farr, err := query.getFormattedCounterArrayDouble(hCounter)
var phdErr *pdhError var phdErr *pdhError
if errors.As(err, &phdErr) && phdErr.ErrorCode != PdhInvalidData && phdErr.ErrorCode != PdhCalcNegativeValue { if errors.As(err, &phdErr) && phdErr.errorCode != pdhInvalidData && phdErr.errorCode != pdhCalcNegativeValue {
time.Sleep(time.Second) time.Sleep(time.Second)
farr, err = query.GetFormattedCounterArrayDouble(hCounter) farr, err = query.getFormattedCounterArrayDouble(hCounter)
} }
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, farr) require.NotEmpty(t, farr)
rarr, err := query.GetRawCounterArray(hCounter) rarr, err := query.getRawCounterArray(hCounter)
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, rarr, "Too") require.NotEmpty(t, rarr, "Too")
require.NoError(t, query.Close()) require.NoError(t, query.close())
} }
func TestWinPerfCountersConfigGet1Integration(t *testing.T) { func TestWinPerfCountersConfigGet1Integration(t *testing.T) {
@ -137,7 +137,7 @@ func TestWinPerfCountersConfigGet1Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.NoError(t, m.ParseConfig()) require.NoError(t, m.parseConfig())
} }
func TestWinPerfCountersConfigGet2Integration(t *testing.T) { func TestWinPerfCountersConfigGet2Integration(t *testing.T) {
@ -165,7 +165,7 @@ func TestWinPerfCountersConfigGet2Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.NoError(t, m.ParseConfig()) require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"] hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -204,7 +204,7 @@ func TestWinPerfCountersConfigGet3Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.NoError(t, m.ParseConfig()) require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"] hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -241,7 +241,7 @@ func TestWinPerfCountersConfigGet4Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.NoError(t, m.ParseConfig()) require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"] hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -278,7 +278,7 @@ func TestWinPerfCountersConfigGet5Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.NoError(t, m.ParseConfig()) require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"] hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -315,7 +315,7 @@ func TestWinPerfCountersConfigGet6Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.NoError(t, m.ParseConfig()) require.NoError(t, m.parseConfig())
_, ok := m.hostCounters["localhost"] _, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -343,7 +343,7 @@ func TestWinPerfCountersConfigGet7Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.NoError(t, m.ParseConfig()) require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"] hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -380,7 +380,7 @@ func TestWinPerfCountersConfigError1Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.Error(t, m.ParseConfig()) require.Error(t, m.parseConfig())
} }
func TestWinPerfCountersConfigError2Integration(t *testing.T) { func TestWinPerfCountersConfigError2Integration(t *testing.T) {
@ -408,7 +408,7 @@ func TestWinPerfCountersConfigError2Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.NoError(t, m.ParseConfig()) require.NoError(t, m.parseConfig())
var acc testutil.Accumulator var acc testutil.Accumulator
require.Error(t, m.Gather(&acc)) require.Error(t, m.Gather(&acc))
} }
@ -438,7 +438,7 @@ func TestWinPerfCountersConfigError3Integration(t *testing.T) {
queryCreator: &performanceQueryCreatorImpl{}, queryCreator: &performanceQueryCreatorImpl{},
} }
require.Error(t, m.ParseConfig()) require.Error(t, m.parseConfig())
} }
func TestWinPerfCountersCollect1Integration(t *testing.T) { func TestWinPerfCountersCollect1Integration(t *testing.T) {

View File

@ -20,7 +20,7 @@ type WinPerfCounters struct {
func (*WinPerfCounters) SampleConfig() string { return sampleConfig } func (*WinPerfCounters) SampleConfig() string { return sampleConfig }
func (w *WinPerfCounters) Init() error { func (w *WinPerfCounters) Init() error {
w.Log.Warn("current platform is not supported") w.Log.Warn("Current platform is not supported")
return nil return nil
} }

View File

@ -22,16 +22,16 @@ type testCounter struct {
status uint32 // allows for tests against specific pdh_error codes, rather than assuming all cases of "value == 0" to indicate error conditions status uint32 // allows for tests against specific pdh_error codes, rather than assuming all cases of "value == 0" to indicate error conditions
} }
type FakePerformanceQuery struct { type fakePerformanceQuery struct {
counters map[string]testCounter counters map[string]testCounter
vistaAndNewer bool vistaAndNewer bool
expandPaths map[string][]string expandPaths map[string][]string
openCalled bool openCalled bool
} }
var MetricTime = time.Date(2018, 5, 28, 12, 0, 0, 0, time.UTC) var metricTime = time.Date(2018, 5, 28, 12, 0, 0, 0, time.UTC)
func (m *testCounter) ToCounterValue(raw bool) *counterValue { func (m *testCounter) toCounterValue(raw bool) *counterValue {
//nolint:dogsled,errcheck // only instance is needed for this helper function in tests //nolint:dogsled,errcheck // only instance is needed for this helper function in tests
_, _, inst, _, _ := extractCounterInfoFromCounterPath(m.path) _, _, inst, _, _ := extractCounterInfoFromCounterPath(m.path)
if inst == "" { if inst == "" {
@ -47,9 +47,9 @@ func (m *testCounter) ToCounterValue(raw bool) *counterValue {
return &counterValue{inst, val} return &counterValue{inst, val}
} }
func (m *FakePerformanceQuery) Open() error { func (m *fakePerformanceQuery) open() error {
if m.openCalled { if m.openCalled {
err := m.Close() err := m.close()
if err != nil { if err != nil {
return err return err
} }
@ -58,81 +58,81 @@ func (m *FakePerformanceQuery) Open() error {
return nil return nil
} }
func (m *FakePerformanceQuery) Close() error { func (m *fakePerformanceQuery) close() error {
if !m.openCalled { if !m.openCalled {
return errors.New("in Close: uninitialized query") return errors.New("in close: uninitialized query")
} }
m.openCalled = false m.openCalled = false
return nil return nil
} }
func (m *FakePerformanceQuery) AddCounterToQuery(counterPath string) (pdhCounterHandle, error) { func (m *fakePerformanceQuery) addCounterToQuery(counterPath string) (pdhCounterHandle, error) {
if !m.openCalled { if !m.openCalled {
return 0, errors.New("in AddCounterToQuery: uninitialized query") return 0, errors.New("in addCounterToQuery: uninitialized query")
} }
if c, ok := m.counters[counterPath]; ok { if c, ok := m.counters[counterPath]; ok {
return c.handle, nil return c.handle, nil
} }
return 0, fmt.Errorf("in AddCounterToQuery: invalid counter path: %q", counterPath) return 0, fmt.Errorf("in addCounterToQuery: invalid counter path: %q", counterPath)
} }
func (m *FakePerformanceQuery) AddEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error) { func (m *fakePerformanceQuery) addEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error) {
if !m.openCalled { if !m.openCalled {
return 0, errors.New("in AddEnglishCounterToQuery: uninitialized query") return 0, errors.New("in addEnglishCounterToQuery: uninitialized query")
} }
if c, ok := m.counters[counterPath]; ok { if c, ok := m.counters[counterPath]; ok {
return c.handle, nil return c.handle, nil
} }
return 0, fmt.Errorf("in AddEnglishCounterToQuery: invalid counter path: %q", counterPath) return 0, fmt.Errorf("in addEnglishCounterToQuery: invalid counter path: %q", counterPath)
} }
func (m *FakePerformanceQuery) GetCounterPath(counterHandle pdhCounterHandle) (string, error) { func (m *fakePerformanceQuery) getCounterPath(counterHandle pdhCounterHandle) (string, error) {
for _, counter := range m.counters { for _, counter := range m.counters {
if counter.handle == counterHandle { if counter.handle == counterHandle {
return counter.path, nil return counter.path, nil
} }
} }
return "", fmt.Errorf("in GetCounterPath: invalid handle: %q", counterHandle) return "", fmt.Errorf("in getCounterPath: invalid handle: %q", counterHandle)
} }
func (m *FakePerformanceQuery) ExpandWildCardPath(counterPath string) ([]string, error) { func (m *fakePerformanceQuery) expandWildCardPath(counterPath string) ([]string, error) {
if e, ok := m.expandPaths[counterPath]; ok { if e, ok := m.expandPaths[counterPath]; ok {
return e, nil return e, nil
} }
return nil, fmt.Errorf("in ExpandWildCardPath: invalid counter path: %q", counterPath) return nil, fmt.Errorf("in expandWildCardPath: invalid counter path: %q", counterPath)
} }
func (m *FakePerformanceQuery) GetFormattedCounterValueDouble(counterHandle pdhCounterHandle) (float64, error) { func (m *fakePerformanceQuery) getFormattedCounterValueDouble(counterHandle pdhCounterHandle) (float64, error) {
if !m.openCalled { if !m.openCalled {
return 0, errors.New("in GetFormattedCounterValueDouble: uninitialized query") return 0, errors.New("in getFormattedCounterValueDouble: uninitialized query")
} }
for _, counter := range m.counters { for _, counter := range m.counters {
if counter.handle == counterHandle { if counter.handle == counterHandle {
if counter.status > 0 { if counter.status > 0 {
return 0, NewPdhError(counter.status) return 0, newPdhError(counter.status)
} }
return counter.value, nil return counter.value, nil
} }
} }
return 0, fmt.Errorf("in GetFormattedCounterValueDouble: invalid handle: %q", counterHandle) return 0, fmt.Errorf("in getFormattedCounterValueDouble: invalid handle: %q", counterHandle)
} }
func (m *FakePerformanceQuery) GetRawCounterValue(counterHandle pdhCounterHandle) (int64, error) { func (m *fakePerformanceQuery) getRawCounterValue(counterHandle pdhCounterHandle) (int64, error) {
if !m.openCalled { if !m.openCalled {
return 0, errors.New("in GetRawCounterValue: uninitialised query") return 0, errors.New("in getRawCounterValue: uninitialised query")
} }
for _, counter := range m.counters { for _, counter := range m.counters {
if counter.handle == counterHandle { if counter.handle == counterHandle {
if counter.status > 0 { if counter.status > 0 {
return 0, NewPdhError(counter.status) return 0, newPdhError(counter.status)
} }
return int64(counter.value), nil return int64(counter.value), nil
} }
} }
return 0, fmt.Errorf("in GetRawCounterValue: invalid handle: %q", counterHandle) return 0, fmt.Errorf("in getRawCounterValue: invalid handle: %q", counterHandle)
} }
func (m *FakePerformanceQuery) findCounterByPath(counterPath string) *testCounter { func (m *fakePerformanceQuery) findCounterByPath(counterPath string) *testCounter {
for _, c := range m.counters { for _, c := range m.counters {
if c.path == counterPath { if c.path == counterPath {
return &c return &c
@ -141,9 +141,9 @@ func (m *FakePerformanceQuery) findCounterByPath(counterPath string) *testCounte
return nil return nil
} }
func (m *FakePerformanceQuery) GetFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error) { func (m *fakePerformanceQuery) getFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error) {
if !m.openCalled { if !m.openCalled {
return nil, errors.New("in GetFormattedCounterArrayDouble: uninitialized query") return nil, errors.New("in getFormattedCounterArrayDouble: uninitialized query")
} }
for _, c := range m.counters { for _, c := range m.counters {
if c.handle == hCounter { if c.handle == hCounter {
@ -152,24 +152,24 @@ func (m *FakePerformanceQuery) GetFormattedCounterArrayDouble(hCounter pdhCounte
for _, p := range e { for _, p := range e {
counter := m.findCounterByPath(p) counter := m.findCounterByPath(p)
if counter == nil { if counter == nil {
return nil, fmt.Errorf("in GetFormattedCounterArrayDouble: invalid counter: %q", p) return nil, fmt.Errorf("in getFormattedCounterArrayDouble: invalid counter: %q", p)
} }
if counter.status > 0 { if counter.status > 0 {
return nil, NewPdhError(counter.status) return nil, newPdhError(counter.status)
} }
counters = append(counters, *counter.ToCounterValue(false)) counters = append(counters, *counter.toCounterValue(false))
} }
return counters, nil return counters, nil
} }
return nil, fmt.Errorf("in GetFormattedCounterArrayDouble: invalid counter: %q", hCounter) return nil, fmt.Errorf("in getFormattedCounterArrayDouble: invalid counter: %q", hCounter)
} }
} }
return nil, fmt.Errorf("in GetFormattedCounterArrayDouble: invalid counter: %q, no paths found", hCounter) return nil, fmt.Errorf("in getFormattedCounterArrayDouble: invalid counter: %q, no paths found", hCounter)
} }
func (m *FakePerformanceQuery) GetRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error) { func (m *fakePerformanceQuery) getRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error) {
if !m.openCalled { if !m.openCalled {
return nil, errors.New("in GetRawCounterArray: uninitialised query") return nil, errors.New("in getRawCounterArray: uninitialised query")
} }
for _, c := range m.counters { for _, c := range m.counters {
if c.handle == hCounter { if c.handle == hCounter {
@ -178,45 +178,45 @@ func (m *FakePerformanceQuery) GetRawCounterArray(hCounter pdhCounterHandle) ([]
for _, p := range e { for _, p := range e {
counter := m.findCounterByPath(p) counter := m.findCounterByPath(p)
if counter == nil { if counter == nil {
return nil, fmt.Errorf("in GetRawCounterArray: invalid counter: %q", p) return nil, fmt.Errorf("in getRawCounterArray: invalid counter: %q", p)
} }
if counter.status > 0 { if counter.status > 0 {
return nil, NewPdhError(counter.status) return nil, newPdhError(counter.status)
} }
counters = append(counters, *counter.ToCounterValue(true)) counters = append(counters, *counter.toCounterValue(true))
} }
return counters, nil return counters, nil
} }
return nil, fmt.Errorf("in GetRawCounterArray: invalid counter: %q", hCounter) return nil, fmt.Errorf("in getRawCounterArray: invalid counter: %q", hCounter)
} }
} }
return nil, fmt.Errorf("in GetRawCounterArray: invalid counter: %q, no paths found", hCounter) return nil, fmt.Errorf("in getRawCounterArray: invalid counter: %q, no paths found", hCounter)
} }
func (m *FakePerformanceQuery) CollectData() error { func (m *fakePerformanceQuery) collectData() error {
if !m.openCalled { if !m.openCalled {
return errors.New("in CollectData: uninitialized query") return errors.New("in collectData: uninitialized query")
} }
return nil return nil
} }
func (m *FakePerformanceQuery) CollectDataWithTime() (time.Time, error) { func (m *fakePerformanceQuery) collectDataWithTime() (time.Time, error) {
if !m.openCalled { if !m.openCalled {
return time.Now(), errors.New("in CollectDataWithTime: uninitialized query") return time.Now(), errors.New("in collectDataWithTime: uninitialized query")
} }
return MetricTime, nil return metricTime, nil
} }
func (m *FakePerformanceQuery) IsVistaOrNewer() bool { func (m *fakePerformanceQuery) isVistaOrNewer() bool {
return m.vistaAndNewer return m.vistaAndNewer
} }
type FakePerformanceQueryCreator struct { type fakePerformanceQueryCreator struct {
fakeQueries map[string]*FakePerformanceQuery fakeQueries map[string]*fakePerformanceQuery
} }
func (m FakePerformanceQueryCreator) NewPerformanceQuery(computer string, _ uint32) PerformanceQuery { func (m fakePerformanceQueryCreator) newPerformanceQuery(computer string, _ uint32) performanceQuery {
var ret PerformanceQuery var ret performanceQuery
var ok bool var ok bool
if ret, ok = m.fakeQueries[computer]; !ok { if ret, ok = m.fakeQueries[computer]; !ok {
panic(fmt.Errorf("query for %q not found", computer)) panic(fmt.Errorf("query for %q not found", computer))
@ -332,8 +332,8 @@ func TestAddItemSimple(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: nil, Object: nil,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, []float64{1.1}, []uint32{0}), counters: createCounterMap(cps1, []float64{1.1}, []uint32{0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cps1[0]: cps1, cps1[0]: cps1,
@ -343,7 +343,7 @@ func TestAddItemSimple(t *testing.T) {
}, },
}, },
} }
err = m.AddItem(cps1[0], "localhost", "O", "I", "c", "test", false, false) err = m.addItem(cps1[0], "localhost", "O", "I", "c", "test", false, false)
require.NoError(t, err) require.NoError(t, err)
counters, ok := m.hostCounters["localhost"] counters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -364,8 +364,8 @@ func TestAddItemInvalidCountPath(t *testing.T) {
PrintValid: false, PrintValid: false,
Object: nil, Object: nil,
UseWildcardsExpansion: true, UseWildcardsExpansion: true,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, []float64{1.1}, []uint32{0}), counters: createCounterMap(cps1, []float64{1.1}, []uint32{0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cps1[0]: {"\\O/C"}, cps1[0]: {"\\O/C"},
@ -376,7 +376,7 @@ func TestAddItemInvalidCountPath(t *testing.T) {
}, },
} }
require.NoError(t, err) require.NoError(t, err)
err = m.AddItem("\\O\\C", "localhost", "O", "------", "C", "test", false, false) err = m.addItem("\\O\\C", "localhost", "O", "------", "C", "test", false, false)
require.Error(t, err) require.Error(t, err)
} }
@ -389,8 +389,8 @@ func TestParseConfigBasic(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, []float64{1.1, 1.2, 1.3, 1.4}, []uint32{0, 0, 0, 0}), counters: createCounterMap(cps1, []float64{1.1, 1.2, 1.3, 1.4}, []uint32{0, 0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cps1[0]: {cps1[0]}, cps1[0]: {cps1[0]},
@ -404,7 +404,7 @@ func TestParseConfigBasic(t *testing.T) {
}, },
} }
require.NoError(t, err) require.NoError(t, err)
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
counters, ok := m.hostCounters["localhost"] counters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -413,7 +413,7 @@ func TestParseConfigBasic(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
m.UseWildcardsExpansion = true m.UseWildcardsExpansion = true
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
counters, ok = m.hostCounters["localhost"] counters, ok = m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -441,8 +441,8 @@ func TestParseConfigMultiComps(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{ fakeQueries: map[string]*fakePerformanceQuery{
"localhost": { "localhost": {
counters: createCounterMap(append(append(cps11, cps21...), cps31...), counters: createCounterMap(append(append(cps11, cps21...), cps31...),
[]float64{1.1, 1.1, 1.2, 2.1, 2.2, 1.1, 1.2, 1.3}, []float64{1.1, 1.1, 1.2, 2.1, 2.2, 1.1, 1.2, 1.3},
@ -495,7 +495,7 @@ func TestParseConfigMultiComps(t *testing.T) {
}, },
} }
require.NoError(t, err) require.NoError(t, err)
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
require.Len(t, m.hostCounters, 3) require.Len(t, m.hostCounters, 3)
@ -673,8 +673,8 @@ func TestParseConfigMultiCompsOverrideMultiplePerfObjects(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, counters: createCounterMap(cps1,
[]float64{1.1, 1.2, 1.3, 1.4}, []float64{1.1, 1.2, 1.3, 1.4},
[]uint32{0, 0, 0, 0}), []uint32{0, 0, 0, 0}),
@ -714,7 +714,7 @@ func TestParseConfigMultiCompsOverrideMultiplePerfObjects(t *testing.T) {
}, },
} }
require.NoError(t, err) require.NoError(t, err)
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
require.Len(t, m.hostCounters, 3) require.Len(t, m.hostCounters, 3)
@ -825,8 +825,8 @@ func TestParseConfigMultiCompsOverrideOnePerfObject(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: []perfObject{perfObj, createPerfObject("", "m", "O1", []string{"I"}, []string{"C"}, false, false, false)[0]}, Object: []perfObject{perfObj, createPerfObject("", "m", "O1", []string{"I"}, []string{"C"}, false, false, false)[0]},
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{ fakeQueries: map[string]*fakePerformanceQuery{
"localhost": { "localhost": {
counters: createCounterMap(cps21, counters: createCounterMap(cps21,
[]float64{1.1}, []float64{1.1},
@ -865,7 +865,7 @@ func TestParseConfigMultiCompsOverrideOnePerfObject(t *testing.T) {
}, },
} }
require.NoError(t, err) require.NoError(t, err)
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
require.Len(t, m.hostCounters, 3) require.Len(t, m.hostCounters, 3)
@ -953,8 +953,8 @@ func TestParseConfigLocalhost(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, []float64{1.1}, []uint32{0}), counters: createCounterMap(cps1, []float64{1.1}, []uint32{0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cps1[0]: {cps1[0]}, cps1[0]: {cps1[0]},
@ -965,7 +965,7 @@ func TestParseConfigLocalhost(t *testing.T) {
}, },
} }
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
hostCounters, ok := m.hostCounters["localhost"] hostCounters, ok := m.hostCounters["localhost"]
@ -980,7 +980,7 @@ func TestParseConfigLocalhost(t *testing.T) {
m.Object[0].Sources = []string{""} m.Object[0].Sources = []string{""}
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
hostCounters, ok = m.hostCounters["localhost"] hostCounters, ok = m.hostCounters["localhost"]
@ -1000,8 +1000,8 @@ func TestParseConfigNoInstance(t *testing.T) {
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
UseWildcardsExpansion: false, UseWildcardsExpansion: false,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, []float64{1.1, 1.2}, []uint32{0, 0}), counters: createCounterMap(cps1, []float64{1.1, 1.2}, []uint32{0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cps1[0]: {cps1[0]}, cps1[0]: {cps1[0]},
@ -1013,7 +1013,7 @@ func TestParseConfigNoInstance(t *testing.T) {
}, },
} }
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
counters, ok := m.hostCounters["localhost"] counters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -1023,7 +1023,7 @@ func TestParseConfigNoInstance(t *testing.T) {
err = m.cleanQueries() err = m.cleanQueries()
require.NoError(t, err) require.NoError(t, err)
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
counters, ok = m.hostCounters["localhost"] counters, ok = m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -1038,8 +1038,8 @@ func TestParseConfigInvalidCounterError(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, []float64{1.1, 1.2, 1.3}, []uint32{0, 0, 0}), counters: createCounterMap(cps1, []float64{1.1, 1.2, 1.3}, []uint32{0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cps1[0]: {cps1[0]}, cps1[0]: {cps1[0]},
@ -1052,14 +1052,14 @@ func TestParseConfigInvalidCounterError(t *testing.T) {
}, },
} }
err = m.ParseConfig() err = m.parseConfig()
require.Error(t, err) require.Error(t, err)
err = m.cleanQueries() err = m.cleanQueries()
require.NoError(t, err) require.NoError(t, err)
m.UseWildcardsExpansion = true m.UseWildcardsExpansion = true
err = m.ParseConfig() err = m.parseConfig()
require.Error(t, err) require.Error(t, err)
err = m.cleanQueries() err = m.cleanQueries()
require.NoError(t, err) require.NoError(t, err)
@ -1073,8 +1073,8 @@ func TestParseConfigInvalidCounterNoError(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, []float64{1.1, 1.2, 1.3}, []uint32{0, 0, 0}), counters: createCounterMap(cps1, []float64{1.1, 1.2, 1.3}, []uint32{0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cps1[0]: {cps1[0]}, cps1[0]: {cps1[0]},
@ -1087,14 +1087,14 @@ func TestParseConfigInvalidCounterNoError(t *testing.T) {
}, },
} }
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
err = m.cleanQueries() err = m.cleanQueries()
require.NoError(t, err) require.NoError(t, err)
m.UseWildcardsExpansion = true m.UseWildcardsExpansion = true
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
err = m.cleanQueries() err = m.cleanQueries()
require.NoError(t, err) require.NoError(t, err)
@ -1109,8 +1109,8 @@ func TestParseConfigTotalExpansion(t *testing.T) {
PrintValid: false, PrintValid: false,
UseWildcardsExpansion: true, UseWildcardsExpansion: true,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(append(cps1, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 0}, []uint32{0, 0, 0, 0, 0}), counters: createCounterMap(append(cps1, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 0}, []uint32{0, 0, 0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
"\\O(*)\\*": cps1, "\\O(*)\\*": cps1,
@ -1121,7 +1121,7 @@ func TestParseConfigTotalExpansion(t *testing.T) {
}, },
LocalizeWildcardsExpansion: true, LocalizeWildcardsExpansion: true,
} }
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
counters, ok := m.hostCounters["localhost"] counters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -1136,8 +1136,8 @@ func TestParseConfigTotalExpansion(t *testing.T) {
PrintValid: false, PrintValid: false,
UseWildcardsExpansion: true, UseWildcardsExpansion: true,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(append(cps1, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 0}, []uint32{0, 0, 0, 0, 0}), counters: createCounterMap(append(cps1, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 0}, []uint32{0, 0, 0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
"\\O(*)\\*": cps1, "\\O(*)\\*": cps1,
@ -1149,7 +1149,7 @@ func TestParseConfigTotalExpansion(t *testing.T) {
LocalizeWildcardsExpansion: true, LocalizeWildcardsExpansion: true,
} }
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
counters, ok = m.hostCounters["localhost"] counters, ok = m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -1167,8 +1167,8 @@ func TestParseConfigExpand(t *testing.T) {
PrintValid: false, PrintValid: false,
UseWildcardsExpansion: true, UseWildcardsExpansion: true,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(append(cps1, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 0}, []uint32{0, 0, 0, 0, 0}), counters: createCounterMap(append(cps1, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 0}, []uint32{0, 0, 0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
"\\O(*)\\*": cps1, "\\O(*)\\*": cps1,
@ -1179,7 +1179,7 @@ func TestParseConfigExpand(t *testing.T) {
}, },
LocalizeWildcardsExpansion: true, LocalizeWildcardsExpansion: true,
} }
err = m.ParseConfig() err = m.parseConfig()
require.NoError(t, err) require.NoError(t, err)
counters, ok := m.hostCounters["localhost"] counters, ok := m.hostCounters["localhost"]
require.True(t, ok) require.True(t, ok)
@ -1200,8 +1200,8 @@ func TestSimpleGather(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap([]string{cp1}, []float64{1.2}, []uint32{0}), counters: createCounterMap([]string{cp1}, []float64{1.2}, []uint32{0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cp1: {cp1}, cp1: {cp1},
@ -1251,9 +1251,9 @@ func TestSimpleGatherNoData(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap([]string{cp1}, []float64{1.2}, []uint32{PdhNoData}), counters: createCounterMap([]string{cp1}, []float64{1.2}, []uint32{pdhNoData}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cp1: {cp1}, cp1: {cp1},
}, },
@ -1305,8 +1305,8 @@ func TestSimpleGatherWithTimestamp(t *testing.T) {
PrintValid: false, PrintValid: false,
UsePerfCounterTime: true, UsePerfCounterTime: true,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap([]string{cp1}, []float64{1.2}, []uint32{0}), counters: createCounterMap([]string{cp1}, []float64{1.2}, []uint32{0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cp1: {cp1}, cp1: {cp1},
@ -1330,7 +1330,7 @@ func TestSimpleGatherWithTimestamp(t *testing.T) {
"source": hostname(), "source": hostname(),
} }
acc1.AssertContainsTaggedFields(t, measurement, fields1, tags1) acc1.AssertContainsTaggedFields(t, measurement, fields1, tags1)
require.True(t, acc1.HasTimestamp(measurement, MetricTime)) require.True(t, acc1.HasTimestamp(measurement, metricTime))
err = m.cleanQueries() err = m.cleanQueries()
require.NoError(t, err) require.NoError(t, err)
} }
@ -1347,9 +1347,9 @@ func TestGatherError(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap([]string{cp1}, []float64{-2}, []uint32{PdhPlaValidationWarning}), counters: createCounterMap([]string{cp1}, []float64{-2}, []uint32{pdhPlaValidationWarning}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cp1: {cp1}, cp1: {cp1},
}, },
@ -1390,9 +1390,9 @@ func TestGatherInvalidDataIgnore(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, []float64{1.2, 1, 0}, []uint32{0, PdhInvalidData, 0}), counters: createCounterMap(cps1, []float64{1.2, 1, 0}, []uint32{0, pdhInvalidData, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
cps1[0]: {cps1[0]}, cps1[0]: {cps1[0]},
cps1[1]: {cps1[1]}, cps1[1]: {cps1[1]},
@ -1440,7 +1440,7 @@ func TestGatherRefreshingWithExpansion(t *testing.T) {
measurement := "test" measurement := "test"
perfObjects := createPerfObject("", measurement, "O", []string{"*"}, []string{"*"}, true, false, false) perfObjects := createPerfObject("", measurement, "O", []string{"*"}, []string{"*"}, true, false, false)
cps1 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I2)\\C1", "\\O(I2)\\C2"} cps1 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I2)\\C1", "\\O(I2)\\C2"}
fpm := &FakePerformanceQuery{ fpm := &fakePerformanceQuery{
counters: createCounterMap(append(cps1, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 0}, []uint32{0, 0, 0, 0, 0}), counters: createCounterMap(append(cps1, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 0}, []uint32{0, 0, 0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
"\\O(*)\\*": cps1, "\\O(*)\\*": cps1,
@ -1452,8 +1452,8 @@ func TestGatherRefreshingWithExpansion(t *testing.T) {
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
UseWildcardsExpansion: true, UseWildcardsExpansion: true,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": fpm}, fakeQueries: map[string]*fakePerformanceQuery{"localhost": fpm},
}, },
CountersRefreshInterval: config.Duration(time.Second * 10), CountersRefreshInterval: config.Duration(time.Second * 10),
LocalizeWildcardsExpansion: true, LocalizeWildcardsExpansion: true,
@ -1491,7 +1491,7 @@ func TestGatherRefreshingWithExpansion(t *testing.T) {
acc1.AssertContainsTaggedFields(t, measurement, fields2, tags2) acc1.AssertContainsTaggedFields(t, measurement, fields2, tags2)
cps2 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I2)\\C1", "\\O(I2)\\C2", "\\O(I3)\\C1", "\\O(I3)\\C2"} cps2 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I2)\\C1", "\\O(I2)\\C2", "\\O(I3)\\C1", "\\O(I3)\\C2"}
fpm = &FakePerformanceQuery{ fpm = &fakePerformanceQuery{
counters: createCounterMap(append(cps2, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 0}, []uint32{0, 0, 0, 0, 0, 0, 0}), counters: createCounterMap(append(cps2, "\\O(*)\\*"), []float64{1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 0}, []uint32{0, 0, 0, 0, 0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
"\\O(*)\\*": cps2, "\\O(*)\\*": cps2,
@ -1499,8 +1499,8 @@ func TestGatherRefreshingWithExpansion(t *testing.T) {
vistaAndNewer: true, vistaAndNewer: true,
} }
m.queryCreator = &FakePerformanceQueryCreator{ m.queryCreator = &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": fpm}, fakeQueries: map[string]*fakePerformanceQuery{"localhost": fpm},
} }
var acc2 testutil.Accumulator var acc2 testutil.Accumulator
@ -1548,7 +1548,7 @@ func TestGatherRefreshingWithoutExpansion(t *testing.T) {
measurement := "test" measurement := "test"
perfObjects := createPerfObject("", measurement, "O", []string{"*"}, []string{"C1", "C2"}, true, false, false) perfObjects := createPerfObject("", measurement, "O", []string{"*"}, []string{"C1", "C2"}, true, false, false)
cps1 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I2)\\C1", "\\O(I2)\\C2"} cps1 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I2)\\C1", "\\O(I2)\\C2"}
fpm := &FakePerformanceQuery{ fpm := &fakePerformanceQuery{
counters: createCounterMap(append([]string{"\\O(*)\\C1", "\\O(*)\\C2"}, cps1...), []float64{0, 0, 1.1, 1.2, 1.3, 1.4}, []uint32{0, 0, 0, 0, 0, 0}), counters: createCounterMap(append([]string{"\\O(*)\\C1", "\\O(*)\\C2"}, cps1...), []float64{0, 0, 1.1, 1.2, 1.3, 1.4}, []uint32{0, 0, 0, 0, 0, 0}),
expandPaths: map[string][]string{ expandPaths: map[string][]string{
"\\O(*)\\C1": {cps1[0], cps1[2]}, "\\O(*)\\C1": {cps1[0], cps1[2]},
@ -1561,8 +1561,8 @@ func TestGatherRefreshingWithoutExpansion(t *testing.T) {
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
UseWildcardsExpansion: false, UseWildcardsExpansion: false,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": fpm}, fakeQueries: map[string]*fakePerformanceQuery{"localhost": fpm},
}, },
CountersRefreshInterval: config.Duration(time.Second * 10)} CountersRefreshInterval: config.Duration(time.Second * 10)}
var acc1 testutil.Accumulator var acc1 testutil.Accumulator
@ -1596,7 +1596,7 @@ func TestGatherRefreshingWithoutExpansion(t *testing.T) {
acc1.AssertContainsTaggedFields(t, measurement, fields2, tags2) acc1.AssertContainsTaggedFields(t, measurement, fields2, tags2)
// test finding new instance // test finding new instance
cps2 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I2)\\C1", "\\O(I2)\\C2", "\\O(I3)\\C1", "\\O(I3)\\C2"} cps2 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I2)\\C1", "\\O(I2)\\C2", "\\O(I3)\\C1", "\\O(I3)\\C2"}
fpm = &FakePerformanceQuery{ fpm = &fakePerformanceQuery{
counters: createCounterMap( counters: createCounterMap(
append([]string{"\\O(*)\\C1", "\\O(*)\\C2"}, cps2...), append([]string{"\\O(*)\\C1", "\\O(*)\\C2"}, cps2...),
[]float64{0, 0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6}, []float64{0, 0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6},
@ -1612,8 +1612,8 @@ func TestGatherRefreshingWithoutExpansion(t *testing.T) {
err = m.cleanQueries() err = m.cleanQueries()
require.NoError(t, err) require.NoError(t, err)
m.lastRefreshed = time.Time{} m.lastRefreshed = time.Time{}
m.queryCreator = &FakePerformanceQueryCreator{ m.queryCreator = &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": fpm}, fakeQueries: map[string]*fakePerformanceQuery{"localhost": fpm},
} }
var acc2 testutil.Accumulator var acc2 testutil.Accumulator
@ -1643,7 +1643,7 @@ func TestGatherRefreshingWithoutExpansion(t *testing.T) {
// test changed configuration // test changed configuration
perfObjects = createPerfObject("", measurement, "O", []string{"*"}, []string{"C1", "C2", "C3"}, true, false, false) perfObjects = createPerfObject("", measurement, "O", []string{"*"}, []string{"C1", "C2", "C3"}, true, false, false)
cps3 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I1)\\C3", "\\O(I2)\\C1", "\\O(I2)\\C2", "\\O(I2)\\C3"} cps3 := []string{"\\O(I1)\\C1", "\\O(I1)\\C2", "\\O(I1)\\C3", "\\O(I2)\\C1", "\\O(I2)\\C2", "\\O(I2)\\C3"}
fpm = &FakePerformanceQuery{ fpm = &fakePerformanceQuery{
counters: createCounterMap( counters: createCounterMap(
append([]string{"\\O(*)\\C1", "\\O(*)\\C2", "\\O(*)\\C3"}, cps3...), append([]string{"\\O(*)\\C1", "\\O(*)\\C2", "\\O(*)\\C3"}, cps3...),
[]float64{0, 0, 0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6}, []float64{0, 0, 0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6},
@ -1659,8 +1659,8 @@ func TestGatherRefreshingWithoutExpansion(t *testing.T) {
err = m.cleanQueries() err = m.cleanQueries()
m.lastRefreshed = time.Time{} m.lastRefreshed = time.Time{}
require.NoError(t, err) require.NoError(t, err)
m.queryCreator = &FakePerformanceQueryCreator{ m.queryCreator = &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": fpm}, fakeQueries: map[string]*fakePerformanceQuery{"localhost": fpm},
} }
m.Object = perfObjects m.Object = perfObjects
@ -1710,8 +1710,8 @@ func TestGatherTotalNoExpansion(t *testing.T) {
PrintValid: false, PrintValid: false,
UseWildcardsExpansion: false, UseWildcardsExpansion: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap( counters: createCounterMap(
append([]string{"\\O(*)\\C1", "\\O(*)\\C2"}, cps1...), append([]string{"\\O(*)\\C1", "\\O(*)\\C2"}, cps1...),
[]float64{0, 0, 1.1, 1.2, 1.3, 1.4}, []float64{0, 0, 1.1, 1.2, 1.3, 1.4},
@ -1789,8 +1789,8 @@ func TestGatherMultiComps(t *testing.T) {
Log: testutil.Logger{}, Log: testutil.Logger{},
PrintValid: false, PrintValid: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap(cps1, counters: createCounterMap(cps1,
[]float64{1.1, 1.2, 1.3, 1.4}, []float64{1.1, 1.2, 1.3, 1.4},
[]uint32{0, 0, 0, 0}), []uint32{0, 0, 0, 0}),
@ -1908,8 +1908,8 @@ func TestGatherRaw(t *testing.T) {
PrintValid: false, PrintValid: false,
UseWildcardsExpansion: false, UseWildcardsExpansion: false,
Object: perfObjects, Object: perfObjects,
queryCreator: &FakePerformanceQueryCreator{ queryCreator: &fakePerformanceQueryCreator{
fakeQueries: map[string]*FakePerformanceQuery{"localhost": { fakeQueries: map[string]*fakePerformanceQuery{"localhost": {
counters: createCounterMap( counters: createCounterMap(
append([]string{"\\O(*)\\C1", "\\O(*)\\C2"}, cps1...), append([]string{"\\O(*)\\C1", "\\O(*)\\C2"}, cps1...),
[]float64{0, 0, 1.1, 2.2, 3.3, 4.4}, []float64{0, 0, 1.1, 2.2, 3.3, 4.4},
@ -2000,16 +2000,16 @@ var stringArraySingleItem = []string{
} }
func TestUTF16ToStringArray(t *testing.T) { func TestUTF16ToStringArray(t *testing.T) {
singleItem := UTF16ToStringArray(unicodeStringListSingleItem) singleItem := utf16ToStringArray(unicodeStringListSingleItem)
require.Equal(t, singleItem, stringArraySingleItem, "Not equal single arrays") require.Equal(t, singleItem, stringArraySingleItem, "Not equal single arrays")
noItem := UTF16ToStringArray(unicodeStringListNoItem) noItem := utf16ToStringArray(unicodeStringListNoItem)
require.Nil(t, noItem) require.Nil(t, noItem)
engStrings := UTF16ToStringArray(unicodeStringListWithEnglishChars) engStrings := utf16ToStringArray(unicodeStringListWithEnglishChars)
require.Equal(t, engStrings, stringArrayWithEnglishChars, "Not equal eng arrays") require.Equal(t, engStrings, stringArrayWithEnglishChars, "Not equal eng arrays")
czechStrings := UTF16ToStringArray(unicodeStringListWithCzechChars) czechStrings := utf16ToStringArray(unicodeStringListWithCzechChars)
require.Equal(t, czechStrings, stringArrayWithCzechChars, "Not equal czech arrays") require.Equal(t, czechStrings, stringArrayWithCzechChars, "Not equal czech arrays")
} }
@ -2069,7 +2069,7 @@ func TestCheckError(t *testing.T) {
{ {
Name: "Ignore PDH_NO_DATA", Name: "Ignore PDH_NO_DATA",
Err: &pdhError{ Err: &pdhError{
ErrorCode: uint32(PdhNoData), errorCode: uint32(pdhNoData),
}, },
IgnoredErrors: []string{ IgnoredErrors: []string{
"PDH_NO_DATA", "PDH_NO_DATA",
@ -2079,10 +2079,10 @@ func TestCheckError(t *testing.T) {
{ {
Name: "Don't ignore PDH_NO_DATA", Name: "Don't ignore PDH_NO_DATA",
Err: &pdhError{ Err: &pdhError{
ErrorCode: uint32(PdhNoData), errorCode: uint32(pdhNoData),
}, },
ExpectedErr: &pdhError{ ExpectedErr: &pdhError{
ErrorCode: uint32(PdhNoData), errorCode: uint32(pdhNoData),
}, },
}, },
} }

View File

@ -23,90 +23,33 @@ import (
//go:embed sample.conf //go:embed sample.conf
var sampleConfig string var sampleConfig string
type serviceError struct { type WinServices struct {
Message string ServiceNames []string `toml:"service_names"`
Service string ServiceNamesExcluded []string `toml:"excluded_service_names"`
Err error
Log telegraf.Logger `toml:"-"`
mgrProvider managerProvider
servicesFilter filter.Filter
} }
func (e *serviceError) Error() string { // winService provides interface for svc.Service
return fmt.Sprintf("%s: %q: %v", e.Message, e.Service, e.Err) type winService interface {
}
func IsPermission(err error) bool {
var serviceErr *serviceError
if errors.As(err, &serviceErr) {
return errors.Is(serviceErr, fs.ErrPermission)
}
return false
}
// WinService provides interface for svc.Service
type WinService interface {
Close() error Close() error
Config() (mgr.Config, error) Config() (mgr.Config, error)
Query() (svc.Status, error) Query() (svc.Status, error)
} }
// ManagerProvider sets interface for acquiring manager instance, like mgr.Mgr // managerProvider sets interface for acquiring manager instance, like mgr.Mgr
type ManagerProvider interface { type managerProvider interface {
Connect() (WinServiceManager, error) connect() (winServiceManager, error)
} }
// WinServiceManager provides interface for mgr.Mgr // winServiceManager provides interface for mgr.Mgr
type WinServiceManager interface { type winServiceManager interface {
Disconnect() error disconnect() error
OpenService(name string) (WinService, error) openService(name string) (winService, error)
ListServices() ([]string, error) listServices() ([]string, error)
}
// winSvcMgr is wrapper for mgr.Mgr implementing WinServiceManager interface
type winSvcMgr struct {
realMgr *mgr.Mgr
}
func (m *winSvcMgr) Disconnect() error {
return m.realMgr.Disconnect()
}
func (m *winSvcMgr) OpenService(name string) (WinService, error) {
serviceName, err := syscall.UTF16PtrFromString(name)
if err != nil {
return nil, fmt.Errorf("cannot convert service name %q: %w", name, err)
}
h, err := windows.OpenService(m.realMgr.Handle, serviceName, windows.GENERIC_READ)
if err != nil {
return nil, err
}
return &mgr.Service{Name: name, Handle: h}, nil
}
func (m *winSvcMgr) ListServices() ([]string, error) {
return m.realMgr.ListServices()
}
// mgProvider is an implementation of WinServiceManagerProvider interface returning winSvcMgr
type mgProvider struct {
}
func (*mgProvider) Connect() (WinServiceManager, error) {
h, err := windows.OpenSCManager(nil, nil, windows.GENERIC_READ)
if err != nil {
return nil, err
}
scmgr := &mgr.Mgr{Handle: h}
return &winSvcMgr{scmgr}, nil
}
// WinServices is an implementation if telegraf.Input interface, providing info about Windows Services
type WinServices struct {
Log telegraf.Logger
ServiceNames []string `toml:"service_names"`
ServiceNamesExcluded []string `toml:"excluded_service_names"`
mgrProvider ManagerProvider
servicesFilter filter.Filter
} }
type serviceInfo struct { type serviceInfo struct {
@ -142,11 +85,11 @@ func (m *WinServices) Init() error {
} }
func (m *WinServices) Gather(acc telegraf.Accumulator) error { func (m *WinServices) Gather(acc telegraf.Accumulator) error {
scmgr, err := m.mgrProvider.Connect() scmgr, err := m.mgrProvider.connect()
if err != nil { if err != nil {
return fmt.Errorf("could not open service manager: %w", err) return fmt.Errorf("could not open service manager: %w", err)
} }
defer scmgr.Disconnect() defer scmgr.disconnect()
serviceNames, err := m.listServices(scmgr) serviceNames, err := m.listServices(scmgr)
if err != nil { if err != nil {
@ -156,7 +99,7 @@ func (m *WinServices) Gather(acc telegraf.Accumulator) error {
for _, srvName := range serviceNames { for _, srvName := range serviceNames {
service, err := collectServiceInfo(scmgr, srvName) service, err := collectServiceInfo(scmgr, srvName)
if err != nil { if err != nil {
if IsPermission(err) { if isPermission(err) {
m.Log.Debug(err.Error()) m.Log.Debug(err.Error())
} else { } else {
m.Log.Error(err.Error()) m.Log.Error(err.Error())
@ -183,8 +126,8 @@ func (m *WinServices) Gather(acc telegraf.Accumulator) error {
} }
// listServices returns a list of services to gather. // listServices returns a list of services to gather.
func (m *WinServices) listServices(scmgr WinServiceManager) ([]string, error) { func (m *WinServices) listServices(scmgr winServiceManager) ([]string, error) {
names, err := scmgr.ListServices() names, err := scmgr.listServices()
if err != nil { if err != nil {
return nil, fmt.Errorf("could not list services: %w", err) return nil, fmt.Errorf("could not list services: %w", err)
} }
@ -201,14 +144,22 @@ func (m *WinServices) listServices(scmgr WinServiceManager) ([]string, error) {
return services, nil return services, nil
} }
func isPermission(err error) bool {
var serviceErr *serviceError
if errors.As(err, &serviceErr) {
return errors.Is(serviceErr, fs.ErrPermission)
}
return false
}
// collectServiceInfo gathers info about a service. // collectServiceInfo gathers info about a service.
func collectServiceInfo(scmgr WinServiceManager, serviceName string) (*serviceInfo, error) { func collectServiceInfo(scmgr winServiceManager, serviceName string) (*serviceInfo, error) {
srv, err := scmgr.OpenService(serviceName) srv, err := scmgr.openService(serviceName)
if err != nil { if err != nil {
return nil, &serviceError{ return nil, &serviceError{
Message: "could not open service", message: "could not open service",
Service: serviceName, service: serviceName,
Err: err, err: err,
} }
} }
defer srv.Close() defer srv.Close()
@ -216,18 +167,18 @@ func collectServiceInfo(scmgr WinServiceManager, serviceName string) (*serviceIn
srvStatus, err := srv.Query() srvStatus, err := srv.Query()
if err != nil { if err != nil {
return nil, &serviceError{ return nil, &serviceError{
Message: "could not query service", message: "could not query service",
Service: serviceName, service: serviceName,
Err: err, err: err,
} }
} }
srvCfg, err := srv.Config() srvCfg, err := srv.Config()
if err != nil { if err != nil {
return nil, &serviceError{ return nil, &serviceError{
Message: "could not get config of service", message: "could not get config of service",
Service: serviceName, service: serviceName,
Err: err, err: err,
} }
} }
@ -240,6 +191,54 @@ func collectServiceInfo(scmgr WinServiceManager, serviceName string) (*serviceIn
return serviceInfo, nil return serviceInfo, nil
} }
type serviceError struct {
message string
service string
err error
}
func (e *serviceError) Error() string {
return fmt.Sprintf("%s: %q: %v", e.message, e.service, e.err)
}
// winSvcMgr is wrapper for mgr.Mgr implementing winServiceManager interface
type winSvcMgr struct {
realMgr *mgr.Mgr
}
func (m *winSvcMgr) disconnect() error {
return m.realMgr.Disconnect()
}
func (m *winSvcMgr) openService(name string) (winService, error) {
serviceName, err := syscall.UTF16PtrFromString(name)
if err != nil {
return nil, fmt.Errorf("cannot convert service name %q: %w", name, err)
}
h, err := windows.OpenService(m.realMgr.Handle, serviceName, windows.GENERIC_READ)
if err != nil {
return nil, err
}
return &mgr.Service{Name: name, Handle: h}, nil
}
func (m *winSvcMgr) listServices() ([]string, error) {
return m.realMgr.ListServices()
}
// mgProvider is an implementation of WinServiceManagerProvider interface returning winSvcMgr
type mgProvider struct {
}
func (*mgProvider) connect() (winServiceManager, error) {
h, err := windows.OpenSCManager(nil, nil, windows.GENERIC_READ)
if err != nil {
return nil, err
}
scmgr := &mgr.Mgr{Handle: h}
return &winSvcMgr{scmgr}, nil
}
func init() { func init() {
inputs.Add("win_services", func() telegraf.Input { inputs.Add("win_services", func() telegraf.Input {
return &WinServices{ return &WinServices{

View File

@ -10,31 +10,31 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var InvalidServices = []string{"XYZ1@", "ZYZ@", "SDF_@#"} var invalidServices = []string{"XYZ1@", "ZYZ@", "SDF_@#"}
var KnownServices = []string{"LanmanServer", "TermService"} var knownServices = []string{"LanmanServer", "TermService"}
func TestListIntegration(t *testing.T) { func TestListIntegration(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("Skipping integration test in short mode") t.Skip("Skipping integration test in short mode")
} }
provider := &mgProvider{} provider := &mgProvider{}
scmgr, err := provider.Connect() scmgr, err := provider.connect()
require.NoError(t, err) require.NoError(t, err)
defer func() { defer func() {
err := scmgr.Disconnect() err := scmgr.disconnect()
require.NoError(t, err) require.NoError(t, err)
}() }()
winServices := &WinServices{ winServices := &WinServices{
ServiceNames: KnownServices, ServiceNames: knownServices,
} }
require.NoError(t, winServices.Init()) require.NoError(t, winServices.Init())
services, err := winServices.listServices(scmgr) services, err := winServices.listServices(scmgr)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, services, 2, "Different number of services") require.Len(t, services, 2, "Different number of services")
require.Equal(t, services[0], KnownServices[0]) require.Equal(t, services[0], knownServices[0])
require.Equal(t, services[1], KnownServices[1]) require.Equal(t, services[1], knownServices[1])
} }
func TestEmptyListIntegration(t *testing.T) { func TestEmptyListIntegration(t *testing.T) {
@ -42,10 +42,10 @@ func TestEmptyListIntegration(t *testing.T) {
t.Skip("Skipping integration test in short mode") t.Skip("Skipping integration test in short mode")
} }
provider := &mgProvider{} provider := &mgProvider{}
scmgr, err := provider.Connect() scmgr, err := provider.connect()
require.NoError(t, err) require.NoError(t, err)
defer func() { defer func() {
err := scmgr.Disconnect() err := scmgr.disconnect()
require.NoError(t, err) require.NoError(t, err)
}() }()
@ -65,7 +65,7 @@ func TestGatherErrorsIntegration(t *testing.T) {
} }
ws := &WinServices{ ws := &WinServices{
Log: testutil.Logger{}, Log: testutil.Logger{},
ServiceNames: InvalidServices, ServiceNames: invalidServices,
mgrProvider: &mgProvider{}, mgrProvider: &mgProvider{},
} }

View File

@ -23,6 +23,7 @@ func (w *WinServices) Init() error {
w.Log.Warn("Current platform is not supported") w.Log.Warn("Current platform is not supported")
return nil return nil
} }
func (*WinServices) Gather(telegraf.Accumulator) error { return nil } func (*WinServices) Gather(telegraf.Accumulator) error { return nil }
func init() { func init() {

View File

@ -18,7 +18,7 @@ import (
// testData is DD wrapper for unit testing of WinServices // testData is DD wrapper for unit testing of WinServices
type testData struct { type testData struct {
// collection that will be returned in ListServices if service array passed into WinServices constructor is empty // collection that will be returned in listServices if service array passed into WinServices constructor is empty
queryServiceList []string queryServiceList []string
mgrConnectError error mgrConnectError error
mgrListServicesError error mgrListServicesError error
@ -39,23 +39,23 @@ type FakeSvcMgr struct {
testData testData testData testData
} }
func (*FakeSvcMgr) Disconnect() error { func (*FakeSvcMgr) disconnect() error {
return nil return nil
} }
func (m *FakeSvcMgr) OpenService(name string) (WinService, error) { func (m *FakeSvcMgr) openService(name string) (winService, error) {
for _, s := range m.testData.services { for _, s := range m.testData.services {
if s.serviceName == name { if s.serviceName == name {
if s.serviceOpenError != nil { if s.serviceOpenError != nil {
return nil, s.serviceOpenError return nil, s.serviceOpenError
} }
return &FakeWinSvc{s}, nil return &fakeWinSvc{s}, nil
} }
} }
return nil, fmt.Errorf("cannot find service %q", name) return nil, fmt.Errorf("cannot find service %q", name)
} }
func (m *FakeSvcMgr) ListServices() ([]string, error) { func (m *FakeSvcMgr) listServices() ([]string, error) {
if m.testData.mgrListServicesError != nil { if m.testData.mgrListServicesError != nil {
return nil, m.testData.mgrListServicesError return nil, m.testData.mgrListServicesError
} }
@ -66,21 +66,22 @@ type FakeMgProvider struct {
testData testData testData testData
} }
func (m *FakeMgProvider) Connect() (WinServiceManager, error) { func (m *FakeMgProvider) connect() (winServiceManager, error) {
if m.testData.mgrConnectError != nil { if m.testData.mgrConnectError != nil {
return nil, m.testData.mgrConnectError return nil, m.testData.mgrConnectError
} }
return &FakeSvcMgr{m.testData}, nil return &FakeSvcMgr{m.testData}, nil
} }
type FakeWinSvc struct { type fakeWinSvc struct {
testData serviceTestInfo testData serviceTestInfo
} }
func (*FakeWinSvc) Close() error { func (*fakeWinSvc) Close() error {
return nil return nil
} }
func (m *FakeWinSvc) Config() (mgr.Config, error) {
func (m *fakeWinSvc) Config() (mgr.Config, error) {
if m.testData.serviceConfigError != nil { if m.testData.serviceConfigError != nil {
return mgr.Config{}, m.testData.serviceConfigError return mgr.Config{}, m.testData.serviceConfigError
} }
@ -98,7 +99,8 @@ func (m *FakeWinSvc) Config() (mgr.Config, error) {
Description: "", Description: "",
}, nil }, nil
} }
func (m *FakeWinSvc) Query() (svc.Status, error) {
func (m *fakeWinSvc) Query() (svc.Status, error) {
if m.testData.serviceQueryError != nil { if m.testData.serviceQueryError != nil {
return svc.Status{}, m.testData.serviceQueryError return svc.Status{}, m.testData.serviceQueryError
} }

View File

@ -15,8 +15,7 @@ import (
"github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/internal"
) )
// Method struct type method struct {
type Method struct {
Namespace string `toml:"namespace"` Namespace string `toml:"namespace"`
ClassName string `toml:"class_name"` ClassName string `toml:"class_name"`
Method string `toml:"method"` Method string `toml:"method"`
@ -30,7 +29,7 @@ type Method struct {
tagFilter filter.Filter tagFilter filter.Filter
} }
func (m *Method) prepare(host string, username, password config.Secret) error { func (m *method) prepare(host string, username, password config.Secret) error {
// Compile the filter // Compile the filter
f, err := filter.Compile(m.TagPropertiesInclude) f, err := filter.Compile(m.TagPropertiesInclude)
if err != nil { if err != nil {
@ -66,7 +65,7 @@ func (m *Method) prepare(host string, username, password config.Secret) error {
return nil return nil
} }
func (m *Method) execute(acc telegraf.Accumulator) error { func (m *method) execute(acc telegraf.Accumulator) error {
// The only way to run WMI queries in parallel while being thread-safe is to // The only way to run WMI queries in parallel while being thread-safe is to
// ensure the CoInitialize[Ex]() call is bound to its current OS thread. // ensure the CoInitialize[Ex]() call is bound to its current OS thread.
// Otherwise, attempting to initialize and run parallel queries across // Otherwise, attempting to initialize and run parallel queries across

View File

@ -17,8 +17,7 @@ import (
"github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/internal"
) )
// Query struct type query struct {
type Query struct {
Namespace string `toml:"namespace"` Namespace string `toml:"namespace"`
ClassName string `toml:"class_name"` ClassName string `toml:"class_name"`
Properties []string `toml:"properties"` Properties []string `toml:"properties"`
@ -31,7 +30,7 @@ type Query struct {
tagFilter filter.Filter tagFilter filter.Filter
} }
func (q *Query) prepare(host string, username, password config.Secret) error { func (q *query) prepare(host string, username, password config.Secret) error {
// Compile the filter // Compile the filter
f, err := filter.Compile(q.TagPropertiesInclude) f, err := filter.Compile(q.TagPropertiesInclude)
if err != nil { if err != nil {
@ -74,7 +73,7 @@ func (q *Query) prepare(host string, username, password config.Secret) error {
return nil return nil
} }
func (q *Query) execute(acc telegraf.Accumulator) error { func (q *query) execute(acc telegraf.Accumulator) error {
// The only way to run WMI queries in parallel while being thread-safe is to // The only way to run WMI queries in parallel while being thread-safe is to
// ensure the CoInitialize[Ex]() call is bound to its current OS thread. // ensure the CoInitialize[Ex]() call is bound to its current OS thread.
// Otherwise, attempting to initialize and run parallel queries across // Otherwise, attempting to initialize and run parallel queries across
@ -143,7 +142,7 @@ func (q *Query) execute(acc telegraf.Accumulator) error {
return nil return nil
} }
func (q *Query) extractProperties(acc telegraf.Accumulator, itemRaw *ole.VARIANT) error { func (q *query) extractProperties(acc telegraf.Accumulator, itemRaw *ole.VARIANT) error {
tags, fields := make(map[string]string), make(map[string]interface{}) tags, fields := make(map[string]string), make(map[string]interface{})
if q.host != "" { if q.host != "" {

View File

@ -16,20 +16,22 @@ import (
//go:embed sample.conf //go:embed sample.conf
var sampleConfig string var sampleConfig string
// Wmi struct // S_FALSE is returned by CoInitializeEx if it was already called on this thread.
const sFalse = 0x00000001
type Wmi struct { type Wmi struct {
Host string `toml:"host"` Host string `toml:"host"`
Username config.Secret `toml:"username"` Username config.Secret `toml:"username"`
Password config.Secret `toml:"password"` Password config.Secret `toml:"password"`
Queries []Query `toml:"query"` Queries []query `toml:"query"`
Methods []Method `toml:"method"` Methods []method `toml:"method"`
Log telegraf.Logger `toml:"-"` Log telegraf.Logger `toml:"-"`
} }
// S_FALSE is returned by CoInitializeEx if it was already called on this thread. func (*Wmi) SampleConfig() string {
const sFalse = 0x00000001 return sampleConfig
}
// Init function
func (w *Wmi) Init() error { func (w *Wmi) Init() error {
for i := range w.Queries { for i := range w.Queries {
q := &w.Queries[i] q := &w.Queries[i]
@ -48,28 +50,22 @@ func (w *Wmi) Init() error {
return nil return nil
} }
// SampleConfig function
func (*Wmi) SampleConfig() string {
return sampleConfig
}
// Gather function
func (w *Wmi) Gather(acc telegraf.Accumulator) error { func (w *Wmi) Gather(acc telegraf.Accumulator) error {
var wg sync.WaitGroup var wg sync.WaitGroup
for _, query := range w.Queries { for _, q := range w.Queries {
wg.Add(1) wg.Add(1)
go func(q Query) { go func(q query) {
defer wg.Done() defer wg.Done()
acc.AddError(q.execute(acc)) acc.AddError(q.execute(acc))
}(query) }(q)
} }
for _, method := range w.Methods { for _, m := range w.Methods {
wg.Add(1) wg.Add(1)
go func(m Method) { go func(m method) {
defer wg.Done() defer wg.Done()
acc.AddError(m.execute(acc)) acc.AddError(m.execute(acc))
}(method) }(m)
} }
wg.Wait() wg.Wait()

View File

@ -17,12 +17,14 @@ type Wmi struct {
Log telegraf.Logger `toml:"-"` Log telegraf.Logger `toml:"-"`
} }
func (*Wmi) SampleConfig() string { return sampleConfig }
func (w *Wmi) Init() error { func (w *Wmi) Init() error {
w.Log.Warn("current platform is not supported") w.Log.Warn("Current platform is not supported")
return nil return nil
} }
func (*Wmi) SampleConfig() string { return sampleConfig }
func (*Wmi) Gather(_ telegraf.Accumulator) error { return nil } func (*Wmi) Gather(telegraf.Accumulator) error { return nil }
func init() { func init() {
inputs.Add("win_wmi", func() telegraf.Input { return &Wmi{} }) inputs.Add("win_wmi", func() telegraf.Input { return &Wmi{} })

View File

@ -19,7 +19,7 @@ var sysDrive = os.Getenv("SystemDrive") + `\` // C:\
func TestBuildWqlStatements(t *testing.T) { func TestBuildWqlStatements(t *testing.T) {
plugin := &Wmi{ plugin := &Wmi{
Queries: []Query{ Queries: []query{
{ {
Namespace: "ROOT\\cimv2", Namespace: "ROOT\\cimv2",
ClassName: "Win32_Volume", ClassName: "Win32_Volume",
@ -53,7 +53,7 @@ func TestQueryIntegration(t *testing.T) {
} }
plugin := &Wmi{ plugin := &Wmi{
Queries: []Query{ Queries: []query{
{ {
Namespace: "ROOT\\cimv2", Namespace: "ROOT\\cimv2",
ClassName: "Win32_Volume", ClassName: "Win32_Volume",
@ -84,7 +84,7 @@ func TestMethodIntegration(t *testing.T) {
} }
plugin := &Wmi{ plugin := &Wmi{
Methods: []Method{ Methods: []method{
{ {
Namespace: "ROOT\\default", Namespace: "ROOT\\default",
ClassName: "StdRegProv", ClassName: "StdRegProv",