1 /* 2 * Copyright 2010, Ingo Weinhold <ingo_weinhold@gmx.de>. 3 * Copyright 2008-2010, Axel Dörfler. All Rights Reserved. 4 * Copyright 2007, Hugo Santos. All Rights Reserved. 5 * 6 * Distributed under the terms of the MIT License. 7 */ 8 9 10 #include <slab/Slab.h> 11 12 #include <algorithm> 13 #include <new> 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <KernelExport.h> 18 19 #include <condition_variable.h> 20 #include <elf.h> 21 #include <kernel.h> 22 #include <low_resource_manager.h> 23 #include <slab/ObjectDepot.h> 24 #include <smp.h> 25 #include <tracing.h> 26 #include <util/AutoLock.h> 27 #include <util/DoublyLinkedList.h> 28 #include <vm/vm.h> 29 #include <vm/VMAddressSpace.h> 30 31 #include "HashedObjectCache.h" 32 #include "MemoryManager.h" 33 #include "slab_debug.h" 34 #include "slab_private.h" 35 #include "SmallObjectCache.h" 36 37 38 #if !USE_GUARDED_HEAP_FOR_OBJECT_CACHE 39 40 41 typedef DoublyLinkedList<ObjectCache> ObjectCacheList; 42 43 typedef DoublyLinkedList<ObjectCache, 44 DoublyLinkedListMemberGetLink<ObjectCache, &ObjectCache::maintenance_link> > 45 MaintenanceQueue; 46 47 static ObjectCacheList sObjectCaches; 48 static mutex sObjectCacheListLock = MUTEX_INITIALIZER("object cache list"); 49 50 static mutex sMaintenanceLock 51 = MUTEX_INITIALIZER("object cache resize requests"); 52 static MaintenanceQueue sMaintenanceQueue; 53 static ConditionVariable sMaintenanceCondition; 54 55 56 #if SLAB_ALLOCATION_TRACKING_AVAILABLE 57 58 struct caller_info { 59 addr_t caller; 60 size_t count; 61 size_t size; 62 }; 63 64 static const int32 kCallerInfoTableSize = 1024; 65 static caller_info sCallerInfoTable[kCallerInfoTableSize]; 66 static int32 sCallerInfoCount = 0; 67 68 static caller_info* get_caller_info(addr_t caller); 69 70 71 RANGE_MARKER_FUNCTION_PROTOTYPES(slab_allocator) 72 RANGE_MARKER_FUNCTION_PROTOTYPES(SlabHashedObjectCache) 73 RANGE_MARKER_FUNCTION_PROTOTYPES(SlabMemoryManager) 74 RANGE_MARKER_FUNCTION_PROTOTYPES(SlabObjectCache) 75 RANGE_MARKER_FUNCTION_PROTOTYPES(SlabObjectDepot) 76 RANGE_MARKER_FUNCTION_PROTOTYPES(Slab) 77 RANGE_MARKER_FUNCTION_PROTOTYPES(SlabSmallObjectCache) 78 79 80 static const addr_t kSlabCodeAddressRanges[] = { 81 RANGE_MARKER_FUNCTION_ADDRESS_RANGE(slab_allocator), 82 RANGE_MARKER_FUNCTION_ADDRESS_RANGE(SlabHashedObjectCache), 83 RANGE_MARKER_FUNCTION_ADDRESS_RANGE(SlabMemoryManager), 84 RANGE_MARKER_FUNCTION_ADDRESS_RANGE(SlabObjectCache), 85 RANGE_MARKER_FUNCTION_ADDRESS_RANGE(SlabObjectDepot), 86 RANGE_MARKER_FUNCTION_ADDRESS_RANGE(Slab), 87 RANGE_MARKER_FUNCTION_ADDRESS_RANGE(SlabSmallObjectCache) 88 }; 89 90 static const uint32 kSlabCodeAddressRangeCount 91 = B_COUNT_OF(kSlabCodeAddressRanges) / 2; 92 93 #endif // SLAB_ALLOCATION_TRACKING_AVAILABLE 94 95 96 RANGE_MARKER_FUNCTION_BEGIN(Slab) 97 98 99 #if SLAB_OBJECT_CACHE_TRACING 100 101 102 namespace SlabObjectCacheTracing { 103 104 class ObjectCacheTraceEntry 105 : public TRACE_ENTRY_SELECTOR(SLAB_OBJECT_CACHE_TRACING_STACK_TRACE) { 106 public: 107 ObjectCacheTraceEntry(ObjectCache* cache) 108 : 109 TraceEntryBase(SLAB_OBJECT_CACHE_TRACING_STACK_TRACE, 0, true), 110 fCache(cache) 111 { 112 } 113 114 protected: 115 ObjectCache* fCache; 116 }; 117 118 119 class Create : public ObjectCacheTraceEntry { 120 public: 121 Create(const char* name, size_t objectSize, size_t alignment, 122 size_t maxByteUsage, uint32 flags, void* cookie, 123 ObjectCache* cache) 124 : 125 ObjectCacheTraceEntry(cache), 126 fObjectSize(objectSize), 127 fAlignment(alignment), 128 fMaxByteUsage(maxByteUsage), 129 fFlags(flags), 130 fCookie(cookie) 131 { 132 fName = alloc_tracing_buffer_strcpy(name, 64, false); 133 Initialized(); 134 } 135 136 virtual void AddDump(TraceOutput& out) 137 { 138 out.Print("object cache create: name: \"%s\", object size: " 139 "%" B_PRIuSIZE ", alignment: %" B_PRIuSIZE ", max usage: " 140 "%" B_PRIuSIZE ", flags: 0x%" B_PRIx32 ", cookie: %p -> cache: %p", 141 fName, fObjectSize, fAlignment, fMaxByteUsage, fFlags, 142 fCookie, fCache); 143 } 144 145 private: 146 const char* fName; 147 size_t fObjectSize; 148 size_t fAlignment; 149 size_t fMaxByteUsage; 150 uint32 fFlags; 151 void* fCookie; 152 }; 153 154 155 class Delete : public ObjectCacheTraceEntry { 156 public: 157 Delete(ObjectCache* cache) 158 : 159 ObjectCacheTraceEntry(cache) 160 { 161 Initialized(); 162 } 163 164 virtual void AddDump(TraceOutput& out) 165 { 166 out.Print("object cache delete: %p", fCache); 167 } 168 }; 169 170 171 class Alloc : public ObjectCacheTraceEntry { 172 public: 173 Alloc(ObjectCache* cache, uint32 flags, void* object) 174 : 175 ObjectCacheTraceEntry(cache), 176 fFlags(flags), 177 fObject(object) 178 { 179 Initialized(); 180 } 181 182 virtual void AddDump(TraceOutput& out) 183 { 184 out.Print("object cache alloc: cache: %p, flags: 0x%" B_PRIx32 185 " -> object: %p", fCache, fFlags, fObject); 186 } 187 188 private: 189 uint32 fFlags; 190 void* fObject; 191 }; 192 193 194 class Free : public ObjectCacheTraceEntry { 195 public: 196 Free(ObjectCache* cache, void* object) 197 : 198 ObjectCacheTraceEntry(cache), 199 fObject(object) 200 { 201 Initialized(); 202 } 203 204 virtual void AddDump(TraceOutput& out) 205 { 206 out.Print("object cache free: cache: %p, object: %p", fCache, 207 fObject); 208 } 209 210 private: 211 void* fObject; 212 }; 213 214 215 class Reserve : public ObjectCacheTraceEntry { 216 public: 217 Reserve(ObjectCache* cache, size_t count, uint32 flags) 218 : 219 ObjectCacheTraceEntry(cache), 220 fCount(count), 221 fFlags(flags) 222 { 223 Initialized(); 224 } 225 226 virtual void AddDump(TraceOutput& out) 227 { 228 out.Print("object cache reserve: cache: %p, count: %" B_PRIu32 ", " 229 "flags: 0x%" B_PRIx32, fCache, fCount, fFlags); 230 } 231 232 private: 233 uint32 fCount; 234 uint32 fFlags; 235 }; 236 237 238 } // namespace SlabObjectCacheTracing 239 240 # define T(x) new(std::nothrow) SlabObjectCacheTracing::x 241 242 #else 243 # define T(x) 244 #endif // SLAB_OBJECT_CACHE_TRACING 245 246 247 // #pragma mark - 248 249 250 static void 251 dump_slab(::slab* slab) 252 { 253 kprintf(" %p %p %6" B_PRIuSIZE " %6" B_PRIuSIZE " %6" B_PRIuSIZE " %p\n", 254 slab, slab->pages, slab->size, slab->count, slab->offset, slab->free); 255 } 256 257 258 static int 259 dump_slabs(int argc, char* argv[]) 260 { 261 kprintf("%*s %22s %8s %8s %8s %6s %8s %8s %8s\n", 262 B_PRINTF_POINTER_WIDTH + 2, "address", "name", "objsize", "align", 263 "usage", "empty", "usedobj", "total", "flags"); 264 265 ObjectCacheList::Iterator it = sObjectCaches.GetIterator(); 266 267 while (it.HasNext()) { 268 ObjectCache* cache = it.Next(); 269 270 kprintf("%p %22s %8lu %8" B_PRIuSIZE " %8lu %6lu %8lu %8lu %8" B_PRIx32 271 "\n", cache, cache->name, cache->object_size, cache->alignment, 272 cache->usage, cache->empty_count, cache->used_count, 273 cache->total_objects, cache->flags); 274 } 275 276 return 0; 277 } 278 279 280 static int 281 dump_cache_info(int argc, char* argv[]) 282 { 283 if (argc < 2) { 284 kprintf("usage: slab_cache [address]\n"); 285 return 0; 286 } 287 288 ObjectCache* cache = (ObjectCache*)parse_expression(argv[1]); 289 290 kprintf("name: %s\n", cache->name); 291 kprintf("lock: %p\n", &cache->lock); 292 kprintf("object_size: %lu\n", cache->object_size); 293 kprintf("alignment: %" B_PRIuSIZE "\n", cache->alignment); 294 kprintf("cache_color_cycle: %lu\n", cache->cache_color_cycle); 295 kprintf("total_objects: %lu\n", cache->total_objects); 296 kprintf("used_count: %lu\n", cache->used_count); 297 kprintf("empty_count: %lu\n", cache->empty_count); 298 kprintf("pressure: %lu\n", cache->pressure); 299 kprintf("slab_size: %lu\n", cache->slab_size); 300 kprintf("usage: %lu\n", cache->usage); 301 kprintf("maximum: %lu\n", cache->maximum); 302 kprintf("flags: 0x%" B_PRIx32 "\n", cache->flags); 303 kprintf("cookie: %p\n", cache->cookie); 304 kprintf("resize entry don't wait: %p\n", cache->resize_entry_dont_wait); 305 kprintf("resize entry can wait: %p\n", cache->resize_entry_can_wait); 306 307 kprintf(" %-*s %-*s size used offset free\n", 308 B_PRINTF_POINTER_WIDTH, "slab", B_PRINTF_POINTER_WIDTH, "chunk"); 309 310 SlabList::Iterator iterator = cache->empty.GetIterator(); 311 if (iterator.HasNext()) 312 kprintf("empty:\n"); 313 while (::slab* slab = iterator.Next()) 314 dump_slab(slab); 315 316 iterator = cache->partial.GetIterator(); 317 if (iterator.HasNext()) 318 kprintf("partial:\n"); 319 while (::slab* slab = iterator.Next()) 320 dump_slab(slab); 321 322 iterator = cache->full.GetIterator(); 323 if (iterator.HasNext()) 324 kprintf("full:\n"); 325 while (::slab* slab = iterator.Next()) 326 dump_slab(slab); 327 328 if ((cache->flags & CACHE_NO_DEPOT) == 0) { 329 kprintf("depot:\n"); 330 dump_object_depot(&cache->depot); 331 } 332 333 return 0; 334 } 335 336 337 static int 338 dump_object_info(int argc, char* argv[]) 339 { 340 if (argc < 2) { 341 kprintf("usage: slab_object [address]\n"); 342 return 0; 343 } 344 345 void* object = (void*)parse_expression(argv[1]); 346 ObjectCache* cache = MemoryManager::DebugObjectCacheForAddress(object); 347 if (cache == NULL) { 348 kprintf("%p does not seem to be in an object_cache\n", object); 349 return 1; 350 } 351 352 kprintf("address %p\n", object); 353 kprintf("\tslab_cache: %p (%s)\n", cache, cache->name); 354 355 MutexTryLocker cacheLocker(cache->lock); 356 if (cacheLocker.IsLocked()) { 357 slab* slab = cache->ObjectSlab(object); 358 const char* slabType = cache->empty.Contains(slab) ? "empty" 359 : cache->partial.Contains(slab) ? "partial" 360 : cache->full.Contains(slab) ? "full" : NULL; 361 362 kprintf("\tobject is in %s slab: %p\n", slabType, slab); 363 } 364 365 return 0; 366 } 367 368 369 // #pragma mark - AllocationTrackingCallback 370 371 372 #if SLAB_ALLOCATION_TRACKING_AVAILABLE 373 374 AllocationTrackingCallback::~AllocationTrackingCallback() 375 { 376 } 377 378 #endif // SLAB_ALLOCATION_TRACKING_AVAILABLE 379 380 381 // #pragma mark - 382 383 384 #if SLAB_ALLOCATION_TRACKING_AVAILABLE 385 386 namespace { 387 388 class AllocationCollectorCallback : public AllocationTrackingCallback { 389 public: 390 AllocationCollectorCallback(bool resetInfos) 391 : 392 fResetInfos(resetInfos) 393 { 394 } 395 396 virtual bool ProcessTrackingInfo(AllocationTrackingInfo* info, 397 void* allocation, size_t allocationSize) 398 { 399 if (!info->IsInitialized()) 400 return true; 401 402 addr_t caller = 0; 403 AbstractTraceEntryWithStackTrace* traceEntry = info->TraceEntry(); 404 405 if (traceEntry != NULL && info->IsTraceEntryValid()) { 406 caller = tracing_find_caller_in_stack_trace( 407 traceEntry->StackTrace(), kSlabCodeAddressRanges, 408 kSlabCodeAddressRangeCount); 409 } 410 411 caller_info* callerInfo = get_caller_info(caller); 412 if (callerInfo == NULL) { 413 kprintf("out of space for caller infos\n"); 414 return false; 415 } 416 417 callerInfo->count++; 418 callerInfo->size += allocationSize; 419 420 if (fResetInfos) 421 info->Clear(); 422 423 return true; 424 } 425 426 private: 427 bool fResetInfos; 428 }; 429 430 431 class AllocationInfoPrinterCallback : public AllocationTrackingCallback { 432 public: 433 AllocationInfoPrinterCallback(bool printStackTrace, addr_t addressFilter, 434 team_id teamFilter, thread_id threadFilter) 435 : 436 fPrintStackTrace(printStackTrace), 437 fAddressFilter(addressFilter), 438 fTeamFilter(teamFilter), 439 fThreadFilter(threadFilter) 440 { 441 } 442 443 virtual bool ProcessTrackingInfo(AllocationTrackingInfo* info, 444 void* allocation, size_t allocationSize) 445 { 446 if (!info->IsInitialized()) 447 return true; 448 449 if (fAddressFilter != 0 && (addr_t)allocation != fAddressFilter) 450 return true; 451 452 AbstractTraceEntryWithStackTrace* traceEntry = info->TraceEntry(); 453 if (traceEntry != NULL && !info->IsTraceEntryValid()) 454 traceEntry = NULL; 455 456 if (traceEntry != NULL) { 457 if (fTeamFilter != -1 && traceEntry->TeamID() != fTeamFilter) 458 return true; 459 if (fThreadFilter != -1 && traceEntry->ThreadID() != fThreadFilter) 460 return true; 461 } else { 462 // we need the info if we have filters set 463 if (fTeamFilter != -1 || fThreadFilter != -1) 464 return true; 465 } 466 467 kprintf("allocation %p, size: %" B_PRIuSIZE, allocation, 468 allocationSize); 469 470 if (traceEntry != NULL) { 471 kprintf(", team: %" B_PRId32 ", thread %" B_PRId32 472 ", time %" B_PRId64 "\n", traceEntry->TeamID(), 473 traceEntry->ThreadID(), traceEntry->Time()); 474 475 if (fPrintStackTrace) 476 tracing_print_stack_trace(traceEntry->StackTrace()); 477 } else 478 kprintf("\n"); 479 480 return true; 481 } 482 483 private: 484 bool fPrintStackTrace; 485 addr_t fAddressFilter; 486 team_id fTeamFilter; 487 thread_id fThreadFilter; 488 }; 489 490 491 class AllocationDetailPrinterCallback : public AllocationTrackingCallback { 492 public: 493 AllocationDetailPrinterCallback(addr_t caller) 494 : 495 fCaller(caller) 496 { 497 } 498 499 virtual bool ProcessTrackingInfo(AllocationTrackingInfo* info, 500 void* allocation, size_t allocationSize) 501 { 502 if (!info->IsInitialized()) 503 return true; 504 505 addr_t caller = 0; 506 AbstractTraceEntryWithStackTrace* traceEntry = info->TraceEntry(); 507 if (traceEntry != NULL && !info->IsTraceEntryValid()) 508 traceEntry = NULL; 509 510 if (traceEntry != NULL) { 511 caller = tracing_find_caller_in_stack_trace( 512 traceEntry->StackTrace(), kSlabCodeAddressRanges, 513 kSlabCodeAddressRangeCount); 514 } 515 516 if (caller != fCaller) 517 return true; 518 519 kprintf("allocation %p, size: %" B_PRIuSIZE "\n", allocation, 520 allocationSize); 521 if (traceEntry != NULL) 522 tracing_print_stack_trace(traceEntry->StackTrace()); 523 524 return true; 525 } 526 527 private: 528 addr_t fCaller; 529 }; 530 531 } // unnamed namespace 532 533 static caller_info* 534 get_caller_info(addr_t caller) 535 { 536 // find the caller info 537 for (int32 i = 0; i < sCallerInfoCount; i++) { 538 if (caller == sCallerInfoTable[i].caller) 539 return &sCallerInfoTable[i]; 540 } 541 542 // not found, add a new entry, if there are free slots 543 if (sCallerInfoCount >= kCallerInfoTableSize) 544 return NULL; 545 546 caller_info* info = &sCallerInfoTable[sCallerInfoCount++]; 547 info->caller = caller; 548 info->count = 0; 549 info->size = 0; 550 551 return info; 552 } 553 554 555 static int 556 caller_info_compare_size(const void* _a, const void* _b) 557 { 558 const caller_info* a = (const caller_info*)_a; 559 const caller_info* b = (const caller_info*)_b; 560 return (int)(b->size - a->size); 561 } 562 563 564 static int 565 caller_info_compare_count(const void* _a, const void* _b) 566 { 567 const caller_info* a = (const caller_info*)_a; 568 const caller_info* b = (const caller_info*)_b; 569 return (int)(b->count - a->count); 570 } 571 572 573 #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING 574 575 static bool 576 analyze_allocation_callers(ObjectCache* cache, slab* slab, 577 AllocationTrackingCallback& callback) 578 { 579 for (uint32 i = 0; i < slab->size; i++) { 580 if (!callback.ProcessTrackingInfo(&slab->tracking[i], 581 cache->ObjectAtIndex(slab, i), cache->object_size)) { 582 return false; 583 } 584 } 585 586 return true; 587 } 588 589 590 static bool 591 analyze_allocation_callers(ObjectCache* cache, const SlabList& slabList, 592 AllocationTrackingCallback& callback) 593 { 594 for (SlabList::ConstIterator it = slabList.GetIterator(); 595 slab* slab = it.Next();) { 596 if (!analyze_allocation_callers(cache, slab, callback)) 597 return false; 598 } 599 600 return true; 601 } 602 603 604 static bool 605 analyze_allocation_callers(ObjectCache* cache, 606 AllocationTrackingCallback& callback) 607 { 608 return analyze_allocation_callers(cache, cache->full, callback) 609 && analyze_allocation_callers(cache, cache->partial, callback); 610 } 611 612 #endif // SLAB_OBJECT_CACHE_ALLOCATION_TRACKING 613 614 615 static int 616 dump_allocation_infos(int argc, char **argv) 617 { 618 ObjectCache* cache = NULL; 619 slab* slab = NULL; 620 addr_t addressFilter = 0; 621 team_id teamFilter = -1; 622 thread_id threadFilter = -1; 623 bool printStackTraces = false; 624 625 for (int32 i = 1; i < argc; i++) { 626 if (strcmp(argv[i], "--stacktrace") == 0) 627 printStackTraces = true; 628 else if (strcmp(argv[i], "-a") == 0) { 629 uint64 address; 630 if (++i >= argc 631 || !evaluate_debug_expression(argv[i], &address, true)) { 632 print_debugger_command_usage(argv[0]); 633 return 0; 634 } 635 636 addressFilter = address; 637 } else if (strcmp(argv[i], "-o") == 0) { 638 uint64 cacheAddress; 639 if (++i >= argc 640 || !evaluate_debug_expression(argv[i], &cacheAddress, true)) { 641 print_debugger_command_usage(argv[0]); 642 return 0; 643 } 644 645 cache = (ObjectCache*)(addr_t)cacheAddress; 646 } else if (strcasecmp(argv[i], "-s") == 0) { 647 uint64 slabAddress; 648 if (++i >= argc 649 || !evaluate_debug_expression(argv[i], &slabAddress, true)) { 650 print_debugger_command_usage(argv[0]); 651 return 0; 652 } 653 654 void* slabPages = (void*)slabAddress; 655 if (strcmp(argv[i], "-s") == 0) { 656 slab = (struct slab*)(addr_t)slabAddress; 657 slabPages = slab->pages; 658 } 659 660 cache = MemoryManager::DebugObjectCacheForAddress(slabPages); 661 if (cache == NULL) { 662 kprintf("Couldn't find object cache for address %p.\n", 663 slabPages); 664 return 0; 665 } 666 667 if (slab == NULL) { 668 slab = cache->ObjectSlab(slabPages); 669 670 if (slab == NULL) { 671 kprintf("Couldn't find slab for address %p.\n", slabPages); 672 return 0; 673 } 674 } 675 } else if (strcmp(argv[i], "--team") == 0) { 676 uint64 team; 677 if (++i >= argc 678 || !evaluate_debug_expression(argv[i], &team, true)) { 679 print_debugger_command_usage(argv[0]); 680 return 0; 681 } 682 683 teamFilter = team; 684 } else if (strcmp(argv[i], "--thread") == 0) { 685 uint64 thread; 686 if (++i >= argc 687 || !evaluate_debug_expression(argv[i], &thread, true)) { 688 print_debugger_command_usage(argv[0]); 689 return 0; 690 } 691 692 threadFilter = thread; 693 } else { 694 print_debugger_command_usage(argv[0]); 695 return 0; 696 } 697 } 698 699 AllocationInfoPrinterCallback callback(printStackTraces, addressFilter, 700 teamFilter, threadFilter); 701 702 if (slab != NULL || cache != NULL) { 703 #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING 704 if (slab != NULL) { 705 if (!analyze_allocation_callers(cache, slab, callback)) 706 return 0; 707 } else if (cache != NULL) { 708 if (!analyze_allocation_callers(cache, callback)) 709 return 0; 710 } 711 #else 712 kprintf("Object cache allocation tracking not available. " 713 "SLAB_OBJECT_CACHE_TRACING (%d) and " 714 "SLAB_OBJECT_CACHE_TRACING_STACK_TRACE (%d) must be enabled.\n", 715 SLAB_OBJECT_CACHE_TRACING, SLAB_OBJECT_CACHE_TRACING_STACK_TRACE); 716 return 0; 717 #endif 718 } else { 719 #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING 720 721 for (ObjectCacheList::Iterator it = sObjectCaches.GetIterator(); 722 it.HasNext();) { 723 if (!analyze_allocation_callers(it.Next(), callback)) 724 return 0; 725 } 726 #endif 727 728 #if SLAB_MEMORY_MANAGER_ALLOCATION_TRACKING 729 if (!MemoryManager::AnalyzeAllocationCallers(callback)) 730 return 0; 731 #endif 732 } 733 734 return 0; 735 } 736 737 738 static int 739 dump_allocations_per_caller(int argc, char **argv) 740 { 741 bool sortBySize = true; 742 bool resetAllocationInfos = false; 743 bool printDetails = false; 744 ObjectCache* cache = NULL; 745 addr_t caller = 0; 746 747 for (int32 i = 1; i < argc; i++) { 748 if (strcmp(argv[i], "-c") == 0) { 749 sortBySize = false; 750 } else if (strcmp(argv[i], "-d") == 0) { 751 uint64 callerAddress; 752 if (++i >= argc 753 || !evaluate_debug_expression(argv[i], &callerAddress, true)) { 754 print_debugger_command_usage(argv[0]); 755 return 0; 756 } 757 758 caller = callerAddress; 759 printDetails = true; 760 } else if (strcmp(argv[i], "-o") == 0) { 761 uint64 cacheAddress; 762 if (++i >= argc 763 || !evaluate_debug_expression(argv[i], &cacheAddress, true)) { 764 print_debugger_command_usage(argv[0]); 765 return 0; 766 } 767 768 cache = (ObjectCache*)(addr_t)cacheAddress; 769 } else if (strcmp(argv[i], "-r") == 0) { 770 resetAllocationInfos = true; 771 } else { 772 print_debugger_command_usage(argv[0]); 773 return 0; 774 } 775 } 776 777 sCallerInfoCount = 0; 778 779 AllocationCollectorCallback collectorCallback(resetAllocationInfos); 780 AllocationDetailPrinterCallback detailsCallback(caller); 781 AllocationTrackingCallback& callback = printDetails 782 ? (AllocationTrackingCallback&)detailsCallback 783 : (AllocationTrackingCallback&)collectorCallback; 784 785 if (cache != NULL) { 786 #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING 787 if (!analyze_allocation_callers(cache, callback)) 788 return 0; 789 #else 790 kprintf("Object cache allocation tracking not available. " 791 "SLAB_OBJECT_CACHE_TRACING (%d) and " 792 "SLAB_OBJECT_CACHE_TRACING_STACK_TRACE (%d) must be enabled.\n", 793 SLAB_OBJECT_CACHE_TRACING, SLAB_OBJECT_CACHE_TRACING_STACK_TRACE); 794 return 0; 795 #endif 796 } else { 797 #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING 798 799 for (ObjectCacheList::Iterator it = sObjectCaches.GetIterator(); 800 it.HasNext();) { 801 if (!analyze_allocation_callers(it.Next(), callback)) 802 return 0; 803 } 804 #endif 805 806 #if SLAB_MEMORY_MANAGER_ALLOCATION_TRACKING 807 if (!MemoryManager::AnalyzeAllocationCallers(callback)) 808 return 0; 809 #endif 810 } 811 812 if (printDetails) 813 return 0; 814 815 // sort the array 816 qsort(sCallerInfoTable, sCallerInfoCount, sizeof(caller_info), 817 sortBySize ? &caller_info_compare_size : &caller_info_compare_count); 818 819 kprintf("%" B_PRId32 " different callers, sorted by %s...\n\n", 820 sCallerInfoCount, sortBySize ? "size" : "count"); 821 822 size_t totalAllocationSize = 0; 823 size_t totalAllocationCount = 0; 824 825 kprintf(" count size caller\n"); 826 kprintf("----------------------------------\n"); 827 for (int32 i = 0; i < sCallerInfoCount; i++) { 828 caller_info& info = sCallerInfoTable[i]; 829 kprintf("%10" B_PRIuSIZE " %10" B_PRIuSIZE " %p", info.count, 830 info.size, (void*)info.caller); 831 832 const char* symbol; 833 const char* imageName; 834 bool exactMatch; 835 addr_t baseAddress; 836 837 if (elf_debug_lookup_symbol_address(info.caller, &baseAddress, &symbol, 838 &imageName, &exactMatch) == B_OK) { 839 kprintf(" %s + %#" B_PRIxADDR " (%s)%s\n", symbol, 840 info.caller - baseAddress, imageName, 841 exactMatch ? "" : " (nearest)"); 842 } else 843 kprintf("\n"); 844 845 totalAllocationCount += info.count; 846 totalAllocationSize += info.size; 847 } 848 849 kprintf("\ntotal allocations: %" B_PRIuSIZE ", %" B_PRIuSIZE " bytes\n", 850 totalAllocationCount, totalAllocationSize); 851 852 return 0; 853 } 854 855 #endif // SLAB_ALLOCATION_TRACKING_AVAILABLE 856 857 858 void 859 add_alloc_tracing_entry(ObjectCache* cache, uint32 flags, void* object) 860 { 861 #if SLAB_OBJECT_CACHE_TRACING 862 #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING 863 MutexLocker _(cache->lock); 864 cache->TrackingInfoFor(object)->Init(T(Alloc(cache, flags, object))); 865 #else 866 T(Alloc(cache, flags, object)); 867 #endif 868 #endif 869 } 870 871 872 // #pragma mark - 873 874 875 void 876 request_memory_manager_maintenance() 877 { 878 MutexLocker locker(sMaintenanceLock); 879 sMaintenanceCondition.NotifyAll(); 880 } 881 882 883 // #pragma mark - 884 885 886 static void 887 delete_object_cache_internal(object_cache* cache) 888 { 889 if (!(cache->flags & CACHE_NO_DEPOT)) 890 object_depot_destroy(&cache->depot, 0); 891 892 mutex_lock(&cache->lock); 893 894 if (!cache->full.IsEmpty()) 895 panic("cache destroy: still has full slabs"); 896 897 if (!cache->partial.IsEmpty()) 898 panic("cache destroy: still has partial slabs"); 899 900 while (!cache->empty.IsEmpty()) 901 cache->ReturnSlab(cache->empty.RemoveHead(), 0); 902 903 mutex_destroy(&cache->lock); 904 cache->Delete(); 905 } 906 907 908 static void 909 increase_object_reserve(ObjectCache* cache) 910 { 911 MutexLocker locker(sMaintenanceLock); 912 913 cache->maintenance_resize = true; 914 915 if (!cache->maintenance_pending) { 916 cache->maintenance_pending = true; 917 sMaintenanceQueue.Add(cache); 918 sMaintenanceCondition.NotifyAll(); 919 } 920 } 921 922 923 /*! Makes sure that \a objectCount objects can be allocated. 924 */ 925 static status_t 926 object_cache_reserve_internal(ObjectCache* cache, size_t objectCount, 927 uint32 flags) 928 { 929 // If someone else is already adding slabs, we wait for that to be finished 930 // first. 931 thread_id thread = find_thread(NULL); 932 while (true) { 933 if (objectCount <= cache->total_objects - cache->used_count) 934 return B_OK; 935 936 ObjectCacheResizeEntry* resizeEntry = NULL; 937 if (cache->resize_entry_dont_wait != NULL) { 938 resizeEntry = cache->resize_entry_dont_wait; 939 if (thread == resizeEntry->thread) 940 return B_WOULD_BLOCK; 941 // Note: We could still have reentered the function, i.e. 942 // resize_entry_can_wait would be ours. That doesn't matter much, 943 // though, since after the don't-wait thread has done its job 944 // everyone will be happy. 945 } else if (cache->resize_entry_can_wait != NULL) { 946 resizeEntry = cache->resize_entry_can_wait; 947 if (thread == resizeEntry->thread) 948 return B_WOULD_BLOCK; 949 950 if ((flags & CACHE_DONT_WAIT_FOR_MEMORY) != 0) 951 break; 952 } else 953 break; 954 955 resizeEntry->condition.Wait(&cache->lock); 956 } 957 958 // prepare the resize entry others can wait on 959 ObjectCacheResizeEntry*& resizeEntry 960 = (flags & CACHE_DONT_WAIT_FOR_MEMORY) != 0 961 ? cache->resize_entry_dont_wait : cache->resize_entry_can_wait; 962 963 ObjectCacheResizeEntry myResizeEntry; 964 resizeEntry = &myResizeEntry; 965 resizeEntry->condition.Init(cache, "wait for slabs"); 966 resizeEntry->thread = thread; 967 968 // add new slabs until there are as many free ones as requested 969 while (objectCount > cache->total_objects - cache->used_count) { 970 slab* newSlab = cache->CreateSlab(flags); 971 if (newSlab == NULL) { 972 resizeEntry->condition.NotifyAll(); 973 resizeEntry = NULL; 974 return B_NO_MEMORY; 975 } 976 977 cache->usage += cache->slab_size; 978 cache->total_objects += newSlab->size; 979 980 cache->empty.Add(newSlab); 981 cache->empty_count++; 982 } 983 984 resizeEntry->condition.NotifyAll(); 985 resizeEntry = NULL; 986 987 return B_OK; 988 } 989 990 991 static void 992 object_cache_low_memory(void* dummy, uint32 resources, int32 level) 993 { 994 if (level == B_NO_LOW_RESOURCE) 995 return; 996 997 MutexLocker cacheListLocker(sObjectCacheListLock); 998 999 // Append the first cache to the end of the queue. We assume that it is 1000 // one of the caches that will never be deleted and thus we use it as a 1001 // marker. 1002 ObjectCache* firstCache = sObjectCaches.RemoveHead(); 1003 sObjectCaches.Add(firstCache); 1004 cacheListLocker.Unlock(); 1005 1006 ObjectCache* cache; 1007 do { 1008 cacheListLocker.Lock(); 1009 1010 cache = sObjectCaches.RemoveHead(); 1011 sObjectCaches.Add(cache); 1012 1013 MutexLocker maintenanceLocker(sMaintenanceLock); 1014 if (cache->maintenance_pending || cache->maintenance_in_progress) { 1015 // We don't want to mess with caches in maintenance. 1016 continue; 1017 } 1018 1019 cache->maintenance_pending = true; 1020 cache->maintenance_in_progress = true; 1021 1022 maintenanceLocker.Unlock(); 1023 cacheListLocker.Unlock(); 1024 1025 // We are calling the reclaimer without the object cache lock 1026 // to give the owner a chance to return objects to the slabs. 1027 1028 if (cache->reclaimer) 1029 cache->reclaimer(cache->cookie, level); 1030 1031 if ((cache->flags & CACHE_NO_DEPOT) == 0) 1032 object_depot_make_empty(&cache->depot, 0); 1033 1034 MutexLocker cacheLocker(cache->lock); 1035 size_t minimumAllowed; 1036 1037 switch (level) { 1038 case B_LOW_RESOURCE_NOTE: 1039 minimumAllowed = cache->pressure / 2 + 1; 1040 cache->pressure -= cache->pressure / 8; 1041 break; 1042 1043 case B_LOW_RESOURCE_WARNING: 1044 cache->pressure /= 2; 1045 minimumAllowed = 0; 1046 break; 1047 1048 default: 1049 cache->pressure = 0; 1050 minimumAllowed = 0; 1051 break; 1052 } 1053 1054 while (cache->empty_count > minimumAllowed) { 1055 // make sure we respect the cache's minimum object reserve 1056 size_t objectsPerSlab = cache->empty.Head()->size; 1057 size_t freeObjects = cache->total_objects - cache->used_count; 1058 if (freeObjects < cache->min_object_reserve + objectsPerSlab) 1059 break; 1060 1061 cache->ReturnSlab(cache->empty.RemoveHead(), 0); 1062 cache->empty_count--; 1063 } 1064 1065 cacheLocker.Unlock(); 1066 1067 // Check whether in the meantime someone has really requested 1068 // maintenance for the cache. 1069 maintenanceLocker.Lock(); 1070 1071 if (cache->maintenance_delete) { 1072 delete_object_cache_internal(cache); 1073 continue; 1074 } 1075 1076 cache->maintenance_in_progress = false; 1077 1078 if (cache->maintenance_resize) 1079 sMaintenanceQueue.Add(cache); 1080 else 1081 cache->maintenance_pending = false; 1082 } while (cache != firstCache); 1083 } 1084 1085 1086 static status_t 1087 object_cache_maintainer(void*) 1088 { 1089 while (true) { 1090 MutexLocker locker(sMaintenanceLock); 1091 1092 // wait for the next request 1093 while (sMaintenanceQueue.IsEmpty()) { 1094 // perform memory manager maintenance, if needed 1095 if (MemoryManager::MaintenanceNeeded()) { 1096 locker.Unlock(); 1097 MemoryManager::PerformMaintenance(); 1098 locker.Lock(); 1099 continue; 1100 } 1101 1102 sMaintenanceCondition.Wait(locker.Get()); 1103 } 1104 1105 ObjectCache* cache = sMaintenanceQueue.RemoveHead(); 1106 1107 while (true) { 1108 bool resizeRequested = cache->maintenance_resize; 1109 bool deleteRequested = cache->maintenance_delete; 1110 1111 if (!resizeRequested && !deleteRequested) { 1112 cache->maintenance_pending = false; 1113 cache->maintenance_in_progress = false; 1114 break; 1115 } 1116 1117 cache->maintenance_resize = false; 1118 cache->maintenance_in_progress = true; 1119 1120 locker.Unlock(); 1121 1122 if (deleteRequested) { 1123 delete_object_cache_internal(cache); 1124 break; 1125 } 1126 1127 // resize the cache, if necessary 1128 1129 MutexLocker cacheLocker(cache->lock); 1130 1131 if (resizeRequested) { 1132 status_t error = object_cache_reserve_internal(cache, 1133 cache->min_object_reserve, 0); 1134 if (error != B_OK) { 1135 dprintf("object cache resizer: Failed to resize object " 1136 "cache %p!\n", cache); 1137 break; 1138 } 1139 } 1140 1141 locker.Lock(); 1142 } 1143 } 1144 1145 // never can get here 1146 return B_OK; 1147 } 1148 1149 1150 // #pragma mark - public API 1151 1152 1153 object_cache* 1154 create_object_cache(const char* name, size_t object_size, size_t alignment, 1155 void* cookie, object_cache_constructor constructor, 1156 object_cache_destructor destructor) 1157 { 1158 return create_object_cache_etc(name, object_size, alignment, 0, 0, 0, 0, 1159 cookie, constructor, destructor, NULL); 1160 } 1161 1162 1163 object_cache* 1164 create_object_cache_etc(const char* name, size_t objectSize, size_t alignment, 1165 size_t maximum, size_t magazineCapacity, size_t maxMagazineCount, 1166 uint32 flags, void* cookie, object_cache_constructor constructor, 1167 object_cache_destructor destructor, object_cache_reclaimer reclaimer) 1168 { 1169 ObjectCache* cache; 1170 1171 if (objectSize == 0) { 1172 cache = NULL; 1173 } else if (objectSize <= 256) { 1174 cache = SmallObjectCache::Create(name, objectSize, alignment, maximum, 1175 magazineCapacity, maxMagazineCount, flags, cookie, constructor, 1176 destructor, reclaimer); 1177 } else { 1178 cache = HashedObjectCache::Create(name, objectSize, alignment, maximum, 1179 magazineCapacity, maxMagazineCount, flags, cookie, constructor, 1180 destructor, reclaimer); 1181 } 1182 1183 if (cache != NULL) { 1184 MutexLocker _(sObjectCacheListLock); 1185 sObjectCaches.Add(cache); 1186 } 1187 1188 T(Create(name, objectSize, alignment, maximum, flags, cookie, cache)); 1189 return cache; 1190 } 1191 1192 1193 void 1194 delete_object_cache(object_cache* cache) 1195 { 1196 T(Delete(cache)); 1197 1198 { 1199 MutexLocker _(sObjectCacheListLock); 1200 sObjectCaches.Remove(cache); 1201 } 1202 1203 MutexLocker cacheLocker(cache->lock); 1204 1205 { 1206 MutexLocker maintenanceLocker(sMaintenanceLock); 1207 if (cache->maintenance_in_progress) { 1208 // The maintainer thread is working with the cache. Just mark it 1209 // to be deleted. 1210 cache->maintenance_delete = true; 1211 return; 1212 } 1213 1214 // unschedule maintenance 1215 if (cache->maintenance_pending) 1216 sMaintenanceQueue.Remove(cache); 1217 } 1218 1219 // at this point no-one should have a reference to the cache anymore 1220 cacheLocker.Unlock(); 1221 1222 delete_object_cache_internal(cache); 1223 } 1224 1225 1226 status_t 1227 object_cache_set_minimum_reserve(object_cache* cache, size_t objectCount) 1228 { 1229 MutexLocker _(cache->lock); 1230 1231 if (cache->min_object_reserve == objectCount) 1232 return B_OK; 1233 1234 cache->min_object_reserve = objectCount; 1235 1236 increase_object_reserve(cache); 1237 1238 return B_OK; 1239 } 1240 1241 1242 void* 1243 object_cache_alloc(object_cache* cache, uint32 flags) 1244 { 1245 if (!(cache->flags & CACHE_NO_DEPOT)) { 1246 void* object = object_depot_obtain(&cache->depot); 1247 if (object) { 1248 add_alloc_tracing_entry(cache, flags, object); 1249 return fill_allocated_block(object, cache->object_size); 1250 } 1251 } 1252 1253 MutexLocker locker(cache->lock); 1254 slab* source = NULL; 1255 1256 while (true) { 1257 source = cache->partial.Head(); 1258 if (source != NULL) 1259 break; 1260 1261 source = cache->empty.RemoveHead(); 1262 if (source != NULL) { 1263 cache->empty_count--; 1264 cache->partial.Add(source); 1265 break; 1266 } 1267 1268 if (object_cache_reserve_internal(cache, 1, flags) != B_OK) { 1269 T(Alloc(cache, flags, NULL)); 1270 return NULL; 1271 } 1272 1273 cache->pressure++; 1274 } 1275 1276 ParanoiaChecker _2(source); 1277 1278 object_link* link = _pop(source->free); 1279 source->count--; 1280 cache->used_count++; 1281 1282 if (cache->total_objects - cache->used_count < cache->min_object_reserve) 1283 increase_object_reserve(cache); 1284 1285 REMOVE_PARANOIA_CHECK(PARANOIA_SUSPICIOUS, source, &link->next, 1286 sizeof(void*)); 1287 1288 TRACE_CACHE(cache, "allocate %p (%p) from %p, %lu remaining.", 1289 link_to_object(link, cache->object_size), link, source, source->count); 1290 1291 if (source->count == 0) { 1292 cache->partial.Remove(source); 1293 cache->full.Add(source); 1294 } 1295 1296 void* object = link_to_object(link, cache->object_size); 1297 locker.Unlock(); 1298 1299 add_alloc_tracing_entry(cache, flags, object); 1300 return fill_allocated_block(object, cache->object_size); 1301 } 1302 1303 1304 void 1305 object_cache_free(object_cache* cache, void* object, uint32 flags) 1306 { 1307 if (object == NULL) 1308 return; 1309 1310 T(Free(cache, object)); 1311 1312 #if PARANOID_KERNEL_FREE 1313 // TODO: allow forcing the check even if we don't find deadbeef 1314 if (*(uint32*)object == 0xdeadbeef) { 1315 if (!cache->AssertObjectNotFreed(object)) 1316 return; 1317 1318 if ((cache->flags & CACHE_NO_DEPOT) == 0) { 1319 if (object_depot_contains_object(&cache->depot, object)) { 1320 panic("object_cache: object %p is already freed", object); 1321 return; 1322 } 1323 } 1324 } 1325 1326 fill_freed_block(object, cache->object_size); 1327 #endif 1328 1329 #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING 1330 mutex_lock(&cache->lock); 1331 cache->TrackingInfoFor(object)->Clear(); 1332 mutex_unlock(&cache->lock); 1333 #endif 1334 1335 if ((cache->flags & CACHE_NO_DEPOT) == 0) { 1336 object_depot_store(&cache->depot, object, flags); 1337 return; 1338 } 1339 1340 MutexLocker _(cache->lock); 1341 cache->ReturnObjectToSlab(cache->ObjectSlab(object), object, flags); 1342 } 1343 1344 1345 status_t 1346 object_cache_reserve(object_cache* cache, size_t objectCount, uint32 flags) 1347 { 1348 if (objectCount == 0) 1349 return B_OK; 1350 1351 T(Reserve(cache, objectCount, flags)); 1352 1353 MutexLocker _(cache->lock); 1354 return object_cache_reserve_internal(cache, objectCount, flags); 1355 } 1356 1357 1358 void 1359 object_cache_get_usage(object_cache* cache, size_t* _allocatedMemory) 1360 { 1361 MutexLocker _(cache->lock); 1362 *_allocatedMemory = cache->usage; 1363 } 1364 1365 1366 void 1367 slab_init(kernel_args* args) 1368 { 1369 MemoryManager::Init(args); 1370 1371 new (&sObjectCaches) ObjectCacheList(); 1372 1373 block_allocator_init_boot(); 1374 } 1375 1376 1377 void 1378 slab_init_post_area() 1379 { 1380 MemoryManager::InitPostArea(); 1381 1382 add_debugger_command("slabs", dump_slabs, "list all object caches"); 1383 add_debugger_command("slab_cache", dump_cache_info, 1384 "dump information about a specific object cache"); 1385 add_debugger_command("slab_depot", dump_object_depot, 1386 "dump contents of an object depot"); 1387 add_debugger_command("slab_magazine", dump_depot_magazine, 1388 "dump contents of a depot magazine"); 1389 add_debugger_command("slab_object", dump_object_info, 1390 "dump information about an object in an object_cache"); 1391 #if SLAB_ALLOCATION_TRACKING_AVAILABLE 1392 add_debugger_command_etc("allocations_per_caller", 1393 &dump_allocations_per_caller, 1394 "Dump current slab allocations summed up per caller", 1395 "[ -c ] [ -d <caller> ] [ -o <object cache> ] [ -r ]\n" 1396 "The current allocations will by summed up by caller (their count and\n" 1397 "size) printed in decreasing order by size or, if \"-c\" is\n" 1398 "specified, by allocation count. If given <object cache> specifies\n" 1399 "the address of the object cache for which to print the allocations.\n" 1400 "If \"-d\" is given, each allocation for caller <caller> is printed\n" 1401 "including the respective stack trace.\n" 1402 "If \"-r\" is given, the allocation infos are reset after gathering\n" 1403 "the information, so the next command invocation will only show the\n" 1404 "allocations made after the reset.\n", 0); 1405 add_debugger_command_etc("allocation_infos", 1406 &dump_allocation_infos, 1407 "Dump current slab allocations", 1408 "[ --stacktrace ] [ -o <object cache> | -s <slab> | -S <address> ] " 1409 "[ -a <allocation> ] [ --team <team ID> ] [ --thread <thread ID> ]\n" 1410 "The current allocations filtered by optional values will be printed.\n" 1411 "If given, <object cache> specifies the address of the object cache\n" 1412 "or <slab> specifies the address of a slab, for which to print the\n" 1413 "allocations. Alternatively <address> specifies any address within\n" 1414 "a slab allocation range.\n" 1415 "The optional \"-a\" address filters for a specific allocation,\n" 1416 "with \"--team\" and \"--thread\" allocations by specific teams\n" 1417 "and/or threads can be filtered (these only work if a corresponding\n" 1418 "tracing entry is still available).\n" 1419 "If \"--stacktrace\" is given, then stack traces of the allocation\n" 1420 "callers are printed, where available\n", 0); 1421 #endif // SLAB_ALLOCATION_TRACKING_AVAILABLE 1422 } 1423 1424 1425 void 1426 slab_init_post_sem() 1427 { 1428 register_low_resource_handler(object_cache_low_memory, NULL, 1429 B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY 1430 | B_KERNEL_RESOURCE_ADDRESS_SPACE, 5); 1431 1432 block_allocator_init_rest(); 1433 } 1434 1435 1436 void 1437 slab_init_post_thread() 1438 { 1439 new(&sMaintenanceQueue) MaintenanceQueue; 1440 sMaintenanceCondition.Init(&sMaintenanceQueue, "object cache maintainer"); 1441 1442 thread_id objectCacheResizer = spawn_kernel_thread(object_cache_maintainer, 1443 "object cache resizer", B_URGENT_PRIORITY, NULL); 1444 if (objectCacheResizer < 0) { 1445 panic("slab_init_post_thread(): failed to spawn object cache resizer " 1446 "thread\n"); 1447 return; 1448 } 1449 1450 resume_thread(objectCacheResizer); 1451 } 1452 1453 1454 RANGE_MARKER_FUNCTION_END(Slab) 1455 1456 1457 #endif // !USE_GUARDED_HEAP_FOR_OBJECT_CACHE 1458