Merge "ART: Logging levels for Checker"
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/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.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/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/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/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 438cebf..377a3c3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4486,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());
@@ -4505,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/common_runtime_test.cc b/runtime/common_runtime_test.cc
index e017699..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);
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 3f6175f..dc85f6c 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -250,6 +250,7 @@
location,
location_checksum,
mem_map,
+ nullptr,
error_msg);
}
@@ -337,9 +338,12 @@
size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map, std::string* error_msg) {
+ 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)) {
dex_file.reset();
}
@@ -349,7 +353,8 @@
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),
@@ -363,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 019c8e6..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;
@@ -391,8 +392,9 @@
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, error_msg);
+ return OpenMemory(base, size, location, location_checksum, NULL, oat_file, error_msg);
}
// Open all classesXXX.dex files from a zip archive.
@@ -891,6 +893,10 @@
// 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 std::unique_ptr<const DexFile> OpenFile(int fd, const char* location,
@@ -927,12 +933,14 @@
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);
@@ -1015,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/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/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/oat_file.cc b/runtime/oat_file.cc
index 358519b..9061bb3 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -456,7 +456,7 @@
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/runtime_linux.cc b/runtime/runtime_linux.cc
index a0adcd1..35d944f 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -25,6 +25,7 @@
#include "base/dumpable.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/stringprintf.h"
#include "thread-inl.h"
@@ -34,6 +35,7 @@
namespace art {
static constexpr bool kDumpHeapObjectOnSigsevg = false;
+static constexpr bool kUseSigRTTimeout = true;
struct Backtrace {
public:
@@ -284,8 +286,15 @@
mcontext_t& context;
};
+// Return the signal number we recognize as timeout. -1 means not active/supported.
static int GetTimeoutSignal() {
- return SIGRTMIN + 2;
+#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) {
@@ -392,7 +401,9 @@
#endif
rc += sigaction(SIGTRAP, &action, NULL);
// Special dump-all timeout.
- rc += sigaction(GetTimeoutSignal(), &action, NULL);
+ if (GetTimeoutSignal() != -1) {
+ rc += sigaction(GetTimeoutSignal(), &action, NULL);
+ }
CHECK_EQ(rc, 0);
}
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/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/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/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), \