Add Vulkan memory allocation callback utilities
authorPyry Haulos <phaulos@google.com>
Fri, 11 Dec 2015 22:20:04 +0000 (14:20 -0800)
committerPyry Haulos <phaulos@google.com>
Thu, 17 Dec 2015 22:36:35 +0000 (14:36 -0800)
Change-Id: I3b81ee1f02825585c5bdcdf90f705d392dd7157b

external/vulkancts/framework/vulkan/CMakeLists.txt
external/vulkancts/framework/vulkan/vkAllocationCallbackUtil.cpp [new file with mode: 0644]
external/vulkancts/framework/vulkan/vkAllocationCallbackUtil.hpp [new file with mode: 0644]

index b765088..9f76ec3 100644 (file)
@@ -37,6 +37,8 @@ set(VKUTIL_SRCS
        vkImageUtil.hpp
        vkTypeUtil.cpp
        vkTypeUtil.hpp
+       vkAllocationCallbackUtil.cpp
+       vkAllocationCallbackUtil.hpp
        )
 
 set(VKUTIL_LIBS
diff --git a/external/vulkancts/framework/vulkan/vkAllocationCallbackUtil.cpp b/external/vulkancts/framework/vulkan/vkAllocationCallbackUtil.cpp
new file mode 100644 (file)
index 0000000..ec748af
--- /dev/null
@@ -0,0 +1,720 @@
+/*-------------------------------------------------------------------------
+ * Vulkan CTS Framework
+ * --------------------
+ *
+ * Copyright (c) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be
+ * included in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by
+ * Khronos, at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Memory allocation callback utilities.
+ *//*--------------------------------------------------------------------*/
+
+#include "vkAllocationCallbackUtil.hpp"
+#include "tcuFormatUtil.hpp"
+#include "tcuTestLog.hpp"
+#include "deSTLUtil.hpp"
+#include "deMemory.h"
+
+#include <map>
+
+namespace vk
+{
+
+// System default allocator
+
+static VKAPI_ATTR void* VKAPI_CALL systemAllocate (void*, size_t size, size_t alignment, VkSystemAllocationScope)
+{
+       if (size > 0)
+               return deAlignedMalloc(size, (deUint32)alignment);
+       else
+               return DE_NULL;
+}
+
+static VKAPI_ATTR void VKAPI_CALL systemFree (void*, void* pMem)
+{
+       deAlignedFree(pMem);
+}
+
+static VKAPI_ATTR void* VKAPI_CALL systemReallocate (void*, void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope)
+{
+       return deAlignedRealloc(pOriginal, size, alignment);
+}
+
+static VKAPI_ATTR void VKAPI_CALL systemInternalAllocationNotification (void*, size_t, VkInternalAllocationType, VkSystemAllocationScope)
+{
+}
+
+static VKAPI_ATTR void VKAPI_CALL systemInternalFreeNotification (void*, size_t, VkInternalAllocationType, VkSystemAllocationScope)
+{
+}
+
+static const VkAllocationCallbacks s_systemAllocator =
+{
+       DE_NULL,                // pUserData
+       systemAllocate,
+       systemReallocate,
+       systemFree,
+       systemInternalAllocationNotification,
+       systemInternalFreeNotification,
+};
+
+const VkAllocationCallbacks* getSystemAllocator (void)
+{
+       return &s_systemAllocator;
+}
+
+// AllocationCallbacks
+
+static VKAPI_ATTR void* VKAPI_CALL allocationCallback (void* pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+       return reinterpret_cast<AllocationCallbacks*>(pUserData)->allocate(size, alignment, allocationScope);
+}
+
+static VKAPI_ATTR void* VKAPI_CALL reallocationCallback (void* pUserData, void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+       return reinterpret_cast<AllocationCallbacks*>(pUserData)->reallocate(pOriginal, size, alignment, allocationScope);
+}
+
+static VKAPI_ATTR void VKAPI_CALL freeCallback (void* pUserData, void* pMem)
+{
+       reinterpret_cast<AllocationCallbacks*>(pUserData)->free(pMem);
+}
+
+static VKAPI_ATTR void VKAPI_CALL internalAllocationNotificationCallback (void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
+{
+       reinterpret_cast<AllocationCallbacks*>(pUserData)->notifyInternalAllocation(size, allocationType, allocationScope);
+}
+
+static VKAPI_ATTR void VKAPI_CALL internalFreeNotificationCallback (void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
+{
+       reinterpret_cast<AllocationCallbacks*>(pUserData)->notifyInternalFree(size, allocationType, allocationScope);
+}
+
+static VkAllocationCallbacks makeCallbacks (AllocationCallbacks* object)
+{
+       const VkAllocationCallbacks callbacks =
+       {
+               reinterpret_cast<void*>(object),
+               allocationCallback,
+               reallocationCallback,
+               freeCallback,
+               internalAllocationNotificationCallback,
+               internalFreeNotificationCallback
+       };
+       return callbacks;
+}
+
+AllocationCallbacks::AllocationCallbacks (void)
+       : m_callbacks(makeCallbacks(this))
+{
+}
+
+AllocationCallbacks::~AllocationCallbacks (void)
+{
+}
+
+// AllocationCallbackRecord
+
+AllocationCallbackRecord AllocationCallbackRecord::allocation (size_t size, size_t alignment, VkSystemAllocationScope scope, void* returnedPtr)
+{
+       AllocationCallbackRecord record;
+
+       record.type                                                     = TYPE_ALLOCATION;
+       record.data.allocation.size                     = size;
+       record.data.allocation.alignment        = alignment;
+       record.data.allocation.scope            = scope;
+       record.data.allocation.returnedPtr      = returnedPtr;
+
+       return record;
+}
+
+AllocationCallbackRecord AllocationCallbackRecord::reallocation (void* original, size_t size, size_t alignment, VkSystemAllocationScope scope, void* returnedPtr)
+{
+       AllocationCallbackRecord record;
+
+       record.type                                                             = TYPE_REALLOCATION;
+       record.data.reallocation.original               = original;
+       record.data.reallocation.size                   = size;
+       record.data.reallocation.alignment              = alignment;
+       record.data.reallocation.scope                  = scope;
+       record.data.reallocation.returnedPtr    = returnedPtr;
+
+       return record;
+}
+
+AllocationCallbackRecord AllocationCallbackRecord::free (void* mem)
+{
+       AllocationCallbackRecord record;
+
+       record.type                             = TYPE_FREE;
+       record.data.free.mem    = mem;
+
+       return record;
+}
+
+AllocationCallbackRecord AllocationCallbackRecord::internalAllocation (size_t size, VkInternalAllocationType type, VkSystemAllocationScope scope)
+{
+       AllocationCallbackRecord record;
+
+       record.type                                                             = TYPE_INTERNAL_ALLOCATION;
+       record.data.internalAllocation.size             = size;
+       record.data.internalAllocation.type             = type;
+       record.data.internalAllocation.scope    = scope;
+
+       return record;
+}
+
+AllocationCallbackRecord AllocationCallbackRecord::internalFree (size_t size, VkInternalAllocationType type, VkSystemAllocationScope scope)
+{
+       AllocationCallbackRecord record;
+
+       record.type                                                             = TYPE_INTERNAL_FREE;
+       record.data.internalAllocation.size             = size;
+       record.data.internalAllocation.type             = type;
+       record.data.internalAllocation.scope    = scope;
+
+       return record;
+}
+
+// ChainedAllocator
+
+ChainedAllocator::ChainedAllocator (const VkAllocationCallbacks* nextAllocator)
+       : m_nextAllocator(nextAllocator)
+{
+}
+
+ChainedAllocator::~ChainedAllocator (void)
+{
+}
+
+void* ChainedAllocator::allocate (size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+       return m_nextAllocator->pfnAllocation(m_nextAllocator->pUserData, size, alignment, allocationScope);
+}
+
+void* ChainedAllocator::reallocate (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+       return m_nextAllocator->pfnReallocation(m_nextAllocator->pUserData, original, size, alignment, allocationScope);
+}
+
+void ChainedAllocator::free (void* mem)
+{
+       m_nextAllocator->pfnFree(m_nextAllocator->pUserData, mem);
+}
+
+void ChainedAllocator::notifyInternalAllocation (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
+{
+       m_nextAllocator->pfnInternalAllocation(m_nextAllocator->pUserData, size, allocationType, allocationScope);
+}
+
+void ChainedAllocator::notifyInternalFree (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
+{
+       m_nextAllocator->pfnInternalFree(m_nextAllocator->pUserData, size, allocationType, allocationScope);
+}
+
+// AllocationCallbackRecorder
+
+AllocationCallbackRecorder::AllocationCallbackRecorder (const VkAllocationCallbacks* allocator, deUint32 callCountHint)
+       : ChainedAllocator      (allocator)
+       , m_records                     (callCountHint)
+{
+}
+
+AllocationCallbackRecorder::~AllocationCallbackRecorder (void)
+{
+}
+
+void* AllocationCallbackRecorder::allocate (size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+       void* const     ptr     = ChainedAllocator::allocate(size, alignment, allocationScope);
+
+       m_records.append(AllocationCallbackRecord::allocation(size, alignment, allocationScope, ptr));
+
+       return ptr;
+}
+
+void* AllocationCallbackRecorder::reallocate (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+       void* const     ptr     = ChainedAllocator::reallocate(original, size, alignment, allocationScope);
+
+       m_records.append(AllocationCallbackRecord::reallocation(original, size, alignment, allocationScope, ptr));
+
+       return ptr;
+}
+
+void AllocationCallbackRecorder::free (void* mem)
+{
+       ChainedAllocator::free(mem);
+
+       m_records.append(AllocationCallbackRecord::free(mem));
+}
+
+void AllocationCallbackRecorder::notifyInternalAllocation (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
+{
+       ChainedAllocator::notifyInternalAllocation(size, allocationType, allocationScope);
+
+       m_records.append(AllocationCallbackRecord::internalAllocation(size, allocationType, allocationScope));
+}
+
+void AllocationCallbackRecorder::notifyInternalFree (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
+{
+       ChainedAllocator::notifyInternalFree(size, allocationType, allocationScope);
+
+       m_records.append(AllocationCallbackRecord::internalFree(size, allocationType, allocationScope));
+}
+
+// DeterministicFailAllocator
+
+DeterministicFailAllocator::DeterministicFailAllocator (const VkAllocationCallbacks* allocator, deUint32 numPassingAllocs)
+       : ChainedAllocator      (allocator)
+       , m_numPassingAllocs(numPassingAllocs)
+       , m_allocationNdx       (0)
+{
+}
+
+DeterministicFailAllocator::~DeterministicFailAllocator (void)
+{
+}
+
+void* DeterministicFailAllocator::allocate (size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+       if (deAtomicIncrementUint32(&m_allocationNdx) <= m_numPassingAllocs)
+               return ChainedAllocator::allocate(size, alignment, allocationScope);
+       else
+               return DE_NULL;
+}
+
+void* DeterministicFailAllocator::reallocate (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+       if (deAtomicIncrementUint32(&m_allocationNdx) <= m_numPassingAllocs)
+               return ChainedAllocator::reallocate(original, size, alignment, allocationScope);
+       else
+               return DE_NULL;
+}
+
+// Utils
+
+AllocationCallbackValidationResults::AllocationCallbackValidationResults (void)
+{
+       deMemset(internalAllocationTotal, 0, sizeof(internalAllocationTotal));
+}
+
+void AllocationCallbackValidationResults::clear (void)
+{
+       liveAllocations.clear();
+       violations.clear();
+       deMemset(internalAllocationTotal, 0, sizeof(internalAllocationTotal));
+}
+
+namespace
+{
+
+struct AllocationSlot
+{
+       AllocationCallbackRecord        record;
+       bool                                            isLive;
+
+       AllocationSlot (void)
+               : isLive        (false)
+       {}
+
+       AllocationSlot (const AllocationCallbackRecord& record_, bool isLive_)
+               : record        (record_)
+               , isLive        (isLive_)
+       {}
+};
+
+size_t getAlignment (const AllocationCallbackRecord& record)
+{
+       if (record.type == AllocationCallbackRecord::TYPE_ALLOCATION)
+               return record.data.allocation.alignment;
+       else if (record.type == AllocationCallbackRecord::TYPE_REALLOCATION)
+               return record.data.reallocation.alignment;
+       else
+       {
+               DE_ASSERT(false);
+               return 0;
+       }
+}
+
+} // anonymous
+
+void validateAllocationCallbacks (const AllocationCallbackRecorder& recorder, AllocationCallbackValidationResults* results)
+{
+       std::vector<AllocationSlot>             allocations;
+       std::map<void*, size_t>                 ptrToSlotIndex;
+
+       DE_ASSERT(results->liveAllocations.empty() && results->violations.empty());
+
+       for (AllocationCallbackRecorder::RecordIterator callbackIter = recorder.getRecordsBegin();
+                callbackIter != recorder.getRecordsEnd();
+                ++callbackIter)
+       {
+               const AllocationCallbackRecord&         record  = *callbackIter;
+
+               // Validate scope
+               {
+                       const VkSystemAllocationScope* const    scopePtr        = record.type == AllocationCallbackRecord::TYPE_ALLOCATION                      ? &record.data.allocation.scope
+                                                                                                                               : record.type == AllocationCallbackRecord::TYPE_REALLOCATION            ? &record.data.reallocation.scope
+                                                                                                                               : record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION     ? &record.data.internalAllocation.scope
+                                                                                                                               : record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE           ? &record.data.internalAllocation.scope
+                                                                                                                               : DE_NULL;
+
+                       if (scopePtr && !de::inBounds(*scopePtr, (VkSystemAllocationScope)0, VK_SYSTEM_ALLOCATION_SCOPE_LAST))
+                               results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_ALLOCATION_SCOPE));
+               }
+
+               // Validate alignment
+               if (record.type == AllocationCallbackRecord::TYPE_ALLOCATION ||
+                       record.type == AllocationCallbackRecord::TYPE_REALLOCATION)
+               {
+                       if (!deIsPowerOfTwoSize(getAlignment(record)))
+                               results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_ALIGNMENT));
+               }
+
+               // Validate actual allocation behavior
+               switch (record.type)
+               {
+                       case AllocationCallbackRecord::TYPE_ALLOCATION:
+                       {
+                               DE_ASSERT(!de::contains(ptrToSlotIndex, record.data.allocation.returnedPtr));
+
+                               if (record.data.allocation.returnedPtr)
+                               {
+                                       ptrToSlotIndex[record.data.allocation.returnedPtr] = allocations.size();
+                                       allocations.push_back(AllocationSlot(record, true));
+                               }
+
+                               break;
+                       }
+
+                       case AllocationCallbackRecord::TYPE_REALLOCATION:
+                       {
+                               if (de::contains(ptrToSlotIndex, record.data.reallocation.original))
+                               {
+                                       const size_t            origSlotNdx             = ptrToSlotIndex[record.data.reallocation.original];
+                                       AllocationSlot&         origSlot                = allocations[origSlotNdx];
+
+                                       DE_ASSERT(record.data.reallocation.original != DE_NULL);
+
+                                       if (record.data.reallocation.size > 0)
+                                       {
+                                               if (getAlignment(origSlot.record) != record.data.reallocation.alignment)
+                                                       results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_DIFFERENT_ALIGNMENT));
+
+                                               if (record.data.reallocation.original == record.data.reallocation.returnedPtr)
+                                               {
+                                                       if (!origSlot.isLive)
+                                                       {
+                                                               results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_FREED_PTR));
+                                                               origSlot.isLive = true; // Mark live to suppress further errors
+                                                       }
+
+                                                       // Just update slot record
+                                                       allocations[origSlotNdx].record = record;
+                                               }
+                                               else
+                                               {
+                                                       DE_ASSERT(!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr));
+
+                                                       if (record.data.reallocation.returnedPtr)
+                                                       {
+                                                               allocations[origSlotNdx].isLive = false;
+                                                               ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size();
+                                                               allocations.push_back(AllocationSlot(record, true));
+                                                       }
+                                                       // else original ptr remains valid and live
+                                               }
+                                       }
+                                       else
+                                       {
+                                               DE_ASSERT(!record.data.reallocation.returnedPtr);
+
+                                               origSlot.isLive = false;
+                                       }
+                               }
+                               else
+                               {
+                                       if (record.data.reallocation.original)
+                                               results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_NOT_ALLOCATED_PTR));
+
+                                       if (record.data.reallocation.returnedPtr)
+                                       {
+                                               DE_ASSERT(!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr));
+                                               ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size();
+                                               allocations.push_back(AllocationSlot(record, true));
+                                       }
+                               }
+
+                               break;
+                       }
+
+                       case AllocationCallbackRecord::TYPE_FREE:
+                       {
+                               if (record.data.free.mem != DE_NULL) // Freeing null pointer is valid and ignored
+                               {
+                                       if (de::contains(ptrToSlotIndex, record.data.free.mem))
+                                       {
+                                               const size_t    slotNdx         = ptrToSlotIndex[record.data.free.mem];
+
+                                               if (allocations[slotNdx].isLive)
+                                                       allocations[slotNdx].isLive = false;
+                                               else
+                                                       results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_DOUBLE_FREE));
+                                       }
+                                       else
+                                               results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_FREE_NOT_ALLOCATED_PTR));
+                               }
+
+                               break;
+                       }
+
+                       case AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION:
+                       case AllocationCallbackRecord::TYPE_INTERNAL_FREE:
+                       {
+                               if (de::inBounds(record.data.internalAllocation.type, (VkInternalAllocationType)0, VK_INTERNAL_ALLOCATION_TYPE_LAST))
+                               {
+                                       size_t* const           totalAllocSizePtr       = &results->internalAllocationTotal[record.data.internalAllocation.type][record.data.internalAllocation.scope];
+                                       const size_t            size                            = record.data.internalAllocation.size;
+
+                                       if (record.type == AllocationCallbackRecord::TYPE_FREE)
+                                       {
+                                               if (*totalAllocSizePtr < size)
+                                               {
+                                                       results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL));
+                                                       *totalAllocSizePtr = 0; // Reset to 0 to suppress compound errors
+                                               }
+                                               else
+                                                       *totalAllocSizePtr -= size;
+                                       }
+                                       else
+                                               *totalAllocSizePtr += size;
+                               }
+                               else
+                                       results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_INTERNAL_ALLOCATION_TYPE));
+
+                               break;
+                       }
+
+                       default:
+                               DE_ASSERT(false);
+               }
+       }
+
+       DE_ASSERT(!de::contains(ptrToSlotIndex, DE_NULL));
+
+       // Collect live allocations
+       for (std::vector<AllocationSlot>::const_iterator slotIter = allocations.begin();
+                slotIter != allocations.end();
+                ++slotIter)
+       {
+               if (slotIter->isLive)
+                       results->liveAllocations.push_back(slotIter->record);
+       }
+}
+
+bool checkAndLog (tcu::TestLog& log, const AllocationCallbackValidationResults& results, deUint32 allowedLiveAllocScopeBits)
+{
+       using tcu::TestLog;
+
+       size_t  numLeaks        = 0;
+
+       if (!results.violations.empty())
+       {
+               for (size_t violationNdx = 0; violationNdx < results.violations.size(); ++violationNdx)
+               {
+                       log << TestLog::Message << "VIOLATION " << (violationNdx+1)
+                                                                                                       << ": " << results.violations[violationNdx]
+                                                                                                       << " (" << results.violations[violationNdx].record << ")"
+                               << TestLog::EndMessage;
+               }
+
+               log << TestLog::Message << "ERROR: Found " << results.violations.size() << " invalid allocation callbacks!" << TestLog::EndMessage;
+       }
+
+       // Verify live allocations
+       for (size_t liveNdx = 0; liveNdx < results.liveAllocations.size(); ++liveNdx)
+       {
+               const AllocationCallbackRecord&         record  = results.liveAllocations[liveNdx];
+               const VkSystemAllocationScope           scope   = record.type == AllocationCallbackRecord::TYPE_ALLOCATION              ? record.data.allocation.scope
+                                                                                                       : record.type == AllocationCallbackRecord::TYPE_REALLOCATION    ? record.data.reallocation.scope
+                                                                                                       : VK_SYSTEM_ALLOCATION_SCOPE_LAST;
+
+               DE_ASSERT(de::inBounds(scope, (VkSystemAllocationScope)0, VK_SYSTEM_ALLOCATION_SCOPE_LAST));
+
+               if ((allowedLiveAllocScopeBits & (1u << scope)) == 0)
+               {
+                       log << TestLog::Message << "LEAK " << (numLeaks+1) << ": " << record << TestLog::EndMessage;
+                       numLeaks += 1;
+               }
+       }
+
+       // Verify internal allocations
+       for (int internalAllocTypeNdx = 0; internalAllocTypeNdx < VK_INTERNAL_ALLOCATION_TYPE_LAST; ++internalAllocTypeNdx)
+       {
+               for (int scopeNdx = 0; scopeNdx < VK_SYSTEM_ALLOCATION_SCOPE_LAST; ++scopeNdx)
+               {
+                       const VkInternalAllocationType  type                    = (VkInternalAllocationType)internalAllocTypeNdx;
+                       const VkSystemAllocationScope   scope                   = (VkSystemAllocationScope)scopeNdx;
+                       const size_t                                    totalAllocated  = results.internalAllocationTotal[type][scope];
+
+                       if ((allowedLiveAllocScopeBits & (1u << scopeNdx)) == 0 &&
+                               totalAllocated > 0)
+                       {
+                               log << TestLog::Message << "LEAK " << (numLeaks+1) << ": " << totalAllocated
+                                                                               << " bytes of (" << type << ", " << scope << ") internal memory is still allocated"
+                                       << TestLog::EndMessage;
+                               numLeaks += 1;
+                       }
+               }
+       }
+
+       if (numLeaks > 0)
+               log << TestLog::Message << "ERROR: Found " << numLeaks << " memory leaks!" << TestLog::EndMessage;
+
+       return results.violations.empty() && numLeaks == 0;
+}
+
+bool validateAndLog (tcu::TestLog& log, const AllocationCallbackRecorder& recorder, deUint32 allowedLiveAllocScopeBits)
+{
+       AllocationCallbackValidationResults     validationResults;
+
+       validateAllocationCallbacks(recorder, &validationResults);
+
+       return checkAndLog(log, validationResults, allowedLiveAllocScopeBits);
+}
+
+std::ostream& operator<< (std::ostream& str, const AllocationCallbackRecord& record)
+{
+       switch (record.type)
+       {
+               case AllocationCallbackRecord::TYPE_ALLOCATION:
+                       str << "ALLOCATION: size=" << record.data.allocation.size
+                               << ", alignment=" << record.data.allocation.alignment
+                               << ", scope=" << record.data.allocation.scope
+                               << ", returnedPtr=" << tcu::toHex(record.data.allocation.returnedPtr);
+                       break;
+
+               case AllocationCallbackRecord::TYPE_REALLOCATION:
+                       str << "REALLOCATION: original=" << tcu::toHex(record.data.reallocation.original)
+                               << ", size=" << record.data.reallocation.size
+                               << ", alignment=" << record.data.reallocation.alignment
+                               << ", scope=" << record.data.reallocation.scope
+                               << ", returnedPtr=" << tcu::toHex(record.data.reallocation.returnedPtr);
+                       break;
+
+               case AllocationCallbackRecord::TYPE_FREE:
+                       str << "FREE: mem=" << tcu::toHex(record.data.free.mem);
+                       break;
+
+               case AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION:
+               case AllocationCallbackRecord::TYPE_INTERNAL_FREE:
+                       str << "INTERNAL_" << (record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION ? "ALLOCATION" : "FREE")
+                               << ": size=" << record.data.internalAllocation.size
+                               << ", type=" << record.data.internalAllocation.type
+                               << ", scope=" << record.data.internalAllocation.scope;
+                       break;
+
+               default:
+                       DE_ASSERT(false);
+       }
+
+       return str;
+}
+
+std::ostream& operator<< (std::ostream& str, const AllocationCallbackViolation& violation)
+{
+       switch (violation.reason)
+       {
+               case AllocationCallbackViolation::REASON_DOUBLE_FREE:
+               {
+                       DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_FREE);
+                       str << "Double free of " << tcu::toHex(violation.record.data.free.mem);
+                       break;
+               }
+
+               case AllocationCallbackViolation::REASON_FREE_NOT_ALLOCATED_PTR:
+               {
+                       DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_FREE);
+                       str << "Attempt to free " << tcu::toHex(violation.record.data.free.mem) << " which has not been allocated";
+                       break;
+               }
+
+               case AllocationCallbackViolation::REASON_REALLOC_NOT_ALLOCATED_PTR:
+               {
+                       DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_REALLOCATION);
+                       str << "Attempt to reallocate " << tcu::toHex(violation.record.data.reallocation.original) << " which has not been allocated";
+                       break;
+               }
+
+               case AllocationCallbackViolation::REASON_REALLOC_FREED_PTR:
+               {
+                       DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_REALLOCATION);
+                       str << "Attempt to reallocate " << tcu::toHex(violation.record.data.reallocation.original) << " which has been freed";
+                       break;
+               }
+
+               case AllocationCallbackViolation::REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL:
+               {
+                       DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE);
+                       str << "Internal allocation total for (" << violation.record.data.internalAllocation.type << ", " << violation.record.data.internalAllocation.scope << ") is negative";
+                       break;
+               }
+
+               case AllocationCallbackViolation::REASON_INVALID_INTERNAL_ALLOCATION_TYPE:
+               {
+                       DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION ||
+                                         violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE);
+                       str << "Invalid internal allocation type " << tcu::toHex(violation.record.data.internalAllocation.type);
+                       break;
+               }
+
+               case AllocationCallbackViolation::REASON_INVALID_ALLOCATION_SCOPE:
+               {
+                       str << "Invalid allocation scope";
+                       break;
+               }
+
+               case AllocationCallbackViolation::REASON_INVALID_ALIGNMENT:
+               {
+                       str << "Invalid alignment";
+                       break;
+               }
+
+               case AllocationCallbackViolation::REASON_REALLOC_DIFFERENT_ALIGNMENT:
+               {
+                       str << "Reallocation with different alignment";
+                       break;
+               }
+
+               default:
+                       DE_ASSERT(false);
+       }
+
+       return str;
+}
+
+} // vk
diff --git a/external/vulkancts/framework/vulkan/vkAllocationCallbackUtil.hpp b/external/vulkancts/framework/vulkan/vkAllocationCallbackUtil.hpp
new file mode 100644 (file)
index 0000000..057dc7e
--- /dev/null
@@ -0,0 +1,235 @@
+#ifndef _VKALLOCATIONCALLBACKUTIL_HPP
+#define _VKALLOCATIONCALLBACKUTIL_HPP
+/*-------------------------------------------------------------------------
+ * Vulkan CTS Framework
+ * --------------------
+ *
+ * Copyright (c) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be
+ * included in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by
+ * Khronos, at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Memory allocation callback utilities.
+ *//*--------------------------------------------------------------------*/
+
+#include "vkDefs.hpp"
+#include "deAppendList.hpp"
+
+#include <vector>
+#include <ostream>
+
+namespace tcu
+{
+class TestLog;
+}
+
+namespace vk
+{
+
+class AllocationCallbacks
+{
+public:
+                                                                       AllocationCallbacks             (void);
+       virtual                                                 ~AllocationCallbacks    (void);
+
+       virtual void*                                   allocate                                (size_t size, size_t alignment, VkSystemAllocationScope allocationScope) = 0;
+       virtual void*                                   reallocate                              (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) = 0;
+       virtual void                                    free                                    (void* mem) = 0;
+
+       virtual void                                    notifyInternalAllocation(size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) = 0;
+       virtual void                                    notifyInternalFree              (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) = 0;
+
+       const VkAllocationCallbacks*    getCallbacks                    (void) const { return &m_callbacks;     }
+
+private:
+       const VkAllocationCallbacks             m_callbacks;
+};
+
+struct AllocationCallbackRecord
+{
+       enum Type
+       {
+               TYPE_ALLOCATION         = 0,            //! Call to pfnAllocation
+               TYPE_REALLOCATION,                              //! Call to pfnReallocation
+               TYPE_FREE,                                              //! Call to pfnFree
+               TYPE_INTERNAL_ALLOCATION,               //! Call to pfnInternalAllocation
+               TYPE_INTERNAL_FREE,                             //! Call to pfnInternalFree
+
+               TYPE_LAST
+       };
+
+       Type    type;
+
+       union
+       {
+               struct
+               {
+                       size_t                                          size;
+                       size_t                                          alignment;
+                       VkSystemAllocationScope         scope;
+                       void*                                           returnedPtr;
+               } allocation;
+
+               struct
+               {
+                       void*                                           original;
+                       size_t                                          size;
+                       size_t                                          alignment;
+                       VkSystemAllocationScope         scope;
+                       void*                                           returnedPtr;
+               } reallocation;
+
+               struct
+               {
+                       void*                                           mem;
+               } free;
+
+               // \note Used for both INTERNAL_ALLOCATION and INTERNAL_FREE
+               struct
+               {
+                       size_t                                          size;
+                       VkInternalAllocationType        type;
+                       VkSystemAllocationScope         scope;
+               } internalAllocation;
+       } data;
+
+                                                                       AllocationCallbackRecord        (void) : type(TYPE_LAST) {}
+
+       static AllocationCallbackRecord allocation                                      (size_t size, size_t alignment, VkSystemAllocationScope scope, void* returnedPtr);
+       static AllocationCallbackRecord reallocation                            (void* original, size_t size, size_t alignment, VkSystemAllocationScope scope, void* returnedPtr);
+       static AllocationCallbackRecord free                                            (void* mem);
+       static AllocationCallbackRecord internalAllocation                      (size_t size, VkInternalAllocationType type, VkSystemAllocationScope scope);
+       static AllocationCallbackRecord internalFree                            (size_t size, VkInternalAllocationType type, VkSystemAllocationScope scope);
+};
+
+class ChainedAllocator : public AllocationCallbacks
+{
+public:
+                                                                       ChainedAllocator                (const VkAllocationCallbacks* nextAllocator);
+                                                                       ~ChainedAllocator               (void);
+
+       void*                                                   allocate                                (size_t size, size_t alignment, VkSystemAllocationScope allocationScope);
+       void*                                                   reallocate                              (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope);
+       void                                                    free                                    (void* mem);
+
+       void                                                    notifyInternalAllocation(size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope);
+       void                                                    notifyInternalFree              (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope);
+
+private:
+       const VkAllocationCallbacks*    m_nextAllocator;
+};
+
+class AllocationCallbackRecorder : public ChainedAllocator
+{
+public:
+                                                       AllocationCallbackRecorder      (const VkAllocationCallbacks* allocator, deUint32 callCountHint = 1024);
+                                                       ~AllocationCallbackRecorder     (void);
+
+       void*                                   allocate                                        (size_t size, size_t alignment, VkSystemAllocationScope allocationScope);
+       void*                                   reallocate                                      (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope);
+       void                                    free                                            (void* mem);
+
+       void                                    notifyInternalAllocation        (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope);
+       void                                    notifyInternalFree                      (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope);
+
+       typedef de::AppendList<AllocationCallbackRecord>::const_iterator        RecordIterator;
+
+       RecordIterator                  getRecordsBegin                         (void) const { return m_records.begin();        }
+       RecordIterator                  getRecordsEnd                           (void) const { return m_records.end();          }
+
+private:
+       typedef de::AppendList<AllocationCallbackRecord> Records;
+
+       Records                                 m_records;
+};
+
+//! Allocator that starts returning null after N allocs
+class DeterministicFailAllocator : public ChainedAllocator
+{
+public:
+                                                       DeterministicFailAllocator      (const VkAllocationCallbacks* allocator, deUint32 numPassingAllocs);
+                                                       ~DeterministicFailAllocator     (void);
+
+       void*                                   allocate                                        (size_t size, size_t alignment, VkSystemAllocationScope allocationScope);
+       void*                                   reallocate                                      (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope);
+
+private:
+       const deUint32                  m_numPassingAllocs;
+       volatile deUint32               m_allocationNdx;
+};
+
+struct AllocationCallbackViolation
+{
+       enum Reason
+       {
+               REASON_DOUBLE_FREE = 0,
+               REASON_FREE_NOT_ALLOCATED_PTR,
+               REASON_REALLOC_NOT_ALLOCATED_PTR,
+               REASON_REALLOC_FREED_PTR,
+               REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL,
+               REASON_INVALID_ALLOCATION_SCOPE,
+               REASON_INVALID_INTERNAL_ALLOCATION_TYPE,
+               REASON_INVALID_ALIGNMENT,
+               REASON_REALLOC_DIFFERENT_ALIGNMENT,
+
+               REASON_LAST
+       };
+
+       AllocationCallbackRecord        record;
+       Reason                                          reason;
+
+       AllocationCallbackViolation (void)
+               : reason(REASON_LAST)
+       {}
+
+       AllocationCallbackViolation (const AllocationCallbackRecord& record_, Reason reason_)
+               : record(record_)
+               , reason(reason_)
+       {}
+};
+
+struct AllocationCallbackValidationResults
+{
+       std::vector<AllocationCallbackRecord>           liveAllocations;
+       size_t                                                                          internalAllocationTotal[VK_INTERNAL_ALLOCATION_TYPE_LAST][VK_SYSTEM_ALLOCATION_SCOPE_LAST];
+       std::vector<AllocationCallbackViolation>        violations;
+
+                                                                                               AllocationCallbackValidationResults     (void);
+
+       void                                                                            clear                                                           (void);
+};
+
+void                                                   validateAllocationCallbacks     (const AllocationCallbackRecorder& recorder, AllocationCallbackValidationResults* results);
+bool                                                   checkAndLog                                     (tcu::TestLog& log, const AllocationCallbackValidationResults& results, deUint32 allowedLiveAllocScopeBits);
+bool                                                   validateAndLog                          (tcu::TestLog& log, const AllocationCallbackRecorder& recorder, deUint32 allowedLiveAllocScopeBits);
+
+std::ostream&                                  operator<<                                      (std::ostream& str, const AllocationCallbackRecord& record);
+std::ostream&                                  operator<<                                      (std::ostream& str, const AllocationCallbackViolation& violation);
+
+const VkAllocationCallbacks*   getSystemAllocator                      (void);
+
+} // vk
+
+#endif // _VKALLOCATIONCALLBACKUTIL_HPP