diff options
71 files changed, 1768 insertions, 472 deletions
diff --git a/Android.mk b/Android.mk index 25796a0179..bb1334a05d 100644 --- a/Android.mk +++ b/Android.mk @@ -93,6 +93,7 @@ include $(art_path)/tools/ahat/Android.mk include $(art_path)/tools/dexfuzz/Android.mk include $(art_path)/tools/dmtracedump/Android.mk include $(art_path)/sigchainlib/Android.mk +include $(art_path)/libart_fake/Android.mk # ART_HOST_DEPENDENCIES depends on Android.executable.mk above for ART_HOST_EXECUTABLES diff --git a/benchmark/jni-perf/src/JniPerfBenchmark.java b/benchmark/jni-perf/src/JniPerfBenchmark.java index b1b21ce0ba..1e7cc2bf46 100644 --- a/benchmark/jni-perf/src/JniPerfBenchmark.java +++ b/benchmark/jni-perf/src/JniPerfBenchmark.java @@ -14,9 +14,7 @@ * limitations under the License. */ -import com.google.caliper.SimpleBenchmark; - -public class JniPerfBenchmark extends SimpleBenchmark { +public class JniPerfBenchmark { private static final String MSG = "ABCDE"; native void perfJniEmptyCall(); diff --git a/benchmark/jobject-benchmark/src/JObjectBenchmark.java b/benchmark/jobject-benchmark/src/JObjectBenchmark.java index f4c059c58b..90a53b3995 100644 --- a/benchmark/jobject-benchmark/src/JObjectBenchmark.java +++ b/benchmark/jobject-benchmark/src/JObjectBenchmark.java @@ -14,9 +14,7 @@ * limitations under the License. */ -import com.google.caliper.SimpleBenchmark; - -public class JObjectBenchmark extends SimpleBenchmark { +public class JObjectBenchmark { public JObjectBenchmark() { // Make sure to link methods before benchmark starts. System.loadLibrary("artbenchmark"); diff --git a/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java b/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java index be276fe48c..0ad9c36950 100644 --- a/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java +++ b/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java @@ -14,9 +14,7 @@ * limitations under the License. */ -import com.google.caliper.SimpleBenchmark; - -public class ScopedPrimitiveArrayBenchmark extends SimpleBenchmark { +public class ScopedPrimitiveArrayBenchmark { // Measure adds the first and last element of the array by using ScopedPrimitiveArray. static native long measureByteArray(int reps, byte[] arr); static native long measureShortArray(int reps, short[] arr); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 00ff522c9a..be720ad2f3 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1028,6 +1028,9 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const { for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root)); + if (dex_cache == nullptr) { + continue; + } const DexFile* dex_file = dex_cache->GetDexFile(); if (!IsInBootImage(dex_cache)) { dex_cache_count += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u; @@ -1044,6 +1047,9 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const { for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root)); + if (dex_cache == nullptr) { + continue; + } const DexFile* dex_file = dex_cache->GetDexFile(); if (!IsInBootImage(dex_cache)) { non_image_dex_caches += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u; @@ -1055,6 +1061,9 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const { for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root)); + if (dex_cache == nullptr) { + continue; + } const DexFile* dex_file = dex_cache->GetDexFile(); if (!IsInBootImage(dex_cache) && image_dex_files.find(dex_file) != image_dex_files.end()) { dex_caches->Set<false>(i, dex_cache); @@ -1213,18 +1222,17 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { AssignMethodOffset(&m, type, oat_index); } (any_dirty ? dirty_methods_ : clean_methods_) += num_methods; - - // Assign offsets for all runtime methods in the IMT since these may hold conflict tables - // live. - if (as_klass->ShouldHaveEmbeddedImtAndVTable()) { - for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { - ArtMethod* imt_method = as_klass->GetEmbeddedImTableEntry(i, target_ptr_size_); - DCHECK(imt_method != nullptr); - if (imt_method->IsRuntimeMethod() && - !IsInBootImage(imt_method) && - !NativeRelocationAssigned(imt_method)) { - AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index); - } + } + // Assign offsets for all runtime methods in the IMT since these may hold conflict tables + // live. + if (as_klass->ShouldHaveEmbeddedImtAndVTable()) { + for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { + ArtMethod* imt_method = as_klass->GetEmbeddedImTableEntry(i, target_ptr_size_); + DCHECK(imt_method != nullptr); + if (imt_method->IsRuntimeMethod() && + !IsInBootImage(imt_method) && + !NativeRelocationAssigned(imt_method)) { + AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index); } } } diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 4e3ace498d..de04175e38 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1124,7 +1124,7 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke, SlowPathCode* slow_path = nullptr; HInstruction* code_point = invoke->InputAt(1); if (code_point->IsIntConstant()) { - if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > + if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > std::numeric_limits<uint16_t>::max()) { // Always needs the slow-path. We could directly dispatch to it, but this case should be // rare, so for simplicity just put the full slow-path down and branch unconditionally. diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index cc5fd65e2e..6cd1726eb3 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1399,7 +1399,7 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke, SlowPathCodeARM64* slow_path = nullptr; HInstruction* code_point = invoke->InputAt(1); if (code_point->IsIntConstant()) { - if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > 0xFFFFU) { + if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) { // Always needs the slow-path. We could directly dispatch to it, but this case should be // rare, so for simplicity just put the full slow-path down and branch unconditionally. slow_path = new (allocator) IntrinsicSlowPathARM64(invoke); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 20b61f8a1c..fa250a3063 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2072,7 +2072,7 @@ static void GenerateStringIndexOf(HInvoke* invoke, SlowPathCodeMIPS* slow_path = nullptr; HInstruction* code_point = invoke->InputAt(1); if (code_point->IsIntConstant()) { - if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) { + if (!IsUint<16>(code_point->AsIntConstant()->GetValue())) { // Always needs the slow-path. We could directly dispatch to it, // but this case should be rare, so for simplicity just put the // full slow-path down and branch unconditionally. diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 7188e1cc75..6c4e64e4b1 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1568,7 +1568,7 @@ static void GenerateStringIndexOf(HInvoke* invoke, SlowPathCodeMIPS64* slow_path = nullptr; HInstruction* code_point = invoke->InputAt(1); if (code_point->IsIntConstant()) { - if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) { + if (!IsUint<16>(code_point->AsIntConstant()->GetValue())) { // Always needs the slow-path. We could directly dispatch to it, // but this case should be rare, so for simplicity just put the // full slow-path down and branch unconditionally. diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 70c46d5f5a..d66940f3ab 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -1422,7 +1422,7 @@ static void GenerateStringIndexOf(HInvoke* invoke, SlowPathCode* slow_path = nullptr; HInstruction* code_point = invoke->InputAt(1); if (code_point->IsIntConstant()) { - if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > + if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > std::numeric_limits<uint16_t>::max()) { // Always needs the slow-path. We could directly dispatch to it, but this case should be // rare, so for simplicity just put the full slow-path down and branch unconditionally. diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 654c393bc5..2a867697e5 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -1521,7 +1521,7 @@ static void GenerateStringIndexOf(HInvoke* invoke, SlowPathCode* slow_path = nullptr; HInstruction* code_point = invoke->InputAt(1); if (code_point->IsIntConstant()) { - if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > + if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > std::numeric_limits<uint16_t>::max()) { // Always needs the slow-path. We could directly dispatch to it, but this case should be // rare, so for simplicity just put the full slow-path down and branch unconditionally. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 12ea059d3f..c08323a0c6 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2290,7 +2290,7 @@ class HExpression : public HTemplateInstruction<N> { // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow // instruction that branches to the exit block. -class HReturnVoid : public HTemplateInstruction<0> { +class HReturnVoid FINAL : public HTemplateInstruction<0> { public: explicit HReturnVoid(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {} @@ -2305,7 +2305,7 @@ class HReturnVoid : public HTemplateInstruction<0> { // Represents dex's RETURN opcodes. A HReturn is a control flow // instruction that branches to the exit block. -class HReturn : public HTemplateInstruction<1> { +class HReturn FINAL : public HTemplateInstruction<1> { public: explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) { @@ -2320,7 +2320,7 @@ class HReturn : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HReturn); }; -class HPhi : public HInstruction { +class HPhi FINAL : public HInstruction { public: HPhi(ArenaAllocator* arena, uint32_t reg_number, @@ -2424,7 +2424,7 @@ class HPhi : public HInstruction { // The exit instruction is the only instruction of the exit block. // Instructions aborting the method (HThrow and HReturn) must branch to the // exit block. -class HExit : public HTemplateInstruction<0> { +class HExit FINAL : public HTemplateInstruction<0> { public: explicit HExit(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {} @@ -2437,7 +2437,7 @@ class HExit : public HTemplateInstruction<0> { }; // Jumps from one block to another. -class HGoto : public HTemplateInstruction<0> { +class HGoto FINAL : public HTemplateInstruction<0> { public: explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {} @@ -2477,7 +2477,7 @@ class HConstant : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HConstant); }; -class HNullConstant : public HConstant { +class HNullConstant FINAL : public HConstant { public: bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -2501,7 +2501,7 @@ class HNullConstant : public HConstant { // Constants of the type int. Those can be from Dex instructions, or // synthesized (for example with the if-eqz instruction). -class HIntConstant : public HConstant { +class HIntConstant FINAL : public HConstant { public: int32_t GetValue() const { return value_; } @@ -2542,7 +2542,7 @@ class HIntConstant : public HConstant { DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; -class HLongConstant : public HConstant { +class HLongConstant FINAL : public HConstant { public: int64_t GetValue() const { return value_; } @@ -2572,7 +2572,7 @@ class HLongConstant : public HConstant { DISALLOW_COPY_AND_ASSIGN(HLongConstant); }; -class HFloatConstant : public HConstant { +class HFloatConstant FINAL : public HConstant { public: float GetValue() const { return value_; } @@ -2625,7 +2625,7 @@ class HFloatConstant : public HConstant { DISALLOW_COPY_AND_ASSIGN(HFloatConstant); }; -class HDoubleConstant : public HConstant { +class HDoubleConstant FINAL : public HConstant { public: double GetValue() const { return value_; } @@ -2678,7 +2678,7 @@ class HDoubleConstant : public HConstant { // Conditional branch. A block ending with an HIf instruction must have // two successors. -class HIf : public HTemplateInstruction<1> { +class HIf FINAL : public HTemplateInstruction<1> { public: explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) { @@ -2707,7 +2707,7 @@ class HIf : public HTemplateInstruction<1> { // non-exceptional control flow. // Normal-flow successor is stored at index zero, exception handlers under // higher indices in no particular order. -class HTryBoundary : public HTemplateInstruction<0> { +class HTryBoundary FINAL : public HTemplateInstruction<0> { public: enum class BoundaryKind { kEntry, @@ -2765,7 +2765,7 @@ class HTryBoundary : public HTemplateInstruction<0> { }; // Deoptimize to interpreter, upon checking a condition. -class HDeoptimize : public HTemplateInstruction<1> { +class HDeoptimize FINAL : public HTemplateInstruction<1> { public: // We set CanTriggerGC to prevent any intermediate address to be live // at the point of the `HDeoptimize`. @@ -2790,7 +2790,7 @@ class HDeoptimize : public HTemplateInstruction<1> { // Represents the ArtMethod that was passed as a first argument to // the method. It is used by instructions that depend on it, like // instructions that work with the dex cache. -class HCurrentMethod : public HExpression<0> { +class HCurrentMethod FINAL : public HExpression<0> { public: explicit HCurrentMethod(Primitive::Type type, uint32_t dex_pc = kNoDexPc) : HExpression(type, SideEffects::None(), dex_pc) {} @@ -2803,7 +2803,7 @@ class HCurrentMethod : public HExpression<0> { // Fetches an ArtMethod from the virtual table or the interface method table // of a class. -class HClassTableGet : public HExpression<1> { +class HClassTableGet FINAL : public HExpression<1> { public: enum class TableKind { kVTable, @@ -2850,7 +2850,7 @@ class HClassTableGet : public HExpression<1> { // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will // have one successor for each entry in the switch table, and the final successor // will be the block containing the next Dex opcode. -class HPackedSwitch : public HTemplateInstruction<1> { +class HPackedSwitch FINAL : public HTemplateInstruction<1> { public: HPackedSwitch(int32_t start_value, uint32_t num_entries, @@ -3095,7 +3095,7 @@ class HCondition : public HBinaryOperation { }; // Instruction to check if two inputs are equal to each other. -class HEqual : public HCondition { +class HEqual FINAL : public HCondition { public: HEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3139,7 +3139,7 @@ class HEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HEqual); }; -class HNotEqual : public HCondition { +class HNotEqual FINAL : public HCondition { public: HNotEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3182,7 +3182,7 @@ class HNotEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HNotEqual); }; -class HLessThan : public HCondition { +class HLessThan FINAL : public HCondition { public: HLessThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3219,7 +3219,7 @@ class HLessThan : public HCondition { DISALLOW_COPY_AND_ASSIGN(HLessThan); }; -class HLessThanOrEqual : public HCondition { +class HLessThanOrEqual FINAL : public HCondition { public: HLessThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3256,7 +3256,7 @@ class HLessThanOrEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual); }; -class HGreaterThan : public HCondition { +class HGreaterThan FINAL : public HCondition { public: HGreaterThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3293,7 +3293,7 @@ class HGreaterThan : public HCondition { DISALLOW_COPY_AND_ASSIGN(HGreaterThan); }; -class HGreaterThanOrEqual : public HCondition { +class HGreaterThanOrEqual FINAL : public HCondition { public: HGreaterThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3330,7 +3330,7 @@ class HGreaterThanOrEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual); }; -class HBelow : public HCondition { +class HBelow FINAL : public HCondition { public: HBelow(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3370,7 +3370,7 @@ class HBelow : public HCondition { DISALLOW_COPY_AND_ASSIGN(HBelow); }; -class HBelowOrEqual : public HCondition { +class HBelowOrEqual FINAL : public HCondition { public: HBelowOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3410,7 +3410,7 @@ class HBelowOrEqual : public HCondition { DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual); }; -class HAbove : public HCondition { +class HAbove FINAL : public HCondition { public: HAbove(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3450,7 +3450,7 @@ class HAbove : public HCondition { DISALLOW_COPY_AND_ASSIGN(HAbove); }; -class HAboveOrEqual : public HCondition { +class HAboveOrEqual FINAL : public HCondition { public: HAboveOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} @@ -3492,7 +3492,7 @@ class HAboveOrEqual : public HCondition { // Instruction to check how two inputs compare to each other. // Result is 0 if input0 == input1, 1 if input0 > input1, or -1 if input0 < input1. -class HCompare : public HBinaryOperation { +class HCompare FINAL : public HBinaryOperation { public: // Note that `comparison_type` is the type of comparison performed // between the comparison's inputs, not the type of the instantiated @@ -3581,7 +3581,7 @@ class HCompare : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HCompare); }; -class HNewInstance : public HExpression<2> { +class HNewInstance FINAL : public HExpression<2> { public: HNewInstance(HInstruction* cls, HCurrentMethod* current_method, @@ -3784,7 +3784,7 @@ class HInvoke : public HInstruction { DISALLOW_COPY_AND_ASSIGN(HInvoke); }; -class HInvokeUnresolved : public HInvoke { +class HInvokeUnresolved FINAL : public HInvoke { public: HInvokeUnresolved(ArenaAllocator* arena, uint32_t number_of_arguments, @@ -3807,7 +3807,7 @@ class HInvokeUnresolved : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved); }; -class HInvokeStaticOrDirect : public HInvoke { +class HInvokeStaticOrDirect FINAL : public HInvoke { public: // Requirements of this method call regarding the class // initialization (clinit) check of its declaring class. @@ -4096,7 +4096,7 @@ class HInvokeStaticOrDirect : public HInvoke { std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs); std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckRequirement rhs); -class HInvokeVirtual : public HInvoke { +class HInvokeVirtual FINAL : public HInvoke { public: HInvokeVirtual(ArenaAllocator* arena, uint32_t number_of_arguments, @@ -4122,7 +4122,7 @@ class HInvokeVirtual : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual); }; -class HInvokeInterface : public HInvoke { +class HInvokeInterface FINAL : public HInvoke { public: HInvokeInterface(ArenaAllocator* arena, uint32_t number_of_arguments, @@ -4149,7 +4149,7 @@ class HInvokeInterface : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeInterface); }; -class HNeg : public HUnaryOperation { +class HNeg FINAL : public HUnaryOperation { public: HNeg(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc) : HUnaryOperation(result_type, input, dex_pc) { @@ -4177,7 +4177,7 @@ class HNeg : public HUnaryOperation { DISALLOW_COPY_AND_ASSIGN(HNeg); }; -class HNewArray : public HExpression<2> { +class HNewArray FINAL : public HExpression<2> { public: HNewArray(HInstruction* length, HCurrentMethod* current_method, @@ -4216,7 +4216,7 @@ class HNewArray : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HNewArray); }; -class HAdd : public HBinaryOperation { +class HAdd FINAL : public HBinaryOperation { public: HAdd(Primitive::Type result_type, HInstruction* left, @@ -4251,7 +4251,7 @@ class HAdd : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HAdd); }; -class HSub : public HBinaryOperation { +class HSub FINAL : public HBinaryOperation { public: HSub(Primitive::Type result_type, HInstruction* left, @@ -4284,7 +4284,7 @@ class HSub : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HSub); }; -class HMul : public HBinaryOperation { +class HMul FINAL : public HBinaryOperation { public: HMul(Primitive::Type result_type, HInstruction* left, @@ -4319,7 +4319,7 @@ class HMul : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HMul); }; -class HDiv : public HBinaryOperation { +class HDiv FINAL : public HBinaryOperation { public: HDiv(Primitive::Type result_type, HInstruction* left, @@ -4371,7 +4371,7 @@ class HDiv : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HDiv); }; -class HRem : public HBinaryOperation { +class HRem FINAL : public HBinaryOperation { public: HRem(Primitive::Type result_type, HInstruction* left, @@ -4422,7 +4422,7 @@ class HRem : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HRem); }; -class HDivZeroCheck : public HExpression<1> { +class HDivZeroCheck FINAL : public HExpression<1> { public: // `HDivZeroCheck` can trigger GC, as it may call the `ArithmeticException` // constructor. @@ -4448,7 +4448,7 @@ class HDivZeroCheck : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck); }; -class HShl : public HBinaryOperation { +class HShl FINAL : public HBinaryOperation { public: HShl(Primitive::Type result_type, HInstruction* value, @@ -4494,7 +4494,7 @@ class HShl : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HShl); }; -class HShr : public HBinaryOperation { +class HShr FINAL : public HBinaryOperation { public: HShr(Primitive::Type result_type, HInstruction* value, @@ -4540,7 +4540,7 @@ class HShr : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HShr); }; -class HUShr : public HBinaryOperation { +class HUShr FINAL : public HBinaryOperation { public: HUShr(Primitive::Type result_type, HInstruction* value, @@ -4588,7 +4588,7 @@ class HUShr : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HUShr); }; -class HAnd : public HBinaryOperation { +class HAnd FINAL : public HBinaryOperation { public: HAnd(Primitive::Type result_type, HInstruction* left, @@ -4625,7 +4625,7 @@ class HAnd : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HAnd); }; -class HOr : public HBinaryOperation { +class HOr FINAL : public HBinaryOperation { public: HOr(Primitive::Type result_type, HInstruction* left, @@ -4662,7 +4662,7 @@ class HOr : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HOr); }; -class HXor : public HBinaryOperation { +class HXor FINAL : public HBinaryOperation { public: HXor(Primitive::Type result_type, HInstruction* left, @@ -4699,7 +4699,7 @@ class HXor : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HXor); }; -class HRor : public HBinaryOperation { +class HRor FINAL : public HBinaryOperation { public: HRor(Primitive::Type result_type, HInstruction* value, HInstruction* distance) : HBinaryOperation(result_type, value, distance) { @@ -4752,7 +4752,7 @@ class HRor : public HBinaryOperation { // The value of a parameter in this method. Its location depends on // the calling convention. -class HParameterValue : public HExpression<0> { +class HParameterValue FINAL : public HExpression<0> { public: HParameterValue(const DexFile& dex_file, uint16_t type_index, @@ -4794,7 +4794,7 @@ class HParameterValue : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; -class HNot : public HUnaryOperation { +class HNot FINAL : public HUnaryOperation { public: HNot(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc) : HUnaryOperation(result_type, input, dex_pc) {} @@ -4827,7 +4827,7 @@ class HNot : public HUnaryOperation { DISALLOW_COPY_AND_ASSIGN(HNot); }; -class HBooleanNot : public HUnaryOperation { +class HBooleanNot FINAL : public HUnaryOperation { public: explicit HBooleanNot(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HUnaryOperation(Primitive::Type::kPrimBoolean, input, dex_pc) {} @@ -4864,7 +4864,7 @@ class HBooleanNot : public HUnaryOperation { DISALLOW_COPY_AND_ASSIGN(HBooleanNot); }; -class HTypeConversion : public HExpression<1> { +class HTypeConversion FINAL : public HExpression<1> { public: // Instantiate a type conversion of `input` to `result_type`. HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc) @@ -4907,7 +4907,7 @@ class HTypeConversion : public HExpression<1> { static constexpr uint32_t kNoRegNumber = -1; -class HNullCheck : public HExpression<1> { +class HNullCheck FINAL : public HExpression<1> { public: // `HNullCheck` can trigger GC, as it may call the `NullPointerException` // constructor. @@ -4969,7 +4969,7 @@ class FieldInfo : public ValueObject { const Handle<mirror::DexCache> dex_cache_; }; -class HInstanceFieldGet : public HExpression<1> { +class HInstanceFieldGet FINAL : public HExpression<1> { public: HInstanceFieldGet(HInstruction* value, Primitive::Type field_type, @@ -5021,7 +5021,7 @@ class HInstanceFieldGet : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HInstanceFieldGet); }; -class HInstanceFieldSet : public HTemplateInstruction<2> { +class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { public: HInstanceFieldSet(HInstruction* object, HInstruction* value, @@ -5072,7 +5072,7 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; -class HArrayGet : public HExpression<2> { +class HArrayGet FINAL : public HExpression<2> { public: HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc) : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) { @@ -5118,7 +5118,7 @@ class HArrayGet : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArrayGet); }; -class HArraySet : public HTemplateInstruction<3> { +class HArraySet FINAL : public HTemplateInstruction<3> { public: HArraySet(HInstruction* array, HInstruction* index, @@ -5218,7 +5218,7 @@ class HArraySet : public HTemplateInstruction<3> { DISALLOW_COPY_AND_ASSIGN(HArraySet); }; -class HArrayLength : public HExpression<1> { +class HArrayLength FINAL : public HExpression<1> { public: HArrayLength(HInstruction* array, uint32_t dex_pc) : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) { @@ -5254,7 +5254,7 @@ class HArrayLength : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HArrayLength); }; -class HBoundsCheck : public HExpression<2> { +class HBoundsCheck FINAL : public HExpression<2> { public: // `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException` // constructor. @@ -5282,7 +5282,7 @@ class HBoundsCheck : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); }; -class HSuspendCheck : public HTemplateInstruction<0> { +class HSuspendCheck FINAL : public HTemplateInstruction<0> { public: explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {} @@ -5324,7 +5324,7 @@ class HNativeDebugInfo : public HTemplateInstruction<0> { /** * Instruction to load a Class object. */ -class HLoadClass : public HExpression<1> { +class HLoadClass FINAL : public HExpression<1> { public: HLoadClass(HCurrentMethod* current_method, uint16_t type_index, @@ -5428,7 +5428,7 @@ class HLoadClass : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HLoadClass); }; -class HLoadString : public HExpression<1> { +class HLoadString FINAL : public HExpression<1> { public: // Determines how to load the String. enum class LoadKind { @@ -5630,7 +5630,7 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { /** * Performs an initialization check on its Class object input. */ -class HClinitCheck : public HExpression<1> { +class HClinitCheck FINAL : public HExpression<1> { public: HClinitCheck(HLoadClass* constant, uint32_t dex_pc) : HExpression( @@ -5660,7 +5660,7 @@ class HClinitCheck : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HClinitCheck); }; -class HStaticFieldGet : public HExpression<1> { +class HStaticFieldGet FINAL : public HExpression<1> { public: HStaticFieldGet(HInstruction* cls, Primitive::Type field_type, @@ -5709,7 +5709,7 @@ class HStaticFieldGet : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet); }; -class HStaticFieldSet : public HTemplateInstruction<2> { +class HStaticFieldSet FINAL : public HTemplateInstruction<2> { public: HStaticFieldSet(HInstruction* cls, HInstruction* value, @@ -5757,7 +5757,7 @@ class HStaticFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; -class HUnresolvedInstanceFieldGet : public HExpression<1> { +class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { public: HUnresolvedInstanceFieldGet(HInstruction* obj, Primitive::Type field_type, @@ -5782,7 +5782,7 @@ class HUnresolvedInstanceFieldGet : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet); }; -class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { +class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { public: HUnresolvedInstanceFieldSet(HInstruction* obj, HInstruction* value, @@ -5820,7 +5820,7 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); }; -class HUnresolvedStaticFieldGet : public HExpression<0> { +class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { public: HUnresolvedStaticFieldGet(Primitive::Type field_type, uint32_t field_index, @@ -5843,7 +5843,7 @@ class HUnresolvedStaticFieldGet : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet); }; -class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { +class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { public: HUnresolvedStaticFieldSet(HInstruction* value, Primitive::Type field_type, @@ -5880,7 +5880,7 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { }; // Implement the move-exception DEX instruction. -class HLoadException : public HExpression<0> { +class HLoadException FINAL : public HExpression<0> { public: explicit HLoadException(uint32_t dex_pc = kNoDexPc) : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc) {} @@ -5895,7 +5895,7 @@ class HLoadException : public HExpression<0> { // Implicit part of move-exception which clears thread-local exception storage. // Must not be removed because the runtime expects the TLS to get cleared. -class HClearException : public HTemplateInstruction<0> { +class HClearException FINAL : public HTemplateInstruction<0> { public: explicit HClearException(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::AllWrites(), dex_pc) {} @@ -5906,7 +5906,7 @@ class HClearException : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HClearException); }; -class HThrow : public HTemplateInstruction<1> { +class HThrow FINAL : public HTemplateInstruction<1> { public: HThrow(HInstruction* exception, uint32_t dex_pc) : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { @@ -5943,7 +5943,7 @@ enum class TypeCheckKind { std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); -class HInstanceOf : public HExpression<2> { +class HInstanceOf FINAL : public HExpression<2> { public: HInstanceOf(HInstruction* object, HLoadClass* constant, @@ -5997,7 +5997,7 @@ class HInstanceOf : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HInstanceOf); }; -class HBoundType : public HExpression<1> { +class HBoundType FINAL : public HExpression<1> { public: HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc), @@ -6041,7 +6041,7 @@ class HBoundType : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HBoundType); }; -class HCheckCast : public HTemplateInstruction<2> { +class HCheckCast FINAL : public HTemplateInstruction<2> { public: HCheckCast(HInstruction* object, HLoadClass* constant, @@ -6086,7 +6086,7 @@ class HCheckCast : public HTemplateInstruction<2> { DISALLOW_COPY_AND_ASSIGN(HCheckCast); }; -class HMemoryBarrier : public HTemplateInstruction<0> { +class HMemoryBarrier FINAL : public HTemplateInstruction<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction( @@ -6111,7 +6111,7 @@ class HMemoryBarrier : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier); }; -class HMonitorOperation : public HTemplateInstruction<1> { +class HMonitorOperation FINAL : public HTemplateInstruction<1> { public: enum class OperationKind { kEnter, @@ -6156,7 +6156,7 @@ class HMonitorOperation : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); }; -class HSelect : public HExpression<3> { +class HSelect FINAL : public HExpression<3> { public: HSelect(HInstruction* condition, HInstruction* true_value, @@ -6269,7 +6269,7 @@ std::ostream& operator<<(std::ostream& os, const MoveOperands& rhs); static constexpr size_t kDefaultNumberOfMoves = 4; -class HParallelMove : public HTemplateInstruction<0> { +class HParallelMove FINAL : public HTemplateInstruction<0> { public: explicit HParallelMove(ArenaAllocator* arena, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc), diff --git a/compiler/optimizing/nodes_arm.h b/compiler/optimizing/nodes_arm.h index 6a1dbb9e70..371e8ef6bb 100644 --- a/compiler/optimizing/nodes_arm.h +++ b/compiler/optimizing/nodes_arm.h @@ -19,7 +19,7 @@ namespace art { -class HArmDexCacheArraysBase : public HExpression<0> { +class HArmDexCacheArraysBase FINAL : public HExpression<0> { public: explicit HArmDexCacheArraysBase(const DexFile& dex_file) : HExpression(Primitive::kPrimInt, SideEffects::None(), kNoDexPc), diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 173852a55d..737aece9c8 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -21,7 +21,7 @@ namespace art { -class HArm64DataProcWithShifterOp : public HExpression<2> { +class HArm64DataProcWithShifterOp FINAL : public HExpression<2> { public: enum OpKind { kLSL, // Logical shift left. @@ -97,7 +97,7 @@ std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::Op // This instruction computes an intermediate address pointing in the 'middle' of an object. The // result pointer cannot be handled by GC, so extra care is taken to make sure that this value is // never used across anything that can trigger GC. -class HArm64IntermediateAddress : public HExpression<2> { +class HArm64IntermediateAddress FINAL : public HExpression<2> { public: HArm64IntermediateAddress(HInstruction* base_address, HInstruction* offset, uint32_t dex_pc) : HExpression(Primitive::kPrimNot, SideEffects::DependsOnGC(), dex_pc) { diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h index c10c718ff4..bdcf54a6fb 100644 --- a/compiler/optimizing/nodes_shared.h +++ b/compiler/optimizing/nodes_shared.h @@ -19,7 +19,7 @@ namespace art { -class HMultiplyAccumulate : public HExpression<3> { +class HMultiplyAccumulate FINAL : public HExpression<3> { public: HMultiplyAccumulate(Primitive::Type type, InstructionKind op, @@ -53,7 +53,7 @@ class HMultiplyAccumulate : public HExpression<3> { DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); }; -class HBitwiseNegatedRight : public HBinaryOperation { +class HBitwiseNegatedRight FINAL : public HBinaryOperation { public: HBitwiseNegatedRight(Primitive::Type result_type, InstructionKind op, diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h index 0b3a84d3d3..c3696b5936 100644 --- a/compiler/optimizing/nodes_x86.h +++ b/compiler/optimizing/nodes_x86.h @@ -20,7 +20,7 @@ namespace art { // Compute the address of the method for X86 Constant area support. -class HX86ComputeBaseMethodAddress : public HExpression<0> { +class HX86ComputeBaseMethodAddress FINAL : public HExpression<0> { public: // Treat the value as an int32_t, but it is really a 32 bit native pointer. HX86ComputeBaseMethodAddress() @@ -33,7 +33,7 @@ class HX86ComputeBaseMethodAddress : public HExpression<0> { }; // Load a constant value from the constant table. -class HX86LoadFromConstantTable : public HExpression<2> { +class HX86LoadFromConstantTable FINAL : public HExpression<2> { public: HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base, HConstant* constant) @@ -57,7 +57,7 @@ class HX86LoadFromConstantTable : public HExpression<2> { }; // Version of HNeg with access to the constant table for FP types. -class HX86FPNeg : public HExpression<2> { +class HX86FPNeg FINAL : public HExpression<2> { public: HX86FPNeg(Primitive::Type result_type, HInstruction* input, @@ -76,7 +76,7 @@ class HX86FPNeg : public HExpression<2> { }; // X86 version of HPackedSwitch that holds a pointer to the base method address. -class HX86PackedSwitch : public HTemplateInstruction<2> { +class HX86PackedSwitch FINAL : public HTemplateInstruction<2> { public: HX86PackedSwitch(int32_t start_value, int32_t num_entries, diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index be38336f03..cb274dcc09 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1312,7 +1312,7 @@ class Dex2Oat FINAL { if (IsBootImage() && image_filenames_.size() > 1) { // If we're compiling the boot image, store the boot classpath into the Key-Value store. // We need this for the multi-image case. - key_value_store_->Put(OatHeader::kBootClassPath, GetMultiImageBootClassPath()); + key_value_store_->Put(OatHeader::kBootClassPathKey, GetMultiImageBootClassPath()); } if (!IsBootImage()) { @@ -1348,12 +1348,22 @@ class Dex2Oat FINAL { // Open dex files for class path. const std::vector<std::string> class_path_locations = GetClassPathLocations(runtime_->GetClassPathString()); - OpenClassPathFiles(class_path_locations, &class_path_files_); + OpenClassPathFiles(class_path_locations, + &class_path_files_, + &opened_oat_files_, + runtime_->GetInstructionSet()); // Store the classpath we have right now. std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_); - key_value_store_->Put(OatHeader::kClassPathKey, - OatFile::EncodeDexFileDependencies(class_path_files)); + std::string encoded_class_path; + if (class_path_locations.size() == 1 && + class_path_locations[0] == OatFile::kSpecialSharedLibrary) { + // When passing the special shared library as the classpath, it is the only path. + encoded_class_path = OatFile::kSpecialSharedLibrary; + } else { + encoded_class_path = OatFile::EncodeDexFileDependencies(class_path_files); + } + key_value_store_->Put(OatHeader::kClassPathKey, encoded_class_path); } // Now that we have finalized key_value_store_, start writing the oat file. @@ -1964,14 +1974,37 @@ class Dex2Oat FINAL { return parsed; } - // Opens requested class path files and appends them to opened_dex_files. + // Opens requested class path files and appends them to opened_dex_files. If the dex files have + // been stripped, this opens them from their oat files and appends them to opened_oat_files. static void OpenClassPathFiles(const std::vector<std::string>& class_path_locations, - std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { - DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles out-param is nullptr"; + std::vector<std::unique_ptr<const DexFile>>* opened_dex_files, + std::vector<std::unique_ptr<OatFile>>* opened_oat_files, + InstructionSet isa) { + DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles dex out-param is nullptr"; + DCHECK(opened_oat_files != nullptr) << "OpenClassPathFiles oat out-param is nullptr"; for (const std::string& location : class_path_locations) { + // Stop early if we detect the special shared library, which may be passed as the classpath + // for dex2oat when we want to skip the shared libraries check. + if (location == OatFile::kSpecialSharedLibrary) { + break; + } std::string error_msg; if (!DexFile::Open(location.c_str(), location.c_str(), &error_msg, opened_dex_files)) { - LOG(WARNING) << "Failed to open dex file '" << location << "': " << error_msg; + // If we fail to open the dex file because it's been stripped, try to open the dex file + // from its corresponding oat file. + OatFileAssistant oat_file_assistant(location.c_str(), isa, false, false); + std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile()); + if (oat_file == nullptr) { + LOG(WARNING) << "Failed to open dex file and associated oat file for '" << location + << "': " << error_msg; + } else { + std::vector<std::unique_ptr<const DexFile>> oat_dex_files = + oat_file_assistant.LoadDexFiles(*oat_file, location.c_str()); + opened_oat_files->push_back(std::move(oat_file)); + opened_dex_files->insert(opened_dex_files->end(), + std::make_move_iterator(oat_dex_files.begin()), + std::make_move_iterator(oat_dex_files.end())); + } } } } @@ -2441,6 +2474,7 @@ class Dex2Oat FINAL { std::unique_ptr<CompilerDriver> driver_; std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_; + std::vector<std::unique_ptr<OatFile>> opened_oat_files_; std::vector<std::unique_ptr<const DexFile>> opened_dex_files_; std::vector<const DexFile*> no_inline_from_dex_files_; diff --git a/libart_fake/Android.mk b/libart_fake/Android.mk new file mode 100644 index 0000000000..ed868a5bd5 --- /dev/null +++ b/libart_fake/Android.mk @@ -0,0 +1,34 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libart_fake +LOCAL_INSTALLED_MODULE_STEM := libart.so +LOCAL_SDK_VERSION := 9 +LOCAL_CPP_EXTENSION := .cc +LOCAL_SRC_FILES := fake.cc +LOCAL_SHARED_LIBRARIES := liblog + +ifdef TARGET_2ND_ARCH + LOCAL_MODULE_PATH_32 := $(TARGET_OUT)/fake-libs + LOCAL_MODULE_PATH_64 := $(TARGET_OUT)/fake-libs64 +else + LOCAL_MODULE_PATH := $(TARGET_OUT)/fake-libs +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/libart_fake/README.md b/libart_fake/README.md new file mode 100644 index 0000000000..6e3621e55c --- /dev/null +++ b/libart_fake/README.md @@ -0,0 +1,5 @@ +libart_fake +==== + +A fake libart made to satisfy some misbehaving apps that will attempt to link +against libart.so. diff --git a/libart_fake/fake.cc b/libart_fake/fake.cc new file mode 100644 index 0000000000..884242101d --- /dev/null +++ b/libart_fake/fake.cc @@ -0,0 +1,46 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "libart_fake" + +#include <android/log.h> + +#define LOGIT(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +namespace art { +class Dbg { + public: + void SuspendVM(); + void ResumeVM(); +}; + +class FaultManager { + public: + void EnsureArtActionInFrontOfSignalChain(); +}; + +void Dbg::SuspendVM() { + LOGIT("Linking to and calling into libart.so internal functions is not supported. " + "This call to '%s' is being ignored.", __func__); +} +void Dbg::ResumeVM() { + LOGIT("Linking to and calling into libart.so internal functions is not supported. " + "This call to '%s' is being ignored.", __func__); +} +void FaultManager::EnsureArtActionInFrontOfSignalChain() { + LOGIT("Linking to and calling into libart.so internal functions is not supported. " + "This call to '%s' is being ignored.", __func__); +} +}; // namespace art diff --git a/profman/profman.cc b/profman/profman.cc index 37a560d203..4d9276f15b 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -189,7 +189,6 @@ class ProfMan FINAL { return -1; } std::string dump = info.DumpInfo(/*dex_files*/ nullptr); - info.Save(fd); std::cout << dump << "\n"; return 0; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e9b8643223..8fcb6b25fa 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1063,9 +1063,8 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { return true; } -static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, - mirror::ClassLoader* class_loader) - SHARED_REQUIRES(Locks::mutator_lock_) { +bool ClassLinker::IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader) { return class_loader == nullptr || class_loader->GetClass() == soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader); @@ -1106,7 +1105,7 @@ static bool FlattenPathClassLoader(mirror::ClassLoader* class_loader, soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements); CHECK(dex_path_list_field != nullptr); CHECK(dex_elements_field != nullptr); - while (!IsBootClassLoader(soa, class_loader)) { + while (!ClassLinker::IsBootClassLoader(soa, class_loader)) { if (class_loader->GetClass() != soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) { *error_msg = StringPrintf("Unknown class loader type %s", PrettyTypeOf(class_loader).c_str()); @@ -5317,6 +5316,19 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex()); uint16_t super_class_idx = class_def.superclass_idx_; if (super_class_idx != DexFile::kDexNoIndex16) { + // Check that a class does not inherit from itself directly. + // + // TODO: This is a cheap check to detect the straightforward case + // of a class extending itself (b/28685551), but we should do a + // proper cycle detection on loaded classes, to detect all cases + // of class circularity errors (b/28830038). + if (super_class_idx == class_def.class_idx_) { + ThrowClassCircularityError(klass.Get(), + "Class %s extends itself", + PrettyDescriptor(klass.Get()).c_str()); + return false; + } + mirror::Class* super_class = ResolveType(dex_file, super_class_idx, klass.Get()); if (super_class == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); @@ -7815,7 +7827,8 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { return descriptor; } -jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) { +jobject ClassLinker::CreatePathClassLoader(Thread* self, + const std::vector<const DexFile*>& dex_files) { // SOAAlreadyRunnable is protected, and we need something to add a global reference. // We could move the jobject to the callers, but all call-sites do this... ScopedObjectAccessUnchecked soa(self); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index d1c8172630..f6ce545a19 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -255,7 +255,7 @@ class ClassLinker { SHARED_REQUIRES(Locks::mutator_lock_); // Resolve a Type with the given index from the DexFile, storing the - // result in the DexCache. The referrer is used to identity the + // result in the DexCache. The referrer is used to identify the // target DexCache and ClassLoader to use for resolution. mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, mirror::Class* referrer) SHARED_REQUIRES(Locks::mutator_lock_) @@ -560,7 +560,7 @@ class ClassLinker { // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files. // Note: the objects are not completely set up. Do not use this outside of tests and the compiler. - jobject CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) + jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); @@ -611,6 +611,10 @@ class ClassLinker { const std::set<DexCacheResolvedClasses>& classes) REQUIRES(!dex_lock_); + static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader) + SHARED_REQUIRES(Locks::mutator_lock_); + ArtMethod* AddMethodToConflictTable(mirror::Class* klass, ArtMethod* conflict_method, ArtMethod* interface_method, diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index b4208fe054..75cce424e9 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -137,13 +137,21 @@ void ThrowClassCircularityError(mirror::Class* c) { ThrowException("Ljava/lang/ClassCircularityError;", c, msg.str().c_str()); } +void ThrowClassCircularityError(mirror::Class* c, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowException("Ljava/lang/ClassCircularityError;", c, fmt, &args); + va_end(args); +} + // ClassFormatError void ThrowClassFormatError(mirror::Class* referrer, const char* fmt, ...) { va_list args; va_start(args, fmt); ThrowException("Ljava/lang/ClassFormatError;", referrer, fmt, &args); - va_end(args);} + va_end(args); +} // IllegalAccessError diff --git a/runtime/common_throws.h b/runtime/common_throws.h index 39c4e52b15..c3a1f09db3 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -58,6 +58,9 @@ void ThrowArrayStoreException(mirror::Class* element_class, mirror::Class* array void ThrowClassCircularityError(mirror::Class* c) SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR; +void ThrowClassCircularityError(mirror::Class* c, const char* fmt, ...) + SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR; + // ClassCastException void ThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 55f68d3f2c..80056423a9 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -582,7 +582,7 @@ class UpdateEntryPointsClassVisitor : public ClassVisitor { if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) && !m.IsNative() && !m.IsProxyMethod()) { - instrumentation_->UpdateMethodsCode(&m, GetQuickToInterpreterBridge()); + instrumentation_->UpdateMethodsCodeFromDebugger(&m, GetQuickToInterpreterBridge()); } } return true; diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 63f3f08bad..af12abfc08 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -518,7 +518,7 @@ const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor, size_t ha return (class_def_idx != DexFile::kDexNoIndex) ? &GetClassDef(class_def_idx) : nullptr; } - // Fast path for rate no class defs case. + // Fast path for rare no class defs case. const uint32_t num_class_defs = NumClassDefs(); if (num_class_defs == 0) { return nullptr; @@ -548,8 +548,8 @@ const DexFile::ClassDef* DexFile::FindClassDef(uint16_t type_idx) const { } const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_klass, - const DexFile::StringId& name, - const DexFile::TypeId& type) const { + const DexFile::StringId& name, + const DexFile::TypeId& type) const { // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx const uint16_t class_idx = GetIndexForTypeId(declaring_klass); const uint32_t name_idx = GetIndexForStringId(name); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index fa540c0f9b..cdd5f2e120 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -311,7 +311,7 @@ Heap::Heap(size_t initial_size, const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader(); const char* boot_classpath = - boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath); + boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey); if (boot_classpath == nullptr) { continue; } diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 34bc458575..61119f8499 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -687,8 +687,7 @@ void Instrumentation::ResetQuickAllocEntryPoints() { } } -void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_code) { - DCHECK(method->GetDeclaringClass()->IsResolved()); +void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code) { const void* new_quick_code; if (LIKELY(!instrumentation_stubs_installed_)) { new_quick_code = quick_code; @@ -710,6 +709,18 @@ void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_cod UpdateEntrypoints(method, new_quick_code); } +void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_code) { + DCHECK(method->GetDeclaringClass()->IsResolved()); + UpdateMethodsCodeImpl(method, quick_code); +} + +void Instrumentation::UpdateMethodsCodeFromDebugger(ArtMethod* method, const void* quick_code) { + // When debugger attaches, we may update the entry points of all methods of a class + // to the interpreter bridge. A method's declaring class might not be in resolved + // state yet in that case. + UpdateMethodsCodeImpl(method, quick_code); +} + bool Instrumentation::AddDeoptimizedMethod(ArtMethod* method) { if (IsDeoptimizedMethod(method)) { // Already in the map. Return. diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index a4c3d41537..ce6ead453f 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -227,6 +227,10 @@ class Instrumentation { void UpdateMethodsCode(ArtMethod* method, const void* quick_code) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_); + // Update the code of a method respecting any installed stubs from debugger. + void UpdateMethodsCodeFromDebugger(ArtMethod* method, const void* quick_code) + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_); + // Get the quick code for the given method. More efficient than asking the class linker as it // will short-cut to GetCode if instrumentation and static method resolution stubs aren't // installed. @@ -493,6 +497,9 @@ class Instrumentation { SHARED_REQUIRES(Locks::mutator_lock_, deoptimized_methods_lock_); bool IsDeoptimizedMethodsEmpty() const SHARED_REQUIRES(Locks::mutator_lock_, deoptimized_methods_lock_); + void UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code) + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_); + // Have we hijacked ArtMethod::code_ so that it calls instrumentation/interpreter code? bool instrumentation_stubs_installed_; diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index dcc6300636..b6b7eb1841 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -565,6 +565,7 @@ class JitCompileTask FINAL : public Task { VLOG(jit) << "Start profiling " << PrettyMethod(method_); } } + ProfileSaver::NotifyJitActivity(); } void Finalize() OVERRIDE { diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index cf46893311..8358ce3601 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -33,14 +33,15 @@ namespace art { // TODO: read the constants from ProfileOptions, // Add a random delay each time we go to sleep so that we don't hammer the CPU // with all profile savers running at the same time. -static constexpr const uint64_t kRandomDelayMaxMs = 30 * 1000; // 30 seconds -static constexpr const uint64_t kMaxBackoffMs = 10 * 60 * 1000; // 10 minutes -static constexpr const uint64_t kSavePeriodMs = 20 * 1000; // 20 seconds +static constexpr const uint64_t kMinSavePeriodNs = MsToNs(20 * 1000); // 20 seconds static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds -static constexpr const double kBackoffCoef = 2.0; static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10; static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10; +static constexpr const uint32_t kMinimumNumberOfNotificationBeforeWake = + kMinimumNumberOfMethodsToSave; +static constexpr const uint32_t kMaximumNumberOfNotificationBeforeWake = 50; + ProfileSaver* ProfileSaver::instance_ = nullptr; pthread_t ProfileSaver::profiler_pthread_ = 0U; @@ -55,6 +56,8 @@ ProfileSaver::ProfileSaver(const std::string& output_filename, shutting_down_(false), last_save_number_of_methods_(0), last_save_number_of_classes_(0), + last_time_ns_saver_woke_up_(0), + jit_activity_notifications_(0), wait_lock_("ProfileSaver wait lock"), period_condition_("ProfileSaver period condition", wait_lock_), total_bytes_written_(0), @@ -65,7 +68,9 @@ ProfileSaver::ProfileSaver(const std::string& output_filename, total_ms_of_sleep_(0), total_ns_of_work_(0), total_number_of_foreign_dex_marks_(0), - max_number_of_profile_entries_cached_(0) { + max_number_of_profile_entries_cached_(0), + total_number_of_hot_spikes_(0), + total_number_of_wake_ups_(0) { AddTrackedLocations(output_filename, app_data_dir, code_paths); if (!app_data_dir.empty()) { // The application directory is used to determine which dex files are owned by app. @@ -83,55 +88,89 @@ ProfileSaver::ProfileSaver(const std::string& output_filename, } void ProfileSaver::Run() { - srand(MicroTime() * getpid()); Thread* self = Thread::Current(); - uint64_t save_period_ms = kSavePeriodMs; - VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms"; - bool cache_resolved_classes = true; + // Fetch the resolved classes for the app images after sleeping for + // kSaveResolvedClassesDelayMs. + // TODO(calin) This only considers the case of the primary profile file. + // Anything that gets loaded in the same VM will not have their resolved + // classes save (unless they started before the initial saving was done). + { + MutexLock mu(self, wait_lock_); + period_condition_.TimedWait(self, kSaveResolvedClassesDelayMs, 0); + total_ms_of_sleep_ += kSaveResolvedClassesDelayMs; + } + FetchAndCacheResolvedClasses(); + + // Loop for the profiled methods. while (!ShuttingDown(self)) { - uint64_t sleep_time_ms; - if (cache_resolved_classes) { - // Sleep less long for the first iteration since we want to record loaded classes shortly - // after app launch. - sleep_time_ms = kSaveResolvedClassesDelayMs; - } else { - const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs; - sleep_time_ms = save_period_ms + random_sleep_delay_ms; - } + uint64_t sleep_start = NanoTime(); { MutexLock mu(self, wait_lock_); - period_condition_.TimedWait(self, sleep_time_ms, 0); + period_condition_.Wait(self); + total_number_of_wake_ups_++; + // We might have been woken up by a huge number of notifications to guarantee saving. + // If we didn't meet the minimum saving period go back to sleep (only if missed by + // a reasonable margin). + uint64_t sleep_time = NanoTime() - last_time_ns_saver_woke_up_; + while (kMinSavePeriodNs - sleep_time > (kMinSavePeriodNs / 10)) { + period_condition_.TimedWait(self, NsToMs(kMinSavePeriodNs - sleep_time), 0); + total_number_of_wake_ups_++; + sleep_time = NanoTime() - last_time_ns_saver_woke_up_; + } } - total_ms_of_sleep_ += sleep_time_ms; + total_ms_of_sleep_ += NsToMs(NanoTime() - sleep_start); + if (ShuttingDown(self)) { break; } - uint64_t start = NanoTime(); - if (cache_resolved_classes) { - // TODO(calin) This only considers the case of the primary profile file. - // Anything that gets loaded in the same VM will not have their resolved - // classes save (unless they started before the initial saving was done). - FetchAndCacheResolvedClasses(); - } else { - bool profile_saved_to_disk = ProcessProfilingInfo(); - if (profile_saved_to_disk) { - // Reset the period to the initial value as it's highly likely to JIT again. - save_period_ms = kSavePeriodMs; - VLOG(profiler) << "Profile saver: saved something, period reset to: " << save_period_ms; - } else { - // If we don't need to save now it is less likely that we will need to do - // so in the future. Increase the time between saves according to the - // kBackoffCoef, but make it no larger than kMaxBackoffMs. - save_period_ms = std::min(kMaxBackoffMs, - static_cast<uint64_t>(kBackoffCoef * save_period_ms)); - VLOG(profiler) << "Profile saver: nothing to save, delaying period to: " << save_period_ms; - } + uint16_t new_methods = 0; + uint64_t start_work = NanoTime(); + bool profile_saved_to_disk = ProcessProfilingInfo(&new_methods); + // Update the notification counter based on result. Note that there might be contention on this + // but we don't care about to be 100% precise. + if (!profile_saved_to_disk) { + // If we didn't save to disk it may be because we didn't have enough new methods. + // Set the jit activity notifications to new_methods so we can wake up earlier if needed. + jit_activity_notifications_ = new_methods; } - cache_resolved_classes = false; + total_ns_of_work_ += NanoTime() - start_work; + } +} - total_ns_of_work_ += (NanoTime() - start); +void ProfileSaver::NotifyJitActivity() { + MutexLock mu(Thread::Current(), *Locks::profiler_lock_); + if (instance_ == nullptr || instance_->shutting_down_) { + return; + } + instance_->NotifyJitActivityInternal(); +} + +void ProfileSaver::WakeUpSaver() { + jit_activity_notifications_ = 0; + last_time_ns_saver_woke_up_ = NanoTime(); + period_condition_.Signal(Thread::Current()); +} + +void ProfileSaver::NotifyJitActivityInternal() { + // Unlikely to overflow but if it happens, + // we would have waken up the saver long before that. + jit_activity_notifications_++; + // Note that we are not as precise as we could be here but we don't want to wake the saver + // every time we see a hot method. + if (jit_activity_notifications_ > kMinimumNumberOfNotificationBeforeWake) { + MutexLock wait_mutex(Thread::Current(), wait_lock_); + if ((NanoTime() - last_time_ns_saver_woke_up_) > kMinSavePeriodNs) { + WakeUpSaver(); + } + } else if (jit_activity_notifications_ > kMaximumNumberOfNotificationBeforeWake) { + // Make sure to wake up the saver if we see a spike in the number of notifications. + // This is a precaution to avoid "loosing" a big number of methods in case + // this is a spike with no jit after. + total_number_of_hot_spikes_++; + MutexLock wait_mutex(Thread::Current(), wait_lock_); + WakeUpSaver(); } } @@ -175,7 +214,7 @@ void ProfileSaver::FetchAndCacheResolvedClasses() { total_number_of_profile_entries_cached); } -bool ProfileSaver::ProcessProfilingInfo() { +bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) { ScopedTrace trace(__PRETTY_FUNCTION__); SafeMap<std::string, std::set<std::string>> tracked_locations; { @@ -186,6 +225,8 @@ bool ProfileSaver::ProcessProfilingInfo() { bool profile_file_saved = false; uint64_t total_number_of_profile_entries_cached = 0; + *new_methods = 0; + for (const auto& it : tracked_locations) { if (ShuttingDown(Thread::Current())) { return true; @@ -216,6 +257,7 @@ bool ProfileSaver::ProcessProfilingInfo() { total_number_of_skipped_writes_++; continue; } + *new_methods = std::max(static_cast<uint16_t>(delta_number_of_methods), *new_methods); uint64_t bytes_written; // Force the save. In case the profile data is corrupted or the the profile // has the wrong version this will "fix" the file to the correct format. @@ -530,7 +572,9 @@ void ProfileSaver::DumpInfo(std::ostream& os) { << "ProfileSaver total_number_of_foreign_dex_marks=" << total_number_of_foreign_dex_marks_ << '\n' << "ProfileSaver max_number_profile_entries_cached=" - << max_number_of_profile_entries_cached_ << '\n'; + << max_number_of_profile_entries_cached_ << '\n' + << "ProfileSaver total_number_of_hot_spikes=" << total_number_of_hot_spikes_ << '\n' + << "ProfileSaver total_number_of_wake_ups=" << total_number_of_wake_ups_ << '\n'; } @@ -544,7 +588,8 @@ void ProfileSaver::ForceProcessProfiles() { // but we only use this in testing when we now this won't happen. // Refactor the way we handle the instance so that we don't end up in this situation. if (saver != nullptr) { - saver->ProcessProfilingInfo(); + uint16_t new_methods; + saver->ProcessProfilingInfo(&new_methods); } } diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index 4f3cdc28cb..c6da95931c 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -49,6 +49,11 @@ class ProfileSaver { // If the profile saver is running, dumps statistics to the `os`. Otherwise it does nothing. static void DumpInstanceInfo(std::ostream& os); + // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock. + static void NotifyJitActivity() + REQUIRES(!Locks::profiler_lock_, !wait_lock_) + NO_THREAD_SAFETY_ANALYSIS; + // Just for testing purpose. static void ForceProcessProfiles(); static bool HasSeenMethod(const std::string& profile, @@ -71,10 +76,13 @@ class ProfileSaver { void Run() REQUIRES(!Locks::profiler_lock_, !wait_lock_); // Processes the existing profiling info from the jit code cache and returns // true if it needed to be saved to disk. - bool ProcessProfilingInfo() + bool ProcessProfilingInfo(uint16_t* new_methods) REQUIRES(!Locks::profiler_lock_) REQUIRES(!Locks::mutator_lock_); + void NotifyJitActivityInternal() REQUIRES(!wait_lock_); + void WakeUpSaver() REQUIRES(wait_lock_); + // Returns true if the saver is shutting down (ProfileSaver::Stop() has been called). bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_); @@ -121,6 +129,8 @@ class ProfileSaver { bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); uint32_t last_save_number_of_methods_; uint32_t last_save_number_of_classes_; + uint64_t last_time_ns_saver_woke_up_ GUARDED_BY(wait_lock_); + uint32_t jit_activity_notifications_; // A local cache for the profile information. Maps each tracked file to its // profile information. The size of this cache is usually very small and tops @@ -142,6 +152,8 @@ class ProfileSaver { uint64_t total_number_of_foreign_dex_marks_; // TODO(calin): replace with an actual size. uint64_t max_number_of_profile_entries_cached_; + uint64_t total_number_of_hot_spikes_; + uint64_t total_number_of_wake_ups_; DISALLOW_COPY_AND_ASSIGN(ProfileSaver); }; diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 5b6ded162b..8c20fa680f 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -673,7 +673,7 @@ class MANAGED Class FINAL : public Object { // `This` and `klass` must be classes. Class* GetCommonSuperClass(Handle<Class> klass) SHARED_REQUIRES(Locks::mutator_lock_); - void SetSuperClass(Class *new_super_class) SHARED_REQUIRES(Locks::mutator_lock_) { + void SetSuperClass(Class* new_super_class) SHARED_REQUIRES(Locks::mutator_lock_) { // Super class is assigned once, except during class linker initialization. Class* old_super_class = GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(Class, super_class_)); DCHECK(old_super_class == nullptr || old_super_class == new_super_class); diff --git a/runtime/monitor.cc b/runtime/monitor.cc index f4bc222d1a..71c866f3d6 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -964,17 +964,13 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { if (!kUseReadBarrier) { DCHECK_EQ(new_lw.ReadBarrierState(), 0U); h_obj->SetLockWord(new_lw, true); - if (ATRACE_ENABLED()) { - ATRACE_END(); - } + AtraceMonitorUnlock(); // Success! return true; } else { // Use CAS to preserve the read barrier state. if (h_obj->CasLockWordWeakSequentiallyConsistent(lock_word, new_lw)) { - if (ATRACE_ENABLED()) { - ATRACE_END(); - } + AtraceMonitorUnlock(); // Success! return true; } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 5ba8df79ca..6c943dc172 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -145,6 +145,10 @@ static jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) { return Dbg::IsDebuggerActive(); } +static jboolean VMRuntime_isNativeDebuggable(JNIEnv*, jobject) { + return Runtime::Current()->IsNativeDebuggable(); +} + static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) { return toStringArray(env, Runtime::Current()->GetProperties()); } @@ -641,6 +645,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), NATIVE_METHOD(VMRuntime, isDebuggerActive, "!()Z"), + NATIVE_METHOD(VMRuntime, isNativeDebuggable, "!()Z"), NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"), NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), NATIVE_METHOD(VMRuntime, newUnpaddedArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), diff --git a/runtime/oat.h b/runtime/oat.h index 543d99f2ad..57675dc738 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -43,7 +43,7 @@ class PACKED(4) OatHeader { static constexpr const char* kNativeDebuggableKey = "native-debuggable"; static constexpr const char* kCompilerFilter = "compiler-filter"; static constexpr const char* kClassPathKey = "classpath"; - static constexpr const char* kBootClassPath = "bootclasspath"; + static constexpr const char* kBootClassPathKey = "bootclasspath"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 9470624df8..aa727ff45b 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -48,6 +48,9 @@ class DummyOatFile; class OatFile { public: + // Special classpath that skips shared library check. + static constexpr const char* kSpecialSharedLibrary = "&"; + typedef art::OatDexFile OatDexFile; // Opens an oat file contained within the given elf file. This is always opened as diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 713e2f3fa9..fba10ca014 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -771,7 +771,11 @@ bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args, argv.push_back("--runtime-arg"); argv.push_back("-classpath"); argv.push_back("--runtime-arg"); - argv.push_back(runtime->GetClassPathString()); + std::string class_path = runtime->GetClassPathString(); + if (class_path == "") { + class_path = OatFile::kSpecialSharedLibrary; + } + argv.push_back(class_path); if (runtime->IsDebuggable()) { argv.push_back("--debuggable"); } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 764b969eaa..15a1aa4d10 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -1264,8 +1264,7 @@ TEST_F(OatFileAssistantTest, LongDexExtension) { class RaceGenerateTask : public Task { public: explicit RaceGenerateTask(const std::string& dex_location, const std::string& oat_location) - : dex_location_(dex_location), oat_location_(oat_location), - loaded_oat_file_(nullptr) + : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr) {} void Run(Thread* self ATTRIBUTE_UNUSED) { diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index bc01da4fc4..0af6716af7 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -36,11 +36,6 @@ namespace art { -// For b/21333911. -// Only enabled for debug builds to prevent bit rot. There are too many performance regressions for -// normal builds. -static constexpr bool kDuplicateClassesCheck = kIsDebugBuild; - // If true, then we attempt to load the application image if it exists. static constexpr bool kEnableAppImage = true; @@ -173,7 +168,7 @@ class DexFileAndClassPair : ValueObject { void Next() { ++current_class_index_; - cached_descriptor_ = GetClassDescriptor(dex_file_.get(), current_class_index_); + cached_descriptor_ = GetClassDescriptor(dex_file_, current_class_index_); } size_t GetCurrentClassIndex() const { @@ -185,7 +180,12 @@ class DexFileAndClassPair : ValueObject { } const DexFile* GetDexFile() const { - return dex_file_.get(); + return dex_file_; + } + + void DeleteDexFile() { + delete dex_file_; + dex_file_ = nullptr; } private: @@ -196,7 +196,7 @@ class DexFileAndClassPair : ValueObject { } const char* cached_descriptor_; - std::shared_ptr<const DexFile> dex_file_; + const DexFile* dex_file_; size_t current_class_index_; bool from_loaded_oat_; // We only need to compare mismatches between what we load now // and what was loaded before. Any old duplicates must have been @@ -219,53 +219,299 @@ static void AddDexFilesFromOat(const OatFile* oat_file, } static void AddNext(/*inout*/DexFileAndClassPair* original, - /*inout*/std::priority_queue<DexFileAndClassPair>* heap) { + /*inout*/std::priority_queue<DexFileAndClassPair>* heap, + bool owning_dex_files) { if (original->DexFileHasMoreClasses()) { original->Next(); heap->push(std::move(*original)); + } else if (owning_dex_files) { + original->DeleteDexFile(); + } +} + +static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap, + bool owning_dex_files) { + if (owning_dex_files) { + while (!heap->empty()) { + delete heap->top().GetDexFile(); + heap->pop(); + } + } +} + +static void IterateOverJavaDexFile(mirror::Object* dex_file, + ArtField* const cookie_field, + std::function<bool(const DexFile*)> fn) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (dex_file != nullptr) { + mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray(); + if (long_array == nullptr) { + // This should never happen so log a warning. + LOG(WARNING) << "Null DexFile::mCookie"; + return; + } + int32_t long_array_size = long_array->GetLength(); + // Start from 1 to skip the oat file. + for (int32_t j = 1; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( + long_array->GetWithoutChecks(j))); + if (!fn(cp_dex_file)) { + return; + } + } + } +} + +static void IterateOverPathClassLoader( + ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ClassLoader> class_loader, + MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements, + std::function<bool(const DexFile*)> fn) SHARED_REQUIRES(Locks::mutator_lock_) { + // Handle this step. + // Handle as if this is the child PathClassLoader. + // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. + // We need to get the DexPathList and loop through it. + ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + mirror::Object* dex_path_list = + soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)-> + GetObject(class_loader.Get()); + if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { + // DexPathList has an array dexElements of Elements[] which each contain a dex file. + mirror::Object* dex_elements_obj = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look + // at the mCookie which is a DexFile vector. + if (dex_elements_obj != nullptr) { + dex_elements.Assign(dex_elements_obj->AsObjectArray<mirror::Object>()); + for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { + mirror::Object* element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + // Should never happen, fall back to java code to throw a NPE. + break; + } + mirror::Object* dex_file = dex_file_field->GetObject(element); + IterateOverJavaDexFile(dex_file, cookie_field, fn); + } + } + } +} + +static bool GetDexFilesFromClassLoader( + ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader, + std::priority_queue<DexFileAndClassPair>* queue) SHARED_REQUIRES(Locks::mutator_lock_) { + if (ClassLinker::IsBootClassLoader(soa, class_loader)) { + // The boot class loader. We don't load any of these files, as we know we compiled against + // them correctly. + return true; + } + + // Unsupported class-loader? + if (class_loader->GetClass() != + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) { + VLOG(class_linker) << "Unsupported class-loader " << PrettyClass(class_loader->GetClass()); + return false; + } + + bool recursive_result = GetDexFilesFromClassLoader(soa, class_loader->GetParent(), queue); + if (!recursive_result) { + // Something wrong up the chain. + return false; } + + // Collect all the dex files. + auto GetDexFilesFn = [&] (const DexFile* cp_dex_file) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (cp_dex_file->NumClassDefs() > 0) { + queue->emplace(cp_dex_file, 0U, true); + } + return true; // Continue looking. + }; + + // Handle for dex-cache-element. + StackHandleScope<3> hs(soa.Self()); + MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements( + hs.NewHandle<mirror::ObjectArray<mirror::Object>>(nullptr)); + Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); + + IterateOverPathClassLoader(soa, h_class_loader, dex_elements, GetDexFilesFn); + + return true; +} + +static void GetDexFilesFromDexElementsArray( + ScopedObjectAccessAlreadyRunnable& soa, + Handle<mirror::ObjectArray<mirror::Object>> dex_elements, + std::priority_queue<DexFileAndClassPair>* queue) SHARED_REQUIRES(Locks::mutator_lock_) { + if (dex_elements.Get() == nullptr) { + // Nothing to do. + return; + } + + ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + const mirror::Class* const element_class = soa.Decode<mirror::Class*>( + WellKnownClasses::dalvik_system_DexPathList__Element); + const mirror::Class* const dexfile_class = soa.Decode<mirror::Class*>( + WellKnownClasses::dalvik_system_DexFile); + + // Collect all the dex files. + auto GetDexFilesFn = [&] (const DexFile* cp_dex_file) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (cp_dex_file != nullptr && cp_dex_file->NumClassDefs() > 0) { + queue->emplace(cp_dex_file, 0U, true); + } + return true; // Continue looking. + }; + + for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { + mirror::Object* element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + continue; + } + + // We support this being dalvik.system.DexPathList$Element and dalvik.system.DexFile. + + mirror::Object* dex_file; + if (element->GetClass() == element_class) { + dex_file = dex_file_field->GetObject(element); + } else if (element->GetClass() == dexfile_class) { + dex_file = element; + } else { + LOG(WARNING) << "Unsupported element in dex_elements: " << PrettyClass(element->GetClass()); + continue; + } + + IterateOverJavaDexFile(dex_file, cookie_field, GetDexFilesFn); + } +} + +static bool AreSharedLibrariesOk(const std::string shared_libraries, + std::priority_queue<DexFileAndClassPair>& queue) { + if (shared_libraries.empty()) { + if (queue.empty()) { + // No shared libraries or oat files, as expected. + return true; + } + } else { + if (shared_libraries.compare(OatFile::kSpecialSharedLibrary) == 0) { + // If we find the special shared library, skip the shared libraries check. + return true; + } + // Shared libraries is a series of dex file paths and their checksums, each separated by '*'. + std::vector<std::string> shared_libraries_split; + Split(shared_libraries, '*', &shared_libraries_split); + + size_t index = 0; + std::priority_queue<DexFileAndClassPair> temp = queue; + while (!temp.empty() && index < shared_libraries_split.size() - 1) { + DexFileAndClassPair pair(temp.top()); + const DexFile* dex_file = pair.GetDexFile(); + std::string dex_filename(dex_file->GetLocation()); + uint32_t dex_checksum = dex_file->GetLocationChecksum(); + if (dex_filename != shared_libraries_split[index] || + dex_checksum != std::stoul(shared_libraries_split[index + 1])) { + break; + } + temp.pop(); + index += 2; + } + + // Check is successful if it made it through the queue and all the shared libraries. + return temp.empty() && index == shared_libraries_split.size(); + } + return false; } // Check for class-def collisions in dex files. // -// This works by maintaining a heap with one class from each dex file, sorted by the class -// descriptor. Then a dex-file/class pair is continually removed from the heap and compared +// This first walks the class loader chain, getting all the dex files from the class loader. If +// the class loader is null or one of the class loaders in the chain is unsupported, we collect +// dex files from all open non-boot oat files to be safe. +// +// This first checks whether the shared libraries are in the expected order and the oat files +// have the expected checksums. If so, we exit early. Otherwise, we do the collision check. +// +// The collision check works by maintaining a heap with one class from each dex file, sorted by the +// class descriptor. Then a dex-file/class pair is continually removed from the heap and compared // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. bool OatFileManager::HasCollisions(const OatFile* oat_file, + jobject class_loader, + jobjectArray dex_elements, std::string* error_msg /*out*/) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); - if (!kDuplicateClassesCheck) { - return false; + + std::priority_queue<DexFileAndClassPair> queue; + bool owning_dex_files = false; + + // Try to get dex files from the given class loader. If the class loader is null, or we do + // not support one of the class loaders in the chain, conservatively compare against all + // (non-boot) oat files. + bool class_loader_ok = false; + { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(Thread::Current()); + Handle<mirror::ClassLoader> h_class_loader = + hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)); + Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements = + hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements)); + if (h_class_loader.Get() != nullptr && + GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &queue)) { + class_loader_ok = true; + + // In this case, also take into account the dex_elements array, if given. We don't need to + // read it otherwise, as we'll compare against all open oat files anyways. + GetDexFilesFromDexElementsArray(soa, h_dex_elements, &queue); + } else if (h_class_loader.Get() != nullptr) { + VLOG(class_linker) << "Something unsupported with " + << PrettyClass(h_class_loader->GetClass()); + } } // Dex files are registered late - once a class is actually being loaded. We have to compare // against the open oat files. Take the oat_file_manager_lock_ that protects oat_files_ accesses. ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); - std::priority_queue<DexFileAndClassPair> queue; + if (!class_loader_ok) { + // Add dex files from already loaded oat files, but skip boot. - // Add dex files from already loaded oat files, but skip boot. - std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); - // The same OatFile can be loaded multiple times at different addresses. In this case, we don't - // need to check both against each other since they would have resolved the same way at compile - // time. - std::unordered_set<std::string> unique_locations; - for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) { - DCHECK_NE(loaded_oat_file.get(), oat_file); - const std::string& location = loaded_oat_file->GetLocation(); - if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) == - boot_oat_files.end() && location != oat_file->GetLocation() && - unique_locations.find(location) == unique_locations.end()) { - unique_locations.insert(location); - AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue); + // Clean up the queue. + while (!queue.empty()) { + queue.pop(); + } + + // Anything we load now is something we own and must be released later. + owning_dex_files = true; + + std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); + // The same OatFile can be loaded multiple times at different addresses. In this case, we don't + // need to check both against each other since they would have resolved the same way at compile + // time. + std::unordered_set<std::string> unique_locations; + for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) { + DCHECK_NE(loaded_oat_file.get(), oat_file); + const std::string& location = loaded_oat_file->GetLocation(); + if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) == + boot_oat_files.end() && location != oat_file->GetLocation() && + unique_locations.find(location) == unique_locations.end()) { + unique_locations.insert(location); + AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue); + } } } - if (queue.empty()) { - // No other oat files, return early. + // Exit if shared libraries are ok. Do a full duplicate classes check otherwise. + const std::string + shared_libraries(oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); + if (AreSharedLibrariesOk(shared_libraries, queue)) { + FreeDexFilesInHeap(&queue, owning_dex_files); return false; } @@ -290,16 +536,17 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, compare_pop.GetCachedDescriptor(), compare_pop.GetDexFile()->GetLocation().c_str(), top.GetDexFile()->GetLocation().c_str()); + FreeDexFilesInHeap(&queue, owning_dex_files); return true; } queue.pop(); - AddNext(&top, &queue); + AddNext(&top, &queue, owning_dex_files); } else { // Something else. Done here. break; } } - AddNext(&compare_pop, &queue); + AddNext(&compare_pop, &queue, owning_dex_files); } return false; @@ -363,7 +610,8 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( if (oat_file != nullptr) { // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = !HasCollisions(oat_file.get(), /*out*/ &error_msg); + bool accept_oat_file = + !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg); if (!accept_oat_file) { // Failed the collision check. Print warning. if (Runtime::Current()->IsDexFileFallbackEnabled()) { diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 7017dfc6ec..a1d1275e63 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -116,9 +116,16 @@ class OatFileManager { void DumpForSigQuit(std::ostream& os); private: - // Check for duplicate class definitions of the given oat file against all open oat files. + // Check that the shared libraries in the given oat file match those in the given class loader and + // dex elements. If the class loader is null or we do not support one of the class loaders in the + // chain, compare against all non-boot oat files instead. If the shared libraries are not ok, + // check for duplicate class definitions of the given oat file against the oat files (either from + // the class loader and dex elements if possible or all non-boot oat files otherwise). // Return true if there are any class definition collisions in the oat_file. - bool HasCollisions(const OatFile* oat_file, /*out*/std::string* error_msg) const + bool HasCollisions(const OatFile* oat_file, + jobject class_loader, + jobjectArray dex_elements, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const diff --git a/runtime/runtime.cc b/runtime/runtime.cc index ca8f8bb510..63976d0b14 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -856,7 +856,7 @@ static bool OpenDexFilesFromImage(const std::string& image_location, if (index == 0) { // First file. See if this is a multi-image environment, and if so, enqueue the other images. const OatHeader& boot_oat_header = oat_file->GetOatHeader(); - const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath); + const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey); if (boot_cp != nullptr) { gc::space::ImageSpace::CreateMultiImageLocations(image_locations[0], boot_cp, diff --git a/runtime/thread.cc b/runtime/thread.cc index fb248282be..424894439f 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -513,14 +513,20 @@ static size_t FixStackSize(size_t stack_size) { return stack_size; } +// Return the nearest page-aligned address below the current stack top. +NO_INLINE +static uint8_t* FindStackTop() { + return reinterpret_cast<uint8_t*>( + AlignDown(__builtin_frame_address(0), kPageSize)); +} + // Install a protected region in the stack. This is used to trigger a SIGSEGV if a stack // overflow is detected. It is located right below the stack_begin_. ATTRIBUTE_NO_SANITIZE_ADDRESS void Thread::InstallImplicitProtection() { uint8_t* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize; - uint8_t* stack_himem = tlsPtr_.stack_end; - uint8_t* stack_top = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(&stack_himem) & - ~(kPageSize - 1)); // Page containing current top of stack. + // Page containing current top of stack. + uint8_t* stack_top = FindStackTop(); // Try to directly protect the stack. VLOG(threads) << "installing stack protected region at " << std::hex << @@ -932,8 +938,7 @@ bool Thread::InitStackHwm() { } // Sanity check. - int stack_variable; - CHECK_GT(&stack_variable, reinterpret_cast<void*>(tlsPtr_.stack_end)); + CHECK_GT(FindStackTop(), reinterpret_cast<void*>(tlsPtr_.stack_end)); return true; } @@ -2764,7 +2769,7 @@ class ReferenceMapVisitor : public StackVisitor { VisitDeclaringClass(m); // Process register map (which native and runtime methods don't have) - if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) { + if (!m->IsNative() && !m->IsRuntimeMethod() && (!m->IsProxyMethod() || m->IsConstructor())) { const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); DCHECK(method_header->IsOptimized()); auto* vreg_base = reinterpret_cast<StackReference<mirror::Object>*>( diff --git a/test/138-duplicate-classes-check/src/FancyLoader.java b/test/138-duplicate-classes-check/src/FancyLoader.java deleted file mode 100644 index 03ec948767..0000000000 --- a/test/138-duplicate-classes-check/src/FancyLoader.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2008 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.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; - -/** - * A class loader with atypical behavior: we try to load a private - * class implementation before asking the system or boot loader. This - * is used to create multiple classes with identical names in a single VM. - * - * If DexFile is available, we use that; if not, we assume we're not in - * Dalvik and instantiate the class with defineClass(). - * - * The location of the DEX files and class data is dependent upon the - * test framework. - */ -public class FancyLoader extends ClassLoader { - /* this is where the "alternate" .class files live */ - static final String CLASS_PATH = "classes-ex/"; - - /* this is the "alternate" DEX/Jar file */ - static final String DEX_FILE = System.getenv("DEX_LOCATION") + - "/138-duplicate-classes-check-ex.jar"; - - /* on Dalvik, this is a DexFile; otherwise, it's null */ - private Class mDexClass; - - private Object mDexFile; - - /** - * Construct FancyLoader, grabbing a reference to the DexFile class - * if we're running under Dalvik. - */ - public FancyLoader(ClassLoader parent) { - super(parent); - - try { - mDexClass = parent.loadClass("dalvik.system.DexFile"); - } catch (ClassNotFoundException cnfe) { - // ignore -- not running Dalvik - } - } - - /** - * Finds the class with the specified binary name. - * - * We search for a file in CLASS_PATH or pull an entry from DEX_FILE. - * If we don't find a match, we throw an exception. - */ - protected Class<?> findClass(String name) throws ClassNotFoundException - { - if (mDexClass != null) { - return findClassDalvik(name); - } else { - return findClassNonDalvik(name); - } - } - - /** - * Finds the class with the specified binary name, from a DEX file. - */ - private Class<?> findClassDalvik(String name) - throws ClassNotFoundException { - - if (mDexFile == null) { - synchronized (FancyLoader.class) { - Constructor ctor; - /* - * Construct a DexFile object through reflection. - */ - try { - ctor = mDexClass.getConstructor(new Class[] {String.class}); - } catch (NoSuchMethodException nsme) { - throw new ClassNotFoundException("getConstructor failed", - nsme); - } - - try { - mDexFile = ctor.newInstance(DEX_FILE); - } catch (InstantiationException ie) { - throw new ClassNotFoundException("newInstance failed", ie); - } catch (IllegalAccessException iae) { - throw new ClassNotFoundException("newInstance failed", iae); - } catch (InvocationTargetException ite) { - throw new ClassNotFoundException("newInstance failed", ite); - } - } - } - - /* - * Call DexFile.loadClass(String, ClassLoader). - */ - Method meth; - - try { - meth = mDexClass.getMethod("loadClass", - new Class[] { String.class, ClassLoader.class }); - } catch (NoSuchMethodException nsme) { - throw new ClassNotFoundException("getMethod failed", nsme); - } - - try { - meth.invoke(mDexFile, name, this); - } catch (IllegalAccessException iae) { - throw new ClassNotFoundException("loadClass failed", iae); - } catch (InvocationTargetException ite) { - throw new ClassNotFoundException("loadClass failed", - ite.getCause()); - } - - return null; - } - - /** - * Finds the class with the specified binary name, from .class files. - */ - private Class<?> findClassNonDalvik(String name) - throws ClassNotFoundException { - - String pathName = CLASS_PATH + name + ".class"; - //System.out.println("--- Fancy: looking for " + pathName); - - File path = new File(pathName); - RandomAccessFile raf; - - try { - raf = new RandomAccessFile(path, "r"); - } catch (FileNotFoundException fnfe) { - throw new ClassNotFoundException("Not found: " + pathName); - } - - /* read the entire file in */ - byte[] fileData; - try { - fileData = new byte[(int) raf.length()]; - raf.readFully(fileData); - } catch (IOException ioe) { - throw new ClassNotFoundException("Read error: " + pathName); - } finally { - try { - raf.close(); - } catch (IOException ioe) { - // drop - } - } - - /* create the class */ - //System.out.println("--- Fancy: defining " + name); - try { - return defineClass(name, fileData, 0, fileData.length); - } catch (Throwable th) { - throw new ClassNotFoundException("defineClass failed", th); - } - } - - /** - * Load a class. - * - * Normally a class loader wouldn't override this, but we want our - * version of the class to take precedence over an already-loaded - * version. - * - * We still want the system classes (e.g. java.lang.Object) from the - * bootstrap class loader. - */ - protected Class<?> loadClass(String name, boolean resolve) - throws ClassNotFoundException - { - Class res; - - /* - * 1. Invoke findLoadedClass(String) to check if the class has - * already been loaded. - * - * This doesn't change. - */ - res = findLoadedClass(name); - if (res != null) { - System.out.println("FancyLoader.loadClass: " - + name + " already loaded"); - if (resolve) - resolveClass(res); - return res; - } - - /* - * 3. Invoke the findClass(String) method to find the class. - */ - try { - res = findClass(name); - if (resolve) - resolveClass(res); - } - catch (ClassNotFoundException e) { - // we couldn't find it, so eat the exception and keep going - } - - /* - * 2. Invoke the loadClass method on the parent class loader. If - * the parent loader is null the class loader built-in to the - * virtual machine is used, instead. - * - * (Since we're not in java.lang, we can't actually invoke the - * parent's loadClass() method, but we passed our parent to the - * super-class which can take care of it for us.) - */ - res = super.loadClass(name, resolve); // returns class or throws - return res; - } -} diff --git a/test/138-duplicate-classes-check/src/Main.java b/test/138-duplicate-classes-check/src/Main.java index a9b5bb04ea..a2ef281939 100644 --- a/test/138-duplicate-classes-check/src/Main.java +++ b/test/138-duplicate-classes-check/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import dalvik.system.DexClassLoader; import java.io.File; import java.lang.reflect.Method; @@ -30,7 +31,11 @@ public class Main { // Now run the class from the -ex file. - FancyLoader loader = new FancyLoader(getClass().getClassLoader()); + String dexPath = System.getenv("DEX_LOCATION") + "/138-duplicate-classes-check-ex.jar"; + String optimizedDirectory = System.getenv("DEX_LOCATION"); + String librarySearchPath = null; + DexClassLoader loader = new DexClassLoader(dexPath, optimizedDirectory, librarySearchPath, + getClass().getClassLoader()); try { Class testEx = loader.loadClass("TestEx"); diff --git a/test/148-multithread-gc-annotations/check b/test/148-multithread-gc-annotations/check new file mode 100755 index 0000000000..842bdc6ae8 --- /dev/null +++ b/test/148-multithread-gc-annotations/check @@ -0,0 +1,22 @@ +#!/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. + +# Check that the string "error" isn't present +if grep error "$2"; then + exit 1 +else + exit 0 +fi diff --git a/test/148-multithread-gc-annotations/expected.txt b/test/148-multithread-gc-annotations/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/148-multithread-gc-annotations/expected.txt diff --git a/test/148-multithread-gc-annotations/gc_coverage.cc b/test/148-multithread-gc-annotations/gc_coverage.cc new file mode 100644 index 0000000000..263eefd3ab --- /dev/null +++ b/test/148-multithread-gc-annotations/gc_coverage.cc @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#include "gc/heap.h" +#include "jni.h" +#include "runtime.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" + +namespace art { +namespace { + +extern "C" JNIEXPORT jboolean JNICALL Java_MovingGCThread_performHomogeneousSpaceCompact(JNIEnv*, jclass) { + return Runtime::Current()->GetHeap()->PerformHomogeneousSpaceCompact() == gc::kSuccess ? + JNI_TRUE : JNI_FALSE; +} + +extern "C" JNIEXPORT jboolean JNICALL Java_MovingGCThread_supportHomogeneousSpaceCompact(JNIEnv*, jclass) { + return Runtime::Current()->GetHeap()->SupportHomogeneousSpaceCompactAndCollectorTransitions() ? + JNI_TRUE : JNI_FALSE; +} + +extern "C" JNIEXPORT jlong JNICALL Java_MovingGCThread_objectAddress(JNIEnv* env, jclass, jobject object) { + ScopedObjectAccess soa(env); + return reinterpret_cast<jlong>(soa.Decode<mirror::Object*>(object)); +} + +} // namespace +} // namespace art diff --git a/test/148-multithread-gc-annotations/info.txt b/test/148-multithread-gc-annotations/info.txt new file mode 100644 index 0000000000..c62e544e08 --- /dev/null +++ b/test/148-multithread-gc-annotations/info.txt @@ -0,0 +1 @@ +Tests that getting annotations works during moving gc. diff --git a/test/148-multithread-gc-annotations/src/AnnoClass1.java b/test/148-multithread-gc-annotations/src/AnnoClass1.java new file mode 100644 index 0000000000..b82c61fd5b --- /dev/null +++ b/test/148-multithread-gc-annotations/src/AnnoClass1.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface AnnoClass1 { + Class value(); +} diff --git a/test/148-multithread-gc-annotations/src/AnnoClass2.java b/test/148-multithread-gc-annotations/src/AnnoClass2.java new file mode 100644 index 0000000000..c75d950e2a --- /dev/null +++ b/test/148-multithread-gc-annotations/src/AnnoClass2.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface AnnoClass2 { + Class value(); +} diff --git a/test/148-multithread-gc-annotations/src/AnnoClass3.java b/test/148-multithread-gc-annotations/src/AnnoClass3.java new file mode 100644 index 0000000000..5b4a378091 --- /dev/null +++ b/test/148-multithread-gc-annotations/src/AnnoClass3.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface AnnoClass3 { + Class value(); +} diff --git a/test/148-multithread-gc-annotations/src/AnnotationThread.java b/test/148-multithread-gc-annotations/src/AnnotationThread.java new file mode 100644 index 0000000000..ebc14e96bb --- /dev/null +++ b/test/148-multithread-gc-annotations/src/AnnotationThread.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.annotation.*; + +@AnnoClass1(AnnoClass2.class) +@AnnoClass2(AnnoClass3.class) +@AnnoClass3(AnnoClass1.class) +public class AnnotationThread implements Runnable { + public void run() { + for (int i = 0; i < 20; i++) { + Annotation[] annotations = AnnotationThread.class.getAnnotations(); + if (annotations == null) { + System.out.println("error: AnnotationThread class has no annotations"); + return; + } + } + } +} diff --git a/test/148-multithread-gc-annotations/src/Main.java b/test/148-multithread-gc-annotations/src/Main.java new file mode 100644 index 0000000000..b652ed6519 --- /dev/null +++ b/test/148-multithread-gc-annotations/src/Main.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) { + System.loadLibrary(args[0]); + Thread annoThread = new Thread(new AnnotationThread(), "Annotation thread"); + Thread gcThread = new Thread(new MovingGCThread(), "Moving GC thread"); + annoThread.start(); + gcThread.start(); + try { + annoThread.join(); + gcThread.join(); + } catch (InterruptedException e) { + System.out.println("error: " + e); + } + System.out.println("Done."); + } +} diff --git a/test/148-multithread-gc-annotations/src/MovingGCThread.java b/test/148-multithread-gc-annotations/src/MovingGCThread.java new file mode 100644 index 0000000000..87de9f4ec6 --- /dev/null +++ b/test/148-multithread-gc-annotations/src/MovingGCThread.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.TreeMap; + +public class MovingGCThread implements Runnable { + private static TreeMap treeMap = new TreeMap(); + + public void run() { + for (int i = 0; i < 20; i++) { + testHomogeneousCompaction(); + } + } + + public static void testHomogeneousCompaction() { + final boolean supportHSC = supportHomogeneousSpaceCompact(); + if (!supportHSC) { + return; + } + Object o = new Object(); + long addressBefore = objectAddress(o); + allocateStuff(); + final boolean success = performHomogeneousSpaceCompact(); + allocateStuff(); + if (!success) { + System.out.println("error: Expected " + supportHSC + " but got " + success); + } + allocateStuff(); + long addressAfter = objectAddress(o); + // This relies on the compaction copying from one space to another space and there being + // no overlap. + if (addressBefore == addressAfter) { + System.out.println("error: Expected different adddress " + addressBefore + " vs " + + addressAfter); + } + } + + private static void allocateStuff() { + for (int i = 0; i < 1000; ++i) { + Object o = new Object(); + treeMap.put(o.hashCode(), o); + } + } + + // Methods to get access to ART internals. + private static native boolean supportHomogeneousSpaceCompact(); + private static native boolean performHomogeneousSpaceCompact(); + private static native long objectAddress(Object object); +} diff --git a/test/800-smali/smali/b_28187158.smali b/test/800-smali/smali/b_28187158.smali index 14d5cec7ee..47e5ef64fd 100644 --- a/test/800-smali/smali/b_28187158.smali +++ b/test/800-smali/smali/b_28187158.smali @@ -9,4 +9,3 @@ iget v0, p0, Ljava/lang/System;->in:Ljava/io/InputStream; return-void .end method - diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java index c883b7f0f5..b2fc005620 100644 --- a/test/800-smali/src/Main.java +++ b/test/800-smali/src/Main.java @@ -174,7 +174,7 @@ public class Main { testCases.add(new TestCase("b/27799205 (5)", "B27799205Helper", "run5", null, new VerifyError(), null)); testCases.add(new TestCase("b/27799205 (6)", "B27799205Helper", "run6", null, null, null)); - testCases.add(new TestCase("b/28187158", "B28187158", "run", new Object[] { null} , + testCases.add(new TestCase("b/28187158", "B28187158", "run", new Object[] { null }, new VerifyError(), null)); } diff --git a/test/804-class-extends-itself/expected.txt b/test/804-class-extends-itself/expected.txt new file mode 100644 index 0000000000..b98f963ce7 --- /dev/null +++ b/test/804-class-extends-itself/expected.txt @@ -0,0 +1,2 @@ +Caught ClassCircularityError +Done! diff --git a/test/804-class-extends-itself/info.txt b/test/804-class-extends-itself/info.txt new file mode 100644 index 0000000000..c48934c21b --- /dev/null +++ b/test/804-class-extends-itself/info.txt @@ -0,0 +1 @@ +Exercise class linker check for classes extending themselves (b/28685551). diff --git a/test/804-class-extends-itself/smali/Main.smali b/test/804-class-extends-itself/smali/Main.smali new file mode 100644 index 0000000000..5c349edcc9 --- /dev/null +++ b/test/804-class-extends-itself/smali/Main.smali @@ -0,0 +1,57 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# We cannot implement Main in Java, as this would require to run +# dexmerger (to merge the Dex file produced from Smali code and the +# Dex file produced from Java code), which loops indefinitely when +# processing class B28685551, as this class inherits from itself. As +# a workaround, implement Main using Smali (we could also have used +# multidex, but this requires a custom build script). + +.class public LMain; +.super Ljava/lang/Object; + +.method public static main([Ljava/lang/String;)V + .registers 3 + .param p0, "args" + + invoke-static {}, LMain;->test()V + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + const-string v1, "Done!" + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + return-void +.end method + +.method static test()V + .registers 4 + + :try_start + const-string v2, "B28685551" + invoke-static {v2}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; + :try_end + .catch Ljava/lang/ClassCircularityError; {:try_start .. :try_end} :catch + + move-result-object v0 + + :goto_7 + return-void + + :catch + move-exception v1 + .local v1, "e":Ljava/lang/ClassCircularityError; + sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; + const-string v3, "Caught ClassCircularityError" + invoke-virtual {v2, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + goto :goto_7 +.end method diff --git a/test/804-class-extends-itself/smali/b_28685551.smali b/test/804-class-extends-itself/smali/b_28685551.smali new file mode 100644 index 0000000000..d98c6e3b32 --- /dev/null +++ b/test/804-class-extends-itself/smali/b_28685551.smali @@ -0,0 +1,18 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Regression test for a class inheriting from itself. + +.class public LB28685551; +.super LB28685551; diff --git a/test/976-conflict-no-methods/expected.txt b/test/976-conflict-no-methods/expected.txt new file mode 100644 index 0000000000..656dfc57d5 --- /dev/null +++ b/test/976-conflict-no-methods/expected.txt @@ -0,0 +1 @@ +Pass diff --git a/test/976-conflict-no-methods/info.txt b/test/976-conflict-no-methods/info.txt new file mode 100644 index 0000000000..cdc314903c --- /dev/null +++ b/test/976-conflict-no-methods/info.txt @@ -0,0 +1 @@ +Regression test for classes that have conflict tables but no methods. b/28707801
\ No newline at end of file diff --git a/test/976-conflict-no-methods/smali/Iface.smali b/test/976-conflict-no-methods/smali/Iface.smali new file mode 100644 index 0000000000..aa4ec37687 --- /dev/null +++ b/test/976-conflict-no-methods/smali/Iface.smali @@ -0,0 +1,281 @@ +# /* +# * Copyright (C) 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ +# +# public interface Iface2 { +# public void abstractMethod0(); +# public void abstractMethod1(); +# public void abstractMethod2(); +# public void abstractMethod3(); +# public void abstractMethod4(); +# public void abstractMethod5(); +# public void abstractMethod6(); +# public void abstractMethod7(); +# public void abstractMethod8(); +# public void abstractMethod9(); +# public void abstractMethod10(); +# public void abstractMethod11(); +# public void abstractMethod12(); +# public void abstractMethod13(); +# public void abstractMethod14(); +# public void abstractMethod15(); +# public void abstractMethod16(); +# public void abstractMethod17(); +# public void abstractMethod18(); +# public void abstractMethod19(); +# public void abstractMethod20(); +# public void abstractMethod21(); +# public void abstractMethod22(); +# public void abstractMethod23(); +# public void abstractMethod24(); +# public void abstractMethod25(); +# public void abstractMethod26(); +# public void abstractMethod27(); +# public void abstractMethod28(); +# public void abstractMethod29(); +# public void abstractMethod30(); +# public void abstractMethod31(); +# public void abstractMethod32(); +# public void abstractMethod33(); +# public void abstractMethod34(); +# public void abstractMethod35(); +# public void abstractMethod36(); +# public void abstractMethod37(); +# public void abstractMethod38(); +# public void abstractMethod39(); +# public void abstractMethod40(); +# public void abstractMethod41(); +# public void abstractMethod42(); +# public void abstractMethod43(); +# public void abstractMethod44(); +# public void abstractMethod45(); +# public void abstractMethod46(); +# public void abstractMethod47(); +# public void abstractMethod48(); +# public void abstractMethod49(); +# public void abstractMethod50(); +# public void abstractMethod51(); +# public void abstractMethod52(); +# public void abstractMethod53(); +# public void abstractMethod54(); +# public void abstractMethod55(); +# public void abstractMethod56(); +# public void abstractMethod57(); +# public void abstractMethod58(); +# public void abstractMethod59(); +# public void abstractMethod60(); +# public void abstractMethod61(); +# public void abstractMethod62(); +# public void abstractMethod63(); +# public void abstractMethod64(); +# } + +.class public abstract interface LIface; +.super Ljava/lang/Object; + +.method public abstract abstractMethod0()V +.end method + +.method public abstract abstractMethod1()V +.end method + +.method public abstract abstractMethod2()V +.end method + +.method public abstract abstractMethod3()V +.end method + +.method public abstract abstractMethod4()V +.end method + +.method public abstract abstractMethod5()V +.end method + +.method public abstract abstractMethod6()V +.end method + +.method public abstract abstractMethod7()V +.end method + +.method public abstract abstractMethod8()V +.end method + +.method public abstract abstractMethod9()V +.end method + +.method public abstract abstractMethod10()V +.end method + +.method public abstract abstractMethod11()V +.end method + +.method public abstract abstractMethod12()V +.end method + +.method public abstract abstractMethod13()V +.end method + +.method public abstract abstractMethod14()V +.end method + +.method public abstract abstractMethod15()V +.end method + +.method public abstract abstractMethod16()V +.end method + +.method public abstract abstractMethod17()V +.end method + +.method public abstract abstractMethod18()V +.end method + +.method public abstract abstractMethod19()V +.end method + +.method public abstract abstractMethod20()V +.end method + +.method public abstract abstractMethod21()V +.end method + +.method public abstract abstractMethod22()V +.end method + +.method public abstract abstractMethod23()V +.end method + +.method public abstract abstractMethod24()V +.end method + +.method public abstract abstractMethod25()V +.end method + +.method public abstract abstractMethod26()V +.end method + +.method public abstract abstractMethod27()V +.end method + +.method public abstract abstractMethod28()V +.end method + +.method public abstract abstractMethod29()V +.end method + +.method public abstract abstractMethod30()V +.end method + +.method public abstract abstractMethod31()V +.end method + +.method public abstract abstractMethod32()V +.end method + +.method public abstract abstractMethod33()V +.end method + +.method public abstract abstractMethod34()V +.end method + +.method public abstract abstractMethod35()V +.end method + +.method public abstract abstractMethod36()V +.end method + +.method public abstract abstractMethod37()V +.end method + +.method public abstract abstractMethod38()V +.end method + +.method public abstract abstractMethod39()V +.end method + +.method public abstract abstractMethod40()V +.end method + +.method public abstract abstractMethod41()V +.end method + +.method public abstract abstractMethod42()V +.end method + +.method public abstract abstractMethod43()V +.end method + +.method public abstract abstractMethod44()V +.end method + +.method public abstract abstractMethod45()V +.end method + +.method public abstract abstractMethod46()V +.end method + +.method public abstract abstractMethod47()V +.end method + +.method public abstract abstractMethod48()V +.end method + +.method public abstract abstractMethod49()V +.end method + +.method public abstract abstractMethod50()V +.end method + +.method public abstract abstractMethod51()V +.end method + +.method public abstract abstractMethod52()V +.end method + +.method public abstract abstractMethod53()V +.end method + +.method public abstract abstractMethod54()V +.end method + +.method public abstract abstractMethod55()V +.end method + +.method public abstract abstractMethod56()V +.end method + +.method public abstract abstractMethod57()V +.end method + +.method public abstract abstractMethod58()V +.end method + +.method public abstract abstractMethod59()V +.end method + +.method public abstract abstractMethod60()V +.end method + +.method public abstract abstractMethod61()V +.end method + +.method public abstract abstractMethod62()V +.end method + +.method public abstract abstractMethod63()V +.end method + +.method public abstract abstractMethod64()V +.end method diff --git a/test/976-conflict-no-methods/smali/Main.smali b/test/976-conflict-no-methods/smali/Main.smali new file mode 100644 index 0000000000..7dd11605b1 --- /dev/null +++ b/test/976-conflict-no-methods/smali/Main.smali @@ -0,0 +1,358 @@ +# /* +# * Copyright (C) 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ +# +.class public LMain; +.super Ljava/lang/Object; +.implements LIface; + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public static main([Ljava/lang/String;)V + .locals 2 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + const-string v1, "Pass" + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public abstractMethod0()V + .locals 0 + return-void +.end method + +.method public abstractMethod1()V + .locals 0 + return-void +.end method + +.method public abstractMethod2()V + .locals 0 + return-void +.end method + +.method public abstractMethod3()V + .locals 0 + return-void +.end method + +.method public abstractMethod4()V + .locals 0 + return-void +.end method + +.method public abstractMethod5()V + .locals 0 + return-void +.end method + +.method public abstractMethod6()V + .locals 0 + return-void +.end method + +.method public abstractMethod7()V + .locals 0 + return-void +.end method + +.method public abstractMethod8()V + .locals 0 + return-void +.end method + +.method public abstractMethod9()V + .locals 0 + return-void +.end method + +.method public abstractMethod10()V + .locals 0 + return-void +.end method + +.method public abstractMethod11()V + .locals 0 + return-void +.end method + +.method public abstractMethod12()V + .locals 0 + return-void +.end method + +.method public abstractMethod13()V + .locals 0 + return-void +.end method + +.method public abstractMethod14()V + .locals 0 + return-void +.end method + +.method public abstractMethod15()V + .locals 0 + return-void +.end method + +.method public abstractMethod16()V + .locals 0 + return-void +.end method + +.method public abstractMethod17()V + .locals 0 + return-void +.end method + +.method public abstractMethod18()V + .locals 0 + return-void +.end method + +.method public abstractMethod19()V + .locals 0 + return-void +.end method + +.method public abstractMethod20()V + .locals 0 + return-void +.end method + +.method public abstractMethod21()V + .locals 0 + return-void +.end method + +.method public abstractMethod22()V + .locals 0 + return-void +.end method + +.method public abstractMethod23()V + .locals 0 + return-void +.end method + +.method public abstractMethod24()V + .locals 0 + return-void +.end method + +.method public abstractMethod25()V + .locals 0 + return-void +.end method + +.method public abstractMethod26()V + .locals 0 + return-void +.end method + +.method public abstractMethod27()V + .locals 0 + return-void +.end method + +.method public abstractMethod28()V + .locals 0 + return-void +.end method + +.method public abstractMethod29()V + .locals 0 + return-void +.end method + +.method public abstractMethod30()V + .locals 0 + return-void +.end method + +.method public abstractMethod31()V + .locals 0 + return-void +.end method + +.method public abstractMethod32()V + .locals 0 + return-void +.end method + +.method public abstractMethod33()V + .locals 0 + return-void +.end method + +.method public abstractMethod34()V + .locals 0 + return-void +.end method + +.method public abstractMethod35()V + .locals 0 + return-void +.end method + +.method public abstractMethod36()V + .locals 0 + return-void +.end method + +.method public abstractMethod37()V + .locals 0 + return-void +.end method + +.method public abstractMethod38()V + .locals 0 + return-void +.end method + +.method public abstractMethod39()V + .locals 0 + return-void +.end method + +.method public abstractMethod40()V + .locals 0 + return-void +.end method + +.method public abstractMethod41()V + .locals 0 + return-void +.end method + +.method public abstractMethod42()V + .locals 0 + return-void +.end method + +.method public abstractMethod43()V + .locals 0 + return-void +.end method + +.method public abstractMethod44()V + .locals 0 + return-void +.end method + +.method public abstractMethod45()V + .locals 0 + return-void +.end method + +.method public abstractMethod46()V + .locals 0 + return-void +.end method + +.method public abstractMethod47()V + .locals 0 + return-void +.end method + +.method public abstractMethod48()V + .locals 0 + return-void +.end method + +.method public abstractMethod49()V + .locals 0 + return-void +.end method + +.method public abstractMethod50()V + .locals 0 + return-void +.end method + +.method public abstractMethod51()V + .locals 0 + return-void +.end method + +.method public abstractMethod52()V + .locals 0 + return-void +.end method + +.method public abstractMethod53()V + .locals 0 + return-void +.end method + +.method public abstractMethod54()V + .locals 0 + return-void +.end method + +.method public abstractMethod55()V + .locals 0 + return-void +.end method + +.method public abstractMethod56()V + .locals 0 + return-void +.end method + +.method public abstractMethod57()V + .locals 0 + return-void +.end method + +.method public abstractMethod58()V + .locals 0 + return-void +.end method + +.method public abstractMethod59()V + .locals 0 + return-void +.end method + +.method public abstractMethod60()V + .locals 0 + return-void +.end method + +.method public abstractMethod61()V + .locals 0 + return-void +.end method + +.method public abstractMethod62()V + .locals 0 + return-void +.end method + +.method public abstractMethod63()V + .locals 0 + return-void +.end method + +.method public abstractMethod64()V + .locals 0 + return-void +.end method diff --git a/test/976-conflict-no-methods/smali/NoMethods.smali b/test/976-conflict-no-methods/smali/NoMethods.smali new file mode 100644 index 0000000000..787e34a423 --- /dev/null +++ b/test/976-conflict-no-methods/smali/NoMethods.smali @@ -0,0 +1,19 @@ +# /* +# * Copyright (C) 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ +# + +.class public LNoMethods; +.super LMain; diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index 21f8141c66..97204d34c4 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -34,6 +34,7 @@ LIBARTTEST_COMMON_SRC_FILES := \ 137-cfi/cfi.cc \ 139-register-natives/regnative.cc \ 141-class-unload/jni_unload.cc \ + 148-multithread-gc-annotations/gc_coverage.cc \ 454-get-vreg/get_vreg_jni.cc \ 457-regs/regs_jni.cc \ 461-get-reference-vreg/get_reference_vreg_jni.cc \ diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index f25fb98c4d..bdcf86d853 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -248,6 +248,11 @@ names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ] }, { + description: "Investigate whether the test is making a wrong assumption with the newly enforced classpath.", + result: EXEC_FAILED, + names: ["dalvik.system.DexClassLoaderTest#testDexThenPathClassLoader"] +}, +{ description: "Made for extending, shouldn't be run", result: EXEC_FAILED, names: ["jsr166.CollectionTest#testEmptyMeansEmpty", diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index b6a19b7b5c..3e5a1c0959 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -44,6 +44,8 @@ vm_args="" # By default, we run the whole JDWP test suite. test="org.apache.harmony.jpda.tests.share.AllTests" host="no" +# Use JIT compiling by default. +use_jit=true while true; do if [[ "$1" == "--mode=host" ]]; then @@ -62,6 +64,11 @@ while true; do elif [[ $1 == -Ximage:* ]]; then image="$1" shift + elif [[ "$1" == "--no-jit" ]]; then + use_jit=false + # Remove the --no-jit from the arguments. + args=${args/$1} + shift elif [[ $1 == "--debug" ]]; then debug="yes" # Remove the --debug from the arguments. @@ -90,8 +97,12 @@ done if [[ "$image" != "" ]]; then vm_args="--vm-arg $image" fi -vm_args="$vm_args --vm-arg -Xusejit:true" -debuggee_args="$debuggee_args -Xusejit:true" +if $use_jit; then + vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --compiler-filter=interpret-only" + debuggee_args="$debuggee_args -Xcompiler-option --compiler-filter=interpret-only" +fi +vm_args="$vm_args --vm-arg -Xusejit:$use_jit" +debuggee_args="$debuggee_args -Xusejit:$use_jit" if [[ $debug == "yes" ]]; then art="$art -d" art_debugee="$art_debugee -d" diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 00bb3c5ff0..d9905f3f55 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -43,6 +43,9 @@ if [ "$ANDROID_SERIAL" = "emulator-5554" ]; then emulator="yes" fi +# Use JIT compiling by default. +use_jit=true + # Packages that currently work correctly with the expectation files. working_packages=("dalvik.system" "libcore.icu" @@ -91,6 +94,11 @@ while true; do # classpath/resources differences when compiling the boot image. vogar_args="$vogar_args --vm-arg -Ximage:/non/existent/vogar.art" shift + elif [[ "$1" == "--no-jit" ]]; then + # Remove the --no-jit from the arguments. + vogar_args=${vogar_args/$1} + use_jit=false + shift elif [[ "$1" == "--debug" ]]; then # Remove the --debug from the arguments. vogar_args=${vogar_args/$1} @@ -111,7 +119,13 @@ vogar_args="$vogar_args --timeout 480" # Use Jack with "1.8" configuration. vogar_args="$vogar_args --toolchain jack --language JN" +# JIT settings. +if $use_jit; then + vogar_args="$vogar_args --vm-arg -Xcompiler-option --vm-arg --compiler-filter=interpret-only" +fi +vogar_args="$vogar_args --vm-arg -Xusejit:$use_jit" + # Run the tests using vogar. echo "Running tests for the following test packages:" echo ${working_packages[@]} | tr " " "\n" -vogar $vogar_args --vm-arg -Xusejit:true $expectations --classpath $jsr166_test_jack --classpath $test_jack ${working_packages[@]} +vogar $vogar_args $expectations --classpath $jsr166_test_jack --classpath $test_jack ${working_packages[@]} |