mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-14 17:06:36 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
|
||||||
with:
|
with:
|
||||||
# renovate: github=golangci/golangci-lint
|
# renovate: github=golangci/golangci-lint
|
||||||
version: v2.2.1
|
version: v2.2.2
|
||||||
args: "--max-same-issues=0"
|
args: "--max-same-issues=0"
|
||||||
|
|||||||
4
.idea/dictionaries/project.xml
generated
4
.idea/dictionaries/project.xml
generated
@@ -2,8 +2,12 @@
|
|||||||
<dictionary name="project">
|
<dictionary name="project">
|
||||||
<words>
|
<words>
|
||||||
<w>containerd</w>
|
<w>containerd</w>
|
||||||
|
<w>endpointstats</w>
|
||||||
|
<w>gochecknoglobals</w>
|
||||||
|
<w>luid</w>
|
||||||
<w>setupapi</w>
|
<w>setupapi</w>
|
||||||
<w>spdx</w>
|
<w>spdx</w>
|
||||||
|
<w>vmcompute</w>
|
||||||
</words>
|
</words>
|
||||||
</dictionary>
|
</dictionary>
|
||||||
</component>
|
</component>
|
||||||
29
README.md
29
README.md
@@ -182,9 +182,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.
|
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
|
## Kubernetes Implementation
|
||||||
|
|
||||||
See detailed steps to install on Windows Kubernetes [here](./kubernetes/kubernetes.md).
|
See detailed steps to install on Windows Kubernetes [here](./kubernetes/kubernetes.md).
|
||||||
@@ -195,16 +192,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.
|
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
|
### HTTP Endpoints
|
||||||
|
|
||||||
windows_exporter provides the following HTTP endpoints:
|
windows_exporter provides the following HTTP endpoints:
|
||||||
@@ -213,18 +200,6 @@ windows_exporter provides the following HTTP endpoints:
|
|||||||
* `/health`: Returns 200 OK when the exporter is running.
|
* `/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.
|
* `/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
|
||||||
|
|
||||||
Using `[defaults]` with `--collectors.enabled` argument which gets expanded with all default collectors.
|
Using `[defaults]` with `--collectors.enabled` argument which gets expanded with all default collectors.
|
||||||
@@ -237,10 +212,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"`
|
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
|
```yaml
|
||||||
collectors:
|
collectors:
|
||||||
enabled: cpu,net,service
|
enabled: cpu,net,service
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
collectors:
|
collectors:
|
||||||
enabled: cpu,cpu_info,exchange,iis,logical_disk,memory,net,os,performancecounter,process,remote_fx,service,system,tcp,time,terminal_services,textfile
|
enabled: cpu,cpu_info,exchange,iis,logical_disk,memory,net,os,performancecounter,process,remote_fx,service,system,tcp,time,terminal_services,textfile
|
||||||
collector:
|
collector:
|
||||||
|
textfile:
|
||||||
|
directories:
|
||||||
|
- 'C:\MyDir1'
|
||||||
|
- 'C:\MyDir2'
|
||||||
service:
|
service:
|
||||||
include: "windows_exporter"
|
include: "windows_exporter"
|
||||||
performancecounter:
|
performancecounter:
|
||||||
|
|||||||
@@ -20,29 +20,32 @@ These metrics are available on supported versions of Windows with compatible GPU
|
|||||||
|
|
||||||
### Adapter-level Metrics
|
### Adapter-level Metrics
|
||||||
|
|
||||||
| Name | Description | Type | Labels |
|
| Name | Description | Type | Labels |
|
||||||
|----------------------------------------------|-------------------------------------------------------------------------|-------|--------------------------------------------------------------------------------------|
|
|--------------------------------------------------|------------------------------------------------------------------------------------|-------|---------------|
|
||||||
| `windows_gpu_adapter_memory_committed_bytes` | Total committed GPU memory in bytes per physical GPU | gauge | `phys` |
|
| `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_adapter_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per physical GPU | gauge | `phys` |
|
| `windows_gpu_dedicated_system_memory_size_bytes` | The size, in bytes, of memory that is dedicated from system memory. | gauge | `luid` |
|
||||||
| `windows_gpu_adapter_memory_shared_bytes` | Shared GPU memory usage in bytes per physical GPU | gauge | `phys` |
|
| `windows_gpu_dedicated_video_memory_size_bytes` | The size, in bytes, of memory that is dedicated from video memory. | gauge | `luid` |
|
||||||
| `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_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_local_adapter_memory_bytes` | Local adapter memory usage in bytes per physical GPU | gauge | `phys` |
|
| `windows_gpu_adapter_memory_committed_bytes` | Total committed GPU memory 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 | `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
|
### Per-process Metrics
|
||||||
|
|
||||||
| Name | Description | Type | Labels |
|
| 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_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 | `phys`,`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 | `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 | `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 | `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 | `phys`,`process_id` |
|
| `windows_gpu_process_memory_shared_bytes` | Shared GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||||
|
|
||||||
## Metric Labels
|
## 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", ...)
|
* `eng`: GPU engine index (e.g., "0", "1", ...)
|
||||||
* `engtype`: GPU engine type (e.g., "3D", "Copy", "VideoDecode", etc.)
|
* `engtype`: GPU engine type (e.g., "3D", "Copy", "VideoDecode", etc.)
|
||||||
* `process_id`: Process ID
|
* `process_id`: Process ID
|
||||||
|
|||||||
12
go.mod
12
go.mod
@@ -4,7 +4,7 @@ go 1.24
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||||
github.com/bmatcuk/doublestar/v4 v4.8.1
|
github.com/bmatcuk/doublestar/v4 v4.9.0
|
||||||
github.com/dimchansky/utfbom v1.1.1
|
github.com/dimchansky/utfbom v1.1.1
|
||||||
github.com/go-ole/go-ole v1.3.0
|
github.com/go-ole/go-ole v1.3.0
|
||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.22.0
|
||||||
@@ -12,7 +12,7 @@ require (
|
|||||||
github.com/prometheus/common v0.65.0
|
github.com/prometheus/common v0.65.0
|
||||||
github.com/prometheus/exporter-toolkit v0.14.0
|
github.com/prometheus/exporter-toolkit v0.14.0
|
||||||
github.com/stretchr/testify v1.10.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
|
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/procfs v0.17.0 // indirect
|
github.com/prometheus/procfs v0.17.0 // indirect
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||||
golang.org/x/crypto v0.39.0 // indirect
|
golang.org/x/crypto v0.40.0 // indirect
|
||||||
golang.org/x/net v0.41.0 // indirect
|
golang.org/x/net v0.42.0 // indirect
|
||||||
golang.org/x/oauth2 v0.30.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/sync v0.15.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/text v0.26.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
24
go.sum
24
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/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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
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.9.0 h1:DBvuZxjdKkRP/dr4GVV4w2fnmrk5Hxc90T51LZjv0JA=
|
||||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
github.com/bmatcuk/doublestar/v4 v4.9.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
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/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
@@ -65,19 +65,19 @@ 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/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 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
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.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
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 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
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 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
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=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -29,10 +29,8 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/alecthomas/kingpin/v2"
|
"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/hcn"
|
||||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
"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/headers/kernel32"
|
||||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
"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.
|
// collectNetworkMetrics collects network metrics for containers.
|
||||||
func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
||||||
endpoints, err := hcn.EnumerateEndpoints()
|
endpoints, err := hcn.ListEndpoints()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error in fetching HCN endpoints: %w", err)
|
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 {
|
for _, endpoint := range endpoints {
|
||||||
properties, err := hcn.GetEndpointProperties(endpoint)
|
if len(endpoint.SharedContainers) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
endpointStats, err := hcn.GetHNSEndpointStats(endpoint.ID)
|
||||||
if err != nil {
|
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),
|
slog.Any("err", err),
|
||||||
)
|
)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(properties.SharedContainers) == 0 {
|
for _, containerId := range endpoint.SharedContainers {
|
||||||
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 {
|
|
||||||
containerInfo, ok := c.annotationsCacheHCS[containerId]
|
containerInfo, ok := c.annotationsCacheHCS[containerId]
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
c.logger.Debug("Unknown container " + containerId + " for endpoint " + endpoint.String())
|
c.logger.Debug("Unknown container " + containerId + " for endpoint " + endpoint.ID)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -593,47 +558,47 @@ func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
endpointId := strings.ToUpper(endpoint.String())
|
endpointId := strings.ToUpper(endpoint.ID)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.bytesReceived,
|
c.bytesReceived,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
float64(endpointStats.InOctets),
|
float64(endpointStats.BytesReceived),
|
||||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.bytesSent,
|
c.bytesSent,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
float64(endpointStats.OutOctets),
|
float64(endpointStats.BytesSent),
|
||||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.packetsReceived,
|
c.packetsReceived,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
float64(endpointStats.InUcastPkts+endpointStats.InNUcastPkts),
|
float64(endpointStats.PacketsReceived),
|
||||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.packetsSent,
|
c.packetsSent,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
float64(endpointStats.OutUcastPkts+endpointStats.OutNUcastPkts),
|
float64(endpointStats.PacketsSent),
|
||||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.droppedPacketsIncoming,
|
c.droppedPacketsIncoming,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
float64(endpointStats.InDiscards+endpointStats.InErrors),
|
float64(endpointStats.DroppedPacketsIncoming),
|
||||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.droppedPacketsOutgoing,
|
c.droppedPacketsOutgoing,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
float64(endpointStats.OutDiscards+endpointStats.OutErrors),
|
float64(endpointStats.DroppedPacketsOutgoing),
|
||||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package filetime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -73,19 +74,10 @@ func NewWithFlags(app *kingpin.Application) *Collector {
|
|||||||
}
|
}
|
||||||
c.config.FilePatterns = make([]string, 0)
|
c.config.FilePatterns = make([]string, 0)
|
||||||
|
|
||||||
var filePatterns string
|
|
||||||
|
|
||||||
app.Flag(
|
app.Flag(
|
||||||
"collector.filetime.file-patterns",
|
"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",
|
"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)
|
).Default(strings.Join(ConfigDefaults.FilePatterns, ",")).StringsVar(&c.config.FilePatterns)
|
||||||
|
|
||||||
app.Action(func(*kingpin.ParseContext) error {
|
|
||||||
// doublestar.Glob() requires forward slashes
|
|
||||||
c.config.FilePatterns = strings.Split(filepath.ToSlash(filePatterns), ",")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return c
|
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 {
|
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)
|
basePathFS := os.DirFS(basePath)
|
||||||
|
|
||||||
matches, err := doublestar.Glob(basePathFS, pattern, doublestar.WithFilesOnly())
|
err := doublestar.GlobWalk(basePathFS, pattern, func(path string, d fs.DirEntry) error {
|
||||||
if err != nil {
|
filePath := filepath.Join(basePath, path)
|
||||||
return fmt.Errorf("failed to glob: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, match := range matches {
|
|
||||||
filePath := filepath.Join(basePath, match)
|
|
||||||
|
|
||||||
fileInfo, err := os.Stat(filePath)
|
fileInfo, err := os.Stat(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -166,7 +153,7 @@ func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern
|
|||||||
slog.Any("err", err),
|
slog.Any("err", err),
|
||||||
)
|
)
|
||||||
|
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
@@ -175,6 +162,11 @@ func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern
|
|||||||
float64(fileInfo.ModTime().UTC().UnixMicro())/1e6,
|
float64(fileInfo.ModTime().UTC().UnixMicro())/1e6,
|
||||||
filePath,
|
filePath,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, doublestar.WithFilesOnly(), doublestar.WithCaseInsensitive())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to glob: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/alecthomas/kingpin/v2"
|
"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/mi"
|
||||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||||
"github.com/prometheus-community/windows_exporter/internal/types"
|
"github.com/prometheus-community/windows_exporter/internal/types"
|
||||||
@@ -41,6 +41,8 @@ var ConfigDefaults = Config{}
|
|||||||
type Collector struct {
|
type Collector struct {
|
||||||
config Config
|
config Config
|
||||||
|
|
||||||
|
gpuDeviceCache map[string]gdi32.GPUDevice
|
||||||
|
|
||||||
// GPU Engine
|
// GPU Engine
|
||||||
gpuEnginePerfDataCollector *pdh.Collector
|
gpuEnginePerfDataCollector *pdh.Collector
|
||||||
gpuEnginePerfDataObject []gpuEnginePerfDataCounterValues
|
gpuEnginePerfDataObject []gpuEnginePerfDataCounterValues
|
||||||
@@ -48,6 +50,10 @@ type Collector struct {
|
|||||||
gpuInfo *prometheus.Desc
|
gpuInfo *prometheus.Desc
|
||||||
gpuEngineRunningTime *prometheus.Desc
|
gpuEngineRunningTime *prometheus.Desc
|
||||||
|
|
||||||
|
gpuSharedSystemMemorySize *prometheus.Desc
|
||||||
|
gpuDedicatedSystemMemorySize *prometheus.Desc
|
||||||
|
gpuDedicatedVideoMemorySize *prometheus.Desc
|
||||||
|
|
||||||
// GPU Adapter Memory
|
// GPU Adapter Memory
|
||||||
gpuAdapterMemoryPerfDataCollector *pdh.Collector
|
gpuAdapterMemoryPerfDataCollector *pdh.Collector
|
||||||
gpuAdapterMemoryPerfDataObject []gpuAdapterMemoryPerfDataCounterValues
|
gpuAdapterMemoryPerfDataObject []gpuAdapterMemoryPerfDataCounterValues
|
||||||
@@ -115,78 +121,97 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
|||||||
c.gpuInfo = prometheus.NewDesc(
|
c.gpuInfo = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "info"),
|
prometheus.BuildFQName(types.Namespace, Name, "info"),
|
||||||
"A metric with a constant '1' value labeled with gpu device information.",
|
"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,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
c.gpuEngineRunningTime = prometheus.NewDesc(
|
c.gpuEngineRunningTime = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "engine_time_seconds"),
|
prometheus.BuildFQName(types.Namespace, Name, "engine_time_seconds"),
|
||||||
"Total running time of the GPU in seconds.",
|
"Total running time of the GPU in seconds.",
|
||||||
[]string{"process_id", "phys", "eng", "engtype"},
|
[]string{"process_id", "luid", "phys", "eng", "engtype"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
c.gpuAdapterMemoryDedicatedUsage = prometheus.NewDesc(
|
c.gpuAdapterMemoryDedicatedUsage = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_dedicated_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_dedicated_bytes"),
|
||||||
"Dedicated GPU memory usage in bytes.",
|
"Dedicated GPU memory usage in bytes.",
|
||||||
[]string{"phys"},
|
[]string{"luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
c.gpuAdapterMemorySharedUsage = prometheus.NewDesc(
|
c.gpuAdapterMemorySharedUsage = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_shared_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_shared_bytes"),
|
||||||
"Shared GPU memory usage in bytes.",
|
"Shared GPU memory usage in bytes.",
|
||||||
[]string{"phys"},
|
[]string{"luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
c.gpuAdapterMemoryTotalCommitted = prometheus.NewDesc(
|
c.gpuAdapterMemoryTotalCommitted = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_committed_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_committed_bytes"),
|
||||||
"Total committed GPU memory in bytes.",
|
"Total committed GPU memory in bytes.",
|
||||||
[]string{"phys"},
|
[]string{"luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
c.gpuLocalAdapterMemoryUsage = prometheus.NewDesc(
|
c.gpuLocalAdapterMemoryUsage = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "local_adapter_memory_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "local_adapter_memory_bytes"),
|
||||||
"Local adapter memory usage in bytes.",
|
"Local adapter memory usage in bytes.",
|
||||||
[]string{"phys"},
|
[]string{"luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
c.gpuNonLocalAdapterMemoryUsage = prometheus.NewDesc(
|
c.gpuNonLocalAdapterMemoryUsage = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "non_local_adapter_memory_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "non_local_adapter_memory_bytes"),
|
||||||
"Non-local adapter memory usage in bytes.",
|
"Non-local adapter memory usage in bytes.",
|
||||||
[]string{"phys"},
|
[]string{"luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
c.gpuProcessMemoryDedicatedUsage = prometheus.NewDesc(
|
c.gpuProcessMemoryDedicatedUsage = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_dedicated_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "process_memory_dedicated_bytes"),
|
||||||
"Dedicated process memory usage in bytes.",
|
"Dedicated process memory usage in bytes.",
|
||||||
[]string{"process_id", "phys"},
|
[]string{"process_id", "luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
c.gpuProcessMemoryLocalUsage = prometheus.NewDesc(
|
c.gpuProcessMemoryLocalUsage = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_local_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "process_memory_local_bytes"),
|
||||||
"Local process memory usage in bytes.",
|
"Local process memory usage in bytes.",
|
||||||
[]string{"process_id", "phys"},
|
[]string{"process_id", "luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
c.gpuProcessMemoryNonLocalUsage = prometheus.NewDesc(
|
c.gpuProcessMemoryNonLocalUsage = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_non_local_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "process_memory_non_local_bytes"),
|
||||||
"Non-local process memory usage in bytes.",
|
"Non-local process memory usage in bytes.",
|
||||||
[]string{"process_id", "phys"},
|
[]string{"process_id", "luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
c.gpuProcessMemorySharedUsage = prometheus.NewDesc(
|
c.gpuProcessMemorySharedUsage = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_shared_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "process_memory_shared_bytes"),
|
||||||
"Shared process memory usage in bytes.",
|
"Shared process memory usage in bytes.",
|
||||||
[]string{"process_id", "phys"},
|
[]string{"process_id", "luid", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
c.gpuProcessMemoryTotalCommitted = prometheus.NewDesc(
|
c.gpuProcessMemoryTotalCommitted = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_committed_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "process_memory_committed_bytes"),
|
||||||
"Total committed process memory in bytes.",
|
"Total committed process memory in bytes.",
|
||||||
[]string{"process_id", "phys"},
|
[]string{"process_id", "luid", "phys"},
|
||||||
nil,
|
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))
|
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...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||||
errs := make([]error, 0)
|
errs := make([]error, 0)
|
||||||
|
|
||||||
if err := c.collectGpuInfo(ch); err != nil {
|
c.collectGpuInfo(ch)
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.collectGpuEngineMetrics(ch); err != nil {
|
if err := c.collectGpuEngineMetrics(ch); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
@@ -250,26 +291,40 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
|||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collector) collectGpuInfo(ch chan<- prometheus.Metric) error {
|
func (c *Collector) collectGpuInfo(ch chan<- prometheus.Metric) {
|
||||||
gpus, err := setupapi.GetGPUDevices()
|
for luid, gpu := range c.gpuDeviceCache {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get GPU devices: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, gpu := range gpus {
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuInfo,
|
c.gpuInfo,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
1.0,
|
1.0,
|
||||||
strconv.Itoa(i),
|
luid,
|
||||||
gpu.PhysicalDeviceObjectName,
|
gpu.AdapterString,
|
||||||
gpu.HardwareID,
|
strconv.FormatInt(int64(gpu.BusNumber), 10),
|
||||||
gpu.FriendlyName,
|
strconv.FormatInt(int64(gpu.DeviceNumber), 10),
|
||||||
gpu.DeviceDesc,
|
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 {
|
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 {
|
for _, data := range c.gpuEnginePerfDataObject {
|
||||||
instance := parseGPUCounterInstanceString(data.Name)
|
instance := parseGPUCounterInstanceString(data.Name)
|
||||||
|
|
||||||
|
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
key := PidPhysEngEngType{
|
key := PidPhysEngEngType{
|
||||||
Pid: instance.Pid,
|
Pid: instance.Pid,
|
||||||
Phys: instance.Phys,
|
Phys: instance.Phys,
|
||||||
|
Luid: instance.Luid,
|
||||||
Eng: instance.Eng,
|
Eng: instance.Eng,
|
||||||
Engtype: instance.Engtype,
|
Engtype: instance.Engtype,
|
||||||
}
|
}
|
||||||
@@ -297,7 +357,7 @@ func (c *Collector) collectGpuEngineMetrics(ch chan<- prometheus.Metric) error {
|
|||||||
c.gpuEngineRunningTime,
|
c.gpuEngineRunningTime,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
runningTime,
|
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 {
|
for _, data := range c.gpuAdapterMemoryPerfDataObject {
|
||||||
instance := parseGPUCounterInstanceString(data.Name)
|
instance := parseGPUCounterInstanceString(data.Name)
|
||||||
|
|
||||||
|
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
key := PidPhysEngEngType{
|
key := PidPhysEngEngType{
|
||||||
Pid: instance.Pid,
|
Pid: instance.Pid,
|
||||||
|
Luid: instance.Luid,
|
||||||
Phys: instance.Phys,
|
Phys: instance.Phys,
|
||||||
Eng: instance.Eng,
|
Eng: instance.Eng,
|
||||||
Engtype: instance.Engtype,
|
Engtype: instance.Engtype,
|
||||||
@@ -333,21 +398,21 @@ func (c *Collector) collectGpuAdapterMemoryMetrics(ch chan<- prometheus.Metric)
|
|||||||
c.gpuAdapterMemoryDedicatedUsage,
|
c.gpuAdapterMemoryDedicatedUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
dedicatedUsage,
|
dedicatedUsage,
|
||||||
key.Phys,
|
key.Luid, key.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuAdapterMemorySharedUsage,
|
c.gpuAdapterMemorySharedUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
sharedUsageMap[key],
|
sharedUsageMap[key],
|
||||||
key.Phys,
|
key.Luid, key.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuAdapterMemoryTotalCommitted,
|
c.gpuAdapterMemoryTotalCommitted,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
totalCommittedMap[key],
|
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)
|
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 {
|
for _, data := range c.gpuLocalAdapterMemoryPerfDataObject {
|
||||||
instance := parseGPUCounterInstanceString(data.Name)
|
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(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuLocalAdapterMemoryUsage,
|
c.gpuLocalAdapterMemoryUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
localUsage,
|
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)
|
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 {
|
for _, data := range c.gpuNonLocalAdapterMemoryPerfDataObject {
|
||||||
instance := parseGPUCounterInstanceString(data.Name)
|
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(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuNonLocalAdapterMemoryUsage,
|
c.gpuNonLocalAdapterMemoryUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
nonLocalUsage,
|
nonLocalUsage,
|
||||||
phys,
|
key.Luid, key.Phys,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,8 +503,13 @@ func (c *Collector) collectGpuProcessMemoryMetrics(ch chan<- prometheus.Metric)
|
|||||||
for _, data := range c.gpuProcessMemoryPerfDataObject {
|
for _, data := range c.gpuProcessMemoryPerfDataObject {
|
||||||
instance := parseGPUCounterInstanceString(data.Name)
|
instance := parseGPUCounterInstanceString(data.Name)
|
||||||
|
|
||||||
|
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
key := PidPhys{
|
key := PidPhys{
|
||||||
Pid: instance.Pid,
|
Pid: instance.Pid,
|
||||||
|
Luid: instance.Luid,
|
||||||
Phys: instance.Phys,
|
Phys: instance.Phys,
|
||||||
}
|
}
|
||||||
processDedicatedUsageMap[key] += data.DedicatedUsage
|
processDedicatedUsageMap[key] += data.DedicatedUsage
|
||||||
@@ -437,35 +524,35 @@ func (c *Collector) collectGpuProcessMemoryMetrics(ch chan<- prometheus.Metric)
|
|||||||
c.gpuProcessMemoryDedicatedUsage,
|
c.gpuProcessMemoryDedicatedUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
dedicatedUsage,
|
dedicatedUsage,
|
||||||
key.Pid, key.Phys,
|
key.Pid, key.Luid, key.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemoryLocalUsage,
|
c.gpuProcessMemoryLocalUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
processLocalUsageMap[key],
|
processLocalUsageMap[key],
|
||||||
key.Pid, key.Phys,
|
key.Pid, key.Luid, key.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemoryNonLocalUsage,
|
c.gpuProcessMemoryNonLocalUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
processNonLocalUsageMap[key],
|
processNonLocalUsageMap[key],
|
||||||
key.Pid, key.Phys,
|
key.Pid, key.Luid, key.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemorySharedUsage,
|
c.gpuProcessMemorySharedUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
processSharedUsageMap[key],
|
processSharedUsageMap[key],
|
||||||
key.Pid, key.Phys,
|
key.Pid, key.Luid, key.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemoryTotalCommitted,
|
c.gpuProcessMemoryTotalCommitted,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
processTotalCommittedMap[key],
|
processTotalCommittedMap[key],
|
||||||
key.Pid, key.Phys,
|
key.Pid, key.Luid, key.Phys,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,13 @@
|
|||||||
package gpu
|
package gpu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
Pid string
|
Pid string
|
||||||
Luid [2]string
|
Luid string
|
||||||
Phys string
|
Phys string
|
||||||
Eng string
|
Eng string
|
||||||
Engtype string
|
Engtype string
|
||||||
@@ -32,11 +33,13 @@ type Instance struct {
|
|||||||
|
|
||||||
type PidPhys struct {
|
type PidPhys struct {
|
||||||
Pid string
|
Pid string
|
||||||
|
Luid string
|
||||||
Phys string
|
Phys string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PidPhysEngEngType struct {
|
type PidPhysEngEngType struct {
|
||||||
Pid string
|
Pid string
|
||||||
|
Luid string
|
||||||
Phys string
|
Phys string
|
||||||
Eng string
|
Eng string
|
||||||
Engtype string
|
Engtype string
|
||||||
@@ -58,8 +61,7 @@ func parseGPUCounterInstanceString(s string) Instance {
|
|||||||
}
|
}
|
||||||
case "luid":
|
case "luid":
|
||||||
if i+2 < len(parts) {
|
if i+2 < len(parts) {
|
||||||
instance.Luid[0] = parts[i+1]
|
instance.Luid = fmt.Sprintf("%s_%s", parts[i+1], parts[i+2])
|
||||||
instance.Luid[1] = parts[i+2]
|
|
||||||
}
|
}
|
||||||
case "phys":
|
case "phys":
|
||||||
if i+1 < len(parts) {
|
if i+1 < len(parts) {
|
||||||
|
|||||||
@@ -144,7 +144,12 @@ func (c *Collector) collectWorker() {
|
|||||||
name, pidString, parentPID, strconv.Itoa(int(processGroupID)), processOwner, cmdLine,
|
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(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.startTime,
|
c.startTime,
|
||||||
|
|||||||
@@ -19,72 +19,52 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// flatten flattens the nested struct.
|
// 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 {
|
||||||
// All keys will be joined by dot
|
convertedMap := make(map[string]V, len(originalMap))
|
||||||
// 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{}{}
|
|
||||||
|
|
||||||
for key, value := range originalMap {
|
for key, value := range originalMap {
|
||||||
if keyString, ok := key.(string); ok {
|
if keyString, ok := any(key).(string); ok {
|
||||||
convertedMap[keyString] = value
|
convertedMap[keyString] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertedMap
|
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//go:build windows
|
package gdi32_test
|
||||||
|
|
||||||
package setupapi_test
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/prometheus-community/windows_exporter/internal/headers/setupapi"
|
"github.com/prometheus-community/windows_exporter/internal/headers/gdi32"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetGPUDevices(t *testing.T) {
|
func TestGetGPUDevices(t *testing.T) {
|
||||||
devices, err := setupapi.GetGPUDevices()
|
devices, err := gdi32.GetGPUDevices()
|
||||||
require.NoError(t, err, "Failed to get GPU devices")
|
require.NoError(t, err, "Failed to get GPU devices")
|
||||||
|
|
||||||
require.NotNil(t, 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
|
package hcn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"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"
|
"github.com/prometheus-community/windows_exporter/internal/utils"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:gochecknoglobals
|
//nolint:gochecknoglobals
|
||||||
var (
|
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) {
|
func ListEndpoints() ([]EndpointProperties, error) {
|
||||||
endpoint, err := OpenEndpoint(endpointID)
|
result, err := hnsCall(hcnMethodGet, hcnPathEndpoints, hcnBodyEmpty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return EndpointProperties{}, fmt.Errorf("failed to open endpoint: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer CloseEndpoint(endpoint)
|
var endpoints struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
result, err := QueryEndpointProperties(endpoint, defaultQuery)
|
Error string `json:"error"`
|
||||||
if err != nil {
|
Output []EndpointProperties `json:"output"`
|
||||||
return EndpointProperties{}, fmt.Errorf("failed to query endpoint properties: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
package hcn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-ole/go-ole"
|
|
||||||
"golang.org/x/sys/windows"
|
"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
|
// https://learn.microsoft.com/en-us/virtualization/api/hcn/hns_schema#HostComputeEndpoint
|
||||||
type EndpointProperties struct {
|
type EndpointProperties struct {
|
||||||
ID string `json:"ID"`
|
ID string `json:"ID"`
|
||||||
State int `json:"State"`
|
State int `json:"State"`
|
||||||
SharedContainers []string `json:"SharedContainers"`
|
SharedContainers []string `json:"SharedContainers"`
|
||||||
Resources EndpointPropertiesResources `json:"Resources"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type EndpointPropertiesResources struct {
|
|
||||||
Allocators []EndpointPropertiesAllocators `json:"Allocators"`
|
|
||||||
}
|
|
||||||
type EndpointPropertiesAllocators struct {
|
|
||||||
AdapterNetCfgInstanceId *ole.GUID `json:"AdapterNetCfgInstanceId"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type EndpointStats struct {
|
type EndpointStats struct {
|
||||||
|
|||||||
@@ -22,16 +22,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/go-ole/go-ole"
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:gochecknoglobals
|
//nolint:gochecknoglobals
|
||||||
var (
|
var (
|
||||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||||
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||||
procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex")
|
|
||||||
procConvertInterfaceGuidToLuid = modiphlpapi.NewProc("ConvertInterfaceGuidToLuid")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetTCPConnectionStates(family uint32) (map[MIB_TCP_STATE]uint32, error) {
|
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
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
|
|
||||||
package setupapi
|
package ntdll
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@@ -23,9 +23,15 @@ import (
|
|||||||
|
|
||||||
//nolint:gochecknoglobals
|
//nolint:gochecknoglobals
|
||||||
var (
|
var (
|
||||||
modSetupAPI = windows.NewLazySystemDLL("setupapi.dll")
|
modNtdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||||
procSetupDiGetClassDevsW = modSetupAPI.NewProc("SetupDiGetClassDevsW")
|
procRtlNtStatusToDosError = modNtdll.NewProc("RtlNtStatusToDosError")
|
||||||
procSetupDiEnumDeviceInfo = modSetupAPI.NewProc("SetupDiEnumDeviceInfo")
|
|
||||||
procSetupDiGetDeviceRegistryPropertyW = modSetupAPI.NewProc("SetupDiGetDeviceRegistryPropertyW")
|
|
||||||
procSetupDiDestroyDeviceInfoList = modSetupAPI.NewProc("SetupDiDestroyDeviceInfoList")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const MAX_PATH = 260
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
BOOL = int32 // BOOL is a 32-bit signed int in Win32
|
||||||
DATE_TIME = windows.Filetime
|
DATE_TIME = windows.Filetime
|
||||||
DWORD = uint32
|
DWORD = uint32
|
||||||
LPWSTR struct {
|
LPWSTR struct {
|
||||||
*uint16
|
*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.
|
// NewLPWSTR creates a new LPWSTR from a string.
|
||||||
|
|||||||
Reference in New Issue
Block a user