Fix vdex fast-verify performance regression
Recent CL I0d06b82e31088c58d4493723a5435309740f1d0c generalized the
fast-verify class redefinition check by checking that all vdex-verified
classes resolve to dex files covered by the vdex and are not duplicates
of classes in parent class loaders. This introduced a performance and
allocated memory regression for dex2oat invoked with
compiler-filter=verify(-profile).
This patch removes the regression by acquiring a list of classpath dex
files from the compiler driver and boot classpath dex files from the
class linker, avoiding class resolution altogether.
A small performance overhead remains as previously only boot classpath
was being searched.
Test: run `dex2oat filter=interpret-only; dex2oat filter=verify`
compare time and allocated memory numbers before CL and after
Change-Id: Ifd690cdafdc99d3eafb9847d67775fc11a5b5023
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 54e94d0..33201cf 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1786,7 +1786,13 @@
hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
std::string error_msg;
- if (!verifier_deps->ValidateDependencies(class_loader, soa.Self(), &error_msg)) {
+ if (!verifier_deps->ValidateDependencies(
+ soa.Self(),
+ class_loader,
+ // This returns classpath dex files in no particular order but VerifierDeps
+ // does not care about the order.
+ classpath_classes_.GetDexFiles(),
+ &error_msg)) {
LOG(WARNING) << "Fast verification failed: " << error_msg;
return false;
}
diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h
index 9915498..377b7fe 100644
--- a/compiler/utils/atomic_dex_ref_map-inl.h
+++ b/compiler/utils/atomic_dex_ref_map-inl.h
@@ -134,6 +134,17 @@
}
}
+template <typename DexFileReferenceType, typename Value>
+inline std::vector<const DexFile*> AtomicDexRefMap<DexFileReferenceType, Value>::GetDexFiles()
+ const {
+ std::vector<const DexFile*> result;
+ result.reserve(arrays_.size());
+ for (auto& it : arrays_) {
+ result.push_back(it.first);
+ }
+ return result;
+}
+
} // namespace art
#endif // ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_INL_H_
diff --git a/compiler/utils/atomic_dex_ref_map.h b/compiler/utils/atomic_dex_ref_map.h
index fc24379..a8c285f 100644
--- a/compiler/utils/atomic_dex_ref_map.h
+++ b/compiler/utils/atomic_dex_ref_map.h
@@ -54,6 +54,9 @@
void AddDexFile(const DexFile* dex_file);
void AddDexFiles(const std::vector<const DexFile*>& dex_files);
+ // Return a vector of all dex files which were added to the map.
+ std::vector<const DexFile*> GetDexFiles() const;
+
bool HaveDexFile(const DexFile* dex_file) const {
return arrays_.find(dex_file) != arrays_.end();
}
diff --git a/compiler/utils/atomic_dex_ref_map_test.cc b/compiler/utils/atomic_dex_ref_map_test.cc
index 4e1ef12..864531e 100644
--- a/compiler/utils/atomic_dex_ref_map_test.cc
+++ b/compiler/utils/atomic_dex_ref_map_test.cc
@@ -41,6 +41,9 @@
EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), 0, 1) == Map::kInsertResultInvalidDexFile);
map.AddDexFile(dex.get());
EXPECT_TRUE(map.HaveDexFile(dex.get()));
+ std::vector<const DexFile*> registered_dex_files = map.GetDexFiles();
+ EXPECT_EQ(1u, registered_dex_files.size());
+ EXPECT_TRUE(registered_dex_files[0] == dex.get());
EXPECT_GT(dex->NumMethodIds(), 10u);
// After we have added the get should succeed but return the default value.
EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value));
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 9b51713..bdffda6 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -29,6 +29,7 @@
#include "dex/dex_file-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
+#include "oat_file.h"
#include "obj_ptr-inl.h"
#include "runtime.h"
@@ -885,11 +886,12 @@
}
}
-bool VerifierDeps::ValidateDependencies(Handle<mirror::ClassLoader> class_loader,
- Thread* self,
+bool VerifierDeps::ValidateDependencies(Thread* self,
+ Handle<mirror::ClassLoader> class_loader,
+ const std::vector<const DexFile*>& classpath,
/* out */ std::string* error_msg) const {
for (const auto& entry : dex_deps_) {
- if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self, error_msg)) {
+ if (!VerifyDexFile(class_loader, *entry.first, *entry.second, classpath, self, error_msg)) {
return false;
}
}
@@ -1113,23 +1115,34 @@
return true;
}
-bool VerifierDeps::VerifyInternalClasses(Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
+bool VerifierDeps::IsInDexFiles(const char* descriptor,
+ size_t hash,
+ const std::vector<const DexFile*>& dex_files,
+ /* out */ const DexFile** out_dex_file) const {
+ for (const DexFile* dex_file : dex_files) {
+ if (OatDexFile::FindClassDef(*dex_file, descriptor, hash) != nullptr) {
+ *out_dex_file = dex_file;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VerifierDeps::VerifyInternalClasses(const DexFile& dex_file,
+ const std::vector<const DexFile*>& classpath,
const std::vector<bool>& verified_classes,
const std::vector<bool>& redefined_classes,
- Thread* self,
/* out */ std::string* error_msg) const {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ const std::vector<const DexFile*>& boot_classpath =
+ Runtime::Current()->GetClassLinker()->GetBootClassPath();
for (ClassAccessor accessor : dex_file.GetClasses()) {
- const std::string descriptor = accessor.GetDescriptor();
- const uint16_t class_def_index = accessor.GetClassDefIndex();
- const bool verified = verified_classes[class_def_index];
- const bool redefined = redefined_classes[class_def_index];
+ const char* descriptor = accessor.GetDescriptor();
- if (redefined) {
- if (verified) {
- *error_msg = "Class " + descriptor + " marked both verified and redefined";
+ const uint16_t class_def_index = accessor.GetClassDefIndex();
+ if (redefined_classes[class_def_index]) {
+ if (verified_classes[class_def_index]) {
+ *error_msg = std::string("Class ") + descriptor + " marked both verified and redefined";
return false;
}
@@ -1137,26 +1150,17 @@
continue;
}
- ObjPtr<mirror::Class> cls =
- FindClassAndClearException(class_linker, self, descriptor, class_loader);
- if (UNLIKELY(cls == nullptr)) {
- // Could not resolve class from the currently verified dex file.
- // This can happen when the class fails to link. Check if this
- // expected by looking in the `verified_classes` bit vector.
- if (verified) {
- *error_msg = "Failed to resolve internal class " + descriptor;
- return false;
- }
- continue;
- }
-
// Check that the class resolved into the same dex file. Otherwise there is
// a different class with the same descriptor somewhere in one of the parent
// class loaders.
- if (&cls->GetDexFile() != &dex_file) {
- *error_msg = "Class " + descriptor + " redefines a class in the classpath "
- + "(dexFile expected=" + accessor.GetDexFile().GetLocation()
- + ", actual=" + cls->GetDexFile().GetLocation() + ")";
+ const size_t hash = ComputeModifiedUtf8Hash(descriptor);
+ const DexFile* cp_dex_file = nullptr;
+ if (IsInDexFiles(descriptor, hash, boot_classpath, &cp_dex_file) ||
+ IsInDexFiles(descriptor, hash, classpath, &cp_dex_file)) {
+ *error_msg = std::string("Class ") + descriptor
+ + " redefines a class in the classpath "
+ + "(dexFile expected=" + dex_file.GetLocation()
+ + ", actual=" + cp_dex_file->GetLocation() + ")";
return false;
}
}
@@ -1167,13 +1171,13 @@
bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFileDeps& deps,
+ const std::vector<const DexFile*>& classpath,
Thread* self,
/* out */ std::string* error_msg) const {
- return VerifyInternalClasses(class_loader,
- dex_file,
+ return VerifyInternalClasses(dex_file,
+ classpath,
deps.verified_classes_,
deps.redefined_classes_,
- self,
error_msg) &&
VerifyAssignability(class_loader,
dex_file,
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index d38b840..bf9cdc7 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -120,8 +120,9 @@
void Dump(VariableIndentationOutputStream* vios) const;
// Verify the encoded dependencies of this `VerifierDeps` are still valid.
- bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader,
- Thread* self,
+ bool ValidateDependencies(Thread* self,
+ Handle<mirror::ClassLoader> class_loader,
+ const std::vector<const DexFile*>& classpath,
/* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -310,10 +311,18 @@
bool VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFileDeps& deps,
+ const std::vector<const DexFile*>& classpath,
Thread* self,
/* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Iterates over `dex_files` and tries to find a class def matching `descriptor`.
+ // Returns true if such class def is found.
+ bool IsInDexFiles(const char* descriptor,
+ size_t hash,
+ const std::vector<const DexFile*>& dex_files,
+ /* out */ const DexFile** cp_dex_file) const;
+
// Check that classes which are to be verified using these dependencies
// are not eclipsed by classes in parent class loaders, e.g. when vdex was
// created against SDK stubs and the app redefines a non-public class on
@@ -321,11 +330,10 @@
// dependencies do not include the dependencies on the presumed-internal class
// and verification must fail unless the class was recorded to have been
// redefined during dependencies' generation too.
- bool VerifyInternalClasses(Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
+ bool VerifyInternalClasses(const DexFile& dex_file,
+ const std::vector<const DexFile*>& classpath,
const std::vector<bool>& verified_classes,
const std::vector<bool>& redefined_classes,
- Thread* self,
/* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);