From 0e197bcb6b824f51a0b7a9868f3df3760656ae79 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Wed, 12 Jun 2019 21:44:53 +0000 Subject: [PATCH] Re-land r363103 ("When reading ObjC class table, use new SPI if it is avail") with a call to snprintf() to find the size of the formatted string, malloc memory, then snprintf again to format it into the buffer, instead of calling asprintf. Orig commit msg: When reading ObjC class table, use new SPI if it is avail In the latest OS betas, the objc runtime has a special interface for the debugger, class_getNameRaw(), instead of the existing class_getName(), which will return class names in their raw, unmangled (in the case of swift) form. When lldb can access the unmangled names of classes, it won't need to fetch them out of the inferior process after we run our "get the objc class table" expression. If the new interface is absent (debugging a process on an older target), lldb will fall back to class_getName and reading any class names that it got back in demangled form, at a bit of a performance cost on the first expression. llvm-svn: 363206 --- .../AppleObjCRuntime/AppleObjCRuntimeV2.cpp | 60 +++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 26fd593029af..64dab7e41ad5 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -157,6 +157,16 @@ __lldb_apple_objc_v2_get_dynamic_class_info (void *gdb_objc_realized_classes_ptr )"; +// We'll substitute in class_getName or class_getNameRaw depending +// on which is present. +static const char *g_shared_cache_class_name_funcptr = R"( +extern "C" +{ + const char *%s(void *objc_class); + const char *(*class_name_lookup_func)(void *) = %s; +} +)"; + static const char *g_get_shared_cache_class_info_name = "__lldb_apple_objc_v2_get_shared_cache_class_info"; // Testing using the new C++11 raw string literals. If this breaks GCC then we @@ -165,7 +175,6 @@ static const char *g_get_shared_cache_class_info_body = R"( extern "C" { - const char *class_getName(void *objc_class); size_t strlen(const char *); char *strncpy (char * s1, const char * s2, size_t n); int printf(const char * format, ...); @@ -286,7 +295,7 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, if (class_infos && idx < max_class_infos) { class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset); - const char *name = class_getName (class_infos[idx].isa); + 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; @@ -329,7 +338,7 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, if (class_infos && idx < max_class_infos) { class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset); - const char *name = class_getName (class_infos[idx].isa); + 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; @@ -1589,9 +1598,52 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() { if (!m_get_shared_cache_class_info_code) { Status error; + + // If the inferior objc.dylib has the class_getNameRaw function, + // use that in our jitted expression. Else fall back to the old + // class_getName. + static ConstString g_class_getName_symbol_name("class_getName"); + static ConstString g_class_getNameRaw_symbol_name("class_getNameRaw"); + ConstString class_name_getter_function_name = g_class_getName_symbol_name; + + ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process); + if (objc_runtime) { + const ModuleList &images = process->GetTarget().GetImages(); + std::lock_guard guard(images.GetMutex()); + for (size_t i = 0; i < images.GetSize(); ++i) { + lldb::ModuleSP mod_sp = images.GetModuleAtIndexUnlocked(i); + if (objc_runtime->IsModuleObjCLibrary(mod_sp)) { + const Symbol *symbol = + mod_sp->FindFirstSymbolWithNameAndType(g_class_getNameRaw_symbol_name, + lldb::eSymbolTypeCode); + if (symbol && + (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())) { + class_name_getter_function_name = g_class_getNameRaw_symbol_name; + } + } + } + } + + // Substitute in the correct class_getName / class_getNameRaw function name, + // concatenate the two parts of our expression text. The format string + // has two %s's, so provide the name twice. + int prefix_string_size = snprintf (nullptr, 0, + g_shared_cache_class_name_funcptr, + class_name_getter_function_name.AsCString(), + class_name_getter_function_name.AsCString()); + + char *class_name_func_ptr_expr = (char*) malloc (prefix_string_size + 1); + snprintf (class_name_func_ptr_expr, prefix_string_size + 1, + g_shared_cache_class_name_funcptr, + class_name_getter_function_name.AsCString(), + class_name_getter_function_name.AsCString()); + std::string shared_class_expression = class_name_func_ptr_expr; + shared_class_expression += g_get_shared_cache_class_info_body; + free (class_name_func_ptr_expr); + m_get_shared_cache_class_info_code.reset( GetTargetRef().GetUtilityFunctionForLanguage( - g_get_shared_cache_class_info_body, eLanguageTypeObjC, + shared_class_expression.c_str(), eLanguageTypeObjC, g_get_shared_cache_class_info_name, error)); if (error.Fail()) { if (log)