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
This commit is contained in:
Michael Lotz
2011-11-02 16:12:48 +00:00
parent 81fea74394
commit 3d1f420b23
+220 -8
View File
@@ -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 <caller> ] [ -o <object cache> ] [ -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 <object cache> | -s <slab> | -S <address> ] "
"[ -a <allocation> ] [ --team <team ID> ] [ --thread <thread ID> ]\n"
"The current allocations filtered by optional values will be printed.\n"
"If given, <object cache> specifies the address of the object cache\n"
"or <slab> specifies the address of a slab, for which to print the\n"
"allocations. Alternatively <address> 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
}