--- /dev/null
+/*-------------------------------------------------------------------------
+ * 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
--- /dev/null
+#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