/*------------------------------------------------------------------------- * Vulkan CTS Framework * -------------------- * * Copyright (c) 2015 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief 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, Mode mode, deUint32 numPassingAllocs) : ChainedAllocator (allocator) { reset(mode, numPassingAllocs); } DeterministicFailAllocator::~DeterministicFailAllocator (void) { } void DeterministicFailAllocator::reset (Mode mode, deUint32 numPassingAllocs) { m_mode = mode; m_numPassingAllocs = numPassingAllocs; m_allocationNdx = 0; } void* DeterministicFailAllocator::allocate (size_t size, size_t alignment, VkSystemAllocationScope allocationScope) { if ((m_mode == MODE_DO_NOT_COUNT) || (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 ((m_mode == MODE_DO_NOT_COUNT) || (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: { if (record.data.allocation.returnedPtr) { if (!de::contains(ptrToSlotIndex, record.data.allocation.returnedPtr)) { ptrToSlotIndex[record.data.allocation.returnedPtr] = allocations.size(); allocations.push_back(AllocationSlot(record, true)); } else { const size_t slotNdx = ptrToSlotIndex[record.data.allocation.returnedPtr]; if (!allocations[slotNdx].isLive) { allocations[slotNdx].isLive = true; allocations[slotNdx].record = record; } else { // we should not have multiple live allocations with the same pointer DE_ASSERT(false); } } } 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 { if (record.data.reallocation.returnedPtr) { allocations[origSlotNdx].isLive = false; if (!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr)) { ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size(); allocations.push_back(AllocationSlot(record, true)); } else { const size_t slotNdx = ptrToSlotIndex[record.data.reallocation.returnedPtr]; if (!allocations[slotNdx].isLive) { allocations[slotNdx].isLive = true; allocations[slotNdx].record = record; } else { // we should not have multiple live allocations with the same pointer DE_ASSERT(false); } } } // 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) { if (!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr)) { ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size(); allocations.push_back(AllocationSlot(record, true)); } else { const size_t slotNdx = ptrToSlotIndex[record.data.reallocation.returnedPtr]; DE_ASSERT(!allocations[slotNdx].isLive); allocations[slotNdx].isLive = true; allocations[slotNdx].record = record; } } } 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_INTERNAL_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); } size_t getLiveSystemAllocationTotal (const AllocationCallbackValidationResults& validationResults) { size_t allocationTotal = 0; DE_ASSERT(validationResults.violations.empty()); for (std::vector::const_iterator alloc = validationResults.liveAllocations.begin(); alloc != validationResults.liveAllocations.end(); ++alloc) { DE_ASSERT(alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION || alloc->type == AllocationCallbackRecord::TYPE_REALLOCATION); const size_t size = (alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ? alloc->data.allocation.size : alloc->data.reallocation.size); const size_t alignment = (alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ? alloc->data.allocation.alignment : alloc->data.reallocation.alignment); allocationTotal += size + alignment - (alignment > 0 ? 1 : 0); } for (int internalAllocationTypeNdx = 0; internalAllocationTypeNdx < VK_INTERNAL_ALLOCATION_TYPE_LAST; ++internalAllocationTypeNdx) { for (int internalAllocationScopeNdx = 0; internalAllocationScopeNdx < VK_SYSTEM_ALLOCATION_SCOPE_LAST; ++internalAllocationScopeNdx) allocationTotal += validationResults.internalAllocationTotal[internalAllocationTypeNdx][internalAllocationScopeNdx]; } return allocationTotal; } 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