summaryrefslogtreecommitdiff
path: root/runtime/class_linker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r--runtime/class_linker.cc221
1 files changed, 124 insertions, 97 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b8ed530981..edd6e3b522 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <deque>
#include <iostream>
+#include <map>
#include <memory>
#include <queue>
#include <string>
@@ -1340,7 +1341,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
// The image space is not yet added to the heap, avoid read barriers.
ObjPtr<mirror::Class> klass = types[j].Read();
if (space->HasAddress(klass.Ptr())) {
- DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
auto it = new_class_set->Find(ClassTable::TableSlot(klass));
DCHECK(it != new_class_set->end());
DCHECK_EQ(it->Read(), klass);
@@ -1703,7 +1704,7 @@ bool ClassLinker::AddImageSpace(
for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
ObjPtr<mirror::Class> klass = types[j].Read();
if (klass != nullptr) {
- DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
}
}
} else {
@@ -2232,7 +2233,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
// For temporary classes we must wait for them to be retired.
if (init_done_ && klass->IsTemp()) {
CHECK(!klass->IsResolved());
- if (klass->IsErroneous()) {
+ if (klass->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
@@ -2240,10 +2241,10 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
Handle<mirror::Class> h_class(hs.NewHandle(klass));
ObjectLock<mirror::Class> lock(self, h_class);
// Loop and wait for the resolving thread to retire this class.
- while (!h_class->IsRetired() && !h_class->IsErroneous()) {
+ while (!h_class->IsRetired() && !h_class->IsErroneousUnresolved()) {
lock.WaitIgnoringInterrupts();
}
- if (h_class->IsErroneous()) {
+ if (h_class->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(h_class.Get());
return nullptr;
}
@@ -2258,7 +2259,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
static const size_t kNumYieldIterations = 1000;
// How long each sleep is in us.
static const size_t kSleepDurationUS = 1000; // 1 ms.
- while (!klass->IsResolved() && !klass->IsErroneous()) {
+ while (!klass->IsResolved() && !klass->IsErroneousUnresolved()) {
StackHandleScope<1> hs(self);
HandleWrapperObjPtr<mirror::Class> h_class(hs.NewHandleWrapper(&klass));
{
@@ -2269,7 +2270,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
// Check for circular dependencies between classes, the lock is required for SetStatus.
if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) {
ThrowClassCircularityError(h_class.Get());
- mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(h_class, mirror::Class::kStatusErrorUnresolved, self);
return nullptr;
}
}
@@ -2286,7 +2287,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self,
++index;
}
- if (klass->IsErroneous()) {
+ if (klass->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
@@ -2649,6 +2650,10 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
dex_class_def,
&new_dex_file,
&new_class_def);
+ // Check to see if an exception happened during runtime callbacks. Return if so.
+ if (self->IsExceptionPending()) {
+ return nullptr;
+ }
ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get());
if (dex_cache == nullptr) {
self->AssertPendingOOMException();
@@ -2687,7 +2692,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
@@ -2697,7 +2702,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
@@ -2716,13 +2721,13 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
self->AssertNoPendingException();
CHECK(h_new_class.Get() != nullptr) << descriptor;
- CHECK(h_new_class->IsResolved()) << descriptor;
+ CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor;
// Instrumentation may have updated entrypoints for all methods of all
// classes. However it could not update methods of this class while we
@@ -2857,9 +2862,12 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void*
return true;
}
- if (runtime->IsFullyDeoptable()) {
- // We need to be able to deoptimize at any time so we should always just ignore precompiled
- // code and go to the interpreter assuming we don't already have jitted code.
+ if (runtime->IsJavaDebuggable()) {
+ // For simplicity, we ignore precompiled code and go to the interpreter
+ // assuming we don't already have jitted code.
+ // We could look at the oat file where `quick_code` is being defined,
+ // and check whether it's been compiled debuggable, but we decided to
+ // only rely on the JIT for debuggable apps.
jit::Jit* jit = Runtime::Current()->GetJit();
return (jit == nullptr) || !jit->GetCodeCache()->ContainsPc(quick_code);
}
@@ -2867,18 +2875,13 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void*
if (runtime->IsNativeDebuggable()) {
DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
// If we are doing native debugging, ignore application's AOT code,
- // since we want to JIT it with extra stackmaps for native debugging.
- // On the other hand, keep all AOT code from the boot image, since the
- // blocking JIT would results in non-negligible performance impact.
+ // since we want to JIT it (at first use) with extra stackmaps for native
+ // debugging. We keep however all AOT code from the boot image,
+ // since the JIT-at-first-use is blocking and would result in non-negligible
+ // startup performance impact.
return !runtime->GetHeap()->IsInBootImageOatFile(quick_code);
}
- if (Dbg::IsDebuggerActive()) {
- // Boot image classes may be AOT-compiled as non-debuggable.
- // This is not suitable for the Java debugger, so ignore the AOT code.
- return runtime->GetHeap()->IsInBootImageOatFile(quick_code);
- }
-
return false;
}
@@ -3817,7 +3820,7 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self,
}
// Need to grab the lock to change status.
ObjectLock<mirror::Class> super_lock(self, klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
@@ -3939,8 +3942,8 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status);
// If the oat file says the class had an error, re-run the verifier. That way we will get a
// precise error message. To ensure a rerun, test:
- // oat_file_class_status == mirror::Class::kStatusError => !preverified
- DCHECK(!(oat_file_class_status == mirror::Class::kStatusError) || !preverified);
+ // mirror::Class::IsErroneous(oat_file_class_status) => !preverified
+ DCHECK(!mirror::Class::IsErroneous(oat_file_class_status) || !preverified);
std::string error_msg;
verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
@@ -3998,7 +4001,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
<< " because: " << error_msg;
self->AssertNoPendingException();
ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
}
if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) {
// Class is verified so we don't need to do any access check on its methods.
@@ -4089,7 +4092,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file,
// at compile time).
return false;
}
- if (oat_file_class_status == mirror::Class::kStatusError) {
+ if (mirror::Class::IsErroneous(oat_file_class_status)) {
// Compile time verification failed with a hard error. This is caused by invalid instructions
// in the class. These errors are unrecoverable.
return false;
@@ -4248,7 +4251,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)));
if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
return nullptr;
}
}
@@ -4463,7 +4466,8 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
return false;
}
- CHECK(klass->IsResolved()) << klass->PrettyClass() << ": state=" << klass->GetStatus();
+ CHECK(klass->IsResolved() && !klass->IsErroneousResolved())
+ << klass->PrettyClass() << ": state=" << klass->GetStatus();
if (!klass->IsVerified()) {
VerifyClass(self, klass);
@@ -4498,7 +4502,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
// A separate thread could have moved us all the way to initialized. A "simple" example
// involves a subclass of the current class being initialized at the same time (which
// will implicitly initialize the superclass, if scheduled that way). b/28254258
- DCHECK_NE(mirror::Class::kStatusError, klass->GetStatus());
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
if (klass->IsInitialized()) {
return true;
}
@@ -4525,7 +4529,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
}
if (!ValidateSuperClassDescriptors(klass)) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
self->AllowThreadSuspension();
@@ -4561,7 +4565,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
<< (self->GetException() != nullptr ? self->GetException()->Dump() : "");
ObjectLock<mirror::Class> lock(self, klass);
// Initialization failed because the super-class is erroneous.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
}
@@ -4592,7 +4596,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
if (!iface_initialized) {
ObjectLock<mirror::Class> lock(self, klass);
// Initialization failed because one of our interfaces with default methods is erroneous.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
}
@@ -4665,7 +4669,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
if (self->IsExceptionPending()) {
WrapExceptionInInitializer(klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
success = false;
} else if (Runtime::Current()->IsTransactionAborted()) {
// The exception thrown when the transaction aborted has been caught and cleared
@@ -4674,7 +4678,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
<< mirror::Class::PrettyDescriptor(klass.Get())
<< " without exception while transaction was aborted: re-throw it now.";
Runtime::Current()->ThrowTransactionAbortError(self);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
success = false;
} else {
RuntimeStats* global_stats = Runtime::Current()->GetStats();
@@ -4758,7 +4762,7 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass,
// we were not using WaitIgnoringInterrupts), bail out.
if (self->IsExceptionPending()) {
WrapExceptionInInitializer(klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
// Spurious wakeup? Go back to waiting.
@@ -5169,7 +5173,7 @@ bool ClassLinker::LinkClass(Thread* self,
klass->SetIFieldsPtrUnchecked(nullptr);
if (UNLIKELY(h_new_class.Get() == nullptr)) {
self->AssertPendingOOMException();
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
return false;
}
@@ -7781,7 +7785,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
}
}
}
- DCHECK((resolved == nullptr) || resolved->IsResolved() || resolved->IsErroneous())
+ DCHECK((resolved == nullptr) || resolved->IsResolved())
<< resolved->PrettyDescriptor() << " " << resolved->GetStatus();
return resolved.Ptr();
}
@@ -8478,71 +8482,94 @@ void ClassLinker::CleanupClassLoaders() {
}
}
-std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_boot_classes) {
- ScopedTrace trace(__PRETTY_FUNCTION__);
- ScopedObjectAccess soa(Thread::Current());
- ScopedAssertNoThreadSuspension ants(__FUNCTION__);
- std::set<DexCacheResolvedClasses> ret;
- VLOG(class_linker) << "Collecting resolved classes";
- const uint64_t start_time = NanoTime();
- ReaderMutexLock mu(soa.Self(), *Locks::dex_lock_);
- // Loop through all the dex caches and inspect resolved classes.
- for (const ClassLinker::DexCacheData& data : GetDexCachesData()) {
- if (soa.Self()->IsJWeakCleared(data.weak_root)) {
- continue;
- }
- ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(data.weak_root);
- if (dex_cache == nullptr) {
- continue;
- }
- const DexFile* dex_file = dex_cache->GetDexFile();
- const std::string& location = dex_file->GetLocation();
- const size_t num_class_defs = dex_file->NumClassDefs();
- // Use the resolved types, this will miss array classes.
- const size_t num_types = dex_file->NumTypeIds();
- VLOG(class_linker) << "Collecting class profile for dex file " << location
- << " types=" << num_types << " class_defs=" << num_class_defs;
- DexCacheResolvedClasses resolved_classes(dex_file->GetLocation(),
- dex_file->GetBaseLocation(),
- dex_file->GetLocationChecksum());
- size_t num_resolved = 0;
- std::unordered_set<dex::TypeIndex> class_set;
- CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
- for (size_t i = 0; i < num_types; ++i) {
- ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(i));
- // Filter out null class loader since that is the boot class loader.
- if (klass == nullptr || (ignore_boot_classes && klass->GetClassLoader() == nullptr)) {
- continue;
- }
- ++num_resolved;
- DCHECK(!klass->IsProxyClass());
- if (!klass->IsResolved()) {
- DCHECK(klass->IsErroneous());
- continue;
+class GetResolvedClassesVisitor : public ClassVisitor {
+ public:
+ GetResolvedClassesVisitor(std::set<DexCacheResolvedClasses>* result, bool ignore_boot_classes)
+ : result_(result),
+ ignore_boot_classes_(ignore_boot_classes),
+ last_resolved_classes_(result->end()),
+ last_dex_file_(nullptr),
+ vlog_is_on_(VLOG_IS_ON(class_linker)),
+ extra_stats_(),
+ last_extra_stats_(extra_stats_.end()) { }
+
+ bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!klass->IsProxyClass() &&
+ !klass->IsArrayClass() &&
+ klass->IsResolved() &&
+ !klass->IsErroneousResolved() &&
+ (!ignore_boot_classes_ || klass->GetClassLoader() != nullptr)) {
+ const DexFile& dex_file = klass->GetDexFile();
+ if (&dex_file != last_dex_file_) {
+ last_dex_file_ = &dex_file;
+ DexCacheResolvedClasses resolved_classes(dex_file.GetLocation(),
+ dex_file.GetBaseLocation(),
+ dex_file.GetLocationChecksum());
+ last_resolved_classes_ = result_->find(resolved_classes);
+ if (last_resolved_classes_ == result_->end()) {
+ last_resolved_classes_ = result_->insert(resolved_classes).first;
+ }
}
- ObjPtr<mirror::DexCache> klass_dex_cache = klass->GetDexCache();
- if (klass_dex_cache == dex_cache) {
- DCHECK(klass->IsResolved());
- CHECK_LT(klass->GetDexClassDefIndex(), num_class_defs);
- class_set.insert(klass->GetDexTypeIndex());
+ bool added = last_resolved_classes_->AddClass(klass->GetDexTypeIndex());
+ if (UNLIKELY(vlog_is_on_) && added) {
+ const DexCacheResolvedClasses* resolved_classes = std::addressof(*last_resolved_classes_);
+ if (last_extra_stats_ == extra_stats_.end() ||
+ last_extra_stats_->first != resolved_classes) {
+ last_extra_stats_ = extra_stats_.find(resolved_classes);
+ if (last_extra_stats_ == extra_stats_.end()) {
+ last_extra_stats_ =
+ extra_stats_.emplace(resolved_classes, ExtraStats(dex_file.NumClassDefs())).first;
+ }
+ }
}
}
+ return true;
+ }
- if (!class_set.empty()) {
- auto it = ret.find(resolved_classes);
- if (it != ret.end()) {
- // Already have the key, union the class type indexes.
- it->AddClasses(class_set.begin(), class_set.end());
- } else {
- resolved_classes.AddClasses(class_set.begin(), class_set.end());
- ret.insert(resolved_classes);
+ void PrintStatistics() const {
+ if (vlog_is_on_) {
+ for (const DexCacheResolvedClasses& resolved_classes : *result_) {
+ auto it = extra_stats_.find(std::addressof(resolved_classes));
+ DCHECK(it != extra_stats_.end());
+ const ExtraStats& extra_stats = it->second;
+ LOG(INFO) << "Dex location " << resolved_classes.GetDexLocation()
+ << " has " << resolved_classes.GetClasses().size() << " / "
+ << extra_stats.number_of_class_defs_ << " resolved classes";
}
}
+ }
+
+ private:
+ struct ExtraStats {
+ explicit ExtraStats(uint32_t number_of_class_defs)
+ : number_of_class_defs_(number_of_class_defs) {}
+ uint32_t number_of_class_defs_;
+ };
+
+ std::set<DexCacheResolvedClasses>* result_;
+ bool ignore_boot_classes_;
+ std::set<DexCacheResolvedClasses>::iterator last_resolved_classes_;
+ const DexFile* last_dex_file_;
- VLOG(class_linker) << "Dex location " << location << " has " << num_resolved << " / "
- << num_class_defs << " resolved classes";
+ // Statistics.
+ bool vlog_is_on_;
+ std::map<const DexCacheResolvedClasses*, ExtraStats> extra_stats_;
+ std::map<const DexCacheResolvedClasses*, ExtraStats>::iterator last_extra_stats_;
+};
+
+std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_boot_classes) {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ ScopedObjectAccess soa(Thread::Current());
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ std::set<DexCacheResolvedClasses> ret;
+ VLOG(class_linker) << "Collecting resolved classes";
+ const uint64_t start_time = NanoTime();
+ GetResolvedClassesVisitor visitor(&ret, ignore_boot_classes);
+ VisitClasses(&visitor);
+ if (VLOG_IS_ON(class_linker)) {
+ visitor.PrintStatistics();
+ LOG(INFO) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time);
}
- VLOG(class_linker) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time);
return ret;
}