Merge "Exercise sun.misc.Unsafe.compareAndSwapObject."
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index f3e1cc3..45b6490 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -61,6 +61,9 @@
# Do you want tracing tests run?
ART_TEST_TRACE ?= $(ART_TEST_FULL)
+# Do you want tracing tests (streaming mode) run?
+ART_TEST_TRACE_STREAM ?= $(ART_TEST_FULL)
+
# Do you want tests with GC verification enabled run?
ART_TEST_GC_VERIFY ?= $(ART_TEST_FULL)
diff --git a/compiler/dex/type_inference.cc b/compiler/dex/type_inference.cc
index a0dfcbe..c93fe20 100644
--- a/compiler/dex/type_inference.cc
+++ b/compiler/dex/type_inference.cc
@@ -572,15 +572,21 @@
if (type_conflict) {
/*
- * We don't normally expect to see a Dalvik register definition used both as a
- * floating point and core value, though technically it could happen with constants.
- * Until we have proper typing, detect this situation and disable register promotion
- * (which relies on the distinction between core a fp usages).
+ * Each dalvik register definition should be used either as a reference, or an
+ * integer or a floating point value. We don't normally expect to see a Dalvik
+ * register definition used in two or three of these roles though technically it
+ * could happen with constants (0 for all three roles, non-zero for integer and
+ * FP). Detect this situation and disable optimizations that rely on correct
+ * typing, i.e. register promotion, GVN/LVN and GVN-based DCE.
*/
LOG(WARNING) << PrettyMethod(cu_->method_idx, *cu_->dex_file)
<< " has type conflict block for sreg " << conflict_s_reg
<< ", disabling register promotion.";
- cu_->disable_opt |= (1 << kPromoteRegs);
+ cu_->disable_opt |=
+ (1u << kPromoteRegs) |
+ (1u << kGlobalValueNumbering) |
+ (1u << kGvnDeadCodeElimination) |
+ (1u << kLocalValueNumbering);
}
}
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 742429c..bde2c70 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -467,11 +467,11 @@
code_ptr += instruction.SizeInCodeUnits();
}
+ // Add Exit to the exit block.
+ exit_block_->AddInstruction(new (arena_) HExit());
// Add the suspend check to the entry block.
entry_block_->AddInstruction(new (arena_) HSuspendCheck(0));
entry_block_->AddInstruction(new (arena_) HGoto());
- // Add Exit to the exit block.
- exit_block_->AddInstruction(new (arena_) HExit());
// Iterate over blocks covered by TryItems and insert TryBoundaries at entry
// and exit points. This requires all control-flow instructions and
@@ -2266,17 +2266,21 @@
} else {
// An Invoke/FilledNewArray and its MoveResult could have landed in
// different blocks if there was a try/catch block boundary between
- // them. We need to insert the StoreLocal after the result definition
- // but before any potential Temporaries.
+ // them. For Invoke, we insert a StoreLocal after the instruction. For
+ // FilledNewArray, the local needs to be updated after the array was
+ // filled, otherwise we might overwrite an input vreg.
HStoreLocal* update_local =
new (arena_) HStoreLocal(GetLocalAt(instruction.VRegA()), latest_result_);
HBasicBlock* block = latest_result_->GetBlock();
- if (latest_result_->IsInvoke()) {
- block->InsertInstructionAfter(update_local, latest_result_);
+ if (block == current_block_) {
+ // MoveResult and the previous instruction are in the same block.
+ current_block_->AddInstruction(update_local);
} else {
- DCHECK(latest_result_->IsNewArray());
- DCHECK(latest_result_->GetNext()->IsTemporary());
- block->InsertInstructionAfter(update_local, latest_result_->GetNext());
+ // The two instructions are in different blocks. Insert the MoveResult
+ // before the final control-flow instruction of the previous block.
+ DCHECK(block->EndsWithControlFlowInstruction());
+ DCHECK(current_block_->GetInstructions().IsEmpty());
+ block->InsertInstructionBefore(update_local, block->GetLastInstruction());
}
latest_result_ = nullptr;
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index a96ecbd..1d10293 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -138,7 +138,7 @@
: HGraphVisitor(graph), codegen_(codegen) {}
#define DECLARE_VISIT_INSTRUCTION(name, super) \
- void Visit##name(H##name* instr);
+ void Visit##name(H##name* instr) OVERRIDE;
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
@@ -168,7 +168,7 @@
InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen);
#define DECLARE_VISIT_INSTRUCTION(name, super) \
- void Visit##name(H##name* instr);
+ void Visit##name(H##name* instr) OVERRIDE;
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index c724a21..ae7c568 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -146,12 +146,18 @@
: HGraphVisitor(graph), codegen_(codegen) {}
#define DECLARE_VISIT_INSTRUCTION(name, super) \
- void Visit##name(H##name* instr);
+ void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
private:
void HandleInvoke(HInvoke* invoke);
void HandleBinaryOp(HBinaryOperation* operation);
@@ -171,12 +177,18 @@
InstructionCodeGeneratorMIPS64(HGraph* graph, CodeGeneratorMIPS64* codegen);
#define DECLARE_VISIT_INSTRUCTION(name, super) \
- void Visit##name(H##name* instr);
+ void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
Mips64Assembler* GetAssembler() const { return assembler_; }
private:
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f2e9e22..95ea966 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -964,6 +964,8 @@
#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M)
+#define FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(M)
+
#define FOR_EACH_CONCRETE_INSTRUCTION_X86(M)
#define FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
@@ -972,6 +974,7 @@
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \
FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \
FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(M) \
FOR_EACH_CONCRETE_INSTRUCTION_X86(M) \
FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 78d3116..f3c111f 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1100,6 +1100,8 @@
.cfi_adjust_cfa_offset -32
ret
+ .cfi_adjust_cfa_offset 32 // Reset unwind info so following code unwinds.
+
.Lthrow_class_cast_exception:
// Restore
ldr xLR, [sp, #24]
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index bee379e..98d0812 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1105,6 +1105,8 @@
addl LITERAL(12), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-12)
ret
+
+ CFI_ADJUST_CFA_OFFSET(12) // Reset unwind info so following code unwinds.
1:
POP eax // pop arguments
POP ecx
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 5c741a5..259cf97 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1134,6 +1134,8 @@
CFI_ADJUST_CFA_OFFSET(-16)
ret
+
+ CFI_ADJUST_CFA_OFFSET(16 + 4 * 8) // Reset unwind info so following code unwinds.
1:
RESTORE_FP_CALLEE_SAVE_FRAME
POP rsi // Pop arguments
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index aa91ca1..678d55b 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -80,6 +80,8 @@
kMarkSweepMarkStackLock,
kInternTableLock,
kOatFileSecondaryLookupLock,
+ kTracingUniqueMethodsLock,
+ kTracingStreamingLock,
kDefaultMutexLevel,
kMarkSweepLargeObjectLock,
kPinTableLock,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7936dd3..23c5942 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2314,22 +2314,47 @@
// Class::VisitFieldRoots may miss some fields or methods.
ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);
// Load static fields.
+ // We allow duplicate definitions of the same field in a class_data_item
+ // but ignore the repeated indexes here, b/21868015.
ClassDataItemIterator it(dex_file, class_data);
- const size_t num_sfields = it.NumStaticFields();
- ArtField* sfields = num_sfields != 0 ? AllocArtFieldArray(self, num_sfields) : nullptr;
- for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
- CHECK_LT(i, num_sfields);
- LoadField(it, klass, &sfields[i]);
+ ArtField* sfields =
+ it.NumStaticFields() != 0 ? AllocArtFieldArray(self, it.NumStaticFields()) : nullptr;
+ size_t num_sfields = 0;
+ uint32_t last_field_idx = 0u;
+ for (; it.HasNextStaticField(); it.Next()) {
+ uint32_t field_idx = it.GetMemberIndex();
+ DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier.
+ if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) {
+ DCHECK_LT(num_sfields, it.NumStaticFields());
+ LoadField(it, klass, &sfields[num_sfields]);
+ ++num_sfields;
+ last_field_idx = field_idx;
+ }
}
klass->SetSFields(sfields);
klass->SetNumStaticFields(num_sfields);
DCHECK_EQ(klass->NumStaticFields(), num_sfields);
// Load instance fields.
- const size_t num_ifields = it.NumInstanceFields();
- ArtField* ifields = num_ifields != 0 ? AllocArtFieldArray(self, num_ifields) : nullptr;
- for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
- CHECK_LT(i, num_ifields);
- LoadField(it, klass, &ifields[i]);
+ ArtField* ifields =
+ it.NumInstanceFields() != 0 ? AllocArtFieldArray(self, it.NumInstanceFields()) : nullptr;
+ size_t num_ifields = 0u;
+ last_field_idx = 0u;
+ for (; it.HasNextInstanceField(); it.Next()) {
+ uint32_t field_idx = it.GetMemberIndex();
+ DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier.
+ if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) {
+ DCHECK_LT(num_ifields, it.NumInstanceFields());
+ LoadField(it, klass, &ifields[num_ifields]);
+ ++num_ifields;
+ last_field_idx = field_idx;
+ }
+ }
+ if (UNLIKELY(num_sfields != it.NumStaticFields()) ||
+ UNLIKELY(num_ifields != it.NumInstanceFields())) {
+ LOG(WARNING) << "Duplicate fields in class " << PrettyDescriptor(klass.Get())
+ << " (unique static fields: " << num_sfields << "/" << it.NumStaticFields()
+ << ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")";
+ // NOTE: Not shrinking the over-allocated sfields/ifields.
}
klass->SetIFields(ifields);
klass->SetNumInstanceFields(num_ifields);
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index c1c7123..d30fac4 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -1113,9 +1113,8 @@
void ClassDataItemIterator::ReadClassDataField() {
field_.field_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_);
field_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
- if (last_idx_ != 0 && field_.field_idx_delta_ == 0) {
- LOG(WARNING) << "Duplicate field in " << dex_file_.GetLocation();
- }
+ // The user of the iterator is responsible for checking if there
+ // are unordered or duplicate indexes.
}
void ClassDataItemIterator::ReadClassDataMethod() {
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index c5b9f65..11921f4 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -107,9 +107,10 @@
GcRoot<mirror::Class>& klass = record->GetClassGcRoot();
// This does not need a read barrier because this is called by GC.
mirror::Object* old_object = klass.Read<kWithoutReadBarrier>();
- mirror::Object* new_object = callback(old_object, arg);
+ mirror::Object* new_object = UNLIKELY(old_object == nullptr) ?
+ nullptr : callback(old_object, arg);
if (UNLIKELY(old_object != new_object)) {
- mirror::Class* new_klass = (UNLIKELY(new_object == nullptr) ? nullptr : new_object->AsClass());
+ mirror::Class* new_klass = UNLIKELY(new_object == nullptr) ? nullptr : new_object->AsClass();
klass = GcRoot<mirror::Class>(new_klass);
}
}
@@ -129,7 +130,7 @@
// This does not need a read barrier because this is called by GC.
mirror::Object* old_object = it->first.Read<kWithoutReadBarrier>();
AllocRecord* record = it->second;
- mirror::Object* new_object = callback(old_object, arg);
+ mirror::Object* new_object = old_object == nullptr ? nullptr : callback(old_object, arg);
if (new_object == nullptr) {
if (count > delete_bound) {
it->first = GcRoot<mirror::Object>(nullptr);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index cb36183..cc176b7 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -554,15 +554,16 @@
return soa.AddLocalReference<jobject>(decoded_obj);
}
- static void DeleteLocalRef(JNIEnv* env, jobject obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ static void DeleteLocalRef(JNIEnv* env, jobject obj) {
if (obj == nullptr) {
return;
}
- IndirectReferenceTable& locals = reinterpret_cast<JNIEnvExt*>(env)->locals;
-
- uint32_t cookie = reinterpret_cast<JNIEnvExt*>(env)->local_ref_cookie;
- if (!locals.Remove(cookie, obj)) {
+ // SOA is only necessary to have exclusion between GC root marking and removing.
+ // We don't want to have the GC attempt to mark a null root if we just removed
+ // it. b/22119403
+ ScopedObjectAccess soa(env);
+ auto* ext_env = down_cast<JNIEnvExt*>(env);
+ if (!ext_env->locals.Remove(ext_env->local_ref_cookie, obj)) {
// Attempting to delete a local reference that is not in the
// topmost local reference frame is a no-op. DeleteLocalRef returns
// void and doesn't throw any exceptions, but we should probably
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 5a43b56..487baed 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -427,7 +427,7 @@
// Do not try to erase, so flush and close explicitly.
if (flush_file) {
if (the_trace->trace_file_->Flush() != 0) {
- PLOG(ERROR) << "Could not flush trace file.";
+ PLOG(WARNING) << "Could not flush trace file.";
}
} else {
the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard.
@@ -581,7 +581,7 @@
buffer_size_(std::max(kMinBufSize, buffer_size)),
start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()), cur_offset_(0),
overflow_(false), interval_us_(0), streaming_lock_(nullptr),
- unique_methods_lock_(new Mutex("unique methods lock")) {
+ unique_methods_lock_(new Mutex("unique methods lock", kTracingUniqueMethodsLock)) {
uint16_t trace_version = GetTraceVersion(clock_source_);
if (output_mode == TraceOutputMode::kStreaming) {
trace_version |= 0xF0U;
@@ -603,13 +603,14 @@
if (output_mode == TraceOutputMode::kStreaming) {
streaming_file_name_ = trace_name;
- streaming_lock_ = new Mutex("tracing lock");
+ streaming_lock_ = new Mutex("tracing lock", LockLevel::kTracingStreamingLock);
seen_threads_.reset(new ThreadIDBitSet());
}
}
Trace::~Trace() {
delete streaming_lock_;
+ delete unique_methods_lock_;
}
static uint64_t ReadBytes(uint8_t* buf, size_t bytes) {
@@ -634,13 +635,15 @@
}
static void GetVisitedMethodsFromBitSets(
- const std::map<mirror::DexCache*, DexIndexBitSet*>& seen_methods,
+ const std::map<const DexFile*, DexIndexBitSet*>& seen_methods,
std::set<ArtMethod*>* visited_methods) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
for (auto& e : seen_methods) {
DexIndexBitSet* bit_set = e.second;
+ mirror::DexCache* dex_cache = class_linker->FindDexCache(*e.first);
for (uint32_t i = 0; i < bit_set->size(); ++i) {
if ((*bit_set)[i]) {
- visited_methods->insert(e.first->GetResolvedMethod(i, sizeof(void*)));
+ visited_methods->insert(dex_cache->GetResolvedMethod(i, sizeof(void*)));
}
}
}
@@ -819,15 +822,16 @@
bool Trace::RegisterMethod(ArtMethod* method) {
mirror::DexCache* dex_cache = method->GetDexCache();
+ const DexFile* dex_file = dex_cache->GetDexFile();
auto* resolved_method = dex_cache->GetResolvedMethod(method->GetDexMethodIndex(), sizeof(void*));
if (resolved_method != method) {
DCHECK(resolved_method == nullptr);
dex_cache->SetResolvedMethod(method->GetDexMethodIndex(), method, sizeof(void*));
}
- if (seen_methods_.find(dex_cache) == seen_methods_.end()) {
- seen_methods_.insert(std::make_pair(dex_cache, new DexIndexBitSet()));
+ if (seen_methods_.find(dex_file) == seen_methods_.end()) {
+ seen_methods_.insert(std::make_pair(dex_file, new DexIndexBitSet()));
}
- DexIndexBitSet* bit_set = seen_methods_.find(dex_cache)->second;
+ DexIndexBitSet* bit_set = seen_methods_.find(dex_file)->second;
if (!(*bit_set)[method->GetDexMethodIndex()]) {
bit_set->set(method->GetDexMethodIndex());
return true;
diff --git a/runtime/trace.h b/runtime/trace.h
index 7bc495a..69e6acc 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -35,12 +35,9 @@
namespace art {
-namespace mirror {
- class DexCache;
-} // namespace mirror
-
class ArtField;
class ArtMethod;
+class DexFile;
class Thread;
using DexIndexBitSet = std::bitset<65536>;
@@ -126,15 +123,18 @@
// Stop tracing. This will finish the trace and write it to file/send it via DDMS.
static void Stop()
- LOCKS_EXCLUDED(Locks::mutator_lock_,
- Locks::thread_list_lock_,
- Locks::trace_lock_);
+ LOCKS_EXCLUDED(Locks::mutator_lock_,
+ Locks::thread_list_lock_,
+ Locks::trace_lock_);
// Abort tracing. This will just stop tracing and *not* write/send the collected data.
static void Abort()
LOCKS_EXCLUDED(Locks::mutator_lock_,
Locks::thread_list_lock_,
Locks::trace_lock_);
- static void Shutdown() LOCKS_EXCLUDED(Locks::trace_lock_);
+ static void Shutdown()
+ LOCKS_EXCLUDED(Locks::mutator_lock_,
+ Locks::thread_list_lock_,
+ Locks::trace_lock_);
static TracingMode GetMethodTracingMode() LOCKS_EXCLUDED(Locks::trace_lock_);
bool UseWallClock();
@@ -188,7 +188,10 @@
// The sampling interval in microseconds is passed as an argument.
static void* RunSamplingThread(void* arg) LOCKS_EXCLUDED(Locks::trace_lock_);
- static void StopTracing(bool finish_tracing, bool flush_file);
+ static void StopTracing(bool finish_tracing, bool flush_file)
+ LOCKS_EXCLUDED(Locks::mutator_lock_,
+ Locks::thread_list_lock_,
+ Locks::trace_lock_);
void FinishTracing() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void ReadClocks(Thread* thread, uint32_t* thread_clock_diff, uint32_t* wall_clock_diff);
@@ -279,12 +282,12 @@
// Streaming mode data.
std::string streaming_file_name_;
Mutex* streaming_lock_;
- std::map<mirror::DexCache*, DexIndexBitSet*> seen_methods_;
+ std::map<const DexFile*, DexIndexBitSet*> seen_methods_;
std::unique_ptr<ThreadIDBitSet> seen_threads_;
// Bijective map from ArtMethod* to index.
// Map from ArtMethod* to index in unique_methods_;
- Mutex* unique_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ Mutex* unique_methods_lock_ ACQUIRED_AFTER(streaming_lock_);
std::unordered_map<ArtMethod*, uint32_t> art_method_id_map_ GUARDED_BY(unique_methods_lock_);
std::vector<ArtMethod*> unique_methods_ GUARDED_BY(unique_methods_lock_);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 02929e8..8a8b455 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -385,7 +385,7 @@
bool allow_thread_suspension)
: self_(self),
reg_types_(can_load_classes),
- work_insn_idx_(-1),
+ work_insn_idx_(DexFile::kDexNoIndex),
dex_method_idx_(dex_method_idx),
mirror_method_(method),
method_access_flags_(method_access_flags),
@@ -409,7 +409,8 @@
has_check_casts_(false),
has_virtual_or_interface_invokes_(false),
verify_to_dump_(verify_to_dump),
- allow_thread_suspension_(allow_thread_suspension) {
+ allow_thread_suspension_(allow_thread_suspension),
+ link_(nullptr) {
self->PushVerifier(this);
DCHECK(class_def != nullptr);
}
@@ -600,12 +601,16 @@
// We need to save the work_line if the instruction wasn't throwing before. Otherwise we'll
// try to merge garbage.
// Note: this assumes that Fail is called before we do any work_line modifications.
- const uint16_t* insns = code_item_->insns_ + work_insn_idx_;
- const Instruction* inst = Instruction::At(insns);
- int opcode_flags = Instruction::FlagsOf(inst->Opcode());
+ // Note: this can fail before we touch any instruction, for the signature of a method. So
+ // add a check.
+ if (work_insn_idx_ < DexFile::kDexNoIndex) {
+ const uint16_t* insns = code_item_->insns_ + work_insn_idx_;
+ const Instruction* inst = Instruction::At(insns);
+ int opcode_flags = Instruction::FlagsOf(inst->Opcode());
- if ((opcode_flags & Instruction::kThrow) == 0 && CurrentInsnFlags()->IsInTry()) {
- saved_line_->CopyFromLine(work_line_.get());
+ if ((opcode_flags & Instruction::kThrow) == 0 && CurrentInsnFlags()->IsInTry()) {
+ saved_line_->CopyFromLine(work_line_.get());
+ }
}
}
break;
@@ -1237,6 +1242,9 @@
PrependToLastFailMessage(prepend);
return false;
}
+ // We may have a runtime failure here, clear.
+ have_pending_runtime_throw_failure_ = false;
+
/* Perform code flow verification. */
if (!CodeFlowVerifyMethod()) {
DCHECK_NE(failures_.size(), 0U);
diff --git a/test/510-checker-try-catch/smali/Builder.smali b/test/510-checker-try-catch/smali/Builder.smali
index a388aa1..f300b21 100644
--- a/test/510-checker-try-catch/smali/Builder.smali
+++ b/test/510-checker-try-catch/smali/Builder.smali
@@ -835,12 +835,15 @@
## CHECK-START: int[] Builder.testMoveResult_FilledNewArray(int, int, int) builder (after)
-## CHECK: <<Res:l\d+>> NewArray
-## CHECK-NEXT: Temporary
-## CHECK-NEXT: StoreLocal [v0,<<Res>>]
-## CHECK: ArraySet [<<Res>>,{{i\d+}},{{i\d+}}]
-## CHECK: ArraySet [<<Res>>,{{i\d+}},{{i\d+}}]
-## CHECK: ArraySet [<<Res>>,{{i\d+}},{{i\d+}}]
+## CHECK: <<Res:l\d+>> NewArray
+## CHECK-NEXT: Temporary
+## CHECK-NEXT: <<Local1:i\d+>> LoadLocal [v0]
+## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local1>>]
+## CHECK-NEXT: <<Local2:i\d+>> LoadLocal [v1]
+## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local2>>]
+## CHECK-NEXT: <<Local3:i\d+>> LoadLocal [v2]
+## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local3>>]
+## CHECK-NEXT: StoreLocal [v0,<<Res>>]
.method public static testMoveResult_FilledNewArray(III)[I
.registers 3
@@ -872,7 +875,6 @@
return-void
:try_end
.catchall {:try_start .. :try_end} :catch_all
-
.end method
# Test case for Return inside a try block. Builder needs to move it outside the
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 938cf5d..7059b6b 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -26,4 +26,5 @@
b/22045582 (wide)
b/21886894
b/22080519
+b/21645819
Done!
diff --git a/test/800-smali/smali/b_21645819.smali b/test/800-smali/smali/b_21645819.smali
new file mode 100644
index 0000000..195d662
--- /dev/null
+++ b/test/800-smali/smali/b_21645819.smali
@@ -0,0 +1,9 @@
+.class public LB21645819;
+.super Ljava/lang/Object;
+
+# The method declares a parameter of an inaccessible class. This should not abort/kill us.
+
+.method public static run(Lpkg/ProtectedClass;)V
+.registers 10
+ return-void
+.end method
\ No newline at end of file
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index cc194d5..30c1b14 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -99,6 +99,8 @@
null));
testCases.add(new TestCase("b/22080519", "B22080519", "run", null,
new NullPointerException(), null));
+ testCases.add(new TestCase("b/21645819", "B21645819", "run", new Object[] { null },
+ null, null));
}
public void runTests() {
diff --git a/test/800-smali/src/pkg/ProtectedClass.java b/test/800-smali/src/pkg/ProtectedClass.java
new file mode 100644
index 0000000..b262155
--- /dev/null
+++ b/test/800-smali/src/pkg/ProtectedClass.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2014 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 pkg;
+
+class ProtectedClass {
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 29c3ea9..6c2ce62 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -104,6 +104,9 @@
ifeq ($(ART_TEST_TRACE),true)
TRACE_TYPES += trace
endif
+ifeq ($(ART_TEST_TRACE_STREAM),true)
+ TRACE_TYPES += stream
+endif
GC_TYPES := cms
ifeq ($(ART_TEST_GC_STRESS),true)
GC_TYPES += gcstress
@@ -313,9 +316,9 @@
137-cfi \
802-deoptimization
-ifneq (,$(filter trace,$(TRACE_TYPES)))
+ifneq (,$(filter trace stream,$(TRACE_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
- $(COMPILER_TYPES),$(RELOCATE_TYPES),trace,$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),trace stream,$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
endif
@@ -671,7 +674,13 @@
ifeq ($(6),ntrace)
test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_TRACE_RULES
else
- $$(error found $(6) expected $(TRACE_TYPES))
+ ifeq ($(6),stream)
+ # Group streaming under normal tracing rules.
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_TRACE_RULES
+ run_test_options += --trace --stream
+ else
+ $$(error found $(6) expected $(TRACE_TYPES))
+ endif
endif
endif
ifeq ($(7),gcverify)
diff --git a/test/run-test b/test/run-test
index ed03321..995d30f 100755
--- a/test/run-test
+++ b/test/run-test
@@ -92,6 +92,7 @@
build_only="no"
suffix64=""
trace="false"
+trace_stream="false"
basic_verify="false"
gc_verify="false"
gc_stress="false"
@@ -268,6 +269,9 @@
elif [ "x$1" = "x--trace" ]; then
trace="true"
shift
+ elif [ "x$1" = "x--stream" ]; then
+ trace_stream="true"
+ shift
elif [ "x$1" = "x--always-clean" ]; then
always_clean="yes"
shift
@@ -307,7 +311,19 @@
run_args="${run_args} --runtime-option -Xgc:SS --runtime-option -Xms2m --runtime-option -Xmx2m"
fi
if [ "$trace" = "true" ]; then
- run_args="${run_args} --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin --runtime-option -Xmethod-trace-file-size:2000000"
+ run_args="${run_args} --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file-size:2000000"
+ if [ "$trace_stream" = "true" ]; then
+ # Streaming mode uses the file size as the buffer size. So output gets really large. Drop
+ # the ability to analyze the file and just write to /dev/null.
+ run_args="${run_args} --runtime-option -Xmethod-trace-file:/dev/null"
+ # Enable streaming mode.
+ run_args="${run_args} --runtime-option -Xmethod-trace-stream"
+ else
+ run_args="${run_args} --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin"
+ fi
+elif [ "$trace_stream" = "true" ]; then
+ echo "Cannot use --stream without --trace."
+ exit 1
fi
# Most interesting target architecture variables are Makefile variables, not environment variables.
@@ -473,6 +489,7 @@
"files."
echo " --64 Run the test in 64-bit mode"
echo " --trace Run with method tracing"
+ echo " --stream Run method tracing in streaming mode (requires --trace)"
echo " --gcstress Run with gc stress testing"
echo " --gcverify Run with gc verification"
echo " --always-clean Delete the test files even if the test fails."