@@ -418,6 +418,7 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
418418 void *class_infos_ptr,
419419 uint64_t *relative_selector_offset,
420420 uint32_t class_infos_byte_size,
421+ uint32_t *start_idx,
421422 uint32_t should_log)
422423{
423424 *relative_selector_offset = 0;
@@ -426,6 +427,7 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
426427 DEBUG_PRINTF ("shared_cache_base_ptr = %p\n", shared_cache_base_ptr);
427428 DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
428429 DEBUG_PRINTF ("class_infos_byte_size = %u (%llu class infos)\n", class_infos_byte_size, (uint64_t)(class_infos_byte_size/sizeof(ClassInfo)));
430+ DEBUG_PRINTF ("start_idx = %u\n", *start_idx);
429431 if (objc_opt_ro_ptr)
430432 {
431433 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,
480482 DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask);
481483 DEBUG_PRINTF ("classOffsets = %p\n", classOffsets);
482484
483- for (uint32_t i=0; i<clsopt->capacity; ++i)
485+ uint32_t original_start_idx = *start_idx;
486+
487+ // 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.
488+ for (uint32_t i=*start_idx; i<clsopt->capacity; ++i)
484489 {
485490 const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset;
486491 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,
524529 else
525530 {
526531 DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n");
532+ *start_idx = i;
533+ break;
527534 }
528535 ++idx;
529536 }
530537
531- const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity];
532- const uint32_t duplicate_count = *duplicate_count_ptr;
533- const objc_classheader_v16_t *duplicateClassOffsets = (const objc_classheader_v16_t *)(&duplicate_count_ptr[1]);
534-
535- DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count);
536- DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets);
537-
538- for (uint32_t i=0; i<duplicate_count; ++i)
539- {
540- const uint64_t objectCacheOffset = duplicateClassOffsets[i].objectCacheOffset;
541- DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset);
538+ if (idx < max_class_infos) {
539+ const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity];
540+ const uint32_t duplicate_count = *duplicate_count_ptr;
541+ const objc_classheader_v16_t *duplicateClassOffsets = (const objc_classheader_v16_t *)(&duplicate_count_ptr[1]);
542542
543- if (classOffsets[i].isDuplicate) {
544- DEBUG_PRINTF("isDuplicate = true\n");
545- continue; // duplicate
546- }
543+ DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count);
544+ DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets);
547545
548- if (objectCacheOffset == 0) {
549- DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n");
550- continue; // invalid offset
551- }
546+ uint32_t duplicate_start_idx = *start_idx < clsopt->capacity ? 0 : *start_idx - clsopt->capacity;
552547
553- if (class_infos && idx < max_class_infos )
548+ for (uint32_t i=duplicate_start_idx; i<duplicate_count; ++i )
554549 {
555- class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset);
550+ const uint64_t objectCacheOffset = duplicateClassOffsets[i].objectCacheOffset;
551+ DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset);
556552
557- // Lookup the class name.
558- const char *name = class_name_lookup_func(class_infos[idx].isa);
559- DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
553+ if (classOffsets[i].isDuplicate) {
554+ DEBUG_PRINTF("isDuplicate = true\n");
555+ continue; // duplicate
556+ }
560557
561- // Hash the class name so we don't have to read it.
562- const char *s = name;
563- uint32_t h = 5381;
564- for (unsigned char c = *s; c; c = *++s)
558+ if (objectCacheOffset == 0) {
559+ DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n");
560+ continue; // invalid offset
561+ }
562+
563+ if (class_infos && idx < max_class_infos)
565564 {
566- // class_getName demangles swift names and the hash must
567- // be calculated on the mangled name. hash==0 means lldb
568- // will fetch the mangled name and compute the hash in
569- // ParseClassInfoArray.
570- if (c == '.')
565+ class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset);
566+
567+ // Lookup the class name.
568+ const char *name = class_name_lookup_func(class_infos[idx].isa);
569+ DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
570+
571+ // Hash the class name so we don't have to read it.
572+ const char *s = name;
573+ uint32_t h = 5381;
574+ for (unsigned char c = *s; c; c = *++s)
571575 {
572- h = 0;
573- break;
576+ // class_getName demangles swift names and the hash must
577+ // be calculated on the mangled name. hash==0 means lldb
578+ // will fetch the mangled name and compute the hash in
579+ // ParseClassInfoArray.
580+ if (c == '.')
581+ {
582+ h = 0;
583+ break;
584+ }
585+ h = ((h << 5) + h) + c;
574586 }
575- h = ((h << 5) + h) + c;
587+ class_infos[idx].hash = h;
588+ } else {
589+ DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n");
590+ *start_idx = i;
591+ break;
576592 }
577- class_infos[ idx].hash = h ;
593+ ++ idx;
578594 }
579- ++idx;
595+ }
596+ // Always make sure start_idx gets updated. There's an edge case where if there are exactly max_class_infos number of classes,
597+ // start_idx will not get updated and LLDB will enter an infinite loop reading.
598+ if (*start_idx == original_start_idx) {
599+ *start_idx = idx;
580600 }
581601 }
582602 else if (objc_opt->version >= 12 && objc_opt->version <= 15)
@@ -1959,6 +1979,9 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::
19591979 CompilerType clang_uint64_t_pointer_type =
19601980 scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize (eEncodingUint, 64 )
19611981 .GetPointerType ();
1982+ CompilerType clang_uint32_t_pointer_type =
1983+ scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize (eEncodingUint, 32 )
1984+ .GetPointerType ();
19621985
19631986 // Next make the function caller for our implementation utility function.
19641987 ValueList arguments;
@@ -1976,6 +1999,13 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::
19761999 value.SetValueType (Value::ValueType::Scalar);
19772000 value.SetCompilerType (clang_uint32_t_type);
19782001 arguments.PushValue (value);
2002+
2003+ value.SetValueType (Value::ValueType::Scalar);
2004+ value.SetCompilerType (clang_uint32_t_pointer_type);
2005+ arguments.PushValue (value);
2006+
2007+ value.SetValueType (Value::ValueType::Scalar);
2008+ value.SetCompilerType (clang_uint32_t_type);
19792009 arguments.PushValue (value);
19802010
19812011 std::unique_ptr<UtilityFunction> utility_fn = std::move (*utility_fn_or_error);
@@ -2313,10 +2343,7 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
23132343
23142344 // The number of entries to pre-allocate room for.
23152345 // Each entry is (addrsize + 4) bytes
2316- // FIXME: It is not sustainable to continue incrementing this value every time
2317- // the shared cache grows. This is because it requires allocating memory in
2318- // the inferior process and some inferior processes have small memory limits.
2319- const uint32_t max_num_classes = 212992 ;
2346+ const uint32_t max_num_classes_in_buffer = 212992 ;
23202347
23212348 UtilityFunction *get_class_info_code = GetClassInfoUtilityFunction (exe_ctx);
23222349 if (!get_class_info_code) {
@@ -2338,22 +2365,40 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
23382365 DiagnosticManager diagnostics;
23392366
23402367 const uint32_t class_info_byte_size = addr_size + 4 ;
2341- const uint32_t class_infos_byte_size = max_num_classes * class_info_byte_size;
2368+ const uint32_t class_infos_byte_size =
2369+ max_num_classes_in_buffer * class_info_byte_size;
23422370 lldb::addr_t class_infos_addr = process->AllocateMemory (
23432371 class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
23442372 const uint32_t relative_selector_offset_addr_size = 64 ;
23452373 lldb::addr_t relative_selector_offset_addr =
23462374 process->AllocateMemory (relative_selector_offset_addr_size,
23472375 ePermissionsReadable | ePermissionsWritable, err);
2376+ constexpr uint32_t class_info_start_idx_byte_size = sizeof (uint32_t );
2377+ lldb::addr_t class_info_start_idx_addr =
2378+ process->AllocateMemory (class_info_start_idx_byte_size,
2379+ ePermissionsReadable | ePermissionsWritable, err);
23482380
2349- if (class_infos_addr == LLDB_INVALID_ADDRESS) {
2381+ if (class_infos_addr == LLDB_INVALID_ADDRESS ||
2382+ relative_selector_offset_addr == LLDB_INVALID_ADDRESS ||
2383+ class_info_start_idx_addr == LLDB_INVALID_ADDRESS) {
23502384 LLDB_LOGF (log,
23512385 " unable to allocate %" PRIu32
23522386 " bytes in process for shared cache read" ,
23532387 class_infos_byte_size);
23542388 return DescriptorMapUpdateResult::Fail ();
23552389 }
23562390
2391+ const uint32_t start_idx_init_value = 0 ;
2392+ size_t bytes_written = process->WriteMemory (
2393+ class_info_start_idx_addr, &start_idx_init_value, sizeof (uint32_t ), err);
2394+ if (bytes_written != sizeof (uint32_t )) {
2395+ LLDB_LOGF (log,
2396+ " unable to write %" PRIu32
2397+ " bytes in process for shared cache read" ,
2398+ class_infos_byte_size);
2399+ return DescriptorMapUpdateResult::Fail ();
2400+ }
2401+
23572402 std::lock_guard<std::mutex> guard (m_mutex);
23582403
23592404 // Fill in our function argument values
@@ -2362,12 +2407,13 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
23622407 arguments.GetValueAtIndex (2 )->GetScalar () = class_infos_addr;
23632408 arguments.GetValueAtIndex (3 )->GetScalar () = relative_selector_offset_addr;
23642409 arguments.GetValueAtIndex (4 )->GetScalar () = class_infos_byte_size;
2410+ arguments.GetValueAtIndex (5 )->GetScalar () = class_info_start_idx_addr;
23652411 // Only dump the runtime classes from the expression evaluation if the log is
23662412 // verbose:
23672413 Log *type_log = GetLog (LLDBLog::Types);
23682414 bool dump_log = type_log && type_log->GetVerbose ();
23692415
2370- arguments.GetValueAtIndex (5 )->GetScalar () = dump_log ? 1 : 0 ;
2416+ arguments.GetValueAtIndex (6 )->GetScalar () = dump_log ? 1 : 0 ;
23712417
23722418 bool success = false ;
23732419
@@ -2394,78 +2440,84 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
23942440
23952441 diagnostics.Clear ();
23962442
2397- // Run the function
2398- ExpressionResults results =
2399- get_shared_cache_class_info_function->ExecuteFunction (
2400- exe_ctx, &m_args, options, diagnostics, return_value);
2401-
2402- if (results == eExpressionCompleted) {
2403- // The result is the number of ClassInfo structures that were filled in
2404- num_class_infos = return_value.GetScalar ().ULong ();
2405- LLDB_LOG (log, " Discovered {0} Objective-C classes in the shared cache" ,
2406- num_class_infos);
2407- // Assert if there were more classes than we pre-allocated
2408- // room for.
2409- assert (num_class_infos <= max_num_classes);
2410- if (num_class_infos > 0 ) {
2411- if (num_class_infos > max_num_classes) {
2412- num_class_infos = max_num_classes;
2413-
2414- success = false ;
2415- } else {
2443+ uint32_t num_class_infos_read = 0 ;
2444+ bool already_read_relative_selector_offset = false ;
2445+
2446+ do {
2447+ // Run the function
2448+ ExpressionResults results =
2449+ get_shared_cache_class_info_function->ExecuteFunction (
2450+ exe_ctx, &m_args, options, diagnostics, return_value);
2451+
2452+ if (results == eExpressionCompleted) {
2453+ // The result is the number of ClassInfo structures that were filled in
2454+ num_class_infos_read = return_value.GetScalar ().ULong ();
2455+ num_class_infos += num_class_infos_read;
2456+ LLDB_LOG (log, " Discovered {0} Objective-C classes in the shared cache" ,
2457+ num_class_infos_read);
2458+ if (num_class_infos_read > 0 ) {
24162459 success = true ;
2417- }
24182460
2419- // Read the relative selector offset.
2420- DataBufferHeap relative_selector_offset_buffer (64 , 0 );
2421- if (process->ReadMemory (relative_selector_offset_addr,
2422- relative_selector_offset_buffer.GetBytes (),
2423- relative_selector_offset_buffer.GetByteSize (),
2424- err) ==
2425- relative_selector_offset_buffer.GetByteSize ()) {
2426- DataExtractor relative_selector_offset_data (
2427- relative_selector_offset_buffer.GetBytes (),
2428- relative_selector_offset_buffer.GetByteSize (),
2429- process->GetByteOrder (), addr_size);
2430- lldb::offset_t offset = 0 ;
2431- uint64_t relative_selector_offset =
2432- relative_selector_offset_data.GetU64 (&offset);
2433- if (relative_selector_offset > 0 ) {
2434- // The offset is relative to the objc_opt struct.
2435- m_runtime.SetRelativeSelectorBaseAddr (objc_opt_ptr +
2436- relative_selector_offset);
2461+ // Read the relative selector offset. This only needs to occur once no
2462+ // matter how many times the function is called.
2463+ if (!already_read_relative_selector_offset) {
2464+ DataBufferHeap relative_selector_offset_buffer (64 , 0 );
2465+ if (process->ReadMemory (
2466+ relative_selector_offset_addr,
2467+ relative_selector_offset_buffer.GetBytes (),
2468+ relative_selector_offset_buffer.GetByteSize (),
2469+ err) == relative_selector_offset_buffer.GetByteSize ()) {
2470+ DataExtractor relative_selector_offset_data (
2471+ relative_selector_offset_buffer.GetBytes (),
2472+ relative_selector_offset_buffer.GetByteSize (),
2473+ process->GetByteOrder (), addr_size);
2474+ lldb::offset_t offset = 0 ;
2475+ uint64_t relative_selector_offset =
2476+ relative_selector_offset_data.GetU64 (&offset);
2477+ if (relative_selector_offset > 0 ) {
2478+ // The offset is relative to the objc_opt struct.
2479+ m_runtime.SetRelativeSelectorBaseAddr (objc_opt_ptr +
2480+ relative_selector_offset);
2481+ }
2482+ }
2483+ already_read_relative_selector_offset = true ;
24372484 }
2438- }
2439-
2440- // Read the ClassInfo structures
2441- DataBufferHeap class_infos_buffer (
2442- num_class_infos * class_info_byte_size, 0 );
2443- if (process->ReadMemory (class_infos_addr, class_infos_buffer.GetBytes (),
2444- class_infos_buffer.GetByteSize (),
2445- err) == class_infos_buffer.GetByteSize ()) {
2446- DataExtractor class_infos_data (class_infos_buffer.GetBytes (),
2447- class_infos_buffer.GetByteSize (),
2448- process->GetByteOrder (), addr_size);
24492485
2450- m_runtime.ParseClassInfoArray (class_infos_data, num_class_infos);
2486+ // Read the ClassInfo structures
2487+ DataBufferHeap class_infos_buffer (
2488+ num_class_infos_read * class_info_byte_size, 0 );
2489+ if (process->ReadMemory (class_infos_addr,
2490+ class_infos_buffer.GetBytes (),
2491+ class_infos_buffer.GetByteSize (),
2492+ err) == class_infos_buffer.GetByteSize ()) {
2493+ DataExtractor class_infos_data (class_infos_buffer.GetBytes (),
2494+ class_infos_buffer.GetByteSize (),
2495+ process->GetByteOrder (), addr_size);
2496+
2497+ m_runtime.ParseClassInfoArray (class_infos_data,
2498+ num_class_infos_read);
2499+ }
24512500 }
24522501 } else {
2453- success = true ;
2454- }
2455- } else {
2456- if (log) {
2457- LLDB_LOGF (log, " Error evaluating our find class name function." );
2458- diagnostics.Dump (log);
2502+ if (log) {
2503+ LLDB_LOGF (log, " Error evaluating our find class name function." );
2504+ diagnostics.Dump (log);
2505+ break ;
2506+ }
24592507 }
2460- }
2508+ } while (num_class_infos_read == max_num_classes_in_buffer);
24612509 } else {
24622510 if (log) {
24632511 LLDB_LOGF (log, " Error writing function arguments." );
24642512 diagnostics.Dump (log);
24652513 }
24662514 }
24672515
2468- // Deallocate the memory we allocated for the ClassInfo array
2516+ LLDB_LOG (log, " Processed {0} Objective-C classes total from the shared cache" ,
2517+ num_class_infos);
2518+ // Cleanup memory we allocated in the process.
2519+ process->DeallocateMemory (relative_selector_offset_addr);
2520+ process->DeallocateMemory (class_info_start_idx_addr);
24692521 process->DeallocateMemory (class_infos_addr);
24702522
24712523 return DescriptorMapUpdateResult (success, false , num_class_infos);
0 commit comments