Test memory leaks in vkEnumeratePhysicalDevices
authorPiotr Byszewski <piotr.byszewski@mobica.com>
Fri, 3 Apr 2020 08:45:27 +0000 (10:45 +0200)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Sat, 18 Apr 2020 09:38:59 +0000 (05:38 -0400)
This change adds dedicated test that checks if vkEnumeratePhysical-
Devices does same amount of memory frees as it does allocations.
Additionally alloc_callback_fail tests were changed so that if
object construction was not successful with given number of
allocations it is attempted one more time with 10000 allowed
allocations. This will mainly affect instance and device
creation tests as they require many allocations and with
this we will be able to detect possible memory leaks.

Components: Vulkan

VK-GL-CTS issue: 149

Affects:
dEQP-VK.api.device_init.enumerate_devices_alloc_leak
dEQP-VK.api.object_management.alloc_callback_fail.*

Change-Id: Ic64054a0753a4ba4fdf3e7ef3104787bf5d2a72b

android/cts/master/vk-master-2020-03-01.txt
android/cts/master/vk-master.txt
external/vulkancts/modules/vulkan/api/vktApiDeviceInitializationTests.cpp
external/vulkancts/modules/vulkan/api/vktApiObjectManagementTests.cpp
external/vulkancts/mustpass/master/vk-default.txt

index a0274a6..cfd2900 100644 (file)
@@ -26,6 +26,7 @@ dEQP-VK.api.info.vulkan1p2_limits_validation.timeline_semaphore
 dEQP-VK.api.info.vulkan1p2_limits_validation.ext_line_rasterization
 dEQP-VK.api.device_init.create_instance_extension_name_abuse
 dEQP-VK.api.device_init.create_instance_layer_name_abuse
+dEQP-VK.api.device_init.enumerate_devices_alloc_leak
 dEQP-VK.api.buffer.basic.size_max_uint64
 dEQP-VK.api.buffer_marker.graphics.external_host_mem.top_of_pipe.sequential.4
 dEQP-VK.api.buffer_marker.graphics.external_host_mem.top_of_pipe.sequential.64
index c43b20e..708c913 100644 (file)
@@ -3734,6 +3734,7 @@ dEQP-VK.api.device_init.create_instance_null_appinfo
 dEQP-VK.api.device_init.create_instance_unsupported_extensions
 dEQP-VK.api.device_init.create_instance_extension_name_abuse
 dEQP-VK.api.device_init.create_instance_layer_name_abuse
+dEQP-VK.api.device_init.enumerate_devices_alloc_leak
 dEQP-VK.api.device_init.create_device
 dEQP-VK.api.device_init.create_multiple_devices
 dEQP-VK.api.device_init.create_device_unsupported_extensions
index 41adc97..4154403 100644 (file)
@@ -34,6 +34,7 @@
 #include "vkMemUtil.hpp"
 #include "vkDeviceUtil.hpp"
 #include "vkApiVersion.hpp"
+#include "vkAllocationCallbackUtil.hpp"
 
 #include "tcuTestLog.hpp"
 #include "tcuResultCollector.hpp"
@@ -555,6 +556,58 @@ tcu::TestStatus createInstanceWithLayerNameAbuseTest (Context& context)
        return tcu::TestStatus::pass("Pass, creating instances with unsupported layers were rejected.");
 }
 
+tcu::TestStatus enumerateDevicesAllocLeakTest(Context& context)
+{
+       // enumeratePhysicalDevices uses instance-provided allocator
+       // and this test checks if all alocated memory is freed
+
+       typedef AllocationCallbackRecorder::RecordIterator RecordIterator;
+
+       const PlatformInterface&        vkp                             (context.getPlatformInterface());
+       const deUint32                          apiVersion              (context.getUsedApiVersion());
+       DeterministicFailAllocator      objAllocator    (getSystemAllocator(), DeterministicFailAllocator::MODE_DO_NOT_COUNT, 0);
+       AllocationCallbackRecorder      recorder                (objAllocator.getCallbacks(), 128);
+       Move<VkInstance>                        instance                (vk::createDefaultInstance(vkp, apiVersion, {}, {}, recorder.getCallbacks()));
+       InstanceDriver                          vki                             (vkp, *instance);
+       vector<VkPhysicalDevice>        devices                 (enumeratePhysicalDevices(vki, *instance));
+       RecordIterator                          recordToCheck   (recorder.getRecordsEnd());
+
+       try
+       {
+               devices = enumeratePhysicalDevices(vki, *instance);
+       }
+       catch (const vk::OutOfMemoryError& e)
+       {
+               if (e.getError() != VK_ERROR_OUT_OF_HOST_MEMORY)
+                       return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Got out of memory error - leaks in enumeratePhysicalDevices not tested.");
+       }
+
+       // make sure that same number of allocations and frees was done
+       deInt32                 allocationRecords       (0);
+       RecordIterator  lastRecordToCheck       (recorder.getRecordsEnd());
+       while (recordToCheck != lastRecordToCheck)
+       {
+               const AllocationCallbackRecord& record = *recordToCheck;
+               switch (record.type)
+               {
+               case AllocationCallbackRecord::TYPE_ALLOCATION:
+                       ++allocationRecords;
+                       break;
+               case AllocationCallbackRecord::TYPE_FREE:
+                       if (record.data.free.mem != DE_NULL)
+                               --allocationRecords;
+                       break;
+               default:
+                       break;
+               }
+               ++recordToCheck;
+       }
+
+       if (allocationRecords)
+               return tcu::TestStatus::fail("enumeratePhysicalDevices leaked memory");
+       return tcu::TestStatus::pass("Ok");
+}
+
 tcu::TestStatus createDeviceTest (Context& context)
 {
        const PlatformInterface&                platformInterface               = context.getPlatformInterface();
@@ -1569,6 +1622,7 @@ tcu::TestCaseGroup* createDeviceInitializationTests (tcu::TestContext& testCtx)
        addFunctionCase(deviceInitializationTests.get(), "create_instance_unsupported_extensions",                      "", createInstanceWithUnsupportedExtensionsTest);
        addFunctionCase(deviceInitializationTests.get(), "create_instance_extension_name_abuse",                        "", createInstanceWithExtensionNameAbuseTest);
        addFunctionCase(deviceInitializationTests.get(), "create_instance_layer_name_abuse",                            "", createInstanceWithLayerNameAbuseTest);
+       addFunctionCase(deviceInitializationTests.get(), "enumerate_devices_alloc_leak",                                        "", enumerateDevicesAllocLeakTest);
        addFunctionCase(deviceInitializationTests.get(), "create_device",                                                                       "", createDeviceTest);
        addFunctionCase(deviceInitializationTests.get(), "create_multiple_devices",                                                     "", createMultipleDevicesTest);
        addFunctionCase(deviceInitializationTests.get(), "create_device_unsupported_extensions",                        "", createDeviceWithUnsupportedExtensionsTest);
index 253f9d4..5e867f5 100644 (file)
@@ -51,6 +51,7 @@
 #include "deInt32.h"
 
 #include <limits>
+#include <algorithm>
 
 #define VK_DESCRIPTOR_TYPE_LAST (VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT + 1)
 
@@ -2554,8 +2555,8 @@ tcu::TestStatus createSingleAllocCallbacksTest (Context& context, typename Objec
        return tcu::TestStatus::pass("Ok");
 }
 
-template<typename Object>      deUint32        getOomIterLimit                                 (void) { return 1024;   }
-template<>                                     deUint32        getOomIterLimit<Device>         (void) { return 20;             }
+template<typename Object>      deUint32        getOomIterLimit                                 (void) { return 40;             }
+template<>                                     deUint32        getOomIterLimit<Device>                 (void) { return 20;             }
 template<>                                     deUint32        getOomIterLimit<DeviceGroup>    (void) { return 20;             }
 
 template<typename Object>
@@ -2576,13 +2577,15 @@ tcu::TestStatus allocCallbackFailTest (Context& context, typename Object::Parame
        deUint32                                                        numPassingAllocs        = 0;
        const deUint32                                          cmdLineIterCount        = (deUint32)context.getTestContext().getCommandLine().getTestIterationCount();
        const deUint32                                          maxTries                        = cmdLineIterCount != 0 ? cmdLineIterCount : getOomIterLimit<Object>();
+       const deUint32                                          finalLimit                      = std::max(maxTries, 10000u);
+       bool                                                            createOk                        = false;
 
        {
                const EnvClone                                          resEnv  (rootEnv, getDefaulDeviceParameters(context), 1u);
                const typename Object::Resources        res             (resEnv.env, params);
 
                // Iterate over test until object allocation succeeds
-               for (; numPassingAllocs < maxTries; ++numPassingAllocs)
+               while(true)
                {
                        DeterministicFailAllocator                      objAllocator(getSystemAllocator(),
                                                                                                                         DeterministicFailAllocator::MODE_COUNT_AND_FAIL,
@@ -2599,13 +2602,13 @@ tcu::TestStatus allocCallbackFailTest (Context& context, typename Object::Parame
                                                                                                                         recorder.getCallbacks(),
                                                                                                                         resEnv.env.maxResourceConsumers,
                                                                                                                         resEnv.env.commandLine);
-                       bool                                                            createOk        = false;
 
                        context.getTestContext().getLog()
                                << TestLog::Message
                                << "Trying to create object with " << numPassingAllocs << " allocation" << (numPassingAllocs != 1 ? "s" : "") << " passing"
                                << TestLog::EndMessage;
 
+                       createOk = false;
                        try
                        {
                                Unique<typename Object::Type>   obj     (Object::create(objEnv, res, params));
@@ -2629,6 +2632,14 @@ tcu::TestStatus allocCallbackFailTest (Context& context, typename Object::Parame
                                        << TestLog::Message << "Object construction succeeded! " << TestLog::EndMessage;
                                break;
                        }
+
+                       ++numPassingAllocs;
+                       // if allocation didn't succeed with huge limit then stop trying
+                       if (numPassingAllocs >= finalLimit)
+                               break;
+                       // if we reached maxTries but didn't create object, try doing it with huge limit
+                       if (numPassingAllocs >= maxTries)
+                               numPassingAllocs = finalLimit;
                }
        }
 
@@ -2637,11 +2648,19 @@ tcu::TestStatus allocCallbackFailTest (Context& context, typename Object::Parame
 
        if (numPassingAllocs == 0)
                return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Allocation callbacks not called");
-       else if (numPassingAllocs == maxTries)
+       else if (numPassingAllocs >= finalLimit)
        {
+               if (createOk)
+               {
+                       context.getTestContext().getLog()
+                               << TestLog::Message << "Maximum iteration count (" << maxTries << ") reached without object construction passing. "
+                               << "Object was succesfully constructed with " << numPassingAllocs << " iterations limit." << TestLog::EndMessage;
+                       return tcu::TestStatus(QP_TEST_RESULT_PASS, "Construction passed but not all iterations were checked");
+               }
+
                context.getTestContext().getLog()
-                       << TestLog::Message << "WARNING: Maximum iteration count (" << maxTries << ") reached without object construction passing. "
-                                                               << "OOM testing incomplete, use --deqp-test-iteration-count= to test with higher limit." << TestLog::EndMessage;
+                       << TestLog::Message << "WARNING: Maximum iteration count (" << finalLimit << ") reached without object construction passing. "
+                       << "OOM testing incomplete, use --deqp-test-iteration-count= to test with higher limit." << TestLog::EndMessage;
                return tcu::TestStatus(QP_TEST_RESULT_PASS, "Max iter count reached");
        }
        else
index dd2d28d..7df6ff4 100644 (file)
@@ -3732,6 +3732,7 @@ dEQP-VK.api.device_init.create_instance_null_appinfo
 dEQP-VK.api.device_init.create_instance_unsupported_extensions
 dEQP-VK.api.device_init.create_instance_extension_name_abuse
 dEQP-VK.api.device_init.create_instance_layer_name_abuse
+dEQP-VK.api.device_init.enumerate_devices_alloc_leak
 dEQP-VK.api.device_init.create_device
 dEQP-VK.api.device_init.create_multiple_devices
 dEQP-VK.api.device_init.create_device_unsupported_extensions