From 4b963415c20bc38b5c0ae0fdbcfa4e94050ab150 Mon Sep 17 00:00:00 2001
From: Greg Clayton <gclayton@apple.com>
Date: Tue, 11 Sep 2012 18:10:27 +0000
Subject: [PATCH] Added "heap" command to get info on all allocations on the
 heap. Currently only objective C objects are supported since they are easy to
 detect.

llvm-svn: 163637
---
 lldb/examples/darwin/heap_find/heap.py        |  42 +++----
 .../darwin/heap_find/heap/heap_find.cpp       | 112 +++++++++++++++---
 2 files changed, 114 insertions(+), 40 deletions(-)

diff --git a/lldb/examples/darwin/heap_find/heap.py b/lldb/examples/darwin/heap_find/heap.py
index 4e002cfb4ab5..8aed86aab0a0 100644
--- a/lldb/examples/darwin/heap_find/heap.py
+++ b/lldb/examples/darwin/heap_find/heap.py
@@ -282,6 +282,7 @@ def heap_search(result, options, arg_str):
         result.AppendMessage(dylid_load_err)
         return
     expr = None
+    print_no_matches = True
     arg_str_description = arg_str
     if options.type == 'pointer':
         expr = 'find_pointer_in_heap((void *)%s)' % (arg_str)
@@ -301,6 +302,10 @@ def heap_search(result, options, arg_str):
     elif options.type == 'addr':
         expr = 'find_block_for_address((void *)%s)' % arg_str
         arg_str_description = 'malloc block for %s' % arg_str
+    elif options.type == 'all':
+        expr = 'get_heap_info(1)'
+        arg_str_description = None
+        print_no_matches = False
     else:
         result.AppendMessage('error: invalid type "%s"\nvalid values are "pointer", "cstr"' % options.type)
         return
@@ -378,29 +383,24 @@ def malloc_info(debugger, command, result, dict):
     else:
         result.AppendMessage('error: no c string arguments were given to search for')
 
-def malloc_history(debugger, command, result, dict):
+def heap(debugger, command, result, dict):
     command_args = shlex.split(command)
     usage = "usage: %prog [options] <EXPR> [EXPR ...]"
-    description='''Gets the allocation history for an expression whose result is an address.
+    description='''Traverse all allocations on the heap and report statistics.
 
-    Programs should set the MallocStackLogging=1 in the environment to enable stack history. This can be done
-    with "process launch -v MallocStackLogging=1 -- [arg1 ...]"'''
-
-    dylid_load_err = load_dylib()
-    if dylid_load_err:
-        result.AppendMessage(dylid_load_err)
+    If programs set the MallocStackLogging=1 in the environment, then stack
+    history is available for any allocations. '''
+    parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
+    add_common_options(parser)
+    try:
+        (options, args) = parser.parse_args(command_args)
+    except:
+        return
+    options.type = 'all'
+    if args:
+        result.AppendMessage('error: heap command takes no arguments, only options')
     else:
-        if command_args:
-            for addr_expr_str in command_args:
-                expr_sbvalue = lldb.frame.EvaluateExpression (addr_expr_str)
-                if expr_sbvalue.error.Success():
-                    addr = expr_sbvalue.unsigned
-                    if addr != 0:
-                        dump_stack_history_entries (addr, 1)
-                else:
-                    result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error))
-        else:
-            result.AppendMessage('error: no address expressions were specified')
+        heap_search (result, options, None)
 
 def section_ptr_refs(debugger, command, result, dict):
     command_args = shlex.split(command)
@@ -490,10 +490,10 @@ if __name__ == '__main__':
 lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.ptr_refs ptr_refs')
 lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.cstr_refs cstr_refs')
 lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_info malloc_info')
-lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_history malloc_history')
+lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.heap heap')
 lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.section_ptr_refs section_ptr_refs')
 lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.objc_refs objc_refs')
-print '"ptr_refs", "cstr_refs", "malloc_info", "malloc_history" and "section_ptr_refs" commands have been installed, use the "--help" options on these commands for detailed help.'
+print '"ptr_refs", "cstr_refs", "malloc_info", "heap" and "section_ptr_refs" commands have been installed, use the "--help" options on these commands for detailed help.'
 
 
 
diff --git a/lldb/examples/darwin/heap_find/heap/heap_find.cpp b/lldb/examples/darwin/heap_find/heap/heap_find.cpp
index b6663d848661..96872864ddeb 100644
--- a/lldb/examples/darwin/heap_find/heap/heap_find.cpp
+++ b/lldb/examples/darwin/heap_find/heap/heap_find.cpp
@@ -159,7 +159,8 @@ enum data_type_t
 {
     eDataTypeAddress,
     eDataTypeContainsData,
-    eDataTypeObjC
+    eDataTypeObjC,
+    eDataTypeHeapInfo
 };
 
 struct aligned_data_t
@@ -205,6 +206,14 @@ struct malloc_stack_entry
     mach_vm_address_t frames[MAX_FRAMES];
 };
 
+struct malloc_block_contents
+{
+    union {
+        Class isa;
+        void *pointers[2];
+    };
+};
+
 static int 
 compare_void_ptr (const void *a, const void *b)
 {
@@ -418,6 +427,14 @@ ObjCClasses g_objc_classes;
 //----------------------------------------------------------------------
 // ObjCClassInfo
 //----------------------------------------------------------------------
+
+enum HeapInfoSortType
+{
+    eSortTypeNone,
+    eSortTypeBytes,
+    eSortTypeCount
+};
+
 class ObjCClassInfo
 {
 public:
@@ -529,15 +546,9 @@ private:
         return 0;
     }
 
-    enum SortType
-    {
-        eSortTypeNone,
-        eSortTypeBytes,
-        eSortTypeCount
-    };
     Entry *m_entries;
     uint32_t m_size;    
-    SortType m_sort_type;
+    HeapInfoSortType m_sort_type;
 };
 
 ObjCClassInfo g_objc_class_snapshot;
@@ -667,16 +678,15 @@ range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr,
         // of any sort where the first pointer in the object is an OBJC class
         // pointer (an isa)
         {
-            struct objc_class *objc_object_ptr = NULL;
-            if (task_peek (task, ptr_addr, sizeof(void *), (void **)&objc_object_ptr) == KERN_SUCCESS)
+            malloc_block_contents *block_contents = NULL;
+            if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS)
             {
                 // We assume that g_objc_classes is up to date
                 // that the class list was verified to have some classes in it
                 // before calling this function
-                const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (objc_object_ptr->isa);
+                const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa);
                 if (objc_class_idx != UINT32_MAX)
                 {
-                    g_objc_class_snapshot.AddInstance (objc_class_idx, ptr_size);
                     bool match = false;
                     if (info->objc.match_isa == 0)
                     {
@@ -687,11 +697,11 @@ range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr,
                     {
                         // Only match exact isa values in the current class or
                         // optionally in the super classes
-                        if (info->objc.match_isa == objc_object_ptr->isa)
+                        if (info->objc.match_isa == block_contents->isa)
                             match = true;
                         else if (info->objc.match_superclasses)
                         {
-                            Class super = class_getSuperclass(objc_object_ptr->isa);
+                            Class super = class_getSuperclass(block_contents->isa);
                             while (super)
                             {
                                 match = super == info->objc.match_isa;
@@ -721,6 +731,32 @@ range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr,
             }
         }
         break;
+
+    case eDataTypeHeapInfo:
+        // Check if the current malloc block contains an objective C object
+        // of any sort where the first pointer in the object is an OBJC class
+        // pointer (an isa)
+        {
+            malloc_block_contents *block_contents = NULL;
+            if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS)
+            {
+                // We assume that g_objc_classes is up to date
+                // that the class list was verified to have some classes in it
+                // before calling this function
+                const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa);
+                if (objc_class_idx != UINT32_MAX)
+                {
+                    // This is an objective C object
+                    g_objc_class_snapshot.AddInstance (objc_class_idx, ptr_size);
+                }
+                else
+                {
+                    // Classify other heap info
+                }
+            }
+        }
+        break;
+
     }
 }
 
@@ -851,8 +887,6 @@ find_objc_objects_in_memory (void *isa)
     g_matches.clear();
     if (g_objc_classes.Update())
     {
-        // Reset all stats
-        g_objc_class_snapshot.Reset ();
         // Setup "info" to look for a malloc block that contains data
         // that is the a pointer 
         range_contains_data_callback_info_t data_info;
@@ -863,13 +897,53 @@ find_objc_objects_in_memory (void *isa)
         data_info.done = false;                      // Set done to false so searching doesn't stop
         range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info };
         foreach_zone_in_this_process (&info);
-        
-        // Sort and print byte total bytes
-        g_objc_class_snapshot.SortByTotalBytes(g_objc_classes, true);
     }
     return g_matches.data();
 }
 
+//----------------------------------------------------------------------
+// get_heap_info
+//
+// Gather information for all allocations on the heap and report 
+// statistics.
+//----------------------------------------------------------------------
+
+void
+get_heap_info (int sort_type)
+{
+    if (g_objc_classes.Update())
+    {
+        // Reset all stats
+        g_objc_class_snapshot.Reset ();
+        // Setup "info" to look for a malloc block that contains data
+        // that is the a pointer 
+        range_contains_data_callback_info_t data_info;
+        data_info.type = eDataTypeHeapInfo; // Check each block for data
+        data_info.match_count = 0;          // Initialize the match count to zero
+        data_info.done = false;             // Set done to false so searching doesn't stop
+        range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info };
+        foreach_zone_in_this_process (&info);
+        
+        // Sort and print byte total bytes
+        switch (sort_type)
+        {
+        case eSortTypeNone:
+        default:
+        case eSortTypeBytes:
+            g_objc_class_snapshot.SortByTotalBytes(g_objc_classes, true);
+            break;
+            
+        case eSortTypeCount:
+            g_objc_class_snapshot.SortByTotalCount(g_objc_classes, true);
+            break;
+        }
+    }
+    else
+    {
+        printf ("error: no objective C classes\n");
+    }
+}
+
 //----------------------------------------------------------------------
 // find_cstring_in_heap
 //