Merge vk-gl-cts/vulkan-cts-1.0.0 into vk-gl-cts/vulkan-cts-1.0.1
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / framework / vulkan / vkAllocationCallbackUtil.cpp
1 /*-------------------------------------------------------------------------
2  * Vulkan CTS Framework
3  * --------------------
4  *
5  * Copyright (c) 2015 Google Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Memory allocation callback utilities.
22  *//*--------------------------------------------------------------------*/
23
24 #include "vkAllocationCallbackUtil.hpp"
25 #include "tcuFormatUtil.hpp"
26 #include "tcuTestLog.hpp"
27 #include "deSTLUtil.hpp"
28 #include "deMemory.h"
29
30 #include <map>
31
32 namespace vk
33 {
34
35 // System default allocator
36
37 static VKAPI_ATTR void* VKAPI_CALL systemAllocate (void*, size_t size, size_t alignment, VkSystemAllocationScope)
38 {
39         if (size > 0)
40                 return deAlignedMalloc(size, (deUint32)alignment);
41         else
42                 return DE_NULL;
43 }
44
45 static VKAPI_ATTR void VKAPI_CALL systemFree (void*, void* pMem)
46 {
47         deAlignedFree(pMem);
48 }
49
50 static VKAPI_ATTR void* VKAPI_CALL systemReallocate (void*, void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope)
51 {
52         return deAlignedRealloc(pOriginal, size, alignment);
53 }
54
55 static VKAPI_ATTR void VKAPI_CALL systemInternalAllocationNotification (void*, size_t, VkInternalAllocationType, VkSystemAllocationScope)
56 {
57 }
58
59 static VKAPI_ATTR void VKAPI_CALL systemInternalFreeNotification (void*, size_t, VkInternalAllocationType, VkSystemAllocationScope)
60 {
61 }
62
63 static const VkAllocationCallbacks s_systemAllocator =
64 {
65         DE_NULL,                // pUserData
66         systemAllocate,
67         systemReallocate,
68         systemFree,
69         systemInternalAllocationNotification,
70         systemInternalFreeNotification,
71 };
72
73 const VkAllocationCallbacks* getSystemAllocator (void)
74 {
75         return &s_systemAllocator;
76 }
77
78 // AllocationCallbacks
79
80 static VKAPI_ATTR void* VKAPI_CALL allocationCallback (void* pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
81 {
82         return reinterpret_cast<AllocationCallbacks*>(pUserData)->allocate(size, alignment, allocationScope);
83 }
84
85 static VKAPI_ATTR void* VKAPI_CALL reallocationCallback (void* pUserData, void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
86 {
87         return reinterpret_cast<AllocationCallbacks*>(pUserData)->reallocate(pOriginal, size, alignment, allocationScope);
88 }
89
90 static VKAPI_ATTR void VKAPI_CALL freeCallback (void* pUserData, void* pMem)
91 {
92         reinterpret_cast<AllocationCallbacks*>(pUserData)->free(pMem);
93 }
94
95 static VKAPI_ATTR void VKAPI_CALL internalAllocationNotificationCallback (void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
96 {
97         reinterpret_cast<AllocationCallbacks*>(pUserData)->notifyInternalAllocation(size, allocationType, allocationScope);
98 }
99
100 static VKAPI_ATTR void VKAPI_CALL internalFreeNotificationCallback (void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
101 {
102         reinterpret_cast<AllocationCallbacks*>(pUserData)->notifyInternalFree(size, allocationType, allocationScope);
103 }
104
105 static VkAllocationCallbacks makeCallbacks (AllocationCallbacks* object)
106 {
107         const VkAllocationCallbacks callbacks =
108         {
109                 reinterpret_cast<void*>(object),
110                 allocationCallback,
111                 reallocationCallback,
112                 freeCallback,
113                 internalAllocationNotificationCallback,
114                 internalFreeNotificationCallback
115         };
116         return callbacks;
117 }
118
119 AllocationCallbacks::AllocationCallbacks (void)
120         : m_callbacks(makeCallbacks(this))
121 {
122 }
123
124 AllocationCallbacks::~AllocationCallbacks (void)
125 {
126 }
127
128 // AllocationCallbackRecord
129
130 AllocationCallbackRecord AllocationCallbackRecord::allocation (size_t size, size_t alignment, VkSystemAllocationScope scope, void* returnedPtr)
131 {
132         AllocationCallbackRecord record;
133
134         record.type                                                     = TYPE_ALLOCATION;
135         record.data.allocation.size                     = size;
136         record.data.allocation.alignment        = alignment;
137         record.data.allocation.scope            = scope;
138         record.data.allocation.returnedPtr      = returnedPtr;
139
140         return record;
141 }
142
143 AllocationCallbackRecord AllocationCallbackRecord::reallocation (void* original, size_t size, size_t alignment, VkSystemAllocationScope scope, void* returnedPtr)
144 {
145         AllocationCallbackRecord record;
146
147         record.type                                                             = TYPE_REALLOCATION;
148         record.data.reallocation.original               = original;
149         record.data.reallocation.size                   = size;
150         record.data.reallocation.alignment              = alignment;
151         record.data.reallocation.scope                  = scope;
152         record.data.reallocation.returnedPtr    = returnedPtr;
153
154         return record;
155 }
156
157 AllocationCallbackRecord AllocationCallbackRecord::free (void* mem)
158 {
159         AllocationCallbackRecord record;
160
161         record.type                             = TYPE_FREE;
162         record.data.free.mem    = mem;
163
164         return record;
165 }
166
167 AllocationCallbackRecord AllocationCallbackRecord::internalAllocation (size_t size, VkInternalAllocationType type, VkSystemAllocationScope scope)
168 {
169         AllocationCallbackRecord record;
170
171         record.type                                                             = TYPE_INTERNAL_ALLOCATION;
172         record.data.internalAllocation.size             = size;
173         record.data.internalAllocation.type             = type;
174         record.data.internalAllocation.scope    = scope;
175
176         return record;
177 }
178
179 AllocationCallbackRecord AllocationCallbackRecord::internalFree (size_t size, VkInternalAllocationType type, VkSystemAllocationScope scope)
180 {
181         AllocationCallbackRecord record;
182
183         record.type                                                             = TYPE_INTERNAL_FREE;
184         record.data.internalAllocation.size             = size;
185         record.data.internalAllocation.type             = type;
186         record.data.internalAllocation.scope    = scope;
187
188         return record;
189 }
190
191 // ChainedAllocator
192
193 ChainedAllocator::ChainedAllocator (const VkAllocationCallbacks* nextAllocator)
194         : m_nextAllocator(nextAllocator)
195 {
196 }
197
198 ChainedAllocator::~ChainedAllocator (void)
199 {
200 }
201
202 void* ChainedAllocator::allocate (size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
203 {
204         return m_nextAllocator->pfnAllocation(m_nextAllocator->pUserData, size, alignment, allocationScope);
205 }
206
207 void* ChainedAllocator::reallocate (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
208 {
209         return m_nextAllocator->pfnReallocation(m_nextAllocator->pUserData, original, size, alignment, allocationScope);
210 }
211
212 void ChainedAllocator::free (void* mem)
213 {
214         m_nextAllocator->pfnFree(m_nextAllocator->pUserData, mem);
215 }
216
217 void ChainedAllocator::notifyInternalAllocation (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
218 {
219         m_nextAllocator->pfnInternalAllocation(m_nextAllocator->pUserData, size, allocationType, allocationScope);
220 }
221
222 void ChainedAllocator::notifyInternalFree (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
223 {
224         m_nextAllocator->pfnInternalFree(m_nextAllocator->pUserData, size, allocationType, allocationScope);
225 }
226
227 // AllocationCallbackRecorder
228
229 AllocationCallbackRecorder::AllocationCallbackRecorder (const VkAllocationCallbacks* allocator, deUint32 callCountHint)
230         : ChainedAllocator      (allocator)
231         , m_records                     (callCountHint)
232 {
233 }
234
235 AllocationCallbackRecorder::~AllocationCallbackRecorder (void)
236 {
237 }
238
239 void* AllocationCallbackRecorder::allocate (size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
240 {
241         void* const     ptr     = ChainedAllocator::allocate(size, alignment, allocationScope);
242
243         m_records.append(AllocationCallbackRecord::allocation(size, alignment, allocationScope, ptr));
244
245         return ptr;
246 }
247
248 void* AllocationCallbackRecorder::reallocate (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
249 {
250         void* const     ptr     = ChainedAllocator::reallocate(original, size, alignment, allocationScope);
251
252         m_records.append(AllocationCallbackRecord::reallocation(original, size, alignment, allocationScope, ptr));
253
254         return ptr;
255 }
256
257 void AllocationCallbackRecorder::free (void* mem)
258 {
259         ChainedAllocator::free(mem);
260
261         m_records.append(AllocationCallbackRecord::free(mem));
262 }
263
264 void AllocationCallbackRecorder::notifyInternalAllocation (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
265 {
266         ChainedAllocator::notifyInternalAllocation(size, allocationType, allocationScope);
267
268         m_records.append(AllocationCallbackRecord::internalAllocation(size, allocationType, allocationScope));
269 }
270
271 void AllocationCallbackRecorder::notifyInternalFree (size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
272 {
273         ChainedAllocator::notifyInternalFree(size, allocationType, allocationScope);
274
275         m_records.append(AllocationCallbackRecord::internalFree(size, allocationType, allocationScope));
276 }
277
278 // DeterministicFailAllocator
279
280 DeterministicFailAllocator::DeterministicFailAllocator (const VkAllocationCallbacks* allocator, deUint32 numPassingAllocs)
281         : ChainedAllocator      (allocator)
282         , m_numPassingAllocs(numPassingAllocs)
283         , m_allocationNdx       (0)
284 {
285 }
286
287 DeterministicFailAllocator::~DeterministicFailAllocator (void)
288 {
289 }
290
291 void* DeterministicFailAllocator::allocate (size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
292 {
293         if (deAtomicIncrementUint32(&m_allocationNdx) <= m_numPassingAllocs)
294                 return ChainedAllocator::allocate(size, alignment, allocationScope);
295         else
296                 return DE_NULL;
297 }
298
299 void* DeterministicFailAllocator::reallocate (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
300 {
301         if (deAtomicIncrementUint32(&m_allocationNdx) <= m_numPassingAllocs)
302                 return ChainedAllocator::reallocate(original, size, alignment, allocationScope);
303         else
304                 return DE_NULL;
305 }
306
307 // Utils
308
309 AllocationCallbackValidationResults::AllocationCallbackValidationResults (void)
310 {
311         deMemset(internalAllocationTotal, 0, sizeof(internalAllocationTotal));
312 }
313
314 void AllocationCallbackValidationResults::clear (void)
315 {
316         liveAllocations.clear();
317         violations.clear();
318         deMemset(internalAllocationTotal, 0, sizeof(internalAllocationTotal));
319 }
320
321 namespace
322 {
323
324 struct AllocationSlot
325 {
326         AllocationCallbackRecord        record;
327         bool                                            isLive;
328
329         AllocationSlot (void)
330                 : isLive        (false)
331         {}
332
333         AllocationSlot (const AllocationCallbackRecord& record_, bool isLive_)
334                 : record        (record_)
335                 , isLive        (isLive_)
336         {}
337 };
338
339 size_t getAlignment (const AllocationCallbackRecord& record)
340 {
341         if (record.type == AllocationCallbackRecord::TYPE_ALLOCATION)
342                 return record.data.allocation.alignment;
343         else if (record.type == AllocationCallbackRecord::TYPE_REALLOCATION)
344                 return record.data.reallocation.alignment;
345         else
346         {
347                 DE_ASSERT(false);
348                 return 0;
349         }
350 }
351
352 } // anonymous
353
354 void validateAllocationCallbacks (const AllocationCallbackRecorder& recorder, AllocationCallbackValidationResults* results)
355 {
356         std::vector<AllocationSlot>             allocations;
357         std::map<void*, size_t>                 ptrToSlotIndex;
358
359         DE_ASSERT(results->liveAllocations.empty() && results->violations.empty());
360
361         for (AllocationCallbackRecorder::RecordIterator callbackIter = recorder.getRecordsBegin();
362                  callbackIter != recorder.getRecordsEnd();
363                  ++callbackIter)
364         {
365                 const AllocationCallbackRecord&         record  = *callbackIter;
366
367                 // Validate scope
368                 {
369                         const VkSystemAllocationScope* const    scopePtr        = record.type == AllocationCallbackRecord::TYPE_ALLOCATION                      ? &record.data.allocation.scope
370                                                                                                                                 : record.type == AllocationCallbackRecord::TYPE_REALLOCATION            ? &record.data.reallocation.scope
371                                                                                                                                 : record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION     ? &record.data.internalAllocation.scope
372                                                                                                                                 : record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE           ? &record.data.internalAllocation.scope
373                                                                                                                                 : DE_NULL;
374
375                         if (scopePtr && !de::inBounds(*scopePtr, (VkSystemAllocationScope)0, VK_SYSTEM_ALLOCATION_SCOPE_LAST))
376                                 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_ALLOCATION_SCOPE));
377                 }
378
379                 // Validate alignment
380                 if (record.type == AllocationCallbackRecord::TYPE_ALLOCATION ||
381                         record.type == AllocationCallbackRecord::TYPE_REALLOCATION)
382                 {
383                         if (!deIsPowerOfTwoSize(getAlignment(record)))
384                                 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_ALIGNMENT));
385                 }
386
387                 // Validate actual allocation behavior
388                 switch (record.type)
389                 {
390                         case AllocationCallbackRecord::TYPE_ALLOCATION:
391                         {
392                                 if (record.data.allocation.returnedPtr)
393                                 {
394                                         if (!de::contains(ptrToSlotIndex, record.data.allocation.returnedPtr))
395                                         {
396                                                 ptrToSlotIndex[record.data.allocation.returnedPtr] = allocations.size();
397                                                 allocations.push_back(AllocationSlot(record, true));
398                                         }
399                                         else
400                                         {
401                                                 const size_t            slotNdx         = ptrToSlotIndex[record.data.allocation.returnedPtr];
402                                                 if (!allocations[slotNdx].isLive)
403                                                 {
404                                                         allocations[slotNdx].isLive = true;
405                                                         allocations[slotNdx].record = record;
406                                                 }
407                                                 else
408                                                 {
409                                                         // we should not have multiple live allocations with the same pointer
410                                                         DE_ASSERT(false);
411                                                 }
412                                         }
413                                 }
414
415                                 break;
416                         }
417
418                         case AllocationCallbackRecord::TYPE_REALLOCATION:
419                         {
420                                 if (de::contains(ptrToSlotIndex, record.data.reallocation.original))
421                                 {
422                                         const size_t            origSlotNdx             = ptrToSlotIndex[record.data.reallocation.original];
423                                         AllocationSlot&         origSlot                = allocations[origSlotNdx];
424
425                                         DE_ASSERT(record.data.reallocation.original != DE_NULL);
426
427                                         if (record.data.reallocation.size > 0)
428                                         {
429                                                 if (getAlignment(origSlot.record) != record.data.reallocation.alignment)
430                                                         results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_DIFFERENT_ALIGNMENT));
431
432                                                 if (record.data.reallocation.original == record.data.reallocation.returnedPtr)
433                                                 {
434                                                         if (!origSlot.isLive)
435                                                         {
436                                                                 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_FREED_PTR));
437                                                                 origSlot.isLive = true; // Mark live to suppress further errors
438                                                         }
439
440                                                         // Just update slot record
441                                                         allocations[origSlotNdx].record = record;
442                                                 }
443                                                 else
444                                                 {
445                                                         if (record.data.reallocation.returnedPtr)
446                                                         {
447                                                                 allocations[origSlotNdx].isLive = false;
448                                                                 if (!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr))
449                                                                 {
450                                                                         ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size();
451                                                                         allocations.push_back(AllocationSlot(record, true));
452                                                                 }
453                                                                 else
454                                                                 {
455                                                                         const size_t slotNdx = ptrToSlotIndex[record.data.reallocation.returnedPtr];
456                                                                         if (!allocations[slotNdx].isLive)
457                                                                         {
458                                                                                 allocations[slotNdx].isLive = true;
459                                                                                 allocations[slotNdx].record = record;
460                                                                         }
461                                                                         else
462                                                                         {
463                                                                                 // we should not have multiple live allocations with the same pointer
464                                                                                 DE_ASSERT(false);
465                                                                         }
466                                                                 }
467                                                         }
468                                                         // else original ptr remains valid and live
469                                                 }
470                                         }
471                                         else
472                                         {
473                                                 DE_ASSERT(!record.data.reallocation.returnedPtr);
474
475                                                 origSlot.isLive = false;
476                                         }
477                                 }
478                                 else
479                                 {
480                                         if (record.data.reallocation.original)
481                                                 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_NOT_ALLOCATED_PTR));
482
483                                         if (record.data.reallocation.returnedPtr)
484                                         {
485                                                 if (!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr))
486                                                 {
487                                                         ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size();
488                                                         allocations.push_back(AllocationSlot(record, true));
489                                                 }
490                                                 else
491                                                 {
492                                                         const size_t slotNdx = ptrToSlotIndex[record.data.reallocation.returnedPtr];
493                                                         DE_ASSERT(!allocations[slotNdx].isLive);
494                                                         allocations[slotNdx].isLive = true;
495                                                         allocations[slotNdx].record = record;
496                                                 }
497                                         }
498                                 }
499
500                                 break;
501                         }
502
503                         case AllocationCallbackRecord::TYPE_FREE:
504                         {
505                                 if (record.data.free.mem != DE_NULL) // Freeing null pointer is valid and ignored
506                                 {
507                                         if (de::contains(ptrToSlotIndex, record.data.free.mem))
508                                         {
509                                                 const size_t    slotNdx         = ptrToSlotIndex[record.data.free.mem];
510
511                                                 if (allocations[slotNdx].isLive)
512                                                         allocations[slotNdx].isLive = false;
513                                                 else
514                                                         results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_DOUBLE_FREE));
515                                         }
516                                         else
517                                                 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_FREE_NOT_ALLOCATED_PTR));
518                                 }
519
520                                 break;
521                         }
522
523                         case AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION:
524                         case AllocationCallbackRecord::TYPE_INTERNAL_FREE:
525                         {
526                                 if (de::inBounds(record.data.internalAllocation.type, (VkInternalAllocationType)0, VK_INTERNAL_ALLOCATION_TYPE_LAST))
527                                 {
528                                         size_t* const           totalAllocSizePtr       = &results->internalAllocationTotal[record.data.internalAllocation.type][record.data.internalAllocation.scope];
529                                         const size_t            size                            = record.data.internalAllocation.size;
530
531                                         if (record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE)
532                                         {
533                                                 if (*totalAllocSizePtr < size)
534                                                 {
535                                                         results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL));
536                                                         *totalAllocSizePtr = 0; // Reset to 0 to suppress compound errors
537                                                 }
538                                                 else
539                                                         *totalAllocSizePtr -= size;
540                                         }
541                                         else
542                                                 *totalAllocSizePtr += size;
543                                 }
544                                 else
545                                         results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_INTERNAL_ALLOCATION_TYPE));
546
547                                 break;
548                         }
549
550                         default:
551                                 DE_ASSERT(false);
552                 }
553         }
554
555         DE_ASSERT(!de::contains(ptrToSlotIndex, DE_NULL));
556
557         // Collect live allocations
558         for (std::vector<AllocationSlot>::const_iterator slotIter = allocations.begin();
559                  slotIter != allocations.end();
560                  ++slotIter)
561         {
562                 if (slotIter->isLive)
563                         results->liveAllocations.push_back(slotIter->record);
564         }
565 }
566
567 bool checkAndLog (tcu::TestLog& log, const AllocationCallbackValidationResults& results, deUint32 allowedLiveAllocScopeBits)
568 {
569         using tcu::TestLog;
570
571         size_t  numLeaks        = 0;
572
573         if (!results.violations.empty())
574         {
575                 for (size_t violationNdx = 0; violationNdx < results.violations.size(); ++violationNdx)
576                 {
577                         log << TestLog::Message << "VIOLATION " << (violationNdx+1)
578                                                                                                         << ": " << results.violations[violationNdx]
579                                                                                                         << " (" << results.violations[violationNdx].record << ")"
580                                 << TestLog::EndMessage;
581                 }
582
583                 log << TestLog::Message << "ERROR: Found " << results.violations.size() << " invalid allocation callbacks!" << TestLog::EndMessage;
584         }
585
586         // Verify live allocations
587         for (size_t liveNdx = 0; liveNdx < results.liveAllocations.size(); ++liveNdx)
588         {
589                 const AllocationCallbackRecord&         record  = results.liveAllocations[liveNdx];
590                 const VkSystemAllocationScope           scope   = record.type == AllocationCallbackRecord::TYPE_ALLOCATION              ? record.data.allocation.scope
591                                                                                                         : record.type == AllocationCallbackRecord::TYPE_REALLOCATION    ? record.data.reallocation.scope
592                                                                                                         : VK_SYSTEM_ALLOCATION_SCOPE_LAST;
593
594                 DE_ASSERT(de::inBounds(scope, (VkSystemAllocationScope)0, VK_SYSTEM_ALLOCATION_SCOPE_LAST));
595
596                 if ((allowedLiveAllocScopeBits & (1u << scope)) == 0)
597                 {
598                         log << TestLog::Message << "LEAK " << (numLeaks+1) << ": " << record << TestLog::EndMessage;
599                         numLeaks += 1;
600                 }
601         }
602
603         // Verify internal allocations
604         for (int internalAllocTypeNdx = 0; internalAllocTypeNdx < VK_INTERNAL_ALLOCATION_TYPE_LAST; ++internalAllocTypeNdx)
605         {
606                 for (int scopeNdx = 0; scopeNdx < VK_SYSTEM_ALLOCATION_SCOPE_LAST; ++scopeNdx)
607                 {
608                         const VkInternalAllocationType  type                    = (VkInternalAllocationType)internalAllocTypeNdx;
609                         const VkSystemAllocationScope   scope                   = (VkSystemAllocationScope)scopeNdx;
610                         const size_t                                    totalAllocated  = results.internalAllocationTotal[type][scope];
611
612                         if ((allowedLiveAllocScopeBits & (1u << scopeNdx)) == 0 &&
613                                 totalAllocated > 0)
614                         {
615                                 log << TestLog::Message << "LEAK " << (numLeaks+1) << ": " << totalAllocated
616                                                                                 << " bytes of (" << type << ", " << scope << ") internal memory is still allocated"
617                                         << TestLog::EndMessage;
618                                 numLeaks += 1;
619                         }
620                 }
621         }
622
623         if (numLeaks > 0)
624                 log << TestLog::Message << "ERROR: Found " << numLeaks << " memory leaks!" << TestLog::EndMessage;
625
626         return results.violations.empty() && numLeaks == 0;
627 }
628
629 bool validateAndLog (tcu::TestLog& log, const AllocationCallbackRecorder& recorder, deUint32 allowedLiveAllocScopeBits)
630 {
631         AllocationCallbackValidationResults     validationResults;
632
633         validateAllocationCallbacks(recorder, &validationResults);
634
635         return checkAndLog(log, validationResults, allowedLiveAllocScopeBits);
636 }
637
638 size_t getLiveSystemAllocationTotal (const AllocationCallbackValidationResults& validationResults)
639 {
640         size_t  allocationTotal = 0;
641
642         DE_ASSERT(validationResults.violations.empty());
643
644         for (std::vector<AllocationCallbackRecord>::const_iterator alloc = validationResults.liveAllocations.begin();
645                  alloc != validationResults.liveAllocations.end();
646                  ++alloc)
647         {
648                 DE_ASSERT(alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ||
649                                   alloc->type == AllocationCallbackRecord::TYPE_REALLOCATION);
650
651                 const size_t    size            = (alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ? alloc->data.allocation.size : alloc->data.reallocation.size);
652                 const size_t    alignment       = (alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ? alloc->data.allocation.alignment : alloc->data.reallocation.alignment);
653
654                 allocationTotal += size + alignment - (alignment > 0 ? 1 : 0);
655         }
656
657         for (int internalAllocationTypeNdx = 0; internalAllocationTypeNdx < VK_INTERNAL_ALLOCATION_TYPE_LAST; ++internalAllocationTypeNdx)
658         {
659                 for (int internalAllocationScopeNdx = 0; internalAllocationScopeNdx < VK_SYSTEM_ALLOCATION_SCOPE_LAST; ++internalAllocationScopeNdx)
660                         allocationTotal += validationResults.internalAllocationTotal[internalAllocationTypeNdx][internalAllocationScopeNdx];
661         }
662
663         return allocationTotal;
664 }
665
666 std::ostream& operator<< (std::ostream& str, const AllocationCallbackRecord& record)
667 {
668         switch (record.type)
669         {
670                 case AllocationCallbackRecord::TYPE_ALLOCATION:
671                         str << "ALLOCATION: size=" << record.data.allocation.size
672                                 << ", alignment=" << record.data.allocation.alignment
673                                 << ", scope=" << record.data.allocation.scope
674                                 << ", returnedPtr=" << tcu::toHex(record.data.allocation.returnedPtr);
675                         break;
676
677                 case AllocationCallbackRecord::TYPE_REALLOCATION:
678                         str << "REALLOCATION: original=" << tcu::toHex(record.data.reallocation.original)
679                                 << ", size=" << record.data.reallocation.size
680                                 << ", alignment=" << record.data.reallocation.alignment
681                                 << ", scope=" << record.data.reallocation.scope
682                                 << ", returnedPtr=" << tcu::toHex(record.data.reallocation.returnedPtr);
683                         break;
684
685                 case AllocationCallbackRecord::TYPE_FREE:
686                         str << "FREE: mem=" << tcu::toHex(record.data.free.mem);
687                         break;
688
689                 case AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION:
690                 case AllocationCallbackRecord::TYPE_INTERNAL_FREE:
691                         str << "INTERNAL_" << (record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION ? "ALLOCATION" : "FREE")
692                                 << ": size=" << record.data.internalAllocation.size
693                                 << ", type=" << record.data.internalAllocation.type
694                                 << ", scope=" << record.data.internalAllocation.scope;
695                         break;
696
697                 default:
698                         DE_ASSERT(false);
699         }
700
701         return str;
702 }
703
704 std::ostream& operator<< (std::ostream& str, const AllocationCallbackViolation& violation)
705 {
706         switch (violation.reason)
707         {
708                 case AllocationCallbackViolation::REASON_DOUBLE_FREE:
709                 {
710                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_FREE);
711                         str << "Double free of " << tcu::toHex(violation.record.data.free.mem);
712                         break;
713                 }
714
715                 case AllocationCallbackViolation::REASON_FREE_NOT_ALLOCATED_PTR:
716                 {
717                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_FREE);
718                         str << "Attempt to free " << tcu::toHex(violation.record.data.free.mem) << " which has not been allocated";
719                         break;
720                 }
721
722                 case AllocationCallbackViolation::REASON_REALLOC_NOT_ALLOCATED_PTR:
723                 {
724                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_REALLOCATION);
725                         str << "Attempt to reallocate " << tcu::toHex(violation.record.data.reallocation.original) << " which has not been allocated";
726                         break;
727                 }
728
729                 case AllocationCallbackViolation::REASON_REALLOC_FREED_PTR:
730                 {
731                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_REALLOCATION);
732                         str << "Attempt to reallocate " << tcu::toHex(violation.record.data.reallocation.original) << " which has been freed";
733                         break;
734                 }
735
736                 case AllocationCallbackViolation::REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL:
737                 {
738                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE);
739                         str << "Internal allocation total for (" << violation.record.data.internalAllocation.type << ", " << violation.record.data.internalAllocation.scope << ") is negative";
740                         break;
741                 }
742
743                 case AllocationCallbackViolation::REASON_INVALID_INTERNAL_ALLOCATION_TYPE:
744                 {
745                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION ||
746                                           violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE);
747                         str << "Invalid internal allocation type " << tcu::toHex(violation.record.data.internalAllocation.type);
748                         break;
749                 }
750
751                 case AllocationCallbackViolation::REASON_INVALID_ALLOCATION_SCOPE:
752                 {
753                         str << "Invalid allocation scope";
754                         break;
755                 }
756
757                 case AllocationCallbackViolation::REASON_INVALID_ALIGNMENT:
758                 {
759                         str << "Invalid alignment";
760                         break;
761                 }
762
763                 case AllocationCallbackViolation::REASON_REALLOC_DIFFERENT_ALIGNMENT:
764                 {
765                         str << "Reallocation with different alignment";
766                         break;
767                 }
768
769                 default:
770                         DE_ASSERT(false);
771         }
772
773         return str;
774 }
775
776 } // vk