mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-08 05:56:37 +00:00
186 lines
6.0 KiB
Go
186 lines
6.0 KiB
Go
// 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),
|
|
)
|
|
}
|