From: Pyry Haulos Date: Fri, 11 Dec 2015 22:20:04 +0000 (-0800) Subject: Add Vulkan memory allocation callback utilities X-Git-Tag: upstream/0.1.0~812^2~432^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ef47c8f5904465053dddcdfa1da0bdb46eb1d6ff;p=platform%2Fupstream%2FVK-GL-CTS.git Add Vulkan memory allocation callback utilities Change-Id: I3b81ee1f02825585c5bdcdf90f705d392dd7157b --- diff --git a/external/vulkancts/framework/vulkan/CMakeLists.txt b/external/vulkancts/framework/vulkan/CMakeLists.txt index b765088..9f76ec3 100644 --- a/external/vulkancts/framework/vulkan/CMakeLists.txt +++ b/external/vulkancts/framework/vulkan/CMakeLists.txt @@ -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 index 0000000..ec748af --- /dev/null +++ b/external/vulkancts/framework/vulkan/vkAllocationCallbackUtil.cpp @@ -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 + +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(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(pUserData)->reallocate(pOriginal, size, alignment, allocationScope); +} + +static VKAPI_ATTR void VKAPI_CALL freeCallback (void* pUserData, void* pMem) +{ + reinterpret_cast(pUserData)->free(pMem); +} + +static VKAPI_ATTR void VKAPI_CALL internalAllocationNotificationCallback (void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) +{ + reinterpret_cast(pUserData)->notifyInternalAllocation(size, allocationType, allocationScope); +} + +static VKAPI_ATTR void VKAPI_CALL internalFreeNotificationCallback (void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) +{ + reinterpret_cast(pUserData)->notifyInternalFree(size, allocationType, allocationScope); +} + +static VkAllocationCallbacks makeCallbacks (AllocationCallbacks* object) +{ + const VkAllocationCallbacks callbacks = + { + reinterpret_cast(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 allocations; + std::map 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::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 index 0000000..057dc7e --- /dev/null +++ b/external/vulkancts/framework/vulkan/vkAllocationCallbackUtil.hpp @@ -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 +#include + +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::const_iterator RecordIterator; + + RecordIterator getRecordsBegin (void) const { return m_records.begin(); } + RecordIterator getRecordsEnd (void) const { return m_records.end(); } + +private: + typedef de::AppendList 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 liveAllocations; + size_t internalAllocationTotal[VK_INTERNAL_ALLOCATION_TYPE_LAST][VK_SYSTEM_ALLOCATION_SCOPE_LAST]; + std::vector 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