blob: 393455eb563999cda2ecd70e3770f7f1f7493e6d [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, (uint32_t)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, uint32_t 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,
uint32_t numPassingAllocs)
: ChainedAllocator(allocator)
{
reset(mode, numPassingAllocs);
}
DeterministicFailAllocator::~DeterministicFailAllocator(void)
{
}
void DeterministicFailAllocator::reset(Mode mode, uint32_t 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;
}
}
} // namespace
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,
uint32_t 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, uint32_t 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;
}
} // namespace vk