mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-10 06:56:38 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcf21bb600 | ||
|
|
cd5f136079 | ||
|
|
4171ec17a5 | ||
|
|
6289499dee | ||
|
|
79917893d1 | ||
|
|
0b8a257b31 | ||
|
|
71cedbc4d0 | ||
|
|
c8a4cb3806 | ||
|
|
558629dff5 | ||
|
|
5a8ebf0c44 | ||
|
|
acbabb926d | ||
|
|
e37392c00b | ||
|
|
00d86ba792 | ||
|
|
691f64f5cc | ||
|
|
19999dea49 | ||
|
|
c2df4d7514 | ||
|
|
8937a5ac91 | ||
|
|
930130f58a |
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: windows-2025
|
runs-on: windows-2025
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
promtool:
|
promtool:
|
||||||
runs-on: windows-2025
|
runs-on: windows-2025
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
git config --global core.autocrlf false
|
git config --global core.autocrlf false
|
||||||
git config --global core.eol lf
|
git config --global core.eol lf
|
||||||
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
@@ -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.3.1
|
version: v2.4.0
|
||||||
args: "--max-same-issues=0"
|
args: "--max-same-issues=0"
|
||||||
|
|||||||
2
.github/workflows/pr-check.yaml
vendored
2
.github/workflows/pr-check.yaml
vendored
@@ -33,7 +33,7 @@ jobs:
|
|||||||
name: check title prefix
|
name: check title prefix
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
- name: check
|
- name: check
|
||||||
run: |
|
run: |
|
||||||
PR_TITLE_PREFIX=$(echo "$PR_TITLE" | cut -d':' -f1)
|
PR_TITLE_PREFIX=$(echo "$PR_TITLE" | cut -d':' -f1)
|
||||||
|
|||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
runs-on: windows-2025
|
runs-on: windows-2025
|
||||||
environment: build
|
environment: build
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
@@ -180,25 +180,25 @@ jobs:
|
|||||||
DOCKER_BUILD_SUMMARY: false
|
DOCKER_BUILD_SUMMARY: false
|
||||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Download Artifacts
|
- name: Download Artifacts
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: windows_exporter_binaries
|
name: windows_exporter_binaries
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_LOGIN }}
|
username: ${{ secrets.DOCKER_HUB_LOGIN }}
|
||||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
- name: Login to quay.io
|
- name: Login to quay.io
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
registry: quay.io
|
registry: quay.io
|
||||||
username: ${{ secrets.QUAY_IO_LOGIN }}
|
username: ${{ secrets.QUAY_IO_LOGIN }}
|
||||||
@@ -206,7 +206,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Login to GitHub container registry
|
- name: Login to GitHub container registry
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
|||||||
2
.github/workflows/spelling.yml
vendored
2
.github/workflows/spelling.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
name: Check for spelling errors
|
name: Check for spelling errors
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
- uses: codespell-project/actions-codespell@master
|
- uses: codespell-project/actions-codespell@master
|
||||||
with:
|
with:
|
||||||
check_filenames: true
|
check_filenames: true
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ output/
|
|||||||
.vscode
|
.vscode
|
||||||
*.syso
|
*.syso
|
||||||
installer/*.msi
|
installer/*.msi
|
||||||
|
installer/*.log
|
||||||
installer/*.wixpdb
|
installer/*.wixpdb
|
||||||
local/
|
local/
|
||||||
|
|
||||||
|
|||||||
2
.idea/dictionaries/project.xml
generated
2
.idea/dictionaries/project.xml
generated
@@ -4,7 +4,9 @@
|
|||||||
<w>containerd</w>
|
<w>containerd</w>
|
||||||
<w>endpointstats</w>
|
<w>endpointstats</w>
|
||||||
<w>gochecknoglobals</w>
|
<w>gochecknoglobals</w>
|
||||||
|
<w>lpwstr</w>
|
||||||
<w>luid</w>
|
<w>luid</w>
|
||||||
|
<w>operationoptions</w>
|
||||||
<w>setupapi</w>
|
<w>setupapi</w>
|
||||||
<w>spdx</w>
|
<w>spdx</w>
|
||||||
<w>textfile</w>
|
<w>textfile</w>
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ func run(ctx context.Context, args []string) int {
|
|||||||
"collectors.enabled",
|
"collectors.enabled",
|
||||||
"Comma-separated list of collectors to use. Use '[defaults]' as a placeholder for all the collectors enabled by default.").
|
"Comma-separated list of collectors to use. Use '[defaults]' as a placeholder for all the collectors enabled by default.").
|
||||||
Default(collector.DefaultCollectors).String()
|
Default(collector.DefaultCollectors).String()
|
||||||
|
disabledCollectors = app.Flag(
|
||||||
|
"collectors.disabled",
|
||||||
|
"Comma-separated list of collectors to exclude. Can be used to disable collector from the defaults.").
|
||||||
|
Default("").String()
|
||||||
timeoutMargin = app.Flag(
|
timeoutMargin = app.Flag(
|
||||||
"scrape.timeout-margin",
|
"scrape.timeout-margin",
|
||||||
"Seconds to subtract from the timeout allowed by the client. Tune to allow for overhead or high loads.",
|
"Seconds to subtract from the timeout allowed by the client. Tune to allow for overhead or high loads.",
|
||||||
@@ -166,6 +170,10 @@ func run(ctx context.Context, args []string) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *disabledCollectors != "" {
|
||||||
|
collectors.Disable(slices.Compact(strings.Split(*disabledCollectors, ",")))
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize collectors before loading
|
// Initialize collectors before loading
|
||||||
if err = collectors.Build(ctx, logger); err != nil {
|
if err = collectors.Build(ctx, logger); err != nil {
|
||||||
for _, err := range utils.SplitError(err) {
|
for _, err := range utils.SplitError(err) {
|
||||||
|
|||||||
@@ -20,28 +20,28 @@ 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_info` | A metric with a constant '1' value labeled with gpu device information. | gauge | `luid`,`name`,`bus_number`,`phys`,`function_number` |
|
| `windows_gpu_info` | A metric with a constant '1' value labeled with gpu device information. | gauge | `bus_number`,`device_id`,`function_number`,`luid`,`name`,`phys` |
|
||||||
| `windows_gpu_dedicated_system_memory_size_bytes` | The size, in bytes, of memory that is dedicated from system memory. | gauge | `luid` |
|
| `windows_gpu_dedicated_system_memory_size_bytes` | The size, in bytes, of memory that is dedicated from system memory. | gauge | `device_id`,`luid` |
|
||||||
| `windows_gpu_dedicated_video_memory_size_bytes` | The size, in bytes, of memory that is dedicated from video memory. | gauge | `luid` |
|
| `windows_gpu_dedicated_video_memory_size_bytes` | The size, in bytes, of memory that is dedicated from video memory. | gauge | `device_id`,`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_shared_system_memory_size_bytes` | The size, in bytes, of memory from system memory that can be shared by many users. | gauge | `device_id`,`luid` |
|
||||||
| `windows_gpu_adapter_memory_committed_bytes` | Total committed GPU memory in bytes per physical GPU | gauge | `luid`,`phys` |
|
| `windows_gpu_adapter_memory_committed_bytes` | Total committed GPU memory in bytes per physical GPU | gauge | `device_id`,`luid`,`phys` |
|
||||||
| `windows_gpu_adapter_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per physical GPU | gauge | `luid`,`phys` |
|
| `windows_gpu_adapter_memory_dedicated_bytes` | Dedicated GPU memory usage in bytes per physical GPU | gauge | `device_id`,`luid`,`phys` |
|
||||||
| `windows_gpu_adapter_memory_shared_bytes` | Shared 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 | `device_id`,`luid`,`phys` |
|
||||||
| `windows_gpu_local_adapter_memory_bytes` | Local adapter 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 | `device_id`,`luid`,`phys`,`part` |
|
||||||
| `windows_gpu_non_local_adapter_memory_bytes` | Non-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 | `device_id`,`luid`,`phys`,`part` |
|
||||||
|
|
||||||
### 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 | `luid`,`phys`, `eng`, `engtype`, `process_id` |
|
| `windows_gpu_engine_time_seconds` | Total running time of the GPU engine in seconds | counter | `device_id`,`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_committed_bytes` | Total committed GPU memory in bytes per process | gauge | `device_id`,`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_dedicated_bytes` | Dedicated GPU memory usage in bytes per process | gauge | `device_id`,`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_local_bytes` | Local GPU memory usage in bytes per process | gauge | `device_id`,`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_non_local_bytes` | Non-local GPU memory usage in bytes per process | gauge | `device_id`,`luid`,`phys`,`process_id` |
|
||||||
| `windows_gpu_process_memory_shared_bytes` | Shared 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 | `device_id`,`luid`,`phys`,`process_id` |
|
||||||
|
|
||||||
## Metric Labels
|
## Metric Labels
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ These are basic queries to help you get started with GPU monitoring on Windows u
|
|||||||
**Show GPU information for a specific physical GPU (0):**
|
**Show GPU information for a specific physical GPU (0):**
|
||||||
|
|
||||||
```promql
|
```promql
|
||||||
windows_gpu_info{description="NVIDIA GeForce GTX 1070",friendly_name="",hardware_id="PCI\\VEN_10DE&DEV_1B81&SUBSYS_61733842&REV_A1",phys="0",physical_device_object_name="\\Device\\NTPNP_PCI0027"} 1
|
windows_gpu_info{bus_number="8",device_id="PCI\\VEN_10DE&DEV_1B81&SUBSYS_61733842&REV_A1",function_number="0",luid="0x00000000_0x00010F8A",name="NVIDIA GeForce GTX 1070",phys="0"} 1
|
||||||
```
|
```
|
||||||
|
|
||||||
**Show total dedicated GPU memory (in bytes) usage on GPU 0:**
|
**Show total dedicated GPU memory (in bytes) usage on GPU 0:**
|
||||||
|
|||||||
@@ -130,10 +130,10 @@ If given, an application needs to *not* match the exclude regexp in order for th
|
|||||||
| `windows_iis_server_output_cache_hits_total` | Total number of successful lookups in output cache (since service startup) | counter | None |
|
| `windows_iis_server_output_cache_hits_total` | Total number of successful lookups in output cache (since service startup) | counter | None |
|
||||||
| `windows_iis_server_output_cache_items_flushed_total` | Total number of items flushed from output cache (since service startup) | counter | None |
|
| `windows_iis_server_output_cache_items_flushed_total` | Total number of items flushed from output cache (since service startup) | counter | None |
|
||||||
| `windows_iis_server_output_cache_flushes_total` | Total number of flushes of output cache (since service startup) | counter | None |
|
| `windows_iis_server_output_cache_flushes_total` | Total number of flushes of output cache (since service startup) | counter | None |
|
||||||
| `http_requests_current_queue_size` | Http Request Current queue size | counter | None |
|
| `windows_iis_http_requests_current_queue_size` | Http Request Current queue size | counter | None |
|
||||||
| `http_request_total_rejected_request` | Http Request total rejected request | counter | None |
|
| `windows_iis_http_request_total_rejected_request` | Http Request total rejected request | counter | None |
|
||||||
| `http_requests_max_queue_item_age` | Http Request Max queue Item age | counter | None |
|
| `windows_iis_http_requests_max_queue_item_age` | Http Request Max queue Item age | counter | None |
|
||||||
| `http_requests_arrival_rate` | Http requests Arrival Rate | counter | None |
|
| `windows_iis_http_requests_arrival_rate` | Http requests Arrival Rate | counter | None |
|
||||||
|
|
||||||
### Example metric
|
### Example metric
|
||||||
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
||||||
|
|||||||
22
go.mod
22
go.mod
@@ -1,19 +1,19 @@
|
|||||||
module github.com/prometheus-community/windows_exporter
|
module github.com/prometheus-community/windows_exporter
|
||||||
|
|
||||||
go 1.24
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||||
github.com/bmatcuk/doublestar/v4 v4.9.1
|
github.com/bmatcuk/doublestar/v4 v4.9.1
|
||||||
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.23.0
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/prometheus/client_model v0.6.2
|
github.com/prometheus/client_model v0.6.2
|
||||||
github.com/prometheus/common v0.65.0
|
github.com/prometheus/common v0.66.1
|
||||||
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.11.1
|
||||||
golang.org/x/sys v0.34.0
|
go.yaml.in/yaml/v3 v3.0.4
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
golang.org/x/sys v0.35.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -30,11 +30,13 @@ 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.40.0 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
golang.org/x/net v0.42.0 // indirect
|
golang.org/x/crypto v0.41.0 // indirect
|
||||||
|
golang.org/x/net v0.43.0 // indirect
|
||||||
golang.org/x/oauth2 v0.30.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/sync v0.16.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/text v0.27.0 // indirect
|
golang.org/x/text v0.28.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.8 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
36
go.sum
36
go.sum
@@ -41,12 +41,12 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||||
github.com/prometheus/exporter-toolkit v0.14.0 h1:NMlswfibpcZZ+H0sZBiTjrA3/aBFHkNZqE+iCj5EmRg=
|
github.com/prometheus/exporter-toolkit v0.14.0 h1:NMlswfibpcZZ+H0sZBiTjrA3/aBFHkNZqE+iCj5EmRg=
|
||||||
github.com/prometheus/exporter-toolkit v0.14.0/go.mod h1:Gu5LnVvt7Nr/oqTBUC23WILZepW0nffNo10XdhQcwWA=
|
github.com/prometheus/exporter-toolkit v0.14.0/go.mod h1:Gu5LnVvt7Nr/oqTBUC23WILZepW0nffNo10XdhQcwWA=
|
||||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||||
@@ -61,27 +61,31 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
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=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||||
|
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||||
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.16.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.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
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=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|||||||
@@ -127,6 +127,23 @@
|
|||||||
/>
|
/>
|
||||||
<!-- END CUSTOM ACTION FOR KILLING THE PROCESS -->
|
<!-- END CUSTOM ACTION FOR KILLING THE PROCESS -->
|
||||||
|
|
||||||
|
<!-- START CUSTOM ACTION FOR SET SERVICE FAILUREFLAG -->
|
||||||
|
<SetProperty
|
||||||
|
Id="ConfigureServiceRecovery"
|
||||||
|
Value=""[WindowsFolder]System32\sc.exe" failureflag "windows_exporter" 1"
|
||||||
|
Before="ConfigureServiceRecovery"
|
||||||
|
Sequence="execute"
|
||||||
|
/>
|
||||||
|
<CustomAction
|
||||||
|
Id="ConfigureServiceRecovery"
|
||||||
|
BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)"
|
||||||
|
DllEntry="WixQuietExec"
|
||||||
|
Execute="deferred"
|
||||||
|
Return="ignore"
|
||||||
|
Impersonate="no"
|
||||||
|
/>
|
||||||
|
<!-- END CUSTOM ACTION FFOR SET SERVICE FAILUREFLAG -->
|
||||||
|
|
||||||
<InstallExecuteSequence>
|
<InstallExecuteSequence>
|
||||||
<!-- Set REINSTALL=all and REINSTALLMODE=amus if the user reruns the
|
<!-- Set REINSTALL=all and REINSTALLMODE=amus if the user reruns the
|
||||||
MSI, which will force reinstalling all files and services. -->
|
MSI, which will force reinstalling all files and services. -->
|
||||||
@@ -135,6 +152,7 @@
|
|||||||
<Custom Action="set_reinstall_all_property" Before="set_reinstallmode_property" Condition="MAINTENANCE"/>
|
<Custom Action="set_reinstall_all_property" Before="set_reinstallmode_property" Condition="MAINTENANCE"/>
|
||||||
<Custom Action="set_reinstallmode_property" Before="LaunchConditions" Condition="MAINTENANCE"/>
|
<Custom Action="set_reinstallmode_property" Before="LaunchConditions" Condition="MAINTENANCE"/>
|
||||||
<Custom Action="CreateConfigFile" Before="InstallServices" Condition="ConfigFile_Default" />
|
<Custom Action="CreateConfigFile" Before="InstallServices" Condition="ConfigFile_Default" />
|
||||||
|
<Custom Action="ConfigureServiceRecovery" After="InstallServices" Condition="NOT REMOVE" />
|
||||||
<Custom Action="KillProcess" Before="RemoveFiles" />
|
<Custom Action="KillProcess" Before="RemoveFiles" />
|
||||||
|
|
||||||
<Custom Action="CheckExtraFlags" Before="InstallInitialize"
|
<Custom Action="CheckExtraFlags" Before="InstallInitialize"
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/alecthomas/kingpin/v2"
|
"github.com/alecthomas/kingpin/v2"
|
||||||
|
"github.com/prometheus-community/windows_exporter/internal/headers/cfgmgr32"
|
||||||
"github.com/prometheus-community/windows_exporter/internal/headers/gdi32"
|
"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"
|
||||||
@@ -41,7 +41,7 @@ var ConfigDefaults = Config{}
|
|||||||
type Collector struct {
|
type Collector struct {
|
||||||
config Config
|
config Config
|
||||||
|
|
||||||
gpuDeviceCache map[string]gdi32.GPUDevice
|
gpuDeviceCache map[string]gpuDevice
|
||||||
|
|
||||||
// GPU Engine
|
// GPU Engine
|
||||||
gpuEnginePerfDataCollector *pdh.Collector
|
gpuEnginePerfDataCollector *pdh.Collector
|
||||||
@@ -85,6 +85,12 @@ type Collector struct {
|
|||||||
gpuProcessMemoryTotalCommitted *prometheus.Desc
|
gpuProcessMemoryTotalCommitted *prometheus.Desc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type gpuDevice struct {
|
||||||
|
gdi32 gdi32.GPUDevice
|
||||||
|
cfgmgr32 cfgmgr32.Device
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
func New(config *Config) *Collector {
|
func New(config *Config) *Collector {
|
||||||
if config == nil {
|
if config == nil {
|
||||||
config = &ConfigDefaults
|
config = &ConfigDefaults
|
||||||
@@ -121,97 +127,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{"luid", "name", "bus_number", "phys", "function_number"},
|
[]string{"luid", "device_id", "name", "bus_number", "phys", "function_number"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
c.gpuSharedSystemMemorySize = prometheus.NewDesc(
|
c.gpuSharedSystemMemorySize = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "shared_system_memory_size_bytes"),
|
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.",
|
"The size, in bytes, of memory from system memory that can be shared by many users.",
|
||||||
[]string{"luid"},
|
[]string{"luid", "device_id"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
c.gpuDedicatedSystemMemorySize = prometheus.NewDesc(
|
c.gpuDedicatedSystemMemorySize = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "dedicated_system_memory_size_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "dedicated_system_memory_size_bytes"),
|
||||||
"The size, in bytes, of memory that is dedicated from system memory.",
|
"The size, in bytes, of memory that is dedicated from system memory.",
|
||||||
[]string{"luid"},
|
[]string{"luid", "device_id"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
c.gpuDedicatedVideoMemorySize = prometheus.NewDesc(
|
c.gpuDedicatedVideoMemorySize = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "dedicated_video_memory_size_bytes"),
|
prometheus.BuildFQName(types.Namespace, Name, "dedicated_video_memory_size_bytes"),
|
||||||
"The size, in bytes, of memory that is dedicated from video memory.",
|
"The size, in bytes, of memory that is dedicated from video memory.",
|
||||||
[]string{"luid"},
|
[]string{"luid", "device_id"},
|
||||||
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", "luid", "phys", "eng", "engtype"},
|
[]string{"process_id", "luid", "device_id", "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{"luid", "phys"},
|
[]string{"luid", "device_id", "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{"luid", "phys"},
|
[]string{"luid", "device_id", "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{"luid", "phys"},
|
[]string{"luid", "device_id", "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{"luid", "phys"},
|
[]string{"luid", "device_id", "phys", "part"},
|
||||||
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{"luid", "phys"},
|
[]string{"luid", "device_id", "phys", "part"},
|
||||||
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", "luid", "phys"},
|
[]string{"process_id", "luid", "device_id", "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", "luid", "phys"},
|
[]string{"process_id", "luid", "device_id", "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", "luid", "phys"},
|
[]string{"process_id", "luid", "device_id", "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", "luid", "phys"},
|
[]string{"process_id", "luid", "device_id", "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", "luid", "phys"},
|
[]string{"process_id", "luid", "device_id", "phys"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -253,11 +259,39 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.gpuDeviceCache == nil {
|
if c.gpuDeviceCache == nil {
|
||||||
c.gpuDeviceCache = make(map[string]gdi32.GPUDevice)
|
c.gpuDeviceCache = make(map[string]gpuDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
luidKey := fmt.Sprintf("0x%08X_0x%08X", gpu.LUID.HighPart, gpu.LUID.LowPart)
|
luidKey := fmt.Sprintf("0x%08X_0x%08X", gpu.LUID.HighPart, gpu.LUID.LowPart)
|
||||||
c.gpuDeviceCache[luidKey] = gpu
|
|
||||||
|
deviceID := gpu.DeviceID
|
||||||
|
|
||||||
|
cfgmgr32Devs, err := cfgmgr32.GetDevicesInstanceIDs(gpu.DeviceID)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("failed to get device instance IDs for device ID %s: %w", gpu.DeviceID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfgmgr32Dev cfgmgr32.Device
|
||||||
|
|
||||||
|
for _, dev := range cfgmgr32Devs {
|
||||||
|
if dev.BusNumber == gpu.BusNumber && dev.DeviceNumber == gpu.DeviceNumber && dev.FunctionNumber == gpu.FunctionNumber {
|
||||||
|
cfgmgr32Dev = dev
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfgmgr32Dev.InstanceID == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("failed to find matching device for device ID %s", gpu.DeviceID))
|
||||||
|
} else {
|
||||||
|
deviceID = cfgmgr32Dev.InstanceID
|
||||||
|
}
|
||||||
|
|
||||||
|
c.gpuDeviceCache[luidKey] = gpuDevice{
|
||||||
|
gdi32: gpu,
|
||||||
|
cfgmgr32: cfgmgr32Dev,
|
||||||
|
ID: deviceID,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
@@ -298,31 +332,32 @@ func (c *Collector) collectGpuInfo(ch chan<- prometheus.Metric) {
|
|||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
1.0,
|
1.0,
|
||||||
luid,
|
luid,
|
||||||
gpu.AdapterString,
|
gpu.ID,
|
||||||
strconv.FormatInt(int64(gpu.BusNumber), 10),
|
gpu.gdi32.AdapterString,
|
||||||
strconv.FormatInt(int64(gpu.DeviceNumber), 10),
|
gpu.gdi32.BusNumber.String(),
|
||||||
strconv.FormatInt(int64(gpu.FunctionNumber), 10),
|
gpu.gdi32.DeviceNumber.String(),
|
||||||
|
gpu.gdi32.FunctionNumber.String(),
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuSharedSystemMemorySize,
|
c.gpuSharedSystemMemorySize,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
float64(gpu.SharedSystemMemorySize),
|
float64(gpu.gdi32.SharedSystemMemorySize),
|
||||||
luid,
|
luid, gpu.ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuDedicatedSystemMemorySize,
|
c.gpuDedicatedSystemMemorySize,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
float64(gpu.DedicatedSystemMemorySize),
|
float64(gpu.gdi32.DedicatedSystemMemorySize),
|
||||||
luid,
|
luid, gpu.ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuDedicatedVideoMemorySize,
|
c.gpuDedicatedVideoMemorySize,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
float64(gpu.DedicatedVideoMemorySize),
|
float64(gpu.gdi32.DedicatedVideoMemorySize),
|
||||||
luid,
|
luid, gpu.ID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -333,31 +368,20 @@ func (c *Collector) collectGpuEngineMetrics(ch chan<- prometheus.Metric) error {
|
|||||||
return fmt.Errorf("failed to collect GPU Engine perf data: %w", err)
|
return fmt.Errorf("failed to collect GPU Engine perf data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
runningTimeMap := make(map[PidPhysEngEngType]float64)
|
|
||||||
// Iterate over the GPU Engine perf data and aggregate the values.
|
// Iterate over the GPU Engine perf data and aggregate the values.
|
||||||
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 {
|
device, ok := c.gpuDeviceCache[instance.Luid]
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
key := PidPhysEngEngType{
|
|
||||||
Pid: instance.Pid,
|
|
||||||
Phys: instance.Phys,
|
|
||||||
Luid: instance.Luid,
|
|
||||||
Eng: instance.Eng,
|
|
||||||
Engtype: instance.Engtype,
|
|
||||||
}
|
|
||||||
runningTimeMap[key] += data.RunningTime / 10_000_000 // RunningTime is in 100ns units, convert to seconds.
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, runningTime := range runningTimeMap {
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuEngineRunningTime,
|
c.gpuEngineRunningTime,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
runningTime,
|
data.RunningTime/10_000_000,
|
||||||
key.Pid, key.Luid, key.Phys, key.Eng, key.Engtype,
|
instance.Pid, instance.Luid, device.ID, instance.Phys, instance.Eng, instance.Engtype,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,49 +394,33 @@ func (c *Collector) collectGpuAdapterMemoryMetrics(ch chan<- prometheus.Metric)
|
|||||||
return fmt.Errorf("failed to collect GPU Adapter Memory perf data: %w", err)
|
return fmt.Errorf("failed to collect GPU Adapter Memory perf data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dedicatedUsageMap := make(map[PidPhysEngEngType]float64)
|
|
||||||
sharedUsageMap := make(map[PidPhysEngEngType]float64)
|
|
||||||
totalCommittedMap := make(map[PidPhysEngEngType]float64)
|
|
||||||
|
|
||||||
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 {
|
device, ok := c.gpuDeviceCache[instance.Luid]
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
key := PidPhysEngEngType{
|
|
||||||
Pid: instance.Pid,
|
|
||||||
Luid: instance.Luid,
|
|
||||||
Phys: instance.Phys,
|
|
||||||
Eng: instance.Eng,
|
|
||||||
Engtype: instance.Engtype,
|
|
||||||
}
|
|
||||||
dedicatedUsageMap[key] += data.DedicatedUsage
|
|
||||||
sharedUsageMap[key] += data.SharedUsage
|
|
||||||
totalCommittedMap[key] += data.TotalCommitted
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, dedicatedUsage := range dedicatedUsageMap {
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuAdapterMemoryDedicatedUsage,
|
c.gpuAdapterMemoryDedicatedUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
dedicatedUsage,
|
data.DedicatedUsage,
|
||||||
key.Luid, key.Phys,
|
instance.Luid, device.ID, instance.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuAdapterMemorySharedUsage,
|
c.gpuAdapterMemorySharedUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
sharedUsageMap[key],
|
data.SharedUsage,
|
||||||
key.Luid, key.Phys,
|
instance.Luid, device.ID, instance.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuAdapterMemoryTotalCommitted,
|
c.gpuAdapterMemoryTotalCommitted,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
totalCommittedMap[key],
|
data.TotalCommitted,
|
||||||
key.Luid, key.Phys,
|
instance.Luid, device.ID, instance.Phys,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,29 +433,19 @@ 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[PidPhysEngEngType]float64)
|
|
||||||
|
|
||||||
for _, data := range c.gpuLocalAdapterMemoryPerfDataObject {
|
for _, data := range c.gpuLocalAdapterMemoryPerfDataObject {
|
||||||
instance := parseGPUCounterInstanceString(data.Name)
|
instance := parseGPUCounterInstanceString(data.Name)
|
||||||
|
|
||||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
device, ok := c.gpuDeviceCache[instance.Luid]
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
key := PidPhysEngEngType{
|
|
||||||
Luid: instance.Luid,
|
|
||||||
Phys: instance.Phys,
|
|
||||||
}
|
|
||||||
|
|
||||||
localAdapterMemoryMap[key] += data.LocalUsage
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, localUsage := range localAdapterMemoryMap {
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuLocalAdapterMemoryUsage,
|
c.gpuLocalAdapterMemoryUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
localUsage,
|
data.LocalUsage,
|
||||||
key.Luid, key.Phys,
|
instance.Luid, device.ID, instance.Phys, instance.Part,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,28 +458,19 @@ 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[PidPhysEngEngType]float64)
|
|
||||||
|
|
||||||
for _, data := range c.gpuNonLocalAdapterMemoryPerfDataObject {
|
for _, data := range c.gpuNonLocalAdapterMemoryPerfDataObject {
|
||||||
instance := parseGPUCounterInstanceString(data.Name)
|
instance := parseGPUCounterInstanceString(data.Name)
|
||||||
|
|
||||||
if _, ok := c.gpuDeviceCache[instance.Luid]; !ok {
|
device, ok := c.gpuDeviceCache[instance.Luid]
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
key := PidPhysEngEngType{
|
|
||||||
Luid: instance.Luid,
|
|
||||||
Phys: instance.Phys,
|
|
||||||
}
|
|
||||||
nonLocalAdapterMemoryMap[key] += data.NonLocalUsage
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, nonLocalUsage := range nonLocalAdapterMemoryMap {
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuNonLocalAdapterMemoryUsage,
|
c.gpuNonLocalAdapterMemoryUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
nonLocalUsage,
|
data.NonLocalUsage,
|
||||||
key.Luid, key.Phys,
|
instance.Luid, device.ID, instance.Phys, instance.Part,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,65 +483,47 @@ func (c *Collector) collectGpuProcessMemoryMetrics(ch chan<- prometheus.Metric)
|
|||||||
return fmt.Errorf("failed to collect GPU Process Memory perf data: %w", err)
|
return fmt.Errorf("failed to collect GPU Process Memory perf data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
processDedicatedUsageMap := make(map[PidPhys]float64)
|
|
||||||
processLocalUsageMap := make(map[PidPhys]float64)
|
|
||||||
processNonLocalUsageMap := make(map[PidPhys]float64)
|
|
||||||
processSharedUsageMap := make(map[PidPhys]float64)
|
|
||||||
processTotalCommittedMap := make(map[PidPhys]float64)
|
|
||||||
|
|
||||||
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 {
|
device, ok := c.gpuDeviceCache[instance.Luid]
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
key := PidPhys{
|
|
||||||
Pid: instance.Pid,
|
|
||||||
Luid: instance.Luid,
|
|
||||||
Phys: instance.Phys,
|
|
||||||
}
|
|
||||||
processDedicatedUsageMap[key] += data.DedicatedUsage
|
|
||||||
processLocalUsageMap[key] += data.LocalUsage
|
|
||||||
processNonLocalUsageMap[key] += data.NonLocalUsage
|
|
||||||
processSharedUsageMap[key] += data.SharedUsage
|
|
||||||
processTotalCommittedMap[key] += data.TotalCommitted
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, dedicatedUsage := range processDedicatedUsageMap {
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemoryDedicatedUsage,
|
c.gpuProcessMemoryDedicatedUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
dedicatedUsage,
|
data.DedicatedUsage,
|
||||||
key.Pid, key.Luid, key.Phys,
|
instance.Pid, instance.Luid, device.ID, instance.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemoryLocalUsage,
|
c.gpuProcessMemoryLocalUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
processLocalUsageMap[key],
|
data.LocalUsage,
|
||||||
key.Pid, key.Luid, key.Phys,
|
instance.Pid, instance.Luid, device.ID, instance.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemoryNonLocalUsage,
|
c.gpuProcessMemoryNonLocalUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
processNonLocalUsageMap[key],
|
data.NonLocalUsage,
|
||||||
key.Pid, key.Luid, key.Phys,
|
instance.Pid, instance.Luid, device.ID, instance.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemorySharedUsage,
|
c.gpuProcessMemorySharedUsage,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
processSharedUsageMap[key],
|
data.SharedUsage,
|
||||||
key.Pid, key.Luid, key.Phys,
|
instance.Pid, instance.Luid, device.ID, instance.Phys,
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.gpuProcessMemoryTotalCommitted,
|
c.gpuProcessMemoryTotalCommitted,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
processTotalCommittedMap[key],
|
data.TotalCommitted,
|
||||||
key.Pid, key.Luid, key.Phys,
|
instance.Pid, instance.Luid, device.ID, instance.Phys,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,26 +23,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
Pid string
|
Pid string
|
||||||
Luid string
|
Luid string
|
||||||
Phys string
|
DeviceID string
|
||||||
Eng string
|
Phys string
|
||||||
Engtype string
|
Eng string
|
||||||
Part string
|
Engtype string
|
||||||
|
Part string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PidPhys struct {
|
type PidPhys struct {
|
||||||
Pid string
|
Pid string
|
||||||
Luid string
|
Luid string
|
||||||
Phys string
|
DeviceID string
|
||||||
|
Phys string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PidPhysEngEngType struct {
|
type PidPhysEngEngType struct {
|
||||||
Pid string
|
Pid string
|
||||||
Luid string
|
Luid string
|
||||||
Phys string
|
DeviceID string
|
||||||
Eng string
|
Phys string
|
||||||
Engtype string
|
Eng string
|
||||||
|
Engtype string
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGPUCounterInstanceString(s string) Instance {
|
func parseGPUCounterInstanceString(s string) Instance {
|
||||||
|
|||||||
@@ -18,34 +18,63 @@
|
|||||||
package mssql
|
package mssql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
"github.com/prometheus-community/windows_exporter/internal/types"
|
"github.com/prometheus-community/windows_exporter/internal/types"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
type collectorInstance struct {
|
type collectorInstance struct {
|
||||||
instances *prometheus.GaugeVec
|
instances *prometheus.Desc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collector) buildInstance() error {
|
func (c *Collector) buildInstance() error {
|
||||||
c.instances = prometheus.NewGaugeVec(
|
c.instances = prometheus.NewDesc(
|
||||||
prometheus.GaugeOpts{
|
prometheus.BuildFQName(types.Namespace, Name, "instance_info"),
|
||||||
Namespace: types.Namespace,
|
"A metric with a constant '1' value labeled with mssql instance information",
|
||||||
Subsystem: Name,
|
|
||||||
Name: "instance_info",
|
|
||||||
Help: "A metric with a constant '1' value labeled with mssql instance information",
|
|
||||||
},
|
|
||||||
[]string{"edition", "mssql_instance", "patch", "version"},
|
[]string{"edition", "mssql_instance", "patch", "version"},
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, instance := range c.mssqlInstances {
|
|
||||||
c.instances.WithLabelValues(instance.edition, instance.name, instance.patchVersion, instance.majorVersion.String()).Set(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collector) collectInstance(ch chan<- prometheus.Metric) error {
|
func (c *Collector) collectInstance(ch chan<- prometheus.Metric) error {
|
||||||
c.instances.Collect(ch)
|
for _, instance := range c.mssqlInstances {
|
||||||
|
regKeyName := fmt.Sprintf(`Software\Microsoft\Microsoft SQL Server\%s\Setup`, instance.instanceName)
|
||||||
|
|
||||||
|
regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyName, registry.QUERY_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Debug(fmt.Sprintf("couldn't open registry %s:", regKeyName),
|
||||||
|
slog.Any("err", err),
|
||||||
|
)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
patchVersion, _, err := regKey.GetStringValue("PatchLevel")
|
||||||
|
_ = regKey.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Debug("couldn't get version from registry",
|
||||||
|
slog.Any("err", err),
|
||||||
|
)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.instances,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
1,
|
||||||
|
instance.edition,
|
||||||
|
instance.name,
|
||||||
|
patchVersion,
|
||||||
|
instance.majorVersion.String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ import (
|
|||||||
|
|
||||||
type mssqlInstance struct {
|
type mssqlInstance struct {
|
||||||
name string
|
name string
|
||||||
|
instanceName string
|
||||||
majorVersion mssqlServerMajorVersion
|
majorVersion mssqlServerMajorVersion
|
||||||
patchVersion string
|
|
||||||
edition string
|
edition string
|
||||||
isFirstInstance bool
|
isFirstInstance bool
|
||||||
}
|
}
|
||||||
@@ -54,14 +54,15 @@ func newMssqlInstance(key, name string) (mssqlInstance, error) {
|
|||||||
return mssqlInstance{}, fmt.Errorf("couldn't get version from registry: %w", err)
|
return mssqlInstance{}, fmt.Errorf("couldn't get version from registry: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instanceName := name
|
||||||
_, name, _ = strings.Cut(name, ".")
|
_, name, _ = strings.Cut(name, ".")
|
||||||
|
|
||||||
return mssqlInstance{
|
return mssqlInstance{
|
||||||
edition: edition,
|
edition: edition,
|
||||||
name: name,
|
name: name,
|
||||||
majorVersion: newMajorVersion(patchVersion),
|
majorVersion: newMajorVersion(patchVersion),
|
||||||
patchVersion: patchVersion,
|
|
||||||
isFirstInstance: key == "MSSQLSERVER",
|
isFirstInstance: key == "MSSQLSERVER",
|
||||||
|
instanceName: instanceName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import (
|
|||||||
"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"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Name = "performancecounter"
|
const Name = "performancecounter"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package performancecounter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Object struct {
|
type Object struct {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
"github.com/prometheus/common/expfmt"
|
"github.com/prometheus/common/expfmt"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Name = "textfile"
|
const Name = "textfile"
|
||||||
@@ -383,7 +384,7 @@ func scrapeFile(path string, logger *slog.Logger) ([]*dto.MetricFamily, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var parser expfmt.TextParser
|
parser := expfmt.NewTextParser(model.UTF8Validation)
|
||||||
|
|
||||||
r, encoding := utfbom.Skip(carriageReturnFilteringReader{r: file})
|
r, encoding := utfbom.Skip(carriageReturnFilteringReader{r: file})
|
||||||
if err = checkBOM(encoding); err != nil {
|
if err = checkBOM(encoding); err != nil {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/alecthomas/kingpin/v2"
|
"github.com/alecthomas/kingpin/v2"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector"
|
"github.com/prometheus-community/windows_exporter/pkg/collector"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// configFile represents the structure of the windows_exporter configuration file,
|
// configFile represents the structure of the windows_exporter configuration file,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Unmarshal good configuration file and confirm data is flattened correctly.
|
// Unmarshal good configuration file and confirm data is flattened correctly.
|
||||||
|
|||||||
92
internal/headers/cfgmgr32/cfgmgr32.go
Normal file
92
internal/headers/cfgmgr32/cfgmgr32.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// 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 cfgmgr32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/prometheus-community/windows_exporter/internal/headers/win32"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetDevicesInstanceIDs(deviceID string) ([]Device, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
listSize uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
deviceIDLWStr := win32.NewLPWSTR(deviceID)
|
||||||
|
|
||||||
|
err = CMGetDeviceIDListSize(deviceIDLWStr, &listSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listBuffer := make([]uint16, listSize)
|
||||||
|
|
||||||
|
err = CMGetDeviceIDList(deviceIDLWStr, listBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceInstanceIDs := win32.ParseMultiSz(listBuffer)
|
||||||
|
devices := make([]Device, 0, len(deviceInstanceIDs))
|
||||||
|
|
||||||
|
for _, deviceInstanceID := range deviceInstanceIDs {
|
||||||
|
var devNode *windows.Handle
|
||||||
|
|
||||||
|
err = CMLocateDevNode(&devNode, deviceInstanceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
busNumber uint32
|
||||||
|
deviceAddress uint32
|
||||||
|
propType uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
propLen := uint32(4)
|
||||||
|
|
||||||
|
err = CMGetDevNodeProperty(devNode, DEVPKEYDeviceBusNumber, &propType, unsafe.Pointer(&busNumber), &propLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if propType != DEVPROP_TYPE_UINT32 {
|
||||||
|
return nil, fmt.Errorf("unexpected property type: 0x%08X", propType)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = CMGetDevNodeProperty(devNode, DEVPKEYDeviceAddress, &propType, unsafe.Pointer(&deviceAddress), &propLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if propType != DEVPROP_TYPE_UINT32 {
|
||||||
|
return nil, fmt.Errorf("unexpected property type: 0x%08X", propType)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices = append(devices, Device{
|
||||||
|
InstanceID: windows.UTF16ToString(deviceInstanceID),
|
||||||
|
BusNumber: win32.UINT(busNumber),
|
||||||
|
DeviceNumber: win32.UINT(deviceAddress >> 16),
|
||||||
|
FunctionNumber: win32.UINT(deviceAddress & 0xFFFF),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices, nil
|
||||||
|
}
|
||||||
94
internal/headers/cfgmgr32/syscall.go
Normal file
94
internal/headers/cfgmgr32/syscall.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
// 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 cfgmgr32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/prometheus-community/windows_exporter/internal/headers/win32"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals
|
||||||
|
var (
|
||||||
|
cfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll")
|
||||||
|
|
||||||
|
procCMGetDeviceIDListW = cfgmgr32.NewProc("CM_Get_Device_ID_ListW")
|
||||||
|
procCMGetDeviceIDListSize = cfgmgr32.NewProc("CM_Get_Device_ID_List_SizeW")
|
||||||
|
procCMGetDevNodePropertyW = cfgmgr32.NewProc("CM_Get_DevNode_PropertyW")
|
||||||
|
procCMLocateDevNodeW = cfgmgr32.NewProc("CM_Locate_DevNodeW")
|
||||||
|
)
|
||||||
|
|
||||||
|
func CMGetDeviceIDListSize(filter *win32.LPWSTR, size *uint32) error {
|
||||||
|
ret, _, _ := procCMGetDeviceIDListSize.Call(
|
||||||
|
uintptr(unsafe.Pointer(size)),
|
||||||
|
filter.Pointer(),
|
||||||
|
uintptr(CM_GETIDLIST_FILTER_PRESENT|CM_GETIDLIST_FILTER_ENUMERATOR),
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret != CR_SUCCESS {
|
||||||
|
return fmt.Errorf("CMGetDeviceIDListSize failed: 0x%02X", ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CMGetDeviceIDList(filter *win32.LPWSTR, buf []uint16) error {
|
||||||
|
ret, _, _ := procCMGetDeviceIDListW.Call(
|
||||||
|
filter.Pointer(),
|
||||||
|
uintptr(unsafe.Pointer(&buf[0])),
|
||||||
|
uintptr(len(buf)),
|
||||||
|
uintptr(CM_GETIDLIST_FILTER_PRESENT|CM_GETIDLIST_FILTER_ENUMERATOR),
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret != CR_SUCCESS {
|
||||||
|
return fmt.Errorf("CMGetDeviceIDList failed: 0x%02X", ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CMLocateDevNode(devInst **windows.Handle, deviceID []uint16) error {
|
||||||
|
ret, _, _ := procCMLocateDevNodeW.Call(
|
||||||
|
uintptr(unsafe.Pointer(devInst)),
|
||||||
|
uintptr(unsafe.Pointer(&deviceID[0])),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret != CR_SUCCESS {
|
||||||
|
return fmt.Errorf("CMLocateDevNode failed: 0x%02X", ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CMGetDevNodeProperty(devInst *windows.Handle, propKey *DEVPROPKEY, propType *uint32, buf unsafe.Pointer, bufLen *uint32) error {
|
||||||
|
ret, _, _ := procCMGetDevNodePropertyW.Call(
|
||||||
|
uintptr(unsafe.Pointer(devInst)),
|
||||||
|
uintptr(unsafe.Pointer(propKey)),
|
||||||
|
uintptr(unsafe.Pointer(propType)),
|
||||||
|
uintptr(buf),
|
||||||
|
uintptr(unsafe.Pointer(bufLen)),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret != CR_SUCCESS {
|
||||||
|
return fmt.Errorf("CMGetDevNodeProperty failed: 0x%02X", ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
70
internal/headers/cfgmgr32/types.go
Normal file
70
internal/headers/cfgmgr32/types.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// 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 cfgmgr32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-ole/go-ole"
|
||||||
|
"github.com/prometheus-community/windows_exporter/internal/headers/win32"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Configuration Manager return codes
|
||||||
|
CR_SUCCESS = 0x00
|
||||||
|
|
||||||
|
// Filter flags
|
||||||
|
CM_GETIDLIST_FILTER_ENUMERATOR = 0x00000001
|
||||||
|
CM_GETIDLIST_FILTER_PRESENT = 0x00000100
|
||||||
|
|
||||||
|
DEVPROP_TYPE_UINT32 uint32 = 0x00000007
|
||||||
|
)
|
||||||
|
|
||||||
|
// DEVPROPKEY represents a device property key (GUID + pid)
|
||||||
|
type DEVPROPKEY struct {
|
||||||
|
FmtID ole.GUID
|
||||||
|
PID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
InstanceID string
|
||||||
|
BusNumber win32.UINT
|
||||||
|
DeviceNumber win32.UINT
|
||||||
|
FunctionNumber win32.UINT
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals
|
||||||
|
var (
|
||||||
|
// https://github.com/Infinidat/infi.devicemanager/blob/8be9ead6b04ff45c63d9e3bc70d82cceafb75c47/src/infi/devicemanager/setupapi/properties.py#L138C1-L143C34
|
||||||
|
DEVPKEYDeviceBusNumber = &DEVPROPKEY{
|
||||||
|
FmtID: ole.GUID{
|
||||||
|
Data1: 0xa45c254e,
|
||||||
|
Data2: 0xdf1c,
|
||||||
|
Data3: 0x4efd,
|
||||||
|
Data4: [8]byte{0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0},
|
||||||
|
},
|
||||||
|
PID: 23, // DEVPROP_TYPE_UINT32
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/Infinidat/infi.devicemanager/blob/8be9ead6b04ff45c63d9e3bc70d82cceafb75c47/src/infi/devicemanager/setupapi/properties.py#L187-L192
|
||||||
|
DEVPKEYDeviceAddress = &DEVPROPKEY{
|
||||||
|
FmtID: ole.GUID{
|
||||||
|
Data1: 0xa45c254e,
|
||||||
|
Data2: 0xdf1c,
|
||||||
|
Data3: 0x4efd,
|
||||||
|
Data4: [8]byte{0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0},
|
||||||
|
},
|
||||||
|
PID: 30, // DEVPROP_TYPE_UINT32
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -34,41 +34,12 @@ const (
|
|||||||
KMTQAITYPE_ADAPTERADDRESS = 6
|
KMTQAITYPE_ADAPTERADDRESS = 6
|
||||||
// KMTQAITYPE_ADAPTERREGISTRYINFO pPrivateDriverData points to a D3DKMT_ADAPTERREGISTRYINFO structure that contains registry information about the graphics adapter.
|
// KMTQAITYPE_ADAPTERREGISTRYINFO pPrivateDriverData points to a D3DKMT_ADAPTERREGISTRYINFO structure that contains registry information about the graphics adapter.
|
||||||
KMTQAITYPE_ADAPTERREGISTRYINFO = 8
|
KMTQAITYPE_ADAPTERREGISTRYINFO = 8
|
||||||
|
// KMTQAITYPE_PHYSICALADAPTERDEVICEIDS pPrivateDriverData points to a D3DKMT_QUERY_DEVICE_IDS structure that specifies the device ID(s) of the physical adapters. Supported starting with Windows 10 (WDDM 2.0).
|
||||||
|
KMTQAITYPE_PHYSICALADAPTERDEVICEIDS = 31
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNoGPUDevices = errors.New("no GPU devices found")
|
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) {
|
func GetGPUDevice(hAdapter D3DKMT_HANDLE) (GPUDevice, error) {
|
||||||
var gpuDevice GPUDevice
|
var gpuDevice GPUDevice
|
||||||
|
|
||||||
@@ -118,6 +89,18 @@ func GetGPUDevice(hAdapter D3DKMT_HANDLE) (GPUDevice, error) {
|
|||||||
|
|
||||||
gpuDevice.AdapterString = windows.UTF16ToString(info.AdapterString[:])
|
gpuDevice.AdapterString = windows.UTF16ToString(info.AdapterString[:])
|
||||||
|
|
||||||
|
var deviceIDs D3DKMT_QUERY_DEVICE_IDS
|
||||||
|
|
||||||
|
query.queryType = KMTQAITYPE_PHYSICALADAPTERDEVICEIDS
|
||||||
|
query.pPrivateDriverData = unsafe.Pointer(&deviceIDs)
|
||||||
|
query.privateDriverDataSize = uint32(unsafe.Sizeof(deviceIDs))
|
||||||
|
|
||||||
|
if err := D3DKMTQueryAdapterInfo(&query); err != nil && !errors.Is(err, windows.ERROR_FILE_NOT_FOUND) {
|
||||||
|
return gpuDevice, fmt.Errorf("D3DKMTQueryAdapterInfo (Device IDs) failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gpuDevice.DeviceID = formatPNPDeviceID(deviceIDs)
|
||||||
|
|
||||||
return gpuDevice, nil
|
return gpuDevice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +134,7 @@ func GetGPUDevices() ([]GPUDevice, error) {
|
|||||||
// Process each adapter
|
// Process each adapter
|
||||||
for i := range enumAdapters.NumAdapters {
|
for i := range enumAdapters.NumAdapters {
|
||||||
adapter := pAdapters[i]
|
adapter := pAdapters[i]
|
||||||
// Validate handle before using it
|
// Validate the handle before using it.
|
||||||
if adapter.HAdapter == 0 {
|
if adapter.HAdapter == 0 {
|
||||||
errs = append(errs, fmt.Errorf("adapter %d has null handle", i))
|
errs = append(errs, fmt.Errorf("adapter %d has null handle", i))
|
||||||
|
|
||||||
@@ -190,3 +173,13 @@ func GetGPUDevices() ([]GPUDevice, error) {
|
|||||||
|
|
||||||
return gpuDevices, nil
|
return gpuDevices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatPNPDeviceID(deviceIDs D3DKMT_QUERY_DEVICE_IDS) string {
|
||||||
|
return fmt.Sprintf("PCI\\VEN_%04X&DEV_%04X&SUBSYS_%04X%04X&REV_%02X",
|
||||||
|
uint16(deviceIDs.DeviceIds.VendorID),
|
||||||
|
uint16(deviceIDs.DeviceIds.DeviceID),
|
||||||
|
uint16(deviceIDs.DeviceIds.SubSystemID),
|
||||||
|
uint16(deviceIDs.DeviceIds.SubVendorID),
|
||||||
|
uint8(deviceIDs.DeviceIds.RevisionID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,9 +73,22 @@ type D3DKMT_ADAPTERADDRESS struct {
|
|||||||
FunctionNumber win32.UINT
|
FunctionNumber win32.UINT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type D3DKMT_QUERY_DEVICE_IDS struct {
|
||||||
|
PhysicalAdapterIndex win32.UINT
|
||||||
|
DeviceIds struct {
|
||||||
|
VendorID win32.UINT
|
||||||
|
DeviceID win32.UINT
|
||||||
|
SubVendorID win32.UINT
|
||||||
|
SubSystemID win32.UINT
|
||||||
|
RevisionID win32.UINT
|
||||||
|
BusType win32.UINT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type GPUDevice struct {
|
type GPUDevice struct {
|
||||||
AdapterString string
|
AdapterString string
|
||||||
LUID windows.LUID
|
LUID windows.LUID
|
||||||
|
DeviceID string
|
||||||
DedicatedVideoMemorySize uint64
|
DedicatedVideoMemorySize uint64
|
||||||
DedicatedSystemMemorySize uint64
|
DedicatedSystemMemorySize uint64
|
||||||
SharedSystemMemorySize uint64
|
SharedSystemMemorySize uint64
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
package win32
|
package win32
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@@ -32,8 +33,8 @@ type (
|
|||||||
LPWSTR struct {
|
LPWSTR struct {
|
||||||
*uint16
|
*uint16
|
||||||
}
|
}
|
||||||
ULONG = uint32 // ULONG is a 32-bit unsigned int in Win32
|
ULONG uint32 // ULONG is a 32-bit unsigned int in Win32
|
||||||
UINT = uint32 // UINT 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.
|
||||||
@@ -60,3 +61,7 @@ func (s *LPWSTR) Pointer() uintptr {
|
|||||||
func (s *LPWSTR) String() string {
|
func (s *LPWSTR) String() string {
|
||||||
return windows.UTF16PtrToString(s.uint16)
|
return windows.UTF16PtrToString(s.uint16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UINT) String() string {
|
||||||
|
return strconv.FormatUint(uint64(*u), 10)
|
||||||
|
}
|
||||||
|
|||||||
55
internal/headers/win32/utils.go
Normal file
55
internal/headers/win32/utils.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// 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 win32
|
||||||
|
|
||||||
|
// ParseMultiSz splits a UTF-16 encoded MULTI_SZ buffer (Windows style) into
|
||||||
|
// individual UTF-16 string slices.
|
||||||
|
//
|
||||||
|
// A MULTI_SZ buffer is a sequence of UTF-16 strings separated by single null
|
||||||
|
// terminators (0x0000) and terminated by an extra null (i.e., two consecutive
|
||||||
|
// nulls) to mark the end of the list.
|
||||||
|
//
|
||||||
|
// Example layout in memory (UTF-16):
|
||||||
|
//
|
||||||
|
// "foo\0bar\0baz\0\0"
|
||||||
|
//
|
||||||
|
// Given such a []uint16, this function returns a [][]uint16 where each inner
|
||||||
|
// slice is one null-terminated string segment without the trailing null.
|
||||||
|
//
|
||||||
|
// The returned slices reference the original buffer (no copying).
|
||||||
|
func ParseMultiSz(buf []uint16) [][]uint16 {
|
||||||
|
var (
|
||||||
|
result [][]uint16
|
||||||
|
start int
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := range buf {
|
||||||
|
if buf[i] == 0 {
|
||||||
|
// Found a null terminator.
|
||||||
|
if i == start {
|
||||||
|
// Two consecutive nulls → end of list.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append current string slice (excluding null).
|
||||||
|
result = append(result, buf[start:i])
|
||||||
|
// Move start to next character after null.
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -117,7 +117,7 @@ func (c *MetricsHTTPHandler) getScrapeTimeout(logger *slog.Logger, r *http.Reque
|
|||||||
|
|
||||||
timeoutSeconds -= c.options.TimeoutMargin
|
timeoutSeconds -= c.options.TimeoutMargin
|
||||||
|
|
||||||
return time.Duration(timeoutSeconds) * time.Second
|
return time.Duration(timeoutSeconds*1e9) * time.Nanosecond
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MetricsHTTPHandler) handlerFactory(logger *slog.Logger, scrapeTimeout time.Duration, requestedCollectors []string) (http.Handler, error) {
|
func (c *MetricsHTTPHandler) handlerFactory(logger *slog.Logger, scrapeTimeout time.Duration, requestedCollectors []string) (http.Handler, error) {
|
||||||
|
|||||||
@@ -1,181 +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 mi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
// operationUnmarshalCallbacksInstanceResult registers a global callback function.
|
|
||||||
// The amount of system callbacks is limited to 2000.
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals
|
|
||||||
var operationUnmarshalCallbacksInstanceResult = sync.OnceValue[uintptr](func() uintptr {
|
|
||||||
// Workaround for a deadlock issue in go.
|
|
||||||
// Ref: https://github.com/golang/go/issues/55015
|
|
||||||
go time.Sleep(time.Duration(math.MaxInt64))
|
|
||||||
|
|
||||||
return windows.NewCallback(func(
|
|
||||||
operation *Operation,
|
|
||||||
callbacks *OperationUnmarshalCallbacks,
|
|
||||||
instance *Instance,
|
|
||||||
moreResults Boolean,
|
|
||||||
instanceResult ResultError,
|
|
||||||
errorMessageUTF16 *uint16,
|
|
||||||
errorDetails *Instance,
|
|
||||||
_ uintptr,
|
|
||||||
) uintptr {
|
|
||||||
if moreResults == False {
|
|
||||||
defer operation.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return callbacks.InstanceResult(operation, instance, moreResults, instanceResult, errorMessageUTF16, errorDetails)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
type OperationUnmarshalCallbacks struct {
|
|
||||||
dst any
|
|
||||||
dv reflect.Value
|
|
||||||
errCh chan<- error
|
|
||||||
|
|
||||||
elemType reflect.Type
|
|
||||||
elemValue reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUnmarshalOperationsCallbacks(dst any, errCh chan<- error) (*OperationCallbacks[OperationUnmarshalCallbacks], error) {
|
|
||||||
dv := reflect.ValueOf(dst)
|
|
||||||
if dv.Kind() != reflect.Ptr || dv.IsNil() {
|
|
||||||
return nil, ErrInvalidEntityType
|
|
||||||
}
|
|
||||||
|
|
||||||
dv = dv.Elem()
|
|
||||||
|
|
||||||
elemType := dv.Type().Elem()
|
|
||||||
elemValue := reflect.ValueOf(reflect.New(elemType).Interface()).Elem()
|
|
||||||
|
|
||||||
if dv.Kind() != reflect.Slice || elemType.Kind() != reflect.Struct {
|
|
||||||
return nil, ErrInvalidEntityType
|
|
||||||
}
|
|
||||||
|
|
||||||
dv.Set(reflect.MakeSlice(dv.Type(), 0, 0))
|
|
||||||
|
|
||||||
return &OperationCallbacks[OperationUnmarshalCallbacks]{
|
|
||||||
CallbackContext: &OperationUnmarshalCallbacks{
|
|
||||||
errCh: errCh,
|
|
||||||
dst: dst,
|
|
||||||
dv: dv,
|
|
||||||
elemType: elemType,
|
|
||||||
elemValue: elemValue,
|
|
||||||
},
|
|
||||||
InstanceResult: operationUnmarshalCallbacksInstanceResult(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *OperationUnmarshalCallbacks) InstanceResult(
|
|
||||||
_ *Operation,
|
|
||||||
instance *Instance,
|
|
||||||
moreResults Boolean,
|
|
||||||
instanceResult ResultError,
|
|
||||||
errorMessageUTF16 *uint16,
|
|
||||||
_ *Instance,
|
|
||||||
) uintptr {
|
|
||||||
defer func() {
|
|
||||||
if moreResults == False {
|
|
||||||
close(o.errCh)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if !errors.Is(instanceResult, MI_RESULT_OK) {
|
|
||||||
o.errCh <- fmt.Errorf("%w: %s", instanceResult, windows.UTF16PtrToString(errorMessageUTF16))
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if instance == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
counter, err := instance.GetElementCount()
|
|
||||||
if err != nil {
|
|
||||||
o.errCh <- fmt.Errorf("failed to get element count: %w", err)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if counter == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range o.elemType.NumField() {
|
|
||||||
field := o.elemValue.Field(i)
|
|
||||||
|
|
||||||
// Check if the field has an `mi` tag
|
|
||||||
miTag := o.elemType.Field(i).Tag.Get("mi")
|
|
||||||
if miTag == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
element, err := instance.GetElement(miTag)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, MI_RESULT_NO_SUCH_PROPERTY) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
o.errCh <- fmt.Errorf("failed to get element %s: %w", miTag, err)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
switch element.valueType {
|
|
||||||
case ValueTypeBOOLEAN:
|
|
||||||
field.SetBool(element.value == 1)
|
|
||||||
case ValueTypeUINT8, ValueTypeUINT16, ValueTypeUINT32, ValueTypeUINT64:
|
|
||||||
field.SetUint(uint64(element.value))
|
|
||||||
case ValueTypeSINT8, ValueTypeSINT16, ValueTypeSINT32, ValueTypeSINT64:
|
|
||||||
field.SetInt(int64(element.value))
|
|
||||||
case ValueTypeSTRING:
|
|
||||||
if element.value == 0 {
|
|
||||||
// value is null
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the UTF-16 string to a Go string
|
|
||||||
stringValue := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(element.value)))
|
|
||||||
|
|
||||||
field.SetString(stringValue)
|
|
||||||
case ValueTypeREAL32, ValueTypeREAL64:
|
|
||||||
field.SetFloat(float64(element.value))
|
|
||||||
default:
|
|
||||||
o.errCh <- fmt.Errorf("unsupported value type: %d", element.valueType)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
o.dv.Set(reflect.Append(o.dv, o.elemValue))
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||||
|
"github.com/prometheus-community/windows_exporter/internal/utils/testutils"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
type win32Process struct {
|
type win32Process struct {
|
||||||
@@ -234,3 +236,54 @@ func Test_MI_Query_Unmarshal(t *testing.T) {
|
|||||||
err = application.Close()
|
err = application.Close()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_MI_FD_Leak(t *testing.T) {
|
||||||
|
application, err := mi.ApplicationInitialize()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, application)
|
||||||
|
|
||||||
|
session, err := application.NewSession(nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, session)
|
||||||
|
|
||||||
|
currentFileHandle, err := testutils.GetProcessHandleCount(windows.CurrentProcess())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Current File Handle Count: ", currentFileHandle)
|
||||||
|
|
||||||
|
queryPrinter, err := mi.NewQuery("SELECT Name FROM Win32_Process")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for range 300 {
|
||||||
|
var processes []win32Process
|
||||||
|
|
||||||
|
err := session.Query(&processes, mi.NamespaceRootCIMv2, queryPrinter)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Current File Handle Count: ", currentFileHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Current File Handle Count: ", currentFileHandle)
|
||||||
|
|
||||||
|
err = session.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Current File Handle Count: ", currentFileHandle)
|
||||||
|
|
||||||
|
err = application.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Current File Handle Count: ", currentFileHandle)
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,11 +41,7 @@ var OperationOptionsTimeout = UTF16PtrFromString[*uint16]("__MI_OPERATIONOPTIONS
|
|||||||
type OperationFlags uint32
|
type OperationFlags uint32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OperationFlagsDefaultRTTI OperationFlags = 0x0000
|
|
||||||
OperationFlagsBasicRTTI OperationFlags = 0x0002
|
|
||||||
OperationFlagsNoRTTI OperationFlags = 0x0400
|
|
||||||
OperationFlagsStandardRTTI OperationFlags = 0x0800
|
OperationFlagsStandardRTTI OperationFlags = 0x0800
|
||||||
OperationFlagsFullRTTI OperationFlags = 0x0004
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Operation represents an operation.
|
// Operation represents an operation.
|
||||||
@@ -123,7 +119,7 @@ func (o *Operation) Cancel() error {
|
|||||||
return ErrNotInitialized
|
return ErrNotInitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
r0, _, _ := syscall.SyscallN(o.ft.Close, uintptr(unsafe.Pointer(o)), 0)
|
r0, _, _ := syscall.SyscallN(o.ft.Cancel, uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
|
||||||
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
|
if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) {
|
||||||
return result
|
return result
|
||||||
@@ -229,10 +225,14 @@ func (o *Operation) Unmarshal(dst any) error {
|
|||||||
field.SetInt(int64(element.value))
|
field.SetInt(int64(element.value))
|
||||||
case ValueTypeSTRING:
|
case ValueTypeSTRING:
|
||||||
if element.value == 0 {
|
if element.value == 0 {
|
||||||
return fmt.Errorf("%s: invalid pointer: value is nil", miTag)
|
field.SetString("") // Set empty string for nil values
|
||||||
|
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the UTF-16 string to a Go string
|
// Convert uintptr to *uint16 for Windows UTF-16 string
|
||||||
|
// This is safe because element.value comes directly from Windows MI API
|
||||||
|
//goland:noinspection GoVetUnsafePointer
|
||||||
stringValue := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(element.value)))
|
stringValue := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(element.value)))
|
||||||
|
|
||||||
field.SetString(stringValue)
|
field.SetString(stringValue)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ package mi
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"reflect"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -200,13 +200,22 @@ func (s *Session) QueryUnmarshal(dst any,
|
|||||||
operationOptions = s.defaultOperationOptions
|
operationOptions = s.defaultOperationOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
errCh := make(chan error, 1)
|
dv := reflect.ValueOf(dst)
|
||||||
|
if dv.Kind() != reflect.Ptr || dv.IsNil() {
|
||||||
operationCallbacks, err := NewUnmarshalOperationsCallbacks(dst, errCh)
|
return ErrInvalidEntityType
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dv = dv.Elem()
|
||||||
|
|
||||||
|
elemType := dv.Type().Elem()
|
||||||
|
elemValue := reflect.ValueOf(reflect.New(elemType).Interface()).Elem()
|
||||||
|
|
||||||
|
if dv.Kind() != reflect.Slice || elemType.Kind() != reflect.Struct {
|
||||||
|
return ErrInvalidEntityType
|
||||||
|
}
|
||||||
|
|
||||||
|
dv.Set(reflect.MakeSlice(dv.Type(), 0, 0))
|
||||||
|
|
||||||
r0, _, _ := syscall.SyscallN(
|
r0, _, _ := syscall.SyscallN(
|
||||||
s.ft.QueryInstances,
|
s.ft.QueryInstances,
|
||||||
uintptr(unsafe.Pointer(s)),
|
uintptr(unsafe.Pointer(s)),
|
||||||
@@ -215,7 +224,7 @@ func (s *Session) QueryUnmarshal(dst any,
|
|||||||
uintptr(unsafe.Pointer(namespaceName)),
|
uintptr(unsafe.Pointer(namespaceName)),
|
||||||
uintptr(unsafe.Pointer(queryDialect)),
|
uintptr(unsafe.Pointer(queryDialect)),
|
||||||
uintptr(unsafe.Pointer(queryExpression)),
|
uintptr(unsafe.Pointer(queryExpression)),
|
||||||
uintptr(unsafe.Pointer(operationCallbacks)),
|
0,
|
||||||
uintptr(unsafe.Pointer(operation)),
|
uintptr(unsafe.Pointer(operation)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -223,25 +232,79 @@ func (s *Session) QueryUnmarshal(dst any,
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := make([]error, 0)
|
defer func() {
|
||||||
|
_ = operation.Close()
|
||||||
// We need an active go routine to prevent a
|
}()
|
||||||
// fatal error: all goroutines are asleep - deadlock!
|
|
||||||
// ref: https://github.com/golang/go/issues/55015
|
|
||||||
// go time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err, ok := <-errCh; err != nil {
|
instance, moreResults, err := operation.GetInstance()
|
||||||
errs = append(errs, err)
|
if err != nil {
|
||||||
} else if !ok {
|
return fmt.Errorf("failed to get instance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if instance == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
counter, err := instance.GetElementCount()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get element count: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if counter == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range elemType.NumField() {
|
||||||
|
field := elemValue.Field(i)
|
||||||
|
|
||||||
|
// Check if the field has an `mi` tag
|
||||||
|
miTag := elemType.Field(i).Tag.Get("mi")
|
||||||
|
if miTag == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
element, err := instance.GetElement(miTag)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, MI_RESULT_NO_SUCH_PROPERTY) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("failed to get element %s: %w", miTag, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch element.valueType {
|
||||||
|
case ValueTypeBOOLEAN:
|
||||||
|
field.SetBool(element.value == 1)
|
||||||
|
case ValueTypeUINT8, ValueTypeUINT16, ValueTypeUINT32, ValueTypeUINT64:
|
||||||
|
field.SetUint(uint64(element.value))
|
||||||
|
case ValueTypeSINT8, ValueTypeSINT16, ValueTypeSINT32, ValueTypeSINT64:
|
||||||
|
field.SetInt(int64(element.value))
|
||||||
|
case ValueTypeSTRING:
|
||||||
|
if element.value == 0 {
|
||||||
|
// value is null
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the UTF-16 string to a Go string
|
||||||
|
stringValue := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(element.value)))
|
||||||
|
|
||||||
|
field.SetString(stringValue)
|
||||||
|
case ValueTypeREAL32, ValueTypeREAL64:
|
||||||
|
field.SetFloat(float64(element.value))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported value type: %d", element.valueType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dv.Set(reflect.Append(dv, elemValue))
|
||||||
|
|
||||||
|
if !moreResults {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeepAlive is used to ensure that the callbacks are not garbage collected before the operation is closed.
|
return nil
|
||||||
runtime.KeepAlive(operationCallbacks.CallbackContext)
|
|
||||||
|
|
||||||
return errors.Join(errs...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query queries for a set of instances based on a query expression.
|
// Query queries for a set of instances based on a query expression.
|
||||||
|
|||||||
44
internal/utils/testutils/handle.go
Normal file
44
internal/utils/testutils/handle.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// 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 testutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
|
||||||
|
procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount")
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetProcessHandleCount(handle windows.Handle) (uint32, error) {
|
||||||
|
var count uint32
|
||||||
|
|
||||||
|
r1, _, err := procGetProcessHandleCount.Call(
|
||||||
|
uintptr(handle),
|
||||||
|
uintptr(unsafe.Pointer(&count)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if r1 != 1 {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
@@ -198,6 +198,15 @@ func (c *Collection) Enable(enabledCollectors []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable removes all collectors that are listed in disabledCollectors.
|
||||||
|
func (c *Collection) Disable(disabledCollectors []string) {
|
||||||
|
for name := range c.collectors {
|
||||||
|
if slices.Contains(disabledCollectors, name) {
|
||||||
|
delete(c.collectors, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build To be called by the exporter for collector initialization.
|
// Build To be called by the exporter for collector initialization.
|
||||||
// Instead, fail fast, it will try to build all collectors and return all errors.
|
// Instead, fail fast, it will try to build all collectors and return all errors.
|
||||||
// errors are joined with errors.Join.
|
// errors are joined with errors.Join.
|
||||||
|
|||||||
Reference in New Issue
Block a user