Merge "Fix for interpreter crash on new instance of class"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index a679ac2..e89a381 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -76,18 +76,6 @@
endif
#
-# Used to enable JIT
-#
-ART_JIT := false
-ifneq ($(wildcard art/JIT_ART),)
-$(info Enabling ART_JIT because of existence of art/JIT_ART)
-ART_JIT := true
-endif
-ifeq ($(WITH_ART_JIT), true)
-ART_JIT := true
-endif
-
-#
# Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS.
#
ART_DEFAULT_GC_TYPE ?= CMS
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index a592162..aab24eb 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1087,8 +1087,11 @@
uint32_t method_index = resolved_method->GetDexMethodIndex();
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(handles_->NewHandle(
+ resolved_method->GetDeclaringClass()->GetClassLoader()));
+
DexCompilationUnit dex_compilation_unit(
- caller_compilation_unit_.GetClassLoader(),
+ class_loader.ToJObject(),
class_linker,
callee_dex_file,
code_item,
diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h
index e2d4c24..68cacd5 100644
--- a/runtime/base/arena_containers.h
+++ b/runtime/base/arena_containers.h
@@ -20,6 +20,7 @@
#include <deque>
#include <queue>
#include <set>
+#include <stack>
#include <utility>
#include "arena_allocator.h"
@@ -54,6 +55,12 @@
using ArenaVector = dchecked_vector<T, ArenaAllocatorAdapter<T>>;
template <typename T, typename Comparator = std::less<T>>
+using ArenaPriorityQueue = std::priority_queue<T, ArenaVector<T>, Comparator>;
+
+template <typename T>
+using ArenaStdStack = std::stack<T, ArenaDeque<T>>;
+
+template <typename T, typename Comparator = std::less<T>>
using ArenaSet = std::set<T, Comparator, ArenaAllocatorAdapter<T>>;
template <typename K, typename V, typename Comparator = std::less<K>>
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f13fea0..9736573 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2200,10 +2200,14 @@
}
}
}
- if (index < kNumYieldIterations) {
- sched_yield();
- } else {
- usleep(kSleepDurationUS);
+ {
+ // Handle wrapper deals with klass moving.
+ ScopedThreadSuspension sts(self, kSuspended);
+ if (index < kNumYieldIterations) {
+ sched_yield();
+ } else {
+ usleep(kSleepDurationUS);
+ }
}
++index;
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 90446b0..0fdc869 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -47,6 +47,10 @@
// If kFilterModUnionCards then we attempt to filter cards that don't need to be dirty in the mod
// union table. Disabled since it does not seem to help the pause much.
static constexpr bool kFilterModUnionCards = kIsDebugBuild;
+// If kDisallowReadBarrierDuringScan is true then the GC aborts if there are any that occur during
+// ConcurrentCopying::Scan. May be used to diagnose possibly unnecessary read barriers.
+// Only enabled for kIsDebugBuild to avoid performance hit.
+static constexpr bool kDisallowReadBarrierDuringScan = kIsDebugBuild;
ConcurrentCopying::ConcurrentCopying(Heap* heap,
const std::string& name_prefix,
@@ -520,7 +524,7 @@
explicit ImmuneSpaceScanObjVisitor(ConcurrentCopying* cc)
: collector_(cc) {}
- ALWAYS_INLINE void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
if (obj->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) {
collector_->ScanImmuneObject(obj);
@@ -534,10 +538,6 @@
}
}
- static void Callback(mirror::Object* obj, void* arg) SHARED_REQUIRES(Locks::mutator_lock_) {
- reinterpret_cast<ImmuneSpaceScanObjVisitor*>(arg)->operator()(obj);
- }
-
private:
ConcurrentCopying* const collector_;
};
@@ -562,15 +562,10 @@
for (auto& space : immune_spaces_.GetSpaces()) {
DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
- accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
ImmuneSpaceScanObjVisitor visitor(this);
- if (table != nullptr) {
- table->VisitObjects(ImmuneSpaceScanObjVisitor::Callback, &visitor);
- } else {
- live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
- reinterpret_cast<uintptr_t>(space->Limit()),
- visitor);
- }
+ live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+ reinterpret_cast<uintptr_t>(space->Limit()),
+ visitor);
}
}
if (kUseBakerReadBarrier) {
@@ -1723,7 +1718,7 @@
// Scan ref fields of an object.
inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
- if (kIsDebugBuild) {
+ if (kDisallowReadBarrierDuringScan) {
// Avoid all read barriers during visit references to help performance.
Thread::Current()->ModifyDebugDisallowReadBarrier(1);
}
@@ -1733,7 +1728,7 @@
// Disable the read barrier for a performance reason.
to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
visitor, visitor);
- if (kIsDebugBuild) {
+ if (kDisallowReadBarrierDuringScan) {
Thread::Current()->ModifyDebugDisallowReadBarrier(-1);
}
}
@@ -1849,7 +1844,10 @@
ScopedGcGraysImmuneObjects scoped_gc_gray_immune_objects(this);
CHECK_ALIGNED(byte_size, kObjectAlignment);
memset(dummy_obj, 0, byte_size);
- mirror::Class* int_array_class = mirror::IntArray::GetArrayClass();
+ // Avoid going through read barrier for since kDisallowReadBarrierDuringScan may be enabled.
+ // Explicitly mark to make sure to get an object in the to-space.
+ mirror::Class* int_array_class = down_cast<mirror::Class*>(
+ Mark(mirror::IntArray::GetArrayClass<kWithoutReadBarrier>()));
CHECK(int_array_class != nullptr);
AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class);
size_t component_size = int_array_class->GetComponentSize<kWithoutReadBarrier>();
@@ -1859,9 +1857,9 @@
// An int array is too big. Use java.lang.Object.
mirror::Class* java_lang_Object = WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object);
AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object);
- CHECK_EQ(byte_size, java_lang_Object->GetObjectSize());
+ CHECK_EQ(byte_size, (java_lang_Object->GetObjectSize<kVerifyNone, kWithoutReadBarrier>()));
dummy_obj->SetClass(java_lang_Object);
- CHECK_EQ(byte_size, dummy_obj->SizeOf());
+ CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone, kWithoutReadBarrier>()));
} else {
// Use an int array.
dummy_obj->SetClass(int_array_class);
@@ -1884,14 +1882,16 @@
CHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment);
Thread* self = Thread::Current();
size_t min_object_size = RoundUp(sizeof(mirror::Object), space::RegionSpace::kAlignment);
- MutexLock mu(self, skipped_blocks_lock_);
- auto it = skipped_blocks_map_.lower_bound(alloc_size);
- if (it == skipped_blocks_map_.end()) {
- // Not found.
- return nullptr;
- }
+ size_t byte_size;
+ uint8_t* addr;
{
- size_t byte_size = it->first;
+ MutexLock mu(self, skipped_blocks_lock_);
+ auto it = skipped_blocks_map_.lower_bound(alloc_size);
+ if (it == skipped_blocks_map_.end()) {
+ // Not found.
+ return nullptr;
+ }
+ byte_size = it->first;
CHECK_GE(byte_size, alloc_size);
if (byte_size > alloc_size && byte_size - alloc_size < min_object_size) {
// If remainder would be too small for a dummy object, retry with a larger request size.
@@ -1904,27 +1904,33 @@
CHECK_GE(it->first - alloc_size, min_object_size)
<< "byte_size=" << byte_size << " it->first=" << it->first << " alloc_size=" << alloc_size;
}
+ // Found a block.
+ CHECK(it != skipped_blocks_map_.end());
+ byte_size = it->first;
+ addr = it->second;
+ CHECK_GE(byte_size, alloc_size);
+ CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr)));
+ CHECK_ALIGNED(byte_size, space::RegionSpace::kAlignment);
+ if (kVerboseMode) {
+ LOG(INFO) << "Reusing skipped bytes : " << reinterpret_cast<void*>(addr) << ", " << byte_size;
+ }
+ skipped_blocks_map_.erase(it);
}
- // Found a block.
- CHECK(it != skipped_blocks_map_.end());
- size_t byte_size = it->first;
- uint8_t* addr = it->second;
- CHECK_GE(byte_size, alloc_size);
- CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr)));
- CHECK_ALIGNED(byte_size, space::RegionSpace::kAlignment);
- if (kVerboseMode) {
- LOG(INFO) << "Reusing skipped bytes : " << reinterpret_cast<void*>(addr) << ", " << byte_size;
- }
- skipped_blocks_map_.erase(it);
memset(addr, 0, byte_size);
if (byte_size > alloc_size) {
// Return the remainder to the map.
CHECK_ALIGNED(byte_size - alloc_size, space::RegionSpace::kAlignment);
CHECK_GE(byte_size - alloc_size, min_object_size);
+ // FillWithDummyObject may mark an object, avoid holding skipped_blocks_lock_ to prevent lock
+ // violation and possible deadlock. The deadlock case is a recursive case:
+ // FillWithDummyObject -> IntArray::GetArrayClass -> Mark -> Copy -> AllocateInSkippedBlock.
FillWithDummyObject(reinterpret_cast<mirror::Object*>(addr + alloc_size),
byte_size - alloc_size);
CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr + alloc_size)));
- skipped_blocks_map_.insert(std::make_pair(byte_size - alloc_size, addr + alloc_size));
+ {
+ MutexLock mu(self, skipped_blocks_lock_);
+ skipped_blocks_map_.insert(std::make_pair(byte_size - alloc_size, addr + alloc_size));
+ }
}
return reinterpret_cast<mirror::Object*>(addr);
}
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 32b05fa..72112fa 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -127,7 +127,7 @@
void PushOntoMarkStack(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
mirror::Object* Copy(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!skipped_blocks_lock_, !mark_stack_lock_);
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
void Scan(mirror::Object* to_ref) SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
void Process(mirror::Object* obj, MemberOffset offset)
@@ -185,9 +185,11 @@
void SweepLargeObjects(bool swap_bitmaps)
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
void FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size)
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
mirror::Object* AllocateInSkippedBlock(size_t alloc_size)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!skipped_blocks_lock_);
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void CheckEmptyMarkStack() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
void IssueEmptyCheckpoint() SHARED_REQUIRES(Locks::mutator_lock_);
bool IsOnAllocStack(mirror::Object* ref) SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 9a21ec2..042c340 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -162,9 +162,10 @@
array_class_ = GcRoot<Class>(array_class);
}
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
static Class* GetArrayClass() SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(!array_class_.IsNull());
- return array_class_.Read();
+ return array_class_.Read<kReadBarrierOption>();
}
static void ResetArrayClass() {
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 8700a90..fd58907 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -16,18 +16,7 @@
#include "oat_file_assistant.h"
-#include <fcntl.h>
-#ifdef __linux__
-#include <sys/sendfile.h>
-#else
-#include <sys/socket.h>
-#endif
-#include <sys/types.h>
#include <sys/stat.h>
-#include <unistd.h>
-
-#include <set>
-
#include "base/logging.h"
#include "base/stringprintf.h"
#include "compiler_filter.h"
@@ -71,7 +60,7 @@
const char* oat_location,
const InstructionSet isa,
bool load_executable)
- : isa_(isa), load_executable_(load_executable) {
+ : isa_(isa), load_executable_(load_executable), odex_(this), oat_(this) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
dex_location_.assign(dex_location);
@@ -81,15 +70,23 @@
load_executable_ = false;
}
+ // Get the odex filename.
std::string error_msg;
- if (!DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name_, &error_msg)) {
+ std::string odex_file_name;
+ if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) {
+ odex_.Reset(odex_file_name);
+ } else {
LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
}
+ // Get the oat filename.
if (oat_location != nullptr) {
- oat_file_name_ = std::string(oat_location);
+ oat_.Reset(oat_location);
} else {
- if (!DexLocationToOatFilename(dex_location_, isa_, &oat_file_name_, &error_msg)) {
+ std::string oat_file_name;
+ if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
+ oat_.Reset(oat_file_name);
+ } else {
LOG(WARNING) << "Failed to determine oat file name for dex location "
<< dex_location_ << ": " << error_msg;
}
@@ -124,11 +121,12 @@
CHECK(error_msg != nullptr);
CHECK(!flock_.HasFile()) << "OatFileAssistant::Lock already acquired";
- if (OatFileName() == nullptr) {
+ const std::string* oat_file_name = oat_.Filename();
+ if (oat_file_name == nullptr) {
*error_msg = "Failed to determine lock file";
return false;
}
- std::string lock_file_name = *OatFileName() + ".flock";
+ std::string lock_file_name = *oat_file_name + ".flock";
if (!flock_.Init(lock_file_name.c_str(), error_msg)) {
unlink(lock_file_name.c_str());
@@ -137,64 +135,33 @@
return true;
}
-static bool GivenOatFileCompilerFilterIsOkay(const OatFile& oat_file,
- CompilerFilter::Filter target,
- bool profile_changed) {
- CompilerFilter::Filter current = oat_file.GetCompilerFilter();
-
- if (profile_changed && CompilerFilter::DependsOnProfile(current)) {
- VLOG(oat) << "Compiler filter not okay because Profile changed";
- return false;
- }
- return CompilerFilter::IsAsGoodAs(current, target);
-}
-
-bool OatFileAssistant::OatFileCompilerFilterIsOkay(CompilerFilter::Filter target,
- bool profile_changed) {
- const OatFile* oat_file = GetOatFile();
- if (oat_file != nullptr) {
- return GivenOatFileCompilerFilterIsOkay(*oat_file, target, profile_changed);
- }
- return false;
-}
-
-bool OatFileAssistant::OdexFileCompilerFilterIsOkay(CompilerFilter::Filter target,
- bool profile_changed) {
- const OatFile* odex_file = GetOdexFile();
- if (odex_file != nullptr) {
- return GivenOatFileCompilerFilterIsOkay(*odex_file, target, profile_changed);
- }
- return false;
-}
-
OatFileAssistant::DexOptNeeded
-OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
- bool profile_changed) {
+OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
// See if the oat file is in good shape as is.
- bool oat_okay = OatFileCompilerFilterIsOkay(target, profile_changed);
+ bool oat_okay = oat_.CompilerFilterIsOkay(target, profile_changed);
if (oat_okay) {
if (compilation_desired) {
- if (OatFileIsUpToDate()) {
+ if (oat_.IsUpToDate()) {
return kNoDexOptNeeded;
}
} else {
- if (!OatFileIsOutOfDate()) {
+ if (!oat_.IsOutOfDate()) {
return kNoDexOptNeeded;
}
}
}
// See if the odex file is in good shape as is.
- bool odex_okay = OdexFileCompilerFilterIsOkay(target, profile_changed);
+ bool odex_okay = odex_.CompilerFilterIsOkay(target, profile_changed);
if (odex_okay) {
if (compilation_desired) {
- if (OdexFileIsUpToDate()) {
+ if (odex_.IsUpToDate()) {
return kNoDexOptNeeded;
}
} else {
- if (!OdexFileIsOutOfDate()) {
+ if (!odex_.IsOutOfDate()) {
return kNoDexOptNeeded;
}
}
@@ -202,11 +169,11 @@
// See if we can get an up-to-date file by running patchoat.
if (compilation_desired) {
- if (odex_okay && OdexFileNeedsRelocation() && OdexFileHasPatchInfo()) {
+ if (odex_okay && odex_.NeedsRelocation() && odex_.HasPatchInfo()) {
return kPatchOatNeeded;
}
- if (oat_okay && OatFileNeedsRelocation() && OatFileHasPatchInfo()) {
+ if (oat_okay && oat_.NeedsRelocation() && oat_.HasPatchInfo()) {
return kSelfPatchOatNeeded;
}
}
@@ -251,8 +218,8 @@
switch (GetDexOptNeeded(target, profile_changed)) {
case kNoDexOptNeeded: return kUpdateSucceeded;
case kDex2OatNeeded: return GenerateOatFile(error_msg);
- case kPatchOatNeeded: return RelocateOatFile(OdexFileName(), error_msg);
- case kSelfPatchOatNeeded: return RelocateOatFile(OatFileName(), error_msg);
+ case kPatchOatNeeded: return RelocateOatFile(odex_.Filename(), error_msg);
+ case kSelfPatchOatNeeded: return RelocateOatFile(oat_.Filename(), error_msg);
}
UNREACHABLE();
}
@@ -263,46 +230,40 @@
// 2. Not out-of-date files that are already opened non-executable.
// 3. Not out-of-date files that we must reopen non-executable.
- if (OatFileIsUpToDate()) {
- oat_file_released_ = true;
- return std::move(cached_oat_file_);
+ if (oat_.IsUpToDate()) {
+ return oat_.ReleaseFile();
}
- if (OdexFileIsUpToDate()) {
- oat_file_released_ = true;
- return std::move(cached_odex_file_);
+ if (odex_.IsUpToDate()) {
+ return odex_.ReleaseFile();
}
VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
<< " attempting to fall back to interpreting oat file instead.";
- if (!OatFileIsOutOfDate() && !OatFileIsExecutable()) {
- oat_file_released_ = true;
- return std::move(cached_oat_file_);
+ if (!oat_.IsOutOfDate() && !oat_.IsExecutable()) {
+ return oat_.ReleaseFile();
}
- if (!OdexFileIsOutOfDate() && !OdexFileIsExecutable()) {
- oat_file_released_ = true;
- return std::move(cached_odex_file_);
+ if (!odex_.IsOutOfDate() && !odex_.IsExecutable()) {
+ return odex_.ReleaseFile();
}
- if (!OatFileIsOutOfDate()) {
+ if (!oat_.IsOutOfDate()) {
load_executable_ = false;
- ClearOatFileCache();
- if (!OatFileIsOutOfDate()) {
- CHECK(!OatFileIsExecutable());
- oat_file_released_ = true;
- return std::move(cached_oat_file_);
+ oat_.Reset();
+ if (!oat_.IsOutOfDate()) {
+ CHECK(!oat_.IsExecutable());
+ return oat_.ReleaseFile();
}
}
- if (!OdexFileIsOutOfDate()) {
+ if (!odex_.IsOutOfDate()) {
load_executable_ = false;
- ClearOdexFileCache();
- if (!OdexFileIsOutOfDate()) {
- CHECK(!OdexFileIsExecutable());
- oat_file_released_ = true;
- return std::move(cached_odex_file_);
+ odex_.Reset();
+ if (!odex_.IsOutOfDate()) {
+ CHECK(!odex_.IsExecutable());
+ return odex_.ReleaseFile();
}
}
@@ -358,43 +319,31 @@
}
const std::string* OatFileAssistant::OdexFileName() {
- return odex_file_name_.empty() ? nullptr : &odex_file_name_;
+ return odex_.Filename();
}
bool OatFileAssistant::OdexFileExists() {
- return GetOdexFile() != nullptr;
+ return odex_.Exists();
}
OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
- if (!odex_file_status_attempted_) {
- odex_file_status_attempted_ = true;
- const OatFile* odex_file = GetOdexFile();
- if (odex_file == nullptr) {
- cached_odex_file_status_ = kOatOutOfDate;
- } else {
- cached_odex_file_status_ = GivenOatFileStatus(*odex_file);
- }
- }
- return cached_odex_file_status_;
+ return odex_.Status();
}
bool OatFileAssistant::OdexFileIsOutOfDate() {
- return OdexFileStatus() == kOatOutOfDate;
+ return odex_.IsOutOfDate();
}
bool OatFileAssistant::OdexFileNeedsRelocation() {
- return OdexFileStatus() == kOatNeedsRelocation;
+ return odex_.NeedsRelocation();
}
bool OatFileAssistant::OdexFileIsUpToDate() {
- return OdexFileStatus() == kOatUpToDate;
+ return odex_.IsUpToDate();
}
CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() {
- const OatFile* odex_file = GetOdexFile();
- CHECK(odex_file != nullptr);
-
- return odex_file->GetCompilerFilter();
+ return odex_.CompilerFilter();
}
static std::string ArtFileName(const OatFile* oat_file) {
@@ -409,43 +358,31 @@
}
const std::string* OatFileAssistant::OatFileName() {
- return oat_file_name_.empty() ? nullptr : &oat_file_name_;
+ return oat_.Filename();
}
bool OatFileAssistant::OatFileExists() {
- return GetOatFile() != nullptr;
+ return oat_.Exists();
}
OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
- if (!oat_file_status_attempted_) {
- oat_file_status_attempted_ = true;
- const OatFile* oat_file = GetOatFile();
- if (oat_file == nullptr) {
- cached_oat_file_status_ = kOatOutOfDate;
- } else {
- cached_oat_file_status_ = GivenOatFileStatus(*oat_file);
- }
- }
- return cached_oat_file_status_;
+ return oat_.Status();
}
bool OatFileAssistant::OatFileIsOutOfDate() {
- return OatFileStatus() == kOatOutOfDate;
+ return oat_.IsOutOfDate();
}
bool OatFileAssistant::OatFileNeedsRelocation() {
- return OatFileStatus() == kOatNeedsRelocation;
+ return oat_.NeedsRelocation();
}
bool OatFileAssistant::OatFileIsUpToDate() {
- return OatFileStatus() == kOatUpToDate;
+ return oat_.IsUpToDate();
}
CompilerFilter::Filter OatFileAssistant::OatFileCompilerFilter() {
- const OatFile* oat_file = GetOatFile();
- CHECK(oat_file != nullptr);
-
- return oat_file->GetCompilerFilter();
+ return oat_.CompilerFilter();
}
OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
@@ -572,12 +509,12 @@
}
const std::string& input_file_name = *input_file;
- if (OatFileName() == nullptr) {
+ if (oat_.Filename() == nullptr) {
*error_msg = "Patching of oat file for dex location " + dex_location_
+ " not attempted because the oat file name could not be determined.";
return kUpdateNotAttempted;
}
- const std::string& oat_file_name = *OatFileName();
+ const std::string& oat_file_name = *oat_.Filename();
const ImageInfo* image_info = GetImageInfo();
Runtime* runtime = Runtime::Current();
@@ -609,7 +546,7 @@
}
// Mark that the oat file has changed and we should try to reload.
- ClearOatFileCache();
+ oat_.Reset();
return kUpdateSucceeded;
}
@@ -624,12 +561,12 @@
return kUpdateNotAttempted;
}
- if (OatFileName() == nullptr) {
+ if (oat_.Filename() == nullptr) {
*error_msg = "Generation of oat file for dex location " + dex_location_
+ " not attempted because the oat file name could not be determined.";
return kUpdateNotAttempted;
}
- const std::string& oat_file_name = *OatFileName();
+ const std::string& oat_file_name = *oat_.Filename();
// dex2oat ignores missing dex files and doesn't report an error.
// Check explicitly here so we can detect the error properly.
@@ -674,7 +611,7 @@
}
// Mark that the oat file has changed and we should try to reload.
- ClearOatFileCache();
+ oat_.Reset();
return kUpdateSucceeded;
}
@@ -821,7 +758,7 @@
has_original_dex_files_ = false;
// Get the checksum from the odex if we can.
- const OatFile* odex_file = GetOdexFile();
+ const OatFile* odex_file = odex_.GetFile();
if (odex_file != nullptr) {
const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(
dex_location_.c_str(), nullptr, false);
@@ -835,86 +772,6 @@
return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
}
-const OatFile* OatFileAssistant::GetOdexFile() {
- CHECK(!oat_file_released_) << "OdexFile called after oat file released.";
- if (!odex_file_load_attempted_) {
- odex_file_load_attempted_ = true;
- if (OdexFileName() != nullptr) {
- const std::string& odex_file_name = *OdexFileName();
- std::string error_msg;
- cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
- odex_file_name.c_str(),
- nullptr,
- nullptr,
- load_executable_,
- /*low_4gb*/false,
- dex_location_.c_str(),
- &error_msg));
- if (cached_odex_file_.get() == nullptr) {
- VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
- << odex_file_name << ": " << error_msg;
- }
- }
- }
- return cached_odex_file_.get();
-}
-
-bool OatFileAssistant::OdexFileIsExecutable() {
- const OatFile* odex_file = GetOdexFile();
- return (odex_file != nullptr && odex_file->IsExecutable());
-}
-
-bool OatFileAssistant::OdexFileHasPatchInfo() {
- const OatFile* odex_file = GetOdexFile();
- return (odex_file != nullptr && odex_file->HasPatchInfo());
-}
-
-void OatFileAssistant::ClearOdexFileCache() {
- odex_file_load_attempted_ = false;
- cached_odex_file_.reset();
- odex_file_status_attempted_ = false;
-}
-
-const OatFile* OatFileAssistant::GetOatFile() {
- CHECK(!oat_file_released_) << "OatFile called after oat file released.";
- if (!oat_file_load_attempted_) {
- oat_file_load_attempted_ = true;
- if (OatFileName() != nullptr) {
- const std::string& oat_file_name = *OatFileName();
- std::string error_msg;
- cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
- oat_file_name.c_str(),
- nullptr,
- nullptr,
- load_executable_,
- /*low_4gb*/false,
- dex_location_.c_str(),
- &error_msg));
- if (cached_oat_file_.get() == nullptr) {
- VLOG(oat) << "OatFileAssistant test for existing oat file "
- << oat_file_name << ": " << error_msg;
- }
- }
- }
- return cached_oat_file_.get();
-}
-
-bool OatFileAssistant::OatFileIsExecutable() {
- const OatFile* oat_file = GetOatFile();
- return (oat_file != nullptr && oat_file->IsExecutable());
-}
-
-bool OatFileAssistant::OatFileHasPatchInfo() {
- const OatFile* oat_file = GetOatFile();
- return (oat_file != nullptr && oat_file->HasPatchInfo());
-}
-
-void OatFileAssistant::ClearOatFileCache() {
- oat_file_load_attempted_ = false;
- cached_oat_file_.reset();
- oat_file_status_attempted_ = false;
-}
-
const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
if (!image_info_load_attempted_) {
image_info_load_attempted_ = true;
@@ -990,5 +847,113 @@
return ret;
}
+OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant)
+ : oat_file_assistant_(oat_file_assistant)
+{}
+
+const std::string* OatFileAssistant::OatFileInfo::Filename() {
+ return filename_provided_ ? &filename_ : nullptr;
+}
+
+bool OatFileAssistant::OatFileInfo::Exists() {
+ return GetFile() != nullptr;
+}
+
+OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
+ if (!status_attempted_) {
+ status_attempted_ = true;
+ const OatFile* file = GetFile();
+ if (file == nullptr) {
+ status_ = kOatOutOfDate;
+ } else {
+ status_ = oat_file_assistant_->GivenOatFileStatus(*file);
+ }
+ }
+ return status_;
+}
+
+bool OatFileAssistant::OatFileInfo::IsOutOfDate() {
+ return Status() == kOatOutOfDate;
+}
+
+bool OatFileAssistant::OatFileInfo::NeedsRelocation() {
+ return Status() == kOatNeedsRelocation;
+}
+
+bool OatFileAssistant::OatFileInfo::IsUpToDate() {
+ return Status() == kOatUpToDate;
+}
+
+CompilerFilter::Filter OatFileAssistant::OatFileInfo::CompilerFilter() {
+ const OatFile* file = GetFile();
+ CHECK(file != nullptr);
+ return file->GetCompilerFilter();
+}
+
+const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
+ CHECK(!file_released_) << "GetFile called after oat file released.";
+ if (!load_attempted_) {
+ load_attempted_ = true;
+ if (filename_provided_) {
+ std::string error_msg;
+ file_.reset(OatFile::Open(filename_.c_str(),
+ filename_.c_str(),
+ nullptr,
+ nullptr,
+ oat_file_assistant_->load_executable_,
+ /*low_4gb*/false,
+ oat_file_assistant_->dex_location_.c_str(),
+ &error_msg));
+ if (file_.get() == nullptr) {
+ VLOG(oat) << "OatFileAssistant test for existing oat file "
+ << filename_ << ": " << error_msg;
+ }
+ }
+ }
+ return file_.get();
+}
+
+bool OatFileAssistant::OatFileInfo::CompilerFilterIsOkay(
+ CompilerFilter::Filter target, bool profile_changed) {
+ const OatFile* file = GetFile();
+ if (file == nullptr) {
+ return false;
+ }
+
+ CompilerFilter::Filter current = file->GetCompilerFilter();
+ if (profile_changed && CompilerFilter::DependsOnProfile(current)) {
+ VLOG(oat) << "Compiler filter not okay because Profile changed";
+ return false;
+ }
+ return CompilerFilter::IsAsGoodAs(current, target);
+}
+
+bool OatFileAssistant::OatFileInfo::IsExecutable() {
+ const OatFile* file = GetFile();
+ return (file != nullptr && file->IsExecutable());
+}
+
+bool OatFileAssistant::OatFileInfo::HasPatchInfo() {
+ const OatFile* file = GetFile();
+ return (file != nullptr && file->HasPatchInfo());
+}
+
+void OatFileAssistant::OatFileInfo::Reset() {
+ load_attempted_ = false;
+ file_.reset();
+ status_attempted_ = false;
+}
+
+void OatFileAssistant::OatFileInfo::Reset(const std::string& filename) {
+ filename_provided_ = true;
+ filename_ = filename;
+ Reset();
+}
+
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFile() {
+ file_released_ = true;
+ return std::move(file_);
+}
+
} // namespace art
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 04bd20c..effeabb 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -306,6 +306,73 @@
std::string location;
};
+ class OatFileInfo {
+ public:
+ // Initially the info is for no file in particular. It will treat the
+ // file as out of date until Reset is called with a real filename to use
+ // the cache for.
+ explicit OatFileInfo(OatFileAssistant* oat_file_assistant);
+
+ const std::string* Filename();
+ bool Exists();
+ OatStatus Status();
+ bool IsOutOfDate();
+ bool NeedsRelocation();
+ bool IsUpToDate();
+ // Must only be called if the associated file exists, i.e, if
+ // |Exists() == true|.
+ CompilerFilter::Filter CompilerFilter();
+
+ // Returns the loaded file.
+ // Loads the file if needed. Returns null if the file failed to load.
+ // The caller shouldn't clean up or free the returned pointer.
+ const OatFile* GetFile();
+
+ // Returns true if the compiler filter used to generate the file is at
+ // least as good as the given target filter. profile_changed should be
+ // true to indicate the profile has recently changed for this dex
+ // location.
+ bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
+
+ // Returns true if the file is opened executable.
+ bool IsExecutable();
+
+ // Returns true if the file has patch info required to run patchoat.
+ bool HasPatchInfo();
+
+ // Clear any cached information about the file that depends on the
+ // contents of the file. This does not reset the provided filename.
+ void Reset();
+
+ // Clear any cached information and switch to getting info about the oat
+ // file with the given filename.
+ void Reset(const std::string& filename);
+
+ // Release the loaded oat file.
+ // Returns null if the oat file hasn't been loaded.
+ //
+ // After this call, no other methods of the OatFileInfo should be
+ // called, because access to the loaded oat file has been taken away from
+ // the OatFileInfo object.
+ std::unique_ptr<OatFile> ReleaseFile();
+
+ private:
+ OatFileAssistant* oat_file_assistant_;
+
+ bool filename_provided_ = false;
+ std::string filename_;
+
+ bool load_attempted_ = false;
+ std::unique_ptr<OatFile> file_;
+
+ bool status_attempted_ = false;
+ OatStatus status_;
+
+ // For debugging only.
+ // If this flag is set, the file has been released to the user and the
+ // OatFileInfo object is in a bad state and should no longer be used.
+ bool file_released_ = false;
+ };
// Returns the current image location.
// Returns an empty string if the image location could not be retrieved.
@@ -322,46 +389,6 @@
// found for the dex_location_ dex file.
const uint32_t* GetRequiredDexChecksum();
- // Returns the loaded odex file.
- // Loads the file if needed. Returns null if the file failed to load.
- // The caller shouldn't clean up or free the returned pointer.
- const OatFile* GetOdexFile();
-
- // Returns true if the compiler filter used to generate the odex file is at
- // least as good as the given target filter. profile_changed should be true
- // to indicate the profile has recently changed for this dex location.
- bool OdexFileCompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
-
- // Returns true if the odex file is opened executable.
- bool OdexFileIsExecutable();
-
- // Returns true if the odex file has patch info required to run patchoat.
- bool OdexFileHasPatchInfo();
-
- // Clear any cached information about the odex file that depends on the
- // contents of the file.
- void ClearOdexFileCache();
-
- // Returns the loaded oat file.
- // Loads the file if needed. Returns null if the file failed to load.
- // The caller shouldn't clean up or free the returned pointer.
- const OatFile* GetOatFile();
-
- // Returns true if the compiler filter used to generate the oat file is at
- // least as good as the given target filter. profile_changed should be true
- // to indicate the profile has recently changed for this dex location.
- bool OatFileCompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
-
- // Returns true if the oat file is opened executable.
- bool OatFileIsExecutable();
-
- // Returns true if the oat file has patch info required to run patchoat.
- bool OatFileHasPatchInfo();
-
- // Clear any cached information about the oat file that depends on the
- // contents of the file.
- void ClearOatFileCache();
-
// Returns the loaded image info.
// Loads the image info if needed. Returns null if the image info failed
// to load.
@@ -391,33 +418,8 @@
bool required_dex_checksum_found_;
bool has_original_dex_files_;
- // The sentinel value "" is used if the odex file name could not be
- // determined.
- std::string odex_file_name_;
-
- // Cached value of the loaded odex file.
- // Use the GetOdexFile method rather than accessing this directly, unless you
- // know the odex file isn't out of date.
- bool odex_file_load_attempted_ = false;
- std::unique_ptr<OatFile> cached_odex_file_;
-
- // Cached results for OdexFileStatus
- bool odex_file_status_attempted_ = false;
- OatStatus cached_odex_file_status_;
-
- // The sentinel value "" is used if the oat file name could not be
- // determined.
- std::string oat_file_name_;
-
- // Cached value of the loaded oat file.
- // Use the GetOatFile method rather than accessing this directly, unless you
- // know the oat file isn't out of date.
- bool oat_file_load_attempted_ = false;
- std::unique_ptr<OatFile> cached_oat_file_;
-
- // Cached results for OatFileStatus
- bool oat_file_status_attempted_ = false;
- OatStatus cached_oat_file_status_;
+ OatFileInfo odex_;
+ OatFileInfo oat_;
// Cached value of the image info.
// Use the GetImageInfo method rather than accessing these directly.
@@ -428,12 +430,6 @@
ImageInfo cached_image_info_;
uint32_t combined_image_checksum_ = 0;
- // For debugging only.
- // If this flag is set, the oat or odex file has been released to the user
- // of the OatFileAssistant object and the OatFileAssistant object is in a
- // bad state and should no longer be used.
- bool oat_file_released_ = false;
-
DISALLOW_COPY_AND_ASSIGN(OatFileAssistant);
};
diff --git a/test/613-inlining-dex-cache/expected.txt b/test/613-inlining-dex-cache/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/613-inlining-dex-cache/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/613-inlining-dex-cache/info.txt b/test/613-inlining-dex-cache/info.txt
new file mode 100644
index 0000000..e80f642
--- /dev/null
+++ b/test/613-inlining-dex-cache/info.txt
@@ -0,0 +1,2 @@
+Regression test for the JIT compiler which used to
+wrongly update the dex cache of a class loader.
diff --git a/test/613-inlining-dex-cache/run b/test/613-inlining-dex-cache/run
new file mode 100644
index 0000000..9c1e7aa
--- /dev/null
+++ b/test/613-inlining-dex-cache/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+flags="$@"
+# We need the dex files pre-verified to avoid running the verifier
+# at runtime which will update the dex cache.
+exec ${RUN} ${flags/verify-at-runtime/interpret-only}
diff --git a/test/613-inlining-dex-cache/src-ex/B.java b/test/613-inlining-dex-cache/src-ex/B.java
new file mode 100644
index 0000000..4da9a1d
--- /dev/null
+++ b/test/613-inlining-dex-cache/src-ex/B.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class B {
+}
diff --git a/test/613-inlining-dex-cache/src-ex/LoadedByAppClassLoader.java b/test/613-inlining-dex-cache/src-ex/LoadedByAppClassLoader.java
new file mode 100644
index 0000000..f4e0f10
--- /dev/null
+++ b/test/613-inlining-dex-cache/src-ex/LoadedByAppClassLoader.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class LoadedByAppClassLoader {
+ public static void letMeInlineYou() {
+ // We used to pass the wrong class loader when trying to inline 'Main.foo'.
+ Main.foo(null);
+ }
+}
diff --git a/test/613-inlining-dex-cache/src/B.java b/test/613-inlining-dex-cache/src/B.java
new file mode 100644
index 0000000..6e7e55d
--- /dev/null
+++ b/test/613-inlining-dex-cache/src/B.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class B {
+ public void foo() {
+ }
+}
diff --git a/test/613-inlining-dex-cache/src/Main.java b/test/613-inlining-dex-cache/src/Main.java
new file mode 100644
index 0000000..31ab1d2
--- /dev/null
+++ b/test/613-inlining-dex-cache/src/Main.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+import dalvik.system.PathClassLoader;
+
+// ClassLoader not delegating for non java. packages.
+class DelegateLastPathClassLoader extends PathClassLoader {
+
+ public DelegateLastPathClassLoader(String dexPath, ClassLoader parent) {
+ super(dexPath, parent);
+ }
+
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (!name.startsWith("java.")) {
+ try {
+ return findClass(name);
+ } catch (ClassNotFoundException ignore) {
+ // Ignore and fall through to parent class loader.
+ }
+ }
+ return super.loadClass(name, resolve);
+ }
+}
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ final String DEX_FILE = System.getenv("DEX_LOCATION") + "/613-inlining-dex-cache-ex.jar";
+ ClassLoader loader = new DelegateLastPathClassLoader(DEX_FILE, Main.class.getClassLoader());
+ Class cls = loader.loadClass("LoadedByAppClassLoader");
+ Method m = cls.getDeclaredMethod("letMeInlineYou");
+ // Invoke the method enough times to get JITted.
+ for (int i = 0; i < 10000; ++i) {
+ m.invoke(null);
+ }
+ ensureJitCompiled(cls, "letMeInlineYou");
+ ClassLoader bLoader = areYouB();
+ if (bLoader != Main.class.getClassLoader()) {
+ throw new Error("Wrong class loader");
+ }
+ }
+
+ public static void foo(Main o) {
+ // LoadedByAppClassLoader.letMeInlineYou will try to inline this
+ // method but used to pass the wrong class loader. As a result,
+ // the lookup of B.foo was updating the dex cache with the other
+ // class loader's B class.
+ if (o != null) {
+ o.myField.foo();
+ }
+ }
+
+ public B myField;
+
+ public static ClassLoader areYouB() {
+ return OtherClass.getB().getClassLoader();
+ }
+
+ public static native void ensureJitCompiled(Class cls, String method_name);
+}
+
+class OtherClass {
+ public static Class getB() {
+ // This used to return the B class of another class loader.
+ return B.class;
+ }
+}