| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2021 The Khronos Group Inc. |
| * |
| * 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. |
| * |
| *//*! |
| * \file |
| * \brief VK_EXT_device_fault extension tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktPostmortemDeviceFaultTests.hpp" |
| #include "vktCustomInstancesDevices.hpp" |
| |
| #include "deStringUtil.hpp" |
| #include "vkDefs.hpp" |
| #include "vktTestCase.hpp" |
| #include "vktTestGroupUtil.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuCommandLine.hpp" |
| |
| #include <functional> |
| #include <limits> |
| #include <sstream> |
| #include <utility> |
| #include <vector> |
| |
| #define ARRAY_LENGTH(a_) std::extent<decltype(a_)>::value |
| |
| #ifndef VK_EXT_DEVICE_FAULT_EXTENSION_NAME |
| #define VK_EXT_DEVICE_FAULT_EXTENSION_NAME "VK_EXT_device_fault" |
| #else |
| // This should never have happened |
| // static_assert(false, "Trying to redefine VK_EXT_DEVICE_FAULT_EXTENSION_NAME"); |
| #endif |
| |
| namespace vkt |
| { |
| namespace postmortem |
| { |
| namespace |
| { |
| using namespace vk; |
| using namespace tcu; |
| |
| enum class TestType |
| { |
| Fake, |
| Real, |
| CustomDevice |
| }; |
| |
| struct TestParams |
| { |
| TestType type; |
| }; |
| |
| class DeviceFaultCase : public TestCase |
| { |
| public: |
| DeviceFaultCase (TestContext& testCtx, |
| const std::string& name, |
| const TestParams& params) |
| : TestCase (testCtx, name, std::string()) |
| , m_params (params) {} |
| virtual ~DeviceFaultCase () = default; |
| virtual TestInstance* createInstance (Context& context) const override; |
| virtual void checkSupport (Context& context) const override; |
| private: |
| const TestParams m_params; |
| }; |
| |
| class DeviceFaultInstance : public TestInstance |
| { |
| public: |
| DeviceFaultInstance (Context& context, const TestParams& params) |
| : TestInstance (context) |
| , m_params (params) {} |
| virtual ~DeviceFaultInstance() = default; |
| |
| virtual TestStatus iterate (void) override; |
| void log (const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, |
| const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, |
| const std::vector<deUint8>& vendorBinaryData) const; |
| private: |
| const TestParams m_params; |
| }; |
| |
| class DeviceFaultCustomInstance : public TestInstance |
| { |
| public: |
| DeviceFaultCustomInstance (Context& context) |
| : TestInstance (context) {} |
| virtual ~DeviceFaultCustomInstance () = default; |
| |
| virtual TestStatus iterate (void) override; |
| }; |
| |
| TestInstance* DeviceFaultCase::createInstance (Context& context) const |
| { |
| TestInstance* instance = nullptr; |
| if (m_params.type == TestType::CustomDevice) |
| instance = new DeviceFaultCustomInstance(context); |
| else instance = new DeviceFaultInstance(context, m_params); |
| return instance; |
| } |
| |
| class CustomDevice |
| { |
| Move<VkDevice> m_logicalDevice; |
| |
| public: |
| CustomDevice (Context& context) |
| { |
| const bool useValidation = context.getTestContext().getCommandLine().isValidationEnabled(); |
| const PlatformInterface& platformInterface = context.getPlatformInterface(); |
| const VkInstance instance = context.getInstance(); |
| const InstanceInterface& instanceInterface = context.getInstanceInterface(); |
| const VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| const float queuePriority = 1.0f; |
| |
| const VkDeviceQueueCreateInfo queueCreateInfo |
| { |
| VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0, // VkDeviceQueueCreateFlags flags; |
| queueFamilyIndex, // uint32_t queueFamilyIndex; |
| 1u, // uint32_t queueCount; |
| &queuePriority // const float* pQueuePriorities; |
| }; |
| |
| VkPhysicalDeviceFaultFeaturesEXT deviceFaultFeatures |
| { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT, // VkStructureType sType; |
| nullptr, // void* pNext; |
| VK_TRUE, // VkBool32 deviceFault; |
| VK_TRUE // VkBool32 deviceFaultVendorBinary; |
| }; |
| |
| VkPhysicalDeviceFeatures2 deviceFeatures2 |
| { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, // VkStructureType sType; |
| &deviceFaultFeatures, // void* pNext; |
| { /* zeroed automatically since c++11 */ } // VkPhysicalDeviceFeatures features; |
| }; |
| instanceInterface.getPhysicalDeviceFeatures(physicalDevice, &deviceFeatures2.features); |
| |
| const VkDeviceCreateInfo deviceCreateInfo |
| { |
| VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // VkStructureType sType; |
| &deviceFeatures2, // const void* pNext; |
| 0u, // VkDeviceCreateFlags flags; |
| 1, // deUint32 queueCreateInfoCount; |
| &queueCreateInfo, // const VkDeviceQueueCreateInfo* pQueueCreateInfos; |
| 0u, // deUint32 enabledLayerCount; |
| nullptr, // const char* const* ppEnabledLayerNames; |
| 0u, // deUint32 enabledExtensionCount; |
| nullptr, // const char* const* ppEnabledExtensionNames; |
| nullptr // const VkPhysicalDeviceFeatures* pEnabledFeatures; |
| }; |
| |
| m_logicalDevice = createCustomDevice(useValidation, platformInterface, instance, instanceInterface, physicalDevice, &deviceCreateInfo); |
| } |
| |
| VkDevice getDevice () const { return *m_logicalDevice; } |
| }; |
| |
| class FakeInstanceInterface : public InstanceDriver |
| { |
| const InstanceInterface& m_instanceInterface; |
| public: |
| |
| FakeInstanceInterface (Context& ctx) |
| : InstanceDriver (ctx.getPlatformInterface(), ctx.getInstance()) |
| , m_instanceInterface (ctx.getInstanceInterface()) {} |
| |
| virtual void getPhysicalDeviceFeatures2 (VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) const override |
| { |
| DE_ASSERT(pFeatures); |
| |
| InstanceDriver::getPhysicalDeviceFeatures(physicalDevice, &pFeatures->features); |
| |
| auto pBaseStructure = reinterpret_cast<VkBaseOutStructure*>(pFeatures)->pNext; |
| while (pBaseStructure) |
| { |
| if (pBaseStructure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT) |
| { |
| const VkPhysicalDeviceFaultFeaturesEXT deviceFaultFeatures |
| { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT, // VkStructureType sType; |
| nullptr, // void* pNext; |
| VK_TRUE, // VkBool32 deviceFault; |
| VK_TRUE // VkBool32 deviceFaultVendorBinary; |
| }; |
| *(VkPhysicalDeviceFaultFeaturesEXT*)pBaseStructure = deviceFaultFeatures; |
| break; |
| } |
| pBaseStructure = pBaseStructure->pNext; |
| } |
| } |
| }; |
| |
| class FakeDeviceInterface : public DeviceDriver |
| { |
| public: |
| FakeDeviceInterface (Context& ctx) |
| : DeviceDriver(ctx.getPlatformInterface(), ctx.getInstance(), ctx.getDevice()) {} |
| |
| struct Header : VkDeviceFaultVendorBinaryHeaderVersionOneEXT |
| { |
| char applicationName[32]; |
| char engineName[32]; |
| Header() { |
| headerSize = sizeof(VkDeviceFaultVendorBinaryHeaderVersionOneEXT); |
| headerVersion = VK_DEVICE_FAULT_VENDOR_BINARY_HEADER_VERSION_ONE_EXT; |
| vendorID = 0x9876; |
| deviceID = 0x5432; |
| driverVersion = VK_MAKE_VERSION(3,4,5); |
| deMemcpy(pipelineCacheUUID, this, sizeof(pipelineCacheUUID)); |
| applicationNameOffset = deUint32(sizeof(VkDeviceFaultVendorBinaryHeaderVersionOneEXT)); |
| applicationVersion = VK_MAKE_API_VERSION(1,7,3,11); |
| engineNameOffset = deUint32(applicationNameOffset + sizeof(applicationName)); |
| engineVersion = VK_MAKE_VERSION(3,4,5); |
| apiVersion = VK_MAKE_API_VERSION(1,7,3,11); |
| |
| strcpy(applicationName, "application.exe"); |
| strcpy(engineName, "driver.so.3.4.5"); |
| } |
| }; |
| |
| virtual VkResult getDeviceFaultInfoEXT (VkDevice, VkDeviceFaultCountsEXT* pFaultCounts, VkDeviceFaultInfoEXT* pFaultInfo) const override |
| { |
| static std::vector<VkDeviceFaultAddressInfoEXT> addressInfos; |
| static std::vector<VkDeviceFaultVendorInfoEXT> vendorInfos; |
| static VkDeviceFaultAddressTypeEXT addressTypes[] |
| { |
| VK_DEVICE_FAULT_ADDRESS_TYPE_NONE_EXT, |
| VK_DEVICE_FAULT_ADDRESS_TYPE_READ_INVALID_EXT, |
| VK_DEVICE_FAULT_ADDRESS_TYPE_WRITE_INVALID_EXT, |
| VK_DEVICE_FAULT_ADDRESS_TYPE_EXECUTE_INVALID_EXT, |
| VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_UNKNOWN_EXT, |
| VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_INVALID_EXT, |
| VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_FAULT_EXT, |
| }; |
| static VkDeviceSize addressPrecisions[] |
| { |
| 2, 4, 8, 16 |
| }; |
| static deUint64 vendorFaultCodes[] |
| { |
| 0x11223344, 0x22334455, 0xAABBCCDD, 0xCCDDEEFF |
| }; |
| static Header vendorBinaryData; |
| |
| if (DE_NULL == pFaultInfo) |
| { |
| if (DE_NULL == pFaultCounts) return VK_ERROR_UNKNOWN; |
| |
| DE_ASSERT(pFaultCounts->sType == VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT); |
| DE_ASSERT(pFaultCounts->pNext == nullptr); |
| |
| pFaultCounts->vendorBinarySize = sizeof(Header); |
| pFaultCounts->vendorInfoCount = 2; |
| pFaultCounts->addressInfoCount = 2; |
| } |
| else |
| { |
| DE_ASSERT(pFaultCounts); |
| DE_ASSERT(pFaultCounts->sType == VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT); |
| DE_ASSERT(pFaultCounts->pNext == nullptr); |
| DE_ASSERT(pFaultInfo->sType == VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT); |
| DE_ASSERT(pFaultInfo->pNext == nullptr); |
| |
| if (pFaultCounts->addressInfoCount && pFaultInfo->pAddressInfos) |
| { |
| VkDeviceAddress deviceAddress = 1024; |
| addressInfos.resize(pFaultCounts->addressInfoCount); |
| for (deUint32 i = 0; i < pFaultCounts->addressInfoCount; ++i) |
| { |
| VkDeviceFaultAddressInfoEXT& info = addressInfos[i]; |
| info.addressType = addressTypes[ i % ARRAY_LENGTH(addressTypes) ]; |
| info.addressPrecision = addressPrecisions[ i % ARRAY_LENGTH(addressPrecisions) ]; |
| info.reportedAddress = deviceAddress; |
| deviceAddress <<= 1; |
| |
| pFaultInfo->pAddressInfos[i] = info; |
| } |
| } |
| |
| if (pFaultCounts->vendorInfoCount && pFaultInfo->pVendorInfos) |
| { |
| vendorInfos.resize(pFaultCounts->vendorInfoCount); |
| for (deUint32 i = 0; i < pFaultCounts->vendorInfoCount; ++i) |
| { |
| VkDeviceFaultVendorInfoEXT& info = vendorInfos[i]; |
| info.vendorFaultCode = vendorFaultCodes[ i % ARRAY_LENGTH(vendorFaultCodes) ]; |
| info.vendorFaultData = (i + 1) % ARRAY_LENGTH(vendorFaultCodes); |
| deMemset(info.description, 0, sizeof(info.description)); |
| |
| std::stringstream s; |
| s << "VendorFaultDescription" << info.vendorFaultData; |
| s.sync(); |
| const auto& str = s.str(); |
| deMemcpy(info.description, str.c_str(), str.length()); |
| |
| pFaultInfo->pVendorInfos[i] = info; |
| } |
| } |
| |
| if (pFaultCounts->vendorBinarySize && pFaultInfo->pVendorBinaryData) |
| { |
| DE_ASSERT(pFaultCounts->vendorBinarySize >= sizeof(VkDeviceFaultVendorBinaryHeaderVersionOneEXT)); |
| deMemcpy(pFaultInfo->pVendorBinaryData, &vendorBinaryData, |
| deMaxu32(sizeof(Header), deUint32(pFaultCounts->vendorBinarySize))); |
| } |
| } |
| |
| return VK_SUCCESS; |
| } |
| }; |
| |
| class FakeContext |
| { |
| FakeDeviceInterface m_deviceInterface; |
| FakeInstanceInterface m_instanceInterface; |
| public: |
| |
| FakeContext (Context& ctx) |
| : m_deviceInterface (ctx) |
| , m_instanceInterface (ctx) {} |
| |
| const DeviceInterface& getDeviceInterface () const { return m_deviceInterface; } |
| const InstanceInterface& getInstanceInterface () const { return m_instanceInterface; } |
| }; |
| |
| void DeviceFaultCase::checkSupport (Context& context) const |
| { |
| FakeContext fakeContext (context); |
| VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); |
| const InstanceInterface& instanceInterface = (m_params.type == TestType::Real) ? context.getInstanceInterface() : fakeContext.getInstanceInterface(); |
| |
| context.requireInstanceFunctionality(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| |
| if (m_params.type == TestType::Real) |
| { |
| context.requireDeviceFunctionality(VK_EXT_DEVICE_FAULT_EXTENSION_NAME); |
| } |
| |
| VkPhysicalDeviceFaultFeaturesEXT deviceFaultFeatures{}; |
| deviceFaultFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; |
| |
| VkPhysicalDeviceFeatures2 deviceFeatures2{}; |
| deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; |
| deviceFeatures2.pNext = &deviceFaultFeatures; |
| |
| instanceInterface.getPhysicalDeviceFeatures2(physicalDevice, &deviceFeatures2); |
| |
| if (VK_FALSE == deviceFaultFeatures.deviceFault) |
| TCU_THROW(NotSupportedError, "VK_EXT_device_fault extension is not supported by device"); |
| } |
| |
| TestStatus DeviceFaultCustomInstance::iterate (void) |
| { |
| CustomDevice customDevice (m_context); |
| const VkDevice device = customDevice.getDevice(); |
| return (device != DE_NULL) ? TestStatus::pass("") : TestStatus::fail(""); |
| } |
| |
| void DeviceFaultInstance::log (const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, |
| const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, |
| const std::vector<deUint8>& vendorBinaryData) const |
| { |
| const char* nl = "\n"; |
| deUint32 cnt = 0; |
| TestLog& log = m_context.getTestContext().getLog(); |
| |
| if (addressInfos.size()) |
| { |
| log << TestLog::Section("addressInfos", ""); |
| auto msg = log << TestLog::Message; |
| cnt = 0; |
| for (const auto& addressInfo : addressInfos) |
| { |
| if (cnt++) msg << nl; |
| msg << addressInfo; |
| } |
| msg << TestLog::EndMessage << TestLog::EndSection; |
| } |
| |
| if (vendorInfos.size()) |
| { |
| log << TestLog::Section("vendorInfos", ""); |
| auto msg = log << TestLog::Message; |
| cnt = 0; |
| for (const auto& vendorInfo : vendorInfos) |
| { |
| if (cnt++) msg << nl; |
| msg << vendorInfo; |
| } |
| msg << TestLog::EndMessage << TestLog::EndSection; |
| } |
| |
| if (vendorBinaryData.size()) |
| { |
| DE_ASSERT(vendorBinaryData.size() >= sizeof(VkDeviceFaultVendorBinaryHeaderVersionOneEXT)); |
| |
| log << TestLog::Section("vendorBinaryData", ""); |
| auto msg = log << TestLog::Message; |
| auto pHeader = reinterpret_cast<VkDeviceFaultVendorBinaryHeaderVersionOneEXT const*>(vendorBinaryData.data()); |
| msg << *pHeader; |
| msg << TestLog::EndMessage << TestLog::EndSection; |
| } |
| } |
| |
| TestStatus DeviceFaultInstance::iterate (void) |
| { |
| FakeContext fakeContext (m_context); |
| const VkDevice device = m_context.getDevice(); |
| const VkPhysicalDevice physicalDevice = m_context.getPhysicalDevice(); |
| const DeviceInterface& deviceInterface = (m_params.type == TestType::Fake) ? fakeContext.getDeviceInterface() : m_context.getDeviceInterface(); |
| const InstanceInterface& instanceInterface = (m_params.type == TestType::Fake) ? fakeContext.getInstanceInterface() : m_context.getInstanceInterface(); |
| |
| VkDeviceFaultCountsEXT fc{}; |
| fc.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT; |
| fc.pNext = nullptr; |
| deviceInterface.getDeviceFaultInfoEXT(device, &fc, nullptr); |
| |
| const deUint32 vendorBinarySize = std::min(deUint32(fc.vendorBinarySize), std::numeric_limits<deUint32>::max()); |
| |
| VkPhysicalDeviceFaultFeaturesEXT deviceFaultFeatures{}; |
| deviceFaultFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; |
| |
| VkPhysicalDeviceFeatures2 deviceFeatures2{}; |
| deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; |
| deviceFeatures2.pNext = &deviceFaultFeatures; |
| |
| instanceInterface.getPhysicalDeviceFeatures2(physicalDevice, &deviceFeatures2); |
| |
| fc.vendorBinarySize = deviceFaultFeatures.deviceFaultVendorBinary ? vendorBinarySize : 0; |
| |
| std::vector<VkDeviceFaultAddressInfoEXT> addressInfos (fc.addressInfoCount); |
| std::vector<VkDeviceFaultVendorInfoEXT> vendorInfos (fc.vendorInfoCount); |
| std::vector<deUint8> vendorBinaryData(vendorBinarySize); |
| |
| VkDeviceFaultInfoEXT fi{}; |
| fi.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT; |
| fi.pNext = nullptr; |
| fi.pAddressInfos = addressInfos.data(); |
| fi.pVendorInfos = vendorInfos.data(); |
| fi.pVendorBinaryData = deviceFaultFeatures.deviceFaultVendorBinary ? vendorBinaryData.data() : nullptr; |
| |
| const VkResult result = deviceInterface.getDeviceFaultInfoEXT(device, &fc, &fi); |
| |
| log(addressInfos, vendorInfos, vendorBinaryData); |
| |
| return (result == VK_SUCCESS) ? TestStatus::pass("") : TestStatus::fail(""); |
| } |
| |
| } // unnamed |
| |
| tcu::TestCaseGroup* createDeviceFaultTests (tcu::TestContext& testCtx) |
| { |
| TestParams p; |
| struct { |
| TestType type; |
| const char* name; |
| } const types[] = { { TestType::Real, "real" }, { TestType::Fake, "fake" }, { TestType::CustomDevice, "custom_device" } }; |
| |
| auto rootGroup = new TestCaseGroup(testCtx, "device_fault", "VK_EXT_device_fault extension tests."); |
| for (const auto& type : types) |
| { |
| p.type = type.type; |
| rootGroup->addChild(new DeviceFaultCase(testCtx, type.name, p)); |
| } |
| return rootGroup; |
| } |
| |
| } // postmortem |
| } // vkt |