Merge gerrit/vulkan-cts-1.0.0 into gerrit/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                                                 DE_ASSERT(!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr));
486                                                 ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size();
487                                                 allocations.push_back(AllocationSlot(record, true));
488                                         }
489                                 }
490
491                                 break;
492                         }
493
494                         case AllocationCallbackRecord::TYPE_FREE:
495                         {
496                                 if (record.data.free.mem != DE_NULL) // Freeing null pointer is valid and ignored
497                                 {
498                                         if (de::contains(ptrToSlotIndex, record.data.free.mem))
499                                         {
500                                                 const size_t    slotNdx         = ptrToSlotIndex[record.data.free.mem];
501
502                                                 if (allocations[slotNdx].isLive)
503                                                         allocations[slotNdx].isLive = false;
504                                                 else
505                                                         results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_DOUBLE_FREE));
506                                         }
507                                         else
508                                                 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_FREE_NOT_ALLOCATED_PTR));
509                                 }
510
511                                 break;
512                         }
513
514                         case AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION:
515                         case AllocationCallbackRecord::TYPE_INTERNAL_FREE:
516                         {
517                                 if (de::inBounds(record.data.internalAllocation.type, (VkInternalAllocationType)0, VK_INTERNAL_ALLOCATION_TYPE_LAST))
518                                 {
519                                         size_t* const           totalAllocSizePtr       = &results->internalAllocationTotal[record.data.internalAllocation.type][record.data.internalAllocation.scope];
520                                         const size_t            size                            = record.data.internalAllocation.size;
521
522                                         if (record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE)
523                                         {
524                                                 if (*totalAllocSizePtr < size)
525                                                 {
526                                                         results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL));
527                                                         *totalAllocSizePtr = 0; // Reset to 0 to suppress compound errors
528                                                 }
529                                                 else
530                                                         *totalAllocSizePtr -= size;
531                                         }
532                                         else
533                                                 *totalAllocSizePtr += size;
534                                 }
535                                 else
536                                         results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_INTERNAL_ALLOCATION_TYPE));
537
538                                 break;
539                         }
540
541                         default:
542                                 DE_ASSERT(false);
543                 }
544         }
545
546         DE_ASSERT(!de::contains(ptrToSlotIndex, DE_NULL));
547
548         // Collect live allocations
549         for (std::vector<AllocationSlot>::const_iterator slotIter = allocations.begin();
550                  slotIter != allocations.end();
551                  ++slotIter)
552         {
553                 if (slotIter->isLive)
554                         results->liveAllocations.push_back(slotIter->record);
555         }
556 }
557
558 bool checkAndLog (tcu::TestLog& log, const AllocationCallbackValidationResults& results, deUint32 allowedLiveAllocScopeBits)
559 {
560         using tcu::TestLog;
561
562         size_t  numLeaks        = 0;
563
564         if (!results.violations.empty())
565         {
566                 for (size_t violationNdx = 0; violationNdx < results.violations.size(); ++violationNdx)
567                 {
568                         log << TestLog::Message << "VIOLATION " << (violationNdx+1)
569                                                                                                         << ": " << results.violations[violationNdx]
570                                                                                                         << " (" << results.violations[violationNdx].record << ")"
571                                 << TestLog::EndMessage;
572                 }
573
574                 log << TestLog::Message << "ERROR: Found " << results.violations.size() << " invalid allocation callbacks!" << TestLog::EndMessage;
575         }
576
577         // Verify live allocations
578         for (size_t liveNdx = 0; liveNdx < results.liveAllocations.size(); ++liveNdx)
579         {
580                 const AllocationCallbackRecord&         record  = results.liveAllocations[liveNdx];
581                 const VkSystemAllocationScope           scope   = record.type == AllocationCallbackRecord::TYPE_ALLOCATION              ? record.data.allocation.scope
582                                                                                                         : record.type == AllocationCallbackRecord::TYPE_REALLOCATION    ? record.data.reallocation.scope
583                                                                                                         : VK_SYSTEM_ALLOCATION_SCOPE_LAST;
584
585                 DE_ASSERT(de::inBounds(scope, (VkSystemAllocationScope)0, VK_SYSTEM_ALLOCATION_SCOPE_LAST));
586
587                 if ((allowedLiveAllocScopeBits & (1u << scope)) == 0)
588                 {
589                         log << TestLog::Message << "LEAK " << (numLeaks+1) << ": " << record << TestLog::EndMessage;
590                         numLeaks += 1;
591                 }
592         }
593
594         // Verify internal allocations
595         for (int internalAllocTypeNdx = 0; internalAllocTypeNdx < VK_INTERNAL_ALLOCATION_TYPE_LAST; ++internalAllocTypeNdx)
596         {
597                 for (int scopeNdx = 0; scopeNdx < VK_SYSTEM_ALLOCATION_SCOPE_LAST; ++scopeNdx)
598                 {
599                         const VkInternalAllocationType  type                    = (VkInternalAllocationType)internalAllocTypeNdx;
600                         const VkSystemAllocationScope   scope                   = (VkSystemAllocationScope)scopeNdx;
601                         const size_t                                    totalAllocated  = results.internalAllocationTotal[type][scope];
602
603                         if ((allowedLiveAllocScopeBits & (1u << scopeNdx)) == 0 &&
604                                 totalAllocated > 0)
605                         {
606                                 log << TestLog::Message << "LEAK " << (numLeaks+1) << ": " << totalAllocated
607                                                                                 << " bytes of (" << type << ", " << scope << ") internal memory is still allocated"
608                                         << TestLog::EndMessage;
609                                 numLeaks += 1;
610                         }
611                 }
612         }
613
614         if (numLeaks > 0)
615                 log << TestLog::Message << "ERROR: Found " << numLeaks << " memory leaks!" << TestLog::EndMessage;
616
617         return results.violations.empty() && numLeaks == 0;
618 }
619
620 bool validateAndLog (tcu::TestLog& log, const AllocationCallbackRecorder& recorder, deUint32 allowedLiveAllocScopeBits)
621 {
622         AllocationCallbackValidationResults     validationResults;
623
624         validateAllocationCallbacks(recorder, &validationResults);
625
626         return checkAndLog(log, validationResults, allowedLiveAllocScopeBits);
627 }
628
629 size_t getLiveSystemAllocationTotal (const AllocationCallbackValidationResults& validationResults)
630 {
631         size_t  allocationTotal = 0;
632
633         DE_ASSERT(validationResults.violations.empty());
634
635         for (std::vector<AllocationCallbackRecord>::const_iterator alloc = validationResults.liveAllocations.begin();
636                  alloc != validationResults.liveAllocations.end();
637                  ++alloc)
638         {
639                 DE_ASSERT(alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ||
640                                   alloc->type == AllocationCallbackRecord::TYPE_REALLOCATION);
641
642                 const size_t    size            = (alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ? alloc->data.allocation.size : alloc->data.reallocation.size);
643                 const size_t    alignment       = (alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ? alloc->data.allocation.alignment : alloc->data.reallocation.alignment);
644
645                 allocationTotal += size + alignment - (alignment > 0 ? 1 : 0);
646         }
647
648         for (int internalAllocationTypeNdx = 0; internalAllocationTypeNdx < VK_INTERNAL_ALLOCATION_TYPE_LAST; ++internalAllocationTypeNdx)
649         {
650                 for (int internalAllocationScopeNdx = 0; internalAllocationScopeNdx < VK_SYSTEM_ALLOCATION_SCOPE_LAST; ++internalAllocationScopeNdx)
651                         allocationTotal += validationResults.internalAllocationTotal[internalAllocationTypeNdx][internalAllocationScopeNdx];
652         }
653
654         return allocationTotal;
655 }
656
657 std::ostream& operator<< (std::ostream& str, const AllocationCallbackRecord& record)
658 {
659         switch (record.type)
660         {
661                 case AllocationCallbackRecord::TYPE_ALLOCATION:
662                         str << "ALLOCATION: size=" << record.data.allocation.size
663                                 << ", alignment=" << record.data.allocation.alignment
664                                 << ", scope=" << record.data.allocation.scope
665                                 << ", returnedPtr=" << tcu::toHex(record.data.allocation.returnedPtr);
666                         break;
667
668                 case AllocationCallbackRecord::TYPE_REALLOCATION:
669                         str << "REALLOCATION: original=" << tcu::toHex(record.data.reallocation.original)
670                                 << ", size=" << record.data.reallocation.size
671                                 << ", alignment=" << record.data.reallocation.alignment
672                                 << ", scope=" << record.data.reallocation.scope
673                                 << ", returnedPtr=" << tcu::toHex(record.data.reallocation.returnedPtr);
674                         break;
675
676                 case AllocationCallbackRecord::TYPE_FREE:
677                         str << "FREE: mem=" << tcu::toHex(record.data.free.mem);
678                         break;
679
680                 case AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION:
681                 case AllocationCallbackRecord::TYPE_INTERNAL_FREE:
682                         str << "INTERNAL_" << (record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION ? "ALLOCATION" : "FREE")
683                                 << ": size=" << record.data.internalAllocation.size
684                                 << ", type=" << record.data.internalAllocation.type
685                                 << ", scope=" << record.data.internalAllocation.scope;
686                         break;
687
688                 default:
689                         DE_ASSERT(false);
690         }
691
692         return str;
693 }
694
695 std::ostream& operator<< (std::ostream& str, const AllocationCallbackViolation& violation)
696 {
697         switch (violation.reason)
698         {
699                 case AllocationCallbackViolation::REASON_DOUBLE_FREE:
700                 {
701                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_FREE);
702                         str << "Double free of " << tcu::toHex(violation.record.data.free.mem);
703                         break;
704                 }
705
706                 case AllocationCallbackViolation::REASON_FREE_NOT_ALLOCATED_PTR:
707                 {
708                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_FREE);
709                         str << "Attempt to free " << tcu::toHex(violation.record.data.free.mem) << " which has not been allocated";
710                         break;
711                 }
712
713                 case AllocationCallbackViolation::REASON_REALLOC_NOT_ALLOCATED_PTR:
714                 {
715                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_REALLOCATION);
716                         str << "Attempt to reallocate " << tcu::toHex(violation.record.data.reallocation.original) << " which has not been allocated";
717                         break;
718                 }
719
720                 case AllocationCallbackViolation::REASON_REALLOC_FREED_PTR:
721                 {
722                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_REALLOCATION);
723                         str << "Attempt to reallocate " << tcu::toHex(violation.record.data.reallocation.original) << " which has been freed";
724                         break;
725                 }
726
727                 case AllocationCallbackViolation::REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL:
728                 {
729                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE);
730                         str << "Internal allocation total for (" << violation.record.data.internalAllocation.type << ", " << violation.record.data.internalAllocation.scope << ") is negative";
731                         break;
732                 }
733
734                 case AllocationCallbackViolation::REASON_INVALID_INTERNAL_ALLOCATION_TYPE:
735                 {
736                         DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION ||
737                                           violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE);
738                         str << "Invalid internal allocation type " << tcu::toHex(violation.record.data.internalAllocation.type);
739                         break;
740                 }
741
742                 case AllocationCallbackViolation::REASON_INVALID_ALLOCATION_SCOPE:
743                 {
744                         str << "Invalid allocation scope";
745                         break;
746                 }
747
748                 case AllocationCallbackViolation::REASON_INVALID_ALIGNMENT:
749                 {
750                         str << "Invalid alignment";
751                         break;
752                 }
753
754                 case AllocationCallbackViolation::REASON_REALLOC_DIFFERENT_ALIGNMENT:
755                 {
756                         str << "Reallocation with different alignment";
757                         break;
758                 }
759
760                 default:
761                         DE_ASSERT(false);
762         }
763
764         return str;
765 }
766
767 } // vk