From 3d1f420b2320c2634d506b7767cd5d3e0640b72a Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Wed, 2 Nov 2011 16:12:48 +0000 Subject: [PATCH] bonefish+mmlr: * Add "allocation_infos" KDL command. Can be used to print the allocation info, including the stack trace, given an object cache, slab, allocation within a slab or allocation address. It can also optionally be filtered by team/thread. * Fix the AllocationDetailPrinterCallback to not access a possibly invalid trace entry for printing a stack trace. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@43118 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/slab/Slab.cpp | 228 ++++++++++++++++++++++++++++++-- 1 file changed, 220 insertions(+), 8 deletions(-) diff --git a/src/system/kernel/slab/Slab.cpp b/src/system/kernel/slab/Slab.cpp index c9c5b08f6c..86d0ef2a13 100644 --- a/src/system/kernel/slab/Slab.cpp +++ b/src/system/kernel/slab/Slab.cpp @@ -391,6 +391,66 @@ private: }; +class AllocationInfoPrinterCallback : public AllocationTrackingCallback { +public: + AllocationInfoPrinterCallback(bool printStackTrace, addr_t addressFilter, + team_id teamFilter, thread_id threadFilter) + : + fPrintStackTrace(printStackTrace), + fAddressFilter(addressFilter), + fTeamFilter(teamFilter), + fThreadFilter(threadFilter) + { + } + + virtual bool ProcessTrackingInfo(AllocationTrackingInfo* info, + void* allocation, size_t allocationSize) + { + if (!info->IsInitialized()) + return true; + + if (fAddressFilter != 0 && (addr_t)allocation != fAddressFilter) + return true; + + AbstractTraceEntryWithStackTrace* traceEntry = info->TraceEntry(); + if (traceEntry != NULL && !info->IsTraceEntryValid()) + traceEntry = NULL; + + if (traceEntry != NULL) { + if (fTeamFilter != -1 && traceEntry->TeamID() != fTeamFilter) + return true; + if (fThreadFilter != -1 && traceEntry->ThreadID() != fThreadFilter) + return true; + } else { + // we need the info if we have filters set + if (fTeamFilter != -1 || fThreadFilter != -1) + return true; + } + + kprintf("allocation %p, size: %" B_PRIuSIZE, allocation, + allocationSize); + + if (traceEntry != NULL) { + kprintf(", team: %" B_PRId32 ", thread %" B_PRId32 + ", time %" B_PRId64 "\n", traceEntry->TeamID(), + traceEntry->ThreadID(), traceEntry->Time()); + + if (fPrintStackTrace) + tracing_print_stack_trace(traceEntry->StackTrace()); + } else + kprintf("\n"); + + return true; + } + +private: + bool fPrintStackTrace; + addr_t fAddressFilter; + team_id fTeamFilter; + thread_id fThreadFilter; +}; + + class AllocationDetailPrinterCallback : public AllocationTrackingCallback { public: AllocationDetailPrinterCallback(addr_t caller) @@ -407,8 +467,10 @@ public: addr_t caller = 0; AbstractTraceEntryWithStackTrace* traceEntry = info->TraceEntry(); + if (traceEntry != NULL && !info->IsTraceEntryValid()) + traceEntry = NULL; - if (traceEntry != NULL && info->IsTraceEntryValid()) { + if (traceEntry != NULL) { caller = tracing_find_caller_in_stack_trace( traceEntry->StackTrace(), kSlabCodeAddressRanges, kSlabCodeAddressRangeCount); @@ -473,18 +535,29 @@ caller_info_compare_count(const void* _a, const void* _b) #if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING +static bool +analyze_allocation_callers(ObjectCache* cache, slab* slab, + AllocationTrackingCallback& callback) +{ + for (uint32 i = 0; i < slab->size; i++) { + if (!callback.ProcessTrackingInfo(&slab->tracking[i], + cache->ObjectAtIndex(slab, i), cache->object_size)) { + return false; + } + } + + return true; +} + + static bool analyze_allocation_callers(ObjectCache* cache, const SlabList& slabList, AllocationTrackingCallback& callback) { for (SlabList::ConstIterator it = slabList.GetIterator(); slab* slab = it.Next();) { - for (uint32 i = 0; i < slab->size; i++) { - if (!callback.ProcessTrackingInfo(&slab->tracking[i], - cache->ObjectAtIndex(slab, i), cache->object_size)) { - return false; - } - } + if (!analyze_allocation_callers(cache, slab, callback)) + return false; } return true; @@ -502,6 +575,129 @@ analyze_allocation_callers(ObjectCache* cache, #endif // SLAB_OBJECT_CACHE_ALLOCATION_TRACKING +static int +dump_allocation_infos(int argc, char **argv) +{ + ObjectCache* cache = NULL; + slab* slab = NULL; + addr_t addressFilter = 0; + team_id teamFilter = -1; + thread_id threadFilter = -1; + bool printStackTraces = false; + + for (int32 i = 1; i < argc; i++) { + if (strcmp(argv[i], "--stacktrace") == 0) + printStackTraces = true; + else if (strcmp(argv[i], "-a") == 0) { + uint64 address; + if (++i >= argc + || !evaluate_debug_expression(argv[i], &address, true)) { + print_debugger_command_usage(argv[0]); + return 0; + } + + addressFilter = address; + } else if (strcmp(argv[i], "-o") == 0) { + uint64 cacheAddress; + if (++i >= argc + || !evaluate_debug_expression(argv[i], &cacheAddress, true)) { + print_debugger_command_usage(argv[0]); + return 0; + } + + cache = (ObjectCache*)(addr_t)cacheAddress; + } else if (strcasecmp(argv[i], "-s") == 0) { + uint64 slabAddress; + if (++i >= argc + || !evaluate_debug_expression(argv[i], &slabAddress, true)) { + print_debugger_command_usage(argv[0]); + return 0; + } + + void* slabPages = (void*)slabAddress; + if (strcmp(argv[i], "-s") == 0) { + slab = (struct slab*)(addr_t)slabAddress; + slabPages = slab->pages; + } + + cache = MemoryManager::DebugObjectCacheForAddress(slabPages); + if (cache == NULL) { + kprintf("Couldn't find object cache for address %p.\n", + slabPages); + return 0; + } + + if (slab == NULL) { + slab = cache->ObjectSlab(slabPages); + + if (slab == NULL) { + kprintf("Couldn't find slab for address %p.\n", slabPages); + return 0; + } + } + } else if (strcmp(argv[i], "--team") == 0) { + uint64 team; + if (++i >= argc + || !evaluate_debug_expression(argv[i], &team, true)) { + print_debugger_command_usage(argv[0]); + return 0; + } + + teamFilter = team; + } else if (strcmp(argv[i], "--thread") == 0) { + uint64 thread; + if (++i >= argc + || !evaluate_debug_expression(argv[i], &thread, true)) { + print_debugger_command_usage(argv[0]); + return 0; + } + + threadFilter = thread; + } else { + print_debugger_command_usage(argv[0]); + return 0; + } + } + + AllocationInfoPrinterCallback callback(printStackTraces, addressFilter, + teamFilter, threadFilter); + + if (slab != NULL || cache != NULL) { +#if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING + if (slab != NULL) { + if (!analyze_allocation_callers(cache, slab, callback)) + return 0; + } else if (cache != NULL) { + if (!analyze_allocation_callers(cache, callback)) + return 0; + } +#else + kprintf("Object cache allocation tracking not available. " + "SLAB_OBJECT_CACHE_TRACING (%d) and " + "SLAB_OBJECT_CACHE_TRACING_STACK_TRACE (%d) must be enabled.\n", + SLAB_OBJECT_CACHE_TRACING, SLAB_OBJECT_CACHE_TRACING_STACK_TRACE); + return 0; +#endif + } else { +#if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING + + for (ObjectCacheList::Iterator it = sObjectCaches.GetIterator(); + it.HasNext();) { + if (!analyze_allocation_callers(it.Next(), callback)) + return 0; + } +#endif + +#if SLAB_MEMORY_MANAGER_ALLOCATION_TRACKING + if (!MemoryManager::AnalyzeAllocationCallers(callback)) + return 0; +#endif + } + + return 0; +} + + static int dump_allocations_per_caller(int argc, char **argv) { @@ -1159,7 +1355,7 @@ slab_init_post_area() #if SLAB_ALLOCATION_TRACKING_AVAILABLE add_debugger_command_etc("allocations_per_caller", &dump_allocations_per_caller, - "Dump current heap allocations summed up per caller", + "Dump current slab allocations summed up per caller", "[ -c ] [ -d ] [ -o ] [ -r ]\n" "The current allocations will by summed up by caller (their count and\n" "size) printed in decreasing order by size or, if \"-c\" is\n" @@ -1170,6 +1366,22 @@ slab_init_post_area() "If \"-r\" is given, the allocation infos are reset after gathering\n" "the information, so the next command invocation will only show the\n" "allocations made after the reset.\n", 0); + add_debugger_command_etc("allocation_infos", + &dump_allocation_infos, + "Dump current slab allocations", + "[ --stacktrace ] [ -o | -s | -S
] " + "[ -a ] [ --team ] [ --thread ]\n" + "The current allocations filtered by optional values will be printed.\n" + "If given, specifies the address of the object cache\n" + "or specifies the address of a slab, for which to print the\n" + "allocations. Alternatively
specifies any address within\n" + "a slab allocation range.\n" + "The optional \"-a\" address filters for a specific allocation,\n" + "with \"--team\" and \"--thread\" allocations by specific teams\n" + "and/or threads can be filtered (these only work if a corresponding\n" + "tracing entry is still available).\n" + "If \"--stacktrace\" is given, then stack traces of the allocation\n" + "callers are printed, where available\n", 0); #endif // SLAB_ALLOCATION_TRACKING_AVAILABLE }