blob: 122aedef54a515594f347253eecddb7c85e5e688 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 <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, 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<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:
{
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<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);
}
size_t getLiveSystemAllocationTotal (const AllocationCallbackValidationResults& validationResults)
{
size_t allocationTotal = 0;
DE_ASSERT(validationResults.violations.empty());
for (std::vector<AllocationCallbackRecord>::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