Skip to content

Commit

Permalink
process: fix fallback to V1 collector
Browse files Browse the repository at this point in the history
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
  • Loading branch information
jkroepke committed Oct 3, 2024
1 parent 79baf99 commit e575542
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 118 deletions.
35 changes: 3 additions & 32 deletions internal/collector/process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {

if utils.PDHEnabled() {
counters := []string{
processID,
percentProcessorTime,
percentPrivilegedTime,
percentUserTime,
Expand All @@ -181,7 +182,6 @@ func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
pageFileBytes,
poolNonPagedBytes,
poolPagedBytes,
processID,
priorityBase,
privateBytes,
threadCount,
Expand All @@ -195,37 +195,8 @@ func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
var err error

c.perfDataCollector, err = perfdata.NewCollector("Process V2", c.config.PerfCounterInstances, counters)
if errors.Is(err, perfdata.NewPdhError(perfdata.PdhNoData)) {
counters = []string{
percentProcessorTime,
percentPrivilegedTime,
percentUserTime,
creatingProcessID,
elapsedTime,
handleCount,
idProcess,
ioDataBytesPerSec,
ioDataOperationsPerSec,
ioOtherBytesPerSec,
ioOtherOperationsPerSec,
ioReadBytesPerSec,
ioReadOperationsPerSec,
ioWriteBytesPerSec,
ioWriteOperationsPerSec,
pageFaultsPerSec,
pageFileBytesPeak,
pageFileBytes,
poolNonPagedBytes,
poolPagedBytes,
priorityBase,
privateBytes,
threadCount,
virtualBytesPeak,
virtualBytes,
workingSetPrivate,
workingSetPeak,
workingSet,
}
if errors.Is(err, perfdata.NewPdhError(perfdata.PdhCstatusNoObject)) {
counters[0] = idProcess

c.perfDataCollector, err = perfdata.NewCollector("Process", c.config.PerfCounterInstances, counters)
}
Expand Down
15 changes: 15 additions & 0 deletions internal/perfdata/error.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
package perfdata

import "errors"

// Error represents error returned from Performance Counters API.
type Error struct {
ErrorCode uint32
errorText string
}

func (m *Error) Is(err error) bool {
if err == nil {
return false
}

var e *Error
if errors.As(err, &e) {
return m.ErrorCode == e.ErrorCode
}

return false
}

func (m *Error) Error() string {
return m.errorText
}
Expand Down
172 changes: 86 additions & 86 deletions internal/perfdata/pdh.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,92 +57,92 @@ type (
// PDH error codes, which can be returned by all Pdh* functions. Taken from mingw-w64 pdhmsg.h

const (
PdhCstatusValidData = 0x00000000 // The returned data is valid.
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.
PdhCstatusNoInstance = 0x800007D1
PdhMoreData = 0x800007D2 // The PdhGetFormattedCounterArray* function can return this if there's 'more data to be displayed'.
PdhCstatusItemNotValidated = 0x800007D3
PdhRetry = 0x800007D4
PdhNoData = 0x800007D5 // The query does not currently contain any counters (for example, limited access)
PdhCalcNegativeDenominator = 0x800007D6
PdhCalcNegativeTimebase = 0x800007D7
PdhCalcNegativeValue = 0x800007D8
PdhDialogCancelled = 0x800007D9
PdhEndOfLogFile = 0x800007DA
PdhAsyncQueryTimeout = 0x800007DB
PdhCannotSetDefaultRealtimeDatasource = 0x800007DC
PdhCstatusNoObject = 0xC0000BB8
PdhCstatusNoCounter = 0xC0000BB9 // The specified counter could not be found.
PdhCstatusInvalidData = 0xC0000BBA // The counter was successfully found, but the data returned is not valid.
PdhMemoryAllocationFailure = 0xC0000BBB
PdhInvalidHandle = 0xC0000BBC
PdhInvalidArgument = 0xC0000BBD // Required argument is missing or incorrect.
PdhFunctionNotFound = 0xC0000BBE
PdhCstatusNoCountername = 0xC0000BBF
PdhCstatusBadCountername = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path.
PdhInvalidBuffer = 0xC0000BC1
PdhInsufficientBuffer = 0xC0000BC2
PdhCannotConnectMachine = 0xC0000BC3
PdhInvalidPath = 0xC0000BC4
PdhInvalidInstance = 0xC0000BC5
PdhInvalidData = 0xC0000BC6 // specified counter does not contain valid data or a successful status code.
PdhNoDialogData = 0xC0000BC7
PdhCannotReadNameStrings = 0xC0000BC8
PdhLogFileCreateError = 0xC0000BC9
PdhLogFileOpenError = 0xC0000BCA
PdhLogTypeNotFound = 0xC0000BCB
PdhNoMoreData = 0xC0000BCC
PdhEntryNotInLogFile = 0xC0000BCD
PdhDataSourceIsLogFile = 0xC0000BCE
PdhDataSourceIsRealTime = 0xC0000BCF
PdhUnableReadLogHeader = 0xC0000BD0
PdhFileNotFound = 0xC0000BD1
PdhFileAlreadyExists = 0xC0000BD2
PdhNotImplemented = 0xC0000BD3
PdhStringNotFound = 0xC0000BD4
PdhUnableMapNameFiles = 0x80000BD5
PdhUnknownLogFormat = 0xC0000BD6
PdhUnknownLogsvcCommand = 0xC0000BD7
PdhLogsvcQueryNotFound = 0xC0000BD8
PdhLogsvcNotOpened = 0xC0000BD9
PdhWbemError = 0xC0000BDA
PdhAccessDenied = 0xC0000BDB
PdhLogFileTooSmall = 0xC0000BDC
PdhInvalidDatasource = 0xC0000BDD
PdhInvalidSqldb = 0xC0000BDE
PdhNoCounters = 0xC0000BDF
PdhSQLAllocFailed = 0xC0000BE0
PdhSQLAllocconFailed = 0xC0000BE1
PdhSQLExecDirectFailed = 0xC0000BE2
PdhSQLFetchFailed = 0xC0000BE3
PdhSQLRowcountFailed = 0xC0000BE4
PdhSQLMoreResultsFailed = 0xC0000BE5
PdhSQLConnectFailed = 0xC0000BE6
PdhSQLBindFailed = 0xC0000BE7
PdhCannotConnectWmiServer = 0xC0000BE8
PdhPlaCollectionAlreadyRunning = 0xC0000BE9
PdhPlaErrorScheduleOverlap = 0xC0000BEA
PdhPlaCollectionNotFound = 0xC0000BEB
PdhPlaErrorScheduleElapsed = 0xC0000BEC
PdhPlaErrorNostart = 0xC0000BED
PdhPlaErrorAlreadyExists = 0xC0000BEE
PdhPlaErrorTypeMismatch = 0xC0000BEF
PdhPlaErrorFilepath = 0xC0000BF0
PdhPlaServiceError = 0xC0000BF1
PdhPlaValidationError = 0xC0000BF2
PdhPlaValidationWarning = 0x80000BF3
PdhPlaErrorNameTooLong = 0xC0000BF4
PdhInvalidSQLLogFormat = 0xC0000BF5
PdhCounterAlreadyInQuery = 0xC0000BF6
PdhBinaryLogCorrupt = 0xC0000BF7
PdhLogSampleTooSmall = 0xC0000BF8
PdhOsLaterVersion = 0xC0000BF9
PdhOsEarlierVersion = 0xC0000BFA
PdhIncorrectAppendTime = 0xC0000BFB
PdhUnmatchedAppendCounter = 0xC0000BFC
PdhSQLAlterDetailFailed = 0xC0000BFD
PdhQueryPerfDataTimeout = 0xC0000BFE
PdhCstatusValidData uint32 = 0x00000000 // The returned data is valid.
PdhCstatusNewData uint32 = 0x00000001 // The return data value is valid and different from the last sample.
PdhCstatusNoMachine uint32 = 0x800007D0 // Unable to connect to the specified computer, or the computer is offline.
PdhCstatusNoInstance uint32 = 0x800007D1
PdhMoreData uint32 = 0x800007D2 // The PdhGetFormattedCounterArray* function can return this if there's 'more data to be displayed'.
PdhCstatusItemNotValidated uint32 = 0x800007D3
PdhRetry uint32 = 0x800007D4
PdhNoData uint32 = 0x800007D5 // The query does not currently contain any counters (for example, limited access)
PdhCalcNegativeDenominator uint32 = 0x800007D6
PdhCalcNegativeTimebase uint32 = 0x800007D7
PdhCalcNegativeValue uint32 = 0x800007D8
PdhDialogCancelled uint32 = 0x800007D9
PdhEndOfLogFile uint32 = 0x800007DA
PdhAsyncQueryTimeout uint32 = 0x800007DB
PdhCannotSetDefaultRealtimeDatasource uint32 = 0x800007DC
PdhCstatusNoObject uint32 = 0xC0000BB8
PdhCstatusNoCounter uint32 = 0xC0000BB9 // The specified counter could not be found.
PdhCstatusInvalidData uint32 = 0xC0000BBA // The counter was successfully found, but the data returned is not valid.
PdhMemoryAllocationFailure uint32 = 0xC0000BBB
PdhInvalidHandle uint32 = 0xC0000BBC
PdhInvalidArgument uint32 = 0xC0000BBD // Required argument is missing or incorrect.
PdhFunctionNotFound uint32 = 0xC0000BBE
PdhCstatusNoCountername uint32 = 0xC0000BBF
PdhCstatusBadCountername uint32 = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path.
PdhInvalidBuffer uint32 = 0xC0000BC1
PdhInsufficientBuffer uint32 = 0xC0000BC2
PdhCannotConnectMachine uint32 = 0xC0000BC3
PdhInvalidPath uint32 = 0xC0000BC4
PdhInvalidInstance uint32 = 0xC0000BC5
PdhInvalidData uint32 = 0xC0000BC6 // specified counter does not contain valid data or a successful status code.
PdhNoDialogData uint32 = 0xC0000BC7
PdhCannotReadNameStrings uint32 = 0xC0000BC8
PdhLogFileCreateError uint32 = 0xC0000BC9
PdhLogFileOpenError uint32 = 0xC0000BCA
PdhLogTypeNotFound uint32 = 0xC0000BCB
PdhNoMoreData uint32 = 0xC0000BCC
PdhEntryNotInLogFile uint32 = 0xC0000BCD
PdhDataSourceIsLogFile uint32 = 0xC0000BCE
PdhDataSourceIsRealTime uint32 = 0xC0000BCF
PdhUnableReadLogHeader uint32 = 0xC0000BD0
PdhFileNotFound uint32 = 0xC0000BD1
PdhFileAlreadyExists uint32 = 0xC0000BD2
PdhNotImplemented uint32 = 0xC0000BD3
PdhStringNotFound uint32 = 0xC0000BD4
PdhUnableMapNameFiles uint32 = 0x80000BD5
PdhUnknownLogFormat uint32 = 0xC0000BD6
PdhUnknownLogsvcCommand uint32 = 0xC0000BD7
PdhLogsvcQueryNotFound uint32 = 0xC0000BD8
PdhLogsvcNotOpened uint32 = 0xC0000BD9
PdhWbemError uint32 = 0xC0000BDA
PdhAccessDenied uint32 = 0xC0000BDB
PdhLogFileTooSmall uint32 = 0xC0000BDC
PdhInvalidDatasource uint32 = 0xC0000BDD
PdhInvalidSqldb uint32 = 0xC0000BDE
PdhNoCounters uint32 = 0xC0000BDF
PdhSQLAllocFailed uint32 = 0xC0000BE0
PdhSQLAllocconFailed uint32 = 0xC0000BE1
PdhSQLExecDirectFailed uint32 = 0xC0000BE2
PdhSQLFetchFailed uint32 = 0xC0000BE3
PdhSQLRowcountFailed uint32 = 0xC0000BE4
PdhSQLMoreResultsFailed uint32 = 0xC0000BE5
PdhSQLConnectFailed uint32 = 0xC0000BE6
PdhSQLBindFailed uint32 = 0xC0000BE7
PdhCannotConnectWmiServer uint32 = 0xC0000BE8
PdhPlaCollectionAlreadyRunning uint32 = 0xC0000BE9
PdhPlaErrorScheduleOverlap uint32 = 0xC0000BEA
PdhPlaCollectionNotFound uint32 = 0xC0000BEB
PdhPlaErrorScheduleElapsed uint32 = 0xC0000BEC
PdhPlaErrorNostart uint32 = 0xC0000BED
PdhPlaErrorAlreadyExists uint32 = 0xC0000BEE
PdhPlaErrorTypeMismatch uint32 = 0xC0000BEF
PdhPlaErrorFilepath uint32 = 0xC0000BF0
PdhPlaServiceError uint32 = 0xC0000BF1
PdhPlaValidationError uint32 = 0xC0000BF2
PdhPlaValidationWarning uint32 = 0x80000BF3
PdhPlaErrorNameTooLong uint32 = 0xC0000BF4
PdhInvalidSQLLogFormat uint32 = 0xC0000BF5
PdhCounterAlreadyInQuery uint32 = 0xC0000BF6
PdhBinaryLogCorrupt uint32 = 0xC0000BF7
PdhLogSampleTooSmall uint32 = 0xC0000BF8
PdhOsLaterVersion uint32 = 0xC0000BF9
PdhOsEarlierVersion uint32 = 0xC0000BFA
PdhIncorrectAppendTime uint32 = 0xC0000BFB
PdhUnmatchedAppendCounter uint32 = 0xC0000BFC
PdhSQLAlterDetailFailed uint32 = 0xC0000BFD
PdhQueryPerfDataTimeout uint32 = 0xC0000BFE
)

var PDHErrors = map[uint32]string{
Expand Down
40 changes: 40 additions & 0 deletions test/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"fmt"
"syscall"
"time"
"unsafe"
)

var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")

Check failure on line 11 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

use of `syscall.NewLazyDLL` forbidden because "use golang.org/x/sys/windows instead of syscall" (forbidigo)
queryPerformanceFrequencyProc = modkernel32.NewProc("QueryPerformanceFrequency")
queryPerformanceCounterProc = modkernel32.NewProc("QueryPerformanceCounter")

qpcFrequency = queryPerformanceFrequency()
qpcBase = queryPerformanceCounter()
)

func main() {
fmt.Printf("qpcFrequency: %d, qpcBase: %d", qpcFrequency, qpcBase)

Check failure on line 20 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

use of `fmt.Printf` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
fmt.Println()

Check failure on line 21 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
fmt.Println("Elapsed time: ", elapsed())

Check failure on line 22 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
}

func elapsed() time.Duration {
elapsed := queryPerformanceCounter() - qpcBase
return time.Duration(elapsed) * time.Second / (time.Duration(qpcFrequency) * time.Nanosecond)

Check failure on line 27 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

return with no blank line before (nlreturn)
}

func queryPerformanceCounter() int64 {
var count int64
syscall.SyscallN(queryPerformanceCounterProc.Addr(), uintptr(unsafe.Pointer(&count)))

Check failure on line 32 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `syscall.SyscallN` is not checked (errcheck)
return count

Check failure on line 33 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

return with no blank line before (nlreturn)
}

func queryPerformanceFrequency() int64 {
var freq int64
syscall.SyscallN(queryPerformanceFrequencyProc.Addr(), uintptr(unsafe.Pointer(&freq)))

Check failure on line 38 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `syscall.SyscallN` is not checked (errcheck)
return freq

Check failure on line 39 in test/main.go

View workflow job for this annotation

GitHub Actions / lint

return with no blank line before (nlreturn)
}

0 comments on commit e575542

Please sign in to comment.