Compare commits

...

18 Commits

Author SHA1 Message Date
renovate[bot]
fcf21bb600 fix(deps): update module github.com/prometheus/client_golang to v1.23.1 (#2199)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 21:37:06 +00:00
renovate[bot]
cd5f136079 chore(deps): update module google.golang.org/protobuf to v1.36.8 (#2198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 18:52:32 +02:00
Jan-Otto Kröpke
4171ec17a5 chore: switch to go.yaml.in/yaml/v3 (#2196) 2025-09-04 23:50:27 +02:00
Karthik Panjaje
6289499dee docs: Fixed HTTP request metrics documentation (#2192) 2025-08-31 14:41:40 +00:00
Jan-Otto Kröpke
79917893d1 installer: set failureflag for Windows service (#2191) 2025-08-29 21:57:28 +02:00
Jan-Otto Kröpke
0b8a257b31 gpu: add device id label (#2186) 2025-08-28 06:36:10 +02:00
Jan-Otto Kröpke
71cedbc4d0 mi: remove callbacks (#2188) 2025-08-26 21:04:56 +02:00
Jan-Otto Kröpke
c8a4cb3806 mssql: expose correct patch level without restart (#2187) 2025-08-26 20:52:09 +02:00
Jan-Otto Kröpke
558629dff5 chore: update to go 1.25 (#2185) 2025-08-24 14:27:00 +02:00
Jan-Otto Kröpke
5a8ebf0c44 collector: support sub-second timeout values. (#2181) 2025-08-15 23:55:24 +02:00
PrometheusBot
acbabb926d Synchronize common files from prometheus/prometheus (#2180) 2025-08-15 20:34:45 +02:00
renovate[bot]
e37392c00b chore(deps): update dependency golangci/golangci-lint to v2.4.0 (#2179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 16:43:04 +02:00
renovate[bot]
00d86ba792 chore(deps): update actions/checkout action to v4.3.0 (#2178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 16:42:52 +02:00
renovate[bot]
691f64f5cc fix(deps): update golang.org/x/ (#2170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 15:39:40 +02:00
renovate[bot]
19999dea49 chore(deps): update docker/login-action action to v3.5.0 (#2169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 15:39:24 +02:00
renovate[bot]
c2df4d7514 chore(deps): update actions/download-artifact action to v5 (#2171)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 15:48:04 +02:00
renovate[bot]
8937a5ac91 chore(deps): update module google.golang.org/protobuf to v1.36.7 (#2168)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 15:30:46 +02:00
Jan-Otto Kröpke
930130f58a collector: Add disable flag (#2165)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-07 08:58:29 +02:00
35 changed files with 814 additions and 464 deletions

View File

@@ -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"

View File

@@ -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)

View File

@@ -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 }}

View File

@@ -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
View File

@@ -6,6 +6,7 @@ output/
.vscode .vscode
*.syso *.syso
installer/*.msi installer/*.msi
installer/*.log
installer/*.wixpdb installer/*.wixpdb
local/ local/

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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:**

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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="&quot;[WindowsFolder]System32\sc.exe&quot; failureflag &quot;windows_exporter&quot; 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"

View File

@@ -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,
) )
} }

View File

@@ -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 {

View File

@@ -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
} }

View File

@@ -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
} }

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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,

View 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.

View 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
}

View 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
}

View 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
}
)

View File

@@ -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),
)
}

View File

@@ -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

View File

@@ -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)
}

View 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
}

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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.

View 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
}

View File

@@ -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.