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, Mode mode, deUint32 numPassingAllocs) 281 : ChainedAllocator (allocator) 282 { 283 reset(mode, numPassingAllocs); 284 } 285 286 DeterministicFailAllocator::~DeterministicFailAllocator (void) 287 { 288 } 289 290 void DeterministicFailAllocator::reset (Mode mode, deUint32 numPassingAllocs) 291 { 292 m_mode = mode; 293 m_numPassingAllocs = numPassingAllocs; 294 m_allocationNdx = 0; 295 } 296 297 void* DeterministicFailAllocator::allocate (size_t size, size_t alignment, VkSystemAllocationScope allocationScope) 298 { 299 if ((m_mode == MODE_DO_NOT_COUNT) || 300 (deAtomicIncrementUint32(&m_allocationNdx) <= m_numPassingAllocs)) 301 return ChainedAllocator::allocate(size, alignment, allocationScope); 302 else 303 return DE_NULL; 304 } 305 306 void* DeterministicFailAllocator::reallocate (void* original, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) 307 { 308 if ((m_mode == MODE_DO_NOT_COUNT) || 309 (deAtomicIncrementUint32(&m_allocationNdx) <= m_numPassingAllocs)) 310 return ChainedAllocator::reallocate(original, size, alignment, allocationScope); 311 else 312 return DE_NULL; 313 } 314 315 // Utils 316 317 AllocationCallbackValidationResults::AllocationCallbackValidationResults (void) 318 { 319 deMemset(internalAllocationTotal, 0, sizeof(internalAllocationTotal)); 320 } 321 322 void AllocationCallbackValidationResults::clear (void) 323 { 324 liveAllocations.clear(); 325 violations.clear(); 326 deMemset(internalAllocationTotal, 0, sizeof(internalAllocationTotal)); 327 } 328 329 namespace 330 { 331 332 struct AllocationSlot 333 { 334 AllocationCallbackRecord record; 335 bool isLive; 336 337 AllocationSlot (void) 338 : isLive (false) 339 {} 340 341 AllocationSlot (const AllocationCallbackRecord& record_, bool isLive_) 342 : record (record_) 343 , isLive (isLive_) 344 {} 345 }; 346 347 size_t getAlignment (const AllocationCallbackRecord& record) 348 { 349 if (record.type == AllocationCallbackRecord::TYPE_ALLOCATION) 350 return record.data.allocation.alignment; 351 else if (record.type == AllocationCallbackRecord::TYPE_REALLOCATION) 352 return record.data.reallocation.alignment; 353 else 354 { 355 DE_ASSERT(false); 356 return 0; 357 } 358 } 359 360 } // anonymous 361 362 void validateAllocationCallbacks (const AllocationCallbackRecorder& recorder, AllocationCallbackValidationResults* results) 363 { 364 std::vector<AllocationSlot> allocations; 365 std::map<void*, size_t> ptrToSlotIndex; 366 367 DE_ASSERT(results->liveAllocations.empty() && results->violations.empty()); 368 369 for (AllocationCallbackRecorder::RecordIterator callbackIter = recorder.getRecordsBegin(); 370 callbackIter != recorder.getRecordsEnd(); 371 ++callbackIter) 372 { 373 const AllocationCallbackRecord& record = *callbackIter; 374 375 // Validate scope 376 { 377 const VkSystemAllocationScope* const scopePtr = record.type == AllocationCallbackRecord::TYPE_ALLOCATION ? &record.data.allocation.scope 378 : record.type == AllocationCallbackRecord::TYPE_REALLOCATION ? &record.data.reallocation.scope 379 : record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION ? &record.data.internalAllocation.scope 380 : record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE ? &record.data.internalAllocation.scope 381 : DE_NULL; 382 383 if (scopePtr && !de::inBounds(*scopePtr, (VkSystemAllocationScope)0, VK_SYSTEM_ALLOCATION_SCOPE_LAST)) 384 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_ALLOCATION_SCOPE)); 385 } 386 387 // Validate alignment 388 if (record.type == AllocationCallbackRecord::TYPE_ALLOCATION || 389 record.type == AllocationCallbackRecord::TYPE_REALLOCATION) 390 { 391 if (!deIsPowerOfTwoSize(getAlignment(record))) 392 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_ALIGNMENT)); 393 } 394 395 // Validate actual allocation behavior 396 switch (record.type) 397 { 398 case AllocationCallbackRecord::TYPE_ALLOCATION: 399 { 400 if (record.data.allocation.returnedPtr) 401 { 402 if (!de::contains(ptrToSlotIndex, record.data.allocation.returnedPtr)) 403 { 404 ptrToSlotIndex[record.data.allocation.returnedPtr] = allocations.size(); 405 allocations.push_back(AllocationSlot(record, true)); 406 } 407 else 408 { 409 const size_t slotNdx = ptrToSlotIndex[record.data.allocation.returnedPtr]; 410 if (!allocations[slotNdx].isLive) 411 { 412 allocations[slotNdx].isLive = true; 413 allocations[slotNdx].record = record; 414 } 415 else 416 { 417 // we should not have multiple live allocations with the same pointer 418 DE_ASSERT(false); 419 } 420 } 421 } 422 423 break; 424 } 425 426 case AllocationCallbackRecord::TYPE_REALLOCATION: 427 { 428 if (de::contains(ptrToSlotIndex, record.data.reallocation.original)) 429 { 430 const size_t origSlotNdx = ptrToSlotIndex[record.data.reallocation.original]; 431 AllocationSlot& origSlot = allocations[origSlotNdx]; 432 433 DE_ASSERT(record.data.reallocation.original != DE_NULL); 434 435 if (record.data.reallocation.size > 0) 436 { 437 if (getAlignment(origSlot.record) != record.data.reallocation.alignment) 438 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_DIFFERENT_ALIGNMENT)); 439 440 if (record.data.reallocation.original == record.data.reallocation.returnedPtr) 441 { 442 if (!origSlot.isLive) 443 { 444 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_FREED_PTR)); 445 origSlot.isLive = true; // Mark live to suppress further errors 446 } 447 448 // Just update slot record 449 allocations[origSlotNdx].record = record; 450 } 451 else 452 { 453 if (record.data.reallocation.returnedPtr) 454 { 455 allocations[origSlotNdx].isLive = false; 456 if (!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr)) 457 { 458 ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size(); 459 allocations.push_back(AllocationSlot(record, true)); 460 } 461 else 462 { 463 const size_t slotNdx = ptrToSlotIndex[record.data.reallocation.returnedPtr]; 464 if (!allocations[slotNdx].isLive) 465 { 466 allocations[slotNdx].isLive = true; 467 allocations[slotNdx].record = record; 468 } 469 else 470 { 471 // we should not have multiple live allocations with the same pointer 472 DE_ASSERT(false); 473 } 474 } 475 } 476 // else original ptr remains valid and live 477 } 478 } 479 else 480 { 481 DE_ASSERT(!record.data.reallocation.returnedPtr); 482 483 origSlot.isLive = false; 484 } 485 } 486 else 487 { 488 if (record.data.reallocation.original) 489 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_REALLOC_NOT_ALLOCATED_PTR)); 490 491 if (record.data.reallocation.returnedPtr) 492 { 493 if (!de::contains(ptrToSlotIndex, record.data.reallocation.returnedPtr)) 494 { 495 ptrToSlotIndex[record.data.reallocation.returnedPtr] = allocations.size(); 496 allocations.push_back(AllocationSlot(record, true)); 497 } 498 else 499 { 500 const size_t slotNdx = ptrToSlotIndex[record.data.reallocation.returnedPtr]; 501 DE_ASSERT(!allocations[slotNdx].isLive); 502 allocations[slotNdx].isLive = true; 503 allocations[slotNdx].record = record; 504 } 505 } 506 } 507 508 break; 509 } 510 511 case AllocationCallbackRecord::TYPE_FREE: 512 { 513 if (record.data.free.mem != DE_NULL) // Freeing null pointer is valid and ignored 514 { 515 if (de::contains(ptrToSlotIndex, record.data.free.mem)) 516 { 517 const size_t slotNdx = ptrToSlotIndex[record.data.free.mem]; 518 519 if (allocations[slotNdx].isLive) 520 allocations[slotNdx].isLive = false; 521 else 522 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_DOUBLE_FREE)); 523 } 524 else 525 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_FREE_NOT_ALLOCATED_PTR)); 526 } 527 528 break; 529 } 530 531 case AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION: 532 case AllocationCallbackRecord::TYPE_INTERNAL_FREE: 533 { 534 if (de::inBounds(record.data.internalAllocation.type, (VkInternalAllocationType)0, VK_INTERNAL_ALLOCATION_TYPE_LAST)) 535 { 536 size_t* const totalAllocSizePtr = &results->internalAllocationTotal[record.data.internalAllocation.type][record.data.internalAllocation.scope]; 537 const size_t size = record.data.internalAllocation.size; 538 539 if (record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE) 540 { 541 if (*totalAllocSizePtr < size) 542 { 543 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL)); 544 *totalAllocSizePtr = 0; // Reset to 0 to suppress compound errors 545 } 546 else 547 *totalAllocSizePtr -= size; 548 } 549 else 550 *totalAllocSizePtr += size; 551 } 552 else 553 results->violations.push_back(AllocationCallbackViolation(record, AllocationCallbackViolation::REASON_INVALID_INTERNAL_ALLOCATION_TYPE)); 554 555 break; 556 } 557 558 default: 559 DE_ASSERT(false); 560 } 561 } 562 563 DE_ASSERT(!de::contains(ptrToSlotIndex, DE_NULL)); 564 565 // Collect live allocations 566 for (std::vector<AllocationSlot>::const_iterator slotIter = allocations.begin(); 567 slotIter != allocations.end(); 568 ++slotIter) 569 { 570 if (slotIter->isLive) 571 results->liveAllocations.push_back(slotIter->record); 572 } 573 } 574 575 bool checkAndLog (tcu::TestLog& log, const AllocationCallbackValidationResults& results, deUint32 allowedLiveAllocScopeBits) 576 { 577 using tcu::TestLog; 578 579 size_t numLeaks = 0; 580 581 if (!results.violations.empty()) 582 { 583 for (size_t violationNdx = 0; violationNdx < results.violations.size(); ++violationNdx) 584 { 585 log << TestLog::Message << "VIOLATION " << (violationNdx+1) 586 << ": " << results.violations[violationNdx] 587 << " (" << results.violations[violationNdx].record << ")" 588 << TestLog::EndMessage; 589 } 590 591 log << TestLog::Message << "ERROR: Found " << results.violations.size() << " invalid allocation callbacks!" << TestLog::EndMessage; 592 } 593 594 // Verify live allocations 595 for (size_t liveNdx = 0; liveNdx < results.liveAllocations.size(); ++liveNdx) 596 { 597 const AllocationCallbackRecord& record = results.liveAllocations[liveNdx]; 598 const VkSystemAllocationScope scope = record.type == AllocationCallbackRecord::TYPE_ALLOCATION ? record.data.allocation.scope 599 : record.type == AllocationCallbackRecord::TYPE_REALLOCATION ? record.data.reallocation.scope 600 : VK_SYSTEM_ALLOCATION_SCOPE_LAST; 601 602 DE_ASSERT(de::inBounds(scope, (VkSystemAllocationScope)0, VK_SYSTEM_ALLOCATION_SCOPE_LAST)); 603 604 if ((allowedLiveAllocScopeBits & (1u << scope)) == 0) 605 { 606 log << TestLog::Message << "LEAK " << (numLeaks+1) << ": " << record << TestLog::EndMessage; 607 numLeaks += 1; 608 } 609 } 610 611 // Verify internal allocations 612 for (int internalAllocTypeNdx = 0; internalAllocTypeNdx < VK_INTERNAL_ALLOCATION_TYPE_LAST; ++internalAllocTypeNdx) 613 { 614 for (int scopeNdx = 0; scopeNdx < VK_SYSTEM_ALLOCATION_SCOPE_LAST; ++scopeNdx) 615 { 616 const VkInternalAllocationType type = (VkInternalAllocationType)internalAllocTypeNdx; 617 const VkSystemAllocationScope scope = (VkSystemAllocationScope)scopeNdx; 618 const size_t totalAllocated = results.internalAllocationTotal[type][scope]; 619 620 if ((allowedLiveAllocScopeBits & (1u << scopeNdx)) == 0 && 621 totalAllocated > 0) 622 { 623 log << TestLog::Message << "LEAK " << (numLeaks+1) << ": " << totalAllocated 624 << " bytes of (" << type << ", " << scope << ") internal memory is still allocated" 625 << TestLog::EndMessage; 626 numLeaks += 1; 627 } 628 } 629 } 630 631 if (numLeaks > 0) 632 log << TestLog::Message << "ERROR: Found " << numLeaks << " memory leaks!" << TestLog::EndMessage; 633 634 return results.violations.empty() && numLeaks == 0; 635 } 636 637 bool validateAndLog (tcu::TestLog& log, const AllocationCallbackRecorder& recorder, deUint32 allowedLiveAllocScopeBits) 638 { 639 AllocationCallbackValidationResults validationResults; 640 641 validateAllocationCallbacks(recorder, &validationResults); 642 643 return checkAndLog(log, validationResults, allowedLiveAllocScopeBits); 644 } 645 646 size_t getLiveSystemAllocationTotal (const AllocationCallbackValidationResults& validationResults) 647 { 648 size_t allocationTotal = 0; 649 650 DE_ASSERT(validationResults.violations.empty()); 651 652 for (std::vector<AllocationCallbackRecord>::const_iterator alloc = validationResults.liveAllocations.begin(); 653 alloc != validationResults.liveAllocations.end(); 654 ++alloc) 655 { 656 DE_ASSERT(alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION || 657 alloc->type == AllocationCallbackRecord::TYPE_REALLOCATION); 658 659 const size_t size = (alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ? alloc->data.allocation.size : alloc->data.reallocation.size); 660 const size_t alignment = (alloc->type == AllocationCallbackRecord::TYPE_ALLOCATION ? alloc->data.allocation.alignment : alloc->data.reallocation.alignment); 661 662 allocationTotal += size + alignment - (alignment > 0 ? 1 : 0); 663 } 664 665 for (int internalAllocationTypeNdx = 0; internalAllocationTypeNdx < VK_INTERNAL_ALLOCATION_TYPE_LAST; ++internalAllocationTypeNdx) 666 { 667 for (int internalAllocationScopeNdx = 0; internalAllocationScopeNdx < VK_SYSTEM_ALLOCATION_SCOPE_LAST; ++internalAllocationScopeNdx) 668 allocationTotal += validationResults.internalAllocationTotal[internalAllocationTypeNdx][internalAllocationScopeNdx]; 669 } 670 671 return allocationTotal; 672 } 673 674 std::ostream& operator<< (std::ostream& str, const AllocationCallbackRecord& record) 675 { 676 switch (record.type) 677 { 678 case AllocationCallbackRecord::TYPE_ALLOCATION: 679 str << "ALLOCATION: size=" << record.data.allocation.size 680 << ", alignment=" << record.data.allocation.alignment 681 << ", scope=" << record.data.allocation.scope 682 << ", returnedPtr=" << tcu::toHex(record.data.allocation.returnedPtr); 683 break; 684 685 case AllocationCallbackRecord::TYPE_REALLOCATION: 686 str << "REALLOCATION: original=" << tcu::toHex(record.data.reallocation.original) 687 << ", size=" << record.data.reallocation.size 688 << ", alignment=" << record.data.reallocation.alignment 689 << ", scope=" << record.data.reallocation.scope 690 << ", returnedPtr=" << tcu::toHex(record.data.reallocation.returnedPtr); 691 break; 692 693 case AllocationCallbackRecord::TYPE_FREE: 694 str << "FREE: mem=" << tcu::toHex(record.data.free.mem); 695 break; 696 697 case AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION: 698 case AllocationCallbackRecord::TYPE_INTERNAL_FREE: 699 str << "INTERNAL_" << (record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION ? "ALLOCATION" : "FREE") 700 << ": size=" << record.data.internalAllocation.size 701 << ", type=" << record.data.internalAllocation.type 702 << ", scope=" << record.data.internalAllocation.scope; 703 break; 704 705 default: 706 DE_ASSERT(false); 707 } 708 709 return str; 710 } 711 712 std::ostream& operator<< (std::ostream& str, const AllocationCallbackViolation& violation) 713 { 714 switch (violation.reason) 715 { 716 case AllocationCallbackViolation::REASON_DOUBLE_FREE: 717 { 718 DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_FREE); 719 str << "Double free of " << tcu::toHex(violation.record.data.free.mem); 720 break; 721 } 722 723 case AllocationCallbackViolation::REASON_FREE_NOT_ALLOCATED_PTR: 724 { 725 DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_FREE); 726 str << "Attempt to free " << tcu::toHex(violation.record.data.free.mem) << " which has not been allocated"; 727 break; 728 } 729 730 case AllocationCallbackViolation::REASON_REALLOC_NOT_ALLOCATED_PTR: 731 { 732 DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_REALLOCATION); 733 str << "Attempt to reallocate " << tcu::toHex(violation.record.data.reallocation.original) << " which has not been allocated"; 734 break; 735 } 736 737 case AllocationCallbackViolation::REASON_REALLOC_FREED_PTR: 738 { 739 DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_REALLOCATION); 740 str << "Attempt to reallocate " << tcu::toHex(violation.record.data.reallocation.original) << " which has been freed"; 741 break; 742 } 743 744 case AllocationCallbackViolation::REASON_NEGATIVE_INTERNAL_ALLOCATION_TOTAL: 745 { 746 DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE); 747 str << "Internal allocation total for (" << violation.record.data.internalAllocation.type << ", " << violation.record.data.internalAllocation.scope << ") is negative"; 748 break; 749 } 750 751 case AllocationCallbackViolation::REASON_INVALID_INTERNAL_ALLOCATION_TYPE: 752 { 753 DE_ASSERT(violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_ALLOCATION || 754 violation.record.type == AllocationCallbackRecord::TYPE_INTERNAL_FREE); 755 str << "Invalid internal allocation type " << tcu::toHex(violation.record.data.internalAllocation.type); 756 break; 757 } 758 759 case AllocationCallbackViolation::REASON_INVALID_ALLOCATION_SCOPE: 760 { 761 str << "Invalid allocation scope"; 762 break; 763 } 764 765 case AllocationCallbackViolation::REASON_INVALID_ALIGNMENT: 766 { 767 str << "Invalid alignment"; 768 break; 769 } 770 771 case AllocationCallbackViolation::REASON_REALLOC_DIFFERENT_ALIGNMENT: 772 { 773 str << "Reallocation with different alignment"; 774 break; 775 } 776 777 default: 778 DE_ASSERT(false); 779 } 780 781 return str; 782 } 783 784 } // vk 785