mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-08 22:16:38 +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
|
||||
with:
|
||||
# renovate: github=golangci/golangci-lint
|
||||
version: v2.2.1
|
||||
version: v2.2.2
|
||||
args: "--max-same-issues=0"
|
||||
|
||||
4
.idea/dictionaries/project.xml
generated
4
.idea/dictionaries/project.xml
generated
@@ -2,8 +2,12 @@
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>containerd</w>
|
||||
<w>endpointstats</w>
|
||||
<w>gochecknoglobals</w>
|
||||
<w>luid</w>
|
||||
<w>setupapi</w>
|
||||
<w>spdx</w>
|
||||
<w>vmcompute</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</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.
|
||||
|
||||
Additionally, a flavor `hostprocess` with `-hostprocess` as suffix is based on the https://github.com/microsoft/windows-host-process-containers-base-image
|
||||
which is designed to run as a Windows host process container. The size of that images is smaller than the default one.
|
||||
|
||||
## Kubernetes Implementation
|
||||
|
||||
See detailed steps to install on Windows Kubernetes [here](./kubernetes/kubernetes.md).
|
||||
@@ -195,16 +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.
|
||||
|
||||
## Usage
|
||||
|
||||
go get -u github.com/prometheus/promu
|
||||
go get -u github.com/prometheus-community/windows_exporter
|
||||
cd $env:GOPATH/src/github.com/prometheus-community/windows_exporter
|
||||
promu build -v
|
||||
.\windows_exporter.exe
|
||||
|
||||
The prometheus metrics will be exposed on [localhost:9182](http://localhost:9182)
|
||||
|
||||
### HTTP Endpoints
|
||||
|
||||
windows_exporter provides the following HTTP endpoints:
|
||||
@@ -213,18 +200,6 @@ windows_exporter provides the following HTTP endpoints:
|
||||
* `/health`: Returns 200 OK when the exporter is running.
|
||||
* `/debug/pprof/`: Exposes the [pprof](https://golang.org/pkg/net/http/pprof/) endpoints. Only, if `--debug.enabled` is set.
|
||||
|
||||
## Examples
|
||||
|
||||
### Enable only service collector and specify a custom query
|
||||
|
||||
.\windows_exporter.exe --collectors.enabled "service" --collector.service.include="windows_exporter"
|
||||
|
||||
### Enable only process collector and specify a custom query
|
||||
|
||||
.\windows_exporter.exe --collectors.enabled "process" --collector.process.include="firefox.+"
|
||||
|
||||
When there are multiple processes with the same name, WMI represents those after the first instance as `process-name#index`. So to get them all, rather than just the first one, the [regular expression](https://en.wikipedia.org/wiki/Regular_expression) must use `.+`. See [process](docs/collector.process.md) for more information.
|
||||
|
||||
### Using [defaults] with `--collectors.enabled` argument
|
||||
|
||||
Using `[defaults]` with `--collectors.enabled` argument which gets expanded with all default collectors.
|
||||
@@ -237,10 +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"`
|
||||
|
||||
It is also possible to load the configuration from a URL. e.g. `.\windows_exporter.exe --config.file="https://example.com/config.yml"`
|
||||
|
||||
If you need to skip TLS verification, you can use the `--config.file.insecure-skip-verify` flag. e.g. `.\windows_exporter.exe --config.file="https://example.com/config.yml" --config.file.insecure-skip-verify`
|
||||
|
||||
```yaml
|
||||
collectors:
|
||||
enabled: cpu,net,service
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
collectors:
|
||||
enabled: cpu,cpu_info,exchange,iis,logical_disk,memory,net,os,performancecounter,process,remote_fx,service,system,tcp,time,terminal_services,textfile
|
||||
collector:
|
||||
textfile:
|
||||
directories:
|
||||
- 'C:\MyDir1'
|
||||
- 'C:\MyDir2'
|
||||
service:
|
||||
include: "windows_exporter"
|
||||
performancecounter:
|
||||
|
||||
@@ -20,29 +20,32 @@ These metrics are available on supported versions of Windows with compatible GPU
|
||||
|
||||
### Adapter-level Metrics
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------|-------------------------------------------------------------------------|-------|--------------------------------------------------------------------------------------|
|
||||
| `windows_gpu_adapter_memory_committed_bytes` | Total committed GPU memory in bytes per physical GPU | gauge | `phys` |
|
||||
| `windows_gpu_adapter_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per physical GPU | gauge | `phys` |
|
||||
| `windows_gpu_adapter_memory_shared_bytes` | Shared GPU memory usage in bytes per physical GPU | gauge | `phys` |
|
||||
| `windows_gpu_info` | A metric with a constant '1' value labeled with gpu device information. | gauge | `phys`, `physical_device_object_name`, `hardware_id`, `friendly_name`, `description` |
|
||||
| `windows_gpu_local_adapter_memory_bytes` | Local adapter memory usage in bytes per physical GPU | gauge | `phys` |
|
||||
| `windows_gpu_non_local_adapter_memory_bytes` | Non-local adapter memory usage in bytes per physical GPU | gauge | `phys` |
|
||||
| Name | Description | Type | Labels |
|
||||
|--------------------------------------------------|------------------------------------------------------------------------------------|-------|---------------|
|
||||
| `windows_gpu_info` | A metric with a constant '1' value labeled with gpu device information. | gauge | `luid`,`name`,`bus_number`,`phys`,`function_number` |
|
||||
| `windows_gpu_dedicated_system_memory_size_bytes` | The size, in bytes, of memory that is dedicated from system memory. | gauge | `luid` |
|
||||
| `windows_gpu_dedicated_video_memory_size_bytes` | The size, in bytes, of memory that is dedicated from video memory. | gauge | `luid` |
|
||||
| `windows_gpu_shared_system_memory_size_bytes` | The size, in bytes, of memory from system memory that can be shared by many users. | gauge | `luid` |
|
||||
| `windows_gpu_adapter_memory_committed_bytes` | Total committed GPU memory in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
| `windows_gpu_adapter_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
| `windows_gpu_adapter_memory_shared_bytes` | Shared GPU memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
| `windows_gpu_local_adapter_memory_bytes` | Local adapter memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
| `windows_gpu_non_local_adapter_memory_bytes` | Non-local adapter memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
||||
|
||||
### Per-process Metrics
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------|-------------------------------------------------------------------------|---------|--------------------------------------------------------------------------------------|
|
||||
| `windows_gpu_engine_time_seconds` | Total running time of the GPU engine in seconds | counter | `phys`, `eng`, `engtype`, `process_id` |
|
||||
| `windows_gpu_process_memory_committed_bytes` | Total committed GPU memory in bytes per process | gauge | `phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per process | gauge | `phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_local_bytes` | Local GPU memory usage in bytes per process | gauge | `phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_non_local_bytes` | Non-local GPU memory usage in bytes per process | gauge | `phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_shared_bytes` | Shared GPU memory usage in bytes per process | gauge | `phys`,`process_id` |
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------|-------------------------------------------------|---------|-----------------------------------------------|
|
||||
| `windows_gpu_engine_time_seconds` | Total running time of the GPU engine in seconds | counter | `luid`,`phys`, `eng`, `engtype`, `process_id` |
|
||||
| `windows_gpu_process_memory_committed_bytes` | Total committed GPU memory in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_local_bytes` | Local GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_non_local_bytes` | Non-local GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
| `windows_gpu_process_memory_shared_bytes` | Shared GPU memory usage in bytes per process | gauge | `luid`,`phys`,`process_id` |
|
||||
|
||||
## Metric Labels
|
||||
|
||||
* `phys`: Physical GPU index (e.g., "0")
|
||||
* `luid`,`phys`: Physical GPU index (e.g., "0")
|
||||
* `eng`: GPU engine index (e.g., "0", "1", ...)
|
||||
* `engtype`: GPU engine type (e.g., "3D", "Copy", "VideoDecode", etc.)
|
||||
* `process_id`: Process ID
|
||||
|
||||
12
go.mod
12
go.mod
@@ -4,7 +4,7 @@ go 1.24
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.0
|
||||
github.com/dimchansky/utfbom v1.1.1
|
||||
github.com/go-ole/go-ole v1.3.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
@@ -12,7 +12,7 @@ require (
|
||||
github.com/prometheus/common v0.65.0
|
||||
github.com/prometheus/exporter-toolkit v0.14.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.org/x/sys v0.34.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -30,11 +30,11 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.0 h1:DBvuZxjdKkRP/dr4GVV4w2fnmrk5Hxc90T51LZjv0JA=
|
||||
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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -29,10 +29,8 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcn"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/iphlpapi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/kernel32"
|
||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||
@@ -523,7 +521,7 @@ func (c *Collector) collectHCSContainer(ch chan<- prometheus.Metric, containerDe
|
||||
|
||||
// collectNetworkMetrics collects network metrics for containers.
|
||||
func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
||||
endpoints, err := hcn.EnumerateEndpoints()
|
||||
endpoints, err := hcn.ListEndpoints()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in fetching HCN endpoints: %w", err)
|
||||
}
|
||||
@@ -533,57 +531,24 @@ func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
properties, err := hcn.GetEndpointProperties(endpoint)
|
||||
if len(endpoint.SharedContainers) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
endpointStats, err := hcn.GetHNSEndpointStats(endpoint.ID)
|
||||
if err != nil {
|
||||
c.logger.Warn("Failed to collect properties for interface "+endpoint.String(),
|
||||
c.logger.Warn("Failed to collect network stats for interface "+endpoint.ID,
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if len(properties.SharedContainers) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var nicGUID *ole.GUID
|
||||
|
||||
for _, allocator := range properties.Resources.Allocators {
|
||||
if allocator.AdapterNetCfgInstanceId != nil {
|
||||
nicGUID = allocator.AdapterNetCfgInstanceId
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if nicGUID == nil {
|
||||
c.logger.Warn("Failed to get nic GUID for endpoint " + endpoint.String())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
luid, err := iphlpapi.ConvertInterfaceGUIDToLUID(*nicGUID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in converting interface GUID to LUID: %w", err)
|
||||
}
|
||||
|
||||
var endpointStats iphlpapi.MIB_IF_ROW2
|
||||
|
||||
endpointStats.InterfaceLuid = luid
|
||||
|
||||
if err := iphlpapi.GetIfEntry2Ex(&endpointStats); err != nil {
|
||||
c.logger.Warn("Failed to get interface entry for endpoint "+endpoint.String(),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for _, containerId := range properties.SharedContainers {
|
||||
for _, containerId := range endpoint.SharedContainers {
|
||||
containerInfo, ok := c.annotationsCacheHCS[containerId]
|
||||
|
||||
if !ok {
|
||||
c.logger.Debug("Unknown container " + containerId + " for endpoint " + endpoint.String())
|
||||
c.logger.Debug("Unknown container " + containerId + " for endpoint " + endpoint.ID)
|
||||
|
||||
continue
|
||||
}
|
||||
@@ -593,47 +558,47 @@ func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
|
||||
continue
|
||||
}
|
||||
|
||||
endpointId := strings.ToUpper(endpoint.String())
|
||||
endpointId := strings.ToUpper(endpoint.ID)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.bytesReceived,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.InOctets),
|
||||
float64(endpointStats.BytesReceived),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.bytesSent,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.OutOctets),
|
||||
float64(endpointStats.BytesSent),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.packetsReceived,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.InUcastPkts+endpointStats.InNUcastPkts),
|
||||
float64(endpointStats.PacketsReceived),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.packetsSent,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.OutUcastPkts+endpointStats.OutNUcastPkts),
|
||||
float64(endpointStats.PacketsSent),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.droppedPacketsIncoming,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.InDiscards+endpointStats.InErrors),
|
||||
float64(endpointStats.DroppedPacketsIncoming),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.droppedPacketsOutgoing,
|
||||
prometheus.CounterValue,
|
||||
float64(endpointStats.OutDiscards+endpointStats.OutErrors),
|
||||
float64(endpointStats.DroppedPacketsOutgoing),
|
||||
containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package filetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -73,19 +74,10 @@ func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
}
|
||||
c.config.FilePatterns = make([]string, 0)
|
||||
|
||||
var filePatterns string
|
||||
|
||||
app.Flag(
|
||||
"collector.filetime.file-patterns",
|
||||
"Comma-separated list of file patterns. Each pattern is a glob pattern that can contain `*`, `?`, and `**` (recursive). See https://github.com/bmatcuk/doublestar#patterns",
|
||||
).Default(strings.Join(ConfigDefaults.FilePatterns, ",")).StringVar(&filePatterns)
|
||||
|
||||
app.Action(func(*kingpin.ParseContext) error {
|
||||
// doublestar.Glob() requires forward slashes
|
||||
c.config.FilePatterns = strings.Split(filepath.ToSlash(filePatterns), ",")
|
||||
|
||||
return nil
|
||||
})
|
||||
).Default(strings.Join(ConfigDefaults.FilePatterns, ",")).StringsVar(&c.config.FilePatterns)
|
||||
|
||||
return c
|
||||
}
|
||||
@@ -148,16 +140,11 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||
}
|
||||
|
||||
func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern string) error {
|
||||
basePath, pattern := doublestar.SplitPattern(filePattern)
|
||||
basePath, pattern := doublestar.SplitPattern(filepath.ToSlash(filePattern))
|
||||
basePathFS := os.DirFS(basePath)
|
||||
|
||||
matches, err := doublestar.Glob(basePathFS, pattern, doublestar.WithFilesOnly())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to glob: %w", err)
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
filePath := filepath.Join(basePath, match)
|
||||
err := doublestar.GlobWalk(basePathFS, pattern, func(path string, d fs.DirEntry) error {
|
||||
filePath := filepath.Join(basePath, path)
|
||||
|
||||
fileInfo, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
@@ -166,7 +153,7 @@ func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -175,6 +162,11 @@ func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern
|
||||
float64(fileInfo.ModTime().UTC().UnixMicro())/1e6,
|
||||
filePath,
|
||||
)
|
||||
|
||||
return nil
|
||||
}, doublestar.WithFilesOnly(), doublestar.WithCaseInsensitive())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to glob: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/setupapi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/gdi32"
|
||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||
"github.com/prometheus-community/windows_exporter/internal/types"
|
||||
@@ -41,6 +41,8 @@ var ConfigDefaults = Config{}
|
||||
type Collector struct {
|
||||
config Config
|
||||
|
||||
gpuDeviceCache map[string]gdi32.GPUDevice
|
||||
|
||||
// GPU Engine
|
||||
gpuEnginePerfDataCollector *pdh.Collector
|
||||
gpuEnginePerfDataObject []gpuEnginePerfDataCounterValues
|
||||
@@ -48,6 +50,10 @@ type Collector struct {
|
||||
gpuInfo *prometheus.Desc
|
||||
gpuEngineRunningTime *prometheus.Desc
|
||||
|
||||
gpuSharedSystemMemorySize *prometheus.Desc
|
||||
gpuDedicatedSystemMemorySize *prometheus.Desc
|
||||
gpuDedicatedVideoMemorySize *prometheus.Desc
|
||||
|
||||
// GPU Adapter Memory
|
||||
gpuAdapterMemoryPerfDataCollector *pdh.Collector
|
||||
gpuAdapterMemoryPerfDataObject []gpuAdapterMemoryPerfDataCounterValues
|
||||
@@ -115,78 +121,97 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
||||
c.gpuInfo = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "info"),
|
||||
"A metric with a constant '1' value labeled with gpu device information.",
|
||||
[]string{"phys", "physical_device_object_name", "hardware_id", "friendly_name", "description"},
|
||||
[]string{"luid", "name", "bus_number", "phys", "function_number"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuSharedSystemMemorySize = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "shared_system_memory_size_bytes"),
|
||||
"The size, in bytes, of memory from system memory that can be shared by many users.",
|
||||
[]string{"luid"},
|
||||
nil,
|
||||
)
|
||||
c.gpuDedicatedSystemMemorySize = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "dedicated_system_memory_size_bytes"),
|
||||
"The size, in bytes, of memory that is dedicated from system memory.",
|
||||
[]string{"luid"},
|
||||
nil,
|
||||
)
|
||||
c.gpuDedicatedVideoMemorySize = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "dedicated_video_memory_size_bytes"),
|
||||
"The size, in bytes, of memory that is dedicated from video memory.",
|
||||
[]string{"luid"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuEngineRunningTime = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "engine_time_seconds"),
|
||||
"Total running time of the GPU in seconds.",
|
||||
[]string{"process_id", "phys", "eng", "engtype"},
|
||||
[]string{"process_id", "luid", "phys", "eng", "engtype"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuAdapterMemoryDedicatedUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_dedicated_bytes"),
|
||||
"Dedicated GPU memory usage in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuAdapterMemorySharedUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_shared_bytes"),
|
||||
"Shared GPU memory usage in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuAdapterMemoryTotalCommitted = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "adapter_memory_committed_bytes"),
|
||||
"Total committed GPU memory in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuLocalAdapterMemoryUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "local_adapter_memory_bytes"),
|
||||
"Local adapter memory usage in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuNonLocalAdapterMemoryUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "non_local_adapter_memory_bytes"),
|
||||
"Non-local adapter memory usage in bytes.",
|
||||
[]string{"phys"},
|
||||
[]string{"luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
|
||||
c.gpuProcessMemoryDedicatedUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_dedicated_bytes"),
|
||||
"Dedicated process memory usage in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuProcessMemoryLocalUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_local_bytes"),
|
||||
"Local process memory usage in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuProcessMemoryNonLocalUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_non_local_bytes"),
|
||||
"Non-local process memory usage in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuProcessMemorySharedUsage = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_shared_bytes"),
|
||||
"Shared process memory usage in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
c.gpuProcessMemoryTotalCommitted = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_committed_bytes"),
|
||||
"Total committed process memory in bytes.",
|
||||
[]string{"process_id", "phys"},
|
||||
[]string{"process_id", "luid", "phys"},
|
||||
nil,
|
||||
)
|
||||
|
||||
@@ -217,15 +242,31 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
||||
errs = append(errs, fmt.Errorf("failed to create GPU Process Memory perf data collector: %w", err))
|
||||
}
|
||||
|
||||
gpus, err := gdi32.GetGPUDevices()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to get GPU devices: %w", err))
|
||||
}
|
||||
|
||||
for _, gpu := range gpus {
|
||||
if gpu.AdapterString == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if c.gpuDeviceCache == nil {
|
||||
c.gpuDeviceCache = make(map[string]gdi32.GPUDevice)
|
||||
}
|
||||
|
||||
luidKey := fmt.Sprintf("0x%08X_0x%08X", gpu.LUID.HighPart, gpu.LUID.LowPart)
|
||||
c.gpuDeviceCache[luidKey] = gpu
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||
errs := make([]error, 0)
|
||||
|
||||
if err := c.collectGpuInfo(ch); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
c.collectGpuInfo(ch)
|
||||
|
||||
if err := c.collectGpuEngineMetrics(ch); err != nil {
|
||||
errs = append(errs, err)
|
||||
@@ -250,26 +291,40 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (c *Collector) collectGpuInfo(ch chan<- prometheus.Metric) error {
|
||||
gpus, err := setupapi.GetGPUDevices()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get GPU devices: %w", err)
|
||||
}
|
||||
|
||||
for i, gpu := range gpus {
|
||||
func (c *Collector) collectGpuInfo(ch chan<- prometheus.Metric) {
|
||||
for luid, gpu := range c.gpuDeviceCache {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuInfo,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
strconv.Itoa(i),
|
||||
gpu.PhysicalDeviceObjectName,
|
||||
gpu.HardwareID,
|
||||
gpu.FriendlyName,
|
||||
gpu.DeviceDesc,
|
||||
luid,
|
||||
gpu.AdapterString,
|
||||
strconv.FormatInt(int64(gpu.BusNumber), 10),
|
||||
strconv.FormatInt(int64(gpu.DeviceNumber), 10),
|
||||
strconv.FormatInt(int64(gpu.FunctionNumber), 10),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuSharedSystemMemorySize,
|
||||
prometheus.GaugeValue,
|
||||
float64(gpu.SharedSystemMemorySize),
|
||||
luid,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuDedicatedSystemMemorySize,
|
||||
prometheus.GaugeValue,
|
||||
float64(gpu.DedicatedSystemMemorySize),
|
||||
luid,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuDedicatedVideoMemorySize,
|
||||
prometheus.GaugeValue,
|
||||
float64(gpu.DedicatedVideoMemorySize),
|
||||
luid,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectGpuEngineMetrics(ch chan<- prometheus.Metric) error {
|
||||
@@ -283,9 +338,14 @@ func (c *Collector) collectGpuEngineMetrics(ch chan<- prometheus.Metric) error {
|
||||
for _, data := range c.gpuEnginePerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhysEngEngType{
|
||||
Pid: instance.Pid,
|
||||
Phys: instance.Phys,
|
||||
Luid: instance.Luid,
|
||||
Eng: instance.Eng,
|
||||
Engtype: instance.Engtype,
|
||||
}
|
||||
@@ -297,7 +357,7 @@ func (c *Collector) collectGpuEngineMetrics(ch chan<- prometheus.Metric) error {
|
||||
c.gpuEngineRunningTime,
|
||||
prometheus.CounterValue,
|
||||
runningTime,
|
||||
key.Pid, key.Phys, key.Eng, key.Engtype,
|
||||
key.Pid, key.Luid, key.Phys, key.Eng, key.Engtype,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -317,8 +377,13 @@ func (c *Collector) collectGpuAdapterMemoryMetrics(ch chan<- prometheus.Metric)
|
||||
for _, data := range c.gpuAdapterMemoryPerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhysEngEngType{
|
||||
Pid: instance.Pid,
|
||||
Luid: instance.Luid,
|
||||
Phys: instance.Phys,
|
||||
Eng: instance.Eng,
|
||||
Engtype: instance.Engtype,
|
||||
@@ -333,21 +398,21 @@ func (c *Collector) collectGpuAdapterMemoryMetrics(ch chan<- prometheus.Metric)
|
||||
c.gpuAdapterMemoryDedicatedUsage,
|
||||
prometheus.GaugeValue,
|
||||
dedicatedUsage,
|
||||
key.Phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuAdapterMemorySharedUsage,
|
||||
prometheus.GaugeValue,
|
||||
sharedUsageMap[key],
|
||||
key.Phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuAdapterMemoryTotalCommitted,
|
||||
prometheus.GaugeValue,
|
||||
totalCommittedMap[key],
|
||||
key.Phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -360,20 +425,29 @@ func (c *Collector) collectGpuLocalAdapterMemoryMetrics(ch chan<- prometheus.Met
|
||||
return fmt.Errorf("failed to collect GPU Local Adapter Memory perf data: %w", err)
|
||||
}
|
||||
|
||||
localAdapterMemoryMap := make(map[string]float64)
|
||||
localAdapterMemoryMap := make(map[PidPhysEngEngType]float64)
|
||||
|
||||
for _, data := range c.gpuLocalAdapterMemoryPerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
localAdapterMemoryMap[instance.Phys] += data.LocalUsage
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhysEngEngType{
|
||||
Luid: instance.Luid,
|
||||
Phys: instance.Phys,
|
||||
}
|
||||
|
||||
localAdapterMemoryMap[key] += data.LocalUsage
|
||||
}
|
||||
|
||||
for phys, localUsage := range localAdapterMemoryMap {
|
||||
for key, localUsage := range localAdapterMemoryMap {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuLocalAdapterMemoryUsage,
|
||||
prometheus.GaugeValue,
|
||||
localUsage,
|
||||
phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -386,20 +460,28 @@ func (c *Collector) collectGpuNonLocalAdapterMemoryMetrics(ch chan<- prometheus.
|
||||
return fmt.Errorf("failed to collect GPU Non Local Adapter Memory perf data: %w", err)
|
||||
}
|
||||
|
||||
nonLocalAdapterMemoryMap := make(map[string]float64)
|
||||
nonLocalAdapterMemoryMap := make(map[PidPhysEngEngType]float64)
|
||||
|
||||
for _, data := range c.gpuNonLocalAdapterMemoryPerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
nonLocalAdapterMemoryMap[instance.Phys] += data.NonLocalUsage
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhysEngEngType{
|
||||
Luid: instance.Luid,
|
||||
Phys: instance.Phys,
|
||||
}
|
||||
nonLocalAdapterMemoryMap[key] += data.NonLocalUsage
|
||||
}
|
||||
|
||||
for phys, nonLocalUsage := range nonLocalAdapterMemoryMap {
|
||||
for key, nonLocalUsage := range nonLocalAdapterMemoryMap {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuNonLocalAdapterMemoryUsage,
|
||||
prometheus.GaugeValue,
|
||||
nonLocalUsage,
|
||||
phys,
|
||||
key.Luid, key.Phys,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -421,8 +503,13 @@ func (c *Collector) collectGpuProcessMemoryMetrics(ch chan<- prometheus.Metric)
|
||||
for _, data := range c.gpuProcessMemoryPerfDataObject {
|
||||
instance := parseGPUCounterInstanceString(data.Name)
|
||||
|
||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key := PidPhys{
|
||||
Pid: instance.Pid,
|
||||
Luid: instance.Luid,
|
||||
Phys: instance.Phys,
|
||||
}
|
||||
processDedicatedUsageMap[key] += data.DedicatedUsage
|
||||
@@ -437,35 +524,35 @@ func (c *Collector) collectGpuProcessMemoryMetrics(ch chan<- prometheus.Metric)
|
||||
c.gpuProcessMemoryDedicatedUsage,
|
||||
prometheus.GaugeValue,
|
||||
dedicatedUsage,
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuProcessMemoryLocalUsage,
|
||||
prometheus.GaugeValue,
|
||||
processLocalUsageMap[key],
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuProcessMemoryNonLocalUsage,
|
||||
prometheus.GaugeValue,
|
||||
processNonLocalUsageMap[key],
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuProcessMemorySharedUsage,
|
||||
prometheus.GaugeValue,
|
||||
processSharedUsageMap[key],
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.gpuProcessMemoryTotalCommitted,
|
||||
prometheus.GaugeValue,
|
||||
processTotalCommittedMap[key],
|
||||
key.Pid, key.Phys,
|
||||
key.Pid, key.Luid, key.Phys,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
package gpu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Instance struct {
|
||||
Pid string
|
||||
Luid [2]string
|
||||
Luid string
|
||||
Phys string
|
||||
Eng string
|
||||
Engtype string
|
||||
@@ -32,11 +33,13 @@ type Instance struct {
|
||||
|
||||
type PidPhys struct {
|
||||
Pid string
|
||||
Luid string
|
||||
Phys string
|
||||
}
|
||||
|
||||
type PidPhysEngEngType struct {
|
||||
Pid string
|
||||
Luid string
|
||||
Phys string
|
||||
Eng string
|
||||
Engtype string
|
||||
@@ -58,8 +61,7 @@ func parseGPUCounterInstanceString(s string) Instance {
|
||||
}
|
||||
case "luid":
|
||||
if i+2 < len(parts) {
|
||||
instance.Luid[0] = parts[i+1]
|
||||
instance.Luid[1] = parts[i+2]
|
||||
instance.Luid = fmt.Sprintf("%s_%s", parts[i+1], parts[i+2])
|
||||
}
|
||||
case "phys":
|
||||
if i+1 < len(parts) {
|
||||
|
||||
@@ -144,7 +144,12 @@ func (c *Collector) collectWorker() {
|
||||
name, pidString, parentPID, strconv.Itoa(int(processGroupID)), processOwner, cmdLine,
|
||||
)
|
||||
|
||||
startTime := float64(time.Now().UnixMicro())/1e6 - data.ElapsedTime
|
||||
startTime := data.ElapsedTime
|
||||
if c.config.CounterVersion == 2 {
|
||||
// For V2, the ElapsedTime is in seconds, so we need to convert it to a timestamp.
|
||||
// The start time is the current time minus the elapsed time.
|
||||
startTime = float64(time.Now().UnixMicro())/1e6 - data.ElapsedTime
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.startTime,
|
||||
|
||||
@@ -19,72 +19,52 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// flatten flattens the nested struct.
|
||||
//
|
||||
// All keys will be joined by dot
|
||||
// e.g. {"a": {"b":"c"}} => {"a.b":"c"}
|
||||
// or {"a": {"b":[1,2]}} => {"a.b.0":1, "a.b.1": 2}.
|
||||
func flatten(data map[string]interface{}) map[string]string {
|
||||
ret := make(map[string]string)
|
||||
|
||||
for k, v := range data {
|
||||
switch typed := v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
for fk, fv := range flatten(convertMap(typed)) {
|
||||
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
|
||||
}
|
||||
case map[string]interface{}:
|
||||
for fk, fv := range flatten(typed) {
|
||||
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
|
||||
}
|
||||
case []interface{}:
|
||||
for fk, fv := range flattenSlice(typed) {
|
||||
ret[fmt.Sprintf("%s.%s", k, fk)] = fv
|
||||
}
|
||||
default:
|
||||
ret[k] = fmt.Sprint(typed)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func flattenSlice(data []interface{}) map[string]string {
|
||||
ret := make(map[string]string)
|
||||
|
||||
for idx, v := range data {
|
||||
switch typed := v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
for fk, fv := range flatten(convertMap(typed)) {
|
||||
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
|
||||
}
|
||||
case map[string]interface{}:
|
||||
for fk, fv := range flatten(typed) {
|
||||
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
|
||||
}
|
||||
case []interface{}:
|
||||
for fk, fv := range flattenSlice(typed) {
|
||||
ret[fmt.Sprintf("%d,%s", idx, fk)] = fv
|
||||
}
|
||||
default:
|
||||
ret[strconv.Itoa(idx)] = fmt.Sprint(typed)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertMap(originalMap map[interface{}]interface{}) map[string]interface{} {
|
||||
convertedMap := map[string]interface{}{}
|
||||
|
||||
// convertMap converts a map with any comparable key type to a map with string keys.
|
||||
func convertMap[K comparable, V any](originalMap map[K]V) map[string]V {
|
||||
convertedMap := make(map[string]V, len(originalMap))
|
||||
for key, value := range originalMap {
|
||||
if keyString, ok := key.(string); ok {
|
||||
if keyString, ok := any(key).(string); ok {
|
||||
convertedMap[keyString] = value
|
||||
}
|
||||
}
|
||||
|
||||
return convertedMap
|
||||
}
|
||||
|
||||
// flatten flattens a nested map, joining keys with dots.
|
||||
// e.g. {"a": {"b":"c"}} => {"a.b":"c"}
|
||||
func flatten(data map[string]any) map[string]string {
|
||||
result := make(map[string]string)
|
||||
|
||||
flattenHelper("", data, result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func flattenHelper(prefix string, data map[string]any, result map[string]string) {
|
||||
for k, v := range data {
|
||||
fullKey := k
|
||||
if prefix != "" {
|
||||
fullKey = prefix + "." + k
|
||||
}
|
||||
|
||||
switch val := v.(type) {
|
||||
case map[any]any:
|
||||
flattenHelper(fullKey, convertMap(val), result)
|
||||
case map[string]any:
|
||||
flattenHelper(fullKey, val, result)
|
||||
case []any:
|
||||
strSlice := make([]string, len(val))
|
||||
for i, elem := range val {
|
||||
strSlice[i] = fmt.Sprint(elem)
|
||||
}
|
||||
|
||||
result[fullKey] = strings.Join(strSlice, ",")
|
||||
default:
|
||||
result[fullKey] = fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
192
internal/headers/gdi32/gdi32.go
Normal file
192
internal/headers/gdi32/gdi32.go
Normal file
@@ -0,0 +1,192 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package gdi32
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/ne-d3dkmthk-_kmtqueryadapterinfotype
|
||||
// https://github.com/nalilord/AMDPlugin/blob/bb405b6d58ea543ff630f3488384473bee79f447/Common/d3dkmthk.pas#L54
|
||||
const (
|
||||
// KMTQAITYPE_GETSEGMENTSIZE pPrivateDriverData points to a D3DKMT_SEGMENTSIZEINFO structure that contains information about the size of memory and aperture segments.
|
||||
KMTQAITYPE_GETSEGMENTSIZE = 3
|
||||
// KMTQAITYPE_ADAPTERADDRESS pPrivateDriverData points to a D3DKMT_ADAPTERADDRESS structure that contains information about the physical location on the PCI bus of the adapter.
|
||||
KMTQAITYPE_ADAPTERADDRESS = 6
|
||||
// KMTQAITYPE_ADAPTERREGISTRYINFO pPrivateDriverData points to a D3DKMT_ADAPTERREGISTRYINFO structure that contains registry information about the graphics adapter.
|
||||
KMTQAITYPE_ADAPTERREGISTRYINFO = 8
|
||||
)
|
||||
|
||||
var ErrNoGPUDevices = errors.New("no GPU devices found")
|
||||
|
||||
func GetGPUDeviceByLUID(adapterLUID windows.LUID) (GPUDevice, error) {
|
||||
open := D3DKMT_OPENADAPTERFROMLUID{
|
||||
AdapterLUID: adapterLUID,
|
||||
}
|
||||
|
||||
if err := D3DKMTOpenAdapterFromLuid(&open); err != nil {
|
||||
return GPUDevice{}, fmt.Errorf("D3DKMTOpenAdapterFromLuid failed: %w", err)
|
||||
}
|
||||
|
||||
errs := make([]error, 0)
|
||||
|
||||
gpuDevice, err := GetGPUDevice(open.HAdapter)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("GetGPUDevice failed: %w", err))
|
||||
}
|
||||
|
||||
if err := D3DKMTCloseAdapter(&D3DKMT_CLOSEADAPTER{
|
||||
HAdapter: open.HAdapter,
|
||||
}); err != nil {
|
||||
errs = append(errs, fmt.Errorf("D3DKMTCloseAdapter failed: %w", err))
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return gpuDevice, fmt.Errorf("errors occurred while getting GPU device: %w", errors.Join(errs...))
|
||||
}
|
||||
|
||||
gpuDevice.LUID = adapterLUID
|
||||
|
||||
return gpuDevice, nil
|
||||
}
|
||||
|
||||
func GetGPUDevice(hAdapter D3DKMT_HANDLE) (GPUDevice, error) {
|
||||
var gpuDevice GPUDevice
|
||||
|
||||
// Try segment size first
|
||||
var size D3DKMT_SEGMENTSIZEINFO
|
||||
|
||||
query := D3DKMT_QUERYADAPTERINFO{
|
||||
hAdapter: hAdapter,
|
||||
queryType: KMTQAITYPE_GETSEGMENTSIZE,
|
||||
pPrivateDriverData: unsafe.Pointer(&size),
|
||||
privateDriverDataSize: uint32(unsafe.Sizeof(size)),
|
||||
}
|
||||
|
||||
if err := D3DKMTQueryAdapterInfo(&query); err != nil {
|
||||
return gpuDevice, fmt.Errorf("D3DKMTQueryAdapterInfo (segment size) failed: %w", err)
|
||||
}
|
||||
|
||||
gpuDevice.DedicatedVideoMemorySize = size.DedicatedVideoMemorySize
|
||||
gpuDevice.DedicatedSystemMemorySize = size.DedicatedSystemMemorySize
|
||||
gpuDevice.SharedSystemMemorySize = size.SharedSystemMemorySize
|
||||
|
||||
// Now try registry info
|
||||
var address D3DKMT_ADAPTERADDRESS
|
||||
|
||||
query.queryType = KMTQAITYPE_ADAPTERADDRESS
|
||||
query.pPrivateDriverData = unsafe.Pointer(&address)
|
||||
query.privateDriverDataSize = uint32(unsafe.Sizeof(address))
|
||||
|
||||
if err := D3DKMTQueryAdapterInfo(&query); err != nil {
|
||||
return gpuDevice, fmt.Errorf("D3DKMTQueryAdapterInfo (adapter address) failed: %w", err)
|
||||
}
|
||||
|
||||
gpuDevice.BusNumber = address.BusNumber
|
||||
gpuDevice.DeviceNumber = address.DeviceNumber
|
||||
gpuDevice.FunctionNumber = address.FunctionNumber
|
||||
|
||||
// Now try registry info
|
||||
var info D3DKMT_ADAPTERREGISTRYINFO
|
||||
|
||||
query.queryType = KMTQAITYPE_ADAPTERREGISTRYINFO
|
||||
query.pPrivateDriverData = unsafe.Pointer(&info)
|
||||
query.privateDriverDataSize = uint32(unsafe.Sizeof(info))
|
||||
|
||||
if err := D3DKMTQueryAdapterInfo(&query); err != nil && !errors.Is(err, windows.ERROR_FILE_NOT_FOUND) {
|
||||
return gpuDevice, fmt.Errorf("D3DKMTQueryAdapterInfo (info) failed: %w", err)
|
||||
}
|
||||
|
||||
gpuDevice.AdapterString = windows.UTF16ToString(info.AdapterString[:])
|
||||
|
||||
return gpuDevice, nil
|
||||
}
|
||||
|
||||
func GetGPUDevices() ([]GPUDevice, error) {
|
||||
gpuDevices := make([]GPUDevice, 0, 2)
|
||||
|
||||
// First call: Get the number of adapters
|
||||
enumAdapters := D3DKMT_ENUMADAPTERS2{
|
||||
NumAdapters: 0,
|
||||
PAdapters: nil,
|
||||
}
|
||||
|
||||
if err := D3DKMTEnumAdapters2(&enumAdapters); err != nil {
|
||||
return gpuDevices, fmt.Errorf("D3DKMTEnumAdapters2 (get count) failed: %w", err)
|
||||
}
|
||||
|
||||
if enumAdapters.NumAdapters == 0 {
|
||||
return gpuDevices, ErrNoGPUDevices
|
||||
}
|
||||
|
||||
// Second call: Get the actual adapter information
|
||||
pAdapters := make([]D3DKMT_ADAPTERINFO, enumAdapters.NumAdapters)
|
||||
enumAdapters.PAdapters = &pAdapters[0]
|
||||
|
||||
if err := D3DKMTEnumAdapters2(&enumAdapters); err != nil {
|
||||
return gpuDevices, fmt.Errorf("D3DKMTEnumAdapters2 (get adapters) failed: %w", err)
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
// Process each adapter
|
||||
for i := range enumAdapters.NumAdapters {
|
||||
adapter := pAdapters[i]
|
||||
// Validate handle before using it
|
||||
if adapter.HAdapter == 0 {
|
||||
errs = append(errs, fmt.Errorf("adapter %d has null handle", i))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
if closeErr := D3DKMTCloseAdapter(&D3DKMT_CLOSEADAPTER{
|
||||
HAdapter: adapter.HAdapter,
|
||||
}); closeErr != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to close adapter %v: %w", adapter.AdapterLUID, closeErr))
|
||||
}
|
||||
}()
|
||||
|
||||
gpuDevice, err := GetGPUDevice(adapter.HAdapter)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to get GPU device for adapter %v: %w", adapter.AdapterLUID, err))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
gpuDevice.LUID = adapter.AdapterLUID
|
||||
|
||||
gpuDevices = append(gpuDevices, gpuDevice)
|
||||
}()
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return gpuDevices, errors.Join(errs...)
|
||||
}
|
||||
|
||||
if len(gpuDevices) == 0 {
|
||||
return gpuDevices, ErrNoGPUDevices
|
||||
}
|
||||
|
||||
return gpuDevices, nil
|
||||
}
|
||||
@@ -13,19 +13,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package setupapi_test
|
||||
package gdi32_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/setupapi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/gdi32"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetGPUDevices(t *testing.T) {
|
||||
devices, err := setupapi.GetGPUDevices()
|
||||
devices, err := gdi32.GetGPUDevices()
|
||||
require.NoError(t, err, "Failed to get GPU devices")
|
||||
|
||||
require.NotNil(t, devices)
|
||||
77
internal/headers/gdi32/syscall.go
Normal file
77
internal/headers/gdi32/syscall.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gdi32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/ntdll"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modGdi32 = windows.NewLazySystemDLL("gdi32.dll")
|
||||
procD3DKMTOpenAdapterFromLuid = modGdi32.NewProc("D3DKMTOpenAdapterFromLuid")
|
||||
procD3DKMTQueryAdapterInfo = modGdi32.NewProc("D3DKMTQueryAdapterInfo")
|
||||
procD3DKMTCloseAdapter = modGdi32.NewProc("D3DKMTCloseAdapter")
|
||||
procD3DKMTEnumAdapters2 = modGdi32.NewProc("D3DKMTEnumAdapters2")
|
||||
)
|
||||
|
||||
func D3DKMTOpenAdapterFromLuid(ptr *D3DKMT_OPENADAPTERFROMLUID) error {
|
||||
ret, _, _ := procD3DKMTOpenAdapterFromLuid.Call(
|
||||
uintptr(unsafe.Pointer(ptr)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("D3DKMTOpenAdapterFromLuid failed: 0x%X: %w", ret, ntdll.RtlNtStatusToDosError(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func D3DKMTEnumAdapters2(ptr *D3DKMT_ENUMADAPTERS2) error {
|
||||
ret, _, _ := procD3DKMTEnumAdapters2.Call(
|
||||
uintptr(unsafe.Pointer(ptr)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("D3DKMTEnumAdapters2 failed: 0x%X: %w", ret, ntdll.RtlNtStatusToDosError(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func D3DKMTQueryAdapterInfo(query *D3DKMT_QUERYADAPTERINFO) error {
|
||||
ret, _, _ := procD3DKMTQueryAdapterInfo.Call(
|
||||
uintptr(unsafe.Pointer(query)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("D3DKMTQueryAdapterInfo failed: 0x%X: %w", ret, ntdll.RtlNtStatusToDosError(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func D3DKMTCloseAdapter(ptr *D3DKMT_CLOSEADAPTER) error {
|
||||
ret, _, _ := procD3DKMTCloseAdapter.Call(
|
||||
uintptr(unsafe.Pointer(ptr)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("D3DKMTCloseAdapter failed: 0x%X: %w", ret, ntdll.RtlNtStatusToDosError(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
85
internal/headers/gdi32/types.go
Normal file
85
internal/headers/gdi32/types.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package gdi32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/win32"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type D3DKMT_HANDLE = win32.UINT
|
||||
|
||||
type D3DKMT_OPENADAPTERFROMLUID struct {
|
||||
AdapterLUID windows.LUID
|
||||
HAdapter D3DKMT_HANDLE
|
||||
}
|
||||
|
||||
type D3DKMT_CLOSEADAPTER struct {
|
||||
HAdapter D3DKMT_HANDLE
|
||||
}
|
||||
|
||||
type D3DKMT_QUERYADAPTERINFO struct {
|
||||
hAdapter D3DKMT_HANDLE
|
||||
queryType int32
|
||||
pPrivateDriverData unsafe.Pointer
|
||||
privateDriverDataSize uint32
|
||||
}
|
||||
|
||||
type D3DKMT_ENUMADAPTERS2 struct {
|
||||
NumAdapters uint32
|
||||
PAdapters *D3DKMT_ADAPTERINFO
|
||||
}
|
||||
|
||||
type D3DKMT_ADAPTERINFO struct {
|
||||
HAdapter D3DKMT_HANDLE
|
||||
AdapterLUID windows.LUID
|
||||
NumOfSources win32.ULONG
|
||||
Present win32.BOOL
|
||||
}
|
||||
|
||||
type D3DKMT_ADAPTERREGISTRYINFO struct {
|
||||
AdapterString [win32.MAX_PATH]uint16
|
||||
BiosString [win32.MAX_PATH]uint16
|
||||
DacType [win32.MAX_PATH]uint16
|
||||
ChipType [win32.MAX_PATH]uint16
|
||||
}
|
||||
|
||||
type D3DKMT_SEGMENTSIZEINFO struct {
|
||||
DedicatedVideoMemorySize uint64
|
||||
DedicatedSystemMemorySize uint64
|
||||
SharedSystemMemorySize uint64
|
||||
}
|
||||
|
||||
type D3DKMT_ADAPTERADDRESS struct {
|
||||
BusNumber win32.UINT
|
||||
DeviceNumber win32.UINT
|
||||
FunctionNumber win32.UINT
|
||||
}
|
||||
|
||||
type GPUDevice struct {
|
||||
AdapterString string
|
||||
LUID windows.LUID
|
||||
DedicatedVideoMemorySize uint64
|
||||
DedicatedSystemMemorySize uint64
|
||||
SharedSystemMemorySize uint64
|
||||
BusNumber win32.UINT
|
||||
DeviceNumber win32.UINT
|
||||
FunctionNumber win32.UINT
|
||||
}
|
||||
@@ -18,30 +18,88 @@
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
||||
"github.com/prometheus-community/windows_exporter/internal/utils"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
defaultQuery = utils.Must(windows.UTF16PtrFromString(`{"SchemaVersion":{"Major": 2,"Minor": 0},"Flags":"None"}`))
|
||||
modvmcompute = windows.NewLazySystemDLL("vmcompute.dll")
|
||||
procHNSCall = modvmcompute.NewProc("HNSCall")
|
||||
|
||||
hcnBodyEmpty = utils.Must(windows.UTF16PtrFromString(""))
|
||||
hcnMethodGet = utils.Must(windows.UTF16PtrFromString("GET"))
|
||||
hcnPathEndpoints = utils.Must(windows.UTF16PtrFromString("/endpoints/"))
|
||||
hcnPathEndpointStats = utils.Must(windows.UTF16FromString("/endpointstats/"))
|
||||
)
|
||||
|
||||
func GetEndpointProperties(endpointID ole.GUID) (EndpointProperties, error) {
|
||||
endpoint, err := OpenEndpoint(endpointID)
|
||||
func ListEndpoints() ([]EndpointProperties, error) {
|
||||
result, err := hnsCall(hcnMethodGet, hcnPathEndpoints, hcnBodyEmpty)
|
||||
if err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to open endpoint: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer CloseEndpoint(endpoint)
|
||||
|
||||
result, err := QueryEndpointProperties(endpoint, defaultQuery)
|
||||
if err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to query endpoint properties: %w", err)
|
||||
var endpoints struct {
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error"`
|
||||
Output []EndpointProperties `json:"output"`
|
||||
}
|
||||
|
||||
return result, nil
|
||||
if err := json.Unmarshal([]byte(result), &endpoints); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON %s: %w", result, err)
|
||||
}
|
||||
|
||||
if !endpoints.Success {
|
||||
return nil, fmt.Errorf("HNSCall failed: %s", endpoints.Error)
|
||||
}
|
||||
|
||||
return endpoints.Output, nil
|
||||
}
|
||||
|
||||
func GetHNSEndpointStats(endpointID string) (EndpointStats, error) {
|
||||
endpointIDUTF16, err := windows.UTF16FromString(endpointID)
|
||||
if err != nil {
|
||||
return EndpointStats{}, fmt.Errorf("failed to convert endpoint ID to UTF16: %w", err)
|
||||
}
|
||||
|
||||
path := hcnPathEndpointStats[:len(hcnPathEndpointStats)-1]
|
||||
path = append(path, endpointIDUTF16...)
|
||||
|
||||
result, err := hnsCall(hcnMethodGet, &path[0], hcnBodyEmpty)
|
||||
if err != nil {
|
||||
return EndpointStats{}, err
|
||||
}
|
||||
|
||||
var stats EndpointStats
|
||||
|
||||
if err := json.Unmarshal([]byte(result), &stats); err != nil {
|
||||
return EndpointStats{}, fmt.Errorf("failed to unmarshal JSON %s: %w", result, err)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func hnsCall(method, path, body *uint16) (string, error) {
|
||||
var responseJSON *uint16
|
||||
|
||||
r1, _, _ := procHNSCall.Call(
|
||||
uintptr(unsafe.Pointer(method)),
|
||||
uintptr(unsafe.Pointer(path)),
|
||||
uintptr(unsafe.Pointer(body)),
|
||||
uintptr(unsafe.Pointer(&responseJSON)),
|
||||
)
|
||||
|
||||
response := windows.UTF16PtrToString(responseJSON)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(responseJSON))
|
||||
|
||||
if r1 != 0 {
|
||||
return "", fmt.Errorf("HNSCall failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcn_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcn"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEnumerateEndpoints(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
endpoints, err := hcn.EnumerateEndpoints()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, endpoints)
|
||||
}
|
||||
|
||||
func TestQueryEndpointProperties(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
endpoints, err := hcn.EnumerateEndpoints()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
t.Skip("No endpoints found")
|
||||
}
|
||||
|
||||
_, err = hcn.GetEndpointProperties(endpoints[0])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modComputeNetwork = windows.NewLazySystemDLL("computenetwork.dll")
|
||||
|
||||
procHcnEnumerateEndpoints = modComputeNetwork.NewProc("HcnEnumerateEndpoints")
|
||||
procHcnOpenEndpoint = modComputeNetwork.NewProc("HcnOpenEndpoint")
|
||||
procHcnQueryEndpointProperties = modComputeNetwork.NewProc("HcnQueryEndpointProperties")
|
||||
procHcnCloseEndpoint = modComputeNetwork.NewProc("HcnCloseEndpoint")
|
||||
)
|
||||
|
||||
// EnumerateEndpoints enumerates the endpoints.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnenumerateendpoints
|
||||
func EnumerateEndpoints() ([]ole.GUID, error) {
|
||||
var (
|
||||
endpointsJSON *uint16
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnEnumerateEndpoints.Call(
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&endpointsJSON)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
result := windows.UTF16PtrToString(endpointsJSON)
|
||||
|
||||
if r1 != 0 {
|
||||
return nil, fmt.Errorf("HcnEnumerateEndpoints failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
var endpoints []ole.GUID
|
||||
|
||||
if err := json.Unmarshal([]byte(result), &endpoints); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// OpenEndpoint opens an endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnopenendpoint
|
||||
func OpenEndpoint(id ole.GUID) (Endpoint, error) {
|
||||
var (
|
||||
endpoint Endpoint
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnOpenEndpoint.Call(
|
||||
uintptr(unsafe.Pointer(&id)),
|
||||
uintptr(unsafe.Pointer(&endpoint)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
|
||||
if r1 != 0 {
|
||||
return 0, fmt.Errorf("HcnOpenEndpoint failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
// QueryEndpointProperties queries the properties of an endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnqueryendpointproperties
|
||||
func QueryEndpointProperties(endpoint Endpoint, propertyQuery *uint16) (EndpointProperties, error) {
|
||||
var (
|
||||
resultDocument *uint16
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnQueryEndpointProperties.Call(
|
||||
uintptr(endpoint),
|
||||
uintptr(unsafe.Pointer(&propertyQuery)),
|
||||
uintptr(unsafe.Pointer(&resultDocument)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
|
||||
result := windows.UTF16PtrToString(resultDocument)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(resultDocument))
|
||||
|
||||
if r1 != 0 {
|
||||
return EndpointProperties{}, fmt.Errorf("HcsGetComputeSystemProperties failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
var properties EndpointProperties
|
||||
|
||||
if err := json.Unmarshal([]byte(result), &properties); err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return properties, nil
|
||||
}
|
||||
|
||||
// CloseEndpoint close a handle to an Endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcncloseendpoint
|
||||
func CloseEndpoint(endpoint Endpoint) {
|
||||
_, _, _ = procHcnCloseEndpoint.Call(uintptr(endpoint))
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"github.com/go-ole/go-ole"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -28,17 +27,9 @@ type Endpoint = windows.Handle
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/hns_schema#HostComputeEndpoint
|
||||
type EndpointProperties struct {
|
||||
ID string `json:"ID"`
|
||||
State int `json:"State"`
|
||||
SharedContainers []string `json:"SharedContainers"`
|
||||
Resources EndpointPropertiesResources `json:"Resources"`
|
||||
}
|
||||
|
||||
type EndpointPropertiesResources struct {
|
||||
Allocators []EndpointPropertiesAllocators `json:"Allocators"`
|
||||
}
|
||||
type EndpointPropertiesAllocators struct {
|
||||
AdapterNetCfgInstanceId *ole.GUID `json:"AdapterNetCfgInstanceId"`
|
||||
ID string `json:"ID"`
|
||||
State int `json:"State"`
|
||||
SharedContainers []string `json:"SharedContainers"`
|
||||
}
|
||||
|
||||
type EndpointStats struct {
|
||||
|
||||
@@ -22,16 +22,13 @@ import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||
procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex")
|
||||
procConvertInterfaceGuidToLuid = modiphlpapi.NewProc("ConvertInterfaceGuidToLuid")
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||
)
|
||||
|
||||
func GetTCPConnectionStates(family uint32) (map[MIB_TCP_STATE]uint32, error) {
|
||||
@@ -131,38 +128,3 @@ func getExtendedTcpTable[T any](ulAf uint32, tableClass uint32) ([]T, error) {
|
||||
|
||||
return unsafe.Slice((*T)(unsafe.Pointer(&buf[4])), binary.LittleEndian.Uint32(buf)), nil
|
||||
}
|
||||
|
||||
// GetIfEntry2Ex function retrieves the specified level of information for the specified interface on the local computer.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-getifentry2ex
|
||||
func GetIfEntry2Ex(row *MIB_IF_ROW2) error {
|
||||
ret, _, _ := procGetIfEntry2Ex.Call(
|
||||
uintptr(0),
|
||||
uintptr(unsafe.Pointer(row)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("GetIfEntry2Ex failed with code %d: %w", ret, windows.Errno(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertInterfaceGUIDToLUID function converts a globally unique identifier (GUID) for a network interface to the
|
||||
// locally unique identifier (LUID) for the interface.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-convertinterfaceguidtoluid
|
||||
func ConvertInterfaceGUIDToLUID(guid ole.GUID) (uint64, error) {
|
||||
var luid uint64
|
||||
|
||||
ret, _, _ := procConvertInterfaceGuidToLuid.Call(
|
||||
uintptr(unsafe.Pointer(&guid)),
|
||||
uintptr(unsafe.Pointer(&luid)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return 0, fmt.Errorf("ConvertInterfaceGUIDToLUID failed with code %d: %w", ret, windows.Errno(ret))
|
||||
}
|
||||
|
||||
return luid, nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
//go:build windows
|
||||
|
||||
package setupapi
|
||||
package ntdll
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -23,9 +23,15 @@ import (
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modSetupAPI = windows.NewLazySystemDLL("setupapi.dll")
|
||||
procSetupDiGetClassDevsW = modSetupAPI.NewProc("SetupDiGetClassDevsW")
|
||||
procSetupDiEnumDeviceInfo = modSetupAPI.NewProc("SetupDiEnumDeviceInfo")
|
||||
procSetupDiGetDeviceRegistryPropertyW = modSetupAPI.NewProc("SetupDiGetDeviceRegistryPropertyW")
|
||||
procSetupDiDestroyDeviceInfoList = modSetupAPI.NewProc("SetupDiDestroyDeviceInfoList")
|
||||
modNtdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
procRtlNtStatusToDosError = modNtdll.NewProc("RtlNtStatusToDosError")
|
||||
)
|
||||
|
||||
func RtlNtStatusToDosError(status uintptr) error {
|
||||
ret, _, _ := procRtlNtStatusToDosError.Call(status)
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return windows.Errno(ret)
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var GUID_DISPLAY_ADAPTER = sync.OnceValue(func() *windows.GUID {
|
||||
return &windows.GUID{
|
||||
Data1: 0x4d36e968,
|
||||
Data2: 0xe325,
|
||||
Data3: 0x11ce,
|
||||
Data4: [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18},
|
||||
}
|
||||
})
|
||||
|
||||
func GetGPUDevices() ([]GPUDevice, error) {
|
||||
hDevInfo, _, err := procSetupDiGetClassDevsW.Call(
|
||||
uintptr(unsafe.Pointer(GUID_DISPLAY_ADAPTER())),
|
||||
0,
|
||||
0,
|
||||
DIGCF_PRESENT,
|
||||
)
|
||||
|
||||
if windows.Handle(hDevInfo) == windows.InvalidHandle {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
devices []GPUDevice
|
||||
deviceData SP_DEVINFO_DATA
|
||||
propertyBuffer [256]uint16
|
||||
)
|
||||
|
||||
deviceData.CbSize = uint32(unsafe.Sizeof(deviceData))
|
||||
|
||||
for i := 0; ; i++ {
|
||||
ret, _, _ := procSetupDiEnumDeviceInfo.Call(hDevInfo, uintptr(i), uintptr(unsafe.Pointer(&deviceData)))
|
||||
if ret == 0 {
|
||||
break // No more devices
|
||||
}
|
||||
|
||||
ret, _, _ = procSetupDiGetDeviceRegistryPropertyW.Call(
|
||||
hDevInfo,
|
||||
uintptr(unsafe.Pointer(&deviceData)),
|
||||
uintptr(SPDRP_DEVICEDESC),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&propertyBuffer[0])),
|
||||
uintptr(len(propertyBuffer)*2),
|
||||
0,
|
||||
)
|
||||
|
||||
gpuDevice := GPUDevice{}
|
||||
|
||||
if ret == 0 {
|
||||
gpuDevice.DeviceDesc = ""
|
||||
} else {
|
||||
gpuDevice.DeviceDesc = windows.UTF16ToString(propertyBuffer[:])
|
||||
}
|
||||
|
||||
ret, _, _ = procSetupDiGetDeviceRegistryPropertyW.Call(
|
||||
hDevInfo,
|
||||
uintptr(unsafe.Pointer(&deviceData)),
|
||||
uintptr(SPDRP_FRIENDLYNAME),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&propertyBuffer[0])),
|
||||
uintptr(len(propertyBuffer)*2),
|
||||
0,
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
gpuDevice.FriendlyName = ""
|
||||
} else {
|
||||
gpuDevice.FriendlyName = windows.UTF16ToString(propertyBuffer[:])
|
||||
}
|
||||
|
||||
ret, _, _ = procSetupDiGetDeviceRegistryPropertyW.Call(
|
||||
hDevInfo,
|
||||
uintptr(unsafe.Pointer(&deviceData)),
|
||||
uintptr(SPDRP_HARDWAREID),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&propertyBuffer[0])),
|
||||
uintptr(len(propertyBuffer)*2),
|
||||
0,
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
gpuDevice.HardwareID = "unknown"
|
||||
} else {
|
||||
gpuDevice.HardwareID = windows.UTF16ToString(propertyBuffer[:])
|
||||
}
|
||||
|
||||
ret, _, _ = procSetupDiGetDeviceRegistryPropertyW.Call(
|
||||
hDevInfo,
|
||||
uintptr(unsafe.Pointer(&deviceData)),
|
||||
uintptr(SPDRP_PHYSICAL_DEVICE_OBJECT_NAME),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&propertyBuffer[0])),
|
||||
uintptr(len(propertyBuffer)*2),
|
||||
0,
|
||||
)
|
||||
|
||||
if ret == 0 {
|
||||
gpuDevice.PhysicalDeviceObjectName = "unknown"
|
||||
} else {
|
||||
gpuDevice.PhysicalDeviceObjectName = windows.UTF16ToString(propertyBuffer[:])
|
||||
}
|
||||
|
||||
devices = append(devices, gpuDevice)
|
||||
}
|
||||
|
||||
_, _, _ = procSetupDiDestroyDeviceInfoList.Call(hDevInfo)
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package setupapi
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
const (
|
||||
DIGCF_PRESENT = 0x00000002
|
||||
SPDRP_DEVICEDESC = 0x00000000
|
||||
SPDRP_FRIENDLYNAME = 0x0000000C
|
||||
SPDRP_HARDWAREID = 0x00000001
|
||||
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E
|
||||
)
|
||||
|
||||
type SP_DEVINFO_DATA struct {
|
||||
CbSize uint32
|
||||
ClassGuid windows.GUID
|
||||
DevInst uint32
|
||||
_ uintptr // Reserved
|
||||
}
|
||||
|
||||
type GPUDevice struct {
|
||||
DeviceDesc string
|
||||
FriendlyName string
|
||||
HardwareID string
|
||||
PhysicalDeviceObjectName string
|
||||
}
|
||||
@@ -23,12 +23,17 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const MAX_PATH = 260
|
||||
|
||||
type (
|
||||
BOOL = int32 // BOOL is a 32-bit signed int in Win32
|
||||
DATE_TIME = windows.Filetime
|
||||
DWORD = uint32
|
||||
LPWSTR struct {
|
||||
*uint16
|
||||
}
|
||||
ULONG = uint32 // ULONG is a 32-bit unsigned int in Win32
|
||||
UINT = uint32 // UINT is a 32-bit unsigned int in Win32
|
||||
)
|
||||
|
||||
// NewLPWSTR creates a new LPWSTR from a string.
|
||||
|
||||
Reference in New Issue
Block a user