From cc2f2393e69a9b1425bad1a89f41aaaf8c38f9e2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 29 Aug 2014 20:19:11 -0700 Subject: [PATCH] Reduce and speed-up class def searches. Use the class linker for descriptor lookups from the compile driver so that dex caches are populated. Reduce the scope of functions for scanning class paths to just the class linker where they are performed. If we see more than a threshold number of find class def misses on a dex file lazily compute an index, so that future lookups are constant time (part of the collection code is taken from https://android-review.googlesource.com/#/c/103865/3). Note that we take a lazy approach so that we don't serialize on loading dex files, this avoids the reason the index was removed in 8b2c0b9abc3f520495f4387ea040132ba85cae69. Remove an implicit and unnecessary std::string creation for PrintableString. Single threaded interpret-only dex2oat performance is improved by roughly 10%. Bug: 16853450 Change-Id: Icf72df76b0a4328f2a24075e81f4ff267b9401f4 (cherry picked from commit 68b56858367e29461ae290fd797443a1ef6d8005) --- compiler/driver/compiler_driver.cc | 180 ++++++++++++++----------------------- oatdump/oatdump.cc | 4 +- runtime/class_linker-inl.h | 5 -- runtime/class_linker.cc | 67 +++++++++----- runtime/class_linker.h | 4 +- runtime/dex_file.cc | 74 +++++++++------ runtime/dex_file.h | 31 ++++--- runtime/jdwp/jdwp_handler.cc | 2 +- runtime/utf.cc | 8 ++ runtime/utf.h | 3 + runtime/utils.cc | 4 +- runtime/utils.h | 2 +- 12 files changed, 197 insertions(+), 187 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c1610c25a..91c7aec56 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1457,51 +1457,6 @@ class ParallelCompilationManager { DISALLOW_COPY_AND_ASSIGN(ParallelCompilationManager); }; -static bool SkipClassCheckClassPath(const char* descriptor, const DexFile& dex_file, - const std::vector& classpath) { - DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, classpath); - CHECK(pair.second != NULL); - if (pair.first != &dex_file) { - LOG(WARNING) << "Skipping class " << descriptor << " from " << dex_file.GetLocation() - << " previously found in " << pair.first->GetLocation(); - return true; - } - return false; -} - -// Return true if the class should be skipped during compilation. -// -// The first case where we skip is for redundant class definitions in -// the boot classpath. We skip all but the first definition in that case. -// -// The second case where we skip is when an app bundles classes found -// in the boot classpath. Since at runtime we will select the class from -// the boot classpath, we ignore the one from the app. -// -// The third case is if the app itself has the class defined in multiple dex files. Then we skip -// it if it is not the first occurrence. -static bool SkipClass(ClassLinker* class_linker, jobject class_loader, const DexFile& dex_file, - const std::vector& dex_files, - const DexFile::ClassDef& class_def) { - const char* descriptor = dex_file.GetClassDescriptor(class_def); - - if (class_loader == NULL) { - return SkipClassCheckClassPath(descriptor, dex_file, class_linker->GetBootClassPath()); - } - - if (class_linker->IsInBootClassPath(descriptor)) { - return true; - } - - if (dex_files.size() > 1) { - // Multi-dex compilation, only take first class. - return SkipClassCheckClassPath(descriptor, dex_file, dex_files); - } else { - // Single dex, take everything. - return false; - } -} - // A fast version of SkipClass above if the class pointer is available // that avoids the expensive FindInClassPath search. static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Class* klass) @@ -1568,81 +1523,84 @@ static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manag // definitions, since many of them many never be referenced by // generated code. const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - if (!SkipClass(class_linker, jclass_loader, dex_file, manager->GetDexFiles(), class_def)) { - ScopedObjectAccess soa(self); - StackHandleScope<2> hs(soa.Self()); - Handle class_loader( - hs.NewHandle(soa.Decode(jclass_loader))); - Handle dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file))); - // Resolve the class. - mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, - class_loader); - bool resolve_fields_and_methods; - if (klass == NULL) { - // Class couldn't be resolved, for example, super-class is in a different dex file. Don't - // attempt to resolve methods and fields when there is no declaring class. - CheckAndClearResolveException(soa.Self()); - resolve_fields_and_methods = false; - } else { - resolve_fields_and_methods = manager->GetCompiler()->IsImage(); + ScopedObjectAccess soa(self); + StackHandleScope<2> hs(soa.Self()); + Handle class_loader( + hs.NewHandle(soa.Decode(jclass_loader))); + Handle dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file))); + // Resolve the class. + mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, + class_loader); + bool resolve_fields_and_methods; + if (klass == nullptr) { + // Class couldn't be resolved, for example, super-class is in a different dex file. Don't + // attempt to resolve methods and fields when there is no declaring class. + CheckAndClearResolveException(soa.Self()); + resolve_fields_and_methods = false; + } else { + // We successfully resolved a class, should we skip it? + if (SkipClass(jclass_loader, dex_file, klass)) { + return; } - // Note the class_data pointer advances through the headers, - // static fields, instance fields, direct methods, and virtual - // methods. - const byte* class_data = dex_file.GetClassData(class_def); - if (class_data == NULL) { - // Empty class such as a marker interface. - requires_constructor_barrier = false; - } else { - ClassDataItemIterator it(dex_file, class_data); - while (it.HasNextStaticField()) { - if (resolve_fields_and_methods) { - mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), - dex_cache, class_loader, true); - if (field == NULL) { - CheckAndClearResolveException(soa.Self()); - } + // We want to resolve the methods and fields eagerly. + resolve_fields_and_methods = true; + } + // Note the class_data pointer advances through the headers, + // static fields, instance fields, direct methods, and virtual + // methods. + const byte* class_data = dex_file.GetClassData(class_def); + if (class_data == NULL) { + // Empty class such as a marker interface. + requires_constructor_barrier = false; + } else { + ClassDataItemIterator it(dex_file, class_data); + while (it.HasNextStaticField()) { + if (resolve_fields_and_methods) { + mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), + dex_cache, class_loader, true); + if (field == NULL) { + CheckAndClearResolveException(soa.Self()); } - it.Next(); } - // We require a constructor barrier if there are final instance fields. - requires_constructor_barrier = false; - while (it.HasNextInstanceField()) { - if ((it.GetMemberAccessFlags() & kAccFinal) != 0) { - requires_constructor_barrier = true; + it.Next(); + } + // We require a constructor barrier if there are final instance fields. + requires_constructor_barrier = false; + while (it.HasNextInstanceField()) { + if ((it.GetMemberAccessFlags() & kAccFinal) != 0) { + requires_constructor_barrier = true; + } + if (resolve_fields_and_methods) { + mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), + dex_cache, class_loader, false); + if (field == NULL) { + CheckAndClearResolveException(soa.Self()); } - if (resolve_fields_and_methods) { - mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), - dex_cache, class_loader, false); - if (field == NULL) { - CheckAndClearResolveException(soa.Self()); - } + } + it.Next(); + } + if (resolve_fields_and_methods) { + while (it.HasNextDirectMethod()) { + mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), + dex_cache, class_loader, + NullHandle(), + it.GetMethodInvokeType(class_def)); + if (method == NULL) { + CheckAndClearResolveException(soa.Self()); } it.Next(); } - if (resolve_fields_and_methods) { - while (it.HasNextDirectMethod()) { - mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), - dex_cache, class_loader, - NullHandle(), - it.GetMethodInvokeType(class_def)); - if (method == NULL) { - CheckAndClearResolveException(soa.Self()); - } - it.Next(); + while (it.HasNextVirtualMethod()) { + mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), + dex_cache, class_loader, + NullHandle(), + it.GetMethodInvokeType(class_def)); + if (method == NULL) { + CheckAndClearResolveException(soa.Self()); } - while (it.HasNextVirtualMethod()) { - mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), - dex_cache, class_loader, - NullHandle(), - it.GetMethodInvokeType(class_def)); - if (method == NULL) { - CheckAndClearResolveException(soa.Self()); - } - it.Next(); - } - DCHECK(!it.HasNext()); + it.Next(); } + DCHECK(!it.HasNext()); } } if (requires_constructor_barrier) { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 75bc49b7d..c1fb267ba 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -916,7 +916,7 @@ class ImageDumper { } else if (type->IsStringClass()) { mirror::String* string = value->AsString(); os << StringPrintf("%p String: %s\n", string, - PrintableString(string->ToModifiedUtf8()).c_str()); + PrintableString(string->ToModifiedUtf8().c_str()).c_str()); } else if (type->IsClassClass()) { mirror::Class* klass = value->AsClass(); os << StringPrintf("%p Class: %s\n", klass, PrettyDescriptor(klass).c_str()); @@ -1049,7 +1049,7 @@ class ImageDumper { PrettyMethod(obj->AsArtMethod()).c_str()); } else if (obj_class->IsStringClass()) { os << StringPrintf("%p: java.lang.String %s\n", obj, - PrintableString(obj->AsString()->ToModifiedUtf8()).c_str()); + PrintableString(obj->AsString()->ToModifiedUtf8().c_str()).c_str()); } else { os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str()); } diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 3af90b2b8..d05f7af87 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -29,11 +29,6 @@ namespace art { -inline bool ClassLinker::IsInBootClassPath(const char* descriptor) { - DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_); - return pair.second != nullptr; -} - inline mirror::Class* ClassLinker::FindSystemClass(Thread* self, const char* descriptor) { return FindClass(self, descriptor, NullHandle()); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 51ee448ff..453ee000a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1899,6 +1899,23 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor, return klass; } +typedef std::pair ClassPathEntry; + +// Search a collection of DexFiles for a descriptor +ClassPathEntry FindInClassPath(const char* descriptor, + const std::vector& class_path) { + for (size_t i = 0; i != class_path.size(); ++i) { + const DexFile* dex_file = class_path[i]; + const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor); + if (dex_class_def != NULL) { + return ClassPathEntry(dex_file, dex_class_def); + } + } + // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved + return ClassPathEntry(reinterpret_cast(NULL), + reinterpret_cast(NULL)); +} + mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, Handle class_loader) { DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; @@ -1911,25 +1928,31 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, } // Find the class in the loaded classes table. mirror::Class* klass = LookupClass(descriptor, class_loader.Get()); - if (klass != NULL) { + if (klass != nullptr) { return EnsureResolved(self, descriptor, klass); } // Class is not yet loaded. if (descriptor[0] == '[') { return CreateArrayClass(self, descriptor, class_loader); } else if (class_loader.Get() == nullptr) { - DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_); - if (pair.second != NULL) { + ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_); + if (pair.second != nullptr) { StackHandleScope<1> hs(self); return DefineClass(descriptor, NullHandle(), *pair.first, *pair.second); } } else if (Runtime::Current()->UseCompileTimeClassPath()) { - // First try the boot class path, we check the descriptor first to avoid an unnecessary - // throw of a NoClassDefFoundError. - if (IsInBootClassPath(descriptor)) { - mirror::Class* system_class = FindSystemClass(self, descriptor); - CHECK(system_class != NULL); - return system_class; + // First try with the bootstrap class loader. + if (class_loader.Get() != nullptr) { + klass = LookupClass(descriptor, nullptr); + if (klass != nullptr) { + return EnsureResolved(self, descriptor, klass); + } + } + // If the lookup failed search the boot class path. We don't perform a recursive call to avoid a NoClassDefFoundError being allocated. + ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_); + if (pair.second != nullptr) { + StackHandleScope<1> hs(self); + return DefineClass(descriptor, NullHandle(), *pair.first, *pair.second); } // Next try the compile time class path. const std::vector* class_path; @@ -1939,38 +1962,36 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, soa.AddLocalReference(class_loader.Get())); class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get()); } - - DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, *class_path); - if (pair.second != NULL) { + pair = FindInClassPath(descriptor, *class_path); + if (pair.second != nullptr) { return DefineClass(descriptor, class_loader, *pair.first, *pair.second); } - } else { ScopedObjectAccessUnchecked soa(self); ScopedLocalRef class_loader_object(soa.Env(), soa.AddLocalReference(class_loader.Get())); std::string class_name_string(DescriptorToDot(descriptor)); - ScopedLocalRef result(soa.Env(), NULL); + ScopedLocalRef result(soa.Env(), nullptr); { ScopedThreadStateChange tsc(self, kNative); ScopedLocalRef class_name_object(soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str())); - if (class_name_object.get() == NULL) { - return NULL; + if (class_name_object.get() == nullptr) { + return nullptr; } - CHECK(class_loader_object.get() != NULL); + CHECK(class_loader_object.get() != nullptr); result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(), WellKnownClasses::java_lang_ClassLoader_loadClass, class_name_object.get())); } if (self->IsExceptionPending()) { // If the ClassLoader threw, pass that exception up. - return NULL; - } else if (result.get() == NULL) { + return nullptr; + } else if (result.get() == nullptr) { // broken loader - throw NPE to be compatible with Dalvik - ThrowNullPointerException(NULL, StringPrintf("ClassLoader.loadClass returned null for %s", + ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s", class_name_string.c_str()).c_str()); - return NULL; + return nullptr; } else { // success, return mirror::Class* return soa.Decode(result.get()); @@ -1978,7 +1999,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, } ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str()); - return NULL; + return nullptr; } mirror::Class* ClassLinker::DefineClass(const char* descriptor, @@ -3084,7 +3105,7 @@ mirror::Class* ClassLinker::LookupClass(const char* descriptor, // Searching the image dex files/caches failed, we don't want to get into this situation // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image // classes into the class table. - const int32_t kMaxFailedDexCacheLookups = 1000; + constexpr uint32_t kMaxFailedDexCacheLookups = 1000; if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) { MoveImageClassesToClassTable(); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index bf2e78116..fbfb8fafd 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -68,8 +68,6 @@ class ClassLinker { // Initialize class linker from one or more images. void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsInBootClassPath(const char* descriptor); - // Finds a class by its descriptor, loading it if necessary. // If class_loader is null, searches boot_class_path_. mirror::Class* FindClass(Thread* self, const char* descriptor, @@ -638,7 +636,7 @@ class ClassLinker { bool dex_cache_image_class_lookup_required_; // Number of times we've searched dex caches for a class. After a certain number of misses we move // the classes into the class_table_ to avoid dex cache based searches. - AtomicInteger failed_dex_cache_class_lookups_; + Atomic failed_dex_cache_class_lookups_; mirror::Class* LookupClassFromTableLocked(const char* descriptor, const mirror::ClassLoader* class_loader, diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index ddc2147e7..38ea75438 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -50,20 +50,6 @@ namespace art { const byte DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' }; const byte DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' }; -DexFile::ClassPathEntry DexFile::FindInClassPath(const char* descriptor, - const ClassPath& class_path) { - for (size_t i = 0; i != class_path.size(); ++i) { - const DexFile* dex_file = class_path[i]; - const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor); - if (dex_class_def != NULL) { - return ClassPathEntry(dex_file, dex_class_def); - } - } - // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved - return ClassPathEntry(reinterpret_cast(NULL), - reinterpret_cast(NULL)); -} - static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) { CHECK(magic != NULL); ScopedFd fd(open(filename, O_RDONLY, 0)); @@ -366,7 +352,10 @@ DexFile::DexFile(const byte* base, size_t size, field_ids_(reinterpret_cast(base + header_->field_ids_off_)), method_ids_(reinterpret_cast(base + header_->method_ids_off_)), proto_ids_(reinterpret_cast(base + header_->proto_ids_off_)), - class_defs_(reinterpret_cast(base + header_->class_defs_off_)) { + class_defs_(reinterpret_cast(base + header_->class_defs_off_)), + find_class_def_misses_(0), + class_def_index_(nullptr), + build_class_def_index_mutex_("DexFile index creation mutex") { CHECK(begin_ != NULL) << GetLocation(); CHECK_GT(size_, 0U) << GetLocation(); } @@ -376,6 +365,8 @@ DexFile::~DexFile() { // that's only called after DetachCurrentThread, which means there's no JNIEnv. We could // re-attach, but cleaning up these global references is not obviously useful. It's not as if // the global reference table is otherwise empty! + // Remove the index if one were created. + delete class_def_index_.LoadRelaxed(); } bool DexFile::Init(std::string* error_msg) { @@ -424,26 +415,51 @@ uint32_t DexFile::GetVersion() const { } const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor) const { - size_t num_class_defs = NumClassDefs(); + // If we have an index lookup the descriptor via that as its constant time to search. + Index* index = class_def_index_.LoadSequentiallyConsistent(); + if (index != nullptr) { + auto it = index->find(descriptor); + return (it == index->end()) ? nullptr : it->second; + } + // Fast path for rate no class defs case. + uint32_t num_class_defs = NumClassDefs(); if (num_class_defs == 0) { - return NULL; + return nullptr; } + // Search for class def with 2 binary searches and then a linear search. const StringId* string_id = FindStringId(descriptor); - if (string_id == NULL) { - return NULL; - } - const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id)); - if (type_id == NULL) { - return NULL; + if (string_id != nullptr) { + const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id)); + if (type_id != nullptr) { + uint16_t type_idx = GetIndexForTypeId(*type_id); + for (size_t i = 0; i < num_class_defs; ++i) { + const ClassDef& class_def = GetClassDef(i); + if (class_def.class_idx_ == type_idx) { + return &class_def; + } + } + } } - uint16_t type_idx = GetIndexForTypeId(*type_id); - for (size_t i = 0; i < num_class_defs; ++i) { - const ClassDef& class_def = GetClassDef(i); - if (class_def.class_idx_ == type_idx) { - return &class_def; + // A miss. If we've had kMaxFailedDexClassDefLookups misses then build an index to speed things + // up. This isn't done eagerly at construction as construction is not performed in multi-threaded + // sections of tools like dex2oat. If we're lazy we hopefully increase the chance of balancing + // out which thread builds the index. + find_class_def_misses_++; + const uint32_t kMaxFailedDexClassDefLookups = 100; + if (find_class_def_misses_ > kMaxFailedDexClassDefLookups) { + MutexLock mu(Thread::Current(), build_class_def_index_mutex_); + // Are we the first ones building the index? + if (class_def_index_.LoadSequentiallyConsistent() == nullptr) { + index = new Index(num_class_defs); + for (uint32_t i = 0; i < num_class_defs; ++i) { + const ClassDef& class_def = GetClassDef(i); + const char* descriptor = GetClassDescriptor(class_def); + index->insert(std::make_pair(descriptor, &class_def)); + } + class_def_index_.StoreSequentiallyConsistent(index); } } - return NULL; + return nullptr; } const DexFile::ClassDef* DexFile::FindClassDef(uint16_t type_idx) const { diff --git a/runtime/dex_file.h b/runtime/dex_file.h index a71a5e1aa..1da1f8139 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "base/logging.h" @@ -27,7 +28,7 @@ #include "invoke_type.h" #include "jni.h" #include "modifiers.h" -#include "safe_map.h" +#include "utf.h" namespace art { @@ -356,13 +357,6 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(AnnotationItem); }; - typedef std::pair ClassPathEntry; - typedef std::vector ClassPath; - - // Search a collection of DexFiles for a descriptor - static ClassPathEntry FindInClassPath(const char* descriptor, - const ClassPath& class_path); - // Returns the checksum of a file for comparison with GetLocationChecksum(). // For .dex files, this is the header checksum. // For zip files, this is the classes.dex zip entry CRC32 checksum. @@ -478,7 +472,7 @@ class DexFile { const StringId* FindStringId(const uint16_t* string) const; // Returns the number of type identifiers in the .dex file. - size_t NumTypeIds() const { + uint32_t NumTypeIds() const { DCHECK(header_ != NULL) << GetLocation(); return header_->type_ids_size_; } @@ -607,7 +601,7 @@ class DexFile { return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length); } // Returns the number of class definitions in the .dex file. - size_t NumClassDefs() const { + uint32_t NumClassDefs() const { DCHECK(header_ != NULL) << GetLocation(); return header_->class_defs_size_; } @@ -969,6 +963,23 @@ class DexFile { // Points to the base of the class definition list. const ClassDef* const class_defs_; + + // Number of misses finding a class def from a descriptor. + mutable Atomic find_class_def_misses_; + + struct UTF16HashCmp { + // Hash function. + size_t operator()(const char* key) const { + return ComputeUtf8Hash(key); + } + // std::equal function. + bool operator()(const char* a, const char* b) const { + return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(a, b) == 0; + } + }; + typedef std::unordered_map Index; + mutable Atomic class_def_index_; + mutable Mutex build_class_def_index_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER; }; std::ostream& operator<<(std::ostream& os, const DexFile& dex_file); diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index b9379f5e7..330d235c0 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -921,7 +921,7 @@ static JdwpError SR_Value(JdwpState*, Request& request, ExpandBuf* pReply) ObjectId stringObject = request.ReadObjectId(); std::string str(Dbg::StringToUtf8(stringObject)); - VLOG(jdwp) << StringPrintf(" --> %s", PrintableString(str).c_str()); + VLOG(jdwp) << StringPrintf(" --> %s", PrintableString(str.c_str()).c_str()); expandBufAddUtf8String(pReply, str); diff --git a/runtime/utf.cc b/runtime/utf.cc index e48d6d209..02cbe3bc0 100644 --- a/runtime/utf.cc +++ b/runtime/utf.cc @@ -85,6 +85,14 @@ int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count) { return hash; } +int32_t ComputeUtf8Hash(const char* chars) { + int32_t hash = 0; + while (*chars != '\0') { + hash = hash * 31 + GetUtf16FromUtf8(&chars); + } + return hash; +} + int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) { for (;;) { if (*utf8_1 == '\0') { diff --git a/runtime/utf.h b/runtime/utf.h index 63cdbdc19..a09db9d54 100644 --- a/runtime/utf.h +++ b/runtime/utf.h @@ -79,6 +79,9 @@ int32_t ComputeUtf16Hash(mirror::CharArray* chars, int32_t offset, size_t char_c SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count); +// Compute a hash code of a UTF-8 string. +int32_t ComputeUtf8Hash(const char* chars); + /* * Retrieve the next UTF-16 character from a UTF-8 string. * diff --git a/runtime/utils.cc b/runtime/utils.cc index d704eec9a..c4e84f347 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -568,10 +568,10 @@ std::string PrintableChar(uint16_t ch) { return result; } -std::string PrintableString(const std::string& utf) { +std::string PrintableString(const char* utf) { std::string result; result += '"'; - const char* p = utf.c_str(); + const char* p = utf; size_t char_count = CountModifiedUtf8Chars(p); for (size_t i = 0; i < char_count; ++i) { uint16_t ch = GetUtf16FromUtf8(&p); diff --git a/runtime/utils.h b/runtime/utils.h index c4a58950c..a34a01d93 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -261,7 +261,7 @@ std::string PrintableChar(uint16_t ch); // Returns an ASCII string corresponding to the given UTF-8 string. // Java escapes are used for non-ASCII characters. -std::string PrintableString(const std::string& utf8); +std::string PrintableString(const char* utf8); // Tests whether 's' starts with 'prefix'. bool StartsWith(const std::string& s, const char* prefix); -- 2.11.0