diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 9fff4adbff79d..27a2c9e1d1dd9 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,11 @@ __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; icapacity; ++i) + const 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; icapacity; ++i) { const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset; DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset); @@ -524,59 +530,77 @@ __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]); + 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]); - DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count); - DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets); + DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count); + DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets); - for (uint32_t i=0; icapacity ? + 0 : + *start_idx - clsopt->capacity; - if (classOffsets[i].isDuplicate) { - DEBUG_PRINTF("isDuplicate = true\n"); - continue; // duplicate - } - - if (objectCacheOffset == 0) { - DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n"); - continue; // invalid offset - } - - if (class_infos && idx < max_class_infos) + for (uint32_t i=duplicate_start_idx; iversion >= 12 && objc_opt->version <= 15) @@ -1959,6 +1983,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 +2003,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 utility_fn = std::move(*utility_fn_or_error); @@ -2313,10 +2347,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 +2369,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 +2392,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 guard(m_mutex); // Fill in our function argument values @@ -2362,12 +2411,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,78 +2444,80 @@ 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) { + } else if (log) { LLDB_LOGF(log, "Error evaluating our find class name function."); diagnostics.Dump(log); + break; } - } - } else { - if (log) { - LLDB_LOGF(log, "Error writing function arguments."); - diagnostics.Dump(log); - } + } while (num_class_infos_read == max_num_classes_in_buffer); + } else if (log) { + LLDB_LOGF(log, "Error writing function arguments."); + diagnostics.Dump(log); } - // 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);