VK_EXT_device_fault
本文档概述了允许应用程序在设备丢失后查询额外诊断信息的功能。
1. 问题陈述
设备丢失错误可能难以诊断。它们可能由多种问题触发,包括无效的应用程序行为、驱动程序错误以及硬件的物理故障或移除。虽然建议将 Vulkan 验证层作为诊断大多数 API 使用问题的首选步骤,但它们无法解决设备丢失的所有可能原因。
本提案旨在为应用程序开发人员提供可能有助于诊断此类错误的额外信息。
2. 解决方案空间
已考虑了以下几种选择
-
提供基础扩展以支持崩溃后分析工具的开发
-
开发旨在将故障归因于各个 Vulkan 对象的扩展或工具
-
依赖于各个供应商的工具和扩展
本提案侧重于第一种选择。它代表了一个部分解决方案,需要进一步扩展才能完全启用崩溃后分析工具。
3. 提案
3.1. API 特性
VK_EXT_device_fault
扩展公开了以下特性
typedef struct VkPhysicalDeviceFaultFeaturesEXT {
VkStructureType sType;
void* pNext;
VkBool32 deviceFault;
VkBool32 deviceFaultVendorBinary;
} VkPhysicalDeviceFaultFeaturesEXT;
deviceFault
是启用此扩展功能的主要特性,如果支持此扩展,则必须支持此特性。
deviceFaultVendorBinary
是一个可选特性,它支持供应商特定的二进制崩溃转储,这些崩溃转储可以通过外部供应商工具进行解释。
3.2. 查询故障信息
设备丢失后,应用程序可以通过调用 vkGetDeviceFaultInfoEXT
查询额外的诊断信息。
typedef struct VkDeviceFaultCountsEXT {
VkStructureType sType;
void* pNext;
uint32_t addressInfoCount;
uint32_t vendorInfoCount;
VkDeviceSize vendorBinarySize;
} VkDeviceFaultCountsEXT;
typedef struct VkDeviceFaultInfoEXT {
VkStructureType sType;
void* pNext;
char description[VK_MAX_DESCRIPTION_SIZE];
VkDeviceFaultAddressInfoEXT* pAddressInfos;
VkDeviceFaultVendorInfoEXT* pVendorInfos;
void* pVendorBinaryData;
} VkDeviceFaultInfoEXT;
VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceFaultInfoEXT(
VkDevice device,
VkDeviceFaultCountsEXT* pFaultCounts,
VkDeviceFaultInfoEXT* pFaultInfo);
vkGetDeviceFaultInfoEXT
的签名旨在镜像现有查询函数的设计,其中第二个参数 (pFaultCounts
) 表示输出数组的大小或写入的结果数。但是,设备故障信息需要多个输出数组。因此,使用 VkDeviceFaultCountsEXT
结构来一次指定多个数组的大小。
// Query number of available results
VkDeviceFaultCountsEXT faultCounts{};
faultCounts.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT;
vkGetDeviceFaultInfoEXT(device, &faultCounts, NULL);
// Allocate output arrays and query fault data
VkDeviceFaultInfoEXT faultInfo{}
info.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT;
info.pAddressInfos = (VkDeviceFaultAddressInfoEXT*) malloc(sizeof(VkDeviceFaultAddressInfoEXT) *
faultCounts.addressInfoCount);
info.pVendorInfos = (VkDeviceFaultVendorInfoEXT*) malloc(sizeof(VkDeviceFaultVendorInfoEXT) *
faultCounts.vendorInfoCount);
info.pVendorBinaryData = malloc(faultCounts.vendorBinarySize);
vkGetDeviceFaultInfoEXT(device, &faultCounts, &faultInfo);
3.3. 解释 GPU 虚拟地址
实现可能会返回有关由无效内存访问生成的页面错误以及指示故障发生时正在执行的指令的指令指针的信息。
typedef enum VkDeviceFaultAddressTypeEXT {
VK_DEVICE_FAULT_ADDRESS_TYPE_NONE_EXT = 0,
VK_DEVICE_FAULT_ADDRESS_TYPE_READ_INVALID_EXT = 1,
VK_DEVICE_FAULT_ADDRESS_TYPE_WRITE_INVALID_EXT = 2,
VK_DEVICE_FAULT_ADDRESS_TYPE_EXECUTE_INVALID_EXT = 3,
VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_UNKNOWN_EXT = 4,
VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_INVALID_EXT = 5,
VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_FAULT_EXT = 6,
VK_DEVICE_FAULT_ADDRESS_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
} VkDeviceFaultAddressTypeEXT;
typedef struct VkDeviceFaultAddressInfoEXT {
VkDeviceFaultAddressTypeEXT addressType;
VkDeviceAddress reportedAddress;
VkDeviceSize addressPrecision;
} VkDeviceFaultAddressInfoEXT;
页面地址和指令指针以 GPU 虚拟地址的形式报告,可能需要额外的扩展或供应商工具才能将这些扩展与各个 Vulkan 对象相关联。
实现可能只能以有限的精度报告这些地址。 reportedAddress
和 addressPrecision
的组合允许计算地址的可能范围,例如
lower_address = (pInfo->reportedAddress & ~(pInfo->addressPrecision-1))
upper_address = (pInfo->reportedAddress | (pInfo->addressPrecision-1))
|
3.4. 厂商二进制崩溃转储
可选地,实现还可以支持生成包含额外诊断信息的厂商特定二进制 blob。所有厂商特定的二进制文件都将以一个公共头部开始。二进制 blob 的其余部分内容是厂商特定的,需要厂商特定的文档或工具来解释。
typedef struct VkDeviceFaultVendorBinaryHeaderVersionOneEXT {
uint32_t headerSize;
VkDeviceFaultVendorBinaryHeaderVersionEXT headerVersion;
uint32_t vendorID;
uint32_t deviceID;
uint32_t driverVersion;
uint8_t pipelineCacheUUID[VK_UUID_SIZE];
uint32_t applicationNameOffset;
uint32_t applicationVersion;
uint32_t engineNameOffset;
} VkDeviceFaultVendorBinaryHeaderVersionOneEXT;
4. 问题
1) vkGetDeviceFaultInfoEXT
是否应该返回多个错误?
已解决:否。此扩展仅旨在识别单个错误作为设备丢失的可能原因,而不是维护多个错误的日志。我们预计,在 GPU 确实遇到多个错误的情况下,这些错误很可能是重复的,例如由同一缺陷代码的并行执行引起的错误。
2) 是否可以在设备丢失之前调用 vkGetDeviceFaultInfoEXT
?
已解决:否。VulkanSC 中的 VK_KHR_fault_handling
支持与此等效的功能,但 VK_KHR_fault_handling
旨在解决不同的用例,即在设备丢失之前轮询错误日志,以便采取补救措施。
3) 页面错误是否需要报告实际访问的地址,还是我们应该允许报告页面地址?
已解决:一些 IHV 硬件报告页面错误时,使用页面对齐,或者使用某些其他硬件单元相关的粒度,而不是触发错误的精确地址。所有地址都以硬件单元相关的粒度报告,并附带相关的精度指示符。此信息可用于计算包含触发错误的原始地址的地址范围。
4) 我们应该如何报告多个管道可能导致错误的情况?
已解决:在无法将错误归因于单个唯一管道的情况下,报告可能的候选管道集是可取的。
5) 页面错误和指令地址信息结构具有相似的结构。它们应该合并吗?
已解决:是的。这些已合并为 VkDeviceFaultAddressInfoEXT
,以减少 API 表面积。
6) 对于厂商特定的错误,实现者应该如何处理可扩展性?他们应该依赖 pNext
链,还是扩展应该引入一个通用结构来在基本结构中返回厂商错误代码和人类可读的描述?
已解决:在适用的情况下,实现者应使用通用的 VkDeviceFaultVendorInfoEXT
结构,并在其不足的情况下回退到扩展 pNext
链。在需要 pNext
链的情况下,厂商应定制其人类可读的错误描述,以建议开发人员可能有其他信息可用。