1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,726 @@
# Windows Performance Counters Input Plugin
This document presents the input plugin to read Performance Counters on Windows
operating systems.
The configuration is parsed and then tested for validity, such as
whether the Object, Instance and Counter exist on Telegraf startup.
Counter paths are refreshed periodically, see the
[CountersRefreshInterval](#countersrefreshinterval) configuration parameter for
more info.
In case of query for all instances `["*"]`, the plugin does not return the
instance `_Total` by default. See [IncludeTotal](#includetotal) for more info.
## Basics
The examples contained in this file have been found on the internet
as counters used when performance monitoring
Active Directory and IIS in particular.
There are a lot of other good objects to monitor, if you know what to look for.
This file is likely to be updated in the future with more examples for
useful configurations for separate scenarios.
For more information on concepts and terminology including object,
counter, and instance names, see the help in the Windows Performance
Monitor app.
### Schema
*Measurement name* is specified per performance object
or `win_perf_counters` by default.
*Tags:*
- source - computer name, as specified in the `Sources` parameter. Name
`localhost` is translated into the host name
- objectname - normalized name of the performance object
- instance - instance name, if performance object supports multiple instances,
otherwise omitted
*Fields* are counters of the performance object.
The field name is normalized counter name.
### Plugin wide
Plugin wide entries are underneath `[[inputs.win_perf_counters]]`.
#### PrintValid
Bool, if set to `true` will print out all matching performance objects.
Example:
`PrintValid=true`
#### UseWildcardsExpansion
If `UseWildcardsExpansion` is true, wildcards can be used in the
instance name and the counter name. Instance indexes will also be
returned in the instance name.
Partial wildcards (e.g. `chrome*`) are supported only in the instance
name on Windows Vista and newer.
If disabled, wildcards (not partial) in instance names can still be
used, but instance indexes will not be returned in the instance names.
Example:
`UseWildcardsExpansion=true`
#### LocalizeWildcardsExpansion
`LocalizeWildcardsExpansion` selects whether object and counter names
are localized when `UseWildcardsExpansion` is true and Telegraf is
running on a localized installation of Windows.
When `LocalizeWildcardsExpansion` is true, Telegraf produces metrics
with localized tags and fields even when object and counter names are
in English.
When `LocalizeWildcardsExpansion` is false, Telegraf expects object
and counter names to be in English and produces metrics with English
tags and fields.
When `LocalizeWildcardsExpansion` is false, wildcards can only be used
in instances. Object and counter names must not have wildcards.
Example:
`LocalizeWildcardsExpansion=true`
#### CountersRefreshInterval
Configured counters are matched against available counters at the interval
specified by the `CountersRefreshInterval` parameter. The default value is `1m`
(1 minute).
If wildcards are used in instance or counter names, they are expanded at this
point, if the `UseWildcardsExpansion` param is set to `true`.
Setting the `CountersRefreshInterval` too low (order of seconds) can cause
Telegraf to create a high CPU load.
Set it to `0s` to disable periodic refreshing.
Example:
`CountersRefreshInterval=1m`
#### PreVistaSupport
(Deprecated in 1.7; Necessary features on Windows Vista and newer are checked
dynamically)
Bool, if set to `true`, the plugin will use the localized PerfCounter interface
that has been present since before Vista for backwards compatibility.
It is recommended NOT to use this on OSes starting with Vista and newer because
it requires more configuration to use this than the newer interface present
since Vista.
Example for Windows Server 2003, this would be set to true:
`PreVistaSupport=true`
#### UsePerfCounterTime
Bool, if set to `true` will request a timestamp along with the PerfCounter data.
If se to `false`, current time will be used.
Supported on Windows Vista/Windows Server 2008 and newer
Example:
`UsePerfCounterTime=true`
#### IgnoredErrors
IgnoredErrors accepts a list of PDH error codes which are defined in pdh.go, if
this error is encountered it will be ignored. For example, you can provide
"PDH_NO_DATA" to ignore performance counters with no instances, but by default
no errors are ignored. You can find the list of possible errors here: [PDH
errors](pdh.go).
Example:
`IgnoredErrors=["PDH_NO_DATA"]`
#### Sources
(Optional)
Host names or ip addresses of computers to gather all performance counters from.
The user running Telegraf must be authenticated to the remote computer(s).
E.g. via Windows sharing `net use \\SQL-SERVER-01`.
Use either localhost (`"localhost"`) or real local computer name to gather
counters also from localhost among other computers. Skip, if gather only from
localhost.
If a performance counter is present only on specific hosts set `Sources` param
on the specific counter level configuration to override global (plugin wide)
sources.
Example:
`Sources = ["localhost", "SQL-SERVER-01", "SQL-SERVER-02", "SQL-SERVER-03"]`
Default:
`Sources = ["localhost"]`
### Object
See Entry below.
### Entry
A new configuration entry consists of the TOML header starting with,
`[[inputs.win_perf_counters.object]]`.
This must follow before other plugin configurations,
beneath the main win_perf_counters entry, `[[inputs.win_perf_counters]]`.
Following this are 3 required key/value pairs and three optional parameters and
their usage.
#### ObjectName
(Required)
ObjectName is the Object to query for, like Processor, DirectoryServices,
LogicalDisk or similar.
Example: `ObjectName = "LogicalDisk"`
#### Instances
(Required)
The instances key (this is an array) declares the instances of a counter you
would like returned, it can be one or more values.
Example: `Instances = ["C:","D:","E:"]`
This will return only for the instances C:, D: and E: where relevant. To get all
instances of a Counter, use `["*"]` only. By default any results containing
`_Total` are stripped, unless this is specified as the wanted instance.
Alternatively see the option `IncludeTotal` below.
It is also possible to set partial wildcards, eg. `["chrome*"]`, if the
`UseWildcardsExpansion` param is set to `true`
Some Objects do not have instances to select from at all.
Here only one option is valid if you want data back,
and that is to specify `Instances = ["------"]`.
#### Counters
(Required)
The Counters key (this is an array) declares the counters of the ObjectName
you would like returned, it can also be one or more values.
Example: `Counters = ["% Idle Time", "% Disk Read Time", "% Disk Write Time"]`
This must be specified for every counter you want the results of, or use
`["*"]` for all the counters of the object, if the `UseWildcardsExpansion` param
is set to `true`.
#### Sources (Object)
(Optional)
Overrides the [Sources](#sources) global parameter for current performance
object. See [Sources](#sources) description for more details.
#### Measurement
(Optional)
This key is optional. If it is not set it will be `win_perf_counters`. In
InfluxDB this is the key underneath which the returned data is stored. So for
ordering your data in a good manner, this is a good key to set with a value when
you want your IIS and Disk results stored separately from Processor results.
Example: `Measurement = "win_disk"`
#### UseRawValues
(Optional)
This key is optional. It is a simple bool. If set to `true`, counter values
will be provided in the raw, integer, form. This is in contrast with the default
behavior, where values are returned in a formatted, displayable, form
as seen in the Windows Performance Monitor.
A field representing raw counter value has the `_Raw` suffix. Raw values should
be further used in a calculation,
e.g. `100-(non_negative_derivative("Percent_Processor_Time_Raw",1s)/100000`
Note: Time based counters (i.e. *% Processor Time*) are reported in hundredths
of nanoseconds.
This key is optional. It is a simple bool.
If set to `true`, counter values will be provided in the raw, integer, form.
This is in contrast with the default behavior, where values are returned in a
formatted, displayable, form as seen in the Windows Performance Monitor.
A field representing raw counter value has the `_Raw` suffix.
Raw values should be further used in a calculation,
e.g. `100-(non_negative_derivative("Percent_Processor_Time_Raw",1s)/100000`
Note: Time based counters (i.e. `% Processor Time`)
are reported in hundredths of nanoseconds.
Example: `UseRawValues = true`
#### IncludeTotal
(Optional)
This key is optional. It is a simple bool.
If it is not set to true or included it is treated as false.
This key only has effect if the Instances key is set to `["*"]`
and you would also like all instances containing `_Total` to be returned,
like `_Total`, `0,_Total` and so on where applicable
(Processor Information is one example).
#### WarnOnMissing
(Optional)
This key is optional. It is a simple bool.
If it is not set to true or included it is treated as false.
This only has effect on the first execution of the plugin.
It will print out any ObjectName/Instance/Counter combinations
asked for that do not match. Useful when debugging new configurations.
#### FailOnMissing
(Internal)
This key should not be used. It is for testing purposes only. It is a simple
bool. If it is not set to true or included this is treated as false. If this is
set to true, the plugin will abort and end prematurely if any of the
combinations of ObjectName/Instances/Counters are invalid.
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Input plugin to counterPath Performance Counters on Windows operating systems
# This plugin ONLY supports Windows
[[inputs.win_perf_counters]]
## By default this plugin returns basic CPU and Disk statistics. See the
## README file for more examples. Uncomment examples below or write your own
## as you see fit. If the system being polled for data does not have the
## Object at startup of the Telegraf agent, it will not be gathered.
## Print All matching performance counters
# PrintValid = false
## Whether request a timestamp along with the PerfCounter data or use current
## time
# UsePerfCounterTime = true
## If UseWildcardsExpansion params is set to true, wildcards (partial
## wildcards in instance names and wildcards in counters names) in configured
## counter paths will be expanded and in case of localized Windows, counter
## paths will be also localized. It also returns instance indexes in instance
## names. If false, wildcards (not partial) in instance names will still be
## expanded, but instance indexes will not be returned in instance names.
# UseWildcardsExpansion = false
## When running on a localized version of Windows and with
## UseWildcardsExpansion = true, Windows will localize object and counter
## names. When LocalizeWildcardsExpansion = false, use the names in
## object.Counters instead of the localized names. Only Instances can have
## wildcards in this case. ObjectName and Counters must not have wildcards
## when this setting is false.
# LocalizeWildcardsExpansion = true
## Period after which counters will be reread from configuration and
## wildcards in counter paths expanded
# CountersRefreshInterval="1m"
## Accepts a list of PDH error codes which are defined in pdh.go, if this
## error is encountered it will be ignored. For example, you can provide
## "PDH_NO_DATA" to ignore performance counters with no instances. By default
## no errors are ignored You can find the list here:
## https://github.com/influxdata/telegraf/blob/master/plugins/inputs/win_perf_counters/pdh.go
## e.g. IgnoredErrors = ["PDH_NO_DATA"]
# IgnoredErrors = []
## Maximum size of the buffer for values returned by the API
## Increase this value if you experience "buffer limit reached" errors.
# MaxBufferSize = "4MiB"
## NOTE: Due to the way TOML is parsed, tables must be at the END of the
## plugin definition, otherwise additional config options are read as part of
## the table
# [[inputs.win_perf_counters.object]]
# Measurement = ""
# ObjectName = ""
# Instances = [""]
# Counters = []
## Additional Object Settings
## * IncludeTotal: set to true to include _Total instance when querying
## for all metrics via '*'
## * WarnOnMissing: print out when the performance counter is missing
## from object, counter or instance
## * UseRawValues: gather raw values instead of formatted. Raw values are
## stored in the field name with the "_Raw" suffix, e.g.
## "Disk_Read_Bytes_sec_Raw".
# IncludeTotal = false
# WarnOnMissing = false
# UseRawValues = false
## Processor usage, alternative to native, reports on a per core.
# [[inputs.win_perf_counters.object]]
# Measurement = "win_cpu"
# ObjectName = "Processor"
# Instances = ["*"]
# UseRawValues = true
# Counters = [
# "% Idle Time",
# "% Interrupt Time",
# "% Privileged Time",
# "% User Time",
# "% Processor Time",
# "% DPC Time",
# ]
## Disk times and queues
# [[inputs.win_perf_counters.object]]
# Measurement = "win_disk"
# ObjectName = "LogicalDisk"
# Instances = ["*"]
# Counters = [
# "% Idle Time",
# "% Disk Time",
# "% Disk Read Time",
# "% Disk Write Time",
# "% User Time",
# "% Free Space",
# "Current Disk Queue Length",
# "Free Megabytes",
# ]
# [[inputs.win_perf_counters.object]]
# Measurement = "win_diskio"
# ObjectName = "PhysicalDisk"
# Instances = ["*"]
# Counters = [
# "Disk Read Bytes/sec",
# "Disk Write Bytes/sec",
# "Current Disk Queue Length",
# "Disk Reads/sec",
# "Disk Writes/sec",
# "% Disk Time",
# "% Disk Read Time",
# "% Disk Write Time",
# ]
# [[inputs.win_perf_counters.object]]
# Measurement = "win_net"
# ObjectName = "Network Interface"
# Instances = ["*"]
# Counters = [
# "Bytes Received/sec",
# "Bytes Sent/sec",
# "Packets Received/sec",
# "Packets Sent/sec",
# "Packets Received Discarded",
# "Packets Outbound Discarded",
# "Packets Received Errors",
# "Packets Outbound Errors",
# ]
# [[inputs.win_perf_counters.object]]
# Measurement = "win_system"
# ObjectName = "System"
# Instances = ["------"]
# Counters = [
# "Context Switches/sec",
# "System Calls/sec",
# "Processor Queue Length",
# "System Up Time",
# ]
## Example counterPath where the Instance portion must be removed to get
## data back, such as from the Memory object.
# [[inputs.win_perf_counters.object]]
# Measurement = "win_mem"
# ObjectName = "Memory"
## Use 6 x - to remove the Instance bit from the counterPath.
# Instances = ["------"]
# Counters = [
# "Available Bytes",
# "Cache Faults/sec",
# "Demand Zero Faults/sec",
# "Page Faults/sec",
# "Pages/sec",
# "Transition Faults/sec",
# "Pool Nonpaged Bytes",
# "Pool Paged Bytes",
# "Standby Cache Reserve Bytes",
# "Standby Cache Normal Priority Bytes",
# "Standby Cache Core Bytes",
# ]
## Example query where the Instance portion must be removed to get data back,
## such as from the Paging File object.
# [[inputs.win_perf_counters.object]]
# Measurement = "win_swap"
# ObjectName = "Paging File"
# Instances = ["_Total"]
# Counters = [
# "% Usage",
# ]
```
### Generic Queries
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
# Processor usage, alternative to native, reports on a per core.
ObjectName = "Processor"
Instances = ["*"]
Counters = ["% Idle Time", "% Interrupt Time", "% Privileged Time", "% User Time", "% Processor Time"]
Measurement = "win_cpu"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# Disk times and queues
ObjectName = "LogicalDisk"
Instances = ["*"]
Counters = ["% Idle Time", "% Disk Time","% Disk Read Time", "% Disk Write Time", "% User Time", "Current Disk Queue Length"]
Measurement = "win_disk"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
ObjectName = "System"
Counters = ["Context Switches/sec","System Calls/sec", "Processor Queue Length"]
Instances = ["------"]
Measurement = "win_system"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# Example query where the Instance portion must be removed to get data back, such as from the Memory object.
ObjectName = "Memory"
Counters = ["Available Bytes","Cache Faults/sec","Demand Zero Faults/sec","Page Faults/sec","Pages/sec","Transition Faults/sec","Pool Nonpaged Bytes","Pool Paged Bytes"]
Instances = ["------"] # Use 6 x - to remove the Instance bit from the query.
Measurement = "win_mem"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# more counters for the Network Interface Object can be found at
# https://msdn.microsoft.com/en-us/library/ms803962.aspx
ObjectName = "Network Interface"
Counters = ["Bytes Received/sec","Bytes Sent/sec","Packets Received/sec","Packets Sent/sec"]
Instances = ["*"] # Use 6 x - to remove the Instance bit from the query.
Measurement = "win_net"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
```
### Active Directory Domain Controller
```toml
[[inputs.win_perf_counters]]
[inputs.win_perf_counters.tags]
monitorgroup = "ActiveDirectory"
[[inputs.win_perf_counters.object]]
ObjectName = "DirectoryServices"
Instances = ["*"]
Counters = ["Base Searches/sec","Database adds/sec","Database deletes/sec","Database modifys/sec","Database recycles/sec","LDAP Client Sessions","LDAP Searches/sec","LDAP Writes/sec"]
Measurement = "win_ad" # Set an alternative measurement to win_perf_counters if wanted.
#Instances = [""] # Gathers all instances by default, specify to only gather these
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
ObjectName = "Security System-Wide Statistics"
Instances = ["*"]
Counters = ["NTLM Authentications","Kerberos Authentications","Digest Authentications"]
Measurement = "win_ad"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
ObjectName = "Database"
Instances = ["*"]
Counters = ["Database Cache % Hit","Database Cache Page Fault Stalls/sec","Database Cache Page Faults/sec","Database Cache Size"]
Measurement = "win_db"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
```
### DFS Namespace + Domain Controllers
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
# AD, DFS N, Useful if the server hosts a DFS Namespace or is a Domain Controller
ObjectName = "DFS Namespace Service Referrals"
Instances = ["*"]
Counters = ["Requests Processed","Requests Failed","Avg. Response Time"]
Measurement = "win_dfsn"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
#WarnOnMissing = false # Print out when the performance counter is missing, either of object, counter or instance.
```
### DFS Replication + Domain Controllers
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
# AD, DFS R, Useful if the server hosts a DFS Replication folder or is a Domain Controller
ObjectName = "DFS Replication Service Volumes"
Instances = ["*"]
Counters = ["Data Lookups","Database Commits"]
Measurement = "win_dfsr"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
#WarnOnMissing = false # Print out when the performance counter is missing, either of object, counter or instance.
```
### DNS Server + Domain Controllers
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
ObjectName = "DNS"
Counters = ["Dynamic Update Received","Dynamic Update Rejected","Recursive Queries","Recursive Queries Failure","Secure Update Failure","Secure Update Received","TCP Query Received","TCP Response Sent","UDP Query Received","UDP Response Sent","Total Query Received","Total Response Sent"]
Instances = ["------"]
Measurement = "win_dns"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
```
### IIS / ASP.NET
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
# HTTP Service request queues in the Kernel before being handed over to User Mode.
ObjectName = "HTTP Service Request Queues"
Instances = ["*"]
Counters = ["CurrentQueueSize","RejectedRequests"]
Measurement = "win_http_queues"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# IIS, ASP.NET Applications
ObjectName = "ASP.NET Applications"
Counters = ["Cache Total Entries","Cache Total Hit Ratio","Cache Total Turnover Rate","Output Cache Entries","Output Cache Hits","Output Cache Hit Ratio","Output Cache Turnover Rate","Compilations Total","Errors Total/Sec","Pipeline Instance Count","Requests Executing","Requests in Application Queue","Requests/Sec"]
Instances = ["*"]
Measurement = "win_aspnet_app"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# IIS, ASP.NET
ObjectName = "ASP.NET"
Counters = ["Application Restarts","Request Wait Time","Requests Current","Requests Queued","Requests Rejected"]
Instances = ["*"]
Measurement = "win_aspnet"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# IIS, Web Service
ObjectName = "Web Service"
Counters = ["Get Requests/sec","Post Requests/sec","Connection Attempts/sec","Current Connections","ISAPI Extension Requests/sec"]
Instances = ["*"]
Measurement = "win_websvc"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# Web Service Cache / IIS
ObjectName = "Web Service Cache"
Counters = ["URI Cache Hits %","Kernel: URI Cache Hits %","File Cache Hits %"]
Instances = ["*"]
Measurement = "win_websvc_cache"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
```
### Process
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
# Process metrics, in this case for IIS only
ObjectName = "Process"
Counters = ["% Processor Time","Handle Count","Private Bytes","Thread Count","Virtual Bytes","Working Set"]
Instances = ["w3wp"]
Measurement = "win_proc"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
```
### .NET Monitoring
```toml
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
# .NET CLR Exceptions, in this case for IIS only
ObjectName = ".NET CLR Exceptions"
Counters = ["# of Exceps Thrown / sec"]
Instances = ["w3wp"]
Measurement = "win_dotnet_exceptions"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# .NET CLR Jit, in this case for IIS only
ObjectName = ".NET CLR Jit"
Counters = ["% Time in Jit","IL Bytes Jitted / sec"]
Instances = ["w3wp"]
Measurement = "win_dotnet_jit"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# .NET CLR Loading, in this case for IIS only
ObjectName = ".NET CLR Loading"
Counters = ["% Time Loading"]
Instances = ["w3wp"]
Measurement = "win_dotnet_loading"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# .NET CLR LocksAndThreads, in this case for IIS only
ObjectName = ".NET CLR LocksAndThreads"
Counters = ["# of current logical Threads","# of current physical Threads","# of current recognized threads","# of total recognized threads","Queue Length / sec","Total # of Contentions","Current Queue Length"]
Instances = ["w3wp"]
Measurement = "win_dotnet_locks"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# .NET CLR Memory, in this case for IIS only
ObjectName = ".NET CLR Memory"
Counters = ["% Time in GC","# Bytes in all Heaps","# Gen 0 Collections","# Gen 1 Collections","# Gen 2 Collections","# Induced GC","Allocated Bytes/sec","Finalization Survivors","Gen 0 heap size","Gen 1 heap size","Gen 2 heap size","Large Object Heap size","# of Pinned Objects"]
Instances = ["w3wp"]
Measurement = "win_dotnet_mem"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
[[inputs.win_perf_counters.object]]
# .NET CLR Security, in this case for IIS only
ObjectName = ".NET CLR Security"
Counters = ["% Time in RT checks","Stack Walk Depth","Total Runtime Checks"]
Instances = ["w3wp"]
Measurement = "win_dotnet_security"
#IncludeTotal=false #Set to true to include _Total instance when querying for all (*).
```
## Troubleshooting
If you are getting an error about an invalid counter, use the `typeperf` command
to check the counter path on the command line. E.g. `typeperf
"Process(chrome*)\% Processor Time"`
If no metrics are emitted even with the default config, you may need to repair
your performance counters.
1. Launch the Command Prompt as Administrator
(right click "Runs As Administrator").
1. Drop into the C:\WINDOWS\System32 directory by typing `C:` then
`cd \Windows\System32`
1. Rebuild your counter values, which may take a few moments so please be
patient, by running:
```batchfile
lodctr /r
```
## Metrics
## Example Output

View file

@ -0,0 +1,56 @@
// Copyright (c) 2010 The win Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This is the official list of 'win' authors for copyright purposes.
//
// Alexander Neumann <an2048@googlemail.com>
// Joseph Watson <jtwatson@linux-consulting.us>
// Kevin Pors <krpors@gmail.com>
//go:build windows
package win_perf_counters
import (
"syscall"
)
type fileTime struct {
dwLowDateTime uint32
dwHighDateTime uint32
}
var (
// Library
libKernelDll *syscall.DLL
// Functions
kernelLocalFileTimeToFileTime *syscall.Proc
)
func init() {
libKernelDll = syscall.MustLoadDLL("Kernel32.dll")
kernelLocalFileTimeToFileTime = libKernelDll.MustFindProc("LocalFileTimeToFileTime")
}

View file

@ -0,0 +1,636 @@
// Copyright (c) 2010 The win Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This is the official list of 'win' authors for copyright purposes.
//
// Alexander Neumann <an2048@googlemail.com>
// Joseph Watson <jtwatson@linux-consulting.us>
// Kevin Pors <krpors@gmail.com>
//go:build windows
package win_perf_counters
import (
"fmt"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
// Error codes
const (
errorSuccess = 0
errorFailure = 1
errorInvalidFunction = 1
epochDifferenceMicros int64 = 11644473600000000
)
type (
handle uintptr
)
// 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
)
var pdhErrors = map[uint32]string{
pdhCstatusValidData: "PDH_CSTATUS_VALID_DATA",
pdhCstatusNewData: "PDH_CSTATUS_NEW_DATA",
pdhCstatusNoMachine: "PDH_CSTATUS_NO_MACHINE",
pdhCstatusNoInstance: "PDH_CSTATUS_NO_INSTANCE",
pdhMoreData: "PDH_MORE_DATA",
pdhCstatusItemNotValidated: "PDH_CSTATUS_ITEM_NOT_VALIDATED",
pdhRetry: "PDH_RETRY",
pdhNoData: "PDH_NO_DATA",
pdhCalcNegativeDenominator: "PDH_CALC_NEGATIVE_DENOMINATOR",
pdhCalcNegativeTimebase: "PDH_CALC_NEGATIVE_TIMEBASE",
pdhCalcNegativeValue: "PDH_CALC_NEGATIVE_VALUE",
pdhDialogCancelled: "PDH_DIALOG_CANCELLED",
pdhEndOfLogFile: "PDH_END_OF_LOG_FILE",
pdhAsyncQueryTimeout: "PDH_ASYNC_QUERY_TIMEOUT",
pdhCannotSetDefaultRealtimeDatasource: "PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE",
pdhCstatusNoObject: "PDH_CSTATUS_NO_OBJECT",
pdhCstatusNoCounter: "PDH_CSTATUS_NO_COUNTER",
pdhCstatusInvalidData: "PDH_CSTATUS_INVALID_DATA",
pdhMemoryAllocationFailure: "PDH_MEMORY_ALLOCATION_FAILURE",
pdhInvalidHandle: "PDH_INVALID_HANDLE",
pdhInvalidArgument: "PDH_INVALID_ARGUMENT",
pdhFunctionNotFound: "PDH_FUNCTION_NOT_FOUND",
pdhCstatusNoCountername: "PDH_CSTATUS_NO_COUNTERNAME",
pdhCstatusBadCountername: "PDH_CSTATUS_BAD_COUNTERNAME",
pdhInvalidBuffer: "PDH_INVALID_BUFFER",
pdhInsufficientBuffer: "PDH_INSUFFICIENT_BUFFER",
pdhCannotConnectMachine: "PDH_CANNOT_CONNECT_MACHINE",
pdhInvalidPath: "PDH_INVALID_PATH",
pdhInvalidInstance: "PDH_INVALID_INSTANCE",
pdhInvalidData: "PDH_INVALID_DATA",
pdhNoDialogData: "PDH_NO_DIALOG_DATA",
pdhCannotReadNameStrings: "PDH_CANNOT_READ_NAME_STRINGS",
pdhLogFileCreateError: "PDH_LOG_FILE_CREATE_ERROR",
pdhLogFileOpenError: "PDH_LOG_FILE_OPEN_ERROR",
pdhLogTypeNotFound: "PDH_LOG_TYPE_NOT_FOUND",
pdhNoMoreData: "PDH_NO_MORE_DATA",
pdhEntryNotInLogFile: "PDH_ENTRY_NOT_IN_LOG_FILE",
pdhDataSourceIsLogFile: "PDH_DATA_SOURCE_IS_LOG_FILE",
pdhDataSourceIsRealTime: "PDH_DATA_SOURCE_IS_REAL_TIME",
pdhUnableReadLogHeader: "PDH_UNABLE_READ_LOG_HEADER",
pdhFileNotFound: "PDH_FILE_NOT_FOUND",
pdhFileAlreadyExists: "PDH_FILE_ALREADY_EXISTS",
pdhNotImplemented: "PDH_NOT_IMPLEMENTED",
pdhStringNotFound: "PDH_STRING_NOT_FOUND",
pdhUnableMapNameFiles: "PDH_UNABLE_MAP_NAME_FILES",
pdhUnknownLogFormat: "PDH_UNKNOWN_LOG_FORMAT",
pdhUnknownLogsvcCommand: "PDH_UNKNOWN_LOGSVC_COMMAND",
pdhLogsvcQueryNotFound: "PDH_LOGSVC_QUERY_NOT_FOUND",
pdhLogsvcNotOpened: "PDH_LOGSVC_NOT_OPENED",
pdhWbemError: "PDH_WBEM_ERROR",
pdhAccessDenied: "PDH_ACCESS_DENIED",
pdhLogFileTooSmall: "PDH_LOG_FILE_TOO_SMALL",
pdhInvalidDatasource: "PDH_INVALID_DATASOURCE",
pdhInvalidSqldb: "PDH_INVALID_SQLDB",
pdhNoCounters: "PDH_NO_COUNTERS",
pdhSQLAllocFailed: "PDH_SQL_ALLOC_FAILED",
pdhSQLAllocconFailed: "PDH_SQL_ALLOCCON_FAILED",
pdhSQLExecDirectFailed: "PDH_SQL_EXEC_DIRECT_FAILED",
pdhSQLFetchFailed: "PDH_SQL_FETCH_FAILED",
pdhSQLRowcountFailed: "PDH_SQL_ROWCOUNT_FAILED",
pdhSQLMoreResultsFailed: "PDH_SQL_MORE_RESULTS_FAILED",
pdhSQLConnectFailed: "PDH_SQL_CONNECT_FAILED",
pdhSQLBindFailed: "PDH_SQL_BIND_FAILED",
pdhCannotConnectWmiServer: "PDH_CANNOT_CONNECT_WMI_SERVER",
pdhPlaCollectionAlreadyRunning: "PDH_PLA_COLLECTION_ALREADY_RUNNING",
pdhPlaErrorScheduleOverlap: "PDH_PLA_ERROR_SCHEDULE_OVERLAP",
pdhPlaCollectionNotFound: "PDH_PLA_COLLECTION_NOT_FOUND",
pdhPlaErrorScheduleElapsed: "PDH_PLA_ERROR_SCHEDULE_ELAPSED",
pdhPlaErrorNostart: "PDH_PLA_ERROR_NOSTART",
pdhPlaErrorAlreadyExists: "PDH_PLA_ERROR_ALREADY_EXISTS",
pdhPlaErrorTypeMismatch: "PDH_PLA_ERROR_TYPE_MISMATCH",
pdhPlaErrorFilepath: "PDH_PLA_ERROR_FILEPATH",
pdhPlaServiceError: "PDH_PLA_SERVICE_ERROR",
pdhPlaValidationError: "PDH_PLA_VALIDATION_ERROR",
pdhPlaValidationWarning: "PDH_PLA_VALIDATION_WARNING",
pdhPlaErrorNameTooLong: "PDH_PLA_ERROR_NAME_TOO_LONG",
pdhInvalidSQLLogFormat: "PDH_INVALID_SQL_LOG_FORMAT",
pdhCounterAlreadyInQuery: "PDH_COUNTER_ALREADY_IN_QUERY",
pdhBinaryLogCorrupt: "PDH_BINARY_LOG_CORRUPT",
pdhLogSampleTooSmall: "PDH_LOG_SAMPLE_TOO_SMALL",
pdhOsLaterVersion: "PDH_OS_LATER_VERSION",
pdhOsEarlierVersion: "PDH_OS_EARLIER_VERSION",
pdhIncorrectAppendTime: "PDH_INCORRECT_APPEND_TIME",
pdhUnmatchedAppendCounter: "PDH_UNMATCHED_APPEND_COUNTER",
pdhSQLAlterDetailFailed: "PDH_SQL_ALTER_DETAIL_FAILED",
pdhQueryPerfDataTimeout: "PDH_QUERY_PERF_DATA_TIMEOUT",
}
// Formatting options for GetFormattedCounterValue().
const (
pdhFmtRaw = 0x00000010
pdhFmtAnsi = 0x00000020
pdhFmtUnicode = 0x00000040
pdhFmtLong = 0x00000100 // Return data as a long int.
pdhFmtDouble = 0x00000200 // Return data as a double precision floating point real.
pdhFmtLarge = 0x00000400 // Return data as a 64 bit integer.
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.
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.
perfDetailCostly = 0x00010000
perfDetailStandard = 0x0000FFFF
)
type (
pdhQueryHandle handle // query handle
pdhCounterHandle handle // counter handle
)
var (
// Library
libPdhDll *syscall.DLL
// Functions
pdhAddCounterWProc *syscall.Proc
pdhAddEnglishCounterWProc *syscall.Proc
pdhCloseQueryProc *syscall.Proc
pdhCollectQueryDataProc *syscall.Proc
pdhCollectQueryDataWithTimeProc *syscall.Proc
pdhGetFormattedCounterValueProc *syscall.Proc
pdhGetFormattedCounterArrayWProc *syscall.Proc
pdhOpenQueryProc *syscall.Proc
pdhExpandWildCardPathWProc *syscall.Proc
pdhGetCounterInfoWProc *syscall.Proc
pdhGetRawCounterValueProc *syscall.Proc
pdhGetRawCounterArrayWProc *syscall.Proc
)
func init() {
// Library
libPdhDll = syscall.MustLoadDLL("pdh.dll")
// Functions
pdhAddCounterWProc = libPdhDll.MustFindProc("PdhAddCounterW")
pdhAddEnglishCounterWProc, _ = libPdhDll.FindProc("PdhAddEnglishCounterW") // XXX: only supported on versions > Vista.
pdhCloseQueryProc = libPdhDll.MustFindProc("PdhCloseQuery")
pdhCollectQueryDataProc = libPdhDll.MustFindProc("PdhCollectQueryData")
pdhCollectQueryDataWithTimeProc, _ = libPdhDll.FindProc("PdhCollectQueryDataWithTime")
pdhGetFormattedCounterValueProc = libPdhDll.MustFindProc("PdhGetFormattedCounterValue")
pdhGetFormattedCounterArrayWProc = libPdhDll.MustFindProc("PdhGetFormattedCounterArrayW")
pdhOpenQueryProc = libPdhDll.MustFindProc("PdhOpenQuery")
pdhExpandWildCardPathWProc = libPdhDll.MustFindProc("PdhExpandWildCardPathW")
pdhGetCounterInfoWProc = libPdhDll.MustFindProc("PdhGetCounterInfoW")
pdhGetRawCounterValueProc = libPdhDll.MustFindProc("PdhGetRawCounterValue")
pdhGetRawCounterArrayWProc = libPdhDll.MustFindProc("PdhGetRawCounterArrayW")
}
// 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.
// 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
// later, call pdhGetCounterInfo() and access dwQueryUserData of the pdhCounterInfo structure.
//
// Examples of szFullCounterPath (in an English version of Windows):
//
// \\Processor(_Total)\\% Idle Time
// \\Processor(_Total)\\% Processor Time
// \\LogicalDisk(C:)\% Free Space
//
// To view all (internationalized...) counters on a system, there are three non-programmatic ways: perfmon utility,
// the typeperf command, and the registry editor. perfmon.exe is perhaps the easiest way, because it's basically a
// full implementation of the pdh.dll API, except with a GUI and all that. The registry setting also provides an
// interface to the available counters, and can be found at the following key:
//
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage
//
// This registry key contains several values as follows:
//
// 1
// 1847
// 2
// System
// 4
// Memory
// 6
// % Processor Time
// ... many, many more
//
// Somehow, these numeric values can be used as szFullCounterPath too:
//
// \2\6 will correspond to \\System\% Processor Time
//
// The typeperf command may also be pretty easy. To find all performance counters, simply execute:
//
// typeperf -qx
func pdhAddCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 {
ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
ret, _, _ := pdhAddCounterWProc.Call(
uintptr(hQuery),
uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
dwUserData,
uintptr(unsafe.Pointer(phCounter))) //nolint:gosec // G103: Valid use of unsafe call to pass phCounter
return uint32(ret)
}
// pdhAddEnglishCounterSupported returns true if PdhAddEnglishCounterW Win API function was found in pdh.dll.
// PdhAddEnglishCounterW function is not supported on pre-Windows Vista systems
func pdhAddEnglishCounterSupported() bool {
return pdhAddEnglishCounterWProc != nil
}
// pdhAddEnglishCounter adds the specified language-neutral counter to the query. See the pdhAddCounter function. This function only exists on
// Windows versions higher than Vista.
func pdhAddEnglishCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 {
if pdhAddEnglishCounterWProc == nil {
return errorInvalidFunction
}
ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath)
ret, _, _ := pdhAddEnglishCounterWProc.Call(
uintptr(hQuery),
uintptr(unsafe.Pointer(ptxt)), //nolint:gosec // G103: Valid use of unsafe call to pass ptxt
dwUserData,
uintptr(unsafe.Pointer(phCounter))) //nolint:gosec // G103: Valid use of unsafe call to pass phCounter
return uint32(ret)
}
// pdhCloseQuery closes all counters contained in the specified query, closes all handles related to the query,
// and frees all memory associated with the query.
func pdhCloseQuery(hQuery pdhQueryHandle) uint32 {
ret, _, _ := pdhCloseQueryProc.Call(uintptr(hQuery))
return uint32(ret)
}
// 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
// of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code
// requires at least two calls:
//
// var handle win.PDH_HQUERY
// var counterHandle win.PDH_HCOUNTER
// ret := win.pdhOpenQuery(0, 0, &handle)
// ret = win.pdhAddEnglishCounter(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle)
// var derp win.PDH_FMT_COUNTERVALUE_DOUBLE
//
// ret = win.pdhCollectQueryData(handle)
// 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.pdhCollectQueryData(handle)
// fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS
// ret = win.pdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
//
// 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.
func pdhCollectQueryData(hQuery pdhQueryHandle) uint32 {
ret, _, _ := pdhCollectQueryDataProc.Call(uintptr(hQuery))
return uint32(ret)
}
// 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.
func pdhCollectQueryDataWithTime(hQuery pdhQueryHandle) (uint32, time.Time) {
var localFileTime fileTime
//nolint:gosec // G103: Valid use of unsafe call to pass localFileTime
ret, _, _ := pdhCollectQueryDataWithTimeProc.Call(uintptr(hQuery), uintptr(unsafe.Pointer(&localFileTime)))
if ret == errorSuccess {
var utcFileTime fileTime
ret, _, _ := kernelLocalFileTimeToFileTime.Call(
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
if ret == 0 {
return uint32(errorFailure), time.Now()
}
// First convert 100-ns intervals to microseconds, then adjust for the
// epoch difference
var totalMicroSeconds int64
totalMicroSeconds = ((int64(utcFileTime.dwHighDateTime) << 32) | int64(utcFileTime.dwLowDateTime)) / 10
totalMicroSeconds -= epochDifferenceMicros
retTime := time.Unix(0, totalMicroSeconds*1000)
return uint32(errorSuccess), retTime
}
return uint32(ret), time.Now()
}
// 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.
func pdhGetFormattedCounterValueDouble(hCounter pdhCounterHandle, lpdwType *uint32, pValue *pdhFmtCountervalueDouble) uint32 {
ret, _, _ := pdhGetFormattedCounterValueProc.Call(
uintptr(hCounter),
uintptr(pdhFmtDouble|pdhFmtNocap100),
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
return uint32(ret)
}
// 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.
// An example of how this function can be used:
//
// okPath := "\\Process(*)\\% Processor Time" // notice the wildcard * character
//
// // omitted all necessary stuff ...
//
// var bufSize uint32
// var bufCount uint32
// var size uint32 = uint32(unsafe.Sizeof(win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
// var emptyBuf [1]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
//
// for {
// // collect
// ret := win.pdhCollectQueryData(queryHandle)
// if ret == win.ERROR_SUCCESS {
// ret = win.pdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN.
// if ret == win.PDH_MORE_DATA {
// filledBuf := make([]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
// ret = win.pdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &filledBuf[0])
// for i := 0; i < int(bufCount); i++ {
// c := filledBuf[i]
// var s string = win.utf16PtrToString(c.SzName)
// fmt.Printf("Index %d -> %s, value %v\n", i, s, c.FmtValue.DoubleValue)
// }
//
// filledBuf = nil
// // Need to at least set bufSize to zero, because if not, the function will not
// // return PDH_MORE_DATA and will not set the bufSize.
// bufCount = 0
// bufSize = 0
// }
//
// time.Sleep(2000 * time.Millisecond)
// }
// }
func pdhGetFormattedCounterArrayDouble(hCounter pdhCounterHandle, lpdwBufferSize, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
ret, _, _ := pdhGetFormattedCounterArrayWProc.Call(
uintptr(hCounter),
uintptr(pdhFmtDouble|pdhFmtNocap100),
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(itemBuffer))) //nolint:gosec // G103: Valid use of unsafe call to pass itemBuffer
return uint32(ret)
}
// 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
// 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,
// 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_
// constant error code, or errorSuccess if the call succeeded.
func pdhOpenQuery(szDataSource, dwUserData uintptr, phQuery *pdhQueryHandle) uint32 {
ret, _, _ := pdhOpenQueryProc.Call(
szDataSource,
dwUserData,
uintptr(unsafe.Pointer(phQuery))) //nolint:gosec // G103: Valid use of unsafe call to pass phQuery
return uint32(ret)
}
// 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:
//
// \\computer\object(parent/instance#index)\counter
//
// The parent, instance, index, and counter components of the counter path may contain either a valid name or a wildcard character.
// The computer, parent, instance, and index components are not necessary for all counters.
//
// The following is a list of the possible formats:
//
// \\computer\object(parent/instance#index)\counter
// \\computer\object(parent/instance)\counter
// \\computer\object(instance#index)\counter
// \\computer\object(instance)\counter
// \\computer\object\counter
// \object(parent/instance#index)\counter
// \object(parent/instance)\counter
// \object(instance#index)\counter
// \object(instance)\counter
// \object\counter
// Use an asterisk (*) as the wildcard character, for example, \object(*)\counter.
//
// If a wildcard character is specified in the parent name, all instances of the specified object
// that match the specified instance and counter fields will be returned.
// For example, \object(*/instance)\counter.
//
// If a wildcard character is specified in the instance name, all instances of the specified object and parent object will be returned if all instance names
// corresponding to the specified index match the wildcard character. For example, \object(parent/*)\counter.
// If the object does not contain an instance, an error occurs.
//
// 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.
func pdhExpandWildCardPath(szWildCardPath string, mszExpandedPathList *uint16, pcchPathListLength *uint32) uint32 {
ptxt, _ := syscall.UTF16PtrFromString(szWildCardPath)
flags := uint32(0) // expand instances and counters
ret, _, _ := pdhExpandWildCardPathWProc.Call(
0, // search counters on local computer
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(pcchPathListLength)), //nolint:gosec // G103: Valid use of unsafe call to pass pcchPathListLength
uintptr(unsafe.Pointer(&flags))) //nolint:gosec // G103: Valid use of unsafe call to pass flags
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
buf := make([]uint16, 300)
_, err := windows.FormatMessage(flags, uintptr(libPdhDll.Handle), msgID, 0, buf, nil)
if err == nil {
return utf16PtrToString(&buf[0])
}
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
// hCounter [in]
// Handle of the counter from which you want to retrieve information. The pdhAddCounter function returns this handle.
//
// bRetrieveExplainText [in]
// Determines whether explain text is retrieved. If you set this parameter to TRUE, the explain text for the counter is retrieved.
// If you set this parameter to FALSE, the field in the returned buffer is NULL.
//
// pdwBufferSize [in, out]
// Size of the lpBuffer buffer, in bytes. If zero on input, the function returns PdhMoreData and sets this parameter to the required buffer size.
// If the buffer is larger than the required size, the function sets this parameter to the actual size of the buffer that was used.
// If the specified size on input is greater than zero but less than the required size, you should not rely on the returned size to reallocate the buffer.
//
// lpBuffer [out]
// 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.
// 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 {
ret, _, _ := pdhGetCounterInfoWProc.Call(
uintptr(hCounter),
uintptr(bRetrieveExplainText),
uintptr(unsafe.Pointer(pdwBufferSize)), //nolint:gosec // G103: Valid use of unsafe call to pass pdwBufferSize
uintptr(unsafe.Pointer(lpBuffer))) //nolint:gosec // G103: Valid use of unsafe call to pass lpBuffer
return uint32(ret)
}
// pdhGetRawCounterValue returns the current raw value of the counter.
// If the specified counter instance does not exist, this function will return errorSuccess
// and the CStatus member of the pdhRawCounter structure will contain PdhCstatusNoInstance.
//
// hCounter [in]
// Handle of the counter from which to retrieve the current raw value. The pdhAddCounter function returns this handle.
//
// lpdwType [out]
// Receives the counter type. For a list of counter types, see the Counter Types section of the Windows Server 2003 Deployment Kit.
// This parameter is optional.
//
// pValue [out]
// A pdhRawCounter structure that receives the counter value.
func pdhGetRawCounterValue(hCounter pdhCounterHandle, lpdwType *uint32, pValue *pdhRawCounter) uint32 {
ret, _, _ := pdhGetRawCounterValueProc.Call(
uintptr(hCounter),
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
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
// of a counter that contains a wildcard character for the instance name.
// hCounter
// Handle of the counter for whose current raw instance values you want to retrieve. The pdhAddCounter function returns this handle.
//
// 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.
// If the buffer is larger than the required size, the function sets this parameter to the actual size of the buffer that was used.
// If the specified size on input is greater than zero but less than the required size, you should not rely on the returned size to reallocate the buffer.
//
// lpdwItemCount
// Number of raw counter values in the ItemBuffer buffer.
//
// ItemBuffer
// 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.
func pdhGetRawCounterArray(hCounter pdhCounterHandle, lpdwBufferSize, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
ret, _, _ := pdhGetRawCounterArrayWProc.Call(
uintptr(hCounter),
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(itemBuffer))) //nolint:gosec // G103: Valid use of unsafe call to pass itemBuffer
return uint32(ret)
}

View file

@ -0,0 +1,126 @@
// Copyright (c) 2010 The win Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This is the official list of 'win' authors for copyright purposes.
//
// Alexander Neumann <an2048@googlemail.com>
// Joseph Watson <jtwatson@linux-consulting.us>
// Kevin Pors <krpors@gmail.com>
//go:build windows
package win_perf_counters
// pdhFmtCountervalueDouble is a union specialization for double values
type pdhFmtCountervalueDouble struct {
CStatus uint32
padding [4]byte
DoubleValue float64
}
// pdhFmtCountervalueLong is a union specialization for long values
type pdhFmtCountervalueLong struct {
CStatus uint32
LongValue int32
padding [4]byte //nolint:unused // Memory reservation
}
type pdhFmtCountervalueItemDouble struct {
SzName *uint16
padding [4]byte //nolint:unused // Memory reservation
FmtValue pdhFmtCountervalueDouble
}
// pdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
type pdhCounterInfo struct {
//Size of the structure, including the appended strings, in bytes.
DwLength uint32
//Counter type. For a list of counter types, see the Counter Types section of the
//<a "href=http://go.microsoft.com/fwlink/p/?linkid=84422">Windows Server 2003 Deployment Kit</a>.
//The counter type constants are defined in Winperf.h.
DwType uint32
//Counter version information. Not used.
CVersion uint32
//Counter status that indicates if the counter value is valid. For a list of possible values,
//see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
CStatus uint32
//Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
//The valid range of this parameter is PDH_MIN_SCALE (7) (the returned value is the actual value times 10⁷) to
//PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned
LScale int32
//Default scale factor as suggested by the counter's provider.
LDefaultScale int32
//The value passed in the dwUserData parameter when calling pdhAddCounter.
DwUserData *uint32
//The value passed in the dwUserData parameter when calling pdhOpenQuery.
DwQueryUserData *uint32
//Null-terminated string that specifies the full counter path. The string follows this structure in memory.
SzFullPath *uint16 // pointer to a string
//Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
//The string follows this structure in memory.
SzMachineName *uint16 // pointer to a string
//Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
SzObjectName *uint16 // pointer to a string
//Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
//The string follows this structure in memory.
SzInstanceName *uint16 // pointer to a string
//Null-terminated string that contains the name of the parent instance specified in the counter path. Is NULL,
//if the path does not specify a parent instance.
//The string follows this structure in memory.
SzParentInstance *uint16 // pointer to a string
//Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
DwInstanceIndex uint32 // pointer to a string
//Null-terminated string that contains the counter name. The string follows this structure in memory.
SzCounterName *uint16 // pointer to a string
//padding
Padding [4]byte
//Help text that describes the counter. Is NULL if the source is a log file.
SzExplainText *uint16 // pointer to a string
//Start of the string data that is appended to the structure.
DataBuffer [1]uint32 // pointer to an extra space
}
// The pdhRawCounter structure returns the data as it was collected from the counter provider. No translation, formatting,
// or other interpretation is performed on the data
type pdhRawCounter struct {
// Counter status that indicates if the counter value is valid. Check this member before using the data in a calculation or displaying its value.
// For a list of possible values, see https://docs.microsoft.com/windows/desktop/PerfCtrs/checking-pdh-interface-return-values
CStatus uint32
// Local time for when the data was collected
TimeStamp fileTime
// First raw counter value.
FirstValue int64
// Second raw counter value. Rate counters require two values in order to compute a displayable value.
SecondValue int64
// If the counter type contains the PERF_MULTI_COUNTER flag, this member contains the additional counter data used in the calculation.
// For example, the PERF_100NSEC_MULTI_TIMER counter type contains the PERF_MULTI_COUNTER flag.
MultiCount uint32
}
type pdhRawCounterItem struct {
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
SzName *uint16
//A pdhRawCounter structure that contains the raw counter value of the instance
RawValue pdhRawCounter
}

View file

@ -0,0 +1,115 @@
// Copyright (c) 2010 The win Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This is the official list of 'win' authors for copyright purposes.
//
// Alexander Neumann <an2048@googlemail.com>
// Joseph Watson <jtwatson@linux-consulting.us>
// Kevin Pors <krpors@gmail.com>
//go:build windows
package win_perf_counters
// pdhFmtCountervalueDouble is a union specialization for double values
type pdhFmtCountervalueDouble struct {
CStatus uint32
DoubleValue float64
}
// pdhFmtCountervalueItemDouble is a union specialization for double values, used by pdhGetFormattedCounterArrayDouble
type pdhFmtCountervalueItemDouble struct {
SzName *uint16
FmtValue pdhFmtCountervalueDouble
}
// pdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
type pdhCounterInfo struct {
// Size of the structure, including the appended strings, in bytes.
DwLength uint32
// Counter type. For a list of counter types,
// see the Counter Types section of the <a "href=http://go.microsoft.com/fwlink/p/?linkid=84422">Windows Server 2003 Deployment Kit</a>.
// The counter type constants are defined in Winperf.h.
DwType uint32
// Counter version information. Not used.
CVersion uint32
// Counter status that indicates if the counter value is valid. For a list of possible values,
// see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
CStatus uint32
// Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
// The valid range of this parameter is PDH_MIN_SCALE (7) (the returned value is the actual value times 10⁷) to
// PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned
LScale int32
// Default scale factor as suggested by the counter's provider.
LDefaultScale int32
// The value passed in the dwUserData parameter when calling pdhAddCounter.
DwUserData *uint32
// The value passed in the dwUserData parameter when calling pdhOpenQuery.
DwQueryUserData *uint32
// Null-terminated string that specifies the full counter path. The string follows this structure in memory.
SzFullPath *uint16 // pointer to a string
// Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
// The string follows this structure in memory.
SzMachineName *uint16 // pointer to a string
// Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
SzObjectName *uint16 // pointer to a string
// Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
// The string follows this structure in memory.
SzInstanceName *uint16 // pointer to a string
// Null-terminated string that contains the name of the parent instance specified in the counter path.
// Is NULL, if the path does not specify a parent instance. The string follows this structure in memory.
SzParentInstance *uint16 // pointer to a string
// Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
DwInstanceIndex uint32 // pointer to a string
// Null-terminated string that contains the counter name. The string follows this structure in memory.
SzCounterName *uint16 // pointer to a string
// Help text that describes the counter. Is NULL if the source is a log file.
SzExplainText *uint16 // pointer to a string
// Start of the string data that is appended to the structure.
DataBuffer [1]uint32 // pointer to an extra space
}
// The pdhRawCounter structure returns the data as it was collected from the counter provider.
// No translation, formatting, or other interpretation is performed on the data
type pdhRawCounter struct {
// Counter status that indicates if the counter value is valid. Check this member before using the data in a calculation or displaying its value.
// For a list of possible values, see https://docs.microsoft.com/windows/desktop/PerfCtrs/checking-pdh-interface-return-values
CStatus uint32
// Local time for when the data was collected
TimeStamp fileTime
// First raw counter value.
FirstValue int64
// Second raw counter value. Rate counters require two values in order to compute a displayable value.
SecondValue int64
// If the counter type contains the PERF_MULTI_COUNTER flag, this member contains the additional counter data used in the calculation.
// For example, the PERF_100NSEC_MULTI_TIMER counter type contains the PERF_MULTI_COUNTER flag.
MultiCount uint32
}
type pdhRawCounterItem struct {
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
SzName *uint16
// A pdhRawCounter structure that contains the raw counter value of the instance
RawValue pdhRawCounter
}

View file

@ -0,0 +1,115 @@
// Copyright (c) 2010 The win Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This is the official list of 'win' authors for copyright purposes.
//
// Alexander Neumann <an2048@googlemail.com>
// Joseph Watson <jtwatson@linux-consulting.us>
// Kevin Pors <krpors@gmail.com>
//go:build windows
package win_perf_counters
// pdhFmtCountervalueDouble is a union specialization for double values
type pdhFmtCountervalueDouble struct {
CStatus uint32
DoubleValue float64
}
type pdhFmtCountervalueItemDouble struct {
SzName *uint16
FmtValue pdhFmtCountervalueDouble
}
// pdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
type pdhCounterInfo struct {
//Size of the structure, including the appended strings, in bytes.
DwLength uint32
//Counter type. For a list of counter types, see the Counter Types section
//of the Windows Server 2003 Deployment Kit (http://go.microsoft.com/fwlink/p/?linkid=84422).
//The counter type constants are defined in Winperf.h.
DwType uint32
//Counter version information. Not used.
CVersion uint32
//Counter status that indicates if the counter value is valid. For a list of possible values,
//see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
CStatus uint32
//Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
//The valid range of this parameter is PDH_MIN_SCALE (7) (the returned value is the actual value times 10⁷) to
//PDH_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned
LScale int32
//Default scale factor as suggested by the counter's provider.
LDefaultScale int32
//The value passed in the dwUserData parameter when calling pdhAddCounter.
DwUserData *uint32
//The value passed in the dwUserData parameter when calling pdhOpenQuery.
DwQueryUserData *uint32
//Null-terminated string that specifies the full counter path. The string follows this structure in memory.
SzFullPath *uint16 // pointer to a string
//Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
//The string follows this structure in memory.
SzMachineName *uint16 // pointer to a string
//Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
SzObjectName *uint16 // pointer to a string
//Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
//The string follows this structure in memory.
SzInstanceName *uint16 // pointer to a string
//Null-terminated string that contains the name of the parent instance specified in the counter path.
//Is NULL, if the path does not specify a parent instance.
//The string follows this structure in memory.
SzParentInstance *uint16 // pointer to a string
//Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
DwInstanceIndex uint32 // pointer to a string
//Null-terminated string that contains the counter name. The string follows this structure in memory.
SzCounterName *uint16 // pointer to a string
//Help text that describes the counter. Is NULL if the source is a log file.
SzExplainText *uint16 // pointer to a string
//Start of the string data that is appended to the structure.
DataBuffer [1]uint32 // pointer to an extra space
}
// The pdhRawCounter structure returns the data as it was collected from the counter provider.
// No translation, formatting, or other interpretation is performed on the data.
type pdhRawCounter struct {
// Counter status that indicates if the counter value is valid. Check this member before using the data in a calculation or displaying its value.
// For a list of possible values, see https://docs.microsoft.com/windows/desktop/PerfCtrs/checking-pdh-interface-return-values
CStatus uint32
// Local time for when the data was collected
TimeStamp fileTime
// First raw counter value.
FirstValue int64
// Second raw counter value. Rate counters require two values in order to compute a displayable value.
SecondValue int64
// If the counter type contains the PERF_MULTI_COUNTER flag, this member contains the additional counter data used in the calculation.
// For example, the PERF_100NSEC_MULTI_TIMER counter type contains the PERF_MULTI_COUNTER flag.
MultiCount uint32
}
type pdhRawCounterItem struct {
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
SzName *uint16
//A pdhRawCounter structure that contains the raw counter value of the instance
RawValue pdhRawCounter
}

View file

@ -0,0 +1,333 @@
// Go API over pdh syscalls
//go:build windows
package win_perf_counters
import (
"errors"
"syscall"
"time"
"unsafe"
)
// Initial buffer size for return buffers
const initialBufferSize = uint32(1024) // 1kB
var errBufferLimitReached = errors.New("buffer limit reached")
// counterValue is abstraction for pdhFmtCountervalueItemDouble
type counterValue struct {
instanceName string
value interface{}
}
// performanceQuery provides wrappers around Windows performance counters API for easy usage in GO
//
//nolint:interfacebloat // conditionally allow to contain more methods
type performanceQuery interface {
open() error
close() error
addCounterToQuery(counterPath string) (pdhCounterHandle, error)
addEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error)
getCounterPath(counterHandle pdhCounterHandle) (string, error)
expandWildCardPath(counterPath string) ([]string, error)
getFormattedCounterValueDouble(hCounter pdhCounterHandle) (float64, error)
getRawCounterValue(hCounter pdhCounterHandle) (int64, error)
getFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error)
getRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error)
collectData() error
collectDataWithTime() (time.Time, error)
isVistaOrNewer() bool
}
type performanceQueryCreator interface {
newPerformanceQuery(string, uint32) performanceQuery
}
// pdhError represents error returned from Performance Counters API
type pdhError struct {
errorCode uint32
errorText string
}
func (m *pdhError) Error() string {
return m.errorText
}
func newPdhError(code uint32) error {
return &pdhError{
errorCode: code,
errorText: pdhFormatError(code),
}
}
// performanceQueryImpl is implementation of performanceQuery interface, which calls phd.dll functions
type performanceQueryImpl struct {
maxBufferSize uint32
query pdhQueryHandle
}
type performanceQueryCreatorImpl struct{}
func (performanceQueryCreatorImpl) newPerformanceQuery(_ string, maxBufferSize uint32) performanceQuery {
return &performanceQueryImpl{maxBufferSize: maxBufferSize}
}
// 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
func (m *performanceQueryImpl) open() error {
if m.query != 0 {
err := m.close()
if err != nil {
return err
}
}
var handle pdhQueryHandle
if ret := pdhOpenQuery(0, 0, &handle); ret != errorSuccess {
return newPdhError(ret)
}
m.query = handle
return nil
}
// close closes the counterPath, releases associated counter handles and frees resources
func (m *performanceQueryImpl) close() error {
if m.query == 0 {
return errors.New("uninitialized query")
}
if ret := pdhCloseQuery(m.query); ret != errorSuccess {
return newPdhError(ret)
}
m.query = 0
return nil
}
func (m *performanceQueryImpl) addCounterToQuery(counterPath string) (pdhCounterHandle, error) {
var counterHandle pdhCounterHandle
if m.query == 0 {
return 0, errors.New("uninitialized query")
}
if ret := pdhAddCounter(m.query, counterPath, 0, &counterHandle); ret != errorSuccess {
return 0, newPdhError(ret)
}
return counterHandle, nil
}
func (m *performanceQueryImpl) addEnglishCounterToQuery(counterPath string) (pdhCounterHandle, error) {
var counterHandle pdhCounterHandle
if m.query == 0 {
return 0, errors.New("uninitialized query")
}
if ret := pdhAddEnglishCounter(m.query, counterPath, 0, &counterHandle); ret != errorSuccess {
return 0, newPdhError(ret)
}
return counterHandle, nil
}
// getCounterPath returns counter information for given handle
func (m *performanceQueryImpl) getCounterPath(counterHandle pdhCounterHandle) (string, error) {
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
buf := make([]byte, buflen)
// Get the info with the current buffer size
size := buflen
ret := pdhGetCounterInfo(counterHandle, 0, &size, &buf[0])
if ret == errorSuccess {
ci := (*pdhCounterInfo)(unsafe.Pointer(&buf[0])) //nolint:gosec // G103: Valid use of unsafe call to create PDH_COUNTER_INFO
return utf16PtrToString(ci.SzFullPath), nil
}
// Use the size as a hint if it exceeds the current buffer size
if size > buflen {
buflen = size
}
// We got a non-recoverable error so exit here
if ret != pdhMoreData {
return "", newPdhError(ret)
}
}
return "", errBufferLimitReached
}
// 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) {
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
buf := make([]uint16, buflen)
// Get the info with the current buffer size
size := buflen
ret := pdhExpandWildCardPath(counterPath, &buf[0], &size)
if ret == errorSuccess {
return utf16ToStringArray(buf), nil
}
// Use the size as a hint if it exceeds the current buffer size
if size > buflen {
buflen = size
}
// We got a non-recoverable error so exit here
if ret != pdhMoreData {
return nil, newPdhError(ret)
}
}
return nil, errBufferLimitReached
}
// getFormattedCounterValueDouble computes a displayable value for the specified counter
func (*performanceQueryImpl) getFormattedCounterValueDouble(hCounter pdhCounterHandle) (float64, error) {
var counterType uint32
var value pdhFmtCountervalueDouble
if ret := pdhGetFormattedCounterValueDouble(hCounter, &counterType, &value); ret != errorSuccess {
return 0, newPdhError(ret)
}
if value.CStatus == pdhCstatusValidData || value.CStatus == pdhCstatusNewData {
return value.DoubleValue, nil
}
return 0, newPdhError(value.CStatus)
}
func (m *performanceQueryImpl) getFormattedCounterArrayDouble(hCounter pdhCounterHandle) ([]counterValue, error) {
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
buf := make([]byte, buflen)
// Get the info with the current buffer size
var itemCount uint32
size := buflen
ret := pdhGetFormattedCounterArrayDouble(hCounter, &size, &itemCount, &buf[0])
if ret == errorSuccess {
//nolint:gosec // G103: Valid use of unsafe call to create PDH_FMT_COUNTERVALUE_ITEM_DOUBLE
items := (*[1 << 20]pdhFmtCountervalueItemDouble)(unsafe.Pointer(&buf[0]))[:itemCount]
values := make([]counterValue, 0, itemCount)
for _, item := range items {
if item.FmtValue.CStatus == pdhCstatusValidData || item.FmtValue.CStatus == pdhCstatusNewData {
val := counterValue{utf16PtrToString(item.SzName), item.FmtValue.DoubleValue}
values = append(values, val)
}
}
return values, nil
}
// Use the size as a hint if it exceeds the current buffer size
if size > buflen {
buflen = size
}
// We got a non-recoverable error so exit here
if ret != pdhMoreData {
return nil, newPdhError(ret)
}
}
return nil, errBufferLimitReached
}
func (m *performanceQueryImpl) getRawCounterArray(hCounter pdhCounterHandle) ([]counterValue, error) {
for buflen := initialBufferSize; buflen <= m.maxBufferSize; buflen *= 2 {
buf := make([]byte, buflen)
// Get the info with the current buffer size
var itemCount uint32
size := buflen
ret := pdhGetRawCounterArray(hCounter, &size, &itemCount, &buf[0])
if ret == errorSuccess {
//nolint:gosec // G103: Valid use of unsafe call to create PDH_RAW_COUNTER_ITEM
items := (*[1 << 20]pdhRawCounterItem)(unsafe.Pointer(&buf[0]))[:itemCount]
values := make([]counterValue, 0, itemCount)
for _, item := range items {
if item.RawValue.CStatus == pdhCstatusValidData || item.RawValue.CStatus == pdhCstatusNewData {
val := counterValue{utf16PtrToString(item.SzName), item.RawValue.FirstValue}
values = append(values, val)
}
}
return values, nil
}
// Use the size as a hint if it exceeds the current buffer size
if size > buflen {
buflen = size
}
// We got a non-recoverable error so exit here
if ret != pdhMoreData {
return nil, newPdhError(ret)
}
}
return nil, errBufferLimitReached
}
func (m *performanceQueryImpl) collectData() error {
var ret uint32
if m.query == 0 {
return errors.New("uninitialized query")
}
if ret = pdhCollectQueryData(m.query); ret != errorSuccess {
return newPdhError(ret)
}
return nil
}
func (m *performanceQueryImpl) collectDataWithTime() (time.Time, error) {
if m.query == 0 {
return time.Now(), errors.New("uninitialized query")
}
ret, mtime := pdhCollectQueryDataWithTime(m.query)
if ret != errorSuccess {
return time.Now(), newPdhError(ret)
}
return mtime, nil
}
func (*performanceQueryImpl) isVistaOrNewer() bool {
return pdhAddEnglishCounterSupported()
}
func (m *performanceQueryImpl) getRawCounterValue(hCounter pdhCounterHandle) (int64, error) {
if m.query == 0 {
return 0, errors.New("uninitialised query")
}
var counterType uint32
var value pdhRawCounter
var ret uint32
if ret = pdhGetRawCounterValue(hCounter, &counterType, &value); ret == errorSuccess {
if value.CStatus == pdhCstatusValidData || value.CStatus == pdhCstatusNewData {
return value.FirstValue, nil
}
return 0, newPdhError(value.CStatus)
}
return 0, newPdhError(ret)
}
// utf16PtrToString converts Windows API LPTSTR (pointer to string) to go string
func utf16PtrToString(s *uint16) string {
if s == nil {
return ""
}
//nolint:gosec // G103: Valid use of unsafe call to create string from Windows API LPTSTR (pointer to string)
return syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(s))[0:])
}
// utf16ToStringArray converts list of Windows API NULL terminated strings to go string array
func utf16ToStringArray(buf []uint16) []string {
var strings []string
nextLineStart := 0
stringLine := utf16PtrToString(&buf[0])
for stringLine != "" {
strings = append(strings, stringLine)
nextLineStart += len([]rune(stringLine)) + 1
remainingBuf := buf[nextLineStart:]
stringLine = utf16PtrToString(&remainingBuf[0])
}
return strings
}

View file

@ -0,0 +1,170 @@
# Input plugin to counterPath Performance Counters on Windows operating systems
# This plugin ONLY supports Windows
[[inputs.win_perf_counters]]
## By default this plugin returns basic CPU and Disk statistics. See the
## README file for more examples. Uncomment examples below or write your own
## as you see fit. If the system being polled for data does not have the
## Object at startup of the Telegraf agent, it will not be gathered.
## Print All matching performance counters
# PrintValid = false
## Whether request a timestamp along with the PerfCounter data or use current
## time
# UsePerfCounterTime = true
## If UseWildcardsExpansion params is set to true, wildcards (partial
## wildcards in instance names and wildcards in counters names) in configured
## counter paths will be expanded and in case of localized Windows, counter
## paths will be also localized. It also returns instance indexes in instance
## names. If false, wildcards (not partial) in instance names will still be
## expanded, but instance indexes will not be returned in instance names.
# UseWildcardsExpansion = false
## When running on a localized version of Windows and with
## UseWildcardsExpansion = true, Windows will localize object and counter
## names. When LocalizeWildcardsExpansion = false, use the names in
## object.Counters instead of the localized names. Only Instances can have
## wildcards in this case. ObjectName and Counters must not have wildcards
## when this setting is false.
# LocalizeWildcardsExpansion = true
## Period after which counters will be reread from configuration and
## wildcards in counter paths expanded
# CountersRefreshInterval="1m"
## Accepts a list of PDH error codes which are defined in pdh.go, if this
## error is encountered it will be ignored. For example, you can provide
## "PDH_NO_DATA" to ignore performance counters with no instances. By default
## no errors are ignored You can find the list here:
## https://github.com/influxdata/telegraf/blob/master/plugins/inputs/win_perf_counters/pdh.go
## e.g. IgnoredErrors = ["PDH_NO_DATA"]
# IgnoredErrors = []
## Maximum size of the buffer for values returned by the API
## Increase this value if you experience "buffer limit reached" errors.
# MaxBufferSize = "4MiB"
## NOTE: Due to the way TOML is parsed, tables must be at the END of the
## plugin definition, otherwise additional config options are read as part of
## the table
# [[inputs.win_perf_counters.object]]
# Measurement = ""
# ObjectName = ""
# Instances = [""]
# Counters = []
## Additional Object Settings
## * IncludeTotal: set to true to include _Total instance when querying
## for all metrics via '*'
## * WarnOnMissing: print out when the performance counter is missing
## from object, counter or instance
## * UseRawValues: gather raw values instead of formatted. Raw values are
## stored in the field name with the "_Raw" suffix, e.g.
## "Disk_Read_Bytes_sec_Raw".
# IncludeTotal = false
# WarnOnMissing = false
# UseRawValues = false
## Processor usage, alternative to native, reports on a per core.
# [[inputs.win_perf_counters.object]]
# Measurement = "win_cpu"
# ObjectName = "Processor"
# Instances = ["*"]
# UseRawValues = true
# Counters = [
# "% Idle Time",
# "% Interrupt Time",
# "% Privileged Time",
# "% User Time",
# "% Processor Time",
# "% DPC Time",
# ]
## Disk times and queues
# [[inputs.win_perf_counters.object]]
# Measurement = "win_disk"
# ObjectName = "LogicalDisk"
# Instances = ["*"]
# Counters = [
# "% Idle Time",
# "% Disk Time",
# "% Disk Read Time",
# "% Disk Write Time",
# "% User Time",
# "% Free Space",
# "Current Disk Queue Length",
# "Free Megabytes",
# ]
# [[inputs.win_perf_counters.object]]
# Measurement = "win_diskio"
# ObjectName = "PhysicalDisk"
# Instances = ["*"]
# Counters = [
# "Disk Read Bytes/sec",
# "Disk Write Bytes/sec",
# "Current Disk Queue Length",
# "Disk Reads/sec",
# "Disk Writes/sec",
# "% Disk Time",
# "% Disk Read Time",
# "% Disk Write Time",
# ]
# [[inputs.win_perf_counters.object]]
# Measurement = "win_net"
# ObjectName = "Network Interface"
# Instances = ["*"]
# Counters = [
# "Bytes Received/sec",
# "Bytes Sent/sec",
# "Packets Received/sec",
# "Packets Sent/sec",
# "Packets Received Discarded",
# "Packets Outbound Discarded",
# "Packets Received Errors",
# "Packets Outbound Errors",
# ]
# [[inputs.win_perf_counters.object]]
# Measurement = "win_system"
# ObjectName = "System"
# Instances = ["------"]
# Counters = [
# "Context Switches/sec",
# "System Calls/sec",
# "Processor Queue Length",
# "System Up Time",
# ]
## Example counterPath where the Instance portion must be removed to get
## data back, such as from the Memory object.
# [[inputs.win_perf_counters.object]]
# Measurement = "win_mem"
# ObjectName = "Memory"
## Use 6 x - to remove the Instance bit from the counterPath.
# Instances = ["------"]
# Counters = [
# "Available Bytes",
# "Cache Faults/sec",
# "Demand Zero Faults/sec",
# "Page Faults/sec",
# "Pages/sec",
# "Transition Faults/sec",
# "Pool Nonpaged Bytes",
# "Pool Paged Bytes",
# "Standby Cache Reserve Bytes",
# "Standby Cache Normal Priority Bytes",
# "Standby Cache Core Bytes",
# ]
## Example query where the Instance portion must be removed to get data back,
## such as from the Paging File object.
# [[inputs.win_perf_counters.object]]
# Measurement = "win_swap"
# ObjectName = "Paging File"
# Instances = ["_Total"]
# Counters = [
# "% Usage",
# ]

View file

@ -0,0 +1,633 @@
//go:generate ../../../tools/readme_config_includer/generator
//go:build windows
package win_perf_counters
import (
_ "embed"
"errors"
"fmt"
"math"
"os"
"strings"
"sync"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
var (
defaultMaxBufferSize = config.Size(100 * 1024 * 1024)
sanitizedChars = strings.NewReplacer("/sec", "_persec", "/Sec", "_persec", " ", "_", "%", "Percent", `\`, "")
)
const emptyInstance = "------"
type WinPerfCounters struct {
PrintValid bool `toml:"PrintValid"`
PreVistaSupport bool `toml:"PreVistaSupport" deprecated:"1.7.0;1.35.0;determined dynamically"`
UsePerfCounterTime bool `toml:"UsePerfCounterTime"`
Object []perfObject `toml:"object"`
CountersRefreshInterval config.Duration `toml:"CountersRefreshInterval"`
UseWildcardsExpansion bool `toml:"UseWildcardsExpansion"`
LocalizeWildcardsExpansion bool `toml:"LocalizeWildcardsExpansion"`
IgnoredErrors []string `toml:"IgnoredErrors"`
MaxBufferSize config.Size `toml:"MaxBufferSize"`
Sources []string `toml:"Sources"`
Log telegraf.Logger `toml:"-"`
lastRefreshed time.Time
queryCreator performanceQueryCreator
hostCounters map[string]*hostCountersInfo
// cached os.Hostname()
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 {
// computer name used as key and for printing
computer string
// computer name used in tag
tag string
counters []*counter
query performanceQuery
timestamp time.Time
}
type counter struct {
counterPath string
computer string
objectName string
counter string
instance string
measurement string
includeTotal bool
useRawValue bool
counterHandle pdhCounterHandle
}
type instanceGrouping struct {
name string
instance string
objectName string
}
type fieldGrouping map[instanceGrouping]map[string]interface{}
func (*WinPerfCounters) SampleConfig() string {
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
// General Counter path pattern is: \\computer\object(parent/instance#index)\counter
// parent/instance#index part is skipped in single instance objects (e.g. Memory): \\computer\object\counter
//
//nolint:revive //function-result-limit conditionally 5 return results allowed
func extractCounterInfoFromCounterPath(counterPath string) (computer string, object string, instance string, counter string, err error) {
leftComputerBorderIndex := -1
rightObjectBorderIndex := -1
leftObjectBorderIndex := -1
leftCounterBorderIndex := -1
rightInstanceBorderIndex := -1
leftInstanceBorderIndex := -1
var bracketLevel int
for i := len(counterPath) - 1; i >= 0; i-- {
switch counterPath[i] {
case '\\':
if bracketLevel == 0 {
if leftCounterBorderIndex == -1 {
leftCounterBorderIndex = i
} else if leftObjectBorderIndex == -1 {
leftObjectBorderIndex = i
} else if leftComputerBorderIndex == -1 {
leftComputerBorderIndex = i
}
}
case '(':
bracketLevel--
if leftInstanceBorderIndex == -1 && bracketLevel == 0 && leftObjectBorderIndex == -1 && leftCounterBorderIndex > -1 {
leftInstanceBorderIndex = i
rightObjectBorderIndex = i
}
case ')':
if rightInstanceBorderIndex == -1 && bracketLevel == 0 && leftCounterBorderIndex > -1 {
rightInstanceBorderIndex = i
}
bracketLevel++
}
}
if rightObjectBorderIndex == -1 {
rightObjectBorderIndex = leftCounterBorderIndex
}
if rightObjectBorderIndex == -1 || leftObjectBorderIndex == -1 {
return "", "", "", "", errors.New("cannot parse object from: " + counterPath)
}
if leftComputerBorderIndex > -1 {
// validate there is leading \\ and not empty computer (\\\O)
if leftComputerBorderIndex != 1 || leftComputerBorderIndex == leftObjectBorderIndex-1 {
return "", "", "", "", errors.New("cannot parse computer from: " + counterPath)
}
computer = counterPath[leftComputerBorderIndex+1 : leftObjectBorderIndex]
}
if leftInstanceBorderIndex > -1 && rightInstanceBorderIndex > -1 {
instance = counterPath[leftInstanceBorderIndex+1 : rightInstanceBorderIndex]
} else if (leftInstanceBorderIndex == -1 && rightInstanceBorderIndex > -1) || (leftInstanceBorderIndex > -1 && rightInstanceBorderIndex == -1) {
return "", "", "", "", errors.New("cannot parse instance from: " + counterPath)
}
object = counterPath[leftObjectBorderIndex+1 : rightObjectBorderIndex]
counter = counterPath[leftCounterBorderIndex+1:]
return computer, object, instance, counter, nil
}
func (m *WinPerfCounters) hostname() string {
if m.cachedHostname != "" {
return m.cachedHostname
}
hostname, err := os.Hostname()
if err != nil {
m.cachedHostname = "localhost"
} else {
m.cachedHostname = hostname
}
return m.cachedHostname
}
//nolint:revive //argument-limit conditionally more arguments allowed for helper function
func newCounter(
counterHandle pdhCounterHandle,
counterPath string,
computer string,
objectName string,
instance string,
counterName string,
measurement string,
includeTotal bool,
useRawValue bool,
) *counter {
measurementName := sanitizedChars.Replace(measurement)
if measurementName == "" {
measurementName = "win_perf_counters"
}
newCounterName := sanitizedChars.Replace(counterName)
if useRawValue {
newCounterName += "_Raw"
}
return &counter{counterPath, computer, objectName, newCounterName, instance, measurementName,
includeTotal, useRawValue, counterHandle}
}
//nolint:revive //argument-limit conditionally more arguments allowed
func (m *WinPerfCounters) addItem(counterPath, computer, objectName, instance, counterName, measurement string, includeTotal bool, useRawValue bool) error {
origCounterPath := counterPath
var err error
var counterHandle pdhCounterHandle
sourceTag := computer
if computer == "localhost" {
sourceTag = m.hostname()
}
if m.hostCounters == nil {
m.hostCounters = make(map[string]*hostCountersInfo)
}
hostCounter, ok := m.hostCounters[computer]
if !ok {
hostCounter = &hostCountersInfo{computer: computer, tag: sourceTag}
m.hostCounters[computer] = hostCounter
hostCounter.query = m.queryCreator.newPerformanceQuery(computer, uint32(m.MaxBufferSize))
if err := hostCounter.query.open(); err != nil {
return err
}
hostCounter.counters = make([]*counter, 0)
}
if !hostCounter.query.isVistaOrNewer() {
counterHandle, err = hostCounter.query.addCounterToQuery(counterPath)
if err != nil {
return err
}
} else {
counterHandle, err = hostCounter.query.addEnglishCounterToQuery(counterPath)
if err != nil {
return err
}
}
if m.UseWildcardsExpansion {
origInstance := instance
counterPath, err = hostCounter.query.getCounterPath(counterHandle)
if err != nil {
return err
}
counters, err := hostCounter.query.expandWildCardPath(counterPath)
if err != nil {
return err
}
_, origObjectName, _, origCounterName, err := extractCounterInfoFromCounterPath(origCounterPath)
if err != nil {
return err
}
for _, counterPath := range counters {
_, err := hostCounter.query.addCounterToQuery(counterPath)
if err != nil {
return err
}
computer, objectName, instance, counterName, err = extractCounterInfoFromCounterPath(counterPath)
if err != nil {
return err
}
var newItem *counter
if !m.LocalizeWildcardsExpansion {
// On localized installations of Windows, Telegraf
// should return English metrics, but
// expandWildCardPath returns localized counters. Undo
// that by using the original object and counter
// names, along with the expanded instance.
var newInstance string
if instance == "" {
newInstance = emptyInstance
} else {
newInstance = instance
}
counterPath = formatPath(computer, origObjectName, newInstance, origCounterName)
counterHandle, err = hostCounter.query.addEnglishCounterToQuery(counterPath)
if err != nil {
return err
}
newItem = newCounter(
counterHandle,
counterPath,
computer,
origObjectName, instance,
origCounterName,
measurement,
includeTotal,
useRawValue,
)
} else {
counterHandle, err = hostCounter.query.addCounterToQuery(counterPath)
if err != nil {
return err
}
newItem = newCounter(
counterHandle,
counterPath,
computer,
objectName,
instance,
counterName,
measurement,
includeTotal,
useRawValue,
)
}
if instance == "_Total" && origInstance == "*" && !includeTotal {
continue
}
hostCounter.counters = append(hostCounter.counters, newItem)
if m.PrintValid {
m.Log.Infof("Valid: %s", counterPath)
}
}
} else {
newItem := newCounter(
counterHandle,
counterPath,
computer,
objectName,
instance,
counterName,
measurement,
includeTotal,
useRawValue,
)
hostCounter.counters = append(hostCounter.counters, newItem)
if m.PrintValid {
m.Log.Infof("Valid: %s", counterPath)
}
}
return nil
}
func formatPath(computer, objectName, instance, counter string) string {
path := ""
if instance == emptyInstance {
path = fmt.Sprintf(`\%s\%s`, objectName, counter)
} else {
path = fmt.Sprintf(`\%s(%s)\%s`, objectName, instance, counter)
}
if computer != "" && computer != "localhost" {
path = fmt.Sprintf(`\\%s%s`, computer, path)
}
return path
}
func (m *WinPerfCounters) parseConfig() error {
var counterPath string
if len(m.Sources) == 0 {
m.Sources = []string{"localhost"}
}
if len(m.Object) == 0 {
err := errors.New("no performance objects configured")
return err
}
for _, PerfObject := range m.Object {
computers := PerfObject.Sources
if len(computers) == 0 {
computers = m.Sources
}
for _, computer := range computers {
if computer == "" {
// localhost as a computer name in counter path doesn't work
computer = "localhost"
}
for _, counter := range PerfObject.Counters {
if len(PerfObject.Instances) == 0 {
m.Log.Warnf("Missing 'Instances' param for object %q", PerfObject.ObjectName)
}
for _, instance := range PerfObject.Instances {
objectName := PerfObject.ObjectName
counterPath = formatPath(computer, objectName, instance, counter)
err := m.addItem(counterPath, computer, objectName, instance, counter,
PerfObject.Measurement, PerfObject.IncludeTotal, PerfObject.UseRawValues)
if err != nil {
if PerfObject.FailOnMissing || PerfObject.WarnOnMissing {
m.Log.Errorf("Invalid counterPath %q: %s", counterPath, err.Error())
}
if PerfObject.FailOnMissing {
return err
}
}
}
}
}
}
return nil
}
func (m *WinPerfCounters) checkError(err error) error {
var pdhErr *pdhError
if errors.As(err, &pdhErr) {
for _, ignoredErrors := range m.IgnoredErrors {
if pdhErrors[pdhErr.errorCode] == ignoredErrors {
return nil
}
}
return err
}
return err
}
func (m *WinPerfCounters) gatherComputerCounters(hostCounterInfo *hostCountersInfo, acc telegraf.Accumulator) error {
var value interface{}
var err error
collectedFields := make(fieldGrouping)
// For iterate over the known metrics and get the samples.
for _, metric := range hostCounterInfo.counters {
// collect
if m.UseWildcardsExpansion {
if metric.useRawValue {
value, err = hostCounterInfo.query.getRawCounterValue(metric.counterHandle)
} else {
value, err = hostCounterInfo.query.getFormattedCounterValueDouble(metric.counterHandle)
}
if err != nil {
// ignore invalid data as some counters from process instances returns this sometimes
if !isKnownCounterDataError(err) {
return fmt.Errorf("error while getting value for counter %q: %w", metric.counterPath, err)
}
m.Log.Warnf("Error while getting value for counter %q, instance: %s, will skip metric: %v", metric.counterPath, metric.instance, err)
continue
}
addCounterMeasurement(metric, metric.instance, value, collectedFields)
} else {
var counterValues []counterValue
if metric.useRawValue {
counterValues, err = hostCounterInfo.query.getRawCounterArray(metric.counterHandle)
} else {
counterValues, err = hostCounterInfo.query.getFormattedCounterArrayDouble(metric.counterHandle)
}
if err != nil {
// ignore invalid data as some counters from process instances returns this sometimes
if !isKnownCounterDataError(err) {
return fmt.Errorf("error while getting value for counter %q: %w", metric.counterPath, err)
}
m.Log.Warnf("Error while getting value for counter %q, instance: %s, will skip metric: %v", metric.counterPath, metric.instance, err)
continue
}
for _, cValue := range counterValues {
if strings.Contains(metric.instance, "#") && strings.HasPrefix(metric.instance, cValue.instanceName) {
// If you are using a multiple instance identifier such as "w3wp#1"
// phd.dll returns only the first 2 characters of the identifier.
cValue.instanceName = metric.instance
}
if shouldIncludeMetric(metric, cValue) {
addCounterMeasurement(metric, cValue.instanceName, cValue.value, collectedFields)
}
}
}
}
for instance, fields := range collectedFields {
var tags = map[string]string{
"objectname": instance.objectName,
}
if len(instance.instance) > 0 {
tags["instance"] = instance.instance
}
if len(hostCounterInfo.tag) > 0 {
tags["source"] = hostCounterInfo.tag
}
acc.AddFields(instance.name, fields, tags, hostCounterInfo.timestamp)
}
return nil
}
func (m *WinPerfCounters) cleanQueries() error {
for _, hostCounterInfo := range m.hostCounters {
if err := hostCounterInfo.query.close(); err != nil {
return err
}
}
m.hostCounters = nil
return nil
}
func shouldIncludeMetric(metric *counter, cValue counterValue) bool {
if metric.includeTotal {
// If IncludeTotal is set, include all.
return true
}
if metric.instance == "*" && !strings.Contains(cValue.instanceName, "_Total") {
// Catch if set to * and that it is not a '*_Total*' instance.
return true
}
if metric.instance == cValue.instanceName {
// Catch if we set it to total or some form of it
return true
}
if metric.instance == emptyInstance {
return true
}
return false
}
func addCounterMeasurement(metric *counter, instanceName string, value interface{}, collectFields fieldGrouping) {
var instance = instanceGrouping{metric.measurement, instanceName, metric.objectName}
if collectFields[instance] == nil {
collectFields[instance] = make(map[string]interface{})
}
collectFields[instance][sanitizedChars.Replace(metric.counter)] = value
}
func isKnownCounterDataError(err error) bool {
var pdhErr *pdhError
if errors.As(err, &pdhErr) && (pdhErr.errorCode == pdhInvalidData ||
pdhErr.errorCode == pdhCalcNegativeDenominator ||
pdhErr.errorCode == pdhCalcNegativeValue ||
pdhErr.errorCode == pdhCstatusInvalidData ||
pdhErr.errorCode == pdhCstatusNoInstance ||
pdhErr.errorCode == pdhNoData) {
return true
}
return false
}
func init() {
inputs.Add("win_perf_counters", func() telegraf.Input {
return &WinPerfCounters{
CountersRefreshInterval: config.Duration(time.Second * 60),
LocalizeWildcardsExpansion: true,
MaxBufferSize: defaultMaxBufferSize,
queryCreator: &performanceQueryCreatorImpl{},
}
})
}

View file

@ -0,0 +1,563 @@
//go:build windows
package win_perf_counters
import (
"errors"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/testutil"
)
func TestWinPerformanceQueryImplIntegration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
query := &performanceQueryImpl{maxBufferSize: uint32(defaultMaxBufferSize)}
err := query.close()
require.Error(t, err, "uninitialized query must return errors")
_, err = query.addCounterToQuery("")
require.Error(t, err, "uninitialized query must return errors")
require.ErrorContains(t, err, "uninitialized")
_, err = query.addEnglishCounterToQuery("")
require.Error(t, err, "uninitialized query must return errors")
require.ErrorContains(t, err, "uninitialized")
err = query.collectData()
require.Error(t, err, "uninitialized query must return errors")
require.ErrorContains(t, err, "uninitialized")
require.NoError(t, query.open())
counterPath := "\\Processor Information(_Total)\\% Processor Time"
hCounter, err := query.addCounterToQuery(counterPath)
require.NoError(t, err)
require.NotEqual(t, 0, hCounter)
require.NoError(t, query.close())
require.NoError(t, query.open())
hCounter, err = query.addEnglishCounterToQuery(counterPath)
require.NoError(t, err)
require.NotEqual(t, 0, hCounter)
cp, err := query.getCounterPath(hCounter)
require.NoError(t, err)
require.True(t, strings.HasSuffix(cp, counterPath))
require.NoError(t, query.collectData())
time.Sleep(time.Second)
require.NoError(t, query.collectData())
fcounter, err := query.getFormattedCounterValueDouble(hCounter)
require.NoError(t, err)
require.Greater(t, fcounter, float64(0))
rcounter, err := query.getRawCounterValue(hCounter)
require.NoError(t, err)
require.Greater(t, rcounter, int64(10000000))
now := time.Now()
mtime, err := query.collectDataWithTime()
require.NoError(t, err)
require.Less(t, mtime.Sub(now), time.Second)
counterPath = "\\Process(*)\\% Processor Time"
paths, err := query.expandWildCardPath(counterPath)
require.NoError(t, err)
require.NotNil(t, paths)
require.Greater(t, len(paths), 1)
counterPath = "\\Process(_Total)\\*"
paths, err = query.expandWildCardPath(counterPath)
require.NoError(t, err)
require.NotNil(t, paths)
require.Greater(t, len(paths), 1)
require.NoError(t, query.open())
counterPath = "\\Process(*)\\% Processor Time"
hCounter, err = query.addEnglishCounterToQuery(counterPath)
require.NoError(t, err)
require.NotEqual(t, 0, hCounter)
require.NoError(t, query.collectData())
time.Sleep(time.Second)
require.NoError(t, query.collectData())
farr, err := query.getFormattedCounterArrayDouble(hCounter)
var phdErr *pdhError
if errors.As(err, &phdErr) && phdErr.errorCode != pdhInvalidData && phdErr.errorCode != pdhCalcNegativeValue {
time.Sleep(time.Second)
farr, err = query.getFormattedCounterArrayDouble(hCounter)
}
require.NoError(t, err)
require.NotEmpty(t, farr)
rarr, err := query.getRawCounterArray(hCounter)
require.NoError(t, err)
require.NotEmpty(t, rarr, "Too")
require.NoError(t, query.close())
}
func TestWinPerfCountersConfigGet1Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total"}
counters := []string{"% Processor Time"}
perfObjects := []perfObject{{
ObjectName: "Processor Information",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.NoError(t, m.parseConfig())
}
func TestWinPerfCountersConfigGet2Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total"}
counters := []string{"% Processor Time"}
perfObjects := []perfObject{{
ObjectName: "Processor Information",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok)
require.Len(t, hostCounters.counters, 1, "There should be exactly one result returned from the counterPath")
}
func TestWinPerfCountersConfigGet3Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
sources := []string{"localhost"}
instances := []string{"_Total"}
counters := []string{"% Processor Time", "% Idle Time"}
perfObjects := []perfObject{{
Sources: sources,
ObjectName: "Processor Information",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok)
require.Len(t, hostCounters.counters, 2, "There should be exactly two results returned from the counterPath")
}
func TestWinPerfCountersConfigGet4Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total", "0,1"}
counters := []string{"% Processor Time"}
perfObjects := []perfObject{{
ObjectName: "Processor Information",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok)
require.Len(t, hostCounters.counters, 2, "There should be exactly two results returned from the counterPath")
}
func TestWinPerfCountersConfigGet5Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total", "0,1"}
counters := []string{"% Processor Time", "% Idle Time"}
perfObjects := []perfObject{{
ObjectName: "Processor Information",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok)
require.Len(t, hostCounters.counters, 4, "There should be exactly four results returned from the counterPath")
}
func TestWinPerfCountersConfigGet6Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"------"}
counters := []string{"Context Switches/sec"}
perfObjects := []perfObject{{
ObjectName: "System",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.NoError(t, m.parseConfig())
_, ok := m.hostCounters["localhost"]
require.True(t, ok)
}
func TestWinPerfCountersConfigGet7Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total"}
counters := []string{"% Processor Time", "% Processor TimeERROR", "% Idle Time"}
perfObjects := []perfObject{{
ObjectName: "Processor Information",
Counters: counters,
Instances: instances,
Measurement: "test",
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.NoError(t, m.parseConfig())
hostCounters, ok := m.hostCounters["localhost"]
require.True(t, ok)
require.Len(t, hostCounters.counters, 2, "There should be exactly two results returned from the counterPath")
}
func TestWinPerfCountersConfigError1Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total"}
counters := []string{"% Processor Time"}
perfObjects := []perfObject{{
ObjectName: "Processor InformationERROR",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.Error(t, m.parseConfig())
}
func TestWinPerfCountersConfigError2Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"SuperERROR"}
counters := []string{"% C1 Time"}
perfObjects := []perfObject{{
ObjectName: "Processor",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.NoError(t, m.parseConfig())
var acc testutil.Accumulator
require.Error(t, m.Gather(&acc))
}
func TestWinPerfCountersConfigError3Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total"}
counters := []string{"% Processor TimeERROR"}
perfObjects := []perfObject{{
ObjectName: "Processor Information",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
require.Error(t, m.parseConfig())
}
func TestWinPerfCountersCollect1Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total"}
counters := []string{"Parking Status"}
perfObjects := []perfObject{{
ObjectName: "Processor Information",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
var acc testutil.Accumulator
require.NoError(t, m.Gather(&acc))
time.Sleep(2000 * time.Millisecond)
require.NoError(t, m.Gather(&acc))
require.Len(t, acc.Metrics, 2)
for _, metric := range acc.Metrics {
_, ok := metric.Fields["Parking_Status"]
require.True(t, ok)
}
}
func TestWinPerfCountersCollect2Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"_Total", "0,0"}
counters := []string{"Performance Limit Flags"}
perfObjects := []perfObject{{
ObjectName: "Processor Information",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
}}
m := WinPerfCounters{
PrintValid: false,
UsePerfCounterTime: true,
Object: perfObjects,
UseWildcardsExpansion: true,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
var acc testutil.Accumulator
require.NoError(t, m.Gather(&acc))
time.Sleep(2000 * time.Millisecond)
require.NoError(t, m.Gather(&acc))
require.Len(t, acc.Metrics, 4)
for _, metric := range acc.Metrics {
_, ok := metric.Fields["Performance_Limit_Flags"]
require.True(t, ok)
}
}
func TestWinPerfCountersCollectRawIntegration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
instances := []string{"*"}
counters := []string{"% Idle Time"}
perfObjects := []perfObject{{
ObjectName: "Processor",
Instances: instances,
Counters: counters,
Measurement: "test",
WarnOnMissing: false,
FailOnMissing: true,
IncludeTotal: false,
UseRawValues: true,
}}
m := WinPerfCounters{
PrintValid: false,
Object: perfObjects,
UseWildcardsExpansion: true,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
var acc testutil.Accumulator
require.NoError(t, m.Gather(&acc))
time.Sleep(2000 * time.Millisecond)
require.NoError(t, m.Gather(&acc))
require.Greater(t, len(acc.Metrics), 1)
expectedCounter := "Percent_Idle_Time_Raw"
for _, metric := range acc.Metrics {
val, ok := metric.Fields[expectedCounter]
require.True(t, ok, "Expected presence of %s field", expectedCounter)
valInt64, ok := val.(int64)
require.Truef(t, ok, "Expected int64, got %T", val)
require.Positivef(t, valInt64, "Value not positive for metric %#v", metric)
}
// Test *Array way
m = WinPerfCounters{
PrintValid: false,
Object: perfObjects,
UseWildcardsExpansion: false,
MaxBufferSize: defaultMaxBufferSize,
Log: testutil.Logger{},
queryCreator: &performanceQueryCreatorImpl{},
}
var acc2 testutil.Accumulator
require.NoError(t, m.Gather(&acc))
time.Sleep(2000 * time.Millisecond)
require.NoError(t, m.Gather(&acc2))
require.Greater(t, len(acc2.Metrics), 1)
for _, metric := range acc2.Metrics {
val, ok := metric.Fields[expectedCounter]
require.True(t, ok, "Expected presence of %s field", expectedCounter)
valInt64, ok := val.(int64)
require.Truef(t, ok, "Expected int64, got %T", val)
require.Positivef(t, valInt64, "Value not positive for metric %#v", metric)
}
}

View file

@ -0,0 +1,33 @@
//go:generate ../../../tools/readme_config_includer/generator
//go:build !windows
package win_perf_counters
import (
_ "embed"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
type WinPerfCounters struct {
Log telegraf.Logger `toml:"-"`
}
func (*WinPerfCounters) SampleConfig() string { return sampleConfig }
func (w *WinPerfCounters) Init() error {
w.Log.Warn("Current platform is not supported")
return nil
}
func (*WinPerfCounters) Gather(telegraf.Accumulator) error { return nil }
func init() {
inputs.Add("win_perf_counters", func() telegraf.Input {
return &WinPerfCounters{}
})
}

File diff suppressed because it is too large Load diff