Merge "mirror: Make Class::Status 64-bit field"
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 6fbb2bd..58b6137 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1943,20 +1943,15 @@
DCHECK_EQ(bss_size_, 0u);
if (HasBootImage()) {
DCHECK(bss_string_entries_.empty());
- if (bss_method_entries_.empty() && bss_type_entries_.empty()) {
- // Nothing to put to the .bss section.
- return;
- }
+ }
+ if (bss_method_entries_.empty() &&
+ bss_type_entries_.empty() &&
+ bss_string_entries_.empty()) {
+ // Nothing to put to the .bss section.
+ return;
}
- // Allocate space for app dex cache arrays in the .bss section.
PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set);
- if (!HasBootImage()) {
- for (const DexFile* dex_file : *dex_files_) {
- DexCacheArraysLayout layout(pointer_size, dex_file);
- bss_size_ += layout.Size();
- }
- }
bss_methods_offset_ = bss_size_;
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index f8f4eb2..a249cac 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -331,8 +331,9 @@
HLoopOptimization::HLoopOptimization(HGraph* graph,
CompilerDriver* compiler_driver,
- HInductionVarAnalysis* induction_analysis)
- : HOptimization(graph, kLoopOptimizationPassName),
+ HInductionVarAnalysis* induction_analysis,
+ OptimizingCompilerStats* stats)
+ : HOptimization(graph, kLoopOptimizationPassName, stats),
compiler_driver_(compiler_driver),
induction_range_(induction_analysis),
loop_allocator_(nullptr),
@@ -625,6 +626,7 @@
TryAssignLastValue(node->loop_info, main_phi, preheader, /*collect_loop_uses*/ true)) {
Vectorize(node, body, exit, trip_count);
graph_->SetHasSIMD(true); // flag SIMD usage
+ MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorized);
return true;
}
return false;
@@ -1724,6 +1726,7 @@
vector_length_,
is_unsigned,
is_rounded));
+ MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom);
} else {
GenerateVecOp(instruction, vector_map_->Get(r), vector_map_->Get(s), type);
}
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index ba9126c..f347518 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -34,7 +34,8 @@
public:
HLoopOptimization(HGraph* graph,
CompilerDriver* compiler_driver,
- HInductionVarAnalysis* induction_analysis);
+ HInductionVarAnalysis* induction_analysis,
+ OptimizingCompilerStats* stats);
void Run() OVERRIDE;
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index b5b03d8..1c5603d 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -31,7 +31,7 @@
allocator_(&pool_),
graph_(CreateGraph(&allocator_)),
iva_(new (&allocator_) HInductionVarAnalysis(graph_)),
- loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_)) {
+ loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_, nullptr)) {
BuildGraph();
}
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index e98c97c..71d91ae 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -509,7 +509,7 @@
} else if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) {
return new (arena) SideEffectsAnalysis(graph);
} else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) {
- return new (arena) HLoopOptimization(graph, driver, most_recent_induction);
+ return new (arena) HLoopOptimization(graph, driver, most_recent_induction, stats);
} else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) {
return new (arena) CHAGuardOptimization(graph);
} else if (opt_name == CodeSinking::kCodeSinkingPassName) {
@@ -770,7 +770,7 @@
LICM* licm = new (arena) LICM(graph, *side_effects1, stats);
HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects1, induction);
- HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction);
+ HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction, stats);
LoadStoreAnalysis* lsa = new (arena) LoadStoreAnalysis(graph);
LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects2, *lsa, stats);
HSharpening* sharpening = new (arena) HSharpening(
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index d6da73c..ff49056 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -63,6 +63,8 @@
kBooleanSimplified,
kIntrinsicRecognized,
kLoopInvariantMoved,
+ kLoopVectorized,
+ kLoopVectorizedIdiom,
kSelectGenerated,
kRemovedInstanceOf,
kInlinedInvokeVirtualOrInterface,
@@ -183,6 +185,8 @@
case kBooleanSimplified : name = "BooleanSimplified"; break;
case kIntrinsicRecognized : name = "IntrinsicRecognized"; break;
case kLoopInvariantMoved : name = "LoopInvariantMoved"; break;
+ case kLoopVectorized : name = "LoopVectorized"; break;
+ case kLoopVectorizedIdiom : name = "LoopVectorizedIdiom"; break;
case kSelectGenerated : name = "SelectGenerated"; break;
case kRemovedInstanceOf: name = "RemovedInstanceOf"; break;
case kInlinedInvokeVirtualOrInterface: name = "InlinedInvokeVirtualOrInterface"; break;
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 6c0d492..277f611 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -810,11 +810,11 @@
}
static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env,
- jobject object ATTRIBUTE_UNUSED,
- jvmtiMonitorUsage* info_ptr ATTRIBUTE_UNUSED) {
+ jobject object,
+ jvmtiMonitorUsage* info_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_get_monitor_info);
- return ERR(NOT_IMPLEMENTED);
+ return ObjectUtil::GetObjectMonitorUsage(env, object, info_ptr);
}
static jvmtiError GetFieldName(jvmtiEnv* env,
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 93eee28..d3f52f6 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -226,7 +226,7 @@
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 1,
.can_get_current_contended_monitor = 0,
- .can_get_monitor_info = 0,
+ .can_get_monitor_info = 1,
.can_pop_frame = 0,
.can_redefine_classes = 1,
.can_signal_thread = 0,
diff --git a/openjdkjvmti/ti_object.cc b/openjdkjvmti/ti_object.cc
index 2506aca..89ce352 100644
--- a/openjdkjvmti/ti_object.cc
+++ b/openjdkjvmti/ti_object.cc
@@ -35,6 +35,8 @@
#include "mirror/object-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_thread.h"
namespace openjdkjvmti {
@@ -73,4 +75,59 @@
return ERR(NONE);
}
+jvmtiError ObjectUtil::GetObjectMonitorUsage(
+ jvmtiEnv* baseenv, jobject obj, jvmtiMonitorUsage* usage) {
+ ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(baseenv);
+ if (obj == nullptr) {
+ return ERR(INVALID_OBJECT);
+ }
+ if (usage == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ art::Thread* self = art::Thread::Current();
+ ThreadUtil::SuspendCheck(self);
+ art::JNIEnvExt* jni = self->GetJniEnv();
+ std::vector<jthread> wait;
+ std::vector<jthread> notify_wait;
+ {
+ art::ScopedObjectAccess soa(self); // Now we know we have the shared lock.
+ art::ScopedThreadSuspension sts(self, art::kNative);
+ art::ScopedSuspendAll ssa("GetObjectMonitorUsage", /*long_suspend*/false);
+ art::ObjPtr<art::mirror::Object> target(self->DecodeJObject(obj));
+ // This gets the list of threads trying to lock or wait on the monitor.
+ art::MonitorInfo info(target.Ptr());
+ usage->owner = info.owner_ != nullptr ?
+ jni->AddLocalReference<jthread>(info.owner_->GetPeerFromOtherThread()) : nullptr;
+ usage->entry_count = info.entry_count_;
+ for (art::Thread* thd : info.waiters_) {
+ // RI seems to consider waiting for notify to be included in those waiting to acquire the
+ // monitor. We will match this behavior.
+ notify_wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+ wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+ }
+ {
+ // Scan all threads to see which are waiting on this particular monitor.
+ art::MutexLock tll(self, *art::Locks::thread_list_lock_);
+ for (art::Thread* thd : art::Runtime::Current()->GetThreadList()->GetList()) {
+ if (thd != info.owner_ && target.Ptr() == thd->GetMonitorEnterObject()) {
+ wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+ }
+ }
+ }
+ }
+ usage->waiter_count = wait.size();
+ usage->notify_waiter_count = notify_wait.size();
+ jvmtiError ret = CopyDataIntoJvmtiBuffer(env,
+ reinterpret_cast<const unsigned char*>(wait.data()),
+ wait.size() * sizeof(jthread),
+ reinterpret_cast<unsigned char**>(&usage->waiters));
+ if (ret != OK) {
+ return ret;
+ }
+ return CopyDataIntoJvmtiBuffer(env,
+ reinterpret_cast<const unsigned char*>(notify_wait.data()),
+ notify_wait.size() * sizeof(jthread),
+ reinterpret_cast<unsigned char**>(&usage->notify_waiters));
+}
+
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_object.h b/openjdkjvmti/ti_object.h
index fa3bd0f..977ec39 100644
--- a/openjdkjvmti/ti_object.h
+++ b/openjdkjvmti/ti_object.h
@@ -42,6 +42,8 @@
static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr);
static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr);
+
+ static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env, jobject object, jvmtiMonitorUsage* usage);
};
} // namespace openjdkjvmti
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d22482f..efef975 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1189,219 +1189,41 @@
}
};
-// Copies data from one array to another array at the same position
-// if pred returns false. If there is a page of continuous data in
-// the src array for which pred consistently returns true then
-// corresponding page in the dst array will not be touched.
-// This should reduce number of allocated physical pages.
-template <class T, class NullPred>
-static void CopyNonNull(const T* src, size_t count, T* dst, const NullPred& pred) {
- for (size_t i = 0; i < count; ++i) {
- if (!pred(src[i])) {
- dst[i] = src[i];
- }
- }
-}
-
-template <typename T>
-static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src,
- size_t count,
- std::atomic<mirror::DexCachePair<T>>* dst) {
- DCHECK_NE(count, 0u);
- DCHECK(!src[0].load(std::memory_order_relaxed).object.IsNull() ||
- src[0].load(std::memory_order_relaxed).index != 0u);
- for (size_t i = 0; i < count; ++i) {
- DCHECK_EQ(dst[i].load(std::memory_order_relaxed).index, 0u);
- DCHECK(dst[i].load(std::memory_order_relaxed).object.IsNull());
- mirror::DexCachePair<T> source = src[i].load(std::memory_order_relaxed);
- if (source.index != 0u || !source.object.IsNull()) {
- dst[i].store(source, std::memory_order_relaxed);
- }
- }
-}
-
-template <typename T>
-static void CopyNativeDexCachePairs(std::atomic<mirror::NativeDexCachePair<T>>* src,
- size_t count,
- std::atomic<mirror::NativeDexCachePair<T>>* dst,
- PointerSize pointer_size) {
- DCHECK_NE(count, 0u);
- DCHECK(mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).object != nullptr ||
- mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).index != 0u);
- for (size_t i = 0; i < count; ++i) {
- DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).index, 0u);
- DCHECK(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).object == nullptr);
- mirror::NativeDexCachePair<T> source =
- mirror::DexCache::GetNativePairPtrSize(src, i, pointer_size);
- if (source.index != 0u || source.object != nullptr) {
- mirror::DexCache::SetNativePairPtrSize(dst, i, source, pointer_size);
- }
- }
-}
-
// new_class_set is the set of classes that were read from the class table section in the image.
// If there was no class table section, it is null.
// Note: using a class here to avoid having to make ClassLinker internals public.
class AppImageClassLoadersAndDexCachesHelper {
public:
- static bool Update(
+ static void Update(
ClassLinker* class_linker,
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
- ClassTable::ClassSet* new_class_set,
- bool* out_forward_dex_cache_array,
- std::string* out_error_msg)
+ ClassTable::ClassSet* new_class_set)
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
};
-bool AppImageClassLoadersAndDexCachesHelper::Update(
+void AppImageClassLoadersAndDexCachesHelper::Update(
ClassLinker* class_linker,
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
- ClassTable::ClassSet* new_class_set,
- bool* out_forward_dex_cache_array,
- std::string* out_error_msg)
+ ClassTable::ClassSet* new_class_set)
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(out_forward_dex_cache_array != nullptr);
- DCHECK(out_error_msg != nullptr);
- PointerSize image_pointer_size = class_linker->GetImagePointerSize();
Thread* const self = Thread::Current();
gc::Heap* const heap = Runtime::Current()->GetHeap();
const ImageHeader& header = space->GetImageHeader();
{
- // Add image classes into the class table for the class loader, and fixup the dex caches and
- // class loader fields.
+ // Register dex caches with the class loader.
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
- // rely on clobering the dex cache arrays in the image to forward to bss.
- size_t num_dex_caches_with_bss_arrays = 0;
const size_t num_dex_caches = dex_caches->GetLength();
for (size_t i = 0; i < num_dex_caches; i++) {
- ObjPtr<mirror::DexCache> const dex_cache = dex_caches->Get(i);
- const DexFile* const dex_file = dex_cache->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
- if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
- ++num_dex_caches_with_bss_arrays;
- }
- }
- *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
- if (*out_forward_dex_cache_array) {
- if (num_dex_caches_with_bss_arrays != num_dex_caches) {
- // Reject application image since we cannot forward only some of the dex cache arrays.
- // TODO: We could get around this by having a dedicated forwarding slot. It should be an
- // uncommon case.
- *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
- num_dex_caches_with_bss_arrays,
- num_dex_caches);
- return false;
- }
- }
-
- // Only add the classes to the class loader after the points where we can return false.
- for (size_t i = 0; i < num_dex_caches; i++) {
ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
const DexFile* const dex_file = dex_cache->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
- if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
- // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
- // copy over the arrays.
- DCHECK(dex_file != nullptr);
- size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize;
- if (dex_file->NumStringIds() < num_strings) {
- num_strings = dex_file->NumStringIds();
- }
- size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
- if (dex_file->NumTypeIds() < num_types) {
- num_types = dex_file->NumTypeIds();
- }
- size_t num_methods = mirror::DexCache::kDexCacheMethodCacheSize;
- if (dex_file->NumMethodIds() < num_methods) {
- num_methods = dex_file->NumMethodIds();
- }
- size_t num_fields = mirror::DexCache::kDexCacheFieldCacheSize;
- if (dex_file->NumFieldIds() < num_fields) {
- num_fields = dex_file->NumFieldIds();
- }
- size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
- if (dex_file->NumProtoIds() < num_method_types) {
- num_method_types = dex_file->NumProtoIds();
- }
- const size_t num_call_sites = dex_file->NumCallSiteIds();
- CHECK_EQ(num_strings, dex_cache->NumStrings());
- CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
- CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
- CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
- CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
- CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites());
- DexCacheArraysLayout layout(image_pointer_size, dex_file);
- uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
- if (num_strings != 0u) {
- mirror::StringDexCacheType* const image_resolved_strings = dex_cache->GetStrings();
- mirror::StringDexCacheType* const strings =
- reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
- CopyDexCachePairs(image_resolved_strings, num_strings, strings);
- dex_cache->SetStrings(strings);
- }
- if (num_types != 0u) {
- mirror::TypeDexCacheType* const image_resolved_types = dex_cache->GetResolvedTypes();
- mirror::TypeDexCacheType* const types =
- reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
- CopyDexCachePairs(image_resolved_types, num_types, types);
- dex_cache->SetResolvedTypes(types);
- }
- if (num_methods != 0u) {
- mirror::MethodDexCacheType* const image_resolved_methods =
- dex_cache->GetResolvedMethods();
- mirror::MethodDexCacheType* const methods =
- reinterpret_cast<mirror::MethodDexCacheType*>(raw_arrays + layout.MethodsOffset());
- CopyNativeDexCachePairs(image_resolved_methods, num_methods, methods, image_pointer_size);
- dex_cache->SetResolvedMethods(methods);
- }
- if (num_fields != 0u) {
- mirror::FieldDexCacheType* const image_resolved_fields = dex_cache->GetResolvedFields();
- mirror::FieldDexCacheType* const fields =
- reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset());
- CopyNativeDexCachePairs(image_resolved_fields, num_fields, fields, image_pointer_size);
- dex_cache->SetResolvedFields(fields);
- }
- if (num_method_types != 0u) {
- // NOTE: We currently (Sep 2016) do not resolve any method types at
- // compile time, but plan to in the future. This code exists for the
- // sake of completeness.
- mirror::MethodTypeDexCacheType* const image_resolved_method_types =
- dex_cache->GetResolvedMethodTypes();
- mirror::MethodTypeDexCacheType* const method_types =
- reinterpret_cast<mirror::MethodTypeDexCacheType*>(
- raw_arrays + layout.MethodTypesOffset());
- CopyDexCachePairs(image_resolved_method_types, num_method_types, method_types);
- dex_cache->SetResolvedMethodTypes(method_types);
- }
- if (num_call_sites != 0u) {
- GcRoot<mirror::CallSite>* const image_resolved_call_sites =
- dex_cache->GetResolvedCallSites();
- GcRoot<mirror::CallSite>* const call_sites =
- reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
- for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) {
- DCHECK(call_sites[j].IsNull());
- }
- CopyNonNull(image_resolved_call_sites,
- num_call_sites,
- call_sites,
- [](const GcRoot<mirror::CallSite>& elem) {
- return elem.IsNull();
- });
- dex_cache->SetResolvedCallSites(call_sites);
- }
- }
{
WriterMutexLock mu2(self, *Locks::dex_lock_);
- // Make sure to do this after we update the arrays since we store the resolved types array
- // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the
- // BSS.
CHECK(!class_linker->FindDexCacheDataLocked(*dex_file).IsValid());
class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get());
}
@@ -1468,7 +1290,6 @@
VerifyDeclaringClassVisitor visitor;
header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize);
}
- return true;
}
// Update the class loader. Should only be used on classes in the image space.
@@ -1947,7 +1768,7 @@
// If we have a class table section, read it and use it for verification in
// UpdateAppImageClassLoadersAndDexCaches.
ClassTable::ClassSet temp_set;
- const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable);
+ const ImageSection& class_table_section = header.GetClassTableSection();
const bool added_class_table = class_table_section.Size() > 0u;
if (added_class_table) {
const uint64_t start_time2 = NanoTime();
@@ -1958,37 +1779,17 @@
VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2);
}
if (app_image) {
- bool forward_dex_cache_arrays = false;
- if (!AppImageClassLoadersAndDexCachesHelper::Update(this,
- space,
- class_loader,
- dex_caches,
- &temp_set,
- /*out*/&forward_dex_cache_arrays,
- /*out*/error_msg)) {
- return false;
- }
+ AppImageClassLoadersAndDexCachesHelper::Update(this,
+ space,
+ class_loader,
+ dex_caches,
+ &temp_set);
// Update class loader and resolved strings. If added_class_table is false, the resolved
// strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
UpdateClassLoaderVisitor visitor(space, class_loader.Get());
for (const ClassTable::TableSlot& root : temp_set) {
visitor(root.Read());
}
- // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
- // In this case, madvise away the dex cache arrays section of the image to reduce RAM usage and
- // mark as PROT_NONE to catch any invalid accesses.
- if (forward_dex_cache_arrays) {
- const ImageSection& dex_cache_section = header.GetDexCacheArraysSection();
- uint8_t* section_begin = AlignUp(space->Begin() + dex_cache_section.Offset(), kPageSize);
- uint8_t* section_end = AlignDown(space->Begin() + dex_cache_section.End(), kPageSize);
- if (section_begin < section_end) {
- madvise(section_begin, section_end - section_begin, MADV_DONTNEED);
- mprotect(section_begin, section_end - section_begin, PROT_NONE);
- VLOG(image) << "Released and protected dex cache array image section from "
- << reinterpret_cast<const void*>(section_begin) << "-"
- << reinterpret_cast<const void*>(section_end);
- }
- }
}
if (!oat_file->GetBssGcRoots().empty()) {
// Insert oat file to class table for visiting .bss GC roots.
@@ -7951,7 +7752,8 @@
// We have a valid method from the DexCache but we need to perform ICCE and IAE checks.
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get());
- DCHECK(klass != nullptr);
+ CHECK(klass != nullptr) << resolved->PrettyMethod() << " " << resolved << " "
+ << resolved->GetAccessFlags();
} else {
// The method was not in the DexCache, resolve the declaring class.
klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 07afedf..56573f5 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -632,6 +632,10 @@
}
}
+static bool IsAbsoluteLocation(const std::string& location) {
+ return !location.empty() && location[0] == '/';
+}
+
bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
ClassLoaderContext expected_context;
if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
@@ -673,18 +677,52 @@
DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
for (size_t k = 0; k < info.classpath.size(); k++) {
- if (info.classpath[k] != expected_info.classpath[k]) {
+ // Compute the dex location that must be compared.
+ // We shouldn't do a naive comparison `info.classpath[k] == expected_info.classpath[k]`
+ // because even if they refer to the same file, one could be encoded as a relative location
+ // and the other as an absolute one.
+ bool is_dex_name_absolute = IsAbsoluteLocation(info.classpath[k]);
+ bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_info.classpath[k]);
+ std::string dex_name;
+ std::string expected_dex_name;
+
+ if (is_dex_name_absolute == is_expected_dex_name_absolute) {
+ // If both locations are absolute or relative then compare them as they are.
+ // This is usually the case for: shared libraries and secondary dex files.
+ dex_name = info.classpath[k];
+ expected_dex_name = expected_info.classpath[k];
+ } else if (is_dex_name_absolute) {
+ // The runtime name is absolute but the compiled name (the expected one) is relative.
+ // This is the case for split apks which depend on base or on other splits.
+ dex_name = info.classpath[k];
+ expected_dex_name = OatFile::ResolveRelativeEncodedDexLocation(
+ info.classpath[k].c_str(), expected_info.classpath[k]);
+ } else {
+ // The runtime name is relative but the compiled name is absolute.
+ // There is no expected use case that would end up here as dex files are always loaded
+ // with their absolute location. However, be tolerant and do the best effort (in case
+ // there are unexpected new use case...).
+ DCHECK(is_expected_dex_name_absolute);
+ dex_name = OatFile::ResolveRelativeEncodedDexLocation(
+ expected_info.classpath[k].c_str(), info.classpath[k]);
+ expected_dex_name = expected_info.classpath[k];
+ }
+
+ // Compare the locations.
+ if (dex_name != expected_dex_name) {
LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
<< ". expected=" << expected_info.classpath[k]
<< ", found=" << info.classpath[k]
<< " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
+
+ // Compare the checksums.
if (info.checksums[k] != expected_info.checksums[k]) {
LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
- << ". expected=" << expected_info.checksums[k]
- << ", found=" << info.checksums[k]
- << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+ << ". expected=" << expected_info.checksums[k]
+ << ", found=" << info.checksums[k]
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
}
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index ddbb73b..1847274 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -697,7 +697,17 @@
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
- ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")));
+ std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
+ ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir));
+
+ std::string dex_location = GetTestDexFileName("ForClassLoaderA");
+ size_t pos = dex_location.rfind('/');
+ ASSERT_NE(std::string::npos, pos);
+ std::string parent = dex_location.substr(0, pos);
+
+ std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
+ ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
+ ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir));
}
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) {
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index f6f20ba..2f63dff 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -46,13 +46,10 @@
DexCacheArraysLayout layout(image_pointer_size, dex_file);
uint8_t* raw_arrays = nullptr;
- const OatDexFile* const oat_dex = dex_file->GetOatDexFile();
- if (oat_dex != nullptr && oat_dex->GetDexCacheArrays() != nullptr) {
- raw_arrays = oat_dex->GetDexCacheArrays();
- } else if (dex_file->NumStringIds() != 0u ||
- dex_file->NumTypeIds() != 0u ||
- dex_file->NumMethodIds() != 0u ||
- dex_file->NumFieldIds() != 0u) {
+ if (dex_file->NumStringIds() != 0u ||
+ dex_file->NumTypeIds() != 0u ||
+ dex_file->NumMethodIds() != 0u ||
+ dex_file->NumFieldIds() != 0u) {
static_assert(ArenaAllocator::kAlignment == 8, "Expecting arena alignment of 8.");
DCHECK(layout.Alignment() == 8u || layout.Alignment() == 16u);
// Zero-initialized.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 5c63dca..80e6ad3 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1498,13 +1498,21 @@
break;
case LockWord::kThinLocked:
owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
+ DCHECK(owner_ != nullptr) << "Thin-locked without owner!";
entry_count_ = 1 + lock_word.ThinLockCount();
// Thin locks have no waiters.
break;
case LockWord::kFatLocked: {
Monitor* mon = lock_word.FatLockMonitor();
owner_ = mon->owner_;
- entry_count_ = 1 + mon->lock_count_;
+ // Here it is okay for the owner to be null since we don't reset the LockWord back to
+ // kUnlocked until we get a GC. In cases where this hasn't happened yet we will have a fat
+ // lock without an owner.
+ if (owner_ != nullptr) {
+ entry_count_ = 1 + mon->lock_count_;
+ } else {
+ DCHECK_EQ(mon->lock_count_, 0) << "Monitor is fat-locked without any owner!";
+ }
for (Thread* waiter = mon->wait_set_; waiter != nullptr; waiter = waiter->GetWaitNext()) {
waiters_.push_back(waiter);
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 1d79ed6..ab7c42e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- // Last oat version changed reason: Add dex section layout info to header.
- static constexpr uint8_t kOatVersion[] = { '1', '3', '2', '\0' };
+ // Last oat version changed reason: Remove DexCache arrays from .bss.
+ static constexpr uint8_t kOatVersion[] = { '1', '3', '3', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index d9cfa53..200681e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -55,7 +55,6 @@
#include "type_lookup_table.h"
#include "utf-inl.h"
#include "utils.h"
-#include "utils/dex_cache_arrays_layout-inl.h"
#include "vdex_file.h"
namespace art {
@@ -279,36 +278,6 @@
return true;
}
-static bool FindDexFileMapItem(const uint8_t* dex_begin,
- const uint8_t* dex_end,
- DexFile::MapItemType map_item_type,
- const DexFile::MapItem** result_item) {
- *result_item = nullptr;
-
- const DexFile::Header* header =
- BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end);
- if (nullptr == header) return false;
-
- if (!DexFile::IsMagicValid(header->magic_)) return true; // Not a dex file, not an error.
-
- const DexFile::MapList* map_list =
- BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end);
- if (nullptr == map_list) return false;
-
- const DexFile::MapItem* map_item = map_list->list_;
- size_t count = map_list->size_;
- while (count--) {
- if (map_item->type_ == static_cast<uint16_t>(map_item_type)) {
- *result_item = map_item;
- break;
- }
- map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end);
- if (nullptr == map_item) return false;
- }
-
- return true;
-}
-
bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -361,7 +330,7 @@
(bss_roots_ != nullptr && (bss_roots_ < bss_begin_ || bss_roots_ > bss_end_)) ||
(bss_methods_ != nullptr && bss_roots_ != nullptr && bss_methods_ > bss_roots_)) {
*error_msg = StringPrintf("In oat file '%s' found bss symbol(s) outside .bss or unordered: "
- "begin = %p, methods_ = %p, roots = %p, end = %p",
+ "begin = %p, methods = %p, roots = %p, end = %p",
GetLocation().c_str(),
bss_begin_,
bss_methods_,
@@ -370,11 +339,15 @@
return false;
}
- uint8_t* after_arrays = (bss_methods_ != nullptr) ? bss_methods_ : bss_roots_; // May be null.
- uint8_t* dex_cache_arrays = (bss_begin_ == after_arrays) ? nullptr : bss_begin_;
- uint8_t* dex_cache_arrays_end =
- (bss_begin_ == after_arrays) ? nullptr : (after_arrays != nullptr) ? after_arrays : bss_end_;
- DCHECK_EQ(dex_cache_arrays != nullptr, dex_cache_arrays_end != nullptr);
+ if (bss_methods_ != nullptr && bss_methods_ != bss_begin_) {
+ *error_msg = StringPrintf("In oat file '%s' found unexpected .bss gap before 'oatbssmethods': "
+ "begin = %p, methods = %p",
+ GetLocation().c_str(),
+ bss_begin_,
+ bss_methods_);
+ return false;
+ }
+
uint32_t dex_file_count = GetOatHeader().GetDexFileCount();
oat_dex_files_storage_.reserve(dex_file_count);
for (size_t i = 0; i < dex_file_count; i++) {
@@ -609,37 +582,6 @@
reinterpret_cast<const DexFile::Header*>(dex_file_pointer)->method_ids_size_);
}
- uint8_t* current_dex_cache_arrays = nullptr;
- if (dex_cache_arrays != nullptr) {
- // All DexCache types except for CallSite have their instance counts in the
- // DexFile header. For CallSites, we need to read the info from the MapList.
- const DexFile::MapItem* call_sites_item = nullptr;
- if (!FindDexFileMapItem(DexBegin(),
- DexEnd(),
- DexFile::MapItemType::kDexTypeCallSiteIdItem,
- &call_sites_item)) {
- *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map",
- GetLocation().c_str());
- return false;
- }
- size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_;
- DexCacheArraysLayout layout(pointer_size, *header, num_call_sites);
- if (layout.Size() != 0u) {
- if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) {
- *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with "
- "truncated dex cache arrays, %zu < %zu.",
- GetLocation().c_str(),
- i,
- dex_file_location.c_str(),
- static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays),
- layout.Size());
- return false;
- }
- current_dex_cache_arrays = dex_cache_arrays;
- dex_cache_arrays += layout.Size();
- }
- }
-
std::string canonical_location = DexFile::GetDexCanonicalLocation(dex_file_location.c_str());
// Create the OatDexFile and add it to the owning container.
@@ -651,7 +593,6 @@
lookup_table_data,
method_bss_mapping,
class_offsets_pointer,
- current_dex_cache_arrays,
dex_layout_sections);
oat_dex_files_storage_.push_back(oat_dex_file);
@@ -664,14 +605,6 @@
}
}
- if (dex_cache_arrays != dex_cache_arrays_end) {
- // We expect the bss section to be either empty (dex_cache_arrays and bss_end_
- // both null) or contain just the dex cache arrays and optionally some GC roots.
- *error_msg = StringPrintf("In oat file '%s' found unexpected bss size bigger by %zu bytes.",
- GetLocation().c_str(),
- static_cast<size_t>(bss_end_ - dex_cache_arrays));
- return false;
- }
return true;
}
@@ -1379,7 +1312,6 @@
const uint8_t* lookup_table_data,
const MethodBssMapping* method_bss_mapping_data,
const uint32_t* oat_class_offsets_pointer,
- uint8_t* dex_cache_arrays,
const DexLayoutSections* dex_layout_sections)
: oat_file_(oat_file),
dex_file_location_(dex_file_location),
@@ -1389,7 +1321,6 @@
lookup_table_data_(lookup_table_data),
method_bss_mapping_(method_bss_mapping_data),
oat_class_offsets_pointer_(oat_class_offsets_pointer),
- dex_cache_arrays_(dex_cache_arrays),
dex_layout_sections_(dex_layout_sections) {
// Initialize TypeLookupTable.
if (lookup_table_data_ != nullptr) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 9a7fe51..e13126b 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -422,10 +422,6 @@
// Returns the offset to the OatClass information. Most callers should use GetOatClass.
uint32_t GetOatClassOffset(uint16_t class_def_index) const;
- uint8_t* GetDexCacheArrays() const {
- return dex_cache_arrays_;
- }
-
const uint8_t* GetLookupTableData() const {
return lookup_table_data_;
}
@@ -470,7 +466,6 @@
const uint8_t* lookup_table_data,
const MethodBssMapping* method_bss_mapping,
const uint32_t* oat_class_offsets_pointer,
- uint8_t* dex_cache_arrays,
const DexLayoutSections* dex_layout_sections);
static void AssertAotCompiler();
@@ -483,7 +478,6 @@
const uint8_t* const lookup_table_data_ = nullptr;
const MethodBssMapping* const method_bss_mapping_ = nullptr;
const uint32_t* const oat_class_offsets_pointer_ = 0u;
- uint8_t* const dex_cache_arrays_ = nullptr;
mutable std::unique_ptr<TypeLookupTable> lookup_table_;
const DexLayoutSections* const dex_layout_sections_ = nullptr;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a8ccf89..a67a6aa 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -417,6 +417,12 @@
return;
}
Thread* self = Thread::Current();
+
+ // Dump all threads first and then the aborting thread. While this is counter the logical flow,
+ // it improves the chance of relevant data surviving in the Android logs.
+
+ DumpAllThreads(os, self);
+
if (self == nullptr) {
os << "(Aborting thread was not attached to runtime!)\n";
DumpKernelStack(os, GetTid(), " kernel: ", false);
@@ -432,7 +438,6 @@
}
}
}
- DumpAllThreads(os, self);
}
// No thread-safety analysis as we do explicitly test for holding the mutator lock.
diff --git a/test/088-monitor-verification/src/Main.java b/test/088-monitor-verification/src/Main.java
index 13a96c7..f5cbc2a 100644
--- a/test/088-monitor-verification/src/Main.java
+++ b/test/088-monitor-verification/src/Main.java
@@ -39,6 +39,7 @@
ensureJitCompiled(Main.class, "constantLock");
ensureJitCompiled(Main.class, "notExcessiveNesting");
ensureJitCompiled(Main.class, "notNested");
+ ensureJitCompiled(TwoPath.class, "twoPath");
Main m = new Main();
diff --git a/test/1930-monitor-info/expected.txt b/test/1930-monitor-info/expected.txt
new file mode 100644
index 0000000..b43f1b2
--- /dev/null
+++ b/test/1930-monitor-info/expected.txt
@@ -0,0 +1,31 @@
+Running with single thread.
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Running with single thread in native.
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Lock twice
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice native
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice Java then native
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice native then Java
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+lock with wait
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [] }
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Wait for notify.
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [Test1930 Thread - testLockWait] }
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
diff --git a/test/1930-monitor-info/info.txt b/test/1930-monitor-info/info.txt
new file mode 100644
index 0000000..8e19edc
--- /dev/null
+++ b/test/1930-monitor-info/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetObjectMonitorUsage function works correctly.
diff --git a/test/1930-monitor-info/monitor.cc b/test/1930-monitor-info/monitor.cc
new file mode 100644
index 0000000..7f97c05
--- /dev/null
+++ b/test/1930-monitor-info/monitor.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1930MonitorInfo {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1930_executeLockedNative(JNIEnv* env,
+ jclass klass,
+ jobject run,
+ jobject l) {
+ ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable"));
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ jmethodID method = env->GetMethodID(runnable.get(), "run", "()V");
+
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ jmethodID printMethod = env->GetStaticMethodID(klass, "printPreLock", "(Ljava/lang/Object;)V");
+ if (env->ExceptionCheck()) {
+ return;
+ }
+
+ env->CallStaticVoidMethod(klass, printMethod, l);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ if (env->MonitorEnter(l) != 0) {
+ return;
+ }
+ env->CallVoidMethod(run, method);
+ env->MonitorExit(l);
+}
+
+} // namespace Test1930MonitorInfo
+} // namespace art
diff --git a/test/1930-monitor-info/run b/test/1930-monitor-info/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1930-monitor-info/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1930-monitor-info/src/Main.java b/test/1930-monitor-info/src/Main.java
new file mode 100644
index 0000000..3328461
--- /dev/null
+++ b/test/1930-monitor-info/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1930.run();
+ }
+}
diff --git a/test/1930-monitor-info/src/art/Monitors.java b/test/1930-monitor-info/src/art/Monitors.java
new file mode 100644
index 0000000..26f7718
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Monitors.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public class Monitors {
+ public static class NamedLock {
+ public final String name;
+ public NamedLock(String name) {
+ this.name = name;
+ }
+ public String toString() {
+ return String.format("NamedLock[%s]", name);
+ }
+ }
+
+ public static final class MonitorUsage {
+ public final Object monitor;
+ public final Thread owner;
+ public final int entryCount;
+ public final Thread[] waiters;
+ public final Thread[] notifyWaiters;
+
+ public MonitorUsage(
+ Object monitor,
+ Thread owner,
+ int entryCount,
+ Thread[] waiters,
+ Thread[] notifyWaiters) {
+ this.monitor = monitor;
+ this.entryCount = entryCount;
+ this.owner = owner;
+ this.waiters = waiters;
+ this.notifyWaiters = notifyWaiters;
+ }
+
+ private static String toNameList(Thread[] ts) {
+ return Arrays.toString(Arrays.stream(ts).map((Thread t) -> t.getName()).toArray());
+ }
+
+ public String toString() {
+ return String.format(
+ "MonitorUsage{ monitor: %s, owner: %s, entryCount: %d, waiters: %s, notify_waiters: %s }",
+ monitor,
+ (owner != null) ? owner.getName() : "<NULL>",
+ entryCount,
+ toNameList(waiters),
+ toNameList(notifyWaiters));
+ }
+ }
+
+ public static native MonitorUsage getObjectMonitorUsage(Object monitor);
+}
+
diff --git a/test/1930-monitor-info/src/art/Test1930.java b/test/1930-monitor-info/src/art/Test1930.java
new file mode 100644
index 0000000..a7fa1c7
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Test1930.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+
+public class Test1930 {
+ public static final int NUM_RETRY = 100;
+ private static void testSingleThread() {
+ Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread");
+ executeLocked(() -> { printMonitorUsage(lk); }, lk);
+ }
+ private static void testSingleThreadNative() {
+ Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread");
+ executeLockedNative(() -> { printMonitorUsage(lk); }, lk);
+ }
+
+ private static void testLockedTwice() {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwice");
+ executeLocked(() -> { executeLocked(() -> { printMonitorUsage(lk); }, lk); }, lk);
+ }
+
+ private static void testLockedTwiceNJ() {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNJ");
+ executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+ }
+
+ private static void testLockedTwiceJN() {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceJN");
+ executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+ }
+
+ private static void testLockedTwiceNative() {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNative");
+ executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+ }
+
+ public final static class ThreadSignaler {
+ public volatile boolean signal = false;
+ }
+
+ private static void testLockWait() throws Exception {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockWait");
+ final Semaphore sem = new Semaphore(0);
+ final Thread t = new Thread(() -> {
+ sem.release();
+ synchronized (lk) {
+ printMonitorUsage(lk);
+ }
+ }, "Test1930 Thread - testLockWait");
+ synchronized (lk) {
+ t.start();
+ // Wait for the other thread to actually start.
+ sem.acquire();
+ // Wait for the other thread to go to sleep trying to get the mutex. This might take a (short)
+ // time since we try spinning first for better performance.
+ boolean found_wait = false;
+ for (long i = 0; i < NUM_RETRY; i++) {
+ if (Arrays.asList(Monitors.getObjectMonitorUsage(lk).waiters).contains(t)) {
+ found_wait = true;
+ break;
+ } else {
+ Thread.sleep(500);
+ Thread.yield();
+ }
+ }
+ if (!found_wait) {
+ System.out.println("other thread doesn't seem to be waiting.");
+ }
+ printMonitorUsage(lk);
+ }
+ t.join();
+ printMonitorUsage(lk);
+ }
+
+ private static void testNotifyWait() throws Exception {
+ final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testNotifyWait");
+ final Semaphore sem = new Semaphore(0);
+ Thread t = new Thread(() -> {
+ synchronized (lk) {
+ printMonitorUsage(lk);
+ sem.release();
+ try {
+ lk.wait();
+ } catch (Exception e) {
+ throw new Error("Error waiting!", e);
+ }
+ printMonitorUsage(lk);
+ }
+ }, "Test1930 Thread - testLockWait");
+ t.start();
+ sem.acquire();
+ synchronized (lk) {
+ printMonitorUsage(lk);
+ lk.notifyAll();
+ }
+ t.join();
+ printMonitorUsage(lk);
+ }
+
+ public static void run() throws Exception {
+ // Single threaded tests.
+ System.out.println("Running with single thread.");
+ testSingleThread();
+ System.out.println("Running with single thread in native.");
+ testSingleThreadNative();
+ System.out.println("Lock twice");
+ testLockedTwice();
+ System.out.println("Lock twice native");
+ testLockedTwiceNative();
+ System.out.println("Lock twice Java then native");
+ testLockedTwiceJN();
+ System.out.println("Lock twice native then Java");
+ testLockedTwiceNJ();
+
+ // Mutli threaded tests.
+ System.out.println("lock with wait");
+ testLockWait();
+ System.out.println("Wait for notify.");
+ testNotifyWait();
+ }
+
+ public static void printPreLock(Object lock) {
+ System.out.println(String.format("Pre-lock[%s]: %s",
+ Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(lock)));
+ }
+
+ public static void executeLocked(Runnable r, Object lock) {
+ printPreLock(lock);
+ synchronized (lock) {
+ r.run();
+ }
+ }
+
+ public native static void executeLockedNative(Runnable r, Object m);
+ public static void printMonitorUsage(Object m) {
+ System.out.println(String.format("Thread[%s]: %s",
+ Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(m)));
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 2a88af1..2f23056 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -250,6 +250,7 @@
"ti-agent/common_helper.cc",
"ti-agent/frame_pop_helper.cc",
"ti-agent/locals_helper.cc",
+ "ti-agent/monitors_helper.cc",
"ti-agent/redefinition_helper.cc",
"ti-agent/suspension_helper.cc",
"ti-agent/stack_trace_helper.cc",
@@ -299,7 +300,8 @@
"1922-owned-monitors-info/owned_monitors.cc",
"1924-frame-pop-toggle/frame_pop_toggle.cc",
"1926-missed-frame-pop/frame_pop_missed.cc",
- "1927-exception-event/exception_event.cc"
+ "1927-exception-event/exception_event.cc",
+ "1930-monitor-info/monitor.cc",
],
shared_libs: [
"libbase",
@@ -349,6 +351,7 @@
],
shared_libs: [
"libbase",
+ "slicer",
],
header_libs: ["libopenjdkjvmti_headers"],
}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 90e2600..c16c487 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -422,10 +422,7 @@
if [[ "$JVMTI_REDEFINE_STRESS" = "y" ]]; then
# We really cannot do this on RI so don't both passing it in that case.
if [[ "$USE_JVM" = "n" ]]; then
- file_1=$(mktemp --tmpdir=${DEX_LOCATION})
- file_2=$(mktemp --tmpdir=${DEX_LOCATION})
- # TODO Remove need for DEXTER_BINARY!
- agent_args="${agent_args},redefine,${DEXTER_BINARY},${file_1},${file_2}"
+ agent_args="${agent_args},redefine"
fi
fi
if [[ "$JVMTI_FIELD_STRESS" = "y" ]]; then
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
index 7280102..c290e9b 100644
--- a/test/ti-agent/jvmti_helper.cc
+++ b/test/ti-agent/jvmti_helper.cc
@@ -50,7 +50,7 @@
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 0,
.can_get_current_contended_monitor = 0,
- .can_get_monitor_info = 0,
+ .can_get_monitor_info = 1,
.can_pop_frame = 0,
.can_redefine_classes = 1,
.can_signal_thread = 0,
diff --git a/test/ti-agent/monitors_helper.cc b/test/ti-agent/monitors_helper.cc
new file mode 100644
index 0000000..7c28ede
--- /dev/null
+++ b/test/ti-agent/monitors_helper.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include "jni.h"
+#include "jvmti.h"
+#include <vector>
+#include "jvmti_helper.h"
+#include "jni_helper.h"
+#include "test_env.h"
+#include "scoped_local_ref.h"
+namespace art {
+namespace common_monitors {
+
+extern "C" JNIEXPORT jobject JNICALL Java_art_Monitors_getObjectMonitorUsage(
+ JNIEnv* env, jclass, jobject obj) {
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Monitors$MonitorUsage"));
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jmethodID constructor = env->GetMethodID(
+ klass.get(),
+ "<init>",
+ "(Ljava/lang/Object;Ljava/lang/Thread;I[Ljava/lang/Thread;[Ljava/lang/Thread;)V");
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jvmtiMonitorUsage usage;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetObjectMonitorUsage(obj, &usage))) {
+ return nullptr;
+ }
+ jobjectArray wait = CreateObjectArray(env, usage.waiter_count, "java/lang/Thread",
+ [&](jint i) { return usage.waiters[i]; });
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters));
+ return nullptr;
+ }
+ jobjectArray notify_wait = CreateObjectArray(env, usage.notify_waiter_count, "java/lang/Thread",
+ [&](jint i) { return usage.notify_waiters[i]; });
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters));
+ return nullptr;
+ }
+ return env->NewObject(klass.get(), constructor,
+ obj, usage.owner, usage.entry_count, wait, notify_wait);
+}
+
+} // namespace common_monitors
+} // namespace art
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index 5d7c2f3..6e29e36 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -28,15 +28,31 @@
#include "jvmti.h"
#include "utils.h"
+#pragma clang diagnostic push
+// slicer defines its own CHECK. b/65422458
+#pragma push_macro("CHECK")
+#undef CHECK
+
+// Slicer's headers have code that triggers these warnings. b/65298177
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wsign-compare"
+#include "code_ir.h"
+#include "control_flow_graph.h"
+#include "dex_ir.h"
+#include "dex_ir_builder.h"
+#include "instrumentation.h"
+#include "reader.h"
+#include "writer.h"
+
+#pragma pop_macro("CHECK")
+#pragma clang diagnostic pop
+
namespace art {
// Should we do a 'full_rewrite' with this test?
static constexpr bool kDoFullRewrite = true;
struct StressData {
- std::string dexter_cmd;
- std::string out_temp_dex;
- std::string in_temp_dex;
bool vm_class_loader_initialized;
bool trace_stress;
bool redefine_stress;
@@ -44,51 +60,60 @@
bool step_stress;
};
-static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
- std::ofstream file(fname, std::ios::binary | std::ios::out | std::ios::trunc);
- file.write(reinterpret_cast<const char*>(data), data_len);
- file.flush();
-}
-
-static bool ReadIntoBuffer(const std::string& fname, /*out*/std::vector<unsigned char>* data) {
- std::ifstream file(fname, std::ios::binary | std::ios::in);
- file.seekg(0, std::ios::end);
- size_t len = file.tellg();
- data->resize(len);
- file.seekg(0);
- file.read(reinterpret_cast<char*>(data->data()), len);
- return len != 0;
-}
-
-// TODO rewrite later.
-static bool DoExtractClassFromData(StressData* data,
- const std::string& class_name,
+static bool DoExtractClassFromData(jvmtiEnv* env,
+ const std::string& descriptor,
jint in_len,
const unsigned char* in_data,
- /*out*/std::vector<unsigned char>* dex) {
- // Write the dex file into a temporary file.
- WriteToFile(data->in_temp_dex, in_len, in_data);
- // Clear out file so even if something suppresses the exit value we will still detect dexter
- // failure.
- WriteToFile(data->out_temp_dex, 0, nullptr);
- // Have dexter do the extraction.
- std::vector<std::string> args;
- args.push_back(data->dexter_cmd);
- if (kDoFullRewrite) {
- args.push_back("-x");
- args.push_back("full_rewrite");
- }
- args.push_back("-e");
- args.push_back(class_name);
- args.push_back("-o");
- args.push_back(data->out_temp_dex);
- args.push_back(data->in_temp_dex);
- std::string error;
- if (ExecAndReturnCode(args, &error) != 0) {
- LOG(ERROR) << "unable to execute dexter: " << error;
+ /*out*/jint* out_len,
+ /*out*/unsigned char** out_data) {
+ dex::Reader reader(in_data, in_len);
+ dex::u4 class_idx = reader.FindClassIndex(descriptor.c_str());
+ if (class_idx != dex::kNoIndex) {
+ reader.CreateClassIr(class_idx);
+ } else {
+ LOG(ERROR) << "ERROR: Can't find class " << descriptor;
return false;
}
- return ReadIntoBuffer(data->out_temp_dex, dex);
+ auto dex_ir = reader.GetIr();
+
+ if (kDoFullRewrite) {
+ for (auto& ir_method : dex_ir->encoded_methods) {
+ if (ir_method->code != nullptr) {
+ lir::CodeIr code_ir(ir_method.get(), dex_ir);
+ lir::ControlFlowGraph cfg_compact(&code_ir, false);
+ lir::ControlFlowGraph cfg_verbose(&code_ir, true);
+ code_ir.Assemble();
+ }
+ }
+ }
+ dex::Writer writer(dex_ir);
+
+ struct Allocator : public dex::Writer::Allocator {
+ explicit Allocator(jvmtiEnv* jvmti_env) : jvmti_env_(jvmti_env) {}
+ virtual void* Allocate(size_t size) {
+ unsigned char* out = nullptr;
+ if (JVMTI_ERROR_NONE != jvmti_env_->Allocate(size, &out)) {
+ return nullptr;
+ } else {
+ return out;
+ }
+ }
+ virtual void Free(void* ptr) {
+ jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
+ }
+ private:
+ jvmtiEnv* jvmti_env_;
+ };
+ Allocator alloc(env);
+ size_t res_len;
+ unsigned char* res = writer.CreateImage(&alloc, &res_len);
+ if (res != nullptr) {
+ *out_data = res;
+ *out_len = res_len;
+ return true;
+ } else {
+ return false;
+ }
}
class ScopedThreadInfo {
@@ -615,10 +640,10 @@
jint* new_class_data_len,
unsigned char** new_class_data) {
std::vector<unsigned char> out;
- std::string name_str(name);
- // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner
- // classes).
- std::replace(name_str.begin(), name_str.end(), '/', '.');
+ // Make the jvmti semi-descriptor into the full descriptor.
+ std::string name_str("L");
+ name_str += name;
+ name_str += ";";
StressData* data = nullptr;
CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
JVMTI_ERROR_NONE);
@@ -626,15 +651,11 @@
LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
<< "initialized. Transforming this class could cause spurious test failures.";
return;
- } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) {
+ } else if (DoExtractClassFromData(jvmti, name_str, class_data_len, class_data,
+ /*out*/ new_class_data_len, /*out*/ new_class_data)) {
LOG(INFO) << "Extracted class: " << name;
- unsigned char* new_data;
- CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data));
- memcpy(new_data, out.data(), out.size());
- *new_class_data_len = static_cast<jint>(out.size());
- *new_class_data = new_data;
} else {
- std::cerr << "Unable to extract class " << name_str << std::endl;
+ std::cerr << "Unable to extract class " << name << std::endl;
*new_class_data_len = 0;
*new_class_data = nullptr;
}
@@ -653,7 +674,7 @@
}
// Options are
-// jvmti-stress,[redefine,${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2},][trace,][field]
+// jvmti-stress,[redefine,][trace,][field]
static void ReadOptions(StressData* data, char* options) {
std::string ops(options);
CHECK_EQ(GetOption(ops), "jvmti-stress") << "Options should start with jvmti-stress";
@@ -668,12 +689,6 @@
data->field_stress = true;
} else if (cur == "redefine") {
data->redefine_stress = true;
- ops = AdvanceOption(ops);
- data->dexter_cmd = GetOption(ops);
- ops = AdvanceOption(ops);
- data->in_temp_dex = GetOption(ops);
- ops = AdvanceOption(ops);
- data->out_temp_dex = GetOption(ops);
} else {
LOG(FATAL) << "Unknown option: " << GetOption(ops);
}
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 5806b61..d27b8fc 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -11,9 +11,11 @@
names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
"libcore.icu.TransliteratorTest#testAll",
"libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
+ "libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157",
"libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
"libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout",
"libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
+ "libcore.javax.crypto.CipherBasicsTest#testBasicEncryption",
"org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]
}
]