jasonmolenda created this revision.
jasonmolenda added a reviewer: jingham.
Herald added subscribers: teemperor, abidh.
Herald added a project: LLDB.

There's a perf problem with Objective-C programs as we add more classes to the 
Darwin libraries over time - when lldb goes to run its first expression on one 
of these systems, it will load the ObjC class names from the run time, both the 
shared cache libraries and any app-contributed classes.  lldb has two 
expressions it runs in the inferior to collect this information into a buffer 
allocated by lldb - returning the isa pointers and a hash of each class name 
used to id them.  lldb would then read the names of the classes out of memory, 
which were not localized to one region of memory, and this has become a larger 
and larger performance issue.

This patch modifies:

1. The two jitted functions, g_get_dynamic_class_info_body and 
g_get_shared_cache_class_info_body, now take the address of an allocated string 
pool buffer and size.  If the address of the string pool is 0, no names are 
copied.  The functions will copy the class names in to the string pool buffer 
and add an offset to the (isa, hash) that were previously being returned.  If 
an entry in the (isa, hash, stroffset) array does not have an entry in the 
string pool, UINT32_MAX is used.  If lldb's pre-allocated string pool buffer is 
too small, entries that did not fit will get UINT32_MAX and lldb will read the 
class names the old slow way.

2. Modifies the two methods that call these jitted functions, 
AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic and 
AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache, to allocate the string 
pool buffer, pass the address and size to the jitted expression, scan the array 
of (isa, hash, stroffset) tuples to find the highest stroffset, read that part 
of the string pool out of the inferior with a single read [*], pass the buffer 
to ParseClassInfoArray, deallocate the stringpool from the inferior.

[X] nb: I just saw an unintended behavior on the last class name as I was 
writing this.  Given that it uses the highest stroffset it finds to read the 
string pool back out of the inferior, the final class name won't be copied up.  
The test in ParseClassInfoArray will not

3. Modifies AppleObjCRuntimeV2::ParseClassInfoArray to grab the class name from 
the stringpool buffer if the stroffset is != UINT32_MAX and is within the 
copied buffer size.

[X] nb: I just saw an unintended behavior on the last class name as I was 
writing this.  Given that it uses the highest stroffset it finds to read the 
string pool back out of the inferior, the final class name won't be copied up.  
The test in ParseClassInfoArray to check that the stroffset is within the 
bounds of the copied buffer should mean that we read the last class name out of 
the inferior aka the old method.  I'll double check this is handled correctly 
tomorrow.

This patch also includes a change that Frederic Riss wrote the other month but 
hadn't upstreamed yet, where we detect swift names in the shared cache and 
don't compute the hash in the jitted function, doing it up in lldb later.

Testing the patch shows no testsuite difference on Mac native.  As an 
experiment, I intentionally introduced a bug where the class names in the 
string pool were corrupted and it caused the testsuite failures I expected.  
From a performance point of view, this shows the packet behavior I was aiming 
for.

rdar://problem/27798609


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D60957

Files:
  source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
  source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h

Index: source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
===================================================================
--- source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
+++ source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -297,7 +297,8 @@
   UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table);
 
   uint32_t ParseClassInfoArray(const lldb_private::DataExtractor &data,
-                               uint32_t num_class_infos);
+                               uint32_t num_class_infos, 
+                               lldb::DataBufferSP strpool_buffer_sp);
 
   DescriptorMapUpdateResult UpdateISAToDescriptorMapSharedCache();
 
Index: source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
===================================================================
--- source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -79,10 +79,11 @@
 extern "C"
 {
     size_t strlen(const char *);
-    char *strncpy (char * s1, const char * s2, size_t n);
+    char *strcpy (char * dst, const char * src);
     int printf(const char * format, ...);
 }
 #define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__)
+#define UINT32_MAX 0xffffffff
 
 typedef struct _NXMapTable {
     void *prototype;
@@ -103,17 +104,21 @@
 {
     Class isa;
     uint32_t hash;
+    uint32_t stroffset;
 } __attribute__((__packed__));
 
 uint32_t
 __lldb_apple_objc_v2_get_dynamic_class_info (void *gdb_objc_realized_classes_ptr,
                                              void *class_infos_ptr,
                                              uint32_t class_infos_byte_size,
+                                             void *string_pool_ptr,
+                                             uint32_t string_pool_byte_size,
                                              uint32_t should_log)
 {
     DEBUG_PRINTF ("gdb_objc_realized_classes_ptr = %p\n", gdb_objc_realized_classes_ptr);
     DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
     DEBUG_PRINTF ("class_infos_byte_size = %u\n", class_infos_byte_size);
+    DEBUG_PRINTF ("string_pool_byte_size = %u\n", string_pool_byte_size);
     const NXMapTable *grc = (const NXMapTable *)gdb_objc_realized_classes_ptr;
     if (grc)
     {
@@ -123,6 +128,8 @@
             const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
             ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
             BucketInfo *buckets = (BucketInfo *)grc->buckets;
+            char *string_pool_base = (char *)string_pool_ptr;
+            uint32_t current_strpool_offset = 0;
             
             uint32_t idx = 0;
             for (unsigned i=0; i<=grc->num_buckets_minus_one; ++i)
@@ -131,12 +138,26 @@
                 {
                     if (idx < max_class_infos)
                     {
-                        const char *s = buckets[i].name_ptr;
+                        const char *name = buckets[i].name_ptr;
+                        const char *s = name;
                         uint32_t h = 5381;
                         for (unsigned char c = *s; c; c = *++s)
                             h = ((h << 5) + h) + c;
                         class_infos[idx].hash = h;
                         class_infos[idx].isa = buckets[i].isa;
+                        class_infos[idx].stroffset = UINT32_MAX; // default to no strpool offset
+                        if (string_pool_base && string_pool_byte_size != 0 && name && h != 0)
+                        {
+                            const int name_len = strlen (name);
+                            const int remaining_strpool = string_pool_byte_size - current_strpool_offset;
+                            if (name_len > 0 && remaining_strpool > (name_len + 1))
+                            {
+                                DEBUG_PRINTF ("[%u] name %s copied into stringpool offset %u\n", idx, name, current_strpool_offset);
+                                strcpy (string_pool_base + current_strpool_offset, name);
+                                class_infos[idx].stroffset = current_strpool_offset;
+                                current_strpool_offset += name_len + 1;
+                            }
+                        }
                     }
                     ++idx;
                 }
@@ -145,6 +166,7 @@
             {
                 class_infos[idx].isa = NULL;
                 class_infos[idx].hash = 0;
+                class_infos[idx].stroffset = UINT32_MAX; // no strpool offset
             }
         }
         return num_classes;
@@ -164,13 +186,13 @@
 {
     const char *class_getName(void *objc_class);
     size_t strlen(const char *);
-    char *strncpy (char * s1, const char * s2, size_t n);
+    char *strcpy (char * dst, const char * src);
     int printf(const char * format, ...);
 }
 
 #define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__)
+#define UINT32_MAX 0xffffffff
 
-
 struct objc_classheader_t {
     int32_t clsOffset;
     int32_t hiOffset;
@@ -212,18 +234,23 @@
 {
     Class isa;
     uint32_t hash;
+    uint32_t stroffset;
 }  __attribute__((__packed__));
 
 uint32_t
 __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
                                                   void *class_infos_ptr,
                                                   uint32_t class_infos_byte_size,
+                                                  void *string_pool_ptr,
+                                                  uint32_t string_pool_byte_size,
                                                   uint32_t should_log)
 {
     uint32_t idx = 0;
     DEBUG_PRINTF ("objc_opt_ro_ptr = %p\n", objc_opt_ro_ptr);
     DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
+    DEBUG_PRINTF ("string_pool_ptr = %p\n", string_pool_ptr);
     DEBUG_PRINTF ("class_infos_byte_size = %u (%llu class infos)\n", class_infos_byte_size, (uint64_t)(class_infos_byte_size/sizeof(ClassInfo)));
+    DEBUG_PRINTF ("string_pool_byte_size = %u\n", string_pool_byte_size);
     if (objc_opt_ro_ptr)
     {
         const objc_opt_t *objc_opt = (objc_opt_t *)objc_opt_ro_ptr;
@@ -254,6 +281,8 @@
             const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
             DEBUG_PRINTF("max_class_infos = %llu\n", (uint64_t)max_class_infos);
             ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
+            char *string_pool_base = (char *)string_pool_ptr;
+            uint32_t current_strpool_offset = 0;
             int32_t invalidEntryOffset = 0;
             // this is safe to do because the version field order is invariant
             if (objc_opt->version == 12)
@@ -289,8 +318,25 @@
                     const char *s = name;
                     uint32_t h = 5381;
                     for (unsigned char c = *s; c; c = *++s)
+                    {
+                        // See comment in ParseClassInfoArray
+                        if (c == '.') { h = 0; break; }
                         h = ((h << 5) + h) + c;
+                    }
                     class_infos[idx].hash = h;
+                    class_infos[idx].stroffset = UINT32_MAX; // default to no strpool offset
+                    if (string_pool_base && string_pool_byte_size != 0 && name && h != 0)
+                    {
+                        const int name_len = strlen (name);
+                        const int remaining_strpool = string_pool_byte_size - current_strpool_offset;
+                        if (name_len > 0 && remaining_strpool > (name_len + 1))
+                        {
+                            DEBUG_PRINTF ("[%u] name %s copied into stringpool offset %u\n", idx, name, current_strpool_offset);
+                            strcpy (string_pool_base + current_strpool_offset, name);
+                            class_infos[idx].stroffset = current_strpool_offset;
+                            current_strpool_offset += name_len + 1;
+                        } 
+                    }
                 }
                 else
                 {
@@ -321,8 +367,25 @@
                     const char *s = name;
                     uint32_t h = 5381;
                     for (unsigned char c = *s; c; c = *++s)
+                    {
+                        // See comment in ParseClassInfoArray
+                        if (c == '.') { h = 0; break; }
                         h = ((h << 5) + h) + c;
+                    }
                     class_infos[idx].hash = h;
+                    class_infos[idx].stroffset = UINT32_MAX; // default to no strpool offset
+                    if (string_pool_base && string_pool_byte_size != 0 && name && h != 0)
+                    {
+                        const int name_len = strlen (name);
+                        const int remaining_strpool = string_pool_byte_size - current_strpool_offset;
+                        if (name_len > 0 && remaining_strpool > (name_len + 1))
+                        {
+                            DEBUG_PRINTF ("[%u] name %s copied into stringpool offset %u\n", idx, name, current_strpool_offset);
+                            strcpy (string_pool_base + current_strpool_offset, name);
+                            current_strpool_offset += name_len + 1;
+                            class_infos[idx].stroffset = current_strpool_offset;
+                        }
+                    }
                 }
                 ++idx;
             }
@@ -1322,16 +1385,19 @@
       return DescriptorMapUpdateResult::Fail();
 
     // Next make the runner function for our implementation utility function.
-    Value value;
-    value.SetValueType(Value::eValueTypeScalar);
-    value.SetCompilerType(clang_void_pointer_type);
-    arguments.PushValue(value);
-    arguments.PushValue(value);
+    Value ptr_value;
+    Value uint32_value;
+    ptr_value.SetValueType(Value::eValueTypeScalar);
+    ptr_value.SetCompilerType(clang_void_pointer_type);
+    uint32_value.SetValueType(Value::eValueTypeScalar);
+    uint32_value.SetCompilerType(clang_uint32_t_type);
 
-    value.SetValueType(Value::eValueTypeScalar);
-    value.SetCompilerType(clang_uint32_t_type);
-    arguments.PushValue(value);
-    arguments.PushValue(value);
+    arguments.PushValue(ptr_value);    // void *gdb_objc_realized_classes_ptr
+    arguments.PushValue(ptr_value);    // void *class_infos_ptr
+    arguments.PushValue(uint32_value); // uint32_t class_infos_byte_size
+    arguments.PushValue(ptr_value);    // void *string_pool_ptr
+    arguments.PushValue(uint32_value); // uint32_t string_pool_byte_size
+    arguments.PushValue(uint32_value); // uint32_t should_log
 
     get_class_info_function = m_get_class_info_code->MakeFunctionCaller(
         clang_uint32_t_type, arguments, thread_sp, error);
@@ -1358,11 +1424,21 @@
 
   diagnostics.Clear();
 
-  const uint32_t class_info_byte_size = addr_size + 4;
+  const uint32_t class_info_byte_size = addr_size + 
+                                        4 /* hash */ + 
+                                        4 /* stroffset */;
   const uint32_t class_infos_byte_size = num_classes * class_info_byte_size;
   lldb::addr_t class_infos_addr = process->AllocateMemory(
       class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
 
+  // Assume classes names will be less than 32 chars on average.  If
+  // we run out of space, entries will have an offset of UINT32_MAX
+  // and lldb will read the class names out of memory individually.  
+  // Actual average is around 23 characters per class name in 2019.
+  const uint32_t string_pool_byte_size = num_classes * 32;
+  addr_t string_pool_addr = process->AllocateMemory(
+      string_pool_byte_size, ePermissionsReadable | ePermissionsWritable, err);
+
   if (class_infos_addr == LLDB_INVALID_ADDRESS) {
     if (log)
       log->Printf("unable to allocate %" PRIu32
@@ -1371,19 +1447,30 @@
     return DescriptorMapUpdateResult::Fail();
   }
 
+  if (string_pool_addr == LLDB_INVALID_ADDRESS) {
+    if (log)
+      log->Printf("unable to allocate %" PRIu32
+                  " bytes in process for shared cache string_pool read, will use slow method",
+                  string_pool_byte_size);
+    // NB: representing invalid address as 0 for simplicity of checking in jitted expr
+    string_pool_addr = 0; 
+  }
+
   std::lock_guard<std::mutex> guard(m_get_class_info_args_mutex);
 
   // Fill in our function argument values
   arguments.GetValueAtIndex(0)->GetScalar() = hash_table.GetTableLoadAddress();
   arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr;
   arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size;
+  arguments.GetValueAtIndex(3)->GetScalar() = string_pool_addr;
+  arguments.GetValueAtIndex(4)->GetScalar() = string_pool_byte_size;
   
   // Only dump the runtime classes from the expression evaluation if the log is
   // verbose:
   Log *type_log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES);
   bool dump_log = type_log && type_log->GetVerbose();
   
-  arguments.GetValueAtIndex(3)->GetScalar() = dump_log ? 1 : 0;
+  arguments.GetValueAtIndex(5)->GetScalar() = dump_log ? 1 : 0;
 
   bool success = false;
 
@@ -1427,7 +1514,31 @@
           DataExtractor class_infos_data(buffer.GetBytes(),
                                          buffer.GetByteSize(),
                                          process->GetByteOrder(), addr_size);
-          ParseClassInfoArray(class_infos_data, num_class_infos);
+
+          // Find the ClassInfo with the highest string pool offset, 
+          // read the stringpool into a local buffer.
+          DataBufferSP strpool_buffer_sp;
+          if (string_pool_addr) {
+            uint32_t max_offset_seen = 0;
+            const int address_size = class_infos_data.GetAddressByteSize();
+            offset_t offset = 0;
+            for (uint32_t i = 0; i < num_class_infos; ++i) {
+              offset += address_size + 4; /* isa + hash */
+              uint32_t strpool_offset = class_infos_data.GetU32(&offset);
+              if (strpool_offset != UINT32_MAX & strpool_offset > max_offset_seen)
+                max_offset_seen = strpool_offset;
+            }
+            if (max_offset_seen != 0) {
+              strpool_buffer_sp.reset(new DataBufferHeap (max_offset_seen, 0));
+              if (process->ReadMemory (string_pool_addr, strpool_buffer_sp->GetBytes(),
+                                       strpool_buffer_sp->GetByteSize(), err) !=
+                                            strpool_buffer_sp->GetByteSize()) {
+                strpool_buffer_sp.reset();
+              }
+            }
+          }
+
+          ParseClassInfoArray(class_infos_data, num_class_infos, strpool_buffer_sp);
         }
       }
       success = true;
@@ -1446,18 +1557,21 @@
 
   // Deallocate the memory we allocated for the ClassInfo array
   process->DeallocateMemory(class_infos_addr);
+  process->DeallocateMemory(string_pool_addr);
 
   return DescriptorMapUpdateResult(success, num_class_infos);
 }
 
 uint32_t AppleObjCRuntimeV2::ParseClassInfoArray(const DataExtractor &data,
-                                                 uint32_t num_class_infos) {
+                                                 uint32_t num_class_infos,
+                                                 DataBufferSP strpool_buffer_sp) {
   // Parses an array of "num_class_infos" packed ClassInfo structures:
   //
   //    struct ClassInfo
   //    {
   //        Class isa;
   //        uint32_t hash;
+  //        uint32_t stroffset;
   //    } __attribute__((__packed__));
 
   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
@@ -1483,18 +1597,54 @@
         log->Printf("AppleObjCRuntimeV2 found cached isa=0x%" PRIx64
                     ", ignoring this class info",
                     isa);
-      offset += 4;
+      offset += 4; // hash
+      offset += 4; // stroffset
     } else {
       // Read the 32 bit hash for the class name
       const uint32_t name_hash = data.GetU32(&offset);
-      ClassDescriptorSP descriptor_sp(new ClassDescriptorV2(*this, isa, NULL));
-      AddClass(isa, descriptor_sp, name_hash);
+      const uint32_t strpool_offset = data.GetU32(&offset);
+
+      bool name_read_from_strpool = false;
+      const char *name = nullptr;
+      if (strpool_buffer_sp.get() 
+          && strpool_offset != UINT32_MAX 
+          && strpool_buffer_sp->GetByteSize() > strpool_offset) {
+        name = (const char*) strpool_buffer_sp->GetBytes() + strpool_offset;
+        name_read_from_strpool = true;
+      }
+      ClassDescriptorSP descriptor_sp(new ClassDescriptorV2(*this, isa, name));
+
+      // The code in g_get_shared_cache_class_info_body sets the value of the hash
+      // to 0 to signal a mangled symbol. We use class_getName() in that code to
+      // find the class name, but this returns a demangled name for Swift symbols.
+      // For those symbols, recompute the hash here by extracing their name from the
+      // runtime (this is slow and cannot be done generally for the 45000+ symbols
+      // of the shared cache.
+
+      if (name_hash) {
+        AddClass(isa, descriptor_sp, name_hash);
+      } else {
+        if (name == nullptr) {
+          name = descriptor_sp->GetClassName().AsCString(nullptr);
+        }
+        AddClass(isa, descriptor_sp, name);
+      }
       num_parsed++;
-      if (should_log)
+      if (should_log) {
+        if (name == nullptr)
+          name = "<unknown>";
+        char name_from_strpool_buf[80];
+        if (name_read_from_strpool) {
+          snprintf (name_from_strpool_buf, sizeof (name_from_strpool_buf),
+                    "read from strpool offset %u", strpool_offset);
+        } else {
+          strcpy (name_from_strpool_buf, "read directly from memory, not via strpool");
+        }
         log->Printf("AppleObjCRuntimeV2 added isa=0x%" PRIx64
-                    ", hash=0x%8.8x, name=%s",
+                    ", hash=0x%8.8x, name=%s %s",
                     isa, name_hash,
-                    descriptor_sp->GetClassName().AsCString("<unknown>"));
+                    name, name_from_strpool_buf);
+      }
     }
   }
   if (should_log)
@@ -1579,18 +1729,19 @@
       return DescriptorMapUpdateResult::Fail();
 
     // Next make the function caller for our implementation utility function.
-    Value value;
-    value.SetValueType(Value::eValueTypeScalar);
-    // value.SetContext (Value::eContextTypeClangType, clang_void_pointer_type);
-    value.SetCompilerType(clang_void_pointer_type);
-    arguments.PushValue(value);
-    arguments.PushValue(value);
+    Value ptr_value;
+    Value uint32_value;
+    ptr_value.SetValueType(Value::eValueTypeScalar);
+    ptr_value.SetCompilerType(clang_void_pointer_type);
+    uint32_value.SetValueType(Value::eValueTypeScalar);
+    uint32_value.SetCompilerType(clang_uint32_t_type);
 
-    value.SetValueType(Value::eValueTypeScalar);
-    // value.SetContext (Value::eContextTypeClangType, clang_uint32_t_type);
-    value.SetCompilerType(clang_uint32_t_type);
-    arguments.PushValue(value);
-    arguments.PushValue(value);
+    arguments.PushValue(ptr_value);    // void *gdb_objc_realized_classes_ptr
+    arguments.PushValue(ptr_value);    // void *class_infos_ptr
+    arguments.PushValue(uint32_value); // uint32_t class_infos_byte_size
+    arguments.PushValue(ptr_value);    // void *string_pool_ptr
+    arguments.PushValue(uint32_value); // uint32_t string_pool_byte_size
+    arguments.PushValue(uint32_value); // uint32_t should_log
 
     get_shared_cache_class_info_function =
         m_get_shared_cache_class_info_code->MakeFunctionCaller(
@@ -1609,11 +1760,21 @@
 
   diagnostics.Clear();
 
-  const uint32_t class_info_byte_size = addr_size + 4;
+  const uint32_t class_info_byte_size = addr_size + 
+                                        4 /* hash */ + 
+                                        4 /* stroffset */;
   const uint32_t class_infos_byte_size = num_classes * class_info_byte_size;
   lldb::addr_t class_infos_addr = process->AllocateMemory(
       class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
 
+  // Assume classes names will be less than 32 chars on average.  If
+  // we run out of space, entries will have an offset of UINT32_MAX
+  // and lldb will read the class names out of memory individually.  
+  // Actual average is around 23 characters per class name in 2019.
+  const uint32_t string_pool_byte_size = num_classes * 32;
+  addr_t string_pool_addr = process->AllocateMemory(
+      string_pool_byte_size, ePermissionsReadable | ePermissionsWritable, err);
+
   if (class_infos_addr == LLDB_INVALID_ADDRESS) {
     if (log)
       log->Printf("unable to allocate %" PRIu32
@@ -1622,18 +1783,29 @@
     return DescriptorMapUpdateResult::Fail();
   }
 
+  if (string_pool_addr == LLDB_INVALID_ADDRESS) {
+    if (log)
+      log->Printf("unable to allocate %" PRIu32
+                  " bytes in process for shared cache string_pool read, will use slow method",
+                  string_pool_byte_size);
+    // NB: representing invalid address as 0 for simplicity of checking in jitted expr
+    string_pool_addr = 0;
+  }
+
   std::lock_guard<std::mutex> guard(m_get_shared_cache_class_info_args_mutex);
 
   // Fill in our function argument values
   arguments.GetValueAtIndex(0)->GetScalar() = objc_opt_ptr;
   arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr;
   arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size;
+  arguments.GetValueAtIndex(3)->GetScalar() = string_pool_addr;
+  arguments.GetValueAtIndex(4)->GetScalar() = string_pool_byte_size;
   // Only dump the runtime classes from the expression evaluation if the log is
   // verbose:
   Log *type_log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES);
   bool dump_log = type_log && type_log->GetVerbose();
   
-  arguments.GetValueAtIndex(3)->GetScalar() = dump_log ? 1 : 0;
+  arguments.GetValueAtIndex(5)->GetScalar() = dump_log ? 1 : 0;
 
   bool success = false;
 
@@ -1691,7 +1863,30 @@
                                          buffer.GetByteSize(),
                                          process->GetByteOrder(), addr_size);
 
-          ParseClassInfoArray(class_infos_data, num_class_infos);
+          // Find the ClassInfo with the highest string pool offset, 
+          // read the stringpool into a local buffer.
+          DataBufferSP strpool_buffer_sp;
+          if (string_pool_addr) {
+            uint32_t max_offset_seen = 0;
+            const int address_size = class_infos_data.GetAddressByteSize();
+            offset_t offset = 0;
+            for (uint32_t i = 0; i < num_class_infos; ++i) {
+              offset += address_size + 4; /* isa + hash */
+              uint32_t strpool_offset = class_infos_data.GetU32(&offset);
+              if (strpool_offset != UINT32_MAX & strpool_offset > max_offset_seen)
+                max_offset_seen = strpool_offset;
+            }
+            if (max_offset_seen != 0) {
+              strpool_buffer_sp.reset(new DataBufferHeap (max_offset_seen, 0));
+              if (process->ReadMemory (string_pool_addr, strpool_buffer_sp->GetBytes(),
+                                       strpool_buffer_sp->GetByteSize(), err) !=
+                                            strpool_buffer_sp->GetByteSize()) {
+                strpool_buffer_sp.reset();
+              }
+            }
+          }
+
+          ParseClassInfoArray(class_infos_data, num_class_infos, strpool_buffer_sp);
         }
       } else {
         success = true;
@@ -1711,6 +1906,7 @@
 
   // Deallocate the memory we allocated for the ClassInfo array
   process->DeallocateMemory(class_infos_addr);
+  process->DeallocateMemory(string_pool_addr);
 
   return DescriptorMapUpdateResult(success, num_class_infos);
 }
@@ -2428,12 +2624,12 @@
     ObjCISA isa, ObjCISA &ret_isa) {
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
 
+  if ((isa & ~m_objc_debug_isa_class_mask) == 0)
+    return false;
+
   if (log)
     log->Printf("AOCRT::NPI Evalulate(isa = 0x%" PRIx64 ")", (uint64_t)isa);
 
-  if ((isa & ~m_objc_debug_isa_class_mask) == 0)
-    return false;
-
   // If all of the indexed ISA variables are set, then its possible that this
   // ISA is indexed, and we should first try to get its value using the index.
   // Note, we check these variables first as the ObjC runtime will set at least
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to