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