mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-08 14:06:38 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e85959a4d | ||
|
|
6253bf812d | ||
|
|
6c2380bd04 | ||
|
|
5266f9ebfe | ||
|
|
6c9a5b66e2 | ||
|
|
c4ab8cb8a5 | ||
|
|
7bcaf81d26 | ||
|
|
5f6ba2c6e7 | ||
|
|
75c85fbde1 | ||
|
|
120c244313 | ||
|
|
0e2d78affe | ||
|
|
f0591d85cd | ||
|
|
255b01f610 | ||
|
|
ab7db07836 | ||
|
|
52056a5cd9 | ||
|
|
524fea08c4 | ||
|
|
6b8c895a68 | ||
|
|
eade0da514 | ||
|
|
b07e866b4a | ||
|
|
98618408ce | ||
|
|
56b9f7fd27 | ||
|
|
8d267336c1 |
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -91,5 +91,5 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
|
||||
with:
|
||||
# renovate: github=golangci/golangci-lint
|
||||
version: v2.2.1
|
||||
version: v2.3.1
|
||||
args: "--max-same-issues=0"
|
||||
|
||||
2
.github/workflows/pr-check.yaml
vendored
2
.github/workflows/pr-check.yaml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
- name: check
|
||||
run: |
|
||||
PR_TITLE_PREFIX=$(echo "$PR_TITLE" | cut -d':' -f1)
|
||||
if [[ -d "internal/collector/$PR_TITLE_PREFIX" ]] || [[ -d "internal/$PR_TITLE_PREFIX" ]] || [[ -d "pkg/$PR_TITLE_PREFIX" ]] || [[ -d "$PR_TITLE_PREFIX" ]] || [[ "$PR_TITLE_PREFIX" == "docs" ]] || [[ "$PR_TITLE_PREFIX" == "ci" ]] || [[ "$PR_TITLE_PREFIX" == "revert" ]] || [[ "$PR_TITLE_PREFIX" == "fix" ]] || [[ "$PR_TITLE_PREFIX" == "fix(deps)" ]] || [[ "$PR_TITLE_PREFIX" == "feat" ]] || [[ "$PR_TITLE_PREFIX" == "chore" ]] || [[ "$PR_TITLE_PREFIX" == "chore(docs)" ]] || [[ "$PR_TITLE_PREFIX" == "chore(deps)" ]] || [[ "$PR_TITLE_PREFIX" == "*" ]] || [[ "$PR_TITLE_PREFIX" == "Release"* ]] || [[ "$PR_TITLE_PREFIX" == "Synchronize common files from prometheus/prometheus" ]]; then
|
||||
if [[ -d "internal/collector/$PR_TITLE_PREFIX" ]] || [[ -d "internal/$PR_TITLE_PREFIX" ]] || [[ -d "pkg/$PR_TITLE_PREFIX" ]] || [[ -d "$PR_TITLE_PREFIX" ]] || [[ "$PR_TITLE_PREFIX" == "docs" ]] || [[ "$PR_TITLE_PREFIX" == "ci" ]] || [[ "$PR_TITLE_PREFIX" == "revert" ]] || [[ "$PR_TITLE_PREFIX" == "fix" ]] || [[ "$PR_TITLE_PREFIX" == "fix(deps)" ]] || [[ "$PR_TITLE_PREFIX" == "feat" ]] || [[ "$PR_TITLE_PREFIX" == "chore" ]] || [[ "$PR_TITLE_PREFIX" == "chore(docs)" ]] || [[ "$PR_TITLE_PREFIX" == "chore(deps)" ]] || [[ "$PR_TITLE_PREFIX" == "*" ]] || [[ "$PR_TITLE_PREFIX" == "Release"* ]] || [[ "$PR_TITLE_PREFIX" == "Synchronize common files from prometheus/prometheus" ]] || [[ "$PR_TITLE_PREFIX" == "[0."* ]] || [[ "$PR_TITLE_PREFIX" == "[1."* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -214,7 +214,7 @@ jobs:
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/prometheus-community/windows-exporter
|
||||
|
||||
5
.idea/dictionaries/project.xml
generated
5
.idea/dictionaries/project.xml
generated
@@ -2,8 +2,13 @@
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>containerd</w>
|
||||
<w>endpointstats</w>
|
||||
<w>gochecknoglobals</w>
|
||||
<w>luid</w>
|
||||
<w>setupapi</w>
|
||||
<w>spdx</w>
|
||||
<w>textfile</w>
|
||||
<w>vmcompute</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
75
README.md
75
README.md
@@ -82,15 +82,15 @@ This can be useful for having different Prometheus servers collect specific metr
|
||||
|
||||
windows_exporter accepts flags to configure certain behaviours. The ones configuring the global behaviour of the exporter are listed below, while collector-specific ones are documented in the respective collector documentation above.
|
||||
|
||||
| Flag | Description | Default value |
|
||||
|--------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
|
||||
| `--web.listen-address` | host:port for exporter. | `:9182` |
|
||||
| `--telemetry.path` | URL path for surfacing collected metrics. | `/metrics` |
|
||||
| `--collectors.enabled` | Comma-separated list of collectors to use. Use `[defaults]` as a placeholder which gets expanded containing all the collectors enabled by default. | `[defaults]` |
|
||||
| `--scrape.timeout-margin` | Seconds to subtract from the timeout allowed by the client. Tune to allow for overhead or high loads. | `0.5` |
|
||||
| `--web.config.file` | A [web config][web_config] for setting up TLS and Auth | None |
|
||||
| `--config.file` | [Using a config file](#using-a-configuration-file) from path or URL | None |
|
||||
| `--log.file` | Output file of log messages. One of [stdout, stderr, eventlog, \<path to log file>]<br>**NOTE:** The MSI installer will add a default argument to the installed service setting this to eventlog | stderr |
|
||||
| Flag | Description | Default value |
|
||||
|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
|
||||
| `--web.listen-address` | host:port for exporter. | `:9182` |
|
||||
| `--telemetry.path` | URL path for surfacing collected metrics. | `/metrics` |
|
||||
| `--collectors.enabled` | Comma-separated list of collectors to use. Use `[defaults]` as a placeholder which gets expanded containing all the collectors enabled by default. | `[defaults]` |
|
||||
| `--scrape.timeout-margin` | Seconds to subtract from the timeout allowed by the client. Tune to allow for overhead or high loads. | `0.5` |
|
||||
| `--web.config.file` | A [web config][web_config] for setting up TLS and Auth | None |
|
||||
| `--config.file` | [Using a config file](#using-a-configuration-file) from path | None |
|
||||
| `--log.file` | Output file of log messages. One of [stdout, stderr, eventlog, \<path to log file>]<br>**NOTE:** The MSI installer will add a default argument to the installed service setting this to eventlog | stderr |
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -112,20 +112,22 @@ The configuration file
|
||||
|
||||
The following parameters are available:
|
||||
|
||||
| Name | Description |
|
||||
|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ENABLED_COLLECTORS` | As the `--collectors.enabled` flag, provide a comma-separated list of enabled collectors |
|
||||
| `CONFIG_FILE` | Use the `--config.file` flag to specify a config file. If empty, no config file will be set. The special value `config.yaml` set the path to the config.yaml at install dir | |
|
||||
| `LISTEN_ADDR` | The IP address to bind to. Defaults to an empty string. (any local address) |
|
||||
| `LISTEN_PORT` | The port to bind to. Defaults to `9182`. |
|
||||
| `METRICS_PATH` | The path at which to serve metrics. Defaults to `/metrics` |
|
||||
| `TEXTFILE_DIRS` | Use the `--collector.textfile.directories` flag to specify one or more directories, separated by commas, where the collector should read text files containing metrics |
|
||||
| `REMOTE_ADDR` | Allows setting comma separated remote IP addresses for the Windows Firewall exception (allow list). Defaults to an empty string (any remote address). |
|
||||
| `EXTRA_FLAGS` | Allows passing full CLI flags. Defaults to an empty string. For `--collectors.enabled` and `--config.file`, use the specialized properties `ENABLED_COLLECTORS` and `CONFIG_FILE` |
|
||||
| `ADDLOCAL` | Enables features within the windows_exporter installer. Supported values: `FirewallException` |
|
||||
| `REMOVE` | Disables features within the windows_exporter installer. Supported values: `FirewallException` |
|
||||
| `APPLICATIONFOLDER` | Directory to install windows_exporter. Defaults to `C:\Program Files\windows_exporter` |
|
||||
| Name | Description |
|
||||
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ENABLED_COLLECTORS` | As the `--collectors.enabled` flag, provide a comma-separated list of enabled collectors |
|
||||
| `CONFIG_FILE` | Use the `--config.file` flag to specify a config file. If empty, default config file at install dir will be used. If set, the config file must be exist before the installation is started. | |
|
||||
| `LISTEN_ADDR` | The IP address to bind to. Defaults to an empty string. (any local address) |
|
||||
| `LISTEN_PORT` | The port to bind to. Defaults to `9182`. |
|
||||
| `METRICS_PATH` | The path at which to serve metrics. Defaults to `/metrics` |
|
||||
| `TEXTFILE_DIRS` | Use the `--collector.textfile.directories` flag to specify one or more directories, separated by commas, where the collector should read text files containing metrics |
|
||||
| `REMOTE_ADDR` | Allows setting comma separated remote IP addresses for the Windows Firewall exception (allow list). Defaults to an empty string (any remote address). |
|
||||
| `EXTRA_FLAGS` | Allows passing full CLI flags. Defaults to an empty string. For `--collectors.enabled` and `--config.file`, use the specialized properties `ENABLED_COLLECTORS` and `CONFIG_FILE` |
|
||||
| `ADDLOCAL` | Enables features within the windows_exporter installer. Supported values: `FirewallException` |
|
||||
| `REMOVE` | Disables features within the windows_exporter installer. Supported values: `FirewallException` |
|
||||
| `APPLICATIONFOLDER` | Directory to install windows_exporter. Defaults to `C:\Program Files\windows_exporter` |
|
||||
|
||||
> [!NOTE]
|
||||
> The installer properties are always preferred over the values defined in the config file. If you prefer to configure via the config file, avoid using any of the properties listed above.
|
||||
|
||||
Parameters are sent to the installer via `msiexec`.
|
||||
On PowerShell, the `--%` should be passed before defining properties.
|
||||
@@ -182,9 +184,6 @@ The windows_exporter can be run as a Docker container. The Docker image is avail
|
||||
|
||||
The Docker image is tagged with the version of the exporter. The `latest` tag is also available and points to the latest release.
|
||||
|
||||
Additionally, a flavor `hostprocess` with `-hostprocess` as suffix is based on the https://github.com/microsoft/windows-host-process-containers-base-image
|
||||
which is designed to run as a Windows host process container. The size of that images is smaller than the default one.
|
||||
|
||||
## Kubernetes Implementation
|
||||
|
||||
See detailed steps to install on Windows Kubernetes [here](./kubernetes/kubernetes.md).
|
||||
@@ -195,16 +194,6 @@ See detailed steps to install on Windows Kubernetes [here](./kubernetes/kubernet
|
||||
|
||||
There are known compatibility issues with Windows Server 2012 R2 and earlier versions.
|
||||
|
||||
## Usage
|
||||
|
||||
go get -u github.com/prometheus/promu
|
||||
go get -u github.com/prometheus-community/windows_exporter
|
||||
cd $env:GOPATH/src/github.com/prometheus-community/windows_exporter
|
||||
promu build -v
|
||||
.\windows_exporter.exe
|
||||
|
||||
The prometheus metrics will be exposed on [localhost:9182](http://localhost:9182)
|
||||
|
||||
### HTTP Endpoints
|
||||
|
||||
windows_exporter provides the following HTTP endpoints:
|
||||
@@ -213,18 +202,6 @@ windows_exporter provides the following HTTP endpoints:
|
||||
* `/health`: Returns 200 OK when the exporter is running.
|
||||
* `/debug/pprof/`: Exposes the [pprof](https://golang.org/pkg/net/http/pprof/) endpoints. Only, if `--debug.enabled` is set.
|
||||
|
||||
## Examples
|
||||
|
||||
### Enable only service collector and specify a custom query
|
||||
|
||||
.\windows_exporter.exe --collectors.enabled "service" --collector.service.include="windows_exporter"
|
||||
|
||||
### Enable only process collector and specify a custom query
|
||||
|
||||
.\windows_exporter.exe --collectors.enabled "process" --collector.process.include="firefox.+"
|
||||
|
||||
When there are multiple processes with the same name, WMI represents those after the first instance as `process-name#index`. So to get them all, rather than just the first one, the [regular expression](https://en.wikipedia.org/wiki/Regular_expression) must use `.+`. See [process](docs/collector.process.md) for more information.
|
||||
|
||||
### Using [defaults] with `--collectors.enabled` argument
|
||||
|
||||
Using `[defaults]` with `--collectors.enabled` argument which gets expanded with all default collectors.
|
||||
@@ -237,10 +214,6 @@ This enables the additional process and container collectors on top of the defau
|
||||
|
||||
YAML configuration files can be specified with the `--config.file` flag. e.g. `.\windows_exporter.exe --config.file=config.yml`. If you are using the absolute path, make sure to quote the path, e.g. `.\windows_exporter.exe --config.file="C:\Program Files\windows_exporter\config.yml"`
|
||||
|
||||
It is also possible to load the configuration from a URL. e.g. `.\windows_exporter.exe --config.file="https://example.com/config.yml"`
|
||||
|
||||
If you need to skip TLS verification, you can use the `--config.file.insecure-skip-verify` flag. e.g. `.\windows_exporter.exe --config.file="https://example.com/config.yml" --config.file.insecure-skip-verify`
|
||||
|
||||
```yaml
|
||||
collectors:
|
||||
enabled: cpu,net,service
|
||||
|
||||
@@ -171,8 +171,10 @@ func waitUntilListening(tb testing.TB, network, address string) error {
|
||||
err error
|
||||
)
|
||||
|
||||
dialer := &net.Dialer{Timeout: 100 * time.Millisecond}
|
||||
|
||||
for range 20 {
|
||||
conn, err = net.DialTimeout(network, address, 100*time.Millisecond)
|
||||
conn, err = dialer.DialContext(tb.Context(), network, address)
|
||||
if err == nil {
|
||||
_ = conn.Close()
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
collectors:
|
||||
enabled: cpu,cpu_info,exchange,iis,logical_disk,memory,net,os,performancecounter,process,remote_fx,service,system,tcp,time,terminal_services,textfile
|
||||
collector:
|
||||
textfile:
|
||||
directories:
|
||||
- 'C:\MyDir1'
|
||||
- 'C:\MyDir2'
|
||||
service:
|
||||
include: "windows_exporter"
|
||||
performancecounter:
|
||||
|
||||
@@ -20,29 +20,32 @@ These metrics are available on supported versions of Windows with compatible GPU
|
||||
|
||||
### Adapter-level Metrics
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------|-------------------------------------------------------------------------|-------|--------------------------------------------------------------------------------------|
|
||||
| `windows_gpu_adapter_memory_committed_bytes` | Total committed GPU memory in bytes per physical GPU | gauge | `phys` |
|
||||
| `windows_gpu_adapter_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per physical GPU | gauge | `phys` |
|
||||
| `windows_gpu_adapter_memory_shared_bytes` | Shared GPU memory usage in bytes per physical GPU | gauge | `phys` |
|
||||
| `windows_gpu_info` | A metric with a constant '1' value labeled with gpu device information. | gauge | `phys`, `physical_device_object_name`, `hardware_id`, `friendly_name`, `description` |
|
||||
| `windows_gpu_local_adapter_memory_bytes` | Local adapter memory usage in bytes per physical GPU | gauge | `phys` |
|
||||
| `windows_gpu_non_local_adapter_memory_bytes` | Non-local adapter memory usage in bytes per physical GPU | gauge | `phys` |
|
||||
| Name | Description | Type | Labels |
|
||||
|--------------------------------------------------|------------------------------------------------------------------------------------|-------|---------------|
|
||||
| `windows_gpu_info` | A metric with a constant '1' value labeled with gpu device information. | gauge | `luid`,`name`,`bus_number`,`phys`,`function_number` |
|
||||
| `windows_gpu_dedicated_system_memory_size_bytes` | The size, in bytes, of memory that is dedicated from system memory. | gauge | `luid` |
|
||||
| `windows_gpu_dedicated_video_memory_size_bytes` | The size, in bytes, of memory that is dedicated from video memory. | gauge | `luid` |
|
||||
| `windows_gpu_shared_system_memory_size_bytes` | The size, in bytes, of memory from system memory that can be shared by many users. | gauge | `luid` |
|
||||
| `windows_gpu_adapter_memory_committed_bytes` | Total committed GPU memory in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
| `windows_gpu_adapter_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
| `windows_gpu_adapter_memory_shared_bytes` | Shared GPU memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
| `windows_gpu_local_adapter_memory_bytes` | Local adapter memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
| `windows_gpu_non_local_adapter_memory_bytes` | Non-local adapter memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
|
||||
### Per-process Metrics
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------|-------------------------------------------------------------------------|---------|--------------------------------------------------------------------------------------|
|
||||
| `windows_gpu_engine_time_seconds` | Total running time of the GPU engine in seconds | counter | `phys`, `eng`, `engtype`, `process_id` |
|
||||
| `windows_gpu_process_memory_committed_bytes` | Total committed GPU memory in bytes per process | gauge | `phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per process | gauge | `phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_local_bytes` | Local GPU memory usage in bytes per process | gauge | `phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_non_local_bytes` | Non-local GPU memory usage in bytes per process | gauge | `phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_shared_bytes` | Shared GPU memory usage in bytes per process | gauge | `phys`,`process_id` |
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------|-------------------------------------------------|---------|-----------------------------------------------|
|
||||
| `windows_gpu_engine_time_seconds` | Total running time of the GPU engine in seconds | counter | `luid`,`phys`, `eng`, `engtype`, `process_id` |
|
||||
| `windows_gpu_process_memory_committed_bytes` | Total committed GPU memory in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_local_bytes` | Local GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_non_local_bytes` | Non-local GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_shared_bytes` | Shared GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
|
||||
## Metric Labels
|
||||
|
||||
* `phys`: Physical GPU index (e.g., "0")
|
||||
* `luid`,`phys`: Physical GPU index (e.g., "0")
|
||||
* `eng`: GPU engine index (e.g., "0", "1", ...)
|
||||
* `engtype`: GPU engine type (e.g., "3D", "Copy", "VideoDecode", etc.)
|
||||
* `process_id`: Process ID
|
||||
|
||||
@@ -42,6 +42,11 @@ Disabled by default, and can be enabled with `--collector.process.iis`. NOTE: Ju
|
||||
Version of the process collector to use. 1 for Process V1, 2 for Process V2.
|
||||
Defaults to 0 which will use the latest version available.
|
||||
|
||||
### `--collector.process.cmdline`
|
||||
|
||||
Enables the `cmdline` label for the process metrics.
|
||||
This label contains the command line used to start the process.
|
||||
Enabled by default, and can be turned off with `--no-collector.process.cmdline`.
|
||||
|
||||
### Example
|
||||
To match all firefox processes: `--collector.process.include="firefox.*"`.
|
||||
|
||||
@@ -13,6 +13,5 @@ scrape:
|
||||
timeout-margin: 0.5
|
||||
telemetry:
|
||||
path: /metrics
|
||||
max-requests: 5
|
||||
web:
|
||||
listen-address: ":9182"
|
||||
|
||||
14
go.mod
14
go.mod
@@ -4,15 +4,15 @@ go 1.24
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1
|
||||
github.com/dimchansky/utfbom v1.1.1
|
||||
github.com/go-ole/go-ole v1.3.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/prometheus/common v0.65.0
|
||||
github.com/prometheus/exporter-toolkit v0.14.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.org/x/sys v0.34.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -30,11 +30,11 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
30
go.sum
30
go.sum
@@ -4,8 +4,8 @@ github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vS
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
@@ -41,8 +41,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
@@ -65,19 +65,21 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
<DirectoryRef Id="APPLICATIONFOLDER">
|
||||
<Component Transitive="yes">
|
||||
<File Id="windows_exporter.exe" Name="windows_exporter.exe" Source="Work\windows_exporter.exe" KeyPath="yes" Vital="yes" Checksum="yes"/>
|
||||
|
||||
<!-- The "Name" field must match the argument to eventlog.Open() -->
|
||||
<util:EventSource Log="Application" Name="windows_exporter"
|
||||
EventMessageFile="%SystemRoot%\System32\EventCreate.exe"
|
||||
SupportsErrors="yes"
|
||||
SupportsInformationals="yes"
|
||||
SupportsWarnings="yes"/>
|
||||
|
||||
<ServiceInstall
|
||||
Id="InstallExporterService"
|
||||
Name="windows_exporter"
|
||||
@@ -45,13 +53,8 @@
|
||||
/>
|
||||
<ServiceDependency Id="wmiApSrv" />
|
||||
</ServiceInstall>
|
||||
<ServiceControl Id="ServiceStateControl" Name="windows_exporter" Remove="uninstall" Start="install" Stop="both"/>
|
||||
<!-- The "Name" field must match the argument to eventlog.Open() -->
|
||||
<util:EventSource Log="Application" Name="windows_exporter"
|
||||
EventMessageFile="%SystemRoot%\System32\EventCreate.exe"
|
||||
SupportsErrors="yes"
|
||||
SupportsInformationals="yes"
|
||||
SupportsWarnings="yes"/>
|
||||
<ServiceControl Id="StartService" Name="windows_exporter" Start="install" Wait="no" />
|
||||
<ServiceControl Id="StopService" Name="windows_exporter" Remove="uninstall" Stop="both" Wait="yes" />
|
||||
</Component>
|
||||
<Component Id="CreateTextfileDirectory" Directory="textfile_inputs" Guid="d03ef58a-9cbf-4165-ad39-d143e9b27e14">
|
||||
<CreateFolder />
|
||||
|
||||
@@ -45,6 +45,40 @@
|
||||
Property="OLDERVERSIONBEINGUPGRADED" />
|
||||
</Upgrade>
|
||||
|
||||
<Media Id="1" Cabinet="windows_exporter.cab" EmbedCab="yes" />
|
||||
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." AllowSameVersionUpgrades="yes" />
|
||||
|
||||
<Property Id="ENABLED_COLLECTORS" Secure="yes" />
|
||||
<SetProperty Id="CollectorsFlag" After="InstallFiles" Sequence="execute" Value="--collectors.enabled [ENABLED_COLLECTORS]" Condition="ENABLED_COLLECTORS" />
|
||||
|
||||
<Property Id="EXTRA_FLAGS" Secure="yes" />
|
||||
<SetProperty Id="ExtraFlags" After="InstallFiles" Sequence="execute" Value="[EXTRA_FLAGS]" Condition="EXTRA_FLAGS" />
|
||||
|
||||
<Property Id="CONFIG_FILE" Secure="yes" Value="config.yaml" />
|
||||
<SetProperty Id="ConfigFile_NonDefault" After="InstallFiles" Sequence="execute" Value="[CONFIG_FILE]" Condition="CONFIG_FILE AND CONFIG_FILE<>"config.yaml"" />
|
||||
<SetProperty Id="ConfigFile_Default" After="InstallFiles" Sequence="execute" Value="[APPLICATIONFOLDER]config.yaml" Condition="CONFIG_FILE="config.yaml"" />
|
||||
<SetProperty Id="ConfigFileFlag" After="InstallFiles" Sequence="execute" Value="--config.file="[ConfigFile_NonDefault][ConfigFile_Default]"" Condition="ConfigFile_NonDefault OR ConfigFile_Default" />
|
||||
|
||||
<Property Id="LISTEN_PORT" Secure="yes" Value="9182" />
|
||||
<SetProperty Id="ListenFlag" After="InstallFiles" Sequence="execute" Value="--web.listen-address="[LISTEN_ADDR]:[LISTEN_PORT]"" Condition="LISTEN_ADDR<>"" OR LISTEN_PORT<>9182" />
|
||||
|
||||
<Property Id="METRICS_PATH" Secure="yes" />
|
||||
<SetProperty Id="MetricsPathFlag" After="InstallFiles" Sequence="execute" Value="--telemetry.path="[METRICS_PATH]"" Condition="METRICS_PATH" />
|
||||
|
||||
<Property Id="REMOTE_ADDR" Secure="yes" />
|
||||
<SetProperty Id="RemoteAddressFlag" After="InstallFiles" Sequence="execute" Value="[REMOTE_ADDR]" Condition="REMOTE_ADDR" />
|
||||
|
||||
<Property Id="TEXTFILE_DIRS" Secure="yes" />
|
||||
<SetProperty Id="TextfileDirsFlag" After="InstallFiles" Sequence="execute" Value="--collector.textfile.directories="[TEXTFILE_DIRS]"" Condition="TEXTFILE_DIRS" />
|
||||
|
||||
<Property Id="ARPHELPLINK" Value="https://github.com/prometheus-community/windows_exporter/issues" />
|
||||
<Property Id="ARPSIZE" Value="9000" />
|
||||
<Property Id="ARPURLINFOABOUT" Value="https://github.com/prometheus-community/windows_exporter" />
|
||||
<!--<Property Id="ARPNOMODIFY" Value="0" />-->
|
||||
<!--<Property Id="ARPNOREPAIR" Value="1" />-->
|
||||
<Property Id="START_MENU_FOLDER" Value="0" />
|
||||
<Property Id="NOSTART" Value="0" />
|
||||
|
||||
<CustomAction Id="CheckExtraFlags"
|
||||
Error="The parameter '--config.file' must not be included in EXTRA_FLAGS. Use CONFIG_FILE instead. Please remove it and try again." />
|
||||
|
||||
@@ -100,47 +134,13 @@
|
||||
Condition="Installed AND (NOT REMOVE) AND (NOT UPGRADINGPRODUCTCODE)"/>
|
||||
<Custom Action="set_reinstall_all_property" Before="set_reinstallmode_property" Condition="MAINTENANCE"/>
|
||||
<Custom Action="set_reinstallmode_property" Before="LaunchConditions" Condition="MAINTENANCE"/>
|
||||
<Custom Action="CreateConfigFile" Before="InstallServices" Condition="ConfigFile_NonDefault OR ConfigFile_Default" />
|
||||
<Custom Action="CreateConfigFile" Before="InstallServices" Condition="ConfigFile_Default" />
|
||||
<Custom Action="KillProcess" Before="RemoveFiles" />
|
||||
|
||||
<Custom Action="CheckExtraFlags" Before="InstallInitialize"
|
||||
Condition="EXTRA_FLAGS AND (EXTRA_FLAGS><"--config.file")" />
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<Media Id="1" Cabinet="windows_exporter.cab" EmbedCab="yes" />
|
||||
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." AllowSameVersionUpgrades="yes" />
|
||||
|
||||
<Property Id="ENABLED_COLLECTORS" Secure="yes" />
|
||||
<SetProperty Id="CollectorsFlag" After="InstallFiles" Sequence="execute" Value="--collectors.enabled [ENABLED_COLLECTORS]" Condition="ENABLED_COLLECTORS" />
|
||||
|
||||
<Property Id="EXTRA_FLAGS" Secure="yes" />
|
||||
<SetProperty Id="ExtraFlags" After="InstallFiles" Sequence="execute" Value="[EXTRA_FLAGS]" Condition="EXTRA_FLAGS" />
|
||||
|
||||
<Property Id="CONFIG_FILE" Secure="yes" Value="config.yaml" />
|
||||
<SetProperty Id="ConfigFile_NonDefault" After="InstallFiles" Sequence="execute" Value="[CONFIG_FILE]" Condition="CONFIG_FILE AND CONFIG_FILE<>"config.yaml"" />
|
||||
<SetProperty Id="ConfigFile_Default" After="InstallFiles" Sequence="execute" Value="[APPLICATIONFOLDER]config.yaml" Condition="CONFIG_FILE="config.yaml"" />
|
||||
<SetProperty Id="ConfigFileFlag" After="InstallFiles" Sequence="execute" Value="--config.file="[ConfigFile_NonDefault][ConfigFile_Default]"" Condition="ConfigFile_NonDefault OR ConfigFile_Default" />
|
||||
|
||||
<Property Id="LISTEN_PORT" Secure="yes" Value="9182" />
|
||||
<SetProperty Id="ListenFlag" After="InstallFiles" Sequence="execute" Value="--web.listen-address [LISTEN_ADDR]:[LISTEN_PORT]" Condition="LISTEN_ADDR<>"" OR LISTEN_PORT<>9182" />
|
||||
|
||||
<Property Id="METRICS_PATH" Secure="yes" />
|
||||
<SetProperty Id="MetricsPathFlag" After="InstallFiles" Sequence="execute" Value="--telemetry.path [METRICS_PATH]" Condition="METRICS_PATH" />
|
||||
|
||||
<Property Id="REMOTE_ADDR" Secure="yes" />
|
||||
<SetProperty Id="RemoteAddressFlag" After="InstallFiles" Sequence="execute" Value="[REMOTE_ADDR]" Condition="REMOTE_ADDR" />
|
||||
|
||||
<Property Id="TEXTFILE_DIRS" Secure="yes" />
|
||||
<SetProperty Id="TextfileDirsFlag" After="InstallFiles" Sequence="execute" Value="--collector.textfile.directories [TEXTFILE_DIRS]" Condition="TEXTFILE_DIRS" />
|
||||
|
||||
<Property Id="ARPHELPLINK" Value="https://github.com/prometheus-community/windows_exporter/issues" />
|
||||
<Property Id="ARPSIZE" Value="9000" />
|
||||
<Property Id="ARPURLINFOABOUT" Value="https://github.com/prometheus-community/windows_exporter" />
|
||||
<!--<Property Id="ARPNOMODIFY" Value="0" />-->
|
||||
<!--<Property Id="ARPNOREPAIR" Value="1" />-->
|
||||
<Property Id="START_MENU_FOLDER" Value="0" />
|
||||
<Property Id="NOSTART" Value="0" />
|
||||
|
||||
<Feature
|
||||
Id="DefaultFeature"
|
||||
Level="1"
|
||||
|
||||
@@ -29,10 +29,8 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcn"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/iphlpapi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/kernel32"
|
||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||
@@ -523,7 +521,7 @@ func (c *Collector) collectHCSContainer(ch chan<- prometheus.Metric, containerDe
|
||||
|
||||
// collectNetworkMetrics collects network metrics for containers.
|
||||
func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
||||
endpoints, err := hcn.EnumerateEndpoints()
|
||||
endpoints, err := hcn.ListEndpoints()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in fetching HCN endpoints: %w", err)
|
||||
}
|
||||
@@ -533,57 +531,24 @@ func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
properties, err := hcn.GetEndpointProperties(endpoint)
|
||||
if len(endpoint.SharedContainers) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
endpointStats, err := hcn.GetHNSEndpointStats(endpoint.ID)
|
||||
if err != nil {
|
||||
c.logger.Warn("Failed to collect properties for interface "+endpoint.String(),
|
||||
c.logger.Warn("Failed to collect network stats for interface "+endpoint.ID,
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if len(properties.SharedContainers) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var nicGUID *ole.GUID
|
||||
|
||||
for _, allocator := range properties.Resources.Allocators {
|
||||
if allocator.AdapterNetCfgInstanceId != nil {
|
||||
nicGUID = allocator.AdapterNetCfgInstanceId
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if nicGUID == nil {
|
||||
c.logger.Warn("Failed to get nic GUID for endpoint " + endpoint.String())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
luid, err := iphlpapi.ConvertInterfaceGUIDToLUID(*nicGUID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in converting interface GUID to LUID: %w", err)
|
||||
}
|
||||
|
||||
var endpointStats iphlpapi.MIB_IF_ROW2
|
||||
|
||||
endpointStats.InterfaceLuid = luid
|
||||
|
||||
if err := iphlpapi.GetIfEntry2Ex(&endpointStats); err != nil {
|
||||
c.logger.Warn("Failed to get interface entry for endpoint "+endpoint.String(),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for _, containerId := range properties.SharedContainers {
|
||||
for _, containerId := range endpoint.SharedContainers {
|
||||
containerInfo, ok := c.annotationsCacheHCS[containerId]
|
||||
|
||||
if !ok {
|
||||
c.logger.Debug("Unknown container " + containerId + " for endpoint " + endpoint.String())
|
||||
c.logger.Debug("Unknown container " + containerId + " for endpoint " + endpoint.ID)
|
||||
|
||||
continue
|
||||
}
|
||||
@@ -593,47 +558,47 @@ func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
||||
continue
|
||||
}
|
||||
|
||||
endpointId := strings.ToUpper(endpoint.String())
|
||||
endpointId := strings.ToUpper(endpoint.ID)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.bytesReceived,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.InOctets),
|
||||
float64(endpointStats.BytesReceived),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.bytesSent,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.OutOctets),
|
||||
float64(endpointStats.BytesSent),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.packetsReceived,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.InUcastPkts+endpointStats.InNUcastPkts),
|
||||
float64(endpointStats.PacketsReceived),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.packetsSent,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.OutUcastPkts+endpointStats.OutNUcastPkts),
|
||||
float64(endpointStats.PacketsSent),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.droppedPacketsIncoming,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.InDiscards+endpointStats.InErrors),
|
||||
float64(endpointStats.DroppedPacketsIncoming),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.droppedPacketsOutgoing,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.OutDiscards+endpointStats.OutErrors),
|
||||
float64(endpointStats.DroppedPacketsOutgoing),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package filetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -73,19 +74,10 @@ func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
}
|
||||
c.config.FilePatterns = make([]string, 0)
|
||||
|
||||
var filePatterns string
|
||||
|
||||
app.Flag(
|
||||
"collector.filetime.file-patterns",
|
||||
"Comma-separated list of file patterns. Each pattern is a glob pattern that can contain `*`, `?`, and `**` (recursive). See https://github.com/bmatcuk/doublestar#patterns",
|
||||
).Default(strings.Join(ConfigDefaults.FilePatterns, ",")).StringVar(&filePatterns)
|
||||
|
||||
app.Action(func(*kingpin.ParseContext) error {
|
||||
// doublestar.Glob() requires forward slashes
|
||||
c.config.FilePatterns = strings.Split(filepath.ToSlash(filePatterns), ",")
|
||||
|
||||
return nil
|
||||
})
|
||||
).Default(strings.Join(ConfigDefaults.FilePatterns, ",")).StringsVar(&c.config.FilePatterns)
|
||||
|
||||
return c
|
||||
}
|
||||
@@ -148,16 +140,11 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||
}
|
||||
|
||||
func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern string) error {
|
||||
basePath, pattern := doublestar.SplitPattern(filePattern)
|
||||
basePath, pattern := doublestar.SplitPattern(filepath.ToSlash(filePattern))
|
||||
basePathFS := os.DirFS(basePath)
|
||||
|
||||
matches, err := doublestar.Glob(basePathFS, pattern, doublestar.WithFilesOnly())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to glob: %w", err)
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
filePath := filepath.Join(basePath, match)
|
||||
err := doublestar.GlobWalk(basePathFS, pattern, func(path string, d fs.DirEntry) error {
|
||||
filePath := filepath.Join(basePath, path)
|
||||
|
||||
fileInfo, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
@@ -166,7 +153,7 @@ func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -175,6 +162,11 @@ func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern
|
||||
float64(fileInfo.ModTime().UTC().UnixMicro())/1e6,
|
||||
filePath,
|
||||
)
|
||||
|
||||
return nil
|
||||
}, doublestar.WithFilesOnly(), doublestar.WithCaseInsensitive())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to glob: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/setupapi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/gdi32"
|
||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||
"github.com/prometheus-community/windows_exporter/internal/types"
|
||||
@@ -41,6 +41,8 @@ var ConfigDefaults = Config{}
|
||||
type Collector struct {
|
||||
config Config
|
||||
|
||||
gpuDeviceCache map[string]gdi32.GPUDevice
|
||||
|
||||
// GPU Engine
|
||||
gpuEnginePerfDataCollector *pdh.Collector
|
||||
gpuEnginePerfDataObject []gpuEnginePerfDataCounterValues
|
||||
@@ -48,6 +50,10 @@ type Collector struct {
|
||||
gpuInfo *prometheus.Desc
|
||||
gpuEngineRunningTime *prometheus.Desc
|
||||
|
||||
gpuSharedSystemMemorySize *prometheus.Desc
|
||||
gpuDedicatedSystemMemorySize *prometheus.Desc
|
||||
gpuDedicatedVideoMemorySize *prometheus.Desc
|
||||
|
||||
// GPU Adapter Memory
|
||||
gpuAdapterMemoryPerfDataCollector *pdh.Collector
|
||||
gpuAdapterMemoryPerfDataObject []gpuAdapterMemoryPerfDataCounterValues
|
||||
@@ -115,78 +121,97 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
||||
c.gpuInfo = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "info"),
|
||||
"A metric with a constant '1' value labeled with gpu device information.",
|
||||
[]string{"phys", "physical_device_object_name", "hardware_id", "friendly_name", "description"},
|
||||
[]string{"luid", "name", "bus_number", "phys", "function_number"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuSharedSystemMemorySize = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "shared_system_memory_size_bytes"),
|
||||
"The size, in bytes, of memory from system memory that can be shared by many users.",
|
||||
[]string{"luid"},
|
||||
nil,
|
||||
)
|
||||
c.gpuDedicatedSystemMemorySize = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "dedicated_system_memory_size_bytes"),
|
||||
"The size, in bytes, of memory that is dedicated from system memory.",
|
||||
[]string{"luid"},
|
||||
nil,
|
||||
)
|
||||
c.gpuDedicatedVideoMemorySize = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "dedicated_video_memory_size_bytes"),
|
||||
"The size, in bytes, of memory that is dedicated from video memory.",
|
||||
[]string{"luid"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuEngineRunningTime = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "engine_time_seconds"),
|
||||
"Total running time of the GPU in seconds.",
|
||||
[]string{"process_id", "phys", "eng", "engtype"},
|
||||
[]string{"process_id", "luid", "phys", "eng", "engtype"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuAdapterMemoryDedicatedUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_dedicated_bytes"),
|
||||
"Dedicated GPU memory usage in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuAdapterMemorySharedUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_shared_bytes"),
|
||||
"Shared GPU memory usage in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuAdapterMemoryTotalCommitted = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_committed_bytes"),
|
||||
"Total committed GPU memory in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuLocalAdapterMemoryUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "local_adapter_memory_bytes"),
|
||||
"Local adapter memory usage in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuNonLocalAdapterMemoryUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "non_local_adapter_memory_bytes"),
|
||||
"Non-local adapter memory usage in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuProcessMemoryDedicatedUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_dedicated_bytes"),
|
||||
"Dedicated process memory usage in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuProcessMemoryLocalUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_local_bytes"),
|
||||
"Local process memory usage in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuProcessMemoryNonLocalUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_non_local_bytes"),
|
||||
"Non-local process memory usage in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuProcessMemorySharedUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_shared_bytes"),
|
||||
"Shared process memory usage in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuProcessMemoryTotalCommitted = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_committed_bytes"),
|
||||
"Total committed process memory in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
|
||||
@@ -217,15 +242,31 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
||||
errs = append(errs, fmt.Errorf("failed to create GPU Process Memory perf data collector: %w", err))
|
||||
}
|
||||
|
||||
gpus, err := gdi32.GetGPUDevices()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to get GPU devices: %w", err))
|
||||
}
|
||||
|
||||
for _, gpu := range gpus {
|
||||
if gpu.AdapterString == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if c.gpuDeviceCache == nil {
|
||||
c.gpuDeviceCache = make(map[string]gdi32.GPUDevice)
|
||||
}
|
||||
|
||||
luidKey := fmt.Sprintf("0x%08X_0x%08X", gpu.LUID.HighPart, gpu.LUID.LowPart)
|
||||
c.gpuDeviceCache[luidKey] = gpu
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||
errs := make([]error, 0)
|
||||
|
||||
if err := c.collectGpuInfo(ch); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
c.collectGpuInfo(ch)
|
||||
|
||||
if err := c.collectGpuEngineMetrics(ch); err != nil {
|
||||
errs = append(errs, err)
|
||||
@@ -250,26 +291,40 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (c *Collector) collectGpuInfo(ch chan<- prometheus.Metric) error {
|
||||
gpus, err := setupapi.GetGPUDevices()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get GPU devices: %w", err)
|
||||
}
|
||||
|
||||
for i, gpu := range gpus {
|
||||
func (c *Collector) collectGpuInfo(ch chan<- prometheus.Metric) {
|
||||
for luid, gpu := range c.gpuDeviceCache {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuInfo,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
strconv.Itoa(i),
|
||||
gpu.PhysicalDeviceObjectName,
|
||||
gpu.HardwareID,
|
||||
gpu.FriendlyName,
|
||||
gpu.DeviceDesc,
|
||||
luid,
|
||||
gpu.AdapterString,
|
||||
strconv.FormatInt(int64(gpu.BusNumber), 10),
|
||||
strconv.FormatInt(int64(gpu.DeviceNumber), 10),
|
||||
strconv.FormatInt(int64(gpu.FunctionNumber), 10),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuSharedSystemMemorySize,
|
||||
prometheus.GaugeValue,
|
||||
float64(gpu.SharedSystemMemorySize),
|
||||
luid,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuDedicatedSystemMemorySize,
|
||||
prometheus.GaugeValue,
|
||||
float64(gpu.DedicatedSystemMemorySize),
|
||||
luid,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuDedicatedVideoMemorySize,
|
||||
prometheus.GaugeValue,
|
||||
float64(gpu.DedicatedVideoMemorySize),
|
||||
luid,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectGpuEngineMetrics(ch chan<- prometheus.Metric) error {
|
||||
@@ -283,9 +338,14 @@ func (c *Collector) collectGpuEngineMetrics(ch chan<- prometheus.Metric) error {
|
||||
for _, data := range c.gpuEnginePerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhysEngEngType{
|
||||
Pid: instance.Pid,
|
||||
Phys: instance.Phys,
|
||||
Luid: instance.Luid,
|
||||
Eng: instance.Eng,
|
||||
Engtype: instance.Engtype,
|
||||
}
|
||||
@@ -297,7 +357,7 @@ func (c *Collector) collectGpuEngineMetrics(ch chan<- prometheus.Metric) error {
|
||||
c.gpuEngineRunningTime,
|
||||
prometheus.CounterValue,
|
||||
runningTime,
|
||||
key.Pid, key.Phys, key.Eng, key.Engtype,
|
||||
key.Pid, key.Luid, key.Phys, key.Eng, key.Engtype,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -317,8 +377,13 @@ func (c *Collector) collectGpuAdapterMemoryMetrics(ch chan<- prometheus.Metric)
|
||||
for _, data := range c.gpuAdapterMemoryPerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhysEngEngType{
|
||||
Pid: instance.Pid,
|
||||
Luid: instance.Luid,
|
||||
Phys: instance.Phys,
|
||||
Eng: instance.Eng,
|
||||
Engtype: instance.Engtype,
|
||||
@@ -333,21 +398,21 @@ func (c *Collector) collectGpuAdapterMemoryMetrics(ch chan<- prometheus.Metric)
|
||||
c.gpuAdapterMemoryDedicatedUsage,
|
||||
prometheus.GaugeValue,
|
||||
dedicatedUsage,
|
||||
key.Phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuAdapterMemorySharedUsage,
|
||||
prometheus.GaugeValue,
|
||||
sharedUsageMap[key],
|
||||
key.Phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuAdapterMemoryTotalCommitted,
|
||||
prometheus.GaugeValue,
|
||||
totalCommittedMap[key],
|
||||
key.Phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -360,20 +425,29 @@ func (c *Collector) collectGpuLocalAdapterMemoryMetrics(ch chan<- prometheus.Met
|
||||
return fmt.Errorf("failed to collect GPU Local Adapter Memory perf data: %w", err)
|
||||
}
|
||||
|
||||
localAdapterMemoryMap := make(map[string]float64)
|
||||
localAdapterMemoryMap := make(map[PidPhysEngEngType]float64)
|
||||
|
||||
for _, data := range c.gpuLocalAdapterMemoryPerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
localAdapterMemoryMap[instance.Phys] += data.LocalUsage
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhysEngEngType{
|
||||
Luid: instance.Luid,
|
||||
Phys: instance.Phys,
|
||||
}
|
||||
|
||||
localAdapterMemoryMap[key] += data.LocalUsage
|
||||
}
|
||||
|
||||
for phys, localUsage := range localAdapterMemoryMap {
|
||||
for key, localUsage := range localAdapterMemoryMap {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuLocalAdapterMemoryUsage,
|
||||
prometheus.GaugeValue,
|
||||
localUsage,
|
||||
phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -386,20 +460,28 @@ func (c *Collector) collectGpuNonLocalAdapterMemoryMetrics(ch chan<- prometheus.
|
||||
return fmt.Errorf("failed to collect GPU Non Local Adapter Memory perf data: %w", err)
|
||||
}
|
||||
|
||||
nonLocalAdapterMemoryMap := make(map[string]float64)
|
||||
nonLocalAdapterMemoryMap := make(map[PidPhysEngEngType]float64)
|
||||
|
||||
for _, data := range c.gpuNonLocalAdapterMemoryPerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
nonLocalAdapterMemoryMap[instance.Phys] += data.NonLocalUsage
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhysEngEngType{
|
||||
Luid: instance.Luid,
|
||||
Phys: instance.Phys,
|
||||
}
|
||||
nonLocalAdapterMemoryMap[key] += data.NonLocalUsage
|
||||
}
|
||||
|
||||
for phys, nonLocalUsage := range nonLocalAdapterMemoryMap {
|
||||
for key, nonLocalUsage := range nonLocalAdapterMemoryMap {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuNonLocalAdapterMemoryUsage,
|
||||
prometheus.GaugeValue,
|
||||
nonLocalUsage,
|
||||
phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -421,8 +503,13 @@ func (c *Collector) collectGpuProcessMemoryMetrics(ch chan<- prometheus.Metric)
|
||||
for _, data := range c.gpuProcessMemoryPerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhys{
|
||||
Pid: instance.Pid,
|
||||
Luid: instance.Luid,
|
||||
Phys: instance.Phys,
|
||||
}
|
||||
processDedicatedUsageMap[key] += data.DedicatedUsage
|
||||
@@ -437,35 +524,35 @@ func (c *Collector) collectGpuProcessMemoryMetrics(ch chan<- prometheus.Metric)
|
||||
c.gpuProcessMemoryDedicatedUsage,
|
||||
prometheus.GaugeValue,
|
||||
dedicatedUsage,
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuProcessMemoryLocalUsage,
|
||||
prometheus.GaugeValue,
|
||||
processLocalUsageMap[key],
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuProcessMemoryNonLocalUsage,
|
||||
prometheus.GaugeValue,
|
||||
processNonLocalUsageMap[key],
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuProcessMemorySharedUsage,
|
||||
prometheus.GaugeValue,
|
||||
processSharedUsageMap[key],
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuProcessMemoryTotalCommitted,
|
||||
prometheus.GaugeValue,
|
||||
processTotalCommittedMap[key],
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
package gpu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Instance struct {
|
||||
Pid string
|
||||
Luid [2]string
|
||||
Luid string
|
||||
Phys string
|
||||
Eng string
|
||||
Engtype string
|
||||
@@ -32,11 +33,13 @@ type Instance struct {
|
||||
|
||||
type PidPhys struct {
|
||||
Pid string
|
||||
Luid string
|
||||
Phys string
|
||||
}
|
||||
|
||||
type PidPhysEngEngType struct {
|
||||
Pid string
|
||||
Luid string
|
||||
Phys string
|
||||
Eng string
|
||||
Engtype string
|
||||
@@ -58,8 +61,7 @@ func parseGPUCounterInstanceString(s string) Instance {
|
||||
}
|
||||
case "luid":
|
||||
if i+2 < len(parts) {
|
||||
instance.Luid[0] = parts[i+1]
|
||||
instance.Luid[1] = parts[i+2]
|
||||
instance.Luid = fmt.Sprintf("%s_%s", parts[i+1], parts[i+2])
|
||||
}
|
||||
case "phys":
|
||||
if i+1 < len(parts) {
|
||||
|
||||
@@ -44,6 +44,7 @@ type Config struct {
|
||||
ProcessInclude *regexp.Regexp `yaml:"include"`
|
||||
ProcessExclude *regexp.Regexp `yaml:"exclude"`
|
||||
EnableWorkerProcess bool `yaml:"iis"`
|
||||
EnableCMDLine bool `yaml:"cmdline"`
|
||||
CounterVersion uint8 `yaml:"counter-version"`
|
||||
}
|
||||
|
||||
@@ -52,6 +53,7 @@ var ConfigDefaults = Config{
|
||||
ProcessInclude: types.RegExpAny,
|
||||
ProcessExclude: types.RegExpEmpty,
|
||||
EnableWorkerProcess: false,
|
||||
EnableCMDLine: true,
|
||||
CounterVersion: 0,
|
||||
}
|
||||
|
||||
@@ -131,6 +133,11 @@ func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
"Enable IIS collectWorker process name queries. May cause the collector to leak memory.",
|
||||
).Default(strconv.FormatBool(c.config.EnableWorkerProcess)).BoolVar(&c.config.EnableWorkerProcess)
|
||||
|
||||
app.Flag(
|
||||
"collector.process.cmdline",
|
||||
"If enabled, the full cmdline is exposed to the windows_process_info metrics.",
|
||||
).Default(strconv.FormatBool(c.config.EnableCMDLine)).BoolVar(&c.config.EnableCMDLine)
|
||||
|
||||
app.Flag(
|
||||
"collector.process.counter-version",
|
||||
"Version of the process collector to use. 1 for Process V1, 2 for Process V2. Defaults to 0 which will use the latest version available.",
|
||||
@@ -415,19 +422,25 @@ func (c *Collector) getExtendedProcessInformation(hProcess windows.Handle) (stri
|
||||
return "", 0, fmt.Errorf("failed to read process memory: %w", err)
|
||||
}
|
||||
|
||||
cmdLineUTF16 := make([]uint16, processParameters.CommandLine.Length)
|
||||
var cmdLine string
|
||||
|
||||
err = windows.ReadProcessMemory(hProcess,
|
||||
uintptr(unsafe.Pointer(processParameters.CommandLine.Buffer)),
|
||||
(*byte)(unsafe.Pointer(&cmdLineUTF16[0])),
|
||||
uintptr(processParameters.CommandLine.Length),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return "", processParameters.ProcessGroupId, fmt.Errorf("failed to read process memory: %w", err)
|
||||
if c.config.EnableCMDLine {
|
||||
cmdLineUTF16 := make([]uint16, processParameters.CommandLine.Length)
|
||||
|
||||
err = windows.ReadProcessMemory(hProcess,
|
||||
uintptr(unsafe.Pointer(processParameters.CommandLine.Buffer)),
|
||||
(*byte)(unsafe.Pointer(&cmdLineUTF16[0])),
|
||||
uintptr(processParameters.CommandLine.Length),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return "", processParameters.ProcessGroupId, fmt.Errorf("failed to read process memory: %w", err)
|
||||
}
|
||||
|
||||
cmdLine = strings.TrimSpace(windows.UTF16ToString(cmdLineUTF16))
|
||||
}
|
||||
|
||||
return strings.TrimSpace(windows.UTF16ToString(cmdLineUTF16)), processParameters.ProcessGroupId, nil
|
||||
return cmdLine, processParameters.ProcessGroupId, nil
|
||||
}
|
||||
|
||||
func (c *Collector) getProcessOwner(logger *slog.Logger, hProcess windows.Handle) (string, error) {
|
||||
|
||||
@@ -144,7 +144,12 @@ func (c *Collector) collectWorker() {
|
||||
name, pidString, parentPID, strconv.Itoa(int(processGroupID)), processOwner, cmdLine,
|
||||
)
|
||||
|
||||
startTime := float64(time.Now().UnixMicro())/1e6 - data.ElapsedTime
|
||||
startTime := data.ElapsedTime
|
||||
if c.config.CounterVersion == 2 {
|
||||
// For V2, the ElapsedTime is in seconds, so we need to convert it to a timestamp.
|
||||
// The start time is the current time minus the elapsed time.
|
||||
startTime = float64(time.Now().UnixMicro())/1e6 - data.ElapsedTime
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.startTime,
|
||||
|
||||
@@ -19,72 +19,52 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// flatten flattens the nested struct.
|
||||
//
|
||||
// All keys will be joined by dot
|
||||
// e.g. {"a": {"b":"c"}} => {"a.b":"c"}
|
||||
// or {"a": {"b":[1,2]}} => {"a.b.0":1, "a.b.1": 2}.
|
||||
func flatten(data map[string]interface{}) map[string]string {
|
||||
ret := make(map[string]string)
|
||||
|
||||
for k, v := range data {
|
||||
switch typed := v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
for fk, fv := range flatten(convertMap(typed)) {
|
||||
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
|
||||
}
|
||||
case map[string]interface{}:
|
||||
for fk, fv := range flatten(typed) {
|
||||
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
|
||||
}
|
||||
case []interface{}:
|
||||
for fk, fv := range flattenSlice(typed) {
|
||||
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
|
||||
}
|
||||
default:
|
||||
ret[k] = fmt.Sprint(typed)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func flattenSlice(data []interface{}) map[string]string {
|
||||
ret := make(map[string]string)
|
||||
|
||||
for idx, v := range data {
|
||||
switch typed := v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
for fk, fv := range flatten(convertMap(typed)) {
|
||||
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
|
||||
}
|
||||
case map[string]interface{}:
|
||||
for fk, fv := range flatten(typed) {
|
||||
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
|
||||
}
|
||||
case []interface{}:
|
||||
for fk, fv := range flattenSlice(typed) {
|
||||
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
|
||||
}
|
||||
default:
|
||||
ret[strconv.Itoa(idx)] = fmt.Sprint(typed)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertMap(originalMap map[interface{}]interface{}) map[string]interface{} {
|
||||
convertedMap := map[string]interface{}{}
|
||||
|
||||
// convertMap converts a map with any comparable key type to a map with string keys.
|
||||
func convertMap[K comparable, V any](originalMap map[K]V) map[string]V {
|
||||
convertedMap := make(map[string]V, len(originalMap))
|
||||
for key, value := range originalMap {
|
||||
if keyString, ok := key.(string); ok {
|
||||
if keyString, ok := any(key).(string); ok {
|
||||
convertedMap[keyString] = value
|
||||
}
|
||||
}
|
||||
|
||||
return convertedMap
|
||||
}
|
||||
|
||||
// flatten flattens a nested map, joining keys with dots.
|
||||
// e.g. {"a": {"b":"c"}} => {"a.b":"c"}
|
||||
func flatten(data map[string]any) map[string]string {
|
||||
result := make(map[string]string)
|
||||
|
||||
flattenHelper("", data, result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func flattenHelper(prefix string, data map[string]any, result map[string]string) {
|
||||
for k, v := range data {
|
||||
fullKey := k
|
||||
if prefix != "" {
|
||||
fullKey = prefix + "." + k
|
||||
}
|
||||
|
||||
switch val := v.(type) {
|
||||
case map[any]any:
|
||||
flattenHelper(fullKey, convertMap(val), result)
|
||||
case map[string]any:
|
||||
flattenHelper(fullKey, val, result)
|
||||
case []any:
|
||||
strSlice := make([]string, len(val))
|
||||
for i, elem := range val {
|
||||
strSlice[i] = fmt.Sprint(elem)
|
||||
}
|
||||
|
||||
result[fullKey] = strings.Join(strSlice, ",")
|
||||
default:
|
||||
result[fullKey] = fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
192
internal/headers/gdi32/gdi32.go
Normal file
192
internal/headers/gdi32/gdi32.go
Normal file
@@ -0,0 +1,192 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package gdi32
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ne-d3dkmthk-_kmtqueryadapterinfotype
|
||||
// https://github.com/nalilord/AMDPlugin/blob/bb405b6d58ea543ff630f3488384473bee79f447/Common/d3dkmthk.pas#L54
|
||||
const (
|
||||
// KMTQAITYPE_GETSEGMENTSIZE pPrivateDriverData points to a D3DKMT_SEGMENTSIZEINFO structure that contains information about the size of memory and aperture segments.
|
||||
KMTQAITYPE_GETSEGMENTSIZE = 3
|
||||
// KMTQAITYPE_ADAPTERADDRESS pPrivateDriverData points to a D3DKMT_ADAPTERADDRESS structure that contains information about the physical location on the PCI bus of the adapter.
|
||||
KMTQAITYPE_ADAPTERADDRESS = 6
|
||||
// KMTQAITYPE_ADAPTERREGISTRYINFO pPrivateDriverData points to a D3DKMT_ADAPTERREGISTRYINFO structure that contains registry information about the graphics adapter.
|
||||
KMTQAITYPE_ADAPTERREGISTRYINFO = 8
|
||||
)
|
||||
|
||||
var ErrNoGPUDevices = errors.New("no GPU devices found")
|
||||
|
||||
func GetGPUDeviceByLUID(adapterLUID windows.LUID) (GPUDevice, error) {
|
||||
open := D3DKMT_OPENADAPTERFROMLUID{
|
||||
AdapterLUID: adapterLUID,
|
||||
}
|
||||
|
||||
if err := D3DKMTOpenAdapterFromLuid(&open); err != nil {
|
||||
return GPUDevice{}, fmt.Errorf("D3DKMTOpenAdapterFromLuid failed: %w", err)
|
||||
}
|
||||
|
||||
errs := make([]error, 0)
|
||||
|
||||
gpuDevice, err := GetGPUDevice(open.HAdapter)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("GetGPUDevice failed: %w", err))
|
||||
}
|
||||
|
||||
if err := D3DKMTCloseAdapter(&D3DKMT_CLOSEADAPTER{
|
||||
HAdapter: open.HAdapter,
|
||||
}); err != nil {
|
||||
errs = append(errs, fmt.Errorf("D3DKMTCloseAdapter failed: %w", err))
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return gpuDevice, fmt.Errorf("errors occurred while getting GPU device: %w", errors.Join(errs...))
|
||||
}
|
||||
|
||||
gpuDevice.LUID = adapterLUID
|
||||
|
||||
return gpuDevice, nil
|
||||
}
|
||||
|
||||
func GetGPUDevice(hAdapter D3DKMT_HANDLE) (GPUDevice, error) {
|
||||
var gpuDevice GPUDevice
|
||||
|
||||
// Try segment size first
|
||||
var size D3DKMT_SEGMENTSIZEINFO
|
||||
|
||||
query := D3DKMT_QUERYADAPTERINFO{
|
||||
hAdapter: hAdapter,
|
||||
queryType: KMTQAITYPE_GETSEGMENTSIZE,
|
||||
pPrivateDriverData: unsafe.Pointer(&size),
|
||||
privateDriverDataSize: uint32(unsafe.Sizeof(size)),
|
||||
}
|
||||
|
||||
if err := D3DKMTQueryAdapterInfo(&query); err != nil {
|
||||
return gpuDevice, fmt.Errorf("D3DKMTQueryAdapterInfo (segment size) failed: %w", err)
|
||||
}
|
||||
|
||||
gpuDevice.DedicatedVideoMemorySize = size.DedicatedVideoMemorySize
|
||||
gpuDevice.DedicatedSystemMemorySize = size.DedicatedSystemMemorySize
|
||||
gpuDevice.SharedSystemMemorySize = size.SharedSystemMemorySize
|
||||
|
||||
// Now try registry info
|
||||
var address D3DKMT_ADAPTERADDRESS
|
||||
|
||||
query.queryType = KMTQAITYPE_ADAPTERADDRESS
|
||||
query.pPrivateDriverData = unsafe.Pointer(&address)
|
||||
query.privateDriverDataSize = uint32(unsafe.Sizeof(address))
|
||||
|
||||
if err := D3DKMTQueryAdapterInfo(&query); err != nil {
|
||||
return gpuDevice, fmt.Errorf("D3DKMTQueryAdapterInfo (adapter address) failed: %w", err)
|
||||
}
|
||||
|
||||
gpuDevice.BusNumber = address.BusNumber
|
||||
gpuDevice.DeviceNumber = address.DeviceNumber
|
||||
gpuDevice.FunctionNumber = address.FunctionNumber
|
||||
|
||||
// Now try registry info
|
||||
var info D3DKMT_ADAPTERREGISTRYINFO
|
||||
|
||||
query.queryType = KMTQAITYPE_ADAPTERREGISTRYINFO
|
||||
query.pPrivateDriverData = unsafe.Pointer(&info)
|
||||
query.privateDriverDataSize = uint32(unsafe.Sizeof(info))
|
||||
|
||||
if err := D3DKMTQueryAdapterInfo(&query); err != nil && !errors.Is(err, windows.ERROR_FILE_NOT_FOUND) {
|
||||
return gpuDevice, fmt.Errorf("D3DKMTQueryAdapterInfo (info) failed: %w", err)
|
||||
}
|
||||
|
||||
gpuDevice.AdapterString = windows.UTF16ToString(info.AdapterString[:])
|
||||
|
||||
return gpuDevice, nil
|
||||
}
|
||||
|
||||
func GetGPUDevices() ([]GPUDevice, error) {
|
||||
gpuDevices := make([]GPUDevice, 0, 2)
|
||||
|
||||
// First call: Get the number of adapters
|
||||
enumAdapters := D3DKMT_ENUMADAPTERS2{
|
||||
NumAdapters: 0,
|
||||
PAdapters: nil,
|
||||
}
|
||||
|
||||
if err := D3DKMTEnumAdapters2(&enumAdapters); err != nil {
|
||||
return gpuDevices, fmt.Errorf("D3DKMTEnumAdapters2 (get count) failed: %w", err)
|
||||
}
|
||||
|
||||
if enumAdapters.NumAdapters == 0 {
|
||||
return gpuDevices, ErrNoGPUDevices
|
||||
}
|
||||
|
||||
// Second call: Get the actual adapter information
|
||||
pAdapters := make([]D3DKMT_ADAPTERINFO, enumAdapters.NumAdapters)
|
||||
enumAdapters.PAdapters = &pAdapters[0]
|
||||
|
||||
if err := D3DKMTEnumAdapters2(&enumAdapters); err != nil {
|
||||
return gpuDevices, fmt.Errorf("D3DKMTEnumAdapters2 (get adapters) failed: %w", err)
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
// Process each adapter
|
||||
for i := range enumAdapters.NumAdapters {
|
||||
adapter := pAdapters[i]
|
||||
// Validate handle before using it
|
||||
if adapter.HAdapter == 0 {
|
||||
errs = append(errs, fmt.Errorf("adapter %d has null handle", i))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
if closeErr := D3DKMTCloseAdapter(&D3DKMT_CLOSEADAPTER{
|
||||
HAdapter: adapter.HAdapter,
|
||||
}); closeErr != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to close adapter %v: %w", adapter.AdapterLUID, closeErr))
|
||||
}
|
||||
}()
|
||||
|
||||
gpuDevice, err := GetGPUDevice(adapter.HAdapter)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to get GPU device for adapter %v: %w", adapter.AdapterLUID, err))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
gpuDevice.LUID = adapter.AdapterLUID
|
||||
|
||||
gpuDevices = append(gpuDevices, gpuDevice)
|
||||
}()
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return gpuDevices, errors.Join(errs...)
|
||||
}
|
||||
|
||||
if len(gpuDevices) == 0 {
|
||||
return gpuDevices, ErrNoGPUDevices
|
||||
}
|
||||
|
||||
return gpuDevices, nil
|
||||
}
|
||||
@@ -13,19 +13,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package setupapi_test
|
||||
package gdi32_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/setupapi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/gdi32"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetGPUDevices(t *testing.T) {
|
||||
devices, err := setupapi.GetGPUDevices()
|
||||
devices, err := gdi32.GetGPUDevices()
|
||||
require.NoError(t, err, "Failed to get GPU devices")
|
||||
|
||||
require.NotNil(t, devices)
|
||||
77
internal/headers/gdi32/syscall.go
Normal file
77
internal/headers/gdi32/syscall.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gdi32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/ntdll"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modGdi32 = windows.NewLazySystemDLL("gdi32.dll")
|
||||
procD3DKMTOpenAdapterFromLuid = modGdi32.NewProc("D3DKMTOpenAdapterFromLuid")
|
||||
procD3DKMTQueryAdapterInfo = modGdi32.NewProc("D3DKMTQueryAdapterInfo")
|
||||
procD3DKMTCloseAdapter = modGdi32.NewProc("D3DKMTCloseAdapter")
|
||||
procD3DKMTEnumAdapters2 = modGdi32.NewProc("D3DKMTEnumAdapters2")
|
||||
)
|
||||
|
||||
func D3DKMTOpenAdapterFromLuid(ptr *D3DKMT_OPENADAPTERFROMLUID) error {
|
||||
ret, _, _ := procD3DKMTOpenAdapterFromLuid.Call(
|
||||
uintptr(unsafe.Pointer(ptr)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("D3DKMTOpenAdapterFromLuid failed: 0x%X: %w", ret, ntdll.RtlNtStatusToDosError(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func D3DKMTEnumAdapters2(ptr *D3DKMT_ENUMADAPTERS2) error {
|
||||
ret, _, _ := procD3DKMTEnumAdapters2.Call(
|
||||
uintptr(unsafe.Pointer(ptr)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("D3DKMTEnumAdapters2 failed: 0x%X: %w", ret, ntdll.RtlNtStatusToDosError(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func D3DKMTQueryAdapterInfo(query *D3DKMT_QUERYADAPTERINFO) error {
|
||||
ret, _, _ := procD3DKMTQueryAdapterInfo.Call(
|
||||
uintptr(unsafe.Pointer(query)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("D3DKMTQueryAdapterInfo failed: 0x%X: %w", ret, ntdll.RtlNtStatusToDosError(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func D3DKMTCloseAdapter(ptr *D3DKMT_CLOSEADAPTER) error {
|
||||
ret, _, _ := procD3DKMTCloseAdapter.Call(
|
||||
uintptr(unsafe.Pointer(ptr)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("D3DKMTCloseAdapter failed: 0x%X: %w", ret, ntdll.RtlNtStatusToDosError(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
85
internal/headers/gdi32/types.go
Normal file
85
internal/headers/gdi32/types.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package gdi32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/win32"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type D3DKMT_HANDLE = win32.UINT
|
||||
|
||||
type D3DKMT_OPENADAPTERFROMLUID struct {
|
||||
AdapterLUID windows.LUID
|
||||
HAdapter D3DKMT_HANDLE
|
||||
}
|
||||
|
||||
type D3DKMT_CLOSEADAPTER struct {
|
||||
HAdapter D3DKMT_HANDLE
|
||||
}
|
||||
|
||||
type D3DKMT_QUERYADAPTERINFO struct {
|
||||
hAdapter D3DKMT_HANDLE
|
||||
queryType int32
|
||||
pPrivateDriverData unsafe.Pointer
|
||||
privateDriverDataSize uint32
|
||||
}
|
||||
|
||||
type D3DKMT_ENUMADAPTERS2 struct {
|
||||
NumAdapters uint32
|
||||
PAdapters *D3DKMT_ADAPTERINFO
|
||||
}
|
||||
|
||||
type D3DKMT_ADAPTERINFO struct {
|
||||
HAdapter D3DKMT_HANDLE
|
||||
AdapterLUID windows.LUID
|
||||
NumOfSources win32.ULONG
|
||||
Present win32.BOOL
|
||||
}
|
||||
|
||||
type D3DKMT_ADAPTERREGISTRYINFO struct {
|
||||
AdapterString [win32.MAX_PATH]uint16
|
||||
BiosString [win32.MAX_PATH]uint16
|
||||
DacType [win32.MAX_PATH]uint16
|
||||
ChipType [win32.MAX_PATH]uint16
|
||||
}
|
||||
|
||||
type D3DKMT_SEGMENTSIZEINFO struct {
|
||||
DedicatedVideoMemorySize uint64
|
||||
DedicatedSystemMemorySize uint64
|
||||
SharedSystemMemorySize uint64
|
||||
}
|
||||
|
||||
type D3DKMT_ADAPTERADDRESS struct {
|
||||
BusNumber win32.UINT
|
||||
DeviceNumber win32.UINT
|
||||
FunctionNumber win32.UINT
|
||||
}
|
||||
|
||||
type GPUDevice struct {
|
||||
AdapterString string
|
||||
LUID windows.LUID
|
||||
DedicatedVideoMemorySize uint64
|
||||
DedicatedSystemMemorySize uint64
|
||||
SharedSystemMemorySize uint64
|
||||
BusNumber win32.UINT
|
||||
DeviceNumber win32.UINT
|
||||
FunctionNumber win32.UINT
|
||||
}
|
||||
@@ -18,30 +18,88 @@
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
||||
"github.com/prometheus-community/windows_exporter/internal/utils"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
defaultQuery = utils.Must(windows.UTF16PtrFromString(`{"SchemaVersion":{"Major": 2,"Minor": 0},"Flags":"None"}`))
|
||||
modvmcompute = windows.NewLazySystemDLL("vmcompute.dll")
|
||||
procHNSCall = modvmcompute.NewProc("HNSCall")
|
||||
|
||||
hcnBodyEmpty = utils.Must(windows.UTF16PtrFromString(""))
|
||||
hcnMethodGet = utils.Must(windows.UTF16PtrFromString("GET"))
|
||||
hcnPathEndpoints = utils.Must(windows.UTF16PtrFromString("/endpoints/"))
|
||||
hcnPathEndpointStats = utils.Must(windows.UTF16FromString("/endpointstats/"))
|
||||
)
|
||||
|
||||
func GetEndpointProperties(endpointID ole.GUID) (EndpointProperties, error) {
|
||||
endpoint, err := OpenEndpoint(endpointID)
|
||||
func ListEndpoints() ([]EndpointProperties, error) {
|
||||
result, err := hnsCall(hcnMethodGet, hcnPathEndpoints, hcnBodyEmpty)
|
||||
if err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to open endpoint: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer CloseEndpoint(endpoint)
|
||||
|
||||
result, err := QueryEndpointProperties(endpoint, defaultQuery)
|
||||
if err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to query endpoint properties: %w", err)
|
||||
var endpoints struct {
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error"`
|
||||
Output []EndpointProperties `json:"output"`
|
||||
}
|
||||
|
||||
return result, nil
|
||||
if err := json.Unmarshal([]byte(result), &endpoints); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON %s: %w", result, err)
|
||||
}
|
||||
|
||||
if !endpoints.Success {
|
||||
return nil, fmt.Errorf("HNSCall failed: %s", endpoints.Error)
|
||||
}
|
||||
|
||||
return endpoints.Output, nil
|
||||
}
|
||||
|
||||
func GetHNSEndpointStats(endpointID string) (EndpointStats, error) {
|
||||
endpointIDUTF16, err := windows.UTF16FromString(endpointID)
|
||||
if err != nil {
|
||||
return EndpointStats{}, fmt.Errorf("failed to convert endpoint ID to UTF16: %w", err)
|
||||
}
|
||||
|
||||
path := hcnPathEndpointStats[:len(hcnPathEndpointStats)-1]
|
||||
path = append(path, endpointIDUTF16...)
|
||||
|
||||
result, err := hnsCall(hcnMethodGet, &path[0], hcnBodyEmpty)
|
||||
if err != nil {
|
||||
return EndpointStats{}, err
|
||||
}
|
||||
|
||||
var stats EndpointStats
|
||||
|
||||
if err := json.Unmarshal([]byte(result), &stats); err != nil {
|
||||
return EndpointStats{}, fmt.Errorf("failed to unmarshal JSON %s: %w", result, err)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func hnsCall(method, path, body *uint16) (string, error) {
|
||||
var responseJSON *uint16
|
||||
|
||||
r1, _, _ := procHNSCall.Call(
|
||||
uintptr(unsafe.Pointer(method)),
|
||||
uintptr(unsafe.Pointer(path)),
|
||||
uintptr(unsafe.Pointer(body)),
|
||||
uintptr(unsafe.Pointer(&responseJSON)),
|
||||
)
|
||||
|
||||
response := windows.UTF16PtrToString(responseJSON)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(responseJSON))
|
||||
|
||||
if r1 != 0 {
|
||||
return "", fmt.Errorf("HNSCall failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcn_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcn"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEnumerateEndpoints(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
endpoints, err := hcn.EnumerateEndpoints()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, endpoints)
|
||||
}
|
||||
|
||||
func TestQueryEndpointProperties(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
endpoints, err := hcn.EnumerateEndpoints()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
t.Skip("No endpoints found")
|
||||
}
|
||||
|
||||
_, err = hcn.GetEndpointProperties(endpoints[0])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modComputeNetwork = windows.NewLazySystemDLL("computenetwork.dll")
|
||||
|
||||
procHcnEnumerateEndpoints = modComputeNetwork.NewProc("HcnEnumerateEndpoints")
|
||||
procHcnOpenEndpoint = modComputeNetwork.NewProc("HcnOpenEndpoint")
|
||||
procHcnQueryEndpointProperties = modComputeNetwork.NewProc("HcnQueryEndpointProperties")
|
||||
procHcnCloseEndpoint = modComputeNetwork.NewProc("HcnCloseEndpoint")
|
||||
)
|
||||
|
||||
// EnumerateEndpoints enumerates the endpoints.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnenumerateendpoints
|
||||
func EnumerateEndpoints() ([]ole.GUID, error) {
|
||||
var (
|
||||
endpointsJSON *uint16
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnEnumerateEndpoints.Call(
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&endpointsJSON)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
result := windows.UTF16PtrToString(endpointsJSON)
|
||||
|
||||
if r1 != 0 {
|
||||
return nil, fmt.Errorf("HcnEnumerateEndpoints failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
var endpoints []ole.GUID
|
||||
|
||||
if err := json.Unmarshal([]byte(result), &endpoints); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// OpenEndpoint opens an endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnopenendpoint
|
||||
func OpenEndpoint(id ole.GUID) (Endpoint, error) {
|
||||
var (
|
||||
endpoint Endpoint
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnOpenEndpoint.Call(
|
||||
uintptr(unsafe.Pointer(&id)),
|
||||
uintptr(unsafe.Pointer(&endpoint)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
|
||||
if r1 != 0 {
|
||||
return 0, fmt.Errorf("HcnOpenEndpoint failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
// QueryEndpointProperties queries the properties of an endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnqueryendpointproperties
|
||||
func QueryEndpointProperties(endpoint Endpoint, propertyQuery *uint16) (EndpointProperties, error) {
|
||||
var (
|
||||
resultDocument *uint16
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnQueryEndpointProperties.Call(
|
||||
uintptr(endpoint),
|
||||
uintptr(unsafe.Pointer(&propertyQuery)),
|
||||
uintptr(unsafe.Pointer(&resultDocument)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
|
||||
result := windows.UTF16PtrToString(resultDocument)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(resultDocument))
|
||||
|
||||
if r1 != 0 {
|
||||
return EndpointProperties{}, fmt.Errorf("HcsGetComputeSystemProperties failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
var properties EndpointProperties
|
||||
|
||||
if err := json.Unmarshal([]byte(result), &properties); err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return properties, nil
|
||||
}
|
||||
|
||||
// CloseEndpoint close a handle to an Endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcncloseendpoint
|
||||
func CloseEndpoint(endpoint Endpoint) {
|
||||
_, _, _ = procHcnCloseEndpoint.Call(uintptr(endpoint))
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -28,17 +27,9 @@ type Endpoint = windows.Handle
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/hns_schema#HostComputeEndpoint
|
||||
type EndpointProperties struct {
|
||||
ID string `json:"ID"`
|
||||
State int `json:"State"`
|
||||
SharedContainers []string `json:"SharedContainers"`
|
||||
Resources EndpointPropertiesResources `json:"Resources"`
|
||||
}
|
||||
|
||||
type EndpointPropertiesResources struct {
|
||||
Allocators []EndpointPropertiesAllocators `json:"Allocators"`
|
||||
}
|
||||
type EndpointPropertiesAllocators struct {
|
||||
AdapterNetCfgInstanceId *ole.GUID `json:"AdapterNetCfgInstanceId"`
|
||||
ID string `json:"ID"`
|
||||
State int `json:"State"`
|
||||
SharedContainers []string `json:"SharedContainers"`
|
||||
}
|
||||
|
||||
type EndpointStats struct {
|
||||
|
||||
@@ -22,16 +22,13 @@ import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||
procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex")
|
||||
procConvertInterfaceGuidToLuid = modiphlpapi.NewProc("ConvertInterfaceGuidToLuid")
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||
)
|
||||
|
||||
func GetTCPConnectionStates(family uint32) (map[MIB_TCP_STATE]uint32, error) {
|
||||
@@ -131,38 +128,3 @@ func getExtendedTcpTable[T any](ulAf uint32, tableClass uint32) ([]T, error) {
|
||||
|
||||
return unsafe.Slice((*T)(unsafe.Pointer(&buf[4])), binary.LittleEndian.Uint32(buf)), nil
|
||||
}
|
||||
|
||||
// GetIfEntry2Ex function retrieves the specified level of information for the specified interface on the local computer.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-getifentry2ex
|
||||
func GetIfEntry2Ex(row *MIB_IF_ROW2) error {
|
||||
ret, _, _ := procGetIfEntry2Ex.Call(
|
||||
uintptr(0),
|
||||
uintptr(unsafe.Pointer(row)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("GetIfEntry2Ex failed with code %d: %w", ret, windows.Errno(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertInterfaceGUIDToLUID function converts a globally unique identifier (GUID) for a network interface to the
|
||||
// locally unique identifier (LUID) for the interface.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-convertinterfaceguidtoluid
|
||||
func ConvertInterfaceGUIDToLUID(guid ole.GUID) (uint64, error) {
|
||||
var luid uint64
|
||||
|
||||
ret, _, _ := procConvertInterfaceGuidToLuid.Call(
|
||||
uintptr(unsafe.Pointer(&guid)),
|
||||
uintptr(unsafe.Pointer(&luid)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return 0, fmt.Errorf("ConvertInterfaceGUIDToLUID failed with code %d: %w", ret, windows.Errno(ret))
|
||||
}
|
||||
|
||||
return luid, nil
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ func TestGetTCPConnectionStates(t *testing.T) {
|
||||
func TestGetOwnerPIDOfTCPPort(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lister, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
var listenConf net.ListenConfig
|
||||
|
||||
lister, err := listenConf.Listen(t.Context(), "tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
//go:build windows
|
||||
|
||||
package setupapi
|
||||
package ntdll
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -23,9 +23,15 @@ import (
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modSetupAPI = windows.NewLazySystemDLL("setupapi.dll")
|
||||
procSetupDiGetClassDevsW = modSetupAPI.NewProc("SetupDiGetClassDevsW")
|
||||
procSetupDiEnumDeviceInfo = modSetupAPI.NewProc("SetupDiEnumDeviceInfo")
|
||||
procSetupDiGetDeviceRegistryPropertyW = modSetupAPI.NewProc("SetupDiGetDeviceRegistryPropertyW")
|
||||
procSetupDiDestroyDeviceInfoList = modSetupAPI.NewProc("SetupDiDestroyDeviceInfoList")
|
||||
modNtdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
procRtlNtStatusToDosError = modNtdll.NewProc("RtlNtStatusToDosError")
|
||||
)
|
||||
|
||||
func RtlNtStatusToDosError(status uintptr) error {
|
||||
ret, _, _ := procRtlNtStatusToDosError.Call(status)
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return windows.Errno(ret)
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var GUID_DISPLAY_ADAPTER = sync.OnceValue(func() *windows.GUID {
|
||||
return &windows.GUID{
|
||||
Data1: 0x4d36e968,
|
||||
Data2: 0xe325,
|
||||
Data3: 0x11ce,
|
||||
Data4: [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18},
|
||||
}
|
||||
})
|
||||
|
||||
func GetGPUDevices() ([]GPUDevice, error) {
|
||||
hDevInfo, _, err := procSetupDiGetClassDevsW.Call(
|
||||
uintptr(unsafe.Pointer(GUID_DISPLAY_ADAPTER())),
|
||||
0,
|
||||
0,
|
||||
DIGCF_PRESENT,
|
||||
)
|
||||
|
||||
if windows.Handle(hDevInfo) == windows.InvalidHandle {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
devices []GPUDevice
|
||||
deviceData SP_DEVINFO_DATA
|
||||
propertyBuffer [256]uint16
|
||||
)
|
||||
|
||||
deviceData.CbSize = uint32(unsafe.Sizeof(deviceData))
|
||||
|
||||
for i := 0; ; i++ {
|
||||
ret, _, _ := procSetupDiEnumDeviceInfo.Call(hDevInfo, uintptr(i), uintptr(unsafe.Pointer(&deviceData)))
|
||||
if ret == 0 {
|
||||
break // No more devices
|
||||
}
|
||||
|
||||
ret, _, _ = procSetupDiGetDeviceRegistryPropertyW.Call(
|
||||
hDevInfo,
|
||||
uintptr(unsafe.Pointer(&deviceData)),
|
||||
uintptr(SPDRP_DEVICEDESC),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&propertyBuffer[0])),
|
||||
uintptr(len(propertyBuffer)*2),
|
||||
0,
|
||||
)
|
||||
|
||||
gpuDevice := GPUDevice{}
|
||||
|
||||
if ret == 0 {
|
||||
gpuDevice.DeviceDesc = ""
|
||||
} else {
|
||||
gpuDevice.DeviceDesc = windows.UTF16ToString(propertyBuffer[:])
|
||||
}
|
||||
|
||||
ret, _, _ = procSetupDiGetDeviceRegistryPropertyW.Call(
|
||||
hDevInfo,
|
||||
uintptr(unsafe.Pointer(&deviceData)),
|
||||
uintptr(SPDRP_FRIENDLYNAME),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&propertyBuffer[0])),
|
||||
uintptr(len(propertyBuffer)*2),
|
||||
0,
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
gpuDevice.FriendlyName = ""
|
||||
} else {
|
||||
gpuDevice.FriendlyName = windows.UTF16ToString(propertyBuffer[:])
|
||||
}
|
||||
|
||||
ret, _, _ = procSetupDiGetDeviceRegistryPropertyW.Call(
|
||||
hDevInfo,
|
||||
uintptr(unsafe.Pointer(&deviceData)),
|
||||
uintptr(SPDRP_HARDWAREID),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&propertyBuffer[0])),
|
||||
uintptr(len(propertyBuffer)*2),
|
||||
0,
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
gpuDevice.HardwareID = "unknown"
|
||||
} else {
|
||||
gpuDevice.HardwareID = windows.UTF16ToString(propertyBuffer[:])
|
||||
}
|
||||
|
||||
ret, _, _ = procSetupDiGetDeviceRegistryPropertyW.Call(
|
||||
hDevInfo,
|
||||
uintptr(unsafe.Pointer(&deviceData)),
|
||||
uintptr(SPDRP_PHYSICAL_DEVICE_OBJECT_NAME),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&propertyBuffer[0])),
|
||||
uintptr(len(propertyBuffer)*2),
|
||||
0,
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
gpuDevice.PhysicalDeviceObjectName = "unknown"
|
||||
} else {
|
||||
gpuDevice.PhysicalDeviceObjectName = windows.UTF16ToString(propertyBuffer[:])
|
||||
}
|
||||
|
||||
devices = append(devices, gpuDevice)
|
||||
}
|
||||
|
||||
_, _, _ = procSetupDiDestroyDeviceInfoList.Call(hDevInfo)
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package setupapi
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
const (
|
||||
DIGCF_PRESENT = 0x00000002
|
||||
SPDRP_DEVICEDESC = 0x00000000
|
||||
SPDRP_FRIENDLYNAME = 0x0000000C
|
||||
SPDRP_HARDWAREID = 0x00000001
|
||||
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E
|
||||
)
|
||||
|
||||
type SP_DEVINFO_DATA struct {
|
||||
CbSize uint32
|
||||
ClassGuid windows.GUID
|
||||
DevInst uint32
|
||||
_ uintptr // Reserved
|
||||
}
|
||||
|
||||
type GPUDevice struct {
|
||||
DeviceDesc string
|
||||
FriendlyName string
|
||||
HardwareID string
|
||||
PhysicalDeviceObjectName string
|
||||
}
|
||||
@@ -23,12 +23,17 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const MAX_PATH = 260
|
||||
|
||||
type (
|
||||
BOOL = int32 // BOOL is a 32-bit signed int in Win32
|
||||
DATE_TIME = windows.Filetime
|
||||
DWORD = uint32
|
||||
LPWSTR struct {
|
||||
*uint16
|
||||
}
|
||||
ULONG = uint32 // ULONG is a 32-bit unsigned int in Win32
|
||||
UINT = uint32 // UINT is a 32-bit unsigned int in Win32
|
||||
)
|
||||
|
||||
// NewLPWSTR creates a new LPWSTR from a string.
|
||||
|
||||
Reference in New Issue
Block a user