Skip to content

Conversation

@bulbazord
Copy link
Member

On Apple's platforms, the size of the shared cache grows steadily. As it grows, so does its list of ObjC classes. LLDB currently accepts an upper limit to the number of classes when it extracts the class information. Every few years we will hit the limit and increase the upper limit of classes.

This approach is fundamentally unsustainable. On top of needing to manually adjust the number every few years, our current method requires us to allocate memory in the inferior process. On macOS this is usually not a problem, but on embedded devices there is usually a limit to how much memory a process can allocate before they are killed by the OS.

My solution involves running the metadata extraction logic multiple times. I've added a new parameter to our utility function start_idx that keeps track of where it stopped during the previous run so that it may pick up again where it stopped.

rdar://91398396

…m growth

On Apple's platforms, the size of the shared cache grows steadily. As it
grows, so does its list of ObjC classes. LLDB currently accepts an upper
limit to the number of classes when it extracts the class information.
Every few years we will hit the limit and increase the upper limit of
classes.

This approach is fundamentally unsustainable. On top of needing to
manually adjust the number every few years, our current method requires
us to allocate memory in the inferior process. On macOS this is usually
not a problem, but on embedded devices there is usually a limit to how
much memory a process can allocate before they are killed by the OS.

My solution involves running the metadata extraction logic multiple
times. I've added a new parameter to our utility function `start_idx`
that keeps track of where it stopped during the previous run so that it
may pick up again where it stopped.

rdar://91398396
@llvmbot
Copy link
Member

llvmbot commented Nov 11, 2025

@llvm/pr-subscribers-lldb

Author: Alex Langford (bulbazord)

Changes

On Apple's platforms, the size of the shared cache grows steadily. As it grows, so does its list of ObjC classes. LLDB currently accepts an upper limit to the number of classes when it extracts the class information. Every few years we will hit the limit and increase the upper limit of classes.

This approach is fundamentally unsustainable. On top of needing to manually adjust the number every few years, our current method requires us to allocate memory in the inferior process. On macOS this is usually not a problem, but on embedded devices there is usually a limit to how much memory a process can allocate before they are killed by the OS.

My solution involves running the metadata extraction logic multiple times. I've added a new parameter to our utility function start_idx that keeps track of where it stopped during the previous run so that it may pick up again where it stopped.

rdar://91398396


Full diff: https://github.com/llvm/llvm-project/pull/167579.diff

1 Files Affected:

  • (modified) lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp (+156-104)
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
index 9fff4adbff79d..0c8f6fd595192 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -418,6 +418,7 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
                                                   void *class_infos_ptr,
                                                   uint64_t *relative_selector_offset,
                                                   uint32_t class_infos_byte_size,
+                                                  uint32_t *start_idx,
                                                   uint32_t should_log)
 {
     *relative_selector_offset = 0;
@@ -426,6 +427,7 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
     DEBUG_PRINTF ("shared_cache_base_ptr = %p\n", shared_cache_base_ptr);
     DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_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 ("start_idx = %u\n", *start_idx);
     if (objc_opt_ro_ptr)
     {
         const objc_opt_t *objc_opt = (objc_opt_t *)objc_opt_ro_ptr;
@@ -480,7 +482,10 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
             DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask);
             DEBUG_PRINTF ("classOffsets = %p\n", classOffsets);
 
-            for (uint32_t i=0; i<clsopt->capacity; ++i)
+            uint32_t original_start_idx = *start_idx;
+
+            // Always start at the start_idx here. If it's greater than the capacity, it will skip the loop entirely and go to the duplicate handling below.
+            for (uint32_t i=*start_idx; i<clsopt->capacity; ++i)
             {
                 const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset;
                 DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset);
@@ -524,59 +529,74 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
                 else
                 {
                     DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n");
+                    *start_idx = i;
+                    break;
                 }
                 ++idx;
             }
 
-            const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity];
-            const uint32_t duplicate_count = *duplicate_count_ptr;
-            const objc_classheader_v16_t *duplicateClassOffsets = (const objc_classheader_v16_t *)(&duplicate_count_ptr[1]);
-
-            DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count);
-            DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets);
-
-            for (uint32_t i=0; i<duplicate_count; ++i)
-            {
-                const uint64_t objectCacheOffset = duplicateClassOffsets[i].objectCacheOffset;
-                DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset);
+            if (idx < max_class_infos) {
+                const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity];
+                const uint32_t duplicate_count = *duplicate_count_ptr;
+                const objc_classheader_v16_t *duplicateClassOffsets = (const objc_classheader_v16_t *)(&duplicate_count_ptr[1]);
 
-                if (classOffsets[i].isDuplicate) {
-                    DEBUG_PRINTF("isDuplicate = true\n");
-                    continue; // duplicate
-                }
+                DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count);
+                DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets);
 
-                if (objectCacheOffset == 0) {
-                    DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n");
-                    continue; // invalid offset
-                }
+                uint32_t duplicate_start_idx = *start_idx < clsopt->capacity ? 0 : *start_idx - clsopt->capacity;
 
-                if (class_infos && idx < max_class_infos)
+                for (uint32_t i=duplicate_start_idx; i<duplicate_count; ++i)
                 {
-                    class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset);
+                    const uint64_t objectCacheOffset = duplicateClassOffsets[i].objectCacheOffset;
+                    DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset);
 
-                    // Lookup the class name.
-                    const char *name = class_name_lookup_func(class_infos[idx].isa);
-                    DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
+                    if (classOffsets[i].isDuplicate) {
+                        DEBUG_PRINTF("isDuplicate = true\n");
+                        continue; // duplicate
+                    }
 
-                    // Hash the class name so we don't have to read it.
-                    const char *s = name;
-                    uint32_t h = 5381;
-                    for (unsigned char c = *s; c; c = *++s)
+                    if (objectCacheOffset == 0) {
+                        DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n");
+                        continue; // invalid offset
+                    }
+
+                    if (class_infos && idx < max_class_infos)
                     {
-                        // class_getName demangles swift names and the hash must
-                        // be calculated on the mangled name.  hash==0 means lldb
-                        // will fetch the mangled name and compute the hash in
-                        // ParseClassInfoArray.
-                        if (c == '.')
+                        class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset);
+
+                        // Lookup the class name.
+                        const char *name = class_name_lookup_func(class_infos[idx].isa);
+                        DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
+
+                        // Hash the class name so we don't have to read it.
+                        const char *s = name;
+                        uint32_t h = 5381;
+                        for (unsigned char c = *s; c; c = *++s)
                         {
-                            h = 0;
-                            break;
+                            // class_getName demangles swift names and the hash must
+                            // be calculated on the mangled name.  hash==0 means lldb
+                            // will fetch the mangled name and compute the hash in
+                            // ParseClassInfoArray.
+                            if (c == '.')
+                            {
+                                h = 0;
+                                break;
+                            }
+                            h = ((h << 5) + h) + c;
                         }
-                        h = ((h << 5) + h) + c;
+                        class_infos[idx].hash = h;
+                    } else {
+                        DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n");
+                        *start_idx = i;
+                        break;
                     }
-                    class_infos[idx].hash = h;
+                    ++idx;
                 }
-                ++idx;
+            }
+            // Always make sure start_idx gets updated. There's an edge case where if there are exactly max_class_infos number of classes,
+            // start_idx will not get updated and LLDB will enter an infinite loop reading.
+            if (*start_idx == original_start_idx) {
+              *start_idx = idx;
             }
         }
         else if (objc_opt->version >= 12 && objc_opt->version <= 15)
@@ -1959,6 +1979,9 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::
   CompilerType clang_uint64_t_pointer_type =
       scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 64)
           .GetPointerType();
+  CompilerType clang_uint32_t_pointer_type =
+      scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32)
+          .GetPointerType();
 
   // Next make the function caller for our implementation utility function.
   ValueList arguments;
@@ -1976,6 +1999,13 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::
   value.SetValueType(Value::ValueType::Scalar);
   value.SetCompilerType(clang_uint32_t_type);
   arguments.PushValue(value);
+
+  value.SetValueType(Value::ValueType::Scalar);
+  value.SetCompilerType(clang_uint32_t_pointer_type);
+  arguments.PushValue(value);
+
+  value.SetValueType(Value::ValueType::Scalar);
+  value.SetCompilerType(clang_uint32_t_type);
   arguments.PushValue(value);
 
   std::unique_ptr<UtilityFunction> utility_fn = std::move(*utility_fn_or_error);
@@ -2313,10 +2343,7 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
 
   // The number of entries to pre-allocate room for.
   // Each entry is (addrsize + 4) bytes
-  // FIXME: It is not sustainable to continue incrementing this value every time
-  // the shared cache grows. This is because it requires allocating memory in
-  // the inferior process and some inferior processes have small memory limits.
-  const uint32_t max_num_classes = 212992;
+  const uint32_t max_num_classes_in_buffer = 212992;
 
   UtilityFunction *get_class_info_code = GetClassInfoUtilityFunction(exe_ctx);
   if (!get_class_info_code) {
@@ -2338,15 +2365,22 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
   DiagnosticManager diagnostics;
 
   const uint32_t class_info_byte_size = addr_size + 4;
-  const uint32_t class_infos_byte_size = max_num_classes * class_info_byte_size;
+  const uint32_t class_infos_byte_size =
+      max_num_classes_in_buffer * class_info_byte_size;
   lldb::addr_t class_infos_addr = process->AllocateMemory(
       class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
   const uint32_t relative_selector_offset_addr_size = 64;
   lldb::addr_t relative_selector_offset_addr =
       process->AllocateMemory(relative_selector_offset_addr_size,
                               ePermissionsReadable | ePermissionsWritable, err);
+  constexpr uint32_t class_info_start_idx_byte_size = sizeof(uint32_t);
+  lldb::addr_t class_info_start_idx_addr =
+      process->AllocateMemory(class_info_start_idx_byte_size,
+                              ePermissionsReadable | ePermissionsWritable, err);
 
-  if (class_infos_addr == LLDB_INVALID_ADDRESS) {
+  if (class_infos_addr == LLDB_INVALID_ADDRESS ||
+      relative_selector_offset_addr == LLDB_INVALID_ADDRESS ||
+      class_info_start_idx_addr == LLDB_INVALID_ADDRESS) {
     LLDB_LOGF(log,
               "unable to allocate %" PRIu32
               " bytes in process for shared cache read",
@@ -2354,6 +2388,17 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
     return DescriptorMapUpdateResult::Fail();
   }
 
+  const uint32_t start_idx_init_value = 0;
+  size_t bytes_written = process->WriteMemory(
+      class_info_start_idx_addr, &start_idx_init_value, sizeof(uint32_t), err);
+  if (bytes_written != sizeof(uint32_t)) {
+    LLDB_LOGF(log,
+              "unable to write %" PRIu32
+              " bytes in process for shared cache read",
+              class_infos_byte_size);
+    return DescriptorMapUpdateResult::Fail();
+  }
+
   std::lock_guard<std::mutex> guard(m_mutex);
 
   // Fill in our function argument values
@@ -2362,12 +2407,13 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
   arguments.GetValueAtIndex(2)->GetScalar() = class_infos_addr;
   arguments.GetValueAtIndex(3)->GetScalar() = relative_selector_offset_addr;
   arguments.GetValueAtIndex(4)->GetScalar() = class_infos_byte_size;
+  arguments.GetValueAtIndex(5)->GetScalar() = class_info_start_idx_addr;
   // Only dump the runtime classes from the expression evaluation if the log is
   // verbose:
   Log *type_log = GetLog(LLDBLog::Types);
   bool dump_log = type_log && type_log->GetVerbose();
 
-  arguments.GetValueAtIndex(5)->GetScalar() = dump_log ? 1 : 0;
+  arguments.GetValueAtIndex(6)->GetScalar() = dump_log ? 1 : 0;
 
   bool success = false;
 
@@ -2394,70 +2440,72 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
 
     diagnostics.Clear();
 
-    // Run the function
-    ExpressionResults results =
-        get_shared_cache_class_info_function->ExecuteFunction(
-            exe_ctx, &m_args, options, diagnostics, return_value);
-
-    if (results == eExpressionCompleted) {
-      // The result is the number of ClassInfo structures that were filled in
-      num_class_infos = return_value.GetScalar().ULong();
-      LLDB_LOG(log, "Discovered {0} Objective-C classes in the shared cache",
-               num_class_infos);
-      // Assert if there were more classes than we pre-allocated
-      // room for.
-      assert(num_class_infos <= max_num_classes);
-      if (num_class_infos > 0) {
-        if (num_class_infos > max_num_classes) {
-          num_class_infos = max_num_classes;
-
-          success = false;
-        } else {
+    uint32_t num_class_infos_read = 0;
+    bool already_read_relative_selector_offset = false;
+
+    do {
+      // Run the function
+      ExpressionResults results =
+          get_shared_cache_class_info_function->ExecuteFunction(
+              exe_ctx, &m_args, options, diagnostics, return_value);
+
+      if (results == eExpressionCompleted) {
+        // The result is the number of ClassInfo structures that were filled in
+        num_class_infos_read = return_value.GetScalar().ULong();
+        num_class_infos += num_class_infos_read;
+        LLDB_LOG(log, "Discovered {0} Objective-C classes in the shared cache",
+                 num_class_infos_read);
+        if (num_class_infos_read > 0) {
           success = true;
-        }
 
-        // Read the relative selector offset.
-        DataBufferHeap relative_selector_offset_buffer(64, 0);
-        if (process->ReadMemory(relative_selector_offset_addr,
-                                relative_selector_offset_buffer.GetBytes(),
-                                relative_selector_offset_buffer.GetByteSize(),
-                                err) ==
-            relative_selector_offset_buffer.GetByteSize()) {
-          DataExtractor relative_selector_offset_data(
-              relative_selector_offset_buffer.GetBytes(),
-              relative_selector_offset_buffer.GetByteSize(),
-              process->GetByteOrder(), addr_size);
-          lldb::offset_t offset = 0;
-          uint64_t relative_selector_offset =
-              relative_selector_offset_data.GetU64(&offset);
-          if (relative_selector_offset > 0) {
-            // The offset is relative to the objc_opt struct.
-            m_runtime.SetRelativeSelectorBaseAddr(objc_opt_ptr +
-                                                  relative_selector_offset);
+          // Read the relative selector offset. This only needs to occur once no
+          // matter how many times the function is called.
+          if (!already_read_relative_selector_offset) {
+            DataBufferHeap relative_selector_offset_buffer(64, 0);
+            if (process->ReadMemory(
+                    relative_selector_offset_addr,
+                    relative_selector_offset_buffer.GetBytes(),
+                    relative_selector_offset_buffer.GetByteSize(),
+                    err) == relative_selector_offset_buffer.GetByteSize()) {
+              DataExtractor relative_selector_offset_data(
+                  relative_selector_offset_buffer.GetBytes(),
+                  relative_selector_offset_buffer.GetByteSize(),
+                  process->GetByteOrder(), addr_size);
+              lldb::offset_t offset = 0;
+              uint64_t relative_selector_offset =
+                  relative_selector_offset_data.GetU64(&offset);
+              if (relative_selector_offset > 0) {
+                // The offset is relative to the objc_opt struct.
+                m_runtime.SetRelativeSelectorBaseAddr(objc_opt_ptr +
+                                                      relative_selector_offset);
+              }
+            }
+            already_read_relative_selector_offset = true;
           }
-        }
-
-        // Read the ClassInfo structures
-        DataBufferHeap class_infos_buffer(
-            num_class_infos * class_info_byte_size, 0);
-        if (process->ReadMemory(class_infos_addr, class_infos_buffer.GetBytes(),
-                                class_infos_buffer.GetByteSize(),
-                                err) == class_infos_buffer.GetByteSize()) {
-          DataExtractor class_infos_data(class_infos_buffer.GetBytes(),
-                                         class_infos_buffer.GetByteSize(),
-                                         process->GetByteOrder(), addr_size);
 
-          m_runtime.ParseClassInfoArray(class_infos_data, num_class_infos);
+          // Read the ClassInfo structures
+          DataBufferHeap class_infos_buffer(
+              num_class_infos_read * class_info_byte_size, 0);
+          if (process->ReadMemory(class_infos_addr,
+                                  class_infos_buffer.GetBytes(),
+                                  class_infos_buffer.GetByteSize(),
+                                  err) == class_infos_buffer.GetByteSize()) {
+            DataExtractor class_infos_data(class_infos_buffer.GetBytes(),
+                                           class_infos_buffer.GetByteSize(),
+                                           process->GetByteOrder(), addr_size);
+
+            m_runtime.ParseClassInfoArray(class_infos_data,
+                                          num_class_infos_read);
+          }
         }
       } else {
-        success = true;
-      }
-    } else {
-      if (log) {
-        LLDB_LOGF(log, "Error evaluating our find class name function.");
-        diagnostics.Dump(log);
+        if (log) {
+          LLDB_LOGF(log, "Error evaluating our find class name function.");
+          diagnostics.Dump(log);
+          break;
+        }
       }
-    }
+    } while (num_class_infos_read == max_num_classes_in_buffer);
   } else {
     if (log) {
       LLDB_LOGF(log, "Error writing function arguments.");
@@ -2465,7 +2513,11 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
     }
   }
 
-  // Deallocate the memory we allocated for the ClassInfo array
+  LLDB_LOG(log, "Processed {0} Objective-C classes total from the shared cache",
+           num_class_infos);
+  // Cleanup memory we allocated in the process.
+  process->DeallocateMemory(relative_selector_offset_addr);
+  process->DeallocateMemory(class_info_start_idx_addr);
   process->DeallocateMemory(class_infos_addr);
 
   return DescriptorMapUpdateResult(success, false, num_class_infos);

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I was commenting on this PR, I started wondering if we're opening ourselves up for a potential race condition by reading this in multiple parts. I think the answer is no because we don't call anything that requires running other threads (i.e. we don't call anything in the Objective-C runtime that locks) and we explicitly disable that functionality with SetTryAllThreads(false) when calling the utility function.

}
}
}
} while (num_class_infos_read == max_num_classes_in_buffer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting way to figure out if we're done reading. I would have (naively) tried to figure out the number of classes to read, but this avoids an additional out argument.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clsopt->capacity + clsopt->duplicate_count gives you an upper bound but each entry must be examined to know if it should be counted. It seemed simpler to call the function until the buffer isn't completely filled.

}

// Deallocate the memory we allocated for the ClassInfo array
LLDB_LOG(log, "Processed {0} Objective-C classes total from the shared cache",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worthwhile (e.g. for performance reasons) to include how many times we had to invoke the utility function to read all the classes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, though this information can be derived from existing logs. I log after every call, this log at the end tells you the total number of classes processed.

@github-actions
Copy link

github-actions bot commented Nov 11, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, though I'd like to have at least one more pair of eyes on this :-)

@jimingham
Copy link
Collaborator

This makes sense to me as well. The only thing I wonder is, given this is partly driven by memory constraints on smaller devices, are we penalizing systems that don't have these constraints by having to call this function more times than necessary? We could fix that by having the platform set the size of the chunk to allocate, but I don't know if that's overkill or not.

@bulbazord
Copy link
Member Author

This makes sense to me as well. The only thing I wonder is, given this is partly driven by memory constraints on smaller devices, are we penalizing systems that don't have these constraints by having to call this function more times than necessary? We could fix that by having the platform set the size of the chunk to allocate, but I don't know if that's overkill or not.

It may be penalizing if those memory unconstrained devices have their shared cache go over the buffer limit. This is a potential performance improvement opportunity in that case, but I haven't been able to reproduce that.

@bulbazord bulbazord merged commit ec4207b into llvm:main Nov 12, 2025
10 checks passed
@bulbazord bulbazord deleted the shared-cache-growth-ohno branch November 12, 2025 18:52
@slydiman
Copy link
Contributor

Buildbots lldb-x86_64-win and lldb-remote-linux-win are broken after this patch.

76.904 [190/10/5540]Building CXX object tools\lldb\source\Plugins\LanguageRuntime\ObjC\AppleObjCRuntime\CMakeFiles\lldbPluginAppleObjCRuntime.dir\AppleObjCRuntimeV2.cpp.obj
FAILED: tools/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/CMakeFiles/lldbPluginAppleObjCRuntime.dir/AppleObjCRuntimeV2.cpp.obj 
ccache C:\PROGRA~1\MICROS~1\2022\COMMUN~1\VC\Tools\MSVC\1444~1.352\bin\Hostx64\x64\cl.exe  /nologo /TP -DCLANG_BUILD_STATIC -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_GLIBCXX_ASSERTIONS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source\Plugins\LanguageRuntime\ObjC\AppleObjCRuntime -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\LanguageRuntime\ObjC\AppleObjCRuntime -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source\Plugins\LanguageRuntime\ObjC -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\include -IC:\Python312\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source -D__OPTIMIZE__ /Zc:inline /Zc:preprocessor /Zc:__cplusplus /Oi /bigobj /permissive- /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd5105 -wd4324 -wd4251 -wd4275 -w14062 -we4238 /Gw /O2 /Ob2  -MD   -wd4018 -wd4068 -wd4150 -wd4201 -wd4251 -wd4521 -wd4530 -wd4589  /EHs-c- /GR- -UNDEBUG -std:c++17 /showIncludes /Fotools\lldb\source\Plugins\LanguageRuntime\ObjC\AppleObjCRuntime\CMakeFiles\lldbPluginAppleObjCRuntime.dir\AppleObjCRuntimeV2.cpp.obj /Fdtools\lldb\source\Plugins\LanguageRuntime\ObjC\AppleObjCRuntime\CMakeFiles\lldbPluginAppleObjCRuntime.dir\lldbPluginAppleObjCRuntime.pdb /FS -c C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\LanguageRuntime\ObjC\AppleObjCRuntime\AppleObjCRuntimeV2.cpp
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\LanguageRuntime\ObjC\AppleObjCRuntime\AppleObjCRuntimeV2.cpp(692): error C2026: string too big, trailing characters truncated

Please take a look.

@bulbazord
Copy link
Member Author

Looking now, will attempt to fix forward and revert otherwise.

@slydiman
Copy link
Contributor

It seems the string is longer than the limit of 16380 single-byte characters for MSVC.
You can break it up as follows:
https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?view=msvc-170

@bulbazord
Copy link
Member Author

Fixing forward: #167761

bulbazord added a commit that referenced this pull request Nov 12, 2025
After my previous change (#167579), the string exceeded 16380
single-byte characters. MSVC did not like this, so I'm splitting it up
into two strings.
bulbazord added a commit to bulbazord/llvm-project that referenced this pull request Nov 12, 2025
After my previous change (llvm#167579), the string exceeded 16380
single-byte characters. MSVC did not like this, so I'm splitting it up
into two strings.

(cherry picked from commit 6806349)
bulbazord added a commit to swiftlang/llvm-project that referenced this pull request Nov 13, 2025
…m growth (llvm#167579)

On Apple's platforms, the size of the shared cache grows steadily. As it
grows, so does its list of ObjC classes. LLDB currently accepts an upper
limit to the number of classes when it extracts the class information.
Every few years we will hit the limit and increase the upper limit of
classes.

This approach is fundamentally unsustainable. On top of needing to
manually adjust the number every few years, our current method requires
us to allocate memory in the inferior process. On macOS this is usually
not a problem, but on embedded devices there is usually a limit to how
much memory a process can allocate before they are killed by the OS.

My solution involves running the metadata extraction logic multiple
times. I've added a new parameter to our utility function `start_idx`
that keeps track of where it stopped during the previous run so that it
may pick up again where it stopped.

rdar://91398396
(cherry picked from commit ec4207b)
git-crd pushed a commit to git-crd/crd-llvm-project that referenced this pull request Nov 13, 2025
…m growth (llvm#167579)

On Apple's platforms, the size of the shared cache grows steadily. As it
grows, so does its list of ObjC classes. LLDB currently accepts an upper
limit to the number of classes when it extracts the class information.
Every few years we will hit the limit and increase the upper limit of
classes.

This approach is fundamentally unsustainable. On top of needing to
manually adjust the number every few years, our current method requires
us to allocate memory in the inferior process. On macOS this is usually
not a problem, but on embedded devices there is usually a limit to how
much memory a process can allocate before they are killed by the OS.

My solution involves running the metadata extraction logic multiple
times. I've added a new parameter to our utility function `start_idx`
that keeps track of where it stopped during the previous run so that it
may pick up again where it stopped.

rdar://91398396
git-crd pushed a commit to git-crd/crd-llvm-project that referenced this pull request Nov 13, 2025
After my previous change (llvm#167579), the string exceeded 16380
single-byte characters. MSVC did not like this, so I'm splitting it up
into two strings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants