Merge "Fix false alarm on thread suspend timeout"
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 7df71f5..96f8e0c 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -164,7 +164,7 @@
compiler_kind, instruction_set,
instruction_set_features_.get(),
true, new std::set<std::string>, nullptr,
- 2, true, true, timer_.get(), -1, ""));
+ 2, true, true, "", timer_.get(), -1, ""));
}
// We typically don't generate an image in unit tests, disable this optimization by default.
compiler_driver_->SetSupportBootImageFixup(false);
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index 36d065f..73b68a5 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -1040,9 +1040,8 @@
// Use LDREXD for the atomic load. (Expect displacement > 0, don't optimize for == 0.)
RegStorage r_ptr = AllocTemp();
OpRegRegImm(kOpAdd, r_ptr, r_base, displacement);
- LIR* lir = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg());
+ load = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg());
FreeTemp(r_ptr);
- return lir;
} else {
load = LoadBaseDispBody(r_base, displacement, r_dest, size);
}
@@ -1174,7 +1173,7 @@
GenMemBarrier(kAnyStore);
}
- LIR* store;
+ LIR* null_ck_insn;
if (is_volatile == kVolatile && (size == k64 || size == kDouble) &&
!cu_->compiler_driver->GetInstructionSetFeatures()->
AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd()) {
@@ -1191,17 +1190,16 @@
RegStorage r_temp = AllocTemp();
RegStorage r_temp_high = AllocTemp(false); // We may not have another temp.
if (r_temp_high.Valid()) {
- NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
+ null_ck_insn = NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
FreeTemp(r_temp_high);
FreeTemp(r_temp);
} else {
// If we don't have another temp, clobber r_ptr in LDREXD and reload it.
- NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg());
+ null_ck_insn = NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg());
FreeTemp(r_temp); // May need the temp for kOpAdd.
OpRegRegImm(kOpAdd, r_ptr, r_base, displacement);
}
- store = NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(),
- r_ptr.GetReg());
+ NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(), r_ptr.GetReg());
OpCmpImmBranch(kCondNe, r_temp, 0, fail_target);
FreeTemp(r_ptr);
} else {
@@ -1210,7 +1208,7 @@
size = k32;
}
- store = StoreBaseDispBody(r_base, displacement, r_src, size);
+ null_ck_insn = StoreBaseDispBody(r_base, displacement, r_src, size);
}
if (UNLIKELY(is_volatile == kVolatile)) {
@@ -1219,7 +1217,7 @@
GenMemBarrier(kAnyAny);
}
- return store;
+ return null_ck_insn;
}
LIR* ArmMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 3733507..d2b32b5 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -939,15 +939,15 @@
}
GenNullCheck(rl_obj.reg, opt_flags);
int field_offset = field_info.FieldOffset().Int32Value();
- LIR* store;
+ LIR* null_ck_insn;
if (IsRef(size)) {
- store = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
+ null_ck_insn = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
kVolatile : kNotVolatile);
} else {
- store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, size,
- field_info.IsVolatile() ? kVolatile : kNotVolatile);
+ null_ck_insn = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, size,
+ field_info.IsVolatile() ? kVolatile : kNotVolatile);
}
- MarkPossibleNullPointerExceptionAfter(opt_flags, store);
+ MarkPossibleNullPointerExceptionAfter(opt_flags, null_ck_insn);
if (IsRef(size) && !mir_graph_->IsConstantNullRef(rl_src)) {
MarkGCCard(opt_flags, rl_src.reg, rl_obj.reg);
}
diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc
index c48833b..4265ae1 100644
--- a/compiler/dex/quick/mips/assemble_mips.cc
+++ b/compiler/dex/quick/mips/assemble_mips.cc
@@ -482,7 +482,7 @@
if (!unconditional) {
hop_target = RawLIR(dalvik_offset, kPseudoTargetLabel);
LIR* hop_branch = RawLIR(dalvik_offset, opcode, lir->operands[0],
- lir->operands[1], 0, 0, 0, hop_target);
+ lir->operands[1], 0, 0, 0, hop_target);
InsertLIRBefore(lir, hop_branch);
}
LIR* curr_pc = RawLIR(dalvik_offset, kMipsCurrPC);
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 8f976df..9c3ce7b 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -216,7 +216,8 @@
void ConvertShortToLongBranch(LIR* lir);
RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2, bool is_div, int flags) OVERRIDE;
- RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) OVERRIDE;
+ RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div)
+ OVERRIDE;
};
} // namespace art
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 0778c3b..aabef60 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -172,7 +172,7 @@
if (r_dest.IsFloat() || r_src.IsFloat())
return OpFpRegCopy(r_dest, r_src);
LIR* res = RawLIR(current_dalvik_offset_, kMipsMove,
- r_dest.GetReg(), r_src.GetReg());
+ r_dest.GetReg(), r_src.GetReg());
if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
res->flags.is_nop = true;
}
@@ -194,7 +194,7 @@
if (src_fp) {
OpRegCopy(r_dest, r_src);
} else {
- /* note the operands are swapped for the mtc1 instr */
+ /* note the operands are swapped for the mtc1 instr */
NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg());
NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg());
}
@@ -240,7 +240,7 @@
}
RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2,
- bool is_div) {
+ bool is_div) {
NewLIR2(kMipsDiv, reg1.GetReg(), reg2.GetReg());
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
if (is_div) {
@@ -252,7 +252,7 @@
}
RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit,
- bool is_div) {
+ bool is_div) {
RegStorage t_reg = AllocTemp();
NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit);
NewLIR2(kMipsDiv, reg1.GetReg(), t_reg.GetReg());
@@ -501,7 +501,7 @@
* Generate array load
*/
void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_dest, int scale) {
+ RegLocation rl_index, RegLocation rl_dest, int scale) {
RegisterClass reg_class = RegClassBySize(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -570,7 +570,7 @@
*
*/
void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
RegisterClass reg_class = RegClassBySize(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -632,7 +632,7 @@
} else {
rl_src = LoadValue(rl_src, reg_class);
if (needs_range_check) {
- GenArrayBoundsCheck(rl_index.reg, reg_len);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
FreeTemp(reg_len);
}
StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index c22ba04..c819903 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -89,9 +89,9 @@
// Convert k64BitSolo into k64BitPair
RegStorage MipsMir2Lir::Solo64ToPair64(RegStorage reg) {
- DCHECK(reg.IsDouble());
- int reg_num = (reg.GetRegNum() & ~1) | RegStorage::kFloatingPoint;
- return RegStorage(RegStorage::k64BitPair, reg_num, reg_num + 1);
+ DCHECK(reg.IsDouble());
+ int reg_num = (reg.GetRegNum() & ~1) | RegStorage::kFloatingPoint;
+ return RegStorage(RegStorage::k64BitPair, reg_num, reg_num + 1);
}
// Return a target-dependent special register.
@@ -223,78 +223,78 @@
if (nc == '!') {
strcpy(tbuf, "!");
} else {
- DCHECK_LT(fmt, fmt_end);
- DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u);
- operand = lir->operands[nc-'0'];
- switch (*fmt++) {
- case 'b':
- strcpy(tbuf, "0000");
- for (i = 3; i >= 0; i--) {
- tbuf[i] += operand & 1;
- operand >>= 1;
- }
- break;
- case 's':
- snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
- break;
- case 'S':
- DCHECK_EQ(RegStorage::RegNum(operand) & 1, 0);
- snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
- break;
- case 'h':
- snprintf(tbuf, arraysize(tbuf), "%04x", operand);
- break;
- case 'M':
- case 'd':
- snprintf(tbuf, arraysize(tbuf), "%d", operand);
- break;
- case 'D':
- snprintf(tbuf, arraysize(tbuf), "%d", operand+1);
- break;
- case 'E':
- snprintf(tbuf, arraysize(tbuf), "%d", operand*4);
- break;
- case 'F':
- snprintf(tbuf, arraysize(tbuf), "%d", operand*2);
- break;
- case 't':
- snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)",
- reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1),
- lir->target);
- break;
- case 'T':
- snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2);
- break;
- case 'u': {
- int offset_1 = lir->operands[0];
- int offset_2 = NEXT_LIR(lir)->operands[0];
- uintptr_t target =
- (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) +
- (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc;
- snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast<void*>(target));
- break;
+ DCHECK_LT(fmt, fmt_end);
+ DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u);
+ operand = lir->operands[nc-'0'];
+ switch (*fmt++) {
+ case 'b':
+ strcpy(tbuf, "0000");
+ for (i = 3; i >= 0; i--) {
+ tbuf[i] += operand & 1;
+ operand >>= 1;
+ }
+ break;
+ case 's':
+ snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
+ break;
+ case 'S':
+ DCHECK_EQ(RegStorage::RegNum(operand) & 1, 0);
+ snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
+ break;
+ case 'h':
+ snprintf(tbuf, arraysize(tbuf), "%04x", operand);
+ break;
+ case 'M':
+ case 'd':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand);
+ break;
+ case 'D':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand+1);
+ break;
+ case 'E':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand*4);
+ break;
+ case 'F':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand*2);
+ break;
+ case 't':
+ snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)",
+ reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1),
+ lir->target);
+ break;
+ case 'T':
+ snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2);
+ break;
+ case 'u': {
+ int offset_1 = lir->operands[0];
+ int offset_2 = NEXT_LIR(lir)->operands[0];
+ uintptr_t target =
+ (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) +
+ (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc;
+ snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast<void*>(target));
+ break;
}
- /* Nothing to print for BLX_2 */
- case 'v':
- strcpy(tbuf, "see above");
- break;
- case 'r':
- DCHECK(operand >= 0 && operand < MIPS_REG_COUNT);
- strcpy(tbuf, mips_reg_name[operand]);
- break;
- case 'N':
- // Placeholder for delay slot handling
- strcpy(tbuf, "; nop");
- break;
- default:
- strcpy(tbuf, "DecodeError");
- break;
- }
- buf += tbuf;
+ /* Nothing to print for BLX_2 */
+ case 'v':
+ strcpy(tbuf, "see above");
+ break;
+ case 'r':
+ DCHECK(operand >= 0 && operand < MIPS_REG_COUNT);
+ strcpy(tbuf, mips_reg_name[operand]);
+ break;
+ case 'N':
+ // Placeholder for delay slot handling
+ strcpy(tbuf, "; nop");
+ break;
+ default:
+ strcpy(tbuf, "DecodeError");
+ break;
+ }
+ buf += tbuf;
}
} else {
- buf += *fmt++;
+ buf += *fmt++;
}
}
return buf;
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index adb9270..15fc69d 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -228,17 +228,17 @@
}
break;
case kOpLsl:
- DCHECK(value >= 0 && value <= 31);
- opcode = kMipsSll;
- break;
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSll;
+ break;
case kOpLsr:
- DCHECK(value >= 0 && value <= 31);
- opcode = kMipsSrl;
- break;
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSrl;
+ break;
case kOpAsr:
- DCHECK(value >= 0 && value <= 31);
- opcode = kMipsSra;
- break;
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSra;
+ break;
case kOpAnd:
if (IS_UIMM16((value))) {
opcode = kMipsAndi;
@@ -324,7 +324,7 @@
}
return res;
case kOp2Char:
- return NewLIR3(kMipsAndi, r_dest_src1.GetReg(), r_src2.GetReg(), 0xFFFF);
+ return NewLIR3(kMipsAndi, r_dest_src1.GetReg(), r_src2.GetReg(), 0xFFFF);
default:
LOG(FATAL) << "Bad case in OpRegReg";
UNREACHABLE();
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9985d66..e371807 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -339,7 +339,8 @@
const InstructionSetFeatures* instruction_set_features,
bool image, std::set<std::string>* image_classes,
std::set<std::string>* compiled_classes, size_t thread_count,
- bool dump_stats, bool dump_passes, CumulativeLogger* timer,
+ bool dump_stats, bool dump_passes,
+ const std::string& dump_cfg_file_name, CumulativeLogger* timer,
int swap_fd, const std::string& profile_file)
: swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())),
@@ -361,6 +362,7 @@
stats_(new AOTCompilationStats),
dump_stats_(dump_stats),
dump_passes_(dump_passes),
+ dump_cfg_file_name_(dump_cfg_file_name),
timings_logger_(timer),
compiler_context_(nullptr),
support_boot_image_fixup_(instruction_set != kMips),
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 7ddc32c..11b4329 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -97,6 +97,7 @@
bool image, std::set<std::string>* image_classes,
std::set<std::string>* compiled_classes,
size_t thread_count, bool dump_stats, bool dump_passes,
+ const std::string& dump_cfg_file_name,
CumulativeLogger* timer, int swap_fd,
const std::string& profile_file);
@@ -371,6 +372,10 @@
return dump_passes_;
}
+ const std::string& GetDumpCfgFileName() const {
+ return dump_cfg_file_name_;
+ }
+
CumulativeLogger* GetTimingsLogger() const {
return timings_logger_;
}
@@ -542,6 +547,7 @@
bool dump_stats_;
const bool dump_passes_;
+ const std::string& dump_cfg_file_name_;
CumulativeLogger* const timings_logger_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index c30cc04..a02e25e 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -106,36 +106,37 @@
// All libcore references should resolve
ScopedObjectAccess soa(Thread::Current());
- const DexFile* dex = java_lang_dex_file_;
- mirror::DexCache* dex_cache = class_linker_->FindDexCache(*dex);
- EXPECT_EQ(dex->NumStringIds(), dex_cache->NumStrings());
+ ASSERT_TRUE(java_lang_dex_file_ != NULL);
+ const DexFile& dex = *java_lang_dex_file_;
+ mirror::DexCache* dex_cache = class_linker_->FindDexCache(dex);
+ EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings());
for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
const mirror::String* string = dex_cache->GetResolvedString(i);
EXPECT_TRUE(string != NULL) << "string_idx=" << i;
}
- EXPECT_EQ(dex->NumTypeIds(), dex_cache->NumResolvedTypes());
+ EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes());
for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
mirror::Class* type = dex_cache->GetResolvedType(i);
EXPECT_TRUE(type != NULL) << "type_idx=" << i
- << " " << dex->GetTypeDescriptor(dex->GetTypeId(i));
+ << " " << dex.GetTypeDescriptor(dex.GetTypeId(i));
}
- EXPECT_EQ(dex->NumMethodIds(), dex_cache->NumResolvedMethods());
+ EXPECT_EQ(dex.NumMethodIds(), dex_cache->NumResolvedMethods());
for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
mirror::ArtMethod* method = dex_cache->GetResolvedMethod(i);
EXPECT_TRUE(method != NULL) << "method_idx=" << i
- << " " << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
- << " " << dex->GetMethodName(dex->GetMethodId(i));
+ << " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i))
+ << " " << dex.GetMethodName(dex.GetMethodId(i));
EXPECT_TRUE(method->GetEntryPointFromQuickCompiledCode() != NULL) << "method_idx=" << i
<< " "
- << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
- << " " << dex->GetMethodName(dex->GetMethodId(i));
+ << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i))
+ << " " << dex.GetMethodName(dex.GetMethodId(i));
}
- EXPECT_EQ(dex->NumFieldIds(), dex_cache->NumResolvedFields());
+ EXPECT_EQ(dex.NumFieldIds(), dex_cache->NumResolvedFields());
for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
mirror::ArtField* field = dex_cache->GetResolvedField(i);
EXPECT_TRUE(field != NULL) << "field_idx=" << i
- << " " << dex->GetFieldDeclaringClassDescriptor(dex->GetFieldId(i))
- << " " << dex->GetFieldName(dex->GetFieldId(i));
+ << " " << dex.GetFieldDeclaringClassDescriptor(dex.GetFieldId(i))
+ << " " << dex.GetFieldName(dex.GetFieldId(i));
}
// TODO check Class::IsVerified for all classes
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 281e3fe..f513ea8 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -973,6 +973,9 @@
}
void JniCompilerTest::UpcallArgumentTypeChecking_InstanceImpl() {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
SetUpForTest(false, "instanceMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldTakeClass));
@@ -985,6 +988,9 @@
JNI_TEST(UpcallArgumentTypeChecking_Instance)
void JniCompilerTest::UpcallArgumentTypeChecking_StaticImpl() {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
SetUpForTest(true, "staticMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldTakeClass));
@@ -1475,6 +1481,9 @@
JNI_TEST(MaxParamNumber)
void JniCompilerTest::WithoutImplementationImpl() {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
SetUpForTest(false, "withoutImplementation", "()V", nullptr);
env_->CallVoidMethod(jobj_, jmethod_);
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index d141538..d3d2055 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -39,10 +39,10 @@
void CheckMethod(mirror::ArtMethod* method,
const OatFile::OatMethod& oat_method,
- const DexFile* dex_file)
+ const DexFile& dex_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(dex_file,
+ compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
method->GetDexMethodIndex()));
if (compiled_method == nullptr) {
@@ -92,7 +92,7 @@
method_inliner_map_.get(),
compiler_kind, insn_set,
insn_features.get(), false, nullptr, nullptr, 2, true,
- true, timer_.get(), -1, ""));
+ true, "", timer_.get(), -1, ""));
jobject class_loader = nullptr;
if (kCompile) {
TimingLogger timings2("OatTest::WriteRead", false, false);
@@ -130,22 +130,23 @@
ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin());
ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey)));
- const DexFile* dex_file = java_lang_dex_file_;
- uint32_t dex_file_checksum = dex_file->GetLocationChecksum();
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation().c_str(),
+ ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+ const DexFile& dex_file = *java_lang_dex_file_;
+ uint32_t dex_file_checksum = dex_file.GetLocationChecksum();
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
&dex_file_checksum);
ASSERT_TRUE(oat_dex_file != nullptr);
- CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
+ CHECK_EQ(dex_file.GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
ScopedObjectAccess soa(Thread::Current());
- for (size_t i = 0; i < dex_file->NumClassDefs(); i++) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
- const uint8_t* class_data = dex_file->GetClassData(class_def);
+ for (size_t i = 0; i < dex_file.NumClassDefs(); i++) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
size_t num_virtual_methods = 0;
if (class_data != nullptr) {
- ClassDataItemIterator it(*dex_file, class_data);
+ ClassDataItemIterator it(dex_file, class_data);
num_virtual_methods = it.NumVirtualMethods();
}
- const char* descriptor = dex_file->GetClassDescriptor(class_def);
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
StackHandleScope<1> hs(soa.Self());
mirror::Class* klass = class_linker->FindClass(soa.Self(), descriptor,
NullHandle<mirror::ClassLoader>());
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 0c1ff9b..9e89070 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -54,6 +54,7 @@
+ GetGraph()->GetTemporariesVRegSlots()
+ 1 /* filler */,
0, /* the baseline compiler does not have live registers at slow path */
+ 0, /* the baseline compiler does not have live registers at slow path */
GetGraph()->GetMaximumNumberOfOutVRegs()
+ 1 /* current method */);
GenerateFrameEntry();
@@ -136,14 +137,16 @@
}
void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots,
- size_t maximum_number_of_live_registers,
+ size_t maximum_number_of_live_core_registers,
+ size_t maximum_number_of_live_fp_registers,
size_t number_of_out_slots) {
first_register_slot_in_slow_path_ = (number_of_out_slots + number_of_spill_slots) * kVRegSize;
SetFrameSize(RoundUp(
number_of_spill_slots * kVRegSize
+ number_of_out_slots * kVRegSize
- + maximum_number_of_live_registers * GetWordSize()
+ + maximum_number_of_live_core_registers * GetWordSize()
+ + maximum_number_of_live_fp_registers * GetFloatingPointSpillSlotSize()
+ FrameEntrySpillSize(),
kStackAlignment));
}
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 8d28f3d..88e50b6 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -109,9 +109,11 @@
virtual HGraphVisitor* GetInstructionVisitor() = 0;
virtual Assembler* GetAssembler() = 0;
virtual size_t GetWordSize() const = 0;
+ virtual size_t GetFloatingPointSpillSlotSize() const = 0;
virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0;
void ComputeFrameSize(size_t number_of_spill_slots,
- size_t maximum_number_of_live_registers,
+ size_t maximum_number_of_live_core_registers,
+ size_t maximum_number_of_live_fp_registers,
size_t number_of_out_slots);
virtual size_t FrameEntrySpillSize() const = 0;
int32_t GetStackSlot(HLocal* local) const;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 4fd545f..1862061 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -49,6 +49,9 @@
static constexpr size_t kRuntimeParameterFpuRegistersLength =
arraysize(kRuntimeParameterFpuRegisters);
+static constexpr DRegister DTMP = D7;
+static constexpr SRegister STMP = S14;
+
class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> {
public:
InvokeRuntimeCallingConvention()
@@ -472,6 +475,11 @@
blocked_core_registers_[R10] = true;
blocked_core_registers_[R11] = true;
+ // Don't allocate our temporary double register.
+ blocked_fpu_registers_[STMP] = true;
+ blocked_fpu_registers_[STMP + 1] = true;
+ DCHECK_EQ(FromLowSToD(STMP), DTMP);
+
blocked_fpu_registers_[S16] = true;
blocked_fpu_registers_[S17] = true;
blocked_fpu_registers_[S18] = true;
@@ -3375,9 +3383,9 @@
} else if (source.IsStackSlot() && destination.IsStackSlot()) {
Exchange(source.GetStackIndex(), destination.GetStackIndex());
} else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
- __ vmovrs(IP, source.AsFpuRegister<SRegister>());
+ __ vmovs(STMP, source.AsFpuRegister<SRegister>());
__ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>());
- __ vmovsr(destination.AsFpuRegister<SRegister>(), IP);
+ __ vmovs(destination.AsFpuRegister<SRegister>(), STMP);
} else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>()
: destination.AsFpuRegister<SRegister>();
@@ -3385,11 +3393,33 @@
? destination.GetStackIndex()
: source.GetStackIndex();
- __ vmovrs(IP, reg);
+ __ vmovs(STMP, reg);
__ LoadSFromOffset(reg, SP, mem);
- __ StoreToOffset(kStoreWord, IP, SP, mem);
+ __ StoreSToOffset(STMP, SP, mem);
+ } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
+ __ vmovd(DTMP, FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
+ __ vmovd(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
+ FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()));
+ __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), DTMP);
+ } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
+ DRegister reg = source.IsFpuRegisterPair()
+ ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())
+ : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
+ int mem = source.IsFpuRegisterPair()
+ ? destination.GetStackIndex()
+ : source.GetStackIndex();
+
+ __ vmovd(DTMP, reg);
+ __ LoadDFromOffset(reg, SP, mem);
+ __ StoreDToOffset(DTMP, SP, mem);
+ } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
+ // TODO: We could use DTMP and ask for a pair scratch register (float or core).
+ // This would save four instructions if two scratch registers are available, and
+ // two instructions if not.
+ Exchange(source.GetStackIndex(), destination.GetStackIndex());
+ Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize));
} else {
- LOG(FATAL) << "Unimplemented";
+ LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
}
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 8b29b15..267d9a2 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -169,6 +169,11 @@
return kArmWordSize;
}
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+ // Allocated in S registers, which are word sized.
+ return kArmWordSize;
+ }
+
size_t FrameEntrySpillSize() const OVERRIDE;
HGraphVisitor* GetLocationBuilder() OVERRIDE {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index e4da07b..590bc1d 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -191,6 +191,11 @@
return kArm64WordSize;
}
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+ // Allocated in D registers, which are word sized.
+ return kArm64WordSize;
+ }
+
uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
vixl::Label* block_entry_label = GetLabelOf(block);
DCHECK(block_entry_label->IsBound());
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index acde122..2d8adb2 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -166,6 +166,11 @@
return kX86WordSize;
}
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+ // 8 bytes == 2 words for each spill.
+ return 2 * kX86WordSize;
+ }
+
size_t FrameEntrySpillSize() const OVERRIDE;
HGraphVisitor* GetLocationBuilder() OVERRIDE {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 87f6b0f..343fba3 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -169,6 +169,10 @@
return kX86_64WordSize;
}
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+ return kX86_64WordSize;
+ }
+
size_t FrameEntrySpillSize() const OVERRIDE;
HGraphVisitor* GetLocationBuilder() OVERRIDE {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 692d452..eaecbb0 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -149,11 +149,12 @@
// Enable C1visualizer output. Must be done in Init() because the compiler
// driver is not fully initialized when passed to the compiler's constructor.
CompilerDriver* driver = GetCompilerDriver();
- if (driver->GetDumpPasses()) {
+ const std::string cfg_file_name = driver->GetDumpCfgFileName();
+ if (!cfg_file_name.empty()) {
CHECK_EQ(driver->GetThreadCount(), 1U)
<< "Graph visualizer requires the compiler to run single-threaded. "
<< "Invoke the compiler with '-j1'.";
- visualizer_output_.reset(new std::ofstream("art.cfg"));
+ visualizer_output_.reset(new std::ofstream(cfg_file_name));
}
}
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 1e93ece..b8f5070 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -37,10 +37,12 @@
// Perform the moves with constant sources.
for (size_t i = 0; i < moves_.Size(); ++i) {
- const MoveOperands& move = *moves_.Get(i);
- if (!move.IsEliminated()) {
- DCHECK(move.GetSource().IsConstant());
+ MoveOperands* move = moves_.Get(i);
+ if (!move->IsEliminated()) {
+ DCHECK(move->GetSource().IsConstant());
EmitMove(i);
+ // Eliminate the move, in case following moves need a scratch register.
+ move->Eliminate();
}
}
diff --git a/compiler/optimizing/parallel_move_resolver.h b/compiler/optimizing/parallel_move_resolver.h
index 309425e..7ec1dd2 100644
--- a/compiler/optimizing/parallel_move_resolver.h
+++ b/compiler/optimizing/parallel_move_resolver.h
@@ -58,6 +58,9 @@
};
bool IsScratchLocation(Location loc);
+
+ // Allocate a scratch register for performing a move. The method will try to use
+ // a register that is the destination of a move, but that move has not been emitted yet.
int AllocateScratchRegister(int blocked, int if_scratch, int register_count, bool* spilled);
// Emit a move.
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 62629bc..210f7d7 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -31,9 +31,13 @@
if (!message_.str().empty()) {
message_ << " ";
}
- message_ << "("
- << move->GetSource().reg()
- << " -> "
+ message_ << "(";
+ if (move->GetSource().IsConstant()) {
+ message_ << "C";
+ } else {
+ message_ << move->GetSource().reg();
+ }
+ message_ << " -> "
<< move->GetDestination().reg()
<< ")";
}
@@ -129,4 +133,21 @@
}
}
+TEST(ParallelMoveTest, ConstantLast) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(new (&allocator) MoveOperands(
+ Location::ConstantLocation(new (&allocator) HIntConstant(0)),
+ Location::RegisterLocation(0),
+ nullptr));
+ moves->AddMove(new (&allocator) MoveOperands(
+ Location::RegisterLocation(1),
+ Location::RegisterLocation(2),
+ nullptr));
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(1 -> 2) (C -> 0)", resolver.GetMessage().c_str());
+}
+
} // namespace art
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 1efc52b..d2f4f9b 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -56,7 +56,8 @@
blocked_core_registers_(codegen->GetBlockedCoreRegisters()),
blocked_fp_registers_(codegen->GetBlockedFloatingPointRegisters()),
reserved_out_slots_(0),
- maximum_number_of_live_registers_(0) {
+ maximum_number_of_live_core_registers_(0),
+ maximum_number_of_live_fp_registers_(0) {
codegen->SetupBlockedRegisters();
physical_core_register_intervals_.SetSize(codegen->GetNumberOfCoreRegisters());
physical_fp_register_intervals_.SetSize(codegen->GetNumberOfFloatingPointRegisters());
@@ -185,9 +186,6 @@
}
LinearScan();
- size_t saved_maximum_number_of_live_registers = maximum_number_of_live_registers_;
- maximum_number_of_live_registers_ = 0;
-
inactive_.Reset();
active_.Reset();
handled_.Reset();
@@ -207,7 +205,6 @@
}
}
LinearScan();
- maximum_number_of_live_registers_ += saved_maximum_number_of_live_registers;
}
void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
@@ -602,8 +599,13 @@
if (current->IsSlowPathSafepoint()) {
// Synthesized interval to record the maximum number of live registers
// at safepoints. No need to allocate a register for it.
- maximum_number_of_live_registers_ =
- std::max(maximum_number_of_live_registers_, active_.Size());
+ if (processing_core_registers_) {
+ maximum_number_of_live_core_registers_ =
+ std::max(maximum_number_of_live_core_registers_, active_.Size());
+ } else {
+ maximum_number_of_live_fp_registers_ =
+ std::max(maximum_number_of_live_fp_registers_, active_.Size());
+ }
DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() > current->GetStart());
continue;
}
@@ -1255,8 +1257,9 @@
switch (source.GetKind()) {
case Location::kRegister: {
locations->AddLiveRegister(source);
- DCHECK_LE(locations->GetNumberOfLiveRegisters(), maximum_number_of_live_registers_);
-
+ DCHECK_LE(locations->GetNumberOfLiveRegisters(),
+ maximum_number_of_live_core_registers_ +
+ maximum_number_of_live_fp_registers_);
if (current->GetType() == Primitive::kPrimNot) {
locations->SetRegisterBit(source.reg());
}
@@ -1349,7 +1352,8 @@
void RegisterAllocator::Resolve() {
codegen_->ComputeFrameSize(
- spill_slots_.Size(), maximum_number_of_live_registers_, reserved_out_slots_);
+ spill_slots_.Size(), maximum_number_of_live_core_registers_,
+ maximum_number_of_live_fp_registers_, reserved_out_slots_);
// Adjust the Out Location of instructions.
// TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index c152a8b..70841b8 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -190,8 +190,11 @@
// Slots reserved for out arguments.
size_t reserved_out_slots_;
- // The maximum live registers at safepoints.
- size_t maximum_number_of_live_registers_;
+ // The maximum live core registers at safepoints.
+ size_t maximum_number_of_live_core_registers_;
+
+ // The maximum live FP registers at safepoints.
+ size_t maximum_number_of_live_fp_registers_;
ART_FRIEND_TEST(RegisterAllocatorTest, FreeUntil);
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 2b55120..6f8b301 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -29,6 +29,10 @@
namespace art {
+// If you want to take a look at the differences between the ART assembler and GCC, set this flag
+// to true. The disassembled files will then remain in the tmp directory.
+static constexpr bool kKeepDisassembledFiles = false;
+
// Helper for a constexpr string length.
constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1);
@@ -685,12 +689,12 @@
bool result = CompareFiles(data_name + ".dis", as_name + ".dis");
- // If you want to take a look at the differences between the ART assembler and GCC, comment
- // out the removal code.
-// std::remove(data_name.c_str());
-// std::remove(as_name.c_str());
-// std::remove((data_name + ".dis").c_str());
-// std::remove((as_name + ".dis").c_str());
+ if (!kKeepDisassembledFiles) {
+ std::remove(data_name.c_str());
+ std::remove(as_name.c_str());
+ std::remove((data_name + ".dis").c_str());
+ std::remove((as_name + ".dis").c_str());
+ }
return result;
}
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index b8d724d..e93f45c 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -330,7 +330,6 @@
assembler->shlq(*reg, shifter);
str << "shlq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n";
}
- printf("%s\n", str.str().c_str());
return str.str();
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 4b4675b..147700f 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -446,7 +446,15 @@
timings_(timings) {}
~Dex2Oat() {
- LogCompletionTime(); // Needs to be before since it accesses the runtime.
+ // Free opened dex files before deleting the runtime_, because ~DexFile
+ // uses MemMap, which is shut down by ~Runtime.
+ class_path_files_.clear();
+ opened_dex_files_.clear();
+
+ // Log completion time before deleting the runtime_, because this accesses
+ // the runtime.
+ LogCompletionTime();
+
if (kIsDebugBuild || (RUNNING_ON_VALGRIND != 0)) {
delete runtime_; // See field declaration for why this is manual.
}
@@ -660,6 +668,8 @@
dump_timing_ = true;
} else if (option == "--dump-passes") {
dump_passes_ = true;
+ } else if (option.starts_with("--dump-cfg=")) {
+ dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
} else if (option == "--dump-stats") {
dump_stats_ = true;
} else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
@@ -1108,18 +1118,24 @@
<< error_msg;
return false;
}
- if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &dex_files_)) {
+ if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &opened_dex_files_)) {
LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location_
<< "': " << error_msg;
return false;
}
+ for (auto& dex_file : opened_dex_files_) {
+ dex_files_.push_back(dex_file.get());
+ }
ATRACE_END();
} else {
- size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, dex_files_);
+ size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, &opened_dex_files_);
if (failure_count > 0) {
LOG(ERROR) << "Failed to open some dex files: " << failure_count;
return false;
}
+ for (auto& dex_file : opened_dex_files_) {
+ dex_files_.push_back(dex_file.get());
+ }
}
constexpr bool kSaveDexInput = false;
@@ -1193,9 +1209,13 @@
Thread* self = Thread::Current();
if (!boot_image_option_.empty()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- std::vector<const DexFile*> class_path_files(dex_files_);
- OpenClassPathFiles(runtime_->GetClassPathString(), class_path_files);
+ OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_);
ScopedObjectAccess soa(self);
+ std::vector<const DexFile*> class_path_files(dex_files_);
+ for (auto& class_path_file : class_path_files_) {
+ class_path_files.push_back(class_path_file.get());
+ }
+
for (size_t i = 0; i < class_path_files.size(); i++) {
class_linker->RegisterDexFile(*class_path_files[i]);
}
@@ -1218,6 +1238,7 @@
thread_count_,
dump_stats_,
dump_passes_,
+ dump_cfg_file_name_,
compiler_phases_timings_.get(),
swap_fd_,
profile_file_));
@@ -1446,7 +1467,8 @@
private:
static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
const std::vector<const char*>& dex_locations,
- std::vector<const DexFile*>& dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "OpenDexFiles out-param is NULL";
size_t failure_count = 0;
for (size_t i = 0; i < dex_filenames.size(); i++) {
const char* dex_filename = dex_filenames[i];
@@ -1457,7 +1479,7 @@
LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
continue;
}
- if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) {
+ if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) {
LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
++failure_count;
}
@@ -1477,10 +1499,12 @@
return false;
}
- // Appends to dex_files any elements of class_path that it doesn't already
- // contain. This will open those dex files as necessary.
+ // Appends to opened_dex_files any elements of class_path that dex_files
+ // doesn't already contain. This will open those dex files as necessary.
static void OpenClassPathFiles(const std::string& class_path,
- std::vector<const DexFile*>& dex_files) {
+ std::vector<const DexFile*> dex_files,
+ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
+ DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles out-param is NULL";
std::vector<std::string> parsed;
Split(class_path, ':', &parsed);
// Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained.
@@ -1490,7 +1514,7 @@
continue;
}
std::string error_msg;
- if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) {
+ if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, opened_dex_files)) {
LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
}
}
@@ -1630,6 +1654,9 @@
DexFileToMethodInlinerMap method_inliner_map_;
std::unique_ptr<QuickCompilerCallbacks> callbacks_;
+ // Ownership for the class path files.
+ std::vector<std::unique_ptr<const DexFile>> class_path_files_;
+
// Not a unique_ptr as we want to just exit on non-debug builds, not bringing the runtime down
// in an orderly fashion. The destructor takes care of deleting this.
Runtime* runtime_;
@@ -1662,12 +1689,14 @@
bool is_host_;
std::string android_root_;
std::vector<const DexFile*> dex_files_;
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
std::unique_ptr<CompilerDriver> driver_;
std::vector<std::string> verbose_methods_;
bool dump_stats_;
bool dump_passes_;
bool dump_timing_;
bool dump_slow_timing_;
+ std::string dump_cfg_file_name_;
std::string swap_file_name_;
int swap_fd_;
std::string profile_file_; // Profile file to use
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index de4ea36..931cca7 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1968,13 +1968,13 @@
ScopedObjectAccess soa(self);
ClassLinker* class_linker = runtime->GetClassLinker();
class_linker->RegisterOatFile(oat_file);
- std::vector<const DexFile*> dex_files;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) {
std::string error_msg;
- const DexFile* dex_file = odf->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> dex_file = odf->OpenDexFile(&error_msg);
CHECK(dex_file != nullptr) << error_msg;
class_linker->RegisterDexFile(*dex_file);
- dex_files.push_back(dex_file);
+ dex_files.push_back(std::move(dex_file));
}
// Need a class loader.
@@ -1983,7 +1983,11 @@
soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
// Fake that we're a compiler.
- runtime->SetCompileTimeClassPath(class_loader, dex_files);
+ std::vector<const DexFile*> class_path;
+ for (auto& dex_file : dex_files) {
+ class_path.push_back(dex_file.get());
+ }
+ runtime->SetCompileTimeClassPath(class_loader, class_path);
// Use the class loader while dumping.
StackHandleScope<1> scope(self);
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 285007c..986b7ec 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -799,6 +799,9 @@
}
TEST_F(StubTest, UnlockObject) {
+ // This will lead to monitor error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
TestUnlockObject(this);
}
@@ -992,6 +995,9 @@
TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ // This will lead to OOM error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
// TODO: Check the "Unresolved" allocation stubs
Thread* self = Thread::Current();
@@ -1116,6 +1122,9 @@
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
// TODO: Check the "Unresolved" allocation stubs
+ // This will lead to OOM error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
Thread* self = Thread::Current();
// Create an object
ScopedObjectAccess soa(self);
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index f3e0918..0764b87 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -303,4 +303,13 @@
#endif
}
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
+ old_ = gMinimumLogSeverity;
+ gMinimumLogSeverity = level;
+}
+
+ScopedLogSeverity::~ScopedLogSeverity() {
+ gMinimumLogSeverity = old_;
+}
+
} // namespace art
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index ae83e33..cc1a4a1 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -254,6 +254,16 @@
DISALLOW_COPY_AND_ASSIGN(LogMessage);
};
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+ explicit ScopedLogSeverity(LogSeverity level);
+ ~ScopedLogSeverity();
+
+ private:
+ LogSeverity old_;
+};
+
} // namespace art
#endif // ART_RUNTIME_BASE_LOGGING_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d119a56..377a3c3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -246,7 +246,7 @@
memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
}
-void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class_path) {
+void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) {
VLOG(startup) << "ClassLinker::Init";
CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it.";
@@ -405,9 +405,10 @@
// DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
// these roots.
CHECK_NE(0U, boot_class_path.size());
- for (const DexFile* dex_file : boot_class_path) {
- CHECK(dex_file != nullptr);
+ for (auto& dex_file : boot_class_path) {
+ CHECK(dex_file.get() != nullptr);
AppendToBootClassPath(self, *dex_file);
+ opened_dex_files_.push_back(std::move(dex_file));
}
// now we can use FindSystemClass
@@ -794,7 +795,7 @@
const uint32_t* dex_location_checksum,
bool generated,
std::vector<std::string>* error_msgs,
- std::vector<const DexFile*>* dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
if (oat_file == nullptr) {
return false;
}
@@ -841,12 +842,12 @@
}
if (success) {
- const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file == nullptr) {
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
success = false;
error_msgs->push_back(error_msg);
} else {
- dex_files->push_back(dex_file);
+ dex_files->push_back(std::move(dex_file));
}
}
@@ -864,14 +865,7 @@
if (success) {
return true;
} else {
- // Free all the dex files we have loaded.
- auto it = dex_files->begin() + old_size;
- auto it_end = dex_files->end();
- for (; it != it_end; it++) {
- delete *it;
- }
- dex_files->erase(dex_files->begin() + old_size, it_end);
-
+ dex_files->erase(dex_files->begin() + old_size, dex_files->end());
return false;
}
}
@@ -882,7 +876,7 @@
// multidex ahead of time.
bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
std::vector<std::string>* error_msgs,
- std::vector<const DexFile*>* dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
// 1) Check whether we have an open oat file.
// This requires a dex checksum, use the "primary" one.
uint32_t dex_location_checksum;
@@ -1232,15 +1226,15 @@
error_msg->c_str());
return false;
}
- dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
+ dex_file = oat_dex_file->OpenDexFile(error_msg);
} else {
bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
kRuntimeISA, error_msg);
if (!verified) {
return false;
}
- dex_file.reset(oat_file->GetOatDexFile(dex_location,
- dex_location_checksum)->OpenDexFile(error_msg));
+ dex_file = oat_file->GetOatDexFile(dex_location,
+ dex_location_checksum)->OpenDexFile(error_msg);
}
return dex_file.get() != nullptr;
}
@@ -1685,8 +1679,8 @@
nullptr);
CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location;
std::string error_msg;
- const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file == nullptr) {
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
<< " from within oat file " << oat_file.GetLocation()
<< " error '" << error_msg << "'";
@@ -1695,7 +1689,8 @@
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
- AppendToBootClassPath(*dex_file, dex_cache);
+ AppendToBootClassPath(*dex_file.get(), dex_cache);
+ opened_dex_files_.push_back(std::move(dex_file));
}
// Set classes on AbstractMethod early so that IsMethod tests can be performed during the live
@@ -1928,7 +1923,6 @@
mirror::ShortArray::ResetArrayClass();
mirror::Throwable::ResetClass();
mirror::StackTraceElement::ResetClass();
- STLDeleteElements(&boot_class_path_);
STLDeleteElements(&oat_files_);
}
@@ -4492,6 +4486,171 @@
return true;
}
+static void CountMethodsAndFields(ClassDataItemIterator& dex_data,
+ size_t* virtual_methods,
+ size_t* direct_methods,
+ size_t* static_fields,
+ size_t* instance_fields) {
+ *virtual_methods = *direct_methods = *static_fields = *instance_fields = 0;
+
+ while (dex_data.HasNextStaticField()) {
+ dex_data.Next();
+ (*static_fields)++;
+ }
+ while (dex_data.HasNextInstanceField()) {
+ dex_data.Next();
+ (*instance_fields)++;
+ }
+ while (dex_data.HasNextDirectMethod()) {
+ (*direct_methods)++;
+ dex_data.Next();
+ }
+ while (dex_data.HasNextVirtualMethod()) {
+ (*virtual_methods)++;
+ dex_data.Next();
+ }
+ DCHECK(!dex_data.HasNext());
+}
+
+static void DumpClass(std::ostream& os,
+ const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+ const char* suffix) {
+ ClassDataItemIterator dex_data(dex_file, dex_file.GetClassData(dex_class_def));
+ os << dex_file.GetClassDescriptor(dex_class_def) << suffix << ":\n";
+ os << " Static fields:\n";
+ while (dex_data.HasNextStaticField()) {
+ const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
+ os << " " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
+ dex_data.Next();
+ }
+ os << " Instance fields:\n";
+ while (dex_data.HasNextInstanceField()) {
+ const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
+ os << " " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
+ dex_data.Next();
+ }
+ os << " Direct methods:\n";
+ while (dex_data.HasNextDirectMethod()) {
+ const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
+ os << " " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
+ dex_data.Next();
+ }
+ os << " Virtual methods:\n";
+ while (dex_data.HasNextVirtualMethod()) {
+ const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
+ os << " " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
+ dex_data.Next();
+ }
+}
+
+static std::string DumpClasses(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
+ const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2) {
+ std::ostringstream os;
+ DumpClass(os, dex_file1, dex_class_def1, " (Compile time)");
+ DumpClass(os, dex_file2, dex_class_def2, " (Runtime)");
+ return os.str();
+}
+
+
+// Very simple structural check on whether the classes match. Only compares the number of
+// methods and fields.
+static bool SimpleStructuralCheck(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
+ const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2,
+ std::string* error_msg) {
+ ClassDataItemIterator dex_data1(dex_file1, dex_file1.GetClassData(dex_class_def1));
+ ClassDataItemIterator dex_data2(dex_file2, dex_file2.GetClassData(dex_class_def2));
+
+ // Counters for current dex file.
+ size_t dex_virtual_methods1, dex_direct_methods1, dex_static_fields1, dex_instance_fields1;
+ CountMethodsAndFields(dex_data1, &dex_virtual_methods1, &dex_direct_methods1, &dex_static_fields1,
+ &dex_instance_fields1);
+ // Counters for compile-time dex file.
+ size_t dex_virtual_methods2, dex_direct_methods2, dex_static_fields2, dex_instance_fields2;
+ CountMethodsAndFields(dex_data2, &dex_virtual_methods2, &dex_direct_methods2, &dex_static_fields2,
+ &dex_instance_fields2);
+
+ if (dex_virtual_methods1 != dex_virtual_methods2) {
+ std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+ *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s", dex_virtual_methods1,
+ dex_virtual_methods2, class_dump.c_str());
+ return false;
+ }
+ if (dex_direct_methods1 != dex_direct_methods2) {
+ std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+ *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s", dex_direct_methods1,
+ dex_direct_methods2, class_dump.c_str());
+ return false;
+ }
+ if (dex_static_fields1 != dex_static_fields2) {
+ std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+ *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s", dex_static_fields1,
+ dex_static_fields2, class_dump.c_str());
+ return false;
+ }
+ if (dex_instance_fields1 != dex_instance_fields2) {
+ std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+ *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s", dex_instance_fields1,
+ dex_instance_fields2, class_dump.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+// Checks whether a the super-class changed from what we had at compile-time. This would
+// invalidate quickening.
+static bool CheckSuperClassChange(Handle<mirror::Class> klass,
+ const DexFile& dex_file,
+ const DexFile::ClassDef& class_def,
+ mirror::Class* super_class)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Check for unexpected changes in the superclass.
+ // Quick check 1) is the super_class class-loader the boot class loader? This always has
+ // precedence.
+ if (super_class->GetClassLoader() != nullptr &&
+ // Quick check 2) different dex cache? Breaks can only occur for different dex files,
+ // which is implied by different dex cache.
+ klass->GetDexCache() != super_class->GetDexCache()) {
+ // Now comes the expensive part: things can be broken if (a) the klass' dex file has a
+ // definition for the super-class, and (b) the files are in separate oat files. The oat files
+ // are referenced from the dex file, so do (b) first. Only relevant if we have oat files.
+ const OatFile* class_oat_file = dex_file.GetOatFile();
+ if (class_oat_file != nullptr) {
+ const OatFile* loaded_super_oat_file = super_class->GetDexFile().GetOatFile();
+ if (loaded_super_oat_file != nullptr && class_oat_file != loaded_super_oat_file) {
+ // Now check (a).
+ const DexFile::ClassDef* super_class_def = dex_file.FindClassDef(class_def.superclass_idx_);
+ if (super_class_def != nullptr) {
+ // Uh-oh, we found something. Do our check.
+ std::string error_msg;
+ if (!SimpleStructuralCheck(dex_file, *super_class_def,
+ super_class->GetDexFile(), *super_class->GetClassDef(),
+ &error_msg)) {
+ // Print a warning to the log. This exception might be caught, e.g., as common in test
+ // drivers. When the class is later tried to be used, we re-throw a new instance, as we
+ // only save the type of the exception.
+ LOG(WARNING) << "Incompatible structural change detected: " <<
+ StringPrintf(
+ "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
+ PrettyType(super_class_def->class_idx_, dex_file).c_str(),
+ class_oat_file->GetLocation().c_str(),
+ loaded_super_oat_file->GetLocation().c_str(),
+ error_msg.c_str());
+ ThrowIncompatibleClassChangeError(klass.Get(),
+ "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
+ PrettyType(super_class_def->class_idx_, dex_file).c_str(),
+ class_oat_file->GetLocation().c_str(),
+ loaded_super_oat_file->GetLocation().c_str(),
+ error_msg.c_str());
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) {
CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus());
const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
@@ -4511,6 +4670,11 @@
}
CHECK(super_class->IsResolved());
klass->SetSuperClass(super_class);
+
+ if (!CheckSuperClassChange(klass, dex_file, class_def, super_class)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return false;
+ }
}
const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def);
if (interfaces != nullptr) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 6461835..6570c5f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -105,7 +105,7 @@
~ClassLinker();
// Initialize class linker by bootstraping from dex files.
- void InitWithoutImage(const std::vector<const DexFile*>& boot_class_path)
+ void InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Initialize class linker from one or more images.
@@ -324,7 +324,7 @@
// (if multidex) into the given vector.
bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
std::vector<std::string>* error_msgs,
- std::vector<const DexFile*>* dex_files)
+ std::vector<std::unique_ptr<const DexFile>>* dex_files)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
// Returns true if the given oat file has the same image checksum as the image it is paired with.
@@ -722,6 +722,7 @@
const void* GetRuntimeQuickGenericJniStub() const;
std::vector<const DexFile*> boot_class_path_;
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 4f09460..6c7c1e2 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -342,28 +342,26 @@
}
}
- void AssertDexFile(const DexFile* dex, mirror::ClassLoader* class_loader)
+ void AssertDexFile(const DexFile& dex, mirror::ClassLoader* class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ASSERT_TRUE(dex != nullptr);
-
// Verify all the classes defined in this file
- for (size_t i = 0; i < dex->NumClassDefs(); i++) {
- const DexFile::ClassDef& class_def = dex->GetClassDef(i);
- const char* descriptor = dex->GetClassDescriptor(class_def);
+ for (size_t i = 0; i < dex.NumClassDefs(); i++) {
+ const DexFile::ClassDef& class_def = dex.GetClassDef(i);
+ const char* descriptor = dex.GetClassDescriptor(class_def);
AssertDexFileClass(class_loader, descriptor);
}
// Verify all the types referenced by this file
- for (size_t i = 0; i < dex->NumTypeIds(); i++) {
- const DexFile::TypeId& type_id = dex->GetTypeId(i);
- const char* descriptor = dex->GetTypeDescriptor(type_id);
+ for (size_t i = 0; i < dex.NumTypeIds(); i++) {
+ const DexFile::TypeId& type_id = dex.GetTypeId(i);
+ const char* descriptor = dex.GetTypeDescriptor(type_id);
AssertDexFileClass(class_loader, descriptor);
}
class_linker_->VisitRoots(TestRootVisitor, nullptr, kVisitRootFlagAllRoots);
// Verify the dex cache has resolution methods in all resolved method slots
- mirror::DexCache* dex_cache = class_linker_->FindDexCache(*dex);
+ mirror::DexCache* dex_cache = class_linker_->FindDexCache(dex);
mirror::ObjectArray<mirror::ArtMethod>* resolved_methods = dex_cache->GetResolvedMethods();
for (size_t i = 0; i < static_cast<size_t>(resolved_methods->GetLength()); i++) {
- EXPECT_TRUE(resolved_methods->Get(i) != nullptr) << dex->GetLocation() << " i=" << i;
+ EXPECT_TRUE(resolved_methods->Get(i) != nullptr) << dex.GetLocation() << " i=" << i;
}
}
@@ -744,7 +742,8 @@
TEST_F(ClassLinkerTest, LibCore) {
ScopedObjectAccess soa(Thread::Current());
- AssertDexFile(java_lang_dex_file_, nullptr);
+ ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+ AssertDexFile(*java_lang_dex_file_, nullptr);
}
// The first reference array element must be a multiple of 4 bytes from the
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 75ba9dd..d48ac9d 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -20,6 +20,7 @@
#include <dlfcn.h>
#include <fcntl.h>
#include <ScopedLocalRef.h>
+#include <stdlib.h>
#include "../../external/icu/icu4c/source/common/unicode/uvernum.h"
#include "base/macros.h"
@@ -43,6 +44,11 @@
#include "well_known_classes.h"
int main(int argc, char **argv) {
+ // Gtests can be very noisy. For example, an executable with multiple tests will trigger native
+ // bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
+ // everything else. In case you want to see all messages, comment out the line.
+ setenv("ANDROID_LOG_TAGS", "*:e", 1);
+
art::InitLogging(argv);
LOG(::art::INFO) << "Running main() from common_runtime_test.cc...";
testing::InitGoogleTest(&argc, argv);
@@ -102,7 +108,11 @@
}
CommonRuntimeTest::CommonRuntimeTest() {}
-CommonRuntimeTest::~CommonRuntimeTest() {}
+CommonRuntimeTest::~CommonRuntimeTest() {
+ // Ensure the dex files are cleaned up before the runtime.
+ loaded_dex_files_.clear();
+ runtime_.reset();
+}
void CommonRuntimeTest::SetUpAndroidRoot() {
if (IsHost()) {
@@ -181,15 +191,15 @@
return GetCoreFileLocation("oat");
}
-const DexFile* CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) {
- std::vector<const DexFile*> dex_files;
+std::unique_ptr<const DexFile> CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
if (!DexFile::Open(location, location, &error_msg, &dex_files)) {
LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
- return nullptr;
+ UNREACHABLE();
} else {
CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location;
- return dex_files[0];
+ return std::move(dex_files[0]);
}
}
@@ -222,6 +232,9 @@
class_linker_ = runtime_->GetClassLinker();
class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
class_linker_->RunRootClinits();
+ boot_class_path_ = class_linker_->GetBootClassPath();
+ java_lang_dex_file_ = boot_class_path_[0];
+
// Runtime::Create acquired the mutator_lock_ that is normally given away when we
// Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess.
@@ -285,8 +298,6 @@
IcuCleanupFn icu_cleanup_fn = reinterpret_cast<IcuCleanupFn>(sym);
(*icu_cleanup_fn)();
- STLDeleteElements(&opened_dex_files_);
-
Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
}
@@ -323,7 +334,7 @@
#define ART_TARGET_NATIVETEST_DIR_STRING ""
#endif
-std::vector<const DexFile*> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
+std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
CHECK(name != nullptr);
std::string filename;
if (IsHost()) {
@@ -336,28 +347,30 @@
filename += name;
filename += ".jar";
std::string error_msg;
- std::vector<const DexFile*> dex_files;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files);
CHECK(success) << "Failed to open '" << filename << "': " << error_msg;
- for (const DexFile* dex_file : dex_files) {
+ for (auto& dex_file : dex_files) {
CHECK_EQ(PROT_READ, dex_file->GetPermissions());
CHECK(dex_file->IsReadOnly());
}
- opened_dex_files_.insert(opened_dex_files_.end(), dex_files.begin(), dex_files.end());
return dex_files;
}
-const DexFile* CommonRuntimeTest::OpenTestDexFile(const char* name) {
- std::vector<const DexFile*> vector = OpenTestDexFiles(name);
+std::unique_ptr<const DexFile> CommonRuntimeTest::OpenTestDexFile(const char* name) {
+ std::vector<std::unique_ptr<const DexFile>> vector = OpenTestDexFiles(name);
EXPECT_EQ(1U, vector.size());
- return vector[0];
+ return std::move(vector[0]);
}
jobject CommonRuntimeTest::LoadDex(const char* dex_name) {
- std::vector<const DexFile*> dex_files = OpenTestDexFiles(dex_name);
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+ std::vector<const DexFile*> class_path;
CHECK_NE(0U, dex_files.size());
- for (const DexFile* dex_file : dex_files) {
+ for (auto& dex_file : dex_files) {
+ class_path.push_back(dex_file.get());
class_linker_->RegisterDexFile(*dex_file);
+ loaded_dex_files_.push_back(std::move(dex_file));
}
Thread* self = Thread::Current();
JNIEnvExt* env = self->GetJniEnv();
@@ -365,7 +378,7 @@
env->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
jobject class_loader = env->NewGlobalRef(class_loader_local.get());
self->SetClassLoaderOverride(class_loader_local.get());
- Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files);
+ Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path);
return class_loader;
}
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 35dc30f..38a9733 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -87,7 +87,7 @@
// File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat
static std::string GetCoreOatLocation();
- const DexFile* LoadExpectSingleDexFile(const char* location);
+ std::unique_ptr<const DexFile> LoadExpectSingleDexFile(const char* location);
virtual void SetUp();
@@ -106,26 +106,30 @@
std::string GetTestAndroidRoot();
- std::vector<const DexFile*> OpenTestDexFiles(const char* name)
+ std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const DexFile* OpenTestDexFile(const char* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::unique_ptr<const DexFile> OpenTestDexFile(const char* name)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
jobject LoadDex(const char* dex_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
std::string android_data_;
std::string dalvik_cache_;
- const DexFile* java_lang_dex_file_; // owned by runtime_
- std::vector<const DexFile*> boot_class_path_; // owned by runtime_
+
std::unique_ptr<Runtime> runtime_;
- // Owned by the runtime
+
+ // The class_linker_, java_lang_dex_file_, and boot_class_path_ are all
+ // owned by the runtime.
ClassLinker* class_linker_;
+ const DexFile* java_lang_dex_file_;
+ std::vector<const DexFile*> boot_class_path_;
private:
static std::string GetCoreFileLocation(const char* suffix);
std::unique_ptr<CompilerCallbacks> callbacks_;
- std::vector<const DexFile*> opened_dex_files_;
+ std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_;
};
// Sets a CheckJni abort hook to catch failures. Note that this will cause CheckJNI to carry on
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 3d4184b..dc85f6c 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -125,7 +125,8 @@
}
bool DexFile::Open(const char* filename, const char* location, std::string* error_msg,
- std::vector<const DexFile*>* dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is NULL";
uint32_t magic;
ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg));
if (fd.get() == -1) {
@@ -139,7 +140,7 @@
std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true,
error_msg));
if (dex_file.get() != nullptr) {
- dex_files->push_back(dex_file.release());
+ dex_files->push_back(std::move(dex_file));
return true;
} else {
return false;
@@ -179,8 +180,8 @@
}
}
-const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify,
- std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify,
+ std::string* error_msg) {
CHECK(location != nullptr);
std::unique_ptr<MemMap> map;
{
@@ -224,13 +225,14 @@
return nullptr;
}
- return dex_file.release();
+ return dex_file;
}
const char* DexFile::kClassesDex = "classes.dex";
bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg,
- std::vector<const DexFile*>* dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is NULL";
std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
if (zip_archive.get() == nullptr) {
DCHECK(!error_msg->empty());
@@ -239,21 +241,22 @@
return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files);
}
-const DexFile* DexFile::OpenMemory(const std::string& location,
- uint32_t location_checksum,
- MemMap* mem_map,
- std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location,
+ uint32_t location_checksum,
+ MemMap* mem_map,
+ std::string* error_msg) {
return OpenMemory(mem_map->Begin(),
mem_map->Size(),
location,
location_checksum,
mem_map,
+ nullptr,
error_msg);
}
-const DexFile* DexFile::Open(const ZipArchive& zip_archive, const char* entry_name,
- const std::string& location, std::string* error_msg,
- ZipOpenErrorCode* error_code) {
+std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, const char* entry_name,
+ const std::string& location, std::string* error_msg,
+ ZipOpenErrorCode* error_code) {
CHECK(!location.empty());
std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
if (zip_entry.get() == NULL) {
@@ -287,11 +290,13 @@
return nullptr;
}
*error_code = ZipOpenErrorCode::kNoError;
- return dex_file.release();
+ return dex_file;
}
bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
- std::string* error_msg, std::vector<const DexFile*>* dex_files) {
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is NULL";
ZipOpenErrorCode error_code;
std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg,
&error_code));
@@ -299,7 +304,7 @@
return false;
} else {
// Had at least classes.dex.
- dex_files->push_back(dex_file.release());
+ dex_files->push_back(std::move(dex_file));
// Now try some more.
size_t i = 2;
@@ -318,7 +323,7 @@
}
break;
} else {
- dex_files->push_back(next_dex_file.release());
+ dex_files->push_back(std::move(next_dex_file));
}
i++;
@@ -329,24 +334,27 @@
}
-const DexFile* DexFile::OpenMemory(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- MemMap* mem_map, std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenMemory(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ MemMap* mem_map,
+ const OatFile* oat_file,
+ std::string* error_msg) {
CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned
- std::unique_ptr<DexFile> dex_file(new DexFile(base, size, location, location_checksum, mem_map));
+ std::unique_ptr<DexFile> dex_file(
+ new DexFile(base, size, location, location_checksum, mem_map, oat_file));
if (!dex_file->Init(error_msg)) {
- return nullptr;
- } else {
- return dex_file.release();
+ dex_file.reset();
}
+ return std::unique_ptr<const DexFile>(dex_file.release());
}
DexFile::DexFile(const uint8_t* base, size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map)
+ MemMap* mem_map,
+ const OatFile* oat_file)
: begin_(base),
size_(size),
location_(location),
@@ -360,7 +368,8 @@
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
find_class_def_misses_(0),
- class_def_index_(nullptr) {
+ class_def_index_(nullptr),
+ oat_file_(oat_file) {
CHECK(begin_ != NULL) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index a71ca42..9b8f254 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -44,6 +44,7 @@
} // namespace mirror
class ClassLinker;
class MemMap;
+class OatFile;
class Signature;
template<class T> class Handle;
class StringPiece;
@@ -385,19 +386,21 @@
// Opens .dex files found in the container, guessing the container format based on file extension.
static bool Open(const char* filename, const char* location, std::string* error_msg,
- std::vector<const DexFile*>* dex_files);
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
// Opens .dex file, backed by existing memory
- static const DexFile* Open(const uint8_t* base, size_t size,
- const std::string& location,
- uint32_t location_checksum,
- std::string* error_msg) {
- return OpenMemory(base, size, location, location_checksum, NULL, error_msg);
+ static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatFile* oat_file,
+ std::string* error_msg) {
+ return OpenMemory(base, size, location, location_checksum, NULL, oat_file, error_msg);
}
// Open all classesXXX.dex files from a zip archive.
static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
- std::string* error_msg, std::vector<const DexFile*>* dex_files);
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
// Closes a .dex file.
virtual ~DexFile();
@@ -890,13 +893,18 @@
// the dex_location where it's file name part has been made canonical.
static std::string GetDexCanonicalLocation(const char* dex_location);
+ const OatFile* GetOatFile() const {
+ return oat_file_;
+ }
+
private:
// Opens a .dex file
- static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg);
+ static std::unique_ptr<const DexFile> OpenFile(int fd, const char* location,
+ bool verify, std::string* error_msg);
// Opens dex files from within a .jar, .zip, or .apk file
static bool OpenZip(int fd, const std::string& location, std::string* error_msg,
- std::vector<const DexFile*>* dex_files);
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
enum class ZipOpenErrorCode { // private
kNoError,
@@ -909,28 +917,30 @@
// Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-nullptr
// return.
- static const DexFile* Open(const ZipArchive& zip_archive, const char* entry_name,
- const std::string& location, std::string* error_msg,
- ZipOpenErrorCode* error_code);
+ static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive, const char* entry_name,
+ const std::string& location, std::string* error_msg,
+ ZipOpenErrorCode* error_code);
// Opens a .dex file at the given address backed by a MemMap
- static const DexFile* OpenMemory(const std::string& location,
- uint32_t location_checksum,
- MemMap* mem_map,
- std::string* error_msg);
+ static std::unique_ptr<const DexFile> OpenMemory(const std::string& location,
+ uint32_t location_checksum,
+ MemMap* mem_map,
+ std::string* error_msg);
// Opens a .dex file at the given address, optionally backed by a MemMap
- static const DexFile* OpenMemory(const uint8_t* dex_file,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- MemMap* mem_map,
- std::string* error_msg);
+ static std::unique_ptr<const DexFile> OpenMemory(const uint8_t* dex_file,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ MemMap* mem_map,
+ const OatFile* oat_file,
+ std::string* error_msg);
DexFile(const uint8_t* base, size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map);
+ MemMap* mem_map,
+ const OatFile* oat_file);
// Top-level initializer that calls other Init methods.
bool Init(std::string* error_msg);
@@ -1013,6 +1023,10 @@
};
typedef HashMap<const char*, const ClassDef*, UTF16EmptyFn, UTF16HashCmp, UTF16HashCmp> Index;
mutable Atomic<Index*> class_def_index_;
+
+ // The oat file this dex file was loaded from. May be null in case the dex file is not coming
+ // from an oat file, e.g., directly from an apk.
+ const OatFile* oat_file_;
};
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 0b54d47..7f5a181 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -32,8 +32,8 @@
TEST_F(DexFileTest, Open) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* dex(OpenTestDexFile("Nested"));
- ASSERT_TRUE(dex != NULL);
+ std::unique_ptr<const DexFile> dex(OpenTestDexFile("Nested"));
+ ASSERT_TRUE(dex.get() != NULL);
}
static const uint8_t kBase64Map[256] = {
@@ -133,8 +133,8 @@
"AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA"
"AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
-static const DexFile* OpenDexFileBase64(const char* base64,
- const char* location) {
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+ const char* location) {
// decode base64
CHECK(base64 != NULL);
size_t length;
@@ -155,11 +155,11 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
std::string error_msg;
- std::vector<const DexFile*> tmp;
+ std::vector<std::unique_ptr<const DexFile>> tmp;
bool success = DexFile::Open(location, location, &error_msg, &tmp);
CHECK(success) << error_msg;
EXPECT_EQ(1U, tmp.size());
- const DexFile* dex_file = tmp[0];
+ std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
EXPECT_TRUE(dex_file->IsReadOnly());
return dex_file;
@@ -198,7 +198,7 @@
TEST_F(DexFileTest, GetLocationChecksum) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("Main"));
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("Main"));
EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum());
}
@@ -213,8 +213,8 @@
TEST_F(DexFileTest, ClassDefs) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("Nested"));
- ASSERT_TRUE(raw != NULL);
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
+ ASSERT_TRUE(raw.get() != nullptr);
EXPECT_EQ(2U, raw->NumClassDefs());
const DexFile::ClassDef& c0 = raw->GetClassDef(0);
@@ -226,8 +226,8 @@
TEST_F(DexFileTest, GetMethodSignature) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
- ASSERT_TRUE(raw != NULL);
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
+ ASSERT_TRUE(raw.get() != nullptr);
EXPECT_EQ(1U, raw->NumClassDefs());
const DexFile::ClassDef& class_def = raw->GetClassDef(0);
@@ -276,8 +276,8 @@
TEST_F(DexFileTest, FindStringId) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
- ASSERT_TRUE(raw != NULL);
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
+ ASSERT_TRUE(raw.get() != nullptr);
EXPECT_EQ(1U, raw->NumClassDefs());
const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index ec1e5f0..00ca8a9 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -101,8 +101,9 @@
return dst.release();
}
-static const DexFile* OpenDexFileBase64(const char* base64, const char* location,
- std::string* error_msg) {
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+ const char* location,
+ std::string* error_msg) {
// decode base64
CHECK(base64 != NULL);
size_t length;
@@ -122,11 +123,11 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
- std::vector<const DexFile*> tmp;
+ std::vector<std::unique_ptr<const DexFile>> tmp;
bool success = DexFile::Open(location, location, error_msg, &tmp);
CHECK(success) << error_msg;
EXPECT_EQ(1U, tmp.size());
- const DexFile* dex_file = tmp[0];
+ std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
EXPECT_TRUE(dex_file->IsReadOnly());
return dex_file;
@@ -166,8 +167,9 @@
header->checksum_ = adler_checksum;
}
-static const DexFile* FixChecksumAndOpen(uint8_t* bytes, size_t length, const char* location,
- std::string* error_msg) {
+static std::unique_ptr<const DexFile> FixChecksumAndOpen(uint8_t* bytes, size_t length,
+ const char* location,
+ std::string* error_msg) {
// Check data.
CHECK(bytes != nullptr);
@@ -187,12 +189,12 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
- std::vector<const DexFile*> tmp;
+ std::vector<std::unique_ptr<const DexFile>> tmp;
if (!DexFile::Open(location, location, error_msg, &tmp)) {
return nullptr;
}
EXPECT_EQ(1U, tmp.size());
- const DexFile* dex_file = tmp[0];
+ std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
EXPECT_TRUE(dex_file->IsReadOnly());
return dex_file;
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index 9f39b80..09d10dd 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -127,6 +127,9 @@
}
void SpaceTest::InitTestBody(CreateSpaceFn create_space) {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
{
// Init < max == growth
std::unique_ptr<Space> space(create_space("test", 16 * MB, 32 * MB, 32 * MB, nullptr));
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 99ee597..1156cf5 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -43,6 +43,9 @@
}
TEST_F(IndirectReferenceTableTest, BasicTest) {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
ScopedObjectAccess soa(Thread::Current());
static const size_t kTableInitial = 10;
static const size_t kTableMax = 20;
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 8e32968..906aa4c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -634,6 +634,9 @@
}
TEST_F(JniInternalTest, FindClass) {
+ // This tests leads to warnings in the log.
+ ScopedLogSeverity sls(LogSeverity::ERROR);
+
FindClassTest(false);
FindClassTest(true);
}
@@ -890,40 +893,44 @@
// Sanity check that no exceptions are pending.
ASSERT_FALSE(env_->ExceptionCheck());
- // Check that registering method without name causes a NoSuchMethodError.
+ // The following can print errors to the log we'd like to ignore.
{
- JNINativeMethod methods[] = { { nullptr, "()V", native_function } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
- }
- ExpectException(jlnsme);
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+ // Check that registering method without name causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { nullptr, "()V", native_function } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
- // Check that registering method without signature causes a NoSuchMethodError.
- {
- JNINativeMethod methods[] = { { "notify", nullptr, native_function } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
- }
- ExpectException(jlnsme);
+ // Check that registering method without signature causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { "notify", nullptr, native_function } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
- // Check that registering method without function causes a NoSuchMethodError.
- {
- JNINativeMethod methods[] = { { "notify", "()V", nullptr } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
- }
- ExpectException(jlnsme);
+ // Check that registering method without function causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { "notify", "()V", nullptr } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
- // Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError.
- {
- JNINativeMethod methods[] = { { "foo", "()V", native_function } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
- }
- ExpectException(jlnsme);
+ // Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { "foo", "()V", native_function } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
- // Check that registering non-native methods causes a NoSuchMethodError.
- {
- JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", native_function } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ // Check that registering non-native methods causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", native_function } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
}
- ExpectException(jlnsme);
// Check that registering native methods is successful.
{
@@ -1707,6 +1714,9 @@
}
TEST_F(JniInternalTest, DeleteLocalRef) {
+ // This tests leads to warnings and errors in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
env_->DeleteLocalRef(s);
@@ -1743,6 +1753,9 @@
ASSERT_EQ(JNI_OK, env_->PushLocalFrame(0));
env_->PopLocalFrame(nullptr);
+ // The following two tests will print errors to the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
// Negative capacities are not allowed.
ASSERT_EQ(JNI_ERR, env_->PushLocalFrame(-1));
@@ -1751,6 +1764,9 @@
}
TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) {
+ // This tests leads to errors in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
jobject original = env_->NewStringUTF("");
ASSERT_NE(original, nullptr);
@@ -1815,6 +1831,9 @@
}
TEST_F(JniInternalTest, DeleteGlobalRef) {
+ // This tests leads to warnings and errors in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
@@ -1865,6 +1884,9 @@
}
TEST_F(JniInternalTest, DeleteWeakGlobalRef) {
+ // This tests leads to warnings and errors in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
@@ -1989,6 +2011,9 @@
}
TEST_F(JniInternalTest, MonitorEnterExit) {
+ // This will print some error messages. Suppress.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
// Create an object to torture.
jclass object_class = env_->FindClass("java/lang/Object");
ASSERT_NE(object_class, nullptr);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index ef6fc67..53e5534 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -34,6 +34,7 @@
TEST_F(DexCacheTest, Open) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
+ ASSERT_TRUE(java_lang_dex_file_ != NULL);
Handle<DexCache> dex_cache(
hs.NewHandle(class_linker_->AllocDexCache(soa.Self(), *java_lang_dex_file_)));
ASSERT_TRUE(dex_cache.Get() != NULL);
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index adc7848..6d1e721 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -356,6 +356,8 @@
TEST_F(MonitorTest, CheckExceptionsWait1) {
// Make the CreateTask wait 10ms, the UseTask wait 10ms.
// => The use task will get the lock first and get to self == owner check.
+ // This will lead to OOM and monitor error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
CommonWaitSetup(this, class_linker_, 10, 50, false, false, 2, 50, true,
"Monitor test thread pool 1");
}
@@ -364,6 +366,8 @@
TEST_F(MonitorTest, CheckExceptionsWait2) {
// Make the CreateTask wait 0ms, the UseTask wait 10ms.
// => The create task will get the lock first and get to ms >= 0
+ // This will lead to OOM and monitor error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
CommonWaitSetup(this, class_linker_, 0, -1, true, false, 10, 50, true,
"Monitor test thread pool 2");
}
@@ -373,6 +377,8 @@
// Make the CreateTask wait 0ms, then Wait for a long time. Make the InterruptTask wait 10ms,
// after which it will interrupt the create task and then wait another 10ms.
// => The create task will get to the interrupted-exception throw.
+ // This will lead to OOM and monitor error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
CommonWaitSetup(this, class_linker_, 0, 500, true, true, 10, 50, true,
"Monitor test thread pool 3");
}
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 44c6d87..037072d 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -115,7 +115,8 @@
}
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- std::unique_ptr<std::vector<const DexFile*>> dex_files(new std::vector<const DexFile*>());
+ std::unique_ptr<std::vector<std::unique_ptr<const DexFile>>> dex_files(
+ new std::vector<std::unique_ptr<const DexFile>>());
std::vector<std::string> error_msgs;
bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs,
@@ -143,9 +144,11 @@
}
}
-static std::vector<const DexFile*>* toDexFiles(jlong dex_file_address, JNIEnv* env) {
- std::vector<const DexFile*>* dex_files = reinterpret_cast<std::vector<const DexFile*>*>(
- static_cast<uintptr_t>(dex_file_address));
+static std::vector<std::unique_ptr<const DexFile>>*
+toDexFiles(jlong dex_file_address, JNIEnv* env) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files
+ = reinterpret_cast<std::vector<std::unique_ptr<const DexFile>>*>(
+ static_cast<uintptr_t>(dex_file_address));
if (UNLIKELY(dex_files == nullptr)) {
ScopedObjectAccess soa(env);
ThrowNullPointerException(NULL, "dex_file == null");
@@ -154,27 +157,29 @@
}
static void DexFile_closeDexFile(JNIEnv* env, jclass, jlong cookie) {
- std::unique_ptr<std::vector<const DexFile*>> dex_files(toDexFiles(cookie, env));
+ std::unique_ptr<std::vector<std::unique_ptr<const DexFile>>> dex_files(toDexFiles(cookie, env));
if (dex_files.get() == nullptr) {
return;
}
ScopedObjectAccess soa(env);
- size_t index = 0;
- for (const DexFile* dex_file : *dex_files) {
+ // The Runtime currently never unloads classes, which means any registered
+ // dex files must be kept around forever in case they are used. We
+ // accomplish this here by explicitly leaking those dex files that are
+ // registered.
+ //
+ // TODO: The Runtime should support unloading of classes and freeing of the
+ // dex files for those unloaded classes rather than leaking dex files here.
+ for (auto& dex_file : *dex_files) {
if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {
- (*dex_files)[index] = nullptr;
+ dex_file.release();
}
- index++;
}
-
- STLDeleteElements(dex_files.get());
- // Unique_ptr will delete the vector itself.
}
static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
jlong cookie) {
- std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env);
+ std::vector<std::unique_ptr<const DexFile>>* dex_files = toDexFiles(cookie, env);
if (dex_files == NULL) {
VLOG(class_linker) << "Failed to find dex_file";
return NULL;
@@ -186,7 +191,7 @@
}
const std::string descriptor(DotToDescriptor(class_name.c_str()));
const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
- for (const DexFile* dex_file : *dex_files) {
+ for (auto& dex_file : *dex_files) {
const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
if (dex_class_def != nullptr) {
ScopedObjectAccess soa(env);
@@ -218,13 +223,13 @@
// Note: this can be an expensive call, as we sort out duplicates in MultiDex files.
static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jlong cookie) {
jobjectArray result = nullptr;
- std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env);
+ std::vector<std::unique_ptr<const DexFile>>* dex_files = toDexFiles(cookie, env);
if (dex_files != nullptr) {
// Push all class descriptors into a set. Use set instead of unordered_set as we want to
// retrieve all in the end.
std::set<const char*, CharPointerComparator> descriptors;
- for (const DexFile* dex_file : *dex_files) {
+ for (auto& dex_file : *dex_files) {
for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 1c6cc8b..9061bb3 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -454,9 +454,9 @@
return reinterpret_cast<const DexFile::Header*>(dex_file_pointer_)->file_size_;
}
-const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
+std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
- dex_file_location_checksum_, error_msg);
+ dex_file_location_checksum_, GetOatFile(), error_msg);
}
uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 831ba1e..6ae3c3e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -210,7 +210,7 @@
class OatDexFile {
public:
// Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
- const DexFile* OpenDexFile(std::string* error_msg) const;
+ std::unique_ptr<const DexFile> OpenDexFile(std::string* error_msg) const;
const OatFile* GetOatFile() const {
return oat_file_;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index c2e814b..fabbbfb 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -625,8 +625,9 @@
}
static bool OpenDexFilesFromImage(const std::string& image_location,
- std::vector<const DexFile*>& dex_files,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
size_t* failures) {
+ DCHECK(dex_files != nullptr) << "OpenDexFilesFromImage: out-param is NULL";
std::string system_filename;
bool has_system = false;
std::string cache_filename_unused;
@@ -670,11 +671,11 @@
*failures += 1;
continue;
}
- const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file == nullptr) {
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
*failures += 1;
} else {
- dex_files.push_back(dex_file);
+ dex_files->push_back(std::move(dex_file));
}
}
Runtime::Current()->GetClassLinker()->RegisterOatFile(oat_file.release());
@@ -685,7 +686,8 @@
static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames,
const std::vector<std::string>& dex_locations,
const std::string& image_location,
- std::vector<const DexFile*>& dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "OpenDexFiles: out-param is NULL";
size_t failure_count = 0;
if (!image_location.empty() && OpenDexFilesFromImage(image_location, dex_files, &failure_count)) {
return failure_count;
@@ -699,7 +701,7 @@
LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
continue;
}
- if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) {
+ if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) {
LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
++failure_count;
}
@@ -887,9 +889,9 @@
CHECK_EQ(dex_filenames.size(), dex_locations.size());
}
- std::vector<const DexFile*> boot_class_path;
- OpenDexFiles(dex_filenames, dex_locations, options->image_, boot_class_path);
- class_linker_->InitWithoutImage(boot_class_path);
+ std::vector<std::unique_ptr<const DexFile>> boot_class_path;
+ OpenDexFiles(dex_filenames, dex_locations, options->image_, &boot_class_path);
+ class_linker_->InitWithoutImage(std::move(boot_class_path));
// TODO: Should we move the following to InitWithoutImage?
SetInstructionSet(kRuntimeISA);
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index e319963..d58fe3c 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -425,6 +425,9 @@
LOCKS_EXCLUDED(method_verifier_lock_);
const std::vector<const DexFile*>& GetCompileTimeClassPath(jobject class_loader);
+
+ // The caller is responsible for ensuring the class_path DexFiles remain
+ // valid as long as the Runtime object remains valid.
void SetCompileTimeClassPath(jobject class_loader, std::vector<const DexFile*>& class_path);
void StartProfiler(const char* profile_output_filename);
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index a5a0204..35d944f 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -25,14 +25,17 @@
#include "base/dumpable.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/stringprintf.h"
#include "thread-inl.h"
+#include "thread_list.h"
#include "utils.h"
namespace art {
static constexpr bool kDumpHeapObjectOnSigsevg = false;
+static constexpr bool kUseSigRTTimeout = true;
struct Backtrace {
public:
@@ -283,10 +286,29 @@
mcontext_t& context;
};
+// Return the signal number we recognize as timeout. -1 means not active/supported.
+static int GetTimeoutSignal() {
+#if defined(__APPLE__)
+ // Mac does not support realtime signals.
+ UNUSED(kUseSigRTTimeout);
+ return -1;
+#else
+ return kUseSigRTTimeout ? (SIGRTMIN + 2) : -1;
+#endif
+}
+
+static bool IsTimeoutSignal(int signal_number) {
+ return signal_number == GetTimeoutSignal();
+}
+
void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
static bool handlingUnexpectedSignal = false;
if (handlingUnexpectedSignal) {
LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n");
+ if (IsTimeoutSignal(signal_number)) {
+ // Ignore a recursive timeout.
+ return;
+ }
_exit(1);
}
handlingUnexpectedSignal = true;
@@ -320,6 +342,10 @@
<< "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);
Runtime* runtime = Runtime::Current();
if (runtime != nullptr) {
+ if (IsTimeoutSignal(signal_number)) {
+ // Special timeout signal. Try to dump all threads.
+ runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL));
+ }
gc::Heap* heap = runtime->GetHeap();
LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage();
if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
@@ -374,6 +400,10 @@
rc += sigaction(SIGSTKFLT, &action, NULL);
#endif
rc += sigaction(SIGTRAP, &action, NULL);
+ // Special dump-all timeout.
+ if (GetTimeoutSignal() != -1) {
+ rc += sigaction(GetTimeoutSignal(), &action, NULL);
+ }
CHECK_EQ(rc, 0);
}
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 364b7c2..0f64883 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -152,8 +152,7 @@
// Dump checkpoint timeout in milliseconds. Larger amount on the host, as dumping will invoke
// addr2line when available.
-static constexpr uint32_t kDumpWaitTimeoutTarget = 10000;
-static constexpr uint32_t kDumpWaitTimeoutHost = 20000;
+static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 10000 : 20000;
// A closure used by Thread::Dump.
class DumpCheckpoint FINAL : public Closure {
@@ -181,8 +180,7 @@
void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) {
Thread* self = Thread::Current();
ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
- bool timed_out = barrier_.Increment(self, threads_running_checkpoint,
- kIsTargetBuild ? kDumpWaitTimeoutTarget : kDumpWaitTimeoutHost);
+ bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kDumpWaitTimeout);
if (timed_out) {
// Avoid a recursive abort.
LOG((kIsDebugBuild && (gAborting == 0)) ? FATAL : ERROR)
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index a98bc90..a3dd13c 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -392,6 +392,9 @@
}
TEST_F(UtilsTest, ExecError) {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
std::vector<std::string> command;
command.push_back("bogus");
std::string error_msg;
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 770ca7e..f67adc1 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -41,14 +41,12 @@
<< error_msg;
}
- void VerifyDexFile(const DexFile* dex)
+ void VerifyDexFile(const DexFile& dex)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ASSERT_TRUE(dex != NULL);
-
// Verify all the classes defined in this file
- for (size_t i = 0; i < dex->NumClassDefs(); i++) {
- const DexFile::ClassDef& class_def = dex->GetClassDef(i);
- const char* descriptor = dex->GetClassDescriptor(class_def);
+ for (size_t i = 0; i < dex.NumClassDefs(); i++) {
+ const DexFile::ClassDef& class_def = dex.GetClassDef(i);
+ const char* descriptor = dex.GetClassDescriptor(class_def);
VerifyClass(descriptor);
}
}
@@ -56,7 +54,8 @@
TEST_F(MethodVerifierTest, LibCore) {
ScopedObjectAccess soa(Thread::Current());
- VerifyDexFile(java_lang_dex_file_);
+ ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+ VerifyDexFile(*java_lang_dex_file_);
}
} // namespace verifier
diff --git a/test/114-ParallelGC/src/Main.java b/test/114-ParallelGC/src/Main.java
index 8e2519d..963fdac 100644
--- a/test/114-ParallelGC/src/Main.java
+++ b/test/114-ParallelGC/src/Main.java
@@ -24,7 +24,10 @@
public class Main implements Runnable {
- public final static long TIMEOUT_VALUE = 5; // Timeout in minutes.
+ // Timeout in minutes. Make it larger than the run-test timeout to get a native thread dump by
+ // ART on timeout when running on the host.
+ public final static long TIMEOUT_VALUE = 7;
+
public final static long MAX_SIZE = 1000; // Maximum size of array-list to allocate.
public static void main(String[] args) throws Exception {
diff --git a/test/122-npe/src/Main.java b/test/122-npe/src/Main.java
index 2fdcb9c..8f68205 100644
--- a/test/122-npe/src/Main.java
+++ b/test/122-npe/src/Main.java
@@ -191,6 +191,132 @@
check(npe, thisLine += 7);
try {
+ ((Value) null).volatileObjectField.toString();
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileObjectField = "Fisk";
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).volatileIntField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileIntField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useFloat(((Value) null).volatileFloatField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileFloatField = 42.0F;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useLong(((Value) null).volatileLongField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileLongField = 42L;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useDouble(((Value) null).volatileDoubleField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileDoubleField = 42.0d;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).volatileByteField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileByteField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ if (((Value) null).volatileBooleanField) { }
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileBooleanField = true;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).volatileCharField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileCharField = '\u0042';
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).volatileShortField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileShortField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
((Object[]) null)[0].toString();
} catch (NullPointerException e) {
npe = e;
@@ -477,11 +603,22 @@
static class Value {
Object objectField;
int intField;
- float floatField; long longField;
+ float floatField;
+ long longField;
double doubleField;
byte byteField;
boolean booleanField;
char charField;
short shortField;
+
+ volatile Object volatileObjectField;
+ volatile int volatileIntField;
+ volatile float volatileFloatField;
+ volatile long volatileLongField;
+ volatile double volatileDoubleField;
+ volatile byte volatileByteField;
+ volatile boolean volatileBooleanField;
+ volatile char volatileCharField;
+ volatile short volatileShortField;
}
}
diff --git a/test/131-structural-change/build b/test/131-structural-change/build
new file mode 100755
index 0000000..7ddc81d
--- /dev/null
+++ b/test/131-structural-change/build
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+mkdir classes-ex
+${JAVAC} -d classes-ex `find src-ex -name '*.java'`
+
+if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+ zip $TEST_NAME.jar classes.dex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ zip ${TEST_NAME}-ex.jar classes.dex
+fi
diff --git a/test/131-structural-change/expected.txt b/test/131-structural-change/expected.txt
new file mode 100644
index 0000000..cc7713d
--- /dev/null
+++ b/test/131-structural-change/expected.txt
@@ -0,0 +1,2 @@
+Should really reach here.
+Done.
diff --git a/test/131-structural-change/info.txt b/test/131-structural-change/info.txt
new file mode 100644
index 0000000..6d5817b
--- /dev/null
+++ b/test/131-structural-change/info.txt
@@ -0,0 +1 @@
+Check whether a structural change in a (non-native) multi-dex scenario is detected.
diff --git a/test/131-structural-change/run b/test/131-structural-change/run
new file mode 100755
index 0000000..63fdb8c
--- /dev/null
+++ b/test/131-structural-change/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 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.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/test/131-structural-change/src-ex/A.java b/test/131-structural-change/src-ex/A.java
new file mode 100644
index 0000000..800347b
--- /dev/null
+++ b/test/131-structural-change/src-ex/A.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 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 A {
+ public void bar() {
+ }
+}
diff --git a/test/131-structural-change/src-ex/B.java b/test/131-structural-change/src-ex/B.java
new file mode 100644
index 0000000..61369db
--- /dev/null
+++ b/test/131-structural-change/src-ex/B.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class B extends A {
+ public void test() {
+ System.out.println("Should not reach this!");
+ }
+}
diff --git a/test/131-structural-change/src/A.java b/test/131-structural-change/src/A.java
new file mode 100644
index 0000000..b07de58
--- /dev/null
+++ b/test/131-structural-change/src/A.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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 A {
+ public void foo() {
+ }
+
+ public void bar() {
+ }
+
+ public void baz() {
+ }
+}
diff --git a/test/131-structural-change/src/Main.java b/test/131-structural-change/src/Main.java
new file mode 100644
index 0000000..8dfa280
--- /dev/null
+++ b/test/131-structural-change/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Structural hazard test.
+ */
+public class Main {
+ public static void main(String[] args) {
+ new Main().run();
+ }
+
+ private void run() {
+ try {
+ Class<?> bClass = getClass().getClassLoader().loadClass("A");
+ System.out.println("Should really reach here.");
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+
+ boolean haveOatFile = hasOat();
+ boolean gotError = false;
+ try {
+ Class<?> bClass = getClass().getClassLoader().loadClass("B");
+ } catch (IncompatibleClassChangeError icce) {
+ gotError = true;
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+ if (haveOatFile ^ gotError) {
+ System.out.println("Did not get expected error.");
+ }
+ System.out.println("Done.");
+ }
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ private native static boolean hasOat();
+}
diff --git a/test/439-swap-double/expected.txt b/test/439-swap-double/expected.txt
new file mode 100644
index 0000000..019c901
--- /dev/null
+++ b/test/439-swap-double/expected.txt
@@ -0,0 +1,4 @@
+-26.0
+-24.0
+-22.0
+-20.0
diff --git a/test/439-swap-double/info.txt b/test/439-swap-double/info.txt
new file mode 100644
index 0000000..23447d2
--- /dev/null
+++ b/test/439-swap-double/info.txt
@@ -0,0 +1,2 @@
+Test for the optimizing compiler's parallel swap support in
+the presence of register pairs (in this case, doubles on ARM).
diff --git a/test/439-swap-double/src/Main.java b/test/439-swap-double/src/Main.java
new file mode 100644
index 0000000..da11577
--- /dev/null
+++ b/test/439-swap-double/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// Test for the optimizing compiler's parallel swap support in
+// the presence of register pairs (in this case, doubles on ARM).
+public class Main {
+ public static void main(String[] args) {
+ new Main().foo();
+ }
+
+ public void foo() {
+ // Do multiple calls to force swapping of registers. Note that
+ // this depends on the calling convention, as a stack-only convention
+ // may not need the swapping.
+ callWithDoubles(a, b, c, d, e, f, g);
+ callWithDoubles(b, c, d, e, f, g, a);
+ callWithDoubles(c, d, e, f, g, a, b);
+ callWithDoubles(d, e, f, g, a, b, c);
+ }
+
+ public static void callWithDoubles(
+ double a, double b, double c, double d, double e, double f, double g) {
+ System.out.println(a - b - c - d - e - f - g);
+ }
+
+ double a = 1.0;
+ double b = 2.0;
+ double c = 3.0;
+ double d = 4.0;
+ double e = 5.0;
+ double f = 6.0;
+ double g = 7.0;
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index dc4ec66..04c590e 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -282,6 +282,7 @@
117-nopatchoat \
118-noimage-dex2oat \
119-noimage-patchoat \
+ 131-structural-change \
ifneq (,$(filter ndebug,$(RUN_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5c0f83f..2710df8 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -37,7 +37,8 @@
RELOCATE="y"
SECONDARY_DEX=""
TIME_OUT="y"
-TIME_OUT_VALUE=5m
+# Value in minutes.
+TIME_OUT_VALUE=5
USE_GDB="n"
USE_JVM="n"
VERIFY="y"
@@ -377,7 +378,13 @@
if [ "$TIME_OUT" = "y" ]; then
# Add timeout command if time out is desired.
- cmdline="timeout $TIME_OUT_VALUE $cmdline"
+ #
+ # Note: We use nested timeouts. The inner timeout sends SIGRTMIN+2 (usually 36) to ART, which
+ # will induce a full thread dump before abort. However, dumping threads might deadlock,
+ # so the outer timeout sends the regular SIGTERM after an additional minute to ensure
+ # termination (without dumping all threads).
+ TIME_PLUS_ONE=$(($TIME_OUT_VALUE + 1))
+ cmdline="timeout ${TIME_PLUS_ONE}m timeout -s SIGRTMIN+2 ${TIME_OUT_VALUE}m $cmdline"
fi
if [ "$DEV_MODE" = "y" ]; then
diff --git a/tools/checker.py b/tools/checker.py
index 406a311..bca0707 100755
--- a/tools/checker.py
+++ b/tools/checker.py
@@ -80,7 +80,9 @@
from subprocess import check_call
class Logger(object):
- SilentMode = False
+
+ class Level(object):
+ NoOutput, Error, Info = range(3)
class Color(object):
Default, Blue, Gray, Purple, Red = range(5)
@@ -100,13 +102,15 @@
else:
return '\033[0m'
+ Verbosity = Level.Info
+
@staticmethod
- def log(text, color=Color.Default, newLine=True, out=sys.stdout):
- if not Logger.SilentMode:
+ def log(text, level=Level.Info, color=Color.Default, newLine=True, out=sys.stdout):
+ if level <= Logger.Verbosity:
text = Logger.Color.terminalCode(color, out) + text + \
Logger.Color.terminalCode(Logger.Color.Default, out)
if newLine:
- print(text, file=out)
+ print(text, flush=True, file=out)
else:
print(text, end="", flush=True, file=out)
@@ -120,9 +124,9 @@
if location:
location += " "
- Logger.log(location, color=Logger.Color.Gray, newLine=False, out=sys.stderr)
- Logger.log("error: ", color=Logger.Color.Red, newLine=False, out=sys.stderr)
- Logger.log(msg, out=sys.stderr)
+ Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr)
+ Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr)
+ Logger.log(msg, Logger.Level.Error, out=sys.stderr)
sys.exit(1)
@staticmethod
@@ -692,6 +696,8 @@
help="print a list of all groups found in the test output")
parser.add_argument("--dump-group", dest="dump_group", metavar="GROUP",
help="print the contents of an output group")
+ parser.add_argument("-q", "--quiet", action="store_true",
+ help="print only errors")
return parser.parse_args()
@@ -713,7 +719,7 @@
classFolder = tempFolder + "/classes"
dexFile = tempFolder + "/test.dex"
oatFile = tempFolder + "/test.oat"
- outputFile = tempFolder + "/art.cfg"
+ outputFile = tempFolder + "/test.cfg"
os.makedirs(classFolder)
# Build a DEX from the source file. We pass "--no-optimize" to dx to avoid
@@ -723,7 +729,7 @@
# Run dex2oat and export the HGraph. The output is stored into ${PWD}/art.cfg.
with cd(tempFolder):
- check_call(["dex2oat", "-j1", "--dump-passes", "--compiler-backend=Optimizing",
+ check_call(["dex2oat", "-j1", "--dump-cfg=" + outputFile, "--compiler-backend=Optimizing",
"--android-root=" + os.environ["ANDROID_HOST_OUT"],
"--boot-image=" + os.environ["ANDROID_HOST_OUT"] + "/framework/core-optimizing.art",
"--runtime-arg", "-Xnorelocate", "--dex-file=" + dexFile, "--oat-file=" + oatFile])
@@ -762,8 +768,10 @@
if __name__ == "__main__":
args = ParseArguments()
- tempFolder = tempfile.mkdtemp()
+ if args.quiet:
+ Logger.Verbosity = Logger.Level.Error
+ tempFolder = tempfile.mkdtemp()
try:
outputFile = CompileTest(args.test_file, tempFolder)
if args.list_groups:
diff --git a/tools/checker_test.py b/tools/checker_test.py
index 3c659c2..1466b93 100755
--- a/tools/checker_test.py
+++ b/tools/checker_test.py
@@ -457,5 +457,5 @@
("def", CheckVariant.DAG) ])) ])
if __name__ == '__main__':
- checker.Logger.SilentMode = True
+ checker.Logger.Verbosity = checker.Logger.Level.NoOutput
unittest.main()