diff options
166 files changed, 3746 insertions, 560 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index d8b780ac35..c87075f51c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -87,7 +87,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $( ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces -ART_GTEST_class_linker_test_DEX_DEPS := ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode +ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 3db73064ff..18a9165854 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -53,7 +53,8 @@ class ElfDebugLineWriter { // Write line table for given set of methods. // Returns the number of bytes written. size_t WriteCompilationUnit(ElfCompilationUnit& compilation_unit) { - const bool is64bit = Is64BitInstructionSet(builder_->GetIsa()); + const InstructionSet isa = builder_->GetIsa(); + const bool is64bit = Is64BitInstructionSet(isa); const Elf_Addr base_address = compilation_unit.is_code_address_text_relative ? builder_->GetText()->GetAddress() : 0; @@ -66,7 +67,7 @@ class ElfDebugLineWriter { std::unordered_map<std::string, size_t> directories_map; int code_factor_bits_ = 0; int dwarf_isa = -1; - switch (builder_->GetIsa()) { + switch (isa) { case kArm: // arm actually means thumb2. case kThumb2: code_factor_bits_ = 1; // 16-bit instuctions @@ -103,7 +104,7 @@ class ElfDebugLineWriter { for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); s++) { StackMap stack_map = code_info.GetStackMapAt(s, encoding); DCHECK(stack_map.IsValid()); - const uint32_t pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding); + const uint32_t pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding, isa); const int32_t dex = stack_map.GetDexPc(encoding.stack_map_encoding); pc2dex_map.push_back({pc, dex}); if (stack_map.HasDexRegisterMap(encoding.stack_map_encoding)) { diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 9645643edd..bce538743b 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -92,7 +92,8 @@ std::vector<VariableLocation> GetVariableLocations( bool is64bitValue, uint64_t compilation_unit_code_address, uint32_t dex_pc_low, - uint32_t dex_pc_high) { + uint32_t dex_pc_high, + InstructionSet isa) { std::vector<VariableLocation> variable_locations; // Get stack maps sorted by pc (they might not be sorted internally). @@ -111,7 +112,7 @@ std::vector<VariableLocation> GetVariableLocations( // The main reason for this is to save space by avoiding undefined gaps. continue; } - const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding.stack_map_encoding); + const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding.stack_map_encoding, isa); DCHECK_LE(pc_offset, method_info->code_size); DCHECK_LE(compilation_unit_code_address, method_info->code_address); const uint32_t low_pc = dchecked_integral_cast<uint32_t>( @@ -196,7 +197,8 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, is64bitValue, compilation_unit_code_address, dex_pc_low, - dex_pc_high); + dex_pc_high, + isa); // Write .debug_loc entries. dwarf::Writer<> debug_loc(debug_loc_buffer); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index faf8b41be1..c03ffcaa31 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -284,7 +284,7 @@ CompilerDriver::CompilerDriver( verification_results_(verification_results), compiler_(Compiler::Create(this, compiler_kind)), compiler_kind_(compiler_kind), - instruction_set_(instruction_set == kArm ? kThumb2: instruction_set), + instruction_set_(instruction_set == kArm ? kThumb2 : instruction_set), instruction_set_features_(instruction_set_features), requires_constructor_barrier_lock_("constructor barrier lock"), compiled_classes_lock_("compiled classes lock"), @@ -1251,7 +1251,7 @@ class ClinitImageUpdate { } } - // java.lang.Reference visitor for VisitReferences. + // java.lang.ref.Reference visitor for VisitReferences. void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, ObjPtr<mirror::Reference> ref ATTRIBUTE_UNUSED) const {} diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index f9e5cb9cb7..eac46e5909 100644 --- a/compiler/exception_test.cc +++ b/compiler/exception_test.cc @@ -61,7 +61,7 @@ class ExceptionTest : public CommonRuntimeTest { ArenaPool pool; ArenaAllocator allocator(&pool); - StackMapStream stack_maps(&allocator); + StackMapStream stack_maps(&allocator, kRuntimeISA); stack_maps.BeginStackMapEntry(/* dex_pc */ 3u, /* native_pc_offset */ 3u, /* register_mask */ 0u, diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 4109345f80..459aca3b38 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1166,9 +1166,9 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, // belongs. oat_index = GetOatIndexForDexCache(dex_cache); ImageInfo& image_info = GetImageInfo(oat_index); - { - // Note: This table is only accessed from the image writer, avoid locking to prevent lock - // order violations from root visiting. + if (!compile_app_image_) { + // Note: Avoid locking to prevent lock order violations from root visiting; + // image_info.class_table_ is only accessed from the image writer. image_info.class_table_->InsertWithoutLocks(as_klass); } for (LengthPrefixedArray<ArtField>* cur_fields : fields) { @@ -1265,7 +1265,14 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, // class loader. mirror::ClassLoader* class_loader = obj->AsClassLoader(); if (class_loader->GetClassTable() != nullptr) { + DCHECK(compile_app_image_); + DCHECK(class_loaders_.empty()); class_loaders_.insert(class_loader); + ImageInfo& image_info = GetImageInfo(oat_index); + // Note: Avoid locking to prevent lock order violations from root visiting; + // image_info.class_table_ table is only accessed from the image writer + // and class_loader->GetClassTable() is iterated but not modified. + image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable()); } } AssignImageBinSlot(obj, oat_index); diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc index 8ca0ffe53e..ba654f4750 100644 --- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc +++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc @@ -160,7 +160,7 @@ const ManagedRegisterEntrySpills& X86_64ManagedRuntimeCallingConvention::EntrySp while (HasNext()) { ManagedRegister in_reg = CurrentParamRegister(); if (!in_reg.IsNoRegister()) { - int32_t size = IsParamALongOrDouble(itr_args_)? 8 : 4; + int32_t size = IsParamALongOrDouble(itr_args_) ? 8 : 4; int32_t spill_offset = CurrentParamStackOffset().Uint32Value(); ManagedRegisterSpill spill(in_reg, size, spill_offset); entry_spills_.push_back(spill); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 70c2738010..99427f05da 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -839,8 +839,8 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, // last emitted is different than the native pc of the stack map just emitted. size_t number_of_stack_maps = stack_map_stream_.GetNumberOfStackMaps(); if (number_of_stack_maps > 1) { - DCHECK_NE(stack_map_stream_.GetStackMap(number_of_stack_maps - 1).native_pc_offset, - stack_map_stream_.GetStackMap(number_of_stack_maps - 2).native_pc_offset); + DCHECK_NE(stack_map_stream_.GetStackMap(number_of_stack_maps - 1).native_pc_code_offset, + stack_map_stream_.GetStackMap(number_of_stack_maps - 2).native_pc_code_offset); } } } @@ -848,7 +848,8 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, bool CodeGenerator::HasStackMapAtCurrentPc() { uint32_t pc = GetAssembler()->CodeSize(); size_t count = stack_map_stream_.GetNumberOfStackMaps(); - return count > 0 && stack_map_stream_.GetStackMap(count - 1).native_pc_offset == pc; + CodeOffset native_pc_offset = stack_map_stream_.GetStackMap(count - 1).native_pc_code_offset; + return (count > 0) && (native_pc_offset.Uint32Value(GetInstructionSet()) == pc); } void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 38d532e1e9..2d129aff22 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -608,7 +608,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { number_of_register_pairs_(number_of_register_pairs), core_callee_save_mask_(core_callee_save_mask), fpu_callee_save_mask_(fpu_callee_save_mask), - stack_map_stream_(graph->GetArena()), + stack_map_stream_(graph->GetArena(), graph->GetInstructionSet()), block_order_(nullptr), jit_string_roots_(StringReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 9c9c604dca..f5b6ebef9c 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1239,7 +1239,8 @@ void CodeGeneratorARM::Finalize(CodeAllocator* allocator) { // Adjust native pc offsets in stack maps. for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) { - uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset; + uint32_t old_position = + stack_map_stream_.GetStackMap(i).native_pc_code_offset.Uint32Value(kThumb2); uint32_t new_position = __ GetAdjustedPosition(old_position); stack_map_stream_.SetStackMapNativePcOffset(i, new_position); } @@ -3331,7 +3332,7 @@ void LocationsBuilderARM::VisitDiv(HDiv* div) { InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); - // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but // we only need the former. locations->SetOut(Location::RegisterLocation(R0)); } @@ -3458,7 +3459,7 @@ void LocationsBuilderARM::VisitRem(HRem* rem) { InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); - // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but // we only need the latter. locations->SetOut(Location::RegisterLocation(R1)); } @@ -7150,18 +7151,7 @@ void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction, HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { - HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; - // We disable pc-relative load when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods - // with irreducible loops. - if (GetGraph()->HasIrreducibleLoops() && - (dispatch_info.method_load_kind == - HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) { - dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; - } - - return dispatch_info; + return desired_dispatch_info; } Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, @@ -7181,8 +7171,7 @@ Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOr // save one load. However, since this is just an intrinsic slow path we prefer this // simple and more robust approach rather that trying to determine if that's the case. SlowPathCode* slow_path = GetCurrentSlowPath(); - DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path. - if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) { + if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) { int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>()); __ LoadFromOffset(kLoadWord, temp, SP, stack_offset); return temp; @@ -7190,7 +7179,8 @@ Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOr return location.AsRegister<Register>(); } -void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { +Location CodeGeneratorARM::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, + Location temp) { Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { @@ -7239,6 +7229,11 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, break; } } + return callee_method; +} + +void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { + Location callee_method = GenerateCalleeMethodStaticOrDirectCall(invoke, temp); switch (invoke->GetCodePtrLocation()) { case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 52d18575ff..df2dbc74ab 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -456,6 +456,7 @@ class CodeGeneratorARM : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; + Location GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp); void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 68d0b869e7..9762ee81b1 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -3993,7 +3993,8 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStatic return desired_dispatch_info; } -void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { +Location CodeGeneratorARM64::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, + Location temp) { // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention. Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { @@ -4047,6 +4048,12 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok break; } } + return callee_method; +} + +void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { + // All registers are assumed to be correctly set up. + Location callee_method = GenerateCalleeMethodStaticOrDirectCall(invoke, temp); switch (invoke->GetCodePtrLocation()) { case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: @@ -4655,7 +4662,7 @@ void LocationsBuilderARM64::VisitMonitorOperation(HMonitorOperation* instruction } void InstructionCodeGeneratorARM64::VisitMonitorOperation(HMonitorOperation* instruction) { - codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject: kQuickUnlockObject, + codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject, instruction, instruction->GetDexPc()); if (instruction->IsEnter()) { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index a9dca92980..7d3c655b27 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -527,6 +527,7 @@ class CodeGeneratorARM64 : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; + Location GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp); void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 592ee5aba6..ffaf18fb4d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -3336,7 +3336,7 @@ void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) { InvokeRuntimeCallingConventionARMVIXL calling_convention; locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); - // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but // we only need the former. locations->SetOut(LocationFrom(r0)); } @@ -3450,7 +3450,7 @@ void LocationsBuilderARMVIXL::VisitRem(HRem* rem) { InvokeRuntimeCallingConventionARMVIXL calling_convention; locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); - // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but // we only need the latter. locations->SetOut(LocationFrom(r1)); } @@ -7233,18 +7233,7 @@ void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruct HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { - HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; - // We disable pc-relative load when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods - // with irreducible loops. - if (GetGraph()->HasIrreducibleLoops() && - (dispatch_info.method_load_kind == - HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) { - dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; - } - - return dispatch_info; + return desired_dispatch_info; } vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter( @@ -7273,7 +7262,7 @@ vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter( return RegisterFrom(location); } -void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( +Location CodeGeneratorARMVIXL::GenerateCalleeMethodStaticOrDirectCall( HInvokeStaticOrDirect* invoke, Location temp) { Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { @@ -7324,6 +7313,12 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( break; } } + return callee_method; +} + +void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, + Location temp) { + Location callee_method = GenerateCalleeMethodStaticOrDirectCall(invoke, temp); switch (invoke->GetCodePtrLocation()) { case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index be653535ea..8ae3b7dc39 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -537,6 +537,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; + Location GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp); void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index a0383826d3..76be74e921 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -496,7 +496,8 @@ void CodeGeneratorMIPS::Finalize(CodeAllocator* allocator) { // Adjust native pc offsets in stack maps. for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) { - uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset; + uint32_t old_position = + stack_map_stream_.GetStackMap(i).native_pc_code_offset.Uint32Value(kMips); uint32_t new_position = __ GetAdjustedPosition(old_position); DCHECK_GE(new_position, old_position); stack_map_stream_.SetStackMapNativePcOffset(i, new_position); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 446dea659e..192b4a5050 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -450,7 +450,8 @@ void CodeGeneratorMIPS64::Finalize(CodeAllocator* allocator) { // Adjust native pc offsets in stack maps. for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) { - uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset; + uint32_t old_position = + stack_map_stream_.GetStackMap(i).native_pc_code_offset.Uint32Value(kMips64); uint32_t new_position = __ GetAdjustedPosition(old_position); DCHECK_GE(new_position, old_position); stack_map_stream_.SetStackMapNativePcOffset(i, new_position); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 89d210c3e6..1b7431612d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1781,7 +1781,7 @@ void InstructionCodeGeneratorX86::VisitSelect(HSelect* select) { cond = X86Condition(condition->GetCondition()); } } else { - // Must be a boolean condition, which needs to be compared to 0. + // Must be a Boolean condition, which needs to be compared to 0. Register cond_reg = locations->InAt(2).AsRegister<Register>(); __ testl(cond_reg, cond_reg); } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 74c71cce1f..c4caf4bf9d 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1809,7 +1809,7 @@ void InstructionCodeGeneratorX86_64::VisitSelect(HSelect* select) { cond = X86_64IntegerCondition(condition->GetCondition()); } } else { - // Must be a boolean condition, which needs to be compared to 0. + // Must be a Boolean condition, which needs to be compared to 0. CpuRegister cond_reg = locations->InAt(2).AsRegister<CpuRegister>(); __ testl(cond_reg, cond_reg); } @@ -4210,7 +4210,7 @@ void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED void CodeGeneratorX86_64::GenerateMemoryBarrier(MemBarrierKind kind) { /* - * According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence. + * According to the JSR-133 Cookbook, for x86-64 only StoreLoad/AnyAny barriers need memory fence. * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86-64 memory model. * For those cases, all we need to ensure is that there is a scheduling barrier in place. */ diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc index 9ddcd563ca..cfcb276a98 100644 --- a/compiler/optimizing/dex_cache_array_fixups_arm.cc +++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc @@ -65,7 +65,7 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { if (invoke->HasPcRelativeDexCache() && !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARMType>(invoke, codegen_)) { HArmDexCacheArraysBase* base = - GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache()); + GetOrCreateDexCacheArrayBase(invoke, invoke->GetDexFileForPcRelativeDexCache()); // Update the element offset in base. DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFileForPcRelativeDexCache()); base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex())); @@ -75,21 +75,28 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { } } - HArmDexCacheArraysBase* GetOrCreateDexCacheArrayBase(const DexFile& dex_file) { - // Ensure we only initialize the pointer once for each dex file. - auto lb = dex_cache_array_bases_.lower_bound(&dex_file); - if (lb != dex_cache_array_bases_.end() && - !dex_cache_array_bases_.key_comp()(&dex_file, lb->first)) { - return lb->second; - } + HArmDexCacheArraysBase* GetOrCreateDexCacheArrayBase(HInstruction* cursor, + const DexFile& dex_file) { + if (GetGraph()->HasIrreducibleLoops()) { + HArmDexCacheArraysBase* base = new (GetGraph()->GetArena()) HArmDexCacheArraysBase(dex_file); + cursor->GetBlock()->InsertInstructionBefore(base, cursor); + return base; + } else { + // Ensure we only initialize the pointer once for each dex file. + auto lb = dex_cache_array_bases_.lower_bound(&dex_file); + if (lb != dex_cache_array_bases_.end() && + !dex_cache_array_bases_.key_comp()(&dex_file, lb->first)) { + return lb->second; + } - // Insert the base at the start of the entry block, move it to a better - // position later in MoveBaseIfNeeded(). - HArmDexCacheArraysBase* base = new (GetGraph()->GetArena()) HArmDexCacheArraysBase(dex_file); - HBasicBlock* entry_block = GetGraph()->GetEntryBlock(); - entry_block->InsertInstructionBefore(base, entry_block->GetFirstInstruction()); - dex_cache_array_bases_.PutBefore(lb, &dex_file, base); - return base; + // Insert the base at the start of the entry block, move it to a better + // position later in MoveBaseIfNeeded(). + HArmDexCacheArraysBase* base = new (GetGraph()->GetArena()) HArmDexCacheArraysBase(dex_file); + HBasicBlock* entry_block = GetGraph()->GetEntryBlock(); + entry_block->InsertInstructionBefore(base, entry_block->GetFirstInstruction()); + dex_cache_array_bases_.PutBefore(lb, &dex_file, base); + return base; + } } CodeGeneratorARMType* codegen_; @@ -100,11 +107,6 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { }; void DexCacheArrayFixups::Run() { - if (graph_->HasIrreducibleLoops()) { - // Do not run this optimization, as irreducible loops do not work with an instruction - // that can be live-in at the irreducible loop header. - return; - } DexCacheArrayFixupsVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBasesIfNeeded(); diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index f5931a2f81..c93bc210be 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -399,7 +399,7 @@ class GlobalValueNumberer : public ValueObject { ArenaVector<ValueSet*> sets_; // BitVector which serves as a fast-access map from block id to - // visited/unvisited boolean. + // visited/unvisited Boolean. ArenaBitVector visited_blocks_; DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index ef8d74dce0..cac385ce3c 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1,4 +1,3 @@ - /* * Copyright (C) 2016 The Android Open Source Project * diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 8f64faeac0..c262cf983d 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -2592,6 +2592,58 @@ void IntrinsicCodeGeneratorARM::VisitDoubleIsInfinite(HInvoke* invoke) { __ Lsr(out, out, 5); } +void IntrinsicLocationsBuilderARM::VisitReferenceGetReferent(HInvoke* invoke) { + if (kEmitCompilerReadBarrier) { + // Do not intrinsify this call with the read barrier configuration. + return; + } + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM::VisitReferenceGetReferent(HInvoke* invoke) { + DCHECK(!kEmitCompilerReadBarrier); + ArmAssembler* const assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register obj = locations->InAt(0).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + + SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); + codegen_->AddSlowPath(slow_path); + + // Load ArtMethod first. + HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect(); + DCHECK(invoke_direct != nullptr); + Register temp = codegen_->GenerateCalleeMethodStaticOrDirectCall( + invoke_direct, locations->GetTemp(0)).AsRegister<Register>(); + + // Now get declaring class. + __ ldr(temp, Address(temp, ArtMethod::DeclaringClassOffset().Int32Value())); + + uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset(); + uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset(); + DCHECK_NE(slow_path_flag_offset, 0u); + DCHECK_NE(disable_flag_offset, 0u); + DCHECK_NE(slow_path_flag_offset, disable_flag_offset); + + // Check static flags that prevent using intrinsic. + __ ldr(IP, Address(temp, disable_flag_offset)); + __ ldr(temp, Address(temp, slow_path_flag_offset)); + __ orr(IP, IP, ShifterOperand(temp)); + __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); + + // Fast path. + __ ldr(out, Address(obj, mirror::Reference::ReferentOffset().Int32Value())); + codegen_->MaybeRecordImplicitNullCheck(invoke); + __ MaybeUnpoisonHeapReference(out); + __ Bind(slow_path->GetExitLabel()); +} + UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble) UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat) UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble) @@ -2605,7 +2657,6 @@ UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rou UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(ARM, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index d8a896e926..bbf826ce7e 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -2773,7 +2773,65 @@ void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) { GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler()); } -UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent) +void IntrinsicLocationsBuilderARM64::VisitReferenceGetReferent(HInvoke* invoke) { + if (kEmitCompilerReadBarrier) { + // Do not intrinsify this call with the read barrier configuration. + return; + } + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM64::VisitReferenceGetReferent(HInvoke* invoke) { + DCHECK(!kEmitCompilerReadBarrier); + MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register obj = InputRegisterAt(invoke, 0); + Register out = OutputRegister(invoke); + + SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + + // Load ArtMethod first. + HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect(); + DCHECK(invoke_direct != nullptr); + Register temp0 = XRegisterFrom(codegen_->GenerateCalleeMethodStaticOrDirectCall( + invoke_direct, locations->GetTemp(0))); + + // Now get declaring class. + __ Ldr(temp0.W(), MemOperand(temp0, ArtMethod::DeclaringClassOffset().Int32Value())); + + uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset(); + uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset(); + DCHECK_NE(slow_path_flag_offset, 0u); + DCHECK_NE(disable_flag_offset, 0u); + DCHECK_NE(slow_path_flag_offset, disable_flag_offset); + + // Check static flags that prevent using intrinsic. + if (slow_path_flag_offset == disable_flag_offset + 1) { + // Load two adjacent flags in one 64-bit load. + __ Ldr(temp0, MemOperand(temp0, disable_flag_offset)); + } else { + UseScratchRegisterScope temps(masm); + Register temp1 = temps.AcquireW(); + __ Ldr(temp1.W(), MemOperand(temp0, disable_flag_offset)); + __ Ldr(temp0.W(), MemOperand(temp0, slow_path_flag_offset)); + __ Orr(temp0, temp1, temp0); + } + __ Cbnz(temp0, slow_path->GetEntryLabel()); + + // Fast path. + __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value())); + codegen_->MaybeRecordImplicitNullCheck(invoke); + codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out); + __ Bind(slow_path->GetExitLabel()); +} + UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit) diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 85e84d8d2c..68c2d2e36e 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -2688,6 +2688,60 @@ void IntrinsicCodeGeneratorARMVIXL::VisitDoubleIsInfinite(HInvoke* invoke) { __ Lsr(out, out, 5); } +void IntrinsicLocationsBuilderARMVIXL::VisitReferenceGetReferent(HInvoke* invoke) { + if (kEmitCompilerReadBarrier) { + // Do not intrinsify this call with the read barrier configuration. + return; + } + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARMVIXL::VisitReferenceGetReferent(HInvoke* invoke) { + DCHECK(!kEmitCompilerReadBarrier); + ArmVIXLAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + vixl32::Register obj = InputRegisterAt(invoke, 0); + vixl32::Register out = OutputRegister(invoke); + + SlowPathCodeARMVIXL* slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke); + codegen_->AddSlowPath(slow_path); + + // Load ArtMethod first. + HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect(); + DCHECK(invoke_direct != nullptr); + vixl32::Register temp0 = RegisterFrom(codegen_->GenerateCalleeMethodStaticOrDirectCall( + invoke_direct, locations->GetTemp(0))); + + // Now get declaring class. + __ Ldr(temp0, MemOperand(temp0, ArtMethod::DeclaringClassOffset().Int32Value())); + + uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset(); + uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset(); + DCHECK_NE(slow_path_flag_offset, 0u); + DCHECK_NE(disable_flag_offset, 0u); + DCHECK_NE(slow_path_flag_offset, disable_flag_offset); + + // Check static flags that prevent using intrinsic. + UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); + vixl32::Register temp1 = temps.Acquire(); + __ Ldr(temp1, MemOperand(temp0, disable_flag_offset)); + __ Ldr(temp0, MemOperand(temp0, slow_path_flag_offset)); + __ Orr(temp0, temp1, temp0); + __ CompareAndBranchIfNonZero(temp0, slow_path->GetEntryLabel()); + + // Fast path. + __ Ldr(out, MemOperand(obj, mirror::Reference::ReferentOffset().Int32Value())); + codegen_->MaybeRecordImplicitNullCheck(invoke); + assembler->MaybeUnpoisonHeapReference(out); + __ Bind(slow_path->GetExitLabel()); +} + UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinDoubleDouble) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinFloatFloat) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxDoubleDouble) @@ -2701,7 +2755,6 @@ UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundFloat) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerLowestOneBit) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index f1ae549928..6cf9b83d44 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1878,7 +1878,7 @@ static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGenerat // If we use 'value' directly, we would lose 'value' // in the case that the store fails. Whether the // store succeeds, or fails, it will load the - // correct boolean value into the 'out' register. + // correct Boolean value into the 'out' register. // This test isn't really necessary. We only support Primitive::kPrimInt, // Primitive::kPrimNot, and we already verified that we're working on one // of those two types. It's left here in case the code needs to support diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 3022e975e8..00a1fa11bb 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1477,7 +1477,7 @@ static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGenerat // If we use 'value' directly, we would lose 'value' // in the case that the store fails. Whether the // store succeeds, or fails, it will load the - // correct boolean value into the 'out' register. + // correct Boolean value into the 'out' register. if (type == Primitive::kPrimLong) { __ Scd(out, TMP); } else { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index a2980dca20..f0ea9e20e6 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -565,7 +565,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { ArtMethod* GetArtMethod() const { return art_method_; } void SetArtMethod(ArtMethod* method) { art_method_ = method; } - // Returns an instruction with the opposite boolean value from 'cond'. + // Returns an instruction with the opposite Boolean value from 'cond'. // The instruction has been inserted into the graph, either as a constant, or // before cursor. HInstruction* InsertOppositeCondition(HInstruction* cond, HInstruction* cursor); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 6087e36507..a9a1e6f592 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -31,7 +31,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; DCHECK_NE(dex_pc, static_cast<uint32_t>(-1)) << "invalid dex_pc"; current_entry_.dex_pc = dex_pc; - current_entry_.native_pc_offset = native_pc_offset; + current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); current_entry_.register_mask = register_mask; current_entry_.sp_mask = sp_mask; current_entry_.num_dex_registers = num_dex_registers; @@ -144,10 +144,10 @@ void StackMapStream::EndInlineInfoEntry() { current_inline_info_ = InlineInfoEntry(); } -uint32_t StackMapStream::ComputeMaxNativePcOffset() const { - uint32_t max_native_pc_offset = 0u; +CodeOffset StackMapStream::ComputeMaxNativePcCodeOffset() const { + CodeOffset max_native_pc_offset; for (const StackMapEntry& entry : stack_maps_) { - max_native_pc_offset = std::max(max_native_pc_offset, entry.native_pc_offset); + max_native_pc_offset = std::max(max_native_pc_offset, entry.native_pc_code_offset); } return max_native_pc_offset; } @@ -157,8 +157,9 @@ size_t StackMapStream::PrepareForFillIn() { dex_register_maps_size_ = ComputeDexRegisterMapsSize(); ComputeInlineInfoEncoding(); // needs dex_register_maps_size_. inline_info_size_ = inline_infos_.size() * inline_info_encoding_.GetEntrySize(); - uint32_t max_native_pc_offset = ComputeMaxNativePcOffset(); - size_t stack_map_size = stack_map_encoding_.SetFromSizes(max_native_pc_offset, + CodeOffset max_native_pc_offset = ComputeMaxNativePcCodeOffset(); + // The stack map contains compressed native offsets. + size_t stack_map_size = stack_map_encoding_.SetFromSizes(max_native_pc_offset.CompressedValue(), dex_pc_max_, dex_register_maps_size_, inline_info_size_, @@ -319,7 +320,7 @@ void StackMapStream::FillIn(MemoryRegion region) { StackMapEntry entry = stack_maps_[i]; stack_map.SetDexPc(stack_map_encoding_, entry.dex_pc); - stack_map.SetNativePcOffset(stack_map_encoding_, entry.native_pc_offset); + stack_map.SetNativePcCodeOffset(stack_map_encoding_, entry.native_pc_code_offset); stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask); size_t number_of_stack_mask_bits = stack_map.GetNumberOfStackMaskBits(stack_map_encoding_); if (entry.sp_mask != nullptr) { @@ -546,7 +547,8 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { StackMapEntry entry = stack_maps_[s]; // Check main stack map fields. - DCHECK_EQ(stack_map.GetNativePcOffset(stack_map_encoding), entry.native_pc_offset); + DCHECK_EQ(stack_map.GetNativePcOffset(stack_map_encoding, instruction_set_), + entry.native_pc_code_offset.Uint32Value(instruction_set_)); DCHECK_EQ(stack_map.GetDexPc(stack_map_encoding), entry.dex_pc); DCHECK_EQ(stack_map.GetRegisterMask(stack_map_encoding), entry.register_mask); size_t num_stack_mask_bits = stack_map.GetNumberOfStackMaskBits(stack_map_encoding); diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index d6f42b373c..8fec472437 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -59,8 +59,10 @@ class DexRegisterLocationHashFn { */ class StackMapStream : public ValueObject { public: - explicit StackMapStream(ArenaAllocator* allocator) + explicit StackMapStream(ArenaAllocator* allocator, + InstructionSet instruction_set) : allocator_(allocator), + instruction_set_(instruction_set), stack_maps_(allocator->Adapter(kArenaAllocStackMapStream)), location_catalog_entries_(allocator->Adapter(kArenaAllocStackMapStream)), location_catalog_entries_indices_(allocator->Adapter(kArenaAllocStackMapStream)), @@ -95,7 +97,7 @@ class StackMapStream : public ValueObject { // See runtime/stack_map.h to know what these fields contain. struct StackMapEntry { uint32_t dex_pc; - uint32_t native_pc_offset; + CodeOffset native_pc_code_offset; uint32_t register_mask; BitVector* sp_mask; uint32_t num_dex_registers; @@ -141,11 +143,9 @@ class StackMapStream : public ValueObject { } void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) { - stack_maps_[i].native_pc_offset = native_pc_offset; + stack_maps_[i].native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); } - uint32_t ComputeMaxNativePcOffset() const; - // Prepares the stream to fill in a memory region. Must be called before FillIn. // Returns the size (in bytes) needed to store this stream. size_t PrepareForFillIn(); @@ -158,6 +158,8 @@ class StackMapStream : public ValueObject { size_t ComputeDexRegisterMapsSize() const; void ComputeInlineInfoEncoding(); + CodeOffset ComputeMaxNativePcCodeOffset() const; + // Returns the index of an entry with the same dex register map as the current_entry, // or kNoSameDexMapFound if no such entry exists. size_t FindEntryWithTheSameDexMap(); @@ -175,6 +177,7 @@ class StackMapStream : public ValueObject { void CheckCodeInfo(MemoryRegion region) const; ArenaAllocator* allocator_; + const InstructionSet instruction_set_; ArenaVector<StackMapEntry> stack_maps_; // A catalog of unique [location_kind, register_value] pairs (per method). diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 22810ea4f7..f68695bcbc 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -47,7 +47,7 @@ using Kind = DexRegisterLocation::Kind; TEST(StackMapTest, Test1) { ArenaPool pool; ArenaAllocator arena(&pool); - StackMapStream stream(&arena); + StackMapStream stream(&arena, kRuntimeISA); ArenaBitVector sp_mask(&arena, 0, false); size_t number_of_dex_registers = 2; @@ -78,7 +78,7 @@ TEST(StackMapTest, Test1) { ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map_encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding)); ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask)); @@ -128,7 +128,7 @@ TEST(StackMapTest, Test1) { TEST(StackMapTest, Test2) { ArenaPool pool; ArenaAllocator arena(&pool); - StackMapStream stream(&arena); + StackMapStream stream(&arena, kRuntimeISA); ArtMethod art_method; ArenaBitVector sp_mask1(&arena, 0, true); @@ -193,7 +193,7 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map_encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding)); ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask1)); @@ -252,7 +252,7 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u, encoding))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u, encoding))); ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map_encoding)); - ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map_encoding)); + ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0xFFu, stack_map.GetRegisterMask(encoding.stack_map_encoding)); ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask2)); @@ -306,7 +306,7 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u, encoding))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u, encoding))); ASSERT_EQ(2u, stack_map.GetDexPc(encoding.stack_map_encoding)); - ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map_encoding)); + ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0xABu, stack_map.GetRegisterMask(encoding.stack_map_encoding)); ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask3)); @@ -360,7 +360,7 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u, encoding))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u, encoding))); ASSERT_EQ(3u, stack_map.GetDexPc(encoding.stack_map_encoding)); - ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map_encoding)); + ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0xCDu, stack_map.GetRegisterMask(encoding.stack_map_encoding)); ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask4)); @@ -412,7 +412,7 @@ TEST(StackMapTest, Test2) { TEST(StackMapTest, TestNonLiveDexRegisters) { ArenaPool pool; ArenaAllocator arena(&pool); - StackMapStream stream(&arena); + StackMapStream stream(&arena, kRuntimeISA); ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 2; @@ -442,7 +442,7 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map_encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding)); ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding)); @@ -491,7 +491,7 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { TEST(StackMapTest, DexRegisterMapOffsetOverflow) { ArenaPool pool; ArenaAllocator arena(&pool); - StackMapStream stream(&arena); + StackMapStream stream(&arena, kRuntimeISA); ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 1024; @@ -554,7 +554,7 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { TEST(StackMapTest, TestShareDexRegisterMap) { ArenaPool pool; ArenaAllocator arena(&pool); - StackMapStream stream(&arena); + StackMapStream stream(&arena, kRuntimeISA); ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 2; @@ -612,7 +612,7 @@ TEST(StackMapTest, TestShareDexRegisterMap) { TEST(StackMapTest, TestNoDexRegisterMap) { ArenaPool pool; ArenaAllocator arena(&pool); - StackMapStream stream(&arena); + StackMapStream stream(&arena, kRuntimeISA); ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 0; @@ -620,7 +620,7 @@ TEST(StackMapTest, TestNoDexRegisterMap) { stream.EndStackMapEntry(); number_of_dex_registers = 1; - stream.BeginStackMapEntry(1, 67, 0x4, &sp_mask, number_of_dex_registers, 0); + stream.BeginStackMapEntry(1, 68, 0x4, &sp_mask, number_of_dex_registers, 0); stream.EndStackMapEntry(); size_t size = stream.PrepareForFillIn(); @@ -641,7 +641,7 @@ TEST(StackMapTest, TestNoDexRegisterMap) { ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map_encoding)); - ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding)); ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding)); @@ -649,9 +649,9 @@ TEST(StackMapTest, TestNoDexRegisterMap) { stack_map = code_info.GetStackMapAt(1, encoding); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1, encoding))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(67, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68, encoding))); ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map_encoding)); - ASSERT_EQ(67u, stack_map.GetNativePcOffset(encoding.stack_map_encoding)); + ASSERT_EQ(68u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA)); ASSERT_EQ(0x4u, stack_map.GetRegisterMask(encoding.stack_map_encoding)); ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding)); @@ -661,7 +661,7 @@ TEST(StackMapTest, TestNoDexRegisterMap) { TEST(StackMapTest, InlineTest) { ArenaPool pool; ArenaAllocator arena(&pool); - StackMapStream stream(&arena); + StackMapStream stream(&arena, kRuntimeISA); ArtMethod art_method; ArenaBitVector sp_mask1(&arena, 0, true); @@ -823,4 +823,20 @@ TEST(StackMapTest, InlineTest) { } } +TEST(StackMapTest, CodeOffsetTest) { + // Test minimum alignments, encoding, and decoding. + CodeOffset offset_thumb2 = CodeOffset::FromOffset(kThumb2InstructionAlignment, kThumb2); + CodeOffset offset_arm64 = CodeOffset::FromOffset(kArm64InstructionAlignment, kArm64); + CodeOffset offset_x86 = CodeOffset::FromOffset(kX86InstructionAlignment, kX86); + CodeOffset offset_x86_64 = CodeOffset::FromOffset(kX86_64InstructionAlignment, kX86_64); + CodeOffset offset_mips = CodeOffset::FromOffset(kMipsInstructionAlignment, kMips); + CodeOffset offset_mips64 = CodeOffset::FromOffset(kMips64InstructionAlignment, kMips64); + EXPECT_EQ(offset_thumb2.Uint32Value(kThumb2), kThumb2InstructionAlignment); + EXPECT_EQ(offset_arm64.Uint32Value(kArm64), kArm64InstructionAlignment); + EXPECT_EQ(offset_x86.Uint32Value(kX86), kX86InstructionAlignment); + EXPECT_EQ(offset_x86_64.Uint32Value(kX86_64), kX86_64InstructionAlignment); + EXPECT_EQ(offset_mips.Uint32Value(kMips), kMipsInstructionAlignment); + EXPECT_EQ(offset_mips64.Uint32Value(kMips64), kMips64InstructionAlignment); +} + } // namespace art diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index f2cbebbfd7..74b8f068c1 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -283,6 +283,38 @@ TEST_F(AssemblerMIPS64Test, Toolchain) { // FP Operations // /////////////////// +TEST_F(AssemblerMIPS64Test, AddS) { + DriverStr(RepeatFFF(&mips64::Mips64Assembler::AddS, "add.s ${reg1}, ${reg2}, ${reg3}"), "add.s"); +} + +TEST_F(AssemblerMIPS64Test, AddD) { + DriverStr(RepeatFFF(&mips64::Mips64Assembler::AddD, "add.d ${reg1}, ${reg2}, ${reg3}"), "add.d"); +} + +TEST_F(AssemblerMIPS64Test, SubS) { + DriverStr(RepeatFFF(&mips64::Mips64Assembler::SubS, "sub.s ${reg1}, ${reg2}, ${reg3}"), "sub.s"); +} + +TEST_F(AssemblerMIPS64Test, SubD) { + DriverStr(RepeatFFF(&mips64::Mips64Assembler::SubD, "sub.d ${reg1}, ${reg2}, ${reg3}"), "sub.d"); +} + +TEST_F(AssemblerMIPS64Test, MulS) { + DriverStr(RepeatFFF(&mips64::Mips64Assembler::MulS, "mul.s ${reg1}, ${reg2}, ${reg3}"), "mul.s"); +} + +TEST_F(AssemblerMIPS64Test, MulD) { + DriverStr(RepeatFFF(&mips64::Mips64Assembler::MulD, "mul.d ${reg1}, ${reg2}, ${reg3}"), "mul.d"); +} + +TEST_F(AssemblerMIPS64Test, DivS) { + DriverStr(RepeatFFF(&mips64::Mips64Assembler::DivS, "div.s ${reg1}, ${reg2}, ${reg3}"), "div.s"); +} + +TEST_F(AssemblerMIPS64Test, DivD) { + DriverStr(RepeatFFF(&mips64::Mips64Assembler::DivD, "div.d ${reg1}, ${reg2}, ${reg3}"), "div.d"); +} + TEST_F(AssemblerMIPS64Test, SqrtS) { DriverStr(RepeatFF(&mips64::Mips64Assembler::SqrtS, "sqrt.s ${reg1}, ${reg2}"), "sqrt.s"); } @@ -567,6 +599,26 @@ TEST_F(AssemblerMIPS64Test, Dmtc1) { DriverStr(RepeatRF(&mips64::Mips64Assembler::Dmtc1, "dmtc1 ${reg1}, ${reg2}"), "Dmtc1"); } +TEST_F(AssemblerMIPS64Test, Lwc1) { + DriverStr(RepeatFRIb(&mips64::Mips64Assembler::Lwc1, -16, "lwc1 ${reg1}, {imm}(${reg2})"), + "lwc1"); +} + +TEST_F(AssemblerMIPS64Test, Ldc1) { + DriverStr(RepeatFRIb(&mips64::Mips64Assembler::Ldc1, -16, "ldc1 ${reg1}, {imm}(${reg2})"), + "ldc1"); +} + +TEST_F(AssemblerMIPS64Test, Swc1) { + DriverStr(RepeatFRIb(&mips64::Mips64Assembler::Swc1, -16, "swc1 ${reg1}, {imm}(${reg2})"), + "swc1"); +} + +TEST_F(AssemblerMIPS64Test, Sdc1) { + DriverStr(RepeatFRIb(&mips64::Mips64Assembler::Sdc1, -16, "sdc1 ${reg1}, {imm}(${reg2})"), + "sdc1"); +} + //////////////// // CALL / JMP // //////////////// @@ -850,6 +902,16 @@ TEST_F(AssemblerMIPS64Test, Ldpc) { DriverStr(RepeatRIb(&mips64::Mips64Assembler::Ldpc, 18, code), "Ldpc"); } +TEST_F(AssemblerMIPS64Test, Auipc) { + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Auipc, 16, "auipc ${reg}, {imm}"), "Auipc"); +} + +TEST_F(AssemblerMIPS64Test, Addiupc) { + // The comment from the Lwpc() test applies to this Addiupc() test as well. + const char* code = ".set imm, {imm}\naddiupc ${reg}, (imm - ((imm & 0x40000) << 1)) << 2"; + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Addiupc, 19, code), "Addiupc"); +} + TEST_F(AssemblerMIPS64Test, LoadFarthestNearLabelAddress) { mips64::Mips64Label label; __ LoadLabelAddress(mips64::V0, &label); @@ -1079,6 +1141,188 @@ TEST_F(AssemblerMIPS64Test, FarLongLiteralAlignmentNop) { EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (5 + kAdduCount) * 4); } +TEST_F(AssemblerMIPS64Test, Addu) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Addu, "addu ${reg1}, ${reg2}, ${reg3}"), "addu"); +} + +TEST_F(AssemblerMIPS64Test, Addiu) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Addiu, -16, "addiu ${reg1}, ${reg2}, {imm}"), + "addiu"); +} + +TEST_F(AssemblerMIPS64Test, Daddu) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Daddu, "daddu ${reg1}, ${reg2}, ${reg3}"), "daddu"); +} + +TEST_F(AssemblerMIPS64Test, Daddiu) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Daddiu, -16, "daddiu ${reg1}, ${reg2}, {imm}"), + "daddiu"); +} + +TEST_F(AssemblerMIPS64Test, Subu) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Subu, "subu ${reg1}, ${reg2}, ${reg3}"), "subu"); +} + +TEST_F(AssemblerMIPS64Test, Dsubu) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dsubu, "dsubu ${reg1}, ${reg2}, ${reg3}"), "dsubu"); +} + +TEST_F(AssemblerMIPS64Test, MulR6) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::MulR6, "mul ${reg1}, ${reg2}, ${reg3}"), "mulR6"); +} + +TEST_F(AssemblerMIPS64Test, DivR6) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::DivR6, "div ${reg1}, ${reg2}, ${reg3}"), "divR6"); +} + +TEST_F(AssemblerMIPS64Test, ModR6) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::ModR6, "mod ${reg1}, ${reg2}, ${reg3}"), "modR6"); +} + +TEST_F(AssemblerMIPS64Test, DivuR6) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::DivuR6, "divu ${reg1}, ${reg2}, ${reg3}"), + "divuR6"); +} + +TEST_F(AssemblerMIPS64Test, ModuR6) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::ModuR6, "modu ${reg1}, ${reg2}, ${reg3}"), + "moduR6"); +} + +TEST_F(AssemblerMIPS64Test, Dmul) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dmul, "dmul ${reg1}, ${reg2}, ${reg3}"), "dmul"); +} + +TEST_F(AssemblerMIPS64Test, Ddiv) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Ddiv, "ddiv ${reg1}, ${reg2}, ${reg3}"), "ddiv"); +} + +TEST_F(AssemblerMIPS64Test, Dmod) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dmod, "dmod ${reg1}, ${reg2}, ${reg3}"), "dmod"); +} + +TEST_F(AssemblerMIPS64Test, Ddivu) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Ddivu, "ddivu ${reg1}, ${reg2}, ${reg3}"), "ddivu"); +} + +TEST_F(AssemblerMIPS64Test, Dmodu) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dmodu, "dmodu ${reg1}, ${reg2}, ${reg3}"), "dmodu"); +} + +TEST_F(AssemblerMIPS64Test, And) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::And, "and ${reg1}, ${reg2}, ${reg3}"), "and"); +} + +TEST_F(AssemblerMIPS64Test, Andi) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Andi, 16, "andi ${reg1}, ${reg2}, {imm}"), "andi"); +} + +TEST_F(AssemblerMIPS64Test, Or) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Or, "or ${reg1}, ${reg2}, ${reg3}"), "or"); +} + +TEST_F(AssemblerMIPS64Test, Ori) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Ori, 16, "ori ${reg1}, ${reg2}, {imm}"), "ori"); +} + +TEST_F(AssemblerMIPS64Test, Xor) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Xor, "xor ${reg1}, ${reg2}, ${reg3}"), "xor"); +} + +TEST_F(AssemblerMIPS64Test, Xori) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Xori, 16, "xori ${reg1}, ${reg2}, {imm}"), "xori"); +} + +TEST_F(AssemblerMIPS64Test, Nor) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Nor, "nor ${reg1}, ${reg2}, ${reg3}"), "nor"); +} + +TEST_F(AssemblerMIPS64Test, Lb) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lb, -16, "lb ${reg1}, {imm}(${reg2})"), "lb"); +} + +TEST_F(AssemblerMIPS64Test, Lh) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lh, -16, "lh ${reg1}, {imm}(${reg2})"), "lh"); +} + +TEST_F(AssemblerMIPS64Test, Lw) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lw, -16, "lw ${reg1}, {imm}(${reg2})"), "lw"); +} + +TEST_F(AssemblerMIPS64Test, Ld) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Ld, -16, "ld ${reg1}, {imm}(${reg2})"), "ld"); +} + +TEST_F(AssemblerMIPS64Test, Lbu) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lbu, -16, "lbu ${reg1}, {imm}(${reg2})"), "lbu"); +} + +TEST_F(AssemblerMIPS64Test, Lhu) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lhu, -16, "lhu ${reg1}, {imm}(${reg2})"), "lhu"); +} + +TEST_F(AssemblerMIPS64Test, Lwu) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lwu, -16, "lwu ${reg1}, {imm}(${reg2})"), "lwu"); +} + +TEST_F(AssemblerMIPS64Test, Lui) { + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lui, 16, "lui ${reg}, {imm}"), "lui"); +} + +TEST_F(AssemblerMIPS64Test, Dahi) { + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Dahi, 16, "dahi ${reg}, ${reg}, {imm}"), "dahi"); +} + +TEST_F(AssemblerMIPS64Test, Dati) { + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Dati, 16, "dati ${reg}, ${reg}, {imm}"), "dati"); +} + +TEST_F(AssemblerMIPS64Test, Sb) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sb, -16, "sb ${reg1}, {imm}(${reg2})"), "sb"); +} + +TEST_F(AssemblerMIPS64Test, Sh) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sh, -16, "sh ${reg1}, {imm}(${reg2})"), "sh"); +} + +TEST_F(AssemblerMIPS64Test, Sw) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sw, -16, "sw ${reg1}, {imm}(${reg2})"), "sw"); +} + +TEST_F(AssemblerMIPS64Test, Sd) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sd, -16, "sd ${reg1}, {imm}(${reg2})"), "sd"); +} + +TEST_F(AssemblerMIPS64Test, Slt) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Slt, "slt ${reg1}, ${reg2}, ${reg3}"), "slt"); +} + +TEST_F(AssemblerMIPS64Test, Sltu) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Sltu, "sltu ${reg1}, ${reg2}, ${reg3}"), "sltu"); +} + +TEST_F(AssemblerMIPS64Test, Slti) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Slti, -16, "slti ${reg1}, ${reg2}, {imm}"), + "slti"); +} + +TEST_F(AssemblerMIPS64Test, Sltiu) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sltiu, -16, "sltiu ${reg1}, ${reg2}, {imm}"), + "sltiu"); +} + +TEST_F(AssemblerMIPS64Test, Move) { + DriverStr(RepeatRR(&mips64::Mips64Assembler::Move, "or ${reg1}, ${reg2}, $zero"), "move"); +} + +TEST_F(AssemblerMIPS64Test, Clear) { + DriverStr(RepeatR(&mips64::Mips64Assembler::Clear, "or ${reg}, $zero, $zero"), "clear"); +} + +TEST_F(AssemblerMIPS64Test, Not) { + DriverStr(RepeatRR(&mips64::Mips64Assembler::Not, "nor ${reg1}, ${reg2}, $zero"), "not"); +} + TEST_F(AssemblerMIPS64Test, Bitswap) { DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap"); } @@ -1230,6 +1474,18 @@ TEST_F(AssemblerMIPS64Test, Dsra32) { "dsra32"); } +TEST_F(AssemblerMIPS64Test, Dsllv) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dsllv, "dsllv ${reg1}, ${reg2}, ${reg3}"), "dsllv"); +} + +TEST_F(AssemblerMIPS64Test, Dsrlv) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dsrlv, "dsrlv ${reg1}, ${reg2}, ${reg3}"), "dsrlv"); +} + +TEST_F(AssemblerMIPS64Test, Dsrav) { + DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dsrav, "dsrav ${reg1}, ${reg2}, ${reg3}"), "dsrav"); +} + TEST_F(AssemblerMIPS64Test, Sc) { DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sc, -9, "sc ${reg1}, {imm}(${reg2})"), "sc"); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 4f06a91448..5fc9972d09 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -1414,7 +1414,14 @@ TEST_F(VerifierDepsTest, VerifyDeps) { ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } - { + // The two tests below make sure that fiddling with the method kind + // (static, virtual, interface) is detected by `ValidateDependencies`. + + // An interface method lookup can succeed with a virtual method lookup on the same class. + // That's OK, as we only want to make sure there is a method being defined with the right + // flags. Therefore, polluting the interface methods with virtual methods does not have + // to fail verification. + if (resolution_kind != kVirtualMethodResolution) { VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); bool found = false; @@ -1433,7 +1440,8 @@ TEST_F(VerifierDepsTest, VerifyDeps) { ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } - { + // See comment above that applies the same way. + if (resolution_kind != kInterfaceMethodResolution) { VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); bool found = false; diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index a2d1190d03..e2ee94022f 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -741,7 +741,7 @@ class ClassDef : public IndexedItem { uint32_t GetAccessFlags() const { return access_flags_; } const TypeId* Superclass() const { return superclass_; } const TypeIdVector* Interfaces() - { return interfaces_ == nullptr ? nullptr: interfaces_->GetTypeList(); } + { return interfaces_ == nullptr ? nullptr : interfaces_->GetTypeList(); } uint32_t InterfacesOffset() { return interfaces_ == nullptr ? 0 : interfaces_->GetOffset(); } const StringId* SourceFile() const { return source_file_; } AnnotationsDirectoryItem* Annotations() const { return annotations_; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 148ee88669..69901c13cd 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -529,6 +529,12 @@ class OatDumper { } } + { + os << "OAT FILE STATS:\n"; + VariableIndentationOutputStream vios(&os); + stats_.Dump(vios); + } + os << std::flush; return success; } @@ -574,6 +580,116 @@ class OatDumper { return nullptr; } + struct Stats { + enum ByteKind { + kByteKindCode, + kByteKindQuickMethodHeader, + kByteKindCodeInfoLocationCatalog, + kByteKindCodeInfoDexRegisterMap, + kByteKindCodeInfoInlineInfo, + kByteKindCodeInfoEncoding, + kByteKindCodeInfoOther, + kByteKindStackMapNativePc, + kByteKindStackMapDexPc, + kByteKindStackMapDexRegisterMap, + kByteKindStackMapInlineInfo, + kByteKindStackMapRegisterMask, + kByteKindStackMapMask, + kByteKindStackMapOther, + kByteKindCount, + kByteKindStackMapFirst = kByteKindCodeInfoOther, + kByteKindStackMapLast = kByteKindStackMapOther, + }; + int64_t bits[kByteKindCount] = {}; + // Since code has deduplication, seen tracks already seen pointers to avoid double counting + // deduplicated code and tables. + std::unordered_set<const void*> seen; + + // Returns true if it was newly added. + bool AddBitsIfUnique(ByteKind kind, int64_t count, const void* address) { + if (seen.insert(address).second == true) { + // True means the address was not already in the set. + AddBits(kind, count); + return true; + } + return false; + } + + void AddBits(ByteKind kind, int64_t count) { + bits[kind] += count; + } + + void Dump(VariableIndentationOutputStream& os) { + const int64_t sum = std::accumulate(bits, bits + kByteKindCount, 0u); + os.Stream() << "Dumping cumulative use of " << sum / kBitsPerByte << " accounted bytes\n"; + if (sum > 0) { + const int64_t stack_map_bits = std::accumulate(bits + kByteKindStackMapFirst, + bits + kByteKindStackMapLast + 1, + 0u); + Dump(os, "Code ", bits[kByteKindCode], sum); + Dump(os, "QuickMethodHeader ", bits[kByteKindQuickMethodHeader], sum); + Dump(os, "CodeInfoEncoding ", bits[kByteKindCodeInfoEncoding], sum); + Dump(os, "CodeInfoLocationCatalog ", bits[kByteKindCodeInfoLocationCatalog], sum); + Dump(os, "CodeInfoDexRegisterMap ", bits[kByteKindCodeInfoDexRegisterMap], sum); + Dump(os, "CodeInfoInlineInfo ", bits[kByteKindCodeInfoInlineInfo], sum); + Dump(os, "CodeInfoStackMap ", stack_map_bits, sum); + { + ScopedIndentation indent1(&os); + Dump(os, + "StackMapNativePc ", + bits[kByteKindStackMapNativePc], + stack_map_bits, + "stack map"); + Dump(os, + "StackMapDexPcEncoding ", + bits[kByteKindStackMapDexPc], + stack_map_bits, + "stack map"); + Dump(os, + "StackMapDexRegisterMap ", + bits[kByteKindStackMapDexRegisterMap], + stack_map_bits, + "stack map"); + Dump(os, + "StackMapInlineInfo ", + bits[kByteKindStackMapInlineInfo], + stack_map_bits, + "stack map"); + Dump(os, + "StackMapRegisterMaskEncoding ", + bits[kByteKindStackMapRegisterMask], + stack_map_bits, + "stack map"); + Dump(os, + "StackMapMask ", + bits[kByteKindStackMapMask], + stack_map_bits, + "stack map"); + Dump(os, + "StackMapOther ", + bits[kByteKindStackMapOther], + stack_map_bits, + "stack map"); + } + } + os.Stream() << "\n" << std::flush; + } + + private: + void Dump(VariableIndentationOutputStream& os, + const char* name, + int64_t size, + int64_t total, + const char* sum_of = "total") { + const double percent = (static_cast<double>(size) / static_cast<double>(total)) * 100; + os.Stream() << StringPrintf("%s = %8" PRId64 " (%2.0f%% of %s)\n", + name, + size / kBitsPerByte, + percent, + sum_of); + } + }; + private: void AddAllOffsets() { // We don't know the length of the code for each method, but we need to know where to stop @@ -1046,7 +1162,9 @@ class OatDumper { vios->Stream() << "OatQuickMethodHeader "; uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset(); const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader(); - + stats_.AddBitsIfUnique(Stats::kByteKindQuickMethodHeader, + sizeof(*method_header) * kBitsPerByte, + method_header); if (options_.absolute_addresses_) { vios->Stream() << StringPrintf("%p ", method_header); } @@ -1118,6 +1236,7 @@ class OatDumper { const void* code = oat_method.GetQuickCode(); uint32_t aligned_code_begin = AlignCodeOffset(code_offset); uint64_t aligned_code_end = aligned_code_begin + code_size; + stats_.AddBitsIfUnique(Stats::kByteKindCode, code_size * kBitsPerByte, code); if (options_.absolute_addresses_) { vios->Stream() << StringPrintf("%p ", code); @@ -1223,7 +1342,8 @@ class OatDumper { code_info.Dump(vios, oat_method.GetCodeOffset(), code_item.registers_size_, - options_.dump_code_info_stack_maps_); + options_.dump_code_info_stack_maps_, + instruction_set_); } void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method, @@ -1329,21 +1449,22 @@ class OatDumper { // For identical native PCs, the order from the CodeInfo is preserved. class StackMapsHelper { public: - explicit StackMapsHelper(const uint8_t* raw_code_info) + explicit StackMapsHelper(const uint8_t* raw_code_info, InstructionSet instruction_set) : code_info_(raw_code_info), encoding_(code_info_.ExtractEncoding()), number_of_stack_maps_(code_info_.GetNumberOfStackMaps(encoding_)), indexes_(), - offset_(static_cast<size_t>(-1)), - stack_map_index_(0u) { + offset_(static_cast<uint32_t>(-1)), + stack_map_index_(0u), + instruction_set_(instruction_set) { if (number_of_stack_maps_ != 0u) { // Check if native PCs are ordered. bool ordered = true; StackMap last = code_info_.GetStackMapAt(0u, encoding_); for (size_t i = 1; i != number_of_stack_maps_; ++i) { StackMap current = code_info_.GetStackMapAt(i, encoding_); - if (last.GetNativePcOffset(encoding_.stack_map_encoding) > - current.GetNativePcOffset(encoding_.stack_map_encoding)) { + if (last.GetNativePcOffset(encoding_.stack_map_encoding, instruction_set) > + current.GetNativePcOffset(encoding_.stack_map_encoding, instruction_set)) { ordered = false; break; } @@ -1359,14 +1480,17 @@ class OatDumper { indexes_.end(), [this](size_t lhs, size_t rhs) { StackMap left = code_info_.GetStackMapAt(lhs, encoding_); - uint32_t left_pc = left.GetNativePcOffset(encoding_.stack_map_encoding); + uint32_t left_pc = left.GetNativePcOffset(encoding_.stack_map_encoding, + instruction_set_); StackMap right = code_info_.GetStackMapAt(rhs, encoding_); - uint32_t right_pc = right.GetNativePcOffset(encoding_.stack_map_encoding); + uint32_t right_pc = right.GetNativePcOffset(encoding_.stack_map_encoding, + instruction_set_); // If the PCs are the same, compare indexes to preserve the original order. return (left_pc < right_pc) || (left_pc == right_pc && lhs < rhs); }); } - offset_ = GetStackMapAt(0).GetNativePcOffset(encoding_.stack_map_encoding); + offset_ = GetStackMapAt(0).GetNativePcOffset(encoding_.stack_map_encoding, + instruction_set_); } } @@ -1378,7 +1502,7 @@ class OatDumper { return encoding_; } - size_t GetOffset() const { + uint32_t GetOffset() const { return offset_; } @@ -1389,8 +1513,9 @@ class OatDumper { void Next() { ++stack_map_index_; offset_ = (stack_map_index_ == number_of_stack_maps_) - ? static_cast<size_t>(-1) - : GetStackMapAt(stack_map_index_).GetNativePcOffset(encoding_.stack_map_encoding); + ? static_cast<uint32_t>(-1) + : GetStackMapAt(stack_map_index_).GetNativePcOffset(encoding_.stack_map_encoding, + instruction_set_); } private: @@ -1406,8 +1531,9 @@ class OatDumper { const CodeInfoEncoding encoding_; const size_t number_of_stack_maps_; dchecked_vector<size_t> indexes_; // Used if stack map native PCs are not ordered. - size_t offset_; + uint32_t offset_; size_t stack_map_index_; + const InstructionSet instruction_set_; }; void DumpCode(VariableIndentationOutputStream* vios, @@ -1423,7 +1549,61 @@ class OatDumper { return; } else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) { // The optimizing compiler outputs its CodeInfo data in the vmap table. - StackMapsHelper helper(oat_method.GetVmapTable()); + StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_); + { + CodeInfoEncoding encoding(helper.GetEncoding()); + StackMapEncoding stack_map_encoding(encoding.stack_map_encoding); + // helper.GetCodeInfo().GetStackMapAt(0, encoding).; + const size_t num_stack_maps = encoding.number_of_stack_maps; + std::vector<uint8_t> size_vector; + encoding.Compress(&size_vector); + if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding, + size_vector.size() * kBitsPerByte, + oat_method.GetVmapTable())) { + stats_.AddBits( + Stats::kByteKindStackMapNativePc, + stack_map_encoding.GetNativePcEncoding().BitSize() * num_stack_maps); + stats_.AddBits( + Stats::kByteKindStackMapDexPc, + stack_map_encoding.GetDexPcEncoding().BitSize() * num_stack_maps); + stats_.AddBits( + Stats::kByteKindStackMapDexRegisterMap, + stack_map_encoding.GetDexRegisterMapEncoding().BitSize() * num_stack_maps); + stats_.AddBits( + Stats::kByteKindStackMapInlineInfo, + stack_map_encoding.GetInlineInfoEncoding().BitSize() * num_stack_maps); + stats_.AddBits( + Stats::kByteKindStackMapRegisterMask, + stack_map_encoding.GetRegisterMaskEncoding().BitSize() * num_stack_maps); + const size_t stack_mask_bits = encoding.stack_map_size_in_bytes * kBitsPerByte - + stack_map_encoding.GetStackMaskBitOffset(); + stats_.AddBits( + Stats::kByteKindStackMapMask, + stack_mask_bits * num_stack_maps); + const size_t stack_map_bits = + stack_map_encoding.GetStackMaskBitOffset() + stack_mask_bits; + stats_.AddBits( + Stats::kByteKindStackMapOther, + (encoding.stack_map_size_in_bytes * kBitsPerByte - stack_map_bits) * num_stack_maps); + const size_t stack_map_bytes = helper.GetCodeInfo().GetStackMapsSize(encoding); + const size_t location_catalog_bytes = + helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding); + stats_.AddBits(Stats::kByteKindCodeInfoLocationCatalog, + kBitsPerByte * location_catalog_bytes); + const size_t dex_register_bytes = + helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, code_item->registers_size_); + stats_.AddBits( + Stats::kByteKindCodeInfoDexRegisterMap, + kBitsPerByte * dex_register_bytes); + const size_t inline_info_bytes = + encoding.non_header_size - + stack_map_bytes - + location_catalog_bytes - + dex_register_bytes; + stats_.AddBits(Stats::kByteKindCodeInfoInlineInfo, + inline_info_bytes * kBitsPerByte); + } + } const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code); size_t offset = 0; while (offset < code_size) { @@ -1436,7 +1616,8 @@ class OatDumper { helper.GetCodeInfo(), helper.GetEncoding(), oat_method.GetCodeOffset(), - code_item->registers_size_); + code_item->registers_size_, + instruction_set_); do { helper.Next(); // There may be multiple stack maps at a given PC. We display only the first one. @@ -1460,6 +1641,7 @@ class OatDumper { const InstructionSet instruction_set_; std::set<uintptr_t> offsets_; Disassembler* disassembler_; + Stats stats_; }; class ImageDumper { @@ -1776,7 +1958,7 @@ class ImageDumper { os << StringPrintf("%d (0x%x)\n", field->GetShort(obj), field->GetShort(obj)); break; case Primitive::kPrimBoolean: - os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj)? "true" : "false", + os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj) ? "true" : "false", field->GetBoolean(obj)); break; case Primitive::kPrimByte: @@ -2132,7 +2314,6 @@ class ImageDumper { size_t managed_code_bytes; size_t managed_code_bytes_ignoring_deduplication; - size_t managed_to_native_code_bytes; size_t native_to_managed_code_bytes; size_t class_initializer_code_bytes; size_t large_initializer_code_bytes; @@ -2161,7 +2342,6 @@ class ImageDumper { alignment_bytes(0), managed_code_bytes(0), managed_code_bytes_ignoring_deduplication(0), - managed_to_native_code_bytes(0), native_to_managed_code_bytes(0), class_initializer_code_bytes(0), large_initializer_code_bytes(0), @@ -2359,7 +2539,6 @@ class ImageDumper { os << StringPrintf("oat_file_bytes = %8zd\n" "managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n" - "managed_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n" "native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n" "class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n" "large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n" @@ -2367,8 +2546,6 @@ class ImageDumper { oat_file_bytes, managed_code_bytes, PercentOfOatBytes(managed_code_bytes), - managed_to_native_code_bytes, - PercentOfOatBytes(managed_to_native_code_bytes), native_to_managed_code_bytes, PercentOfOatBytes(native_to_managed_code_bytes), class_initializer_code_bytes, diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc index e77d03bae7..ba57d1860c 100644 --- a/oatdump/oatdump_test.cc +++ b/oatdump/oatdump_test.cc @@ -102,6 +102,7 @@ class OatDumpTest : public CommonRuntimeTest { // Code and dex code do not show up if list only. expected_prefixes.push_back("DEX CODE:"); expected_prefixes.push_back("CODE:"); + expected_prefixes.push_back("CodeInfoEncoding"); } if (mode == kModeArt) { exec_argv.push_back("--image=" + core_art_location_); diff --git a/runtime/arch/arm/quick_method_frame_info_arm.h b/runtime/arch/arm/quick_method_frame_info_arm.h index 4b23c77bc5..35f1948138 100644 --- a/runtime/arch/arm/quick_method_frame_info_arm.h +++ b/runtime/arch/arm/quick_method_frame_info_arm.h @@ -62,7 +62,7 @@ constexpr uint32_t ArmCalleeSaveCoreSpills(Runtime::CalleeSaveType type) { constexpr uint32_t ArmCalleeSaveFpSpills(Runtime::CalleeSaveType type) { return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills | - (type == Runtime::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills: 0) | + (type == Runtime::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) | (type == Runtime::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) | (type == Runtime::kSaveEverything ? kArmCalleeSaveFpEverythingSpills : 0); } diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index c59874332f..01bd177221 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -33,7 +33,16 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( const std::string& variant, std::string* error_msg) { // Look for variants that need a fix for a53 erratum 835769. static const char* arm64_variants_with_a53_835769_bug[] = { - "default", "generic", "cortex-a53" // Pessimistically assume all generic ARM64s are A53s. + // Pessimistically assume all generic CPUs are cortex-a53. + "default", + "generic", + "cortex-a53", + "cortex-a53.a57", + "cortex-a53.a72", + // Pessimistically assume all "big" cortex CPUs are paired with a cortex-a53. + "cortex-a57", + "cortex-a72", + "cortex-a73", }; bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug, arraysize(arm64_variants_with_a53_835769_bug), @@ -42,7 +51,10 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( if (!needs_a53_835769_fix) { // Check to see if this is an expected variant. static const char* arm64_known_variants[] = { - "denver64", "kryo", "exynos-m1" + "cortex-a35", + "exynos-m1", + "denver64", + "kryo" }; if (!FindVariantInArray(arm64_known_variants, arraysize(arm64_known_variants), variant)) { std::ostringstream os; diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc index cefa4993c8..91cb58fedf 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc @@ -30,6 +30,40 @@ TEST(Arm64InstructionSetFeaturesTest, Arm64Features) { EXPECT_TRUE(arm64_features->Equals(arm64_features.get())); EXPECT_STREQ("a53", arm64_features->GetFeatureString().c_str()); EXPECT_EQ(arm64_features->AsBitmap(), 1U); + + std::unique_ptr<const InstructionSetFeatures> cortex_a57_features( + InstructionSetFeatures::FromVariant(kArm64, "cortex-a57", &error_msg)); + ASSERT_TRUE(cortex_a57_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a57_features->GetInstructionSet(), kArm64); + EXPECT_TRUE(cortex_a57_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("a53", cortex_a57_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a57_features->AsBitmap(), 1U); + + std::unique_ptr<const InstructionSetFeatures> cortex_a73_features( + InstructionSetFeatures::FromVariant(kArm64, "cortex-a73", &error_msg)); + ASSERT_TRUE(cortex_a73_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a73_features->GetInstructionSet(), kArm64); + EXPECT_TRUE(cortex_a73_features->Equals(cortex_a73_features.get())); + EXPECT_STREQ("a53", cortex_a73_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a73_features->AsBitmap(), 1U); + + std::unique_ptr<const InstructionSetFeatures> cortex_a35_features( + InstructionSetFeatures::FromVariant(kArm64, "cortex-a35", &error_msg)); + ASSERT_TRUE(cortex_a35_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a35_features->GetInstructionSet(), kArm64); + EXPECT_TRUE(cortex_a35_features->Equals(cortex_a35_features.get())); + EXPECT_STREQ("-a53", cortex_a35_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a35_features->AsBitmap(), 0U); + + std::unique_ptr<const InstructionSetFeatures> kryo_features( + InstructionSetFeatures::FromVariant(kArm64, "kryo", &error_msg)); + ASSERT_TRUE(kryo_features.get() != nullptr) << error_msg; + EXPECT_EQ(kryo_features->GetInstructionSet(), kArm64); + EXPECT_TRUE(kryo_features->Equals(kryo_features.get())); + EXPECT_TRUE(kryo_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str()); + EXPECT_EQ(kryo_features->AsBitmap(), 0U); } } // namespace art diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/quick_method_frame_info_arm64.h index 36f283bc91..32d9d08739 100644 --- a/runtime/arch/arm64/quick_method_frame_info_arm64.h +++ b/runtime/arch/arm64/quick_method_frame_info_arm64.h @@ -85,7 +85,7 @@ constexpr uint32_t Arm64CalleeSaveCoreSpills(Runtime::CalleeSaveType type) { constexpr uint32_t Arm64CalleeSaveFpSpills(Runtime::CalleeSaveType type) { return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills | - (type == Runtime::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills: 0) | + (type == Runtime::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) | (type == Runtime::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) | (type == Runtime::kSaveEverything ? kArm64CalleeSaveFpEverythingSpills : 0); } diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h new file mode 100644 index 0000000000..ab04b1eaa7 --- /dev/null +++ b/runtime/arch/code_offset.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_ARCH_CODE_OFFSET_H_ +#define ART_RUNTIME_ARCH_CODE_OFFSET_H_ + +#include <iosfwd> + +#include "base/bit_utils.h" +#include "base/logging.h" +#include "instruction_set.h" + +namespace art { + +// CodeOffset is a holder for compressed code offsets. Since some architectures have alignment +// requirements it is possible to compress code offsets to reduce stack map sizes. +class CodeOffset { + public: + ALWAYS_INLINE static CodeOffset FromOffset(uint32_t offset, InstructionSet isa = kRuntimeISA) { + return CodeOffset(offset / GetInstructionSetInstructionAlignment(isa)); + } + + ALWAYS_INLINE static CodeOffset FromCompressedOffset(uint32_t offset) { + return CodeOffset(offset); + } + + ALWAYS_INLINE uint32_t Uint32Value(InstructionSet isa = kRuntimeISA) const { + uint32_t decoded = value_ * GetInstructionSetInstructionAlignment(isa); + DCHECK_GE(decoded, value_) << "Integer overflow"; + return decoded; + } + + // Return compressed internal value. + ALWAYS_INLINE uint32_t CompressedValue() const { + return value_; + } + + ALWAYS_INLINE CodeOffset() = default; + ALWAYS_INLINE CodeOffset(const CodeOffset&) = default; + ALWAYS_INLINE CodeOffset& operator=(const CodeOffset&) = default; + ALWAYS_INLINE CodeOffset& operator=(CodeOffset&&) = default; + + private: + ALWAYS_INLINE explicit CodeOffset(uint32_t value) : value_(value) {} + + uint32_t value_ = 0u; +}; + +inline bool operator==(const CodeOffset& a, const CodeOffset& b) { + return a.CompressedValue() == b.CompressedValue(); +} + +inline bool operator!=(const CodeOffset& a, const CodeOffset& b) { + return !(a == b); +} + +inline bool operator<(const CodeOffset& a, const CodeOffset& b) { + return a.CompressedValue() < b.CompressedValue(); +} + +inline bool operator<=(const CodeOffset& a, const CodeOffset& b) { + return a.CompressedValue() <= b.CompressedValue(); +} + +inline bool operator>(const CodeOffset& a, const CodeOffset& b) { + return a.CompressedValue() > b.CompressedValue(); +} + +inline bool operator>=(const CodeOffset& a, const CodeOffset& b) { + return a.CompressedValue() >= b.CompressedValue(); +} + +inline std::ostream& operator<<(std::ostream& os, const CodeOffset& offset) { + return os << offset.Uint32Value(); +} + +} // namespace art + +#endif // ART_RUNTIME_ARCH_CODE_OFFSET_H_ diff --git a/runtime/arch/instruction_set.h b/runtime/arch/instruction_set.h index 4a8bea45e5..99aea62468 100644 --- a/runtime/arch/instruction_set.h +++ b/runtime/arch/instruction_set.h @@ -75,6 +75,14 @@ static constexpr size_t kMipsAlignment = 8; // X86 instruction alignment. This is the recommended alignment for maximum performance. static constexpr size_t kX86Alignment = 16; +// Different than code alignment since code alignment is only first instruction of method. +static constexpr size_t kThumb2InstructionAlignment = 2; +static constexpr size_t kArm64InstructionAlignment = 4; +static constexpr size_t kX86InstructionAlignment = 1; +static constexpr size_t kX86_64InstructionAlignment = 1; +static constexpr size_t kMipsInstructionAlignment = 2; +static constexpr size_t kMips64InstructionAlignment = 2; + const char* GetInstructionSetString(InstructionSet isa); // Note: Returns kNone when the string cannot be parsed to a known value. @@ -106,6 +114,17 @@ static inline PointerSize GetInstructionSetPointerSize(InstructionSet isa) { } } +ALWAYS_INLINE static inline constexpr size_t GetInstructionSetInstructionAlignment( + InstructionSet isa) { + return (isa == kThumb2 || isa == kArm) ? kThumb2InstructionAlignment : + (isa == kArm64) ? kArm64InstructionAlignment : + (isa == kX86) ? kX86InstructionAlignment : + (isa == kX86_64) ? kX86_64InstructionAlignment : + (isa == kMips) ? kMipsInstructionAlignment : + (isa == kMips64) ? kMips64InstructionAlignment : + 0; // Invalid case, but constexpr doesn't support asserts. +} + static inline bool IsValidInstructionSet(InstructionSet isa) { switch (isa) { case kArm: diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc index 5aae93acc5..b251b57c99 100644 --- a/runtime/arch/instruction_set_test.cc +++ b/runtime/arch/instruction_set_test.cc @@ -44,6 +44,15 @@ TEST(InstructionSetTest, GetInstructionSetString) { EXPECT_STREQ("none", GetInstructionSetString(kNone)); } +TEST(InstructionSetTest, GetInstructionSetInstructionAlignment) { + EXPECT_EQ(GetInstructionSetInstructionAlignment(kThumb2), kThumb2InstructionAlignment); + EXPECT_EQ(GetInstructionSetInstructionAlignment(kArm64), kArm64InstructionAlignment); + EXPECT_EQ(GetInstructionSetInstructionAlignment(kX86), kX86InstructionAlignment); + EXPECT_EQ(GetInstructionSetInstructionAlignment(kX86_64), kX86_64InstructionAlignment); + EXPECT_EQ(GetInstructionSetInstructionAlignment(kMips), kMipsInstructionAlignment); + EXPECT_EQ(GetInstructionSetInstructionAlignment(kMips64), kMips64InstructionAlignment); +} + TEST(InstructionSetTest, TestRoundTrip) { EXPECT_EQ(kRuntimeISA, GetInstructionSetFromString(GetInstructionSetString(kRuntimeISA))); } diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/quick_method_frame_info_mips64.h index 397776e999..d774473289 100644 --- a/runtime/arch/mips64/quick_method_frame_info_mips64.h +++ b/runtime/arch/mips64/quick_method_frame_info_mips64.h @@ -78,7 +78,7 @@ constexpr uint32_t Mips64CalleeSaveCoreSpills(Runtime::CalleeSaveType type) { constexpr uint32_t Mips64CalleeSaveFpSpills(Runtime::CalleeSaveType type) { return kMips64CalleeSaveFpRefSpills | - (type == Runtime::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills: 0) | + (type == Runtime::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) | (type == Runtime::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) | (type == Runtime::kSaveEverything ? kMips64CalleeSaveFpEverythingSpills : 0); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 14918df4d5..b8ed530981 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1398,7 +1398,11 @@ class UpdateClassLoaderVisitor { class_loader_(class_loader) {} bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) { - klass->SetClassLoader(class_loader_); + // Do not update class loader for boot image classes where the app image + // class loader is only the initiating loader but not the defining loader. + if (klass->GetClassLoader() != nullptr) { + klass->SetClassLoader(class_loader_); + } return true; } @@ -2458,10 +2462,8 @@ mirror::Class* ClassLinker::FindClass(Thread* self, return EnsureResolved(self, descriptor, klass); } // Class is not yet loaded. - if (descriptor[0] == '[') { - return CreateArrayClass(self, descriptor, hash, class_loader); - } else if (class_loader.Get() == nullptr) { - // The boot class loader, search the boot class path. + if (descriptor[0] != '[' && class_loader.Get() == nullptr) { + // Non-array class and the boot class loader, search the boot class path. ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); if (pair.second != nullptr) { return DefineClass(self, @@ -2474,14 +2476,21 @@ mirror::Class* ClassLinker::FindClass(Thread* self, // The boot class loader is searched ahead of the application class loader, failures are // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to // trigger the chaining with a proper stack trace. - ObjPtr<mirror::Throwable> pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + ObjPtr<mirror::Throwable> pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); self->SetException(pre_allocated); return nullptr; } + } + ObjPtr<mirror::Class> result_ptr; + bool descriptor_equals; + if (descriptor[0] == '[') { + result_ptr = CreateArrayClass(self, descriptor, hash, class_loader); + DCHECK_EQ(result_ptr == nullptr, self->IsExceptionPending()); + DCHECK(result_ptr == nullptr || result_ptr->DescriptorEquals(descriptor)); + descriptor_equals = true; } else { ScopedObjectAccessUnchecked soa(self); - ObjPtr<mirror::Class> result_ptr; - bool descriptor_equals; bool known_hierarchy = FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr); if (result_ptr != nullptr) { @@ -2525,16 +2534,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self, WellKnownClasses::java_lang_ClassLoader_loadClass, class_name_object.get())); } - if (self->IsExceptionPending()) { - // If the ClassLoader threw, pass that exception up. - // However, to comply with the RI behavior, first check if another thread succeeded. - result_ptr = LookupClass(self, descriptor, hash, class_loader.Get()); - if (result_ptr != nullptr && !result_ptr->IsErroneous()) { - self->ClearException(); - return EnsureResolved(self, descriptor, result_ptr); - } - return nullptr; - } else if (result.get() == nullptr) { + if (result.get() == nullptr && !self->IsExceptionPending()) { // broken loader - throw NPE to be compatible with Dalvik ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s", class_name_string.c_str()).c_str()); @@ -2542,50 +2542,60 @@ mirror::Class* ClassLinker::FindClass(Thread* self, } result_ptr = soa.Decode<mirror::Class>(result.get()); // Check the name of the returned class. - descriptor_equals = result_ptr->DescriptorEquals(descriptor); + descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor); } + } - // Try to insert the class to the class table, checking for mismatch. - ObjPtr<mirror::Class> old; - { - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); - old = class_table->Lookup(descriptor, hash); - if (old == nullptr) { - old = result_ptr; // For the comparison below, after releasing the lock. - if (descriptor_equals) { - class_table->InsertWithHash(result_ptr.Ptr(), hash); - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); - } // else throw below, after releasing the lock. - } - } - if (UNLIKELY(old != result_ptr)) { - // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel - // capable class loaders. (All class loaders are considered parallel capable on Android.) - mirror::Class* loader_class = class_loader->GetClass(); - const char* loader_class_name = - loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex()); - LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name) - << " is not well-behaved; it returned a different Class for racing loadClass(\"" - << DescriptorToDot(descriptor) << "\")."; - return EnsureResolved(self, descriptor, old); - } - if (UNLIKELY(!descriptor_equals)) { - std::string result_storage; - const char* result_name = result_ptr->GetDescriptor(&result_storage); - std::string loader_storage; - const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage); - ThrowNoClassDefFoundError( - "Initiating class loader of type %s returned class %s instead of %s.", - DescriptorToDot(loader_class_name).c_str(), - DescriptorToDot(result_name).c_str(), - DescriptorToDot(descriptor).c_str()); - return nullptr; + if (self->IsExceptionPending()) { + // If the ClassLoader threw or array class allocation failed, pass that exception up. + // However, to comply with the RI behavior, first check if another thread succeeded. + result_ptr = LookupClass(self, descriptor, hash, class_loader.Get()); + if (result_ptr != nullptr && !result_ptr->IsErroneous()) { + self->ClearException(); + return EnsureResolved(self, descriptor, result_ptr); } - // success, return mirror::Class* - return result_ptr.Ptr(); + return nullptr; } - UNREACHABLE(); + + // Try to insert the class to the class table, checking for mismatch. + ObjPtr<mirror::Class> old; + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); + old = class_table->Lookup(descriptor, hash); + if (old == nullptr) { + old = result_ptr; // For the comparison below, after releasing the lock. + if (descriptor_equals) { + class_table->InsertWithHash(result_ptr.Ptr(), hash); + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + } // else throw below, after releasing the lock. + } + } + if (UNLIKELY(old != result_ptr)) { + // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel + // capable class loaders. (All class loaders are considered parallel capable on Android.) + mirror::Class* loader_class = class_loader->GetClass(); + const char* loader_class_name = + loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex()); + LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name) + << " is not well-behaved; it returned a different Class for racing loadClass(\"" + << DescriptorToDot(descriptor) << "\")."; + return EnsureResolved(self, descriptor, old); + } + if (UNLIKELY(!descriptor_equals)) { + std::string result_storage; + const char* result_name = result_ptr->GetDescriptor(&result_storage); + std::string loader_storage; + const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage); + ThrowNoClassDefFoundError( + "Initiating class loader of type %s returned class %s instead of %s.", + DescriptorToDot(loader_class_name).c_str(), + DescriptorToDot(result_name).c_str(), + DescriptorToDot(descriptor).c_str()); + return nullptr; + } + // success, return mirror::Class* + return result_ptr.Ptr(); } mirror::Class* ClassLinker::DefineClass(Thread* self, @@ -2626,13 +2636,26 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, self->AssertPendingOOMException(); return nullptr; } - ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(dex_file, class_loader.Get()); + // Get the real dex file. This will return the input if there aren't any callbacks or they do + // nothing. + DexFile const* new_dex_file = nullptr; + DexFile::ClassDef const* new_class_def = nullptr; + // TODO We should ideally figure out some way to move this after we get a lock on the klass so it + // will only be called once. + Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor, + klass, + class_loader, + dex_file, + dex_class_def, + &new_dex_file, + &new_class_def); + ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get()); if (dex_cache == nullptr) { self->AssertPendingOOMException(); return nullptr; } klass->SetDexCache(dex_cache); - SetupClass(dex_file, dex_class_def, klass, class_loader.Get()); + SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get()); // Mark the string class by setting its access flag. if (UNLIKELY(!init_done_)) { @@ -2658,7 +2681,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, // end up allocating unfree-able linear alloc resources and then lose the race condition. The // other reason is that the field roots are only visited from the class table. So we need to be // inserted before we allocate / fill in these fields. - LoadClass(self, dex_file, dex_class_def, klass); + LoadClass(self, *new_dex_file, *new_class_def, klass); if (self->IsExceptionPending()) { VLOG(class_linker) << self->GetException()->Dump(); // An exception occured during load, set status to erroneous while holding klass' lock in case @@ -2671,7 +2694,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, // Finish loading (if necessary) by finding parents CHECK(!klass->IsLoaded()); - if (!LoadSuperAndInterfaces(klass, dex_file)) { + if (!LoadSuperAndInterfaces(klass, *new_dex_file)) { // Loading failed. if (!klass->IsErroneous()) { mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); @@ -3494,7 +3517,8 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto // class to the hash table --- necessary because of possible races with // other threads.) if (class_loader.Get() != component_type->GetClassLoader()) { - ObjPtr<mirror::Class> new_class = LookupClass(self, descriptor, hash, component_type->GetClassLoader()); + ObjPtr<mirror::Class> new_class = + LookupClass(self, descriptor, hash, component_type->GetClassLoader()); if (new_class != nullptr) { return new_class.Ptr(); } @@ -7712,7 +7736,7 @@ ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file, type = LookupClass(self, descriptor, hash, class_loader.Ptr()); } } - if (type != nullptr || type->IsResolved()) { + if (type != nullptr && type->IsResolved()) { return type.Ptr(); } return nullptr; diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 8da979b36f..d3bb58dc33 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -1199,6 +1199,23 @@ class ClassLoadCallback { public: virtual ~ClassLoadCallback() {} + // If set we will replace initial_class_def & initial_dex_file with the final versions. The + // callback author is responsible for ensuring these are allocated in such a way they can be + // cleaned up if another transformation occurs. Note that both must be set or null/unchanged on + // return. + // Note: the class may be temporary, in which case a following ClassPrepare event will be a + // different object. It is the listener's responsibility to handle this. + // Note: This callback is rarely useful so a default implementation has been given that does + // nothing. + virtual void ClassPreDefine(const char* descriptor ATTRIBUTE_UNUSED, + Handle<mirror::Class> klass ATTRIBUTE_UNUSED, + Handle<mirror::ClassLoader> class_loader ATTRIBUTE_UNUSED, + const DexFile& initial_dex_file ATTRIBUTE_UNUSED, + const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED, + /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED, + /*out*/DexFile::ClassDef const** final_dex_cache ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) {} + // A class has been loaded. // Note: the class may be temporary, in which case a following ClassPrepare event will be a // different object. It is the listener's responsibility to handle this. diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 0341c64969..7b6c0dc510 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -487,7 +487,7 @@ struct CheckOffsets { // says AccessibleObject is 9 bytes but sizeof(AccessibleObject) is 12 bytes due to padding. // The RoundUp is to get around this case. static constexpr size_t kPackAlignment = 4; - size_t expected_size = RoundUp(is_static ? klass->GetClassSize(): klass->GetObjectSize(), + size_t expected_size = RoundUp(is_static ? klass->GetClassSize() : klass->GetObjectSize(), kPackAlignment); if (sizeof(T) != expected_size) { LOG(ERROR) << "Class size mismatch:" @@ -612,7 +612,7 @@ struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> { ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") { addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods"); - addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_cache_), "originalDexCache"); + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_bytes_), "originalDexFile"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError"); } }; @@ -906,6 +906,41 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { klass); } +TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("AllFields")))); + // Get the AllFields class for the dex cache and dex file. + ObjPtr<mirror::Class> all_fields_klass + = class_linker_->FindClass(soa.Self(), "LAllFields;", class_loader); + ASSERT_OBJ_PTR_NE(all_fields_klass, ObjPtr<mirror::Class>(nullptr)); + Handle<mirror::DexCache> dex_cache = hs.NewHandle(all_fields_klass->GetDexCache()); + const DexFile& dex_file = *dex_cache->GetDexFile(); + // Get the index of the array class we want to test. + const DexFile::TypeId* array_id = dex_file.FindTypeId("[Ljava/lang/Object;"); + ASSERT_TRUE(array_id != nullptr); + dex::TypeIndex array_idx = dex_file.GetIndexForTypeId(*array_id); + // Check that the array class wasn't resolved yet. + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + ObjPtr<mirror::Class>(nullptr)); + // Resolve the array class we want to test. + ObjPtr<mirror::Class> array_klass + = class_linker_->FindClass(soa.Self(), "[Ljava/lang/Object;", class_loader); + ASSERT_OBJ_PTR_NE(array_klass, ObjPtr<mirror::Class>(nullptr)); + // Test that LookupResolvedType() finds the array class. + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + array_klass); + // Zero out the resolved type and make sure LookupResolvedType() still finds it. + dex_cache->SetResolvedType(array_idx, nullptr); + EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr); + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + array_klass); +} + TEST_F(ClassLinkerTest, LibCore) { ScopedObjectAccess soa(Thread::Current()); ASSERT_TRUE(java_lang_dex_file_ != nullptr); diff --git a/runtime/class_table.cc b/runtime/class_table.cc index 0f985c6424..ff846a718e 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -129,6 +129,19 @@ void ClassTable::Insert(ObjPtr<mirror::Class> klass) { classes_.back().InsertWithHash(TableSlot(klass, hash), hash); } +void ClassTable::CopyWithoutLocks(const ClassTable& source_table) { + if (kIsDebugBuild) { + for (ClassSet& class_set : classes_) { + CHECK(class_set.Empty()); + } + } + for (const ClassSet& class_set : source_table.classes_) { + for (const TableSlot& slot : class_set) { + classes_.back().Insert(slot); + } + } +} + void ClassTable::InsertWithoutLocks(ObjPtr<mirror::Class> klass) { const uint32_t hash = TableSlot::HashDescriptor(klass); classes_.back().InsertWithHash(TableSlot(klass, hash), hash); diff --git a/runtime/class_table.h b/runtime/class_table.h index f27d8093ce..c8ec28eca4 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -240,6 +240,7 @@ class ClassTable { } private: + void CopyWithoutLocks(const ClassTable& source_table) NO_THREAD_SAFETY_ANALYSIS; void InsertWithoutLocks(ObjPtr<mirror::Class> klass) NO_THREAD_SAFETY_ANALYSIS; size_t CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader, diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 34d82845dc..268cca0cfd 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -360,7 +360,7 @@ Heap::Heap(size_t initial_size, // If we are the zygote, the non moving space becomes the zygote space when we run // PreZygoteFork the first time. In this case, call the map "zygote space" since we can't // rename the mem map later. - const char* space_name = is_zygote ? kZygoteSpaceName: kNonMovingSpaceName; + const char* space_name = is_zygote ? kZygoteSpaceName : kNonMovingSpaceName; // Reserve the non moving mem map before the other two since it needs to be at a specific // address. non_moving_space_mem_map.reset( diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h index b15544d549..38b68cbbe8 100644 --- a/runtime/gc/reference_processor.h +++ b/runtime/gc/reference_processor.h @@ -42,7 +42,7 @@ class GarbageCollector; class Heap; -// Used to process java.lang.References concurrently or paused. +// Used to process java.lang.ref.Reference instances concurrently or paused. class ReferenceProcessor { public: explicit ReferenceProcessor(); diff --git a/runtime/gc/scoped_gc_critical_section.h b/runtime/gc/scoped_gc_critical_section.h index ec93bca802..1271ff7af3 100644 --- a/runtime/gc/scoped_gc_critical_section.h +++ b/runtime/gc/scoped_gc_critical_section.h @@ -27,8 +27,8 @@ class Thread; namespace gc { -// Wait until the GC is finished and then prevent GC from starting until the destructor. Used -// to prevent deadlocks in places where we call ClassLinker::VisitClass with all th threads +// Wait until the GC is finished and then prevent the GC from starting until the destructor. Used +// to prevent deadlocks in places where we call ClassLinker::VisitClass with all the threads // suspended. class ScopedGCCriticalSection { public: diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 76777d938b..28bcb97105 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -596,7 +596,7 @@ bool DoInvokePolymorphic(Thread* self, // Get the register arguments for the invoke. inst->GetVarArgs(args, inst_data); // Drop the first register which is the method handle performing the invoke. - memcpy(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1)); + memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1)); args[Instruction::kMaxVarArgRegs - 1] = 0; return DoInvokePolymorphic<is_range, do_access_check>(self, invoke_method, diff --git a/runtime/interpreter/mterp/arm64/op_iput_wide_quick.S b/runtime/interpreter/mterp/arm64/op_iput_wide_quick.S index 6cec3633a6..28e831a5f8 100644 --- a/runtime/interpreter/mterp/arm64/op_iput_wide_quick.S +++ b/runtime/interpreter/mterp/arm64/op_iput_wide_quick.S @@ -4,7 +4,7 @@ GET_VREG w2, w2 // w2<- fp[B], the object pointer ubfx w0, wINST, #8, #4 // w0<- A cbz w2, common_errNullObject // object was null - GET_VREG_WIDE x0, w0 // x0-< fp[A] + GET_VREG_WIDE x0, w0 // x0<- fp[A] FETCH_ADVANCE_INST 2 // advance rPC, load wINST str x0, [x2, x3] // obj.field<- x0 GET_INST_OPCODE ip // extract opcode from wINST diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index 681790daa8..7d442c0b4b 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -6593,7 +6593,7 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w2, w2 // w2<- fp[B], the object pointer ubfx w0, wINST, #8, #4 // w0<- A cbz w2, common_errNullObject // object was null - GET_VREG_WIDE x0, w0 // x0-< fp[A] + GET_VREG_WIDE x0, w0 // x0<- fp[A] FETCH_ADVANCE_INST 2 // advance rPC, load wINST str x0, [x2, x3] // obj.field<- x0 GET_INST_OPCODE ip // extract opcode from wINST diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 7dd3d3db4d..feb6e0857a 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -1443,7 +1443,7 @@ void UnstartedRuntime::UnstartedMethodInvoke( ObjPtr<mirror::Object> java_method_obj = shadow_frame->GetVRegReference(arg_offset); ScopedLocalRef<jobject> java_method(env, - java_method_obj == nullptr ? nullptr :env->AddLocalReference<jobject>(java_method_obj)); + java_method_obj == nullptr ? nullptr : env->AddLocalReference<jobject>(java_method_obj)); ObjPtr<mirror::Object> java_receiver_obj = shadow_frame->GetVRegReference(arg_offset + 1); ScopedLocalRef<jobject> java_receiver(env, diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 2bb8819cb3..6deb03dc41 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -514,7 +514,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, } } - native_pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding) + + native_pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA) + osr_method->GetEntryPoint(); VLOG(jit) << "Jumping to " << method_name diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 7c6a710cef..efd949e031 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -113,6 +113,11 @@ void ClassExt::SetVerifyError(ObjPtr<Object> err) { } } +void ClassExt::SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) { + DCHECK(!Runtime::Current()->IsActiveTransaction()); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_), bytes); +} + void ClassExt::SetClass(ObjPtr<Class> dalvik_system_ClassExt) { CHECK(dalvik_system_ClassExt != nullptr); dalvik_system_ClassExt_ = GcRoot<Class>(dalvik_system_ClassExt); diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h index 91046314db..ad8a61b676 100644 --- a/runtime/mirror/class_ext.h +++ b/runtime/mirror/class_ext.h @@ -61,6 +61,12 @@ class MANAGED ClassExt : public Object { return GetFieldObject<PointerArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_)); } + ByteArray* GetOriginalDexFileBytes() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<ByteArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_)); + } + + void SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) REQUIRES_SHARED(Locks::mutator_lock_); + void SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches) REQUIRES_SHARED(Locks::mutator_lock_); @@ -80,7 +86,7 @@ class MANAGED ClassExt : public Object { HeapReference<PointerArray> obsolete_methods_; - HeapReference<DexCache> original_dex_cache_; + HeapReference<ByteArray> original_dex_file_bytes_; // The saved verification error of this class. HeapReference<Object> verify_error_; diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 9c0927584e..071b0e2c75 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1394,6 +1394,12 @@ void MonitorList::SweepMonitorList(IsMarkedVisitor* visitor) { } } +size_t MonitorList::Size() { + Thread* self = Thread::Current(); + MutexLock mu(self, monitor_list_lock_); + return list_.size(); +} + class MonitorDeflateVisitor : public IsMarkedVisitor { public: MonitorDeflateVisitor() : self_(Thread::Current()), deflate_count_(0) {} diff --git a/runtime/monitor.h b/runtime/monitor.h index c3da5636ad..1fa46826eb 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -331,6 +331,7 @@ class MonitorList { void BroadcastForNewMonitors() REQUIRES(!monitor_list_lock_); // Returns how many monitors were deflated. size_t DeflateMonitors() REQUIRES(!monitor_list_lock_) REQUIRES(Locks::mutator_lock_); + size_t Size() REQUIRES(!monitor_list_lock_); typedef std::list<Monitor*, TrackingAllocator<Monitor*, kAllocatorTagMonitorList>> Monitors; diff --git a/runtime/oat.h b/runtime/oat.h index 953b445e4e..29821a2eea 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '1', '0', '2', '\0' }; // Enabling CC + static constexpr uint8_t kOatVersion[] = { '1', '0', '3', '\0' }; // Native pc change static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 8554fa2693..8a23457470 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -33,6 +33,7 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "utils.h" +#include "vdex_file.h" namespace art { @@ -216,28 +217,38 @@ std::string OatFileAssistant::GetStatusDump() { bool oat_file_exists = false; bool odex_file_exists = false; if (oat_.Status() != kOatCannotOpen) { - // If we can open the file, neither Filename nor GetFile should return null. + // If we can open the file, Filename should not return null. CHECK(oat_.Filename() != nullptr); - CHECK(oat_.GetFile() != nullptr); oat_file_exists = true; - status << *oat_.Filename() << " [compilation_filter="; - status << CompilerFilter::NameOfFilter(oat_.GetFile()->GetCompilerFilter()); - status << ", status=" << oat_.Status(); + status << *oat_.Filename() << "[status=" << oat_.Status() << ", "; + const OatFile* file = oat_.GetFile(); + if (file == nullptr) { + // If the file is null even though the status is not kOatCannotOpen, it + // means we must have a vdex file with no corresponding oat file. In + // this case we cannot determine the compilation filter. Indicate that + // we have only the vdex file instead. + status << "vdex-only"; + } else { + status << "compilation_filter=" << CompilerFilter::NameOfFilter(file->GetCompilerFilter()); + } } if (odex_.Status() != kOatCannotOpen) { - // If we can open the file, neither Filename nor GetFile should return null. + // If we can open the file, Filename should not return null. CHECK(odex_.Filename() != nullptr); - CHECK(odex_.GetFile() != nullptr); odex_file_exists = true; if (oat_file_exists) { status << "] "; } - status << *odex_.Filename() << " [compilation_filter="; - status << CompilerFilter::NameOfFilter(odex_.GetFile()->GetCompilerFilter()); - status << ", status=" << odex_.Status(); + status << *odex_.Filename() << "[status=" << odex_.Status() << ", "; + const OatFile* file = odex_.GetFile(); + if (file == nullptr) { + status << "vdex-only"; + } else { + status << "compilation_filter=" << CompilerFilter::NameOfFilter(file->GetCompilerFilter()); + } } if (!oat_file_exists && !odex_file_exists) { @@ -303,24 +314,55 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { return oat_.Status(); } -OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) { - // Verify the ART_USE_READ_BARRIER state. - const bool is_cc = file.GetOatHeader().IsConcurrentCopying(); - constexpr bool kRuntimeIsCC = kUseReadBarrier; - if (is_cc != kRuntimeIsCC) { - return kOatCannotOpen; +bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) { + // TODO: Use GetRequiredDexChecksum to get secondary checksums as well, not + // just the primary. Because otherwise we may fail to see a secondary + // checksum failure in the case when the original (multidex) files are + // stripped but we have a newer odex file. + const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum(); + if (dex_checksum_pointer != nullptr) { + uint32_t actual_checksum = file.GetLocationChecksum(0); + if (*dex_checksum_pointer != actual_checksum) { + VLOG(oat) << "Dex checksum does not match for primary dex: " << dex_location_ + << ". Expected: " << *dex_checksum_pointer + << ", Actual: " << actual_checksum; + return false; + } } - // Verify the dex checksum. + // Verify the dex checksums for any secondary multidex files + for (uint32_t i = 1; i < file.GetHeader().GetNumberOfDexFiles(); i++) { + std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); + uint32_t expected_secondary_checksum = 0; + if (DexFile::GetChecksum(secondary_dex_location.c_str(), + &expected_secondary_checksum, + error_msg)) { + uint32_t actual_secondary_checksum = file.GetLocationChecksum(i); + if (expected_secondary_checksum != actual_secondary_checksum) { + VLOG(oat) << "Dex checksum does not match for secondary dex: " + << secondary_dex_location + << ". Expected: " << expected_secondary_checksum + << ", Actual: " << actual_secondary_checksum; + return false; + } + } else { + // If we can't get the checksum for the secondary location, we assume + // the dex checksum is up to date for this and all other secondary dex + // files. + break; + } + } + return true; +} + +bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) { // Note: GetOatDexFile will return null if the dex checksum doesn't match // what we provide, which verifies the primary dex checksum for us. - std::string error_msg; const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum(); const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile( - dex_location_.c_str(), dex_checksum_pointer, &error_msg); + dex_location_.c_str(), dex_checksum_pointer, error_msg); if (oat_dex_file == nullptr) { - LOG(ERROR) << error_msg; - return kOatDexOutOfDate; + return false; } // Verify the dex checksums for any secondary multidex files @@ -335,7 +377,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& uint32_t expected_secondary_checksum = 0; if (DexFile::GetChecksum(secondary_dex_location.c_str(), - &expected_secondary_checksum, &error_msg)) { + &expected_secondary_checksum, error_msg)) { uint32_t actual_secondary_checksum = secondary_oat_dex_file->GetDexFileLocationChecksum(); if (expected_secondary_checksum != actual_secondary_checksum) { @@ -343,7 +385,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& << secondary_dex_location << ". Expected: " << expected_secondary_checksum << ", Actual: " << actual_secondary_checksum; - return kOatDexOutOfDate; + return false; } } else { // If we can't get the checksum for the secondary location, we assume @@ -352,6 +394,35 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& break; } } + return true; +} + +OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) { + // Verify the ART_USE_READ_BARRIER state. + // TODO: Don't fully reject files due to read barrier state. If they contain + // compiled code and are otherwise okay, we should return something like + // kOatRelocationOutOfDate. If they don't contain compiled code, the read + // barrier state doesn't matter. + const bool is_cc = file.GetOatHeader().IsConcurrentCopying(); + constexpr bool kRuntimeIsCC = kUseReadBarrier; + if (is_cc != kRuntimeIsCC) { + return kOatCannotOpen; + } + + // Verify the dex checksum. + std::string error_msg; + if (kIsVdexEnabled) { + VdexFile* vdex = file.GetVdexFile(); + if (!DexChecksumUpToDate(*vdex, &error_msg)) { + LOG(ERROR) << error_msg; + return kOatDexOutOfDate; + } + } else { + if (!DexChecksumUpToDate(file, &error_msg)) { + LOG(ERROR) << error_msg; + return kOatDexOutOfDate; + } + } CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter(); @@ -777,7 +848,27 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() { status_attempted_ = true; const OatFile* file = GetFile(); if (file == nullptr) { - status_ = kOatCannotOpen; + // Check to see if there is a vdex file we can make use of. + std::string error_msg; + std::string vdex_filename = ReplaceFileExtension(filename_, "vdex"); + std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_filename, + /*writeable*/false, + /*low_4gb*/false, + &error_msg)); + if (vdex == nullptr) { + status_ = kOatCannotOpen; + VLOG(oat) << "unable to open vdex file " << vdex_filename << ": " << error_msg; + } else { + if (oat_file_assistant_->DexChecksumUpToDate(*vdex, &error_msg)) { + // The vdex file does not contain enough information to determine + // whether it is up to date with respect to the boot image, so we + // assume it is out of date. + VLOG(oat) << error_msg; + status_ = kOatBootImageOutOfDate; + } else { + status_ = kOatDexOutOfDate; + } + } } else { status_ = oat_file_assistant_->GivenOatFileStatus(*file); VLOG(oat) << file->GetLocation() << " is " << status_ diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 588a698be7..6d47ad2228 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -379,6 +379,16 @@ class OatFileAssistant { // Return info for the best oat file. OatFileInfo& GetBestInfo(); + // Returns true if the dex checksums in the given vdex file are up to date + // with respect to the dex location. If the dex checksums are not up to + // date, error_msg is updated with a message describing the problem. + bool DexChecksumUpToDate(const VdexFile& file, std::string* error_msg); + + // Returns true if the dex checksums in the given oat file are up to date + // with respect to the dex location. If the dex checksums are not up to + // date, error_msg is updated with a message describing the problem. + bool DexChecksumUpToDate(const OatFile& file, std::string* error_msg); + // Return the status for a given opened oat file with respect to the dex // location. OatStatus GivenOatFileStatus(const OatFile& file); diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 84eacde8b7..9669dabe2e 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -343,27 +343,69 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } -// Case: We have a DEX file and ODEX file for a different dex location. -// Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, OatForDifferentDex) { - // Generate an odex file for OatForDifferentDex_A.jar - std::string dex_location_a = GetScratchDir() + "/OatForDifferentDex_A.jar"; - std::string odex_location = GetOdexDir() + "/OatForDifferentDex.odex"; - Copy(GetDexSrc1(), dex_location_a); - GenerateOdexForTest(dex_location_a, odex_location, CompilerFilter::kSpeed); - - // Try to use that odex file for OatForDifferentDex.jar - std::string dex_location = GetScratchDir() + "/OatForDifferentDex.jar"; +// Case: We have a DEX file and up-to-date (ODEX) VDEX file for it, but no +// ODEX file. +TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { + // This test case is only meaningful if vdex is enabled. + if (!kIsVdexEnabled) { + return; + } + + std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar"; + std::string oat_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat"; + Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + // Generating and deleting the oat file should have the side effect of + // creating an up-to-date vdex file. + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); - EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); + + // Even though the vdex file is up to date, because we don't have the oat + // file, we can't know that the vdex depends on the boot image and is up to + // date with respect to the boot image. Instead we must assume the vdex file + // depends on the boot image and is out of date with respect to the boot + // image. + EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OdexFileStatus()); - EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); + // Make sure we don't crash in this case when we dump the status. We don't + // care what the actual dumped value is. + oat_file_assistant.GetStatusDump(); +} + +// Case: We have a DEX file and up-to-date (OAT) VDEX file for it, but no OAT +// file. +TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { + // This test case is only meaningful if vdex is enabled. + if (!kIsVdexEnabled) { + return; + } + + std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOat.jar"; + std::string oat_location; + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( + dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + // Even though the vdex file is up to date, because we don't have the oat + // file, we can't know that the vdex depends on the boot image and is up to + // date with respect to the boot image. Instead we must assume the vdex file + // depends on the boot image and is out of date with respect to the boot + // image. + EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); } // Case: We have a DEX file and speed-profile OAT file for it. @@ -486,6 +528,56 @@ TEST_F(OatFileAssistantTest, OatDexOutOfDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a DEX file and an (ODEX) VDEX file out of date with respect +// to the dex checksum, but no ODEX file. +TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { + // This test case is only meaningful if vdex is enabled. + if (!kIsVdexEnabled) { + return; + } + + std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar"; + std::string oat_location = GetOdexDir() + "/VdexDexOutOfDate.oat"; + + Copy(GetDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); + Copy(GetDexSrc2(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); + + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); +} + +// Case: We have a MultiDEX (ODEX) VDEX file where the secondary dex file is +// out of date and there is no corresponding ODEX file. +TEST_F(OatFileAssistantTest, VdexMultiDexSecondaryOutOfDate) { + // This test case is only meaningful if vdex is enabled. + if (!kIsVdexEnabled) { + return; + } + + std::string dex_location = GetScratchDir() + "/VdexMultiDexSecondaryOutOfDate.jar"; + std::string oat_location = GetOdexDir() + "/VdexMultiDexSecondaryOutOfDate.oat"; + + Copy(GetMultiDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); + Copy(GetMultiDexSrc2(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); + + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); +} + // Case: We have a DEX file and an OAT file out of date with respect to the // boot image. TEST_F(OatFileAssistantTest, OatImageOutOfDate) { @@ -1177,6 +1269,4 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { // - Dex is stripped, don't have odex. // - Oat file corrupted after status check, before reload unexecutable // because it's unrelocated and no dex2oat -// * Test unrelocated specific target compilation type can be relocated to -// make it up to date. } // namespace art diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc index 9c2378d42d..fd84426bb8 100644 --- a/runtime/oat_quick_method_header.cc +++ b/runtime/oat_quick_method_header.cc @@ -80,7 +80,7 @@ uintptr_t OatQuickMethodHeader::ToNativeQuickPc(ArtMethod* method, : code_info.GetStackMapForDexPc(dex_pc, encoding); if (stack_map.IsValid()) { return reinterpret_cast<uintptr_t>(entry_point) + - stack_map.GetNativePcOffset(encoding.stack_map_encoding); + stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA); } if (abort_on_failure) { ScopedObjectAccess soa(Thread::Current()); diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index d5c652035a..976a1e7902 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -21,12 +21,15 @@ cc_defaults { "object_tagging.cc", "OpenjdkJvmTi.cc", "ti_class.cc", + "ti_class_definition.cc", + "ti_dump.cc", "ti_field.cc", "ti_heap.cc", "ti_jni.cc", "ti_method.cc", "ti_monitor.cc", "ti_object.cc", + "ti_phase.cc", "ti_properties.cc", "ti_search.cc", "ti_stack.cc", diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index e9b7cf5b10..417d1041a8 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -49,12 +49,14 @@ #include "thread-inl.h" #include "thread_list.h" #include "ti_class.h" +#include "ti_dump.h" #include "ti_field.h" #include "ti_heap.h" #include "ti_jni.h" #include "ti_method.h" #include "ti_monitor.h" #include "ti_object.h" +#include "ti_phase.h" #include "ti_properties.h" #include "ti_redefine.h" #include "ti_search.h" @@ -198,11 +200,11 @@ class JvmtiFunctions { } static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::SetThreadLocalStorage(env, thread, data); } static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::GetThreadLocalStorage(env, thread, data_ptr); } static jvmtiError GetTopThreadGroups(jvmtiEnv* env, @@ -593,7 +595,7 @@ class JvmtiFunctions { jclass klass, jint* minor_version_ptr, jint* major_version_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetClassVersionNumbers(env, klass, minor_version_ptr, major_version_ptr); } static jvmtiError GetConstantPool(jvmtiEnv* env, @@ -1099,11 +1101,12 @@ class JvmtiFunctions { } static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) { - return ERR(NOT_IMPLEMENTED); + return PhaseUtil::GetPhase(env, phase_ptr); } static jvmtiError DisposeEnvironment(jvmtiEnv* env) { ENSURE_VALID_ENV(env); + gEventHandler.RemoveArtJvmTiEnv(ArtJvmTiEnv::AsArtJvmTiEnv(env)); delete env; return OK; } @@ -1300,8 +1303,29 @@ static jint GetEnvHandler(art::JavaVMExt* vm, /*out*/void** env, jint version) { // The plugin initialization function. This adds the jvmti environment. extern "C" bool ArtPlugin_Initialize() { art::Runtime* runtime = art::Runtime::Current(); + + if (runtime->IsStarted()) { + PhaseUtil::SetToLive(); + } else { + PhaseUtil::SetToOnLoad(); + } + PhaseUtil::Register(&gEventHandler); + ThreadUtil::Register(&gEventHandler); + ClassUtil::Register(&gEventHandler); + DumpUtil::Register(&gEventHandler); + runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); runtime->AddSystemWeakHolder(&gObjectTagTable); + + return true; +} + +extern "C" bool ArtPlugin_Deinitialize() { + PhaseUtil::Unregister(); + ThreadUtil::Unregister(); + ClassUtil::Unregister(); + DumpUtil::Unregister(); + return true; } diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 1c84d4d0ce..256c3a6cec 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -36,6 +36,7 @@ #include <jni.h> +#include "base/array_slice.h" #include "base/casts.h" #include "base/logging.h" #include "base/macros.h" @@ -125,29 +126,6 @@ static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned cha return ret; } -struct ArtClassDefinition { - jclass klass; - jobject loader; - std::string name; - jobject protection_domain; - jint dex_len; - JvmtiUniquePtr dex_data; - bool modified; - - ArtClassDefinition() = default; - ArtClassDefinition(ArtClassDefinition&& o) = default; - - void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) { - if (new_dex_data == nullptr) { - return; - } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) { - modified = true; - dex_len = new_dex_len; - dex_data = MakeJvmtiUniquePtr(env, new_dex_data); - } - } -}; - const jvmtiCapabilities kPotentialCapabilities = { .can_tag_objects = 1, .can_generate_field_modification_events = 0, diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc index f38aa869d9..d3f8001d69 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -144,6 +144,18 @@ void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { envs.push_back(env); } +void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) { + auto it = std::find(envs.begin(), envs.end(), env); + if (it != envs.end()) { + envs.erase(it); + for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal); + i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal); + ++i) { + RecalculateGlobalEventMask(static_cast<ArtJvmtiEvent>(i)); + } + } +} + static bool IsThreadControllable(ArtJvmtiEvent event) { switch (event) { case ArtJvmtiEvent::kVmInit: @@ -200,7 +212,7 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { thread.get(), object.get(), klass.get(), - byte_count); + static_cast<jlong>(byte_count)); } } diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h index 08a87659c7..8e246ded8e 100644 --- a/runtime/openjdkjvmti/events.h +++ b/runtime/openjdkjvmti/events.h @@ -141,6 +141,9 @@ class EventHandler { // enabled, yet. void RegisterArtJvmTiEnv(ArtJvmTiEnv* env); + // Remove an env. + void RemoveArtJvmTiEnv(ArtJvmTiEnv* env); + bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const { if (!EventMask::EventIsInRange(event)) { return false; diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index d1324bc13f..450f75a813 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -31,16 +31,119 @@ #include "ti_class.h" +#include <mutex> +#include <unordered_set> + #include "art_jvmti.h" +#include "base/macros.h" #include "class_table-inl.h" #include "class_linker.h" +#include "events-inl.h" +#include "handle.h" +#include "jni_env_ext-inl.h" #include "jni_internal.h" #include "runtime.h" +#include "runtime_callbacks.h" +#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" +#include "thread_list.h" namespace openjdkjvmti { +struct ClassCallback : public art::ClassLoadCallback { + void ClassLoad(art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassLoad)) { + art::Thread* thread = art::Thread::Current(); + ScopedLocalRef<jclass> jklass(thread->GetJniEnv(), + thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get())); + ScopedLocalRef<jclass> jthread( + thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer())); + { + art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); + event_handler->DispatchEvent(thread, + ArtJvmtiEvent::kClassLoad, + reinterpret_cast<JNIEnv*>(thread->GetJniEnv()), + jthread.get(), + jklass.get()); + } + AddTempClass(thread, jklass.get()); + } + } + + void ClassPrepare(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED, + art::Handle<art::mirror::Class> klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassPrepare)) { + art::Thread* thread = art::Thread::Current(); + ScopedLocalRef<jclass> jklass(thread->GetJniEnv(), + thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get())); + ScopedLocalRef<jclass> jthread( + thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer())); + art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); + event_handler->DispatchEvent(thread, + ArtJvmtiEvent::kClassPrepare, + reinterpret_cast<JNIEnv*>(thread->GetJniEnv()), + jthread.get(), + jklass.get()); + } + } + + void AddTempClass(art::Thread* self, jclass klass) { + std::unique_lock<std::mutex> mu(temp_classes_lock); + temp_classes.push_back(reinterpret_cast<jclass>(self->GetJniEnv()->NewGlobalRef(klass))); + } + + void HandleTempClass(art::Handle<art::mirror::Class> temp_klass, + art::Handle<art::mirror::Class> klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + std::unique_lock<std::mutex> mu(temp_classes_lock); + if (temp_classes.empty()) { + return; + } + + art::Thread* self = art::Thread::Current(); + for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) { + if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) { + temp_classes.erase(it); + FixupTempClass(temp_klass, klass); + } + } + } + + void FixupTempClass(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED, + art::Handle<art::mirror::Class> klass ATTRIBUTE_UNUSED) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + // TODO: Implement. + } + + // A set of all the temp classes we have handed out. We have to fix up references to these. + // For simplicity, we store the temp classes as JNI global references in a vector. Normally a + // Prepare event will closely follow, so the vector should be small. + std::mutex temp_classes_lock; + std::vector<jclass> temp_classes; + + EventHandler* event_handler = nullptr; +}; + +ClassCallback gClassCallback; + +void ClassUtil::Register(EventHandler* handler) { + gClassCallback.event_handler = handler; + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Add load callback"); + art::Runtime::Current()->GetRuntimeCallbacks()->AddClassLoadCallback(&gClassCallback); +} + +void ClassUtil::Unregister() { + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Remove thread callback"); + art::Runtime* runtime = art::Runtime::Current(); + runtime->GetRuntimeCallbacks()->RemoveClassLoadCallback(&gClassCallback); +} + jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env, jclass jklass, jint* field_count_ptr, @@ -200,7 +303,9 @@ jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env, } // TODO: Support generic signature. - *generic_ptr = nullptr; + if (generic_ptr != nullptr) { + *generic_ptr = nullptr; + } // Everything is fine, release the buffers. sig_copy.release(); @@ -417,4 +522,35 @@ jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env, return ERR(NONE); } +jvmtiError ClassUtil::GetClassVersionNumbers(jvmtiEnv* env ATTRIBUTE_UNUSED, + jclass jklass, + jint* minor_version_ptr, + jint* major_version_ptr) { + art::ScopedObjectAccess soa(art::Thread::Current()); + if (jklass == nullptr) { + return ERR(INVALID_CLASS); + } + art::ObjPtr<art::mirror::Object> jklass_obj = soa.Decode<art::mirror::Object>(jklass); + if (!jklass_obj->IsClass()) { + return ERR(INVALID_CLASS); + } + art::ObjPtr<art::mirror::Class> klass = jklass_obj->AsClass(); + if (klass->IsPrimitive() || klass->IsArrayClass()) { + return ERR(INVALID_CLASS); + } + + if (minor_version_ptr == nullptr || major_version_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + // Note: proxies will show the dex file version of java.lang.reflect.Proxy, as that is + // what their dex cache copies from. + uint32_t version = klass->GetDexFile().GetHeader().GetVersion(); + + *major_version_ptr = static_cast<jint>(version); + *minor_version_ptr = 0; + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h index 7a0fafbc83..aa2260f035 100644 --- a/runtime/openjdkjvmti/ti_class.h +++ b/runtime/openjdkjvmti/ti_class.h @@ -37,8 +37,13 @@ namespace openjdkjvmti { +class EventHandler; + class ClassUtil { public: + static void Register(EventHandler* event_handler); + static void Unregister(); + static jvmtiError GetClassFields(jvmtiEnv* env, jclass klass, jint* field_count_ptr, @@ -72,6 +77,11 @@ class ClassUtil { static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr); static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr); + + static jvmtiError GetClassVersionNumbers(jvmtiEnv* env, + jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr); }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class_definition.cc b/runtime/openjdkjvmti/ti_class_definition.cc new file mode 100644 index 0000000000..2c2a79bc58 --- /dev/null +++ b/runtime/openjdkjvmti/ti_class_definition.cc @@ -0,0 +1,55 @@ +/* Copyright (C) 2016 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_class_definition.h" + +#include "dex_file.h" +#include "handle_scope-inl.h" +#include "handle.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "thread.h" + +namespace openjdkjvmti { + +bool ArtClassDefinition::IsModified(art::Thread* self) const { + if (modified) { + return true; + } + // Check if the dex file we want to set is the same as the current one. + art::StackHandleScope<1> hs(self); + art::Handle<art::mirror::Class> h_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass())); + const art::DexFile& cur_dex_file = h_klass->GetDexFile(); + return static_cast<jint>(cur_dex_file.Size()) != dex_len || + memcmp(cur_dex_file.Begin(), dex_data.get(), dex_len) != 0; +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/runtime/openjdkjvmti/ti_class_definition.h new file mode 100644 index 0000000000..dbe5da2d63 --- /dev/null +++ b/runtime/openjdkjvmti/ti_class_definition.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_ + +#include "art_jvmti.h" + +namespace openjdkjvmti { + +// A struct that stores data needed for redefining/transforming classes. This structure should only +// even be accessed from a single thread and must not survive past the completion of the +// redefinition/retransformation function that created it. +struct ArtClassDefinition { + public: + jclass klass; + jobject loader; + std::string name; + jobject protection_domain; + jint dex_len; + JvmtiUniquePtr dex_data; + art::ArraySlice<const unsigned char> original_dex_file; + + ArtClassDefinition() = default; + ArtClassDefinition(ArtClassDefinition&& o) = default; + + void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) { + if (new_dex_data == nullptr) { + return; + } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) { + SetModified(); + dex_len = new_dex_len; + dex_data = MakeJvmtiUniquePtr(env, new_dex_data); + } + } + + void SetModified() { + modified = true; + } + + bool IsModified(art::Thread* self) const REQUIRES_SHARED(art::Locks::mutator_lock_); + + private: + bool modified; +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_ diff --git a/runtime/openjdkjvmti/ti_dump.cc b/runtime/openjdkjvmti/ti_dump.cc new file mode 100644 index 0000000000..2ee5c409f6 --- /dev/null +++ b/runtime/openjdkjvmti/ti_dump.cc @@ -0,0 +1,74 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_dump.h" + +#include <limits> + + +#include "art_jvmti.h" +#include "base/mutex.h" +#include "events-inl.h" +#include "runtime_callbacks.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "thread_list.h" + +namespace openjdkjvmti { + +struct DumpCallback : public art::RuntimeSigQuitCallback { + void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::Thread* thread = art::Thread::Current(); + art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); + event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kDataDumpRequest); + } + + EventHandler* event_handler = nullptr; +}; + +static DumpCallback gDumpCallback; + +void DumpUtil::Register(EventHandler* handler) { + gDumpCallback.event_handler = handler; + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Add sigquit callback"); + art::Runtime::Current()->GetRuntimeCallbacks()->AddRuntimeSigQuitCallback(&gDumpCallback); +} + +void DumpUtil::Unregister() { + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Remove sigquit callback"); + art::Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimeSigQuitCallback(&gDumpCallback); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_dump.h b/runtime/openjdkjvmti/ti_dump.h new file mode 100644 index 0000000000..67cb2394f8 --- /dev/null +++ b/runtime/openjdkjvmti/ti_dump.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class EventHandler; + +class DumpUtil { + public: + static void Register(EventHandler* event_handler); + static void Unregister(); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_ diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc new file mode 100644 index 0000000000..154406a5db --- /dev/null +++ b/runtime/openjdkjvmti/ti_phase.cc @@ -0,0 +1,142 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_phase.h" + +#include "art_jvmti.h" +#include "base/macros.h" +#include "events-inl.h" +#include "runtime.h" +#include "runtime_callbacks.h" +#include "ScopedLocalRef.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "thread_list.h" + +namespace openjdkjvmti { + +jvmtiPhase PhaseUtil::current_phase_ = static_cast<jvmtiPhase>(0); + +struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { + inline static JNIEnv* GetJniEnv() { + return reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv()); + } + + inline static jthread GetCurrentJThread() { + art::ScopedObjectAccess soa(art::Thread::Current()); + return soa.AddLocalReference<jthread>(soa.Self()->GetPeer()); + } + + void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { + // TODO: Events. + switch (phase) { + case RuntimePhase::kInitialAgents: + PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL; + break; + case RuntimePhase::kStart: + { + art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); + event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv()); + PhaseUtil::current_phase_ = JVMTI_PHASE_START; + } + break; + case RuntimePhase::kInit: + { + ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread()); + art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); + event_handler->DispatchEvent(nullptr, + ArtJvmtiEvent::kVmInit, + GetJniEnv(), + thread.get()); + PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; + } + break; + case RuntimePhase::kDeath: + { + art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); + event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv()); + PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD; + } + // TODO: Block events now. + break; + } + } + + EventHandler* event_handler = nullptr; +}; + +PhaseUtil::PhaseCallback gPhaseCallback; + +jvmtiError PhaseUtil::GetPhase(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiPhase* phase_ptr) { + if (phase_ptr == nullptr) { + return ERR(NULL_POINTER); + } + jvmtiPhase now = PhaseUtil::current_phase_; + DCHECK(now == JVMTI_PHASE_ONLOAD || + now == JVMTI_PHASE_PRIMORDIAL || + now == JVMTI_PHASE_START || + now == JVMTI_PHASE_LIVE || + now == JVMTI_PHASE_DEAD); + *phase_ptr = now; + return ERR(NONE); +} + +void PhaseUtil::SetToOnLoad() { + DCHECK_EQ(0u, static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD; +} + +void PhaseUtil::SetToPrimordial() { + DCHECK_EQ(static_cast<size_t>(JVMTI_PHASE_ONLOAD), static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD; +} + +void PhaseUtil::SetToLive() { + DCHECK_EQ(static_cast<size_t>(0), static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; +} + +void PhaseUtil::Register(EventHandler* handler) { + gPhaseCallback.event_handler = handler; + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Add phase callback"); + art::Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gPhaseCallback); +} + +void PhaseUtil::Unregister() { + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Remove phase callback"); + art::Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gPhaseCallback); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h new file mode 100644 index 0000000000..bd15fa6891 --- /dev/null +++ b/runtime/openjdkjvmti/ti_phase.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class EventHandler; + +class PhaseUtil { + public: + static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr); + + static void Register(EventHandler* event_handler); + static void Unregister(); + + // Move the phase from unitialized to LOAD. + static void SetToOnLoad(); + + // Move the phase from LOAD to PRIMORDIAL. + static void SetToPrimordial(); + + // Move the phase from unitialized to LIVE. + static void SetToLive(); + + struct PhaseCallback; + + private: + static jvmtiPhase current_phase_; +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 2db8a40ad4..34efc502e1 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -36,6 +36,7 @@ #include "android-base/stringprintf.h" #include "art_jvmti.h" +#include "base/array_slice.h" #include "base/logging.h" #include "dex_file.h" #include "dex_file_types.h" @@ -228,11 +229,17 @@ std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& orig return map; } -Redefiner::ClassRedefinition::ClassRedefinition(Redefiner* driver, - jclass klass, - const art::DexFile* redefined_dex_file, - const char* class_sig) : - driver_(driver), klass_(klass), dex_file_(redefined_dex_file), class_sig_(class_sig) { +Redefiner::ClassRedefinition::ClassRedefinition( + Redefiner* driver, + jclass klass, + const art::DexFile* redefined_dex_file, + const char* class_sig, + art::ArraySlice<const unsigned char> orig_dex_file) : + driver_(driver), + klass_(klass), + dex_file_(redefined_dex_file), + class_sig_(class_sig), + original_dex_file_(orig_dex_file) { GetMirrorClass()->MonitorEnter(driver_->self_); } @@ -280,7 +287,9 @@ jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env, def.dex_len = definitions[i].class_byte_count; def.dex_data = MakeJvmtiUniquePtr(env, class_bytes_copy); // We are definitely modified. - def.modified = true; + def.SetModified(); + def.original_dex_file = art::ArraySlice<const unsigned char>(definitions[i].class_bytes, + definitions[i].class_byte_count); res = Transformer::FillInTransformationData(env, definitions[i].klass, &def); if (res != OK) { return res; @@ -313,12 +322,10 @@ jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env, art::jit::ScopedJitSuspend suspend_jit; // Get shared mutator lock so we can lock all the classes. art::ScopedObjectAccess soa(self); - std::vector<Redefiner::ClassRedefinition> redefinitions; - redefinitions.reserve(definitions.size()); Redefiner r(runtime, self, error_msg); for (const ArtClassDefinition& def : definitions) { // Only try to transform classes that have been modified. - if (def.modified) { + if (def.IsModified(self)) { jvmtiError res = r.AddRedefinition(env, def); if (res != OK) { return res; @@ -371,7 +378,11 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition return ERR(INVALID_CLASS_FORMAT); } redefinitions_.push_back( - Redefiner::ClassRedefinition(this, def.klass, dex_file.release(), signature_ptr)); + Redefiner::ClassRedefinition(this, + def.klass, + dex_file.release(), + signature_ptr, + def.original_dex_file)); return OK; } @@ -509,44 +520,48 @@ void Redefiner::RecordFailure(jvmtiError result, result_ = result; } -bool Redefiner::ClassRedefinition::FinishRemainingAllocations( - /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader, - /*out*/art::MutableHandle<art::mirror::Object>* java_dex_file_obj, - /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie, - /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache) { - art::StackHandleScope<4> hs(driver_->self_); - // This shouldn't allocate - art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader())); - if (loader.Get() == nullptr) { - // TODO Better error msg. - RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); - return false; +// Allocates a ByteArray big enough to store the given number of bytes and copies them from the +// bytes pointer. +static art::mirror::ByteArray* AllocateAndFillBytes(art::Thread* self, + const uint8_t* bytes, + int32_t num_bytes) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::StackHandleScope<1> hs(self); + art::Handle<art::mirror::ByteArray> arr(hs.NewHandle( + art::mirror::ByteArray::Alloc(self, num_bytes))); + if (!arr.IsNull()) { + // Copy it in. Just skip if it's null + memcpy(arr->GetData(), bytes, num_bytes); } - art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader))); - if (dex_file_obj.Get() == nullptr) { - // TODO Better error msg. - RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); - return false; + return arr.Get(); +} + +art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() { + // If we have been specifically given a new set of bytes use that + if (original_dex_file_.size() != 0) { + return AllocateAndFillBytes(driver_->self_, + &original_dex_file_.At(0), + original_dex_file_.size()); } - art::Handle<art::mirror::LongArray> new_cookie(hs.NewHandle(AllocateDexFileCookie(dex_file_obj))); - if (new_cookie.Get() == nullptr) { - driver_->self_->AssertPendingOOMException(); - driver_->self_->ClearException(); - RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader"); - return false; + + // See if we already have one set. + art::ObjPtr<art::mirror::ClassExt> ext(GetMirrorClass()->GetExtData()); + if (!ext.IsNull()) { + art::ObjPtr<art::mirror::ByteArray> old_original_bytes(ext->GetOriginalDexFileBytes()); + if (!old_original_bytes.IsNull()) { + // We do. Use it. + return old_original_bytes.Ptr(); + } } - art::Handle<art::mirror::DexCache> dex_cache(hs.NewHandle(CreateNewDexCache(loader))); - if (dex_cache.Get() == nullptr) { - driver_->self_->AssertPendingOOMException(); - driver_->self_->ClearException(); - RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache"); - return false; + + // Copy the current dex_file + const art::DexFile& current_dex_file = GetMirrorClass()->GetDexFile(); + // TODO Handle this or make it so it cannot happen. + if (current_dex_file.NumClassDefs() != 1) { + LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses " + << "on this class might fail if no transformations are applied to it!"; } - source_class_loader->Assign(loader.Get()); - java_dex_file_obj->Assign(dex_file_obj.Get()); - new_dex_file_cookie->Assign(new_cookie.Get()); - new_dex_cache->Assign(dex_cache.Get()); - return true; + return AllocateAndFillBytes(driver_->self_, current_dex_file.Begin(), current_dex_file.Size()); } struct CallbackCtx { @@ -741,9 +756,10 @@ class RedefinitionDataHolder { kSlotNewDexFileCookie = 2, kSlotNewDexCache = 3, kSlotMirrorClass = 4, + kSlotOrigDexFile = 5, // Must be last one. - kNumSlots = 5, + kNumSlots = 6, }; // This needs to have a HandleScope passed in that is capable of creating a new Handle without @@ -784,6 +800,11 @@ class RedefinitionDataHolder { return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass)); } + art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile)); + } + void SetSourceClassLoader(jint klass_index, art::mirror::ClassLoader* loader) REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotSourceClassLoader, loader); @@ -804,6 +825,10 @@ class RedefinitionDataHolder { REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotMirrorClass, klass); } + void SetOriginalDexFileBytes(jint klass_index, art::mirror::ByteArray* bytes) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotOrigDexFile, bytes); + } int32_t Length() REQUIRES_SHARED(art::Locks::mutator_lock_) { return arr_->GetLength() / kNumSlots; @@ -829,6 +854,51 @@ class RedefinitionDataHolder { DISALLOW_COPY_AND_ASSIGN(RedefinitionDataHolder); }; +bool Redefiner::ClassRedefinition::FinishRemainingAllocations( + int32_t klass_index, /*out*/RedefinitionDataHolder* holder) { + art::StackHandleScope<2> hs(driver_->self_); + holder->SetMirrorClass(klass_index, GetMirrorClass()); + // This shouldn't allocate + art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader())); + holder->SetSourceClassLoader(klass_index, loader.Get()); + if (loader.Get() == nullptr) { + // TODO Better error msg. + RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); + return false; + } + art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader))); + holder->SetJavaDexFile(klass_index, dex_file_obj.Get()); + if (dex_file_obj.Get() == nullptr) { + // TODO Better error msg. + RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); + return false; + } + holder->SetNewDexFileCookie(klass_index, AllocateDexFileCookie(dex_file_obj)); + if (holder->GetNewDexFileCookie(klass_index) == nullptr) { + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader"); + return false; + } + holder->SetNewDexCache(klass_index, CreateNewDexCache(loader)); + if (holder->GetNewDexCache(klass_index) == nullptr) { + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache"); + return false; + } + + // We won't always need to set this field. + holder->SetOriginalDexFileBytes(klass_index, AllocateOrGetOriginalDexFileBytes()); + if (holder->GetOriginalDexFileBytes(klass_index) == nullptr) { + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate array for original dex file"); + return false; + } + return true; +} + bool Redefiner::CheckAllRedefinitionAreValid() { for (Redefiner::ClassRedefinition& redef : redefinitions_) { if (!redef.CheckRedefinitionIsValid()) { @@ -849,33 +919,11 @@ bool Redefiner::EnsureAllClassAllocationsFinished() { bool Redefiner::FinishAllRemainingAllocations(RedefinitionDataHolder& holder) { int32_t cnt = 0; - art::StackHandleScope<4> hs(self_); - art::MutableHandle<art::mirror::Object> java_dex_file(hs.NewHandle<art::mirror::Object>(nullptr)); - art::MutableHandle<art::mirror::ClassLoader> source_class_loader( - hs.NewHandle<art::mirror::ClassLoader>(nullptr)); - art::MutableHandle<art::mirror::LongArray> new_dex_file_cookie( - hs.NewHandle<art::mirror::LongArray>(nullptr)); - art::MutableHandle<art::mirror::DexCache> new_dex_cache( - hs.NewHandle<art::mirror::DexCache>(nullptr)); for (Redefiner::ClassRedefinition& redef : redefinitions_) { - // Reset the out pointers to null - source_class_loader.Assign(nullptr); - java_dex_file.Assign(nullptr); - new_dex_file_cookie.Assign(nullptr); - new_dex_cache.Assign(nullptr); // Allocate the data this redefinition requires. - if (!redef.FinishRemainingAllocations(&source_class_loader, - &java_dex_file, - &new_dex_file_cookie, - &new_dex_cache)) { + if (!redef.FinishRemainingAllocations(cnt, &holder)) { return false; } - // Save the allocated data into the holder. - holder.SetSourceClassLoader(cnt, source_class_loader.Get()); - holder.SetJavaDexFile(cnt, java_dex_file.Get()); - holder.SetNewDexFileCookie(cnt, new_dex_file_cookie.Get()); - holder.SetNewDexCache(cnt, new_dex_cache.Get()); - holder.SetMirrorClass(cnt, redef.GetMirrorClass()); cnt++; } return true; @@ -941,7 +989,7 @@ jvmtiError Redefiner::Run() { redef.UpdateJavaDexFile(holder.GetJavaDexFile(cnt), holder.GetNewDexFileCookie(cnt)); // TODO Rewrite so we don't do a stack walk for each and every class. redef.FindAndAllocateObsoleteMethods(klass); - redef.UpdateClass(klass, holder.GetNewDexCache(cnt)); + redef.UpdateClass(klass, holder.GetNewDexCache(cnt), holder.GetOriginalDexFileBytes(cnt)); cnt++; } // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have @@ -1034,8 +1082,10 @@ void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class> } // Performs updates to class that will allow us to verify it. -void Redefiner::ClassRedefinition::UpdateClass(art::ObjPtr<art::mirror::Class> mclass, - art::ObjPtr<art::mirror::DexCache> new_dex_cache) { +void Redefiner::ClassRedefinition::UpdateClass( + art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache, + art::ObjPtr<art::mirror::ByteArray> original_dex_file) { DCHECK_EQ(dex_file_->NumClassDefs(), 1u); const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0); UpdateMethods(mclass, new_dex_cache, class_def); @@ -1047,6 +1097,9 @@ void Redefiner::ClassRedefinition::UpdateClass(art::ObjPtr<art::mirror::Class> m mclass->SetDexCache(new_dex_cache.Ptr()); mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(class_def)); mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str()))); + art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData()); + CHECK(!ext.IsNull()); + ext->SetOriginalDexFileBytes(original_dex_file); } void Redefiner::ClassRedefinition::UpdateJavaDexFile( diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index f8d51ad124..29a7e1f3ac 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -38,6 +38,7 @@ #include "art_jvmti.h" #include "art_method.h" +#include "base/array_slice.h" #include "class_linker.h" #include "dex_file.h" #include "gc_root-inl.h" @@ -56,6 +57,7 @@ #include "obj_ptr.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" +#include "ti_class_definition.h" #include "thread_list.h" #include "transform.h" #include "utf.h" @@ -100,7 +102,8 @@ class Redefiner { ClassRedefinition(Redefiner* driver, jclass klass, const art::DexFile* redefined_dex_file, - const char* class_sig) + const char* class_sig, + art::ArraySlice<const unsigned char> orig_dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_); // NO_THREAD_SAFETY_ANALYSIS so we can unlock the class in the destructor. @@ -111,7 +114,8 @@ class Redefiner { : driver_(other.driver_), klass_(other.klass_), dex_file_(std::move(other.dex_file_)), - class_sig_(std::move(other.class_sig_)) { + class_sig_(std::move(other.class_sig_)), + original_dex_file_(other.original_dex_file_) { other.driver_ = nullptr; } @@ -130,15 +134,15 @@ class Redefiner { art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> j_dex_file_obj) REQUIRES_SHARED(art::Locks::mutator_lock_); + // This may return nullptr with a OOME pending if allocation fails. + art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes() + REQUIRES_SHARED(art::Locks::mutator_lock_); + void RecordFailure(jvmtiError e, const std::string& err) { driver_->RecordFailure(e, class_sig_, err); } - bool FinishRemainingAllocations( - /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader, - /*out*/art::MutableHandle<art::mirror::Object>* source_dex_file_obj, - /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie, - /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache) + bool FinishRemainingAllocations(int32_t klass_index, /*out*/RedefinitionDataHolder* holder) REQUIRES_SHARED(art::Locks::mutator_lock_); void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) @@ -191,7 +195,8 @@ class Redefiner { REQUIRES(art::Locks::mutator_lock_); void UpdateClass(art::ObjPtr<art::mirror::Class> mclass, - art::ObjPtr<art::mirror::DexCache> new_dex_cache) + art::ObjPtr<art::mirror::DexCache> new_dex_cache, + art::ObjPtr<art::mirror::ByteArray> original_dex_file) REQUIRES(art::Locks::mutator_lock_); void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); @@ -201,6 +206,7 @@ class Redefiner { jclass klass_; std::unique_ptr<const art::DexFile> dex_file_; std::string class_sig_; + art::ArraySlice<const unsigned char> original_dex_file_; }; jvmtiError result_; diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc index 970cc24f12..9f81d6ba97 100644 --- a/runtime/openjdkjvmti/ti_thread.cc +++ b/runtime/openjdkjvmti/ti_thread.cc @@ -31,10 +31,12 @@ #include "ti_thread.h" +#include "android-base/strings.h" #include "art_field.h" #include "art_jvmti.h" #include "base/logging.h" #include "base/mutex.h" +#include "events-inl.h" #include "gc/system_weak.h" #include "gc_root-inl.h" #include "jni_internal.h" @@ -43,6 +45,8 @@ #include "mirror/string.h" #include "obj_ptr.h" #include "runtime.h" +#include "runtime_callbacks.h" +#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" @@ -50,6 +54,77 @@ namespace openjdkjvmti { +struct ThreadCallback : public art::ThreadLifecycleCallback, public art::RuntimePhaseCallback { + jthread GetThreadObject(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (self->GetPeer() == nullptr) { + return nullptr; + } + return self->GetJniEnv()->AddLocalReference<jthread>(self->GetPeer()); + } + void Post(art::Thread* self, ArtJvmtiEvent type) REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK_EQ(self, art::Thread::Current()); + ScopedLocalRef<jthread> thread(self->GetJniEnv(), GetThreadObject(self)); + art::ScopedThreadSuspension sts(self, art::ThreadState::kNative); + event_handler->DispatchEvent(self, type, self->GetJniEnv(), thread.get()); + } + + void ThreadStart(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (!started) { + // Runtime isn't started. We only expect at most the signal handler or JIT threads to be + // started here. + if (art::kIsDebugBuild) { + std::string name; + self->GetThreadName(name); + if (name != "Signal Catcher" && !android::base::StartsWith(name, "Jit thread pool")) { + LOG(FATAL) << "Unexpected thread before start: " << name; + } + } + return; + } + Post(self, ArtJvmtiEvent::kThreadStart); + } + + void ThreadDeath(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + Post(self, ArtJvmtiEvent::kThreadEnd); + } + + void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (phase == RuntimePhase::kInit) { + // We moved to VMInit. Report the main thread as started (it was attached early, and must + // not be reported until Init. + started = true; + Post(art::Thread::Current(), ArtJvmtiEvent::kThreadStart); + } + } + + EventHandler* event_handler = nullptr; + bool started = false; +}; + +ThreadCallback gThreadCallback; + +void ThreadUtil::Register(EventHandler* handler) { + art::Runtime* runtime = art::Runtime::Current(); + + gThreadCallback.started = runtime->IsStarted(); + gThreadCallback.event_handler = handler; + + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Add thread callback"); + runtime->GetRuntimeCallbacks()->AddThreadLifecycleCallback(&gThreadCallback); + runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gThreadCallback); +} + +void ThreadUtil::Unregister() { + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Remove thread callback"); + art::Runtime* runtime = art::Runtime::Current(); + runtime->GetRuntimeCallbacks()->RemoveThreadLifecycleCallback(&gThreadCallback); + runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gThreadCallback); +} + jvmtiError ThreadUtil::GetCurrentThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread* thread_ptr) { art::Thread* self = art::Thread::Current(); diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h index 5aaec583da..f6f93ee91a 100644 --- a/runtime/openjdkjvmti/ti_thread.h +++ b/runtime/openjdkjvmti/ti_thread.h @@ -37,8 +37,13 @@ namespace openjdkjvmti { +class EventHandler; + class ThreadUtil { public: + static void Register(EventHandler* event_handler); + static void Unregister(); + static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr); static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr); diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc index 2809cb6926..af4fb7187a 100644 --- a/runtime/openjdkjvmti/transform.cc +++ b/runtime/openjdkjvmti/transform.cc @@ -47,6 +47,7 @@ #include "mem_map.h" #include "mirror/array.h" #include "mirror/class-inl.h" +#include "mirror/class_ext.h" #include "mirror/class_loader-inl.h" #include "mirror/string-inl.h" #include "oat_file.h" @@ -138,28 +139,41 @@ jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* return OK; } -// TODO Implement this for real once transformed dex data is actually saved. +static jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env, + const unsigned char* source, + jint len, + /*out*/unsigned char** dest) { + jvmtiError res = env->Allocate(len, dest); + if (res != OK) { + return res; + } + memcpy(reinterpret_cast<void*>(*dest), + reinterpret_cast<const void*>(source), + len); + return OK; +} + jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env, art::Handle<art::mirror::Class> klass, /*out*/jint* dex_data_len, /*out*/unsigned char** dex_data) { + art::StackHandleScope<2> hs(art::Thread::Current()); + art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData())); + if (!ext.IsNull()) { + art::Handle<art::mirror::ByteArray> orig_dex(hs.NewHandle(ext->GetOriginalDexFileBytes())); + if (!orig_dex.IsNull()) { + *dex_data_len = static_cast<jint>(orig_dex->GetLength()); + return CopyDataIntoJvmtiBuffer(env, + reinterpret_cast<const unsigned char*>(orig_dex->GetData()), + *dex_data_len, + /*out*/dex_data); + } + } // TODO De-quicken the dex file before passing it to the agents. LOG(WARNING) << "Dex file is not de-quickened yet! Quickened dex instructions might be present"; - LOG(WARNING) << "Caching of initial dex data is not yet performed! Dex data might have been " - << "transformed by agent already"; const art::DexFile& dex = klass->GetDexFile(); *dex_data_len = static_cast<jint>(dex.Size()); - unsigned char* new_dex_data = nullptr; - jvmtiError alloc_error = env->Allocate(*dex_data_len, &new_dex_data); - if (alloc_error != OK) { - return alloc_error; - } - // Copy the data into a temporary buffer. - memcpy(reinterpret_cast<void*>(new_dex_data), - reinterpret_cast<const void*>(dex.Begin()), - *dex_data_len); - *dex_data = new_dex_data; - return OK; + return CopyDataIntoJvmtiBuffer(env, dex.Begin(), *dex_data_len, /*out*/dex_data); } // TODO Move this function somewhere more appropriate. diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h index 0ff2bd1d40..65f2ae1353 100644 --- a/runtime/openjdkjvmti/transform.h +++ b/runtime/openjdkjvmti/transform.h @@ -37,6 +37,7 @@ #include <jni.h> #include "art_jvmti.h" +#include "ti_class_definition.h" #include "jvmti.h" namespace openjdkjvmti { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index a72159bc80..d1ad77c910 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -302,6 +302,9 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .IntoKey(M::Plugins) .Define("-Xfully-deoptable") .IntoKey(M::FullyDeoptable) + .Define("-XX:ThreadSuspendTimeout=_") // in ms + .WithType<MillisecondsToNanoseconds>() // store as ns + .IntoKey(M::ThreadSuspendTimeout) .Ignore({ "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa", "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_", @@ -724,6 +727,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -XX:MaxSpinsBeforeThinLockInflation=integervalue\n"); UsageMessage(stream, " -XX:LongPauseLogThreshold=integervalue\n"); UsageMessage(stream, " -XX:LongGCLogThreshold=integervalue\n"); + UsageMessage(stream, " -XX:ThreadSuspendTimeout=integervalue\n"); UsageMessage(stream, " -XX:DumpGCPerformanceOnShutdown\n"); UsageMessage(stream, " -XX:DumpJITInfoOnShutdown\n"); UsageMessage(stream, " -XX:IgnoreMaxFootprint\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 8b355c8c19..06cd7ff4b5 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -735,6 +735,13 @@ bool Runtime::Start() { GetInstructionSetString(kRuntimeISA)); } + // Send the initialized phase event. Send it before starting daemons, as otherwise + // sending thread events becomes complicated. + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit); + } + StartDaemonThreads(); { @@ -756,12 +763,6 @@ bool Runtime::Start() { 0); } - // Send the initialized phase event. - { - ScopedObjectAccess soa(self); - callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit); - } - return true; } @@ -1045,7 +1046,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { monitor_list_ = new MonitorList; monitor_pool_ = MonitorPool::Create(); - thread_list_ = new ThreadList; + thread_list_ = new ThreadList(runtime_options.GetOrDefault(Opt::ThreadSuspendTimeout)); intern_table_ = new InternTable; verify_ = runtime_options.GetOrDefault(Opt::Verify); @@ -1383,6 +1384,10 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { LOG(ERROR) << "Unable to load an agent: " << err; } } + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInitialAgents); + } VLOG(startup) << "Runtime::Init exiting"; @@ -1395,7 +1400,7 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so"; // Is the plugin already loaded? - for (Plugin p : *plugins) { + for (const Plugin& p : *plugins) { if (p.GetLibrary() == plugin_name) { return true; } diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index 7b15a4f1b5..25324b52d1 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -67,6 +67,36 @@ void RuntimeCallbacks::ClassLoad(Handle<mirror::Class> klass) { } } +void RuntimeCallbacks::ClassPreDefine(const char* descriptor, + Handle<mirror::Class> temp_class, + Handle<mirror::ClassLoader> loader, + const DexFile& initial_dex_file, + const DexFile::ClassDef& initial_class_def, + /*out*/DexFile const** final_dex_file, + /*out*/DexFile::ClassDef const** final_class_def) { + DexFile const* current_dex_file = &initial_dex_file; + DexFile::ClassDef const* current_class_def = &initial_class_def; + for (ClassLoadCallback* cb : class_callbacks_) { + DexFile const* new_dex_file = nullptr; + DexFile::ClassDef const* new_class_def = nullptr; + cb->ClassPreDefine(descriptor, + temp_class, + loader, + *current_dex_file, + *current_class_def, + &new_dex_file, + &new_class_def); + if ((new_dex_file != nullptr && new_dex_file != current_dex_file) || + (new_class_def != nullptr && new_class_def != current_class_def)) { + DCHECK(new_dex_file != nullptr && new_class_def != nullptr); + current_dex_file = new_dex_file; + current_class_def = new_class_def; + } + } + *final_dex_file = current_dex_file; + *final_class_def = current_class_def; +} + void RuntimeCallbacks::ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass) { for (ClassLoadCallback* cb : class_callbacks_) { cb->ClassPrepare(temp_klass, klass); diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index 6344c69b1f..d321254e17 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -21,12 +21,14 @@ #include "base/macros.h" #include "base/mutex.h" +#include "dex_file.h" #include "handle.h" namespace art { namespace mirror { class Class; +class ClassLoader; } // namespace mirror class ClassLoadCallback; @@ -58,9 +60,10 @@ class RuntimeSigQuitCallback { class RuntimePhaseCallback { public: enum RuntimePhase { - kStart, // The runtime is started. - kInit, // The runtime is initialized (and will run user code soon). - kDeath, // The runtime just died. + kInitialAgents, // Initial agent loading is done. + kStart, // The runtime is started. + kInit, // The runtime is initialized (and will run user code soon). + kDeath, // The runtime just died. }; virtual ~RuntimePhaseCallback() {} @@ -98,6 +101,15 @@ class RuntimeCallbacks { void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase) REQUIRES_SHARED(Locks::mutator_lock_); + void ClassPreDefine(const char* descriptor, + Handle<mirror::Class> temp_class, + Handle<mirror::ClassLoader> loader, + const DexFile& initial_dex_file, + const DexFile::ClassDef& initial_class_def, + /*out*/DexFile const** final_dex_file, + /*out*/DexFile::ClassDef const** final_class_def) + REQUIRES_SHARED(Locks::mutator_lock_); + private: std::vector<ThreadLifecycleCallback*> thread_callbacks_ GUARDED_BY(Locks::mutator_lock_); diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index c379b5c267..66eb2ec3b1 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -249,6 +249,21 @@ class ClassLoadCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { } struct Callback : public ClassLoadCallback { + virtual void ClassPreDefine(const char* descriptor, + Handle<mirror::Class> klass ATTRIBUTE_UNUSED, + Handle<mirror::ClassLoader> class_loader ATTRIBUTE_UNUSED, + const DexFile& initial_dex_file, + const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED, + /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED, + /*out*/DexFile::ClassDef const** final_dex_cache ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + std::string location(initial_dex_file.GetLocation()); + std::string event = + std::string("PreDefine:") + descriptor + " <" + + location.substr(location.rfind("/") + 1, location.size()) + ">"; + data.push_back(event); + } + void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { std::string tmp; std::string event = std::string("Load:") + klass->GetDescriptor(&tmp); @@ -281,14 +296,21 @@ TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) { hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader))); ASSERT_TRUE(h_Y.Get() != nullptr); - bool expect1 = Expect({ "Load:LX;", "Prepare:LX;[LX;]", "Load:LY;", "Prepare:LY;[LY;]" }); + bool expect1 = Expect({ "PreDefine:LY; <art-gtest-XandY.jar>", + "PreDefine:LX; <art-gtest-XandY.jar>", + "Load:LX;", + "Prepare:LX;[LX;]", + "Load:LY;", + "Prepare:LY;[LY;]" }); EXPECT_TRUE(expect1); cb_.data.clear(); ASSERT_TRUE(class_linker_->EnsureInitialized(Thread::Current(), h_Y, true, true)); - bool expect2 = Expect({ "Load:LY$Z;", "Prepare:LY$Z;[LY$Z;]" }); + bool expect2 = Expect({ "PreDefine:LY$Z; <art-gtest-XandY.jar>", + "Load:LY$Z;", + "Prepare:LY$Z;[LY$Z;]" }); EXPECT_TRUE(expect2); } @@ -351,8 +373,13 @@ class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { struct Callback : public RuntimePhaseCallback { void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase p) OVERRIDE { - if (p == RuntimePhaseCallback::RuntimePhase::kStart) { - if (init_seen > 0) { + if (p == RuntimePhaseCallback::RuntimePhase::kInitialAgents) { + if (start_seen > 0 || init_seen > 0 || death_seen > 0) { + LOG(FATAL) << "Unexpected order"; + } + ++initial_agents_seen; + } else if (p == RuntimePhaseCallback::RuntimePhase::kStart) { + if (init_seen > 0 || death_seen > 0) { LOG(FATAL) << "Init seen before start."; } ++start_seen; @@ -365,6 +392,7 @@ class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { } } + size_t initial_agents_seen = 0; size_t start_seen = 0; size_t init_seen = 0; size_t death_seen = 0; @@ -374,6 +402,7 @@ class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { }; TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) { + ASSERT_EQ(0u, cb_.initial_agents_seen); ASSERT_EQ(0u, cb_.start_seen); ASSERT_EQ(0u, cb_.init_seen); ASSERT_EQ(0u, cb_.death_seen); @@ -386,6 +415,7 @@ TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) { ASSERT_TRUE(started); } + ASSERT_EQ(0u, cb_.initial_agents_seen); ASSERT_EQ(1u, cb_.start_seen); ASSERT_EQ(1u, cb_.init_seen); ASSERT_EQ(0u, cb_.death_seen); @@ -393,6 +423,7 @@ TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) { // Delete the runtime. runtime_.reset(); + ASSERT_EQ(0u, cb_.initial_agents_seen); ASSERT_EQ(1u, cb_.start_seen); ASSERT_EQ(1u, cb_.init_seen); ASSERT_EQ(1u, cb_.death_seen); diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc index e75481c78a..aa147198fc 100644 --- a/runtime/runtime_options.cc +++ b/runtime/runtime_options.cc @@ -21,6 +21,7 @@ #include "gc/heap.h" #include "monitor.h" #include "runtime.h" +#include "thread_list.h" #include "trace.h" #include "utils.h" #include "debugger.h" diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index ecabf9adc5..749a36e3b3 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -60,6 +60,8 @@ RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ LongPauseLogThreshold, gc::Heap::kDefaultLongPauseLogThreshold) RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ LongGCLogThreshold, gc::Heap::kDefaultLongGCLogThreshold) +RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ + ThreadSuspendTimeout, ThreadList::kDefaultThreadSuspendTimeout) RUNTIME_OPTIONS_KEY (Unit, DumpGCPerformanceOnShutdown) RUNTIME_OPTIONS_KEY (Unit, DumpJITInfoOnShutdown) RUNTIME_OPTIONS_KEY (Unit, IgnoreMaxFootprint) diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 9ebf9a7bdd..690b069c8e 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -116,7 +116,8 @@ void InlineInfoEncoding::Dump(VariableIndentationOutputStream* vios) const { void CodeInfo::Dump(VariableIndentationOutputStream* vios, uint32_t code_offset, uint16_t number_of_dex_registers, - bool dump_stack_maps) const { + bool dump_stack_maps, + InstructionSet instruction_set) const { CodeInfoEncoding encoding = ExtractEncoding(); size_t number_of_stack_maps = GetNumberOfStackMaps(encoding); vios->Stream() @@ -139,6 +140,7 @@ void CodeInfo::Dump(VariableIndentationOutputStream* vios, encoding, code_offset, number_of_dex_registers, + instruction_set, " " + std::to_string(i)); } } @@ -188,14 +190,17 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, const CodeInfoEncoding& encoding, uint32_t code_offset, uint16_t number_of_dex_registers, + InstructionSet instruction_set, const std::string& header_suffix) const { StackMapEncoding stack_map_encoding = encoding.stack_map_encoding; + const uint32_t pc_offset = GetNativePcOffset(stack_map_encoding, instruction_set); vios->Stream() << "StackMap" << header_suffix << std::hex - << " [native_pc=0x" << code_offset + GetNativePcOffset(stack_map_encoding) << "]" + << " [native_pc=0x" << code_offset + pc_offset << "]" + << " [entry_size=0x" << encoding.stack_map_size_in_bytes << "]" << " (dex_pc=0x" << GetDexPc(stack_map_encoding) - << ", native_pc_offset=0x" << GetNativePcOffset(stack_map_encoding) + << ", native_pc_offset=0x" << pc_offset << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding) << ", inline_info_offset=0x" << GetInlineDescriptorOffset(stack_map_encoding) << ", register_mask=0x" << GetRegisterMask(stack_map_encoding) diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 13886f2109..cd9a3f04cf 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_STACK_MAP_H_ #define ART_RUNTIME_STACK_MAP_H_ +#include "arch/code_offset.h" #include "base/bit_vector.h" #include "base/bit_utils.h" #include "dex_file.h" @@ -805,12 +806,16 @@ class StackMap { encoding.GetDexPcEncoding().Store(region_, dex_pc); } - ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding) const { - return encoding.GetNativePcEncoding().Load(region_); + ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding, + InstructionSet instruction_set) const { + CodeOffset offset( + CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_))); + return offset.Uint32Value(instruction_set); } - ALWAYS_INLINE void SetNativePcOffset(const StackMapEncoding& encoding, uint32_t native_pc_offset) { - encoding.GetNativePcEncoding().Store(region_, native_pc_offset); + ALWAYS_INLINE void SetNativePcCodeOffset(const StackMapEncoding& encoding, + CodeOffset native_pc_offset) { + encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue()); } ALWAYS_INLINE uint32_t GetDexRegisterMapOffset(const StackMapEncoding& encoding) const { @@ -866,6 +871,7 @@ class StackMap { const CodeInfoEncoding& encoding, uint32_t code_offset, uint16_t number_of_dex_registers, + InstructionSet instruction_set, const std::string& header_suffix = "") const; // Special (invalid) offset for the DexRegisterMapOffset field meaning @@ -1176,6 +1182,17 @@ class CodeInfo { } } + size_t GetDexRegisterMapsSize(const CodeInfoEncoding& encoding, + uint32_t number_of_dex_registers) const { + size_t total = 0; + for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) { + StackMap stack_map = GetStackMapAt(i, encoding); + DexRegisterMap map(GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers)); + total += map.Size(); + } + return total; + } + // Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`. DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, InlineInfo inline_info, @@ -1234,15 +1251,16 @@ class CodeInfo { if (stack_map.GetDexPc(stack_map_encoding) == dex_pc) { StackMap other = GetStackMapAt(i + 1, encoding); if (other.GetDexPc(stack_map_encoding) == dex_pc && - other.GetNativePcOffset(stack_map_encoding) == - stack_map.GetNativePcOffset(stack_map_encoding)) { + other.GetNativePcOffset(stack_map_encoding, kRuntimeISA) == + stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA)) { DCHECK_EQ(other.GetDexRegisterMapOffset(stack_map_encoding), stack_map.GetDexRegisterMapOffset(stack_map_encoding)); DCHECK(!stack_map.HasInlineInfo(stack_map_encoding)); if (i < e - 2) { // Make sure there are not three identical stack maps following each other. - DCHECK_NE(stack_map.GetNativePcOffset(stack_map_encoding), - GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding)); + DCHECK_NE( + stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA), + GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding, kRuntimeISA)); } return stack_map; } @@ -1258,7 +1276,8 @@ class CodeInfo { // we could do binary search. for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) { StackMap stack_map = GetStackMapAt(i, encoding); - if (stack_map.GetNativePcOffset(encoding.stack_map_encoding) == native_pc_offset) { + if (stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA) == + native_pc_offset) { return stack_map; } } @@ -1273,7 +1292,8 @@ class CodeInfo { void Dump(VariableIndentationOutputStream* vios, uint32_t code_offset, uint16_t number_of_dex_registers, - bool dump_stack_maps) const; + bool dump_stack_maps, + InstructionSet instruction_set) const; // Check that the code info has valid stack map and abort if it does not. void AssertValidStackMap(const CodeInfoEncoding& encoding) const { diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index c5c7e2cc16..01c940e9df 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -57,7 +57,6 @@ namespace art { using android::base::StringPrintf; static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5); -static constexpr uint64_t kThreadSuspendTimeoutMs = 30 * 1000; // 30s. // Use 0 since we want to yield to prevent blocking for an unpredictable amount of time. static constexpr useconds_t kThreadSuspendInitialSleepUs = 0; static constexpr useconds_t kThreadSuspendMaxYieldUs = 3000; @@ -68,12 +67,13 @@ static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000; // Turned off again. b/29248079 static constexpr bool kDumpUnattachedThreadNativeStackForSigQuit = false; -ThreadList::ThreadList() +ThreadList::ThreadList(uint64_t thread_suspend_timeout_ns) : suspend_all_count_(0), debug_suspend_all_count_(0), unregistering_count_(0), suspend_all_historam_("suspend all histogram", 16, 64), long_suspend_(false), + thread_suspend_timeout_ns_(thread_suspend_timeout_ns), empty_checkpoint_barrier_(new Barrier(0)) { CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1, 0U))); } @@ -554,12 +554,14 @@ void ThreadList::SuspendAll(const char* cause, bool long_suspend) { // Make sure this thread grabs exclusive access to the mutator lock and its protected data. #if HAVE_TIMED_RWLOCK while (true) { - if (Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) { + if (Locks::mutator_lock_->ExclusiveLockWithTimeout(self, + NsToMs(thread_suspend_timeout_ns_), + 0)) { break; } else if (!long_suspend_) { // Reading long_suspend without the mutator lock is slightly racy, in some rare cases, this // could result in a thread suspend timeout. - // Timeout if we wait more than kThreadSuspendTimeoutMs seconds. + // Timeout if we wait more than thread_suspend_timeout_ns_ nanoseconds. UnsafeLogFatalForThreadSuspendAllTimeout(); } } @@ -653,7 +655,7 @@ void ThreadList::SuspendAllInternal(Thread* self, // is done with a timeout so that we can detect problems. #if ART_USE_FUTEXES timespec wait_timeout; - InitTimeSpec(false, CLOCK_MONOTONIC, kIsDebugBuild ? 50000 : 10000, 0, &wait_timeout); + InitTimeSpec(false, CLOCK_MONOTONIC, NsToMs(thread_suspend_timeout_ns_), 0, &wait_timeout); #endif const uint64_t start_time = NanoTime(); while (true) { @@ -863,7 +865,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, return thread; } const uint64_t total_delay = NanoTime() - start_time; - if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) { + if (total_delay >= thread_suspend_timeout_ns_) { ThreadSuspendByPeerWarning(self, ::android::base::FATAL, "Thread suspension timed out", @@ -969,7 +971,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, return thread; } const uint64_t total_delay = NanoTime() - start_time; - if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) { + if (total_delay >= thread_suspend_timeout_ns_) { ThreadSuspendByThreadIdWarning(::android::base::WARNING, "Thread suspension timed out", thread_id); diff --git a/runtime/thread_list.h b/runtime/thread_list.h index 658db00ec3..b60fca1fdc 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -20,6 +20,7 @@ #include "barrier.h" #include "base/histogram.h" #include "base/mutex.h" +#include "base/time_utils.h" #include "base/value_object.h" #include "gc_root.h" #include "jni.h" @@ -41,11 +42,12 @@ class TimingLogger; class ThreadList { public: - static const uint32_t kMaxThreadId = 0xFFFF; - static const uint32_t kInvalidThreadId = 0; - static const uint32_t kMainThreadId = 1; + static constexpr uint32_t kMaxThreadId = 0xFFFF; + static constexpr uint32_t kInvalidThreadId = 0; + static constexpr uint32_t kMainThreadId = 1; + static constexpr uint64_t kDefaultThreadSuspendTimeout = MsToNs(kIsDebugBuild ? 50000 : 10000); - explicit ThreadList(); + explicit ThreadList(uint64_t thread_suspend_timeout_ns); ~ThreadList(); void DumpForSigQuit(std::ostream& os) @@ -219,6 +221,9 @@ class ThreadList { // Whether or not the current thread suspension is long. bool long_suspend_; + // Thread suspension timeout in nanoseconds. + const uint64_t thread_suspend_timeout_ns_; + std::unique_ptr<Barrier> empty_checkpoint_barrier_; friend class Thread; diff --git a/runtime/trace.cc b/runtime/trace.cc index 9d9360e9cb..2add955f8e 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -54,6 +54,7 @@ static constexpr size_t TraceActionBits = MinimumBitsToStore( static_cast<size_t>(kTraceMethodActionMask)); static constexpr uint8_t kOpNewMethod = 1U; static constexpr uint8_t kOpNewThread = 2U; +static constexpr uint8_t kOpTraceSummary = 3U; class BuildStackTraceVisitor : public StackVisitor { public: @@ -700,20 +701,19 @@ void Trace::FinishTracing() { std::string header(os.str()); if (trace_output_mode_ == TraceOutputMode::kStreaming) { - File file(streaming_file_name_ + ".sec", O_CREAT | O_WRONLY, true); - if (!file.IsOpened()) { - LOG(WARNING) << "Could not open secondary trace file!"; - return; - } - if (!file.WriteFully(header.c_str(), header.length())) { - file.Erase(); - std::string detail(StringPrintf("Trace data write failed: %s", strerror(errno))); - PLOG(ERROR) << detail; - ThrowRuntimeException("%s", detail.c_str()); - } - if (file.FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Could not write secondary file"; - } + MutexLock mu(Thread::Current(), *streaming_lock_); // To serialize writing. + // Write a special token to mark the end of trace records and the start of + // trace summary. + uint8_t buf[7]; + Append2LE(buf, 0); + buf[2] = kOpTraceSummary; + Append4LE(buf + 3, static_cast<uint32_t>(header.length())); + WriteToBuf(buf, sizeof(buf)); + // Write the trace summary. The summary is identical to the file header when + // the output mode is not streaming (except for methods). + WriteToBuf(reinterpret_cast<const uint8_t*>(header.c_str()), header.length()); + // Flush the buffer, which may include some trace records before the summary. + FlushBuf(); } else { if (trace_file_.get() == nullptr) { iovec iov[2]; @@ -894,6 +894,14 @@ void Trace::WriteToBuf(const uint8_t* src, size_t src_size) { memcpy(buf_.get() + old_offset, src, src_size); } +void Trace::FlushBuf() { + int32_t offset = cur_offset_.LoadRelaxed(); + if (!trace_file_->WriteFully(buf_.get(), offset)) { + PLOG(WARNING) << "Failed flush the remaining data in streaming."; + } + cur_offset_.StoreRelease(0); +} + void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, instrumentation::Instrumentation::InstrumentationEvent event, uint32_t thread_clock_diff, uint32_t wall_clock_diff) { diff --git a/runtime/trace.h b/runtime/trace.h index 824b15003a..485e9a133a 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -202,7 +202,8 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // This causes the negative annotations to incorrectly have a false positive. TODO: Figure out // how to annotate this. NO_THREAD_SAFETY_ANALYSIS; - void FinishTracing() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_); + void FinishTracing() + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_); void ReadClocks(Thread* thread, uint32_t* thread_clock_diff, uint32_t* wall_clock_diff); @@ -229,6 +230,9 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // annotation. void WriteToBuf(const uint8_t* src, size_t src_size) REQUIRES(streaming_lock_); + // Flush the main buffer to file. Used for streaming. Exposed here for lock annotation. + void FlushBuf() + REQUIRES(streaming_lock_); uint32_t EncodeTraceMethod(ArtMethod* method) REQUIRES(!*unique_methods_lock_); uint32_t EncodeTraceMethodAndAction(ArtMethod* method, TraceAction action) diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 15cc566cc6..113160785c 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -963,20 +963,25 @@ bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader, // Check recorded fields are resolved the same way, have the same recorded class, // and have the same recorded flags. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - StackHandleScope<1> hs(self); - Handle<mirror::DexCache> dex_cache( - hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false))); for (const auto& entry : fields) { - ArtField* field = class_linker->ResolveFieldJLS( - dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader); - - if (field == nullptr) { - DCHECK(self->IsExceptionPending()); - self->ClearException(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex()); + StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_)); + StringPiece type(dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_)); + // Only use field_id.class_idx_ when the entry is unresolved, which is rare. + // Otherwise, we might end up resolving an application class, which is expensive. + std::string expected_decl_klass = entry.IsResolved() + ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex()) + : dex_file.StringByTypeIdx(field_id.class_idx_); + mirror::Class* cls = FindClassAndClearException( + class_linker, self, expected_decl_klass.c_str(), class_loader); + if (cls == nullptr) { + LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass; + return false; } + DCHECK(cls->IsResolved()); + ArtField* field = mirror::Class::FindField(self, cls, name, type); if (entry.IsResolved()) { - std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex()); std::string temp; if (field == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve field " @@ -1025,11 +1030,16 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, const char* name = dex_file.GetMethodName(method_id); const Signature signature = dex_file.GetMethodSignature(method_id); - const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id); - - mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader); + // Only use method_id.class_idx_ when the entry is unresolved, which is rare. + // Otherwise, we might end up resolving an application class, which is expensive. + std::string expected_decl_klass = entry.IsResolved() + ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex()) + : dex_file.StringByTypeIdx(method_id.class_idx_); + + mirror::Class* cls = FindClassAndClearException( + class_linker, self, expected_decl_klass.c_str(), class_loader); if (cls == nullptr) { - LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor; + LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass; return false; } DCHECK(cls->IsResolved()); @@ -1045,7 +1055,6 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, if (entry.IsResolved()) { std::string temp; - std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex()); if (method == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve " << kind diff --git a/test/596-monitor-inflation/expected.txt b/test/596-monitor-inflation/expected.txt new file mode 100644 index 0000000000..2add6966a0 --- /dev/null +++ b/test/596-monitor-inflation/expected.txt @@ -0,0 +1,6 @@ +JNI_OnLoad called +Monitor list grew by at least 4000 monitors +Monitor list shrank correctly +Finished first check +Finished second check +Total checks: 10000 diff --git a/test/596-monitor-inflation/info.txt b/test/596-monitor-inflation/info.txt new file mode 100644 index 0000000000..81dedb60aa --- /dev/null +++ b/test/596-monitor-inflation/info.txt @@ -0,0 +1,5 @@ +A simple test that forces many monitors to be inflated, while checking +that hashcodes are consistently maintained. + +This allocates more monitors and hence may exercise the monitor pool +differently, and with more context, than the monitor_pool_test gtest. diff --git a/test/596-monitor-inflation/monitor_inflation.cc b/test/596-monitor-inflation/monitor_inflation.cc new file mode 100644 index 0000000000..fb4275b711 --- /dev/null +++ b/test/596-monitor-inflation/monitor_inflation.cc @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gc/heap.h" +#include "jni.h" +#include "monitor.h" +#include "runtime.h" +#include "thread-inl.h" + +namespace art { +namespace { + +extern "C" JNIEXPORT void JNICALL Java_Main_trim(JNIEnv*, jclass) { + Runtime::Current()->GetHeap()->Trim(Thread::Current()); +} + +extern "C" JNIEXPORT jint JNICALL Java_Main_monitorListSize(JNIEnv*, jclass) { + return Runtime::Current()->GetMonitorList()->Size(); +} + +} // namespace +} // namespace art diff --git a/test/596-monitor-inflation/src/Main.java b/test/596-monitor-inflation/src/Main.java new file mode 100644 index 0000000000..d97c7667e9 --- /dev/null +++ b/test/596-monitor-inflation/src/Main.java @@ -0,0 +1,79 @@ +/* + * 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.IdentityHashMap; +import dalvik.system.VMRuntime; + +public class Main { + public static void main(String[] args) { + System.loadLibrary(args[0]); + int initialSize = monitorListSize(); + IdentityHashMap<Object, Integer> all = new IdentityHashMap(); + for (int i = 0; i < 5000; ++i) { + Object obj = new Object(); + synchronized(obj) { + // Should force inflation. + all.put(obj, obj.hashCode()); + } + } + // Since monitor deflation is delayed significantly, we believe that even with an intervening + // GC, monitors should remain inflated. We allow some slop for unrelated concurrent runtime + // actions. + int inflatedSize = monitorListSize(); + if (inflatedSize >= initialSize + 4000) { + System.out.println("Monitor list grew by at least 4000 monitors"); + } else { + System.out.println("Monitor list did not grow as expected"); + } + // Encourage monitor deflation. + // trim() (Heap::Trim()) deflates only in JANK_IMPERCEPTIBLE state. + // Some of this mirrors code in ActivityThread.java. + final int DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE = 0; + final int DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1; + VMRuntime.getRuntime().updateProcessState(DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE); + System.gc(); + System.runFinalization(); + trim(); + VMRuntime.getRuntime().updateProcessState(DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE); + int finalSize = monitorListSize(); + if (finalSize > initialSize + 1000) { + System.out.println("Monitor list failed to shrink properly"); + } else { + System.out.println("Monitor list shrank correctly"); + } + int j = 0; + for (Object obj: all.keySet()) { + ++j; + if (obj.hashCode() != all.get(obj)) { + throw new AssertionError("Failed hashcode test!"); + } + } + System.out.println("Finished first check"); + for (Object obj: all.keySet()) { + ++j; + synchronized(obj) { + if (obj.hashCode() != all.get(obj)) { + throw new AssertionError("Failed hashcode test!"); + } + } + } + System.out.println("Finished second check"); + System.out.println("Total checks: " + j); + } + + private static native void trim(); + + private static native int monitorListSize(); +} diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 052fb9ac13..0b17656303 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -28,6 +28,46 @@ namespace art { namespace Test901HelloTi { +static void EnableEvent(jvmtiEnv* env, jvmtiEvent evt) { + jvmtiError error = env->SetEventNotificationMode(JVMTI_ENABLE, evt, nullptr); + if (error != JVMTI_ERROR_NONE) { + printf("Failed to enable event"); + } +} + +static void JNICALL VMStartCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED, + JNIEnv* jni_env ATTRIBUTE_UNUSED) { + printf("VMStart\n"); +} + +static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env ATTRIBUTE_UNUSED, + JNIEnv* jni_env ATTRIBUTE_UNUSED, + jthread thread ATTRIBUTE_UNUSED) { + printf("VMInit\n"); +} + +static void JNICALL VMDeatchCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED, + JNIEnv* jni_env ATTRIBUTE_UNUSED) { + printf("VMDeath\n"); +} + + +static void InstallVMEvents(jvmtiEnv* env) { + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); + callbacks.VMStart = VMStartCallback; + callbacks.VMInit = VMInitCallback; + callbacks.VMDeath = VMDeatchCallback; + jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks)); + if (ret != JVMTI_ERROR_NONE) { + printf("Failed to install callbacks"); + } + + EnableEvent(env, JVMTI_EVENT_VM_START); + EnableEvent(env, JVMTI_EVENT_VM_INIT); + EnableEvent(env, JVMTI_EVENT_VM_DEATH); +} + jint OnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { @@ -72,6 +112,10 @@ jint OnLoad(JavaVM* vm, printf("Unexpected version number!\n"); return -1; } + + InstallVMEvents(env); + InstallVMEvents(env2); + CHECK_CALL_SUCCESS(env->DisposeEnvironment()); CHECK_CALL_SUCCESS(env2->DisposeEnvironment()); #undef CHECK_CALL_SUCCESS @@ -82,6 +126,19 @@ jint OnLoad(JavaVM* vm, } SetAllCapabilities(jvmti_env); + jvmtiPhase current_phase; + jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase); + if (phase_result != JVMTI_ERROR_NONE) { + printf("Could not get phase"); + return 1; + } + if (current_phase != JVMTI_PHASE_ONLOAD) { + printf("Wrong phase"); + return 1; + } + + InstallVMEvents(jvmti_env); + return JNI_OK; } @@ -92,5 +149,15 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setVerboseFlag( JvmtiErrorToException(env, result); } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jvmtiPhase current_phase; + jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase); + if (JvmtiErrorToException(env, phase_result)) { + return JNI_FALSE; + } + return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE; +} + } // namespace Test901HelloTi } // namespace art diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt index 2aee99b25a..c4b24cba90 100644 --- a/test/901-hello-ti-agent/expected.txt +++ b/test/901-hello-ti-agent/expected.txt @@ -1,8 +1,12 @@ Loaded Agent for test 901-hello-ti-agent +VMStart +VMInit Hello, world! +Agent in live phase. 0 1 2 4 8 JVMTI_ERROR_ILLEGAL_ARGUMENT +VMDeath diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java index 775e5c2e0c..4d62ed3f5d 100644 --- a/test/901-hello-ti-agent/src/Main.java +++ b/test/901-hello-ti-agent/src/Main.java @@ -16,10 +16,12 @@ public class Main { public static void main(String[] args) { - System.loadLibrary(args[1]); - System.out.println("Hello, world!"); + if (checkLivePhase()) { + System.out.println("Agent in live phase."); + } + set(0); // OTHER set(1); // GC set(2); // CLASS @@ -37,5 +39,6 @@ public class Main { } } + private static native boolean checkLivePhase(); private static native void setVerboseFlag(int flag, boolean value); } diff --git a/test/902-hello-transformation/src/Main.java b/test/902-hello-transformation/src/Main.java index ec4711954a..471c82ba28 100644 --- a/test/902-hello-transformation/src/Main.java +++ b/test/902-hello-transformation/src/Main.java @@ -49,7 +49,6 @@ public class Main { "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); public static void main(String[] args) { - System.loadLibrary(args[1]); doTest(new Transform()); } diff --git a/test/903-hello-tagging/src/Main.java b/test/903-hello-tagging/src/Main.java index a8aedb410b..2f0365a921 100644 --- a/test/903-hello-tagging/src/Main.java +++ b/test/903-hello-tagging/src/Main.java @@ -20,7 +20,6 @@ import java.util.Arrays; public class Main { public static void main(String[] args) { - System.loadLibrary(args[1]); doTest(); testGetTaggedObjects(); } diff --git a/test/904-object-allocation/src/Main.java b/test/904-object-allocation/src/Main.java index fc8a112e49..df59179cc8 100644 --- a/test/904-object-allocation/src/Main.java +++ b/test/904-object-allocation/src/Main.java @@ -18,8 +18,6 @@ import java.util.ArrayList; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - // Use a list to ensure objects must be allocated. ArrayList<Object> l = new ArrayList<>(100); diff --git a/test/905-object-free/src/Main.java b/test/905-object-free/src/Main.java index 16dec5d3e1..e41e378c19 100644 --- a/test/905-object-free/src/Main.java +++ b/test/905-object-free/src/Main.java @@ -19,8 +19,6 @@ import java.util.Arrays; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/906-iterate-heap/src/Main.java b/test/906-iterate-heap/src/Main.java index 544a3656b2..cab27be4d2 100644 --- a/test/906-iterate-heap/src/Main.java +++ b/test/906-iterate-heap/src/Main.java @@ -19,8 +19,6 @@ import java.util.Collections; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/907-get-loaded-classes/src/Main.java b/test/907-get-loaded-classes/src/Main.java index 468d037a52..370185a4d9 100644 --- a/test/907-get-loaded-classes/src/Main.java +++ b/test/907-get-loaded-classes/src/Main.java @@ -20,8 +20,6 @@ import java.util.HashSet; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/908-gc-start-finish/src/Main.java b/test/908-gc-start-finish/src/Main.java index 2be0eea975..05388c917a 100644 --- a/test/908-gc-start-finish/src/Main.java +++ b/test/908-gc-start-finish/src/Main.java @@ -18,8 +18,6 @@ import java.util.ArrayList; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/910-methods/src/Main.java b/test/910-methods/src/Main.java index bf25a0d028..932a1ea414 100644 --- a/test/910-methods/src/Main.java +++ b/test/910-methods/src/Main.java @@ -20,8 +20,6 @@ import java.util.Arrays; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt index dad08c9f97..2687f85b09 100644 --- a/test/911-get-stack-trace/expected.txt +++ b/test/911-get-stack-trace/expected.txt @@ -22,7 +22,7 @@ From top bar (IIILControlData;)J 0 24 foo (IIILControlData;)I 0 19 doTest ()V 38 23 - main ([Ljava/lang/String;)V 6 21 + main ([Ljava/lang/String;)V 3 21 --------- print (Ljava/lang/Thread;II)V 0 34 printOrWait (IILControlData;)V 6 39 @@ -42,7 +42,7 @@ From top bar (IIILControlData;)J 0 24 foo (IIILControlData;)I 0 19 doTest ()V 42 24 - main ([Ljava/lang/String;)V 6 21 + main ([Ljava/lang/String;)V 3 21 --------- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 print (Ljava/lang/Thread;II)V 0 34 @@ -57,13 +57,13 @@ From top baz (IIILControlData;)Ljava/lang/Object; 9 32 From bottom --------- - main ([Ljava/lang/String;)V 6 21 + main ([Ljava/lang/String;)V 3 21 --------- baz (IIILControlData;)Ljava/lang/Object; 9 32 bar (IIILControlData;)J 0 24 foo (IIILControlData;)I 0 19 doTest ()V 65 30 - main ([Ljava/lang/String;)V 6 21 + main ([Ljava/lang/String;)V 3 21 --------- bar (IIILControlData;)J 0 24 foo (IIILControlData;)I 0 19 @@ -358,7 +358,7 @@ main getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 printAll (I)V 0 73 doTest ()V 102 57 - main ([Ljava/lang/String;)V 30 33 + main ([Ljava/lang/String;)V 27 33 --------- FinalizerDaemon @@ -590,7 +590,7 @@ main getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 printAll (I)V 0 73 doTest ()V 107 59 - main ([Ljava/lang/String;)V 30 33 + main ([Ljava/lang/String;)V 27 33 ######################################## @@ -659,7 +659,7 @@ main getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 printList ([Ljava/lang/Thread;I)V 0 66 doTest ()V 96 52 - main ([Ljava/lang/String;)V 38 37 + main ([Ljava/lang/String;)V 35 37 --------- Thread-14 @@ -771,7 +771,7 @@ main getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 printList ([Ljava/lang/Thread;I)V 0 66 doTest ()V 101 54 - main ([Ljava/lang/String;)V 38 37 + main ([Ljava/lang/String;)V 35 37 ################### @@ -782,7 +782,7 @@ JVMTI_ERROR_ILLEGAL_ARGUMENT [public static native java.lang.Object[] Frames.getFrameLocation(java.lang.Thread,int), ffffffff] [public static void Frames.doTestSameThread(), 38] [public static void Frames.doTest() throws java.lang.Exception, 0] -[public static void Main.main(java.lang.String[]) throws java.lang.Exception, 2e] +[public static void Main.main(java.lang.String[]) throws java.lang.Exception, 2b] JVMTI_ERROR_NO_MORE_FRAMES ################################ diff --git a/test/911-get-stack-trace/src/Main.java b/test/911-get-stack-trace/src/Main.java index b199033c32..96a427d77b 100644 --- a/test/911-get-stack-trace/src/Main.java +++ b/test/911-get-stack-trace/src/Main.java @@ -16,7 +16,7 @@ public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); + bindTest911Classes(); SameThread.doTest(); @@ -42,4 +42,6 @@ public class Main { System.out.println("Done"); } + + private static native void bindTest911Classes(); } diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc index d162e8a169..68f6d8dfb2 100644 --- a/test/911-get-stack-trace/stack_trace.cc +++ b/test/911-get-stack-trace/stack_trace.cc @@ -34,6 +34,14 @@ namespace Test911GetStackTrace { using android::base::StringPrintf; +extern "C" JNIEXPORT void JNICALL Java_Main_bindTest911Classes( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { + BindFunctions(jvmti_env, env, "AllTraces"); + BindFunctions(jvmti_env, env, "Frames"); + BindFunctions(jvmti_env, env, "PrintThread"); + BindFunctions(jvmti_env, env, "ThreadListTraces"); +} + static jint FindLineNumber(jint line_number_count, jvmtiLineNumberEntry* line_number_table, jlocation location) { diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index a22d1d72d8..d13436ebf6 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -20,6 +20,7 @@ #include "jni.h" #include "openjdkjvmti/jvmti.h" #include "ScopedLocalRef.h" +#include "thread-inl.h" #include "ti-agent/common_helper.h" #include "ti-agent/common_load.h" @@ -241,5 +242,138 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassLoaderClasses( return ret; } +extern "C" JNIEXPORT jintArray JNICALL Java_Main_getClassVersion( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { + jint major, minor; + jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + jintArray int_array = env->NewIntArray(2); + if (int_array == nullptr) { + return nullptr; + } + jint buf[2] = { major, minor }; + env->SetIntArrayRegion(int_array, 0, 2, buf); + + return int_array; +} + +static std::string GetClassName(jvmtiEnv* jenv, JNIEnv* jni_env, jclass klass) { + char* name; + jvmtiError result = jenv->GetClassSignature(klass, &name, nullptr); + if (result != JVMTI_ERROR_NONE) { + if (jni_env != nullptr) { + JvmtiErrorToException(jni_env, result); + } else { + printf("Failed to get class signature.\n"); + } + return ""; + } + + std::string tmp(name); + jenv->Deallocate(reinterpret_cast<unsigned char*>(name)); + + return tmp; +} + +static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) { + jvmtiThreadInfo info; + jvmtiError result = jenv->GetThreadInfo(thread, &info); + if (result != JVMTI_ERROR_NONE) { + if (jni_env != nullptr) { + JvmtiErrorToException(jni_env, result); + } else { + printf("Failed to get thread name.\n"); + } + return ""; + } + + std::string tmp(info.name); + jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name)); + jni_env->DeleteLocalRef(info.context_class_loader); + jni_env->DeleteLocalRef(info.thread_group); + + return tmp; +} + +static std::string GetThreadName(Thread* thread) { + std::string tmp; + thread->GetThreadName(tmp); + return tmp; +} + +static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv, + JNIEnv* jni_env, + jthread thread, + jclass klass) { + std::string name = GetClassName(jenv, jni_env, klass); + if (name == "") { + return; + } + std::string thread_name = GetThreadName(jenv, jni_env, thread); + if (thread_name == "") { + return; + } + std::string cur_thread_name = GetThreadName(Thread::Current()); + printf("Prepare: %s on %s (cur=%s)\n", + name.c_str(), + thread_name.c_str(), + cur_thread_name.c_str()); +} + +static void JNICALL ClassLoadCallback(jvmtiEnv* jenv, + JNIEnv* jni_env, + jthread thread, + jclass klass) { + std::string name = GetClassName(jenv, jni_env, klass); + if (name == "") { + return; + } + std::string thread_name = GetThreadName(jenv, jni_env, thread); + if (thread_name == "") { + return; + } + printf("Load: %s on %s\n", name.c_str(), thread_name.c_str()); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadEvents( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) { + if (b == JNI_FALSE) { + jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_CLASS_LOAD, + nullptr); + if (JvmtiErrorToException(env, ret)) { + return; + } + ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_CLASS_PREPARE, + nullptr); + JvmtiErrorToException(env, ret); + return; + } + + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); + callbacks.ClassLoad = ClassLoadCallback; + callbacks.ClassPrepare = ClassPrepareCallback; + jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); + if (JvmtiErrorToException(env, ret)) { + return; + } + + ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_CLASS_LOAD, + nullptr); + if (JvmtiErrorToException(env, ret)) { + return; + } + ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_CLASS_PREPARE, + nullptr); + JvmtiErrorToException(env, ret); +} + } // namespace Test912Classes } // namespace art diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt index a95a465860..d0b77a4bae 100644 --- a/test/912-classes/expected.txt +++ b/test/912-classes/expected.txt @@ -59,3 +59,35 @@ boot <- src (B) <- src-ex (A, List) boot <- src+src-ex (A,B) 912-classes.jar+ -> [class A, class B, class java.lang.Object] + +[37, 0] + +B, false +Load: LB; on main +Prepare: LB; on main (cur=main) +B, true +Load: LB; on main +Prepare: LB; on main (cur=main) +C, false +Load: LA; on main +Prepare: LA; on main (cur=main) +Load: LC; on main +Prepare: LC; on main (cur=main) +A, false +C, true +Load: LA; on main +Prepare: LA; on main (cur=main) +Load: LC; on main +Prepare: LC; on main (cur=main) +A, true +A, true +Load: LA; on main +Prepare: LA; on main (cur=main) +C, true +Load: LC; on main +Prepare: LC; on main (cur=main) +C, true +Load: LA; on TestRunner +Prepare: LA; on TestRunner (cur=TestRunner) +Load: LC; on TestRunner +Prepare: LC; on TestRunner (cur=TestRunner) diff --git a/test/912-classes/src-ex/A.java b/test/912-classes/src-ex/A.java index 64acb2fcfe..2c43cfbd79 100644 --- a/test/912-classes/src-ex/A.java +++ b/test/912-classes/src-ex/A.java @@ -15,4 +15,4 @@ */ public class A { -}
\ No newline at end of file +} diff --git a/test/912-classes/src-ex/C.java b/test/912-classes/src-ex/C.java new file mode 100644 index 0000000000..97f8021486 --- /dev/null +++ b/test/912-classes/src-ex/C.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class C extends A { +} diff --git a/test/912-classes/src/B.java b/test/912-classes/src/B.java index f1458c3bca..52ce4dd58e 100644 --- a/test/912-classes/src/B.java +++ b/test/912-classes/src/B.java @@ -15,4 +15,4 @@ */ public class B { -}
\ No newline at end of file +} diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java index ea3c49c87b..6ad23a4869 100644 --- a/test/912-classes/src/Main.java +++ b/test/912-classes/src/Main.java @@ -21,8 +21,6 @@ import java.util.Comparator; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } @@ -80,6 +78,14 @@ public class Main { testClassLoader(getProxyClass()); testClassLoaderClasses(); + + System.out.println(); + + testClassVersion(); + + System.out.println(); + + testClassEvents(); } private static Class<?> proxyClass = null; @@ -202,6 +208,71 @@ public class Main { } } + private static void testClassVersion() { + System.out.println(Arrays.toString(getClassVersion(Main.class))); + } + + private static void testClassEvents() throws Exception { + ClassLoader cl = Main.class.getClassLoader(); + while (cl.getParent() != null) { + cl = cl.getParent(); + } + final ClassLoader boot = cl; + + Runnable r = new Runnable() { + @Override + public void run() { + try { + ClassLoader cl6 = create(boot, DEX1, DEX2); + System.out.println("C, true"); + Class.forName("C", true, cl6); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + + Thread dummyThread = new Thread(); + dummyThread.start(); + dummyThread.join(); + + ensureJitCompiled(Main.class, "testClassEvents"); + + enableClassLoadEvents(true); + + ClassLoader cl1 = create(boot, DEX1, DEX2); + System.out.println("B, false"); + Class.forName("B", false, cl1); + + ClassLoader cl2 = create(boot, DEX1, DEX2); + System.out.println("B, true"); + Class.forName("B", true, cl2); + + ClassLoader cl3 = create(boot, DEX1, DEX2); + System.out.println("C, false"); + Class.forName("C", false, cl3); + System.out.println("A, false"); + Class.forName("A", false, cl3); + + ClassLoader cl4 = create(boot, DEX1, DEX2); + System.out.println("C, true"); + Class.forName("C", true, cl4); + System.out.println("A, true"); + Class.forName("A", true, cl4); + + ClassLoader cl5 = create(boot, DEX1, DEX2); + System.out.println("A, true"); + Class.forName("A", true, cl5); + System.out.println("C, true"); + Class.forName("C", true, cl5); + + Thread t = new Thread(r, "TestRunner"); + t.start(); + t.join(); + + enableClassLoadEvents(false); + } + private static void printClassLoaderClasses(ClassLoader cl) { for (;;) { if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { @@ -262,6 +333,12 @@ public class Main { private static native Class<?>[] getClassLoaderClasses(ClassLoader cl); + private static native int[] getClassVersion(Class<?> c); + + private static native void enableClassLoadEvents(boolean b); + + private static native void ensureJitCompiled(Class c, String name); + private static class TestForNonInit { public static double dummy = Math.random(); // So it can't be compile-time initialized. } diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java index 564596e02b..5a11a5b144 100644 --- a/test/913-heaps/src/Main.java +++ b/test/913-heaps/src/Main.java @@ -21,8 +21,6 @@ import java.util.HashSet; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); doFollowReferencesTest(); } diff --git a/test/914-hello-obsolescence/src/Main.java b/test/914-hello-obsolescence/src/Main.java index 46266efb28..8a1471693a 100644 --- a/test/914-hello-obsolescence/src/Main.java +++ b/test/914-hello-obsolescence/src/Main.java @@ -53,7 +53,6 @@ public class Main { "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA="); public static void main(String[] args) { - System.loadLibrary(args[1]); doTest(new Transform()); } diff --git a/test/915-obsolete-2/src/Main.java b/test/915-obsolete-2/src/Main.java index bbeb726858..0e3145c220 100644 --- a/test/915-obsolete-2/src/Main.java +++ b/test/915-obsolete-2/src/Main.java @@ -79,7 +79,6 @@ public class Main { "IAAAFwAAAD4CAAADIAAABAAAAAgEAAAAIAAAAQAAACYEAAAAEAAAAQAAADwEAAA="); public static void main(String[] args) { - System.loadLibrary(args[1]); doTest(new Transform()); } diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java index 1e43f7ee9d..1b03200ba5 100644 --- a/test/916-obsolete-jit/src/Main.java +++ b/test/916-obsolete-jit/src/Main.java @@ -113,7 +113,6 @@ public class Main { } public static void main(String[] args) { - System.loadLibrary(args[1]); doTest(new Transform(), new TestWatcher()); } diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java index 5378bb7a05..632a5c8b21 100644 --- a/test/917-fields-transformation/src/Main.java +++ b/test/917-fields-transformation/src/Main.java @@ -55,7 +55,6 @@ public class Main { "AAIgAAAMAAAAXAEAAAMgAAACAAAA4QEAAAAgAAABAAAA8AEAAAAQAAABAAAABAIAAA=="); public static void main(String[] args) { - System.loadLibrary(args[1]); doTest(new Transform("Hello", "Goodbye"), new Transform("start", "end")); } diff --git a/test/918-fields/src/Main.java b/test/918-fields/src/Main.java index 8af6e7b375..3ba535b31b 100644 --- a/test/918-fields/src/Main.java +++ b/test/918-fields/src/Main.java @@ -19,8 +19,6 @@ import java.util.Arrays; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/919-obsolete-fields/src/Main.java b/test/919-obsolete-fields/src/Main.java index 895c7a3fa4..1d893f125a 100644 --- a/test/919-obsolete-fields/src/Main.java +++ b/test/919-obsolete-fields/src/Main.java @@ -116,7 +116,6 @@ public class Main { } public static void main(String[] args) { - System.loadLibrary(args[1]); TestWatcher w = new TestWatcher(); doTest(new Transform(w), w); } diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java index 43d6e9ed07..67ca1e15d6 100644 --- a/test/921-hello-failure/src/Main.java +++ b/test/921-hello-failure/src/Main.java @@ -18,7 +18,6 @@ import java.util.ArrayList; public class Main { public static void main(String[] args) { - System.loadLibrary(args[1]); NewName.doTest(new Transform()); DifferentAccess.doTest(new Transform()); NewInterface.doTest(new Transform2()); diff --git a/test/922-properties/src/Main.java b/test/922-properties/src/Main.java index 6cec6e97f2..8ad742f0ca 100644 --- a/test/922-properties/src/Main.java +++ b/test/922-properties/src/Main.java @@ -19,8 +19,6 @@ import java.util.TreeSet; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/923-monitors/src/Main.java b/test/923-monitors/src/Main.java index e35ce12608..ef00728d10 100644 --- a/test/923-monitors/src/Main.java +++ b/test/923-monitors/src/Main.java @@ -21,8 +21,6 @@ import java.util.List; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt index 32e3368d02..67d20eb750 100644 --- a/test/924-threads/expected.txt +++ b/test/924-threads/expected.txt @@ -29,3 +29,9 @@ e1 = ALIVE|WAITING_WITH_TIMEOUT|SLEEPING|WAITING 5 = ALIVE|RUNNABLE 2 = TERMINATED [Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system], Thread[main,5,main]] +JVMTI_ERROR_THREAD_NOT_ALIVE +JVMTI_ERROR_THREAD_NOT_ALIVE +Constructed thread +Thread(EventTestThread): start +Thread(EventTestThread): end +Thread joined diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java index 492a7ac6d1..29c4aa330c 100644 --- a/test/924-threads/src/Main.java +++ b/test/924-threads/src/Main.java @@ -25,8 +25,6 @@ import java.util.Map; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } @@ -56,6 +54,10 @@ public class Main { doStateTests(); doAllThreadsTests(); + + doTLSTests(); + + doTestEvents(); } private static class Holder { @@ -164,6 +166,84 @@ public class Main { System.out.println(Arrays.toString(threads)); } + private static void doTLSTests() throws Exception { + doTLSNonLiveTests(); + doTLSLiveTests(); + } + + private static void doTLSNonLiveTests() throws Exception { + Thread t = new Thread(); + try { + setTLS(t, 1); + System.out.println("Expected failure setting TLS for non-live thread"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + t.start(); + t.join(); + try { + setTLS(t, 1); + System.out.println("Expected failure setting TLS for non-live thread"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + private static void doTLSLiveTests() throws Exception { + setTLS(Thread.currentThread(), 1); + + long l = getTLS(Thread.currentThread()); + if (l != 1) { + throw new RuntimeException("Unexpected TLS value: " + l); + }; + + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + + Runnable r = new Runnable() { + @Override + public void run() { + try { + cdl1.countDown(); + cdl2.await(); + setTLS(Thread.currentThread(), 2); + if (getTLS(Thread.currentThread()) != 2) { + throw new RuntimeException("Different thread issue"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + + Thread t = new Thread(r); + t.start(); + cdl1.await(); + setTLS(Thread.currentThread(), 1); + cdl2.countDown(); + + t.join(); + if (getTLS(Thread.currentThread()) != 1) { + throw new RuntimeException("Got clobbered"); + } + } + + private static void doTestEvents() throws Exception { + enableThreadEvents(true); + + Thread t = new Thread("EventTestThread"); + + System.out.println("Constructed thread"); + Thread.yield(); + + t.start(); + t.join(); + + System.out.println("Thread joined"); + + enableThreadEvents(false); + } + private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() { public int compare(Thread o1, Thread o2) { return o1.getName().compareTo(o2.getName()); @@ -229,4 +309,7 @@ public class Main { private static native Object[] getThreadInfo(Thread t); private static native int getThreadState(Thread t); private static native Thread[] getAllThreads(); + private static native void setTLS(Thread t, long l); + private static native long getTLS(Thread t); + private static native void enableThreadEvents(boolean b); } diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc index 1487b7c64d..0380433d19 100644 --- a/test/924-threads/threads.cc +++ b/test/924-threads/threads.cc @@ -120,5 +120,88 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getAllThreads( return ret; } +extern "C" JNIEXPORT jlong JNICALL Java_Main_getTLS( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) { + void* tls; + jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls); + if (JvmtiErrorToException(env, result)) { + return 0; + } + return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls)); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_setTLS( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) { + const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val)); + jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls); + JvmtiErrorToException(env, result); +} + +static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env, + JNIEnv* jni_env, + jthread thread, + bool is_start) { + jvmtiThreadInfo info; + jvmtiError result = jvmti_env->GetThreadInfo(thread, &info); + if (result != JVMTI_ERROR_NONE) { + printf("Error getting thread info"); + return; + } + printf("Thread(%s): %s\n", info.name, is_start ? "start" : "end"); + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name)); + jni_env->DeleteLocalRef(info.thread_group); + jni_env->DeleteLocalRef(info.context_class_loader); +} + +static void JNICALL ThreadStart(jvmtiEnv* jvmti_env, + JNIEnv* jni_env, + jthread thread) { + ThreadEvent(jvmti_env, jni_env, thread, true); +} + +static void JNICALL ThreadEnd(jvmtiEnv* jvmti_env, + JNIEnv* jni_env, + jthread thread) { + ThreadEvent(jvmti_env, jni_env, thread, false); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_enableThreadEvents( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) { + if (b == JNI_FALSE) { + jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_THREAD_START, + nullptr); + if (JvmtiErrorToException(env, ret)) { + return; + } + ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_THREAD_END, + nullptr); + JvmtiErrorToException(env, ret); + return; + } + + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); + callbacks.ThreadStart = ThreadStart; + callbacks.ThreadEnd = ThreadEnd; + jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); + if (JvmtiErrorToException(env, ret)) { + return; + } + + ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_THREAD_START, + nullptr); + if (JvmtiErrorToException(env, ret)) { + return; + } + ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_THREAD_END, + nullptr); + JvmtiErrorToException(env, ret); +} + } // namespace Test924Threads } // namespace art diff --git a/test/925-threadgroups/src/Main.java b/test/925-threadgroups/src/Main.java index c59efe2f7b..3d7a4ca740 100644 --- a/test/925-threadgroups/src/Main.java +++ b/test/925-threadgroups/src/Main.java @@ -19,8 +19,6 @@ import java.util.Comparator; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/926-multi-obsolescence/src/Main.java b/test/926-multi-obsolescence/src/Main.java index 8a6cf84b8b..6d9f96ca0e 100644 --- a/test/926-multi-obsolescence/src/Main.java +++ b/test/926-multi-obsolescence/src/Main.java @@ -92,7 +92,6 @@ public class Main { "AACUAQAAAiAAABEAAACiAQAAAyAAAAIAAACXAgAAACAAAAEAAAClAgAAABAAAAEAAAC0AgAA")); public static void main(String[] args) { - System.loadLibrary(args[1]); doTest(new Transform(), new Transform2()); } diff --git a/test/927-timers/src/Main.java b/test/927-timers/src/Main.java index 2f5c85cab5..b67f66d3a7 100644 --- a/test/927-timers/src/Main.java +++ b/test/927-timers/src/Main.java @@ -18,8 +18,6 @@ import java.util.Arrays; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/928-jni-table/src/Main.java b/test/928-jni-table/src/Main.java index b0baea1f9d..fd61b7d955 100644 --- a/test/928-jni-table/src/Main.java +++ b/test/928-jni-table/src/Main.java @@ -16,8 +16,6 @@ public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doJNITableTest(); System.out.println("Done"); diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java index d253e6fdf6..bbeb0816c8 100644 --- a/test/929-search/src/Main.java +++ b/test/929-search/src/Main.java @@ -18,8 +18,6 @@ import java.util.Arrays; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - doTest(); } diff --git a/test/930-hello-retransform/src/Main.java b/test/930-hello-retransform/src/Main.java index 12194c3235..0063c82897 100644 --- a/test/930-hello-retransform/src/Main.java +++ b/test/930-hello-retransform/src/Main.java @@ -49,7 +49,6 @@ public class Main { "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); public static void main(String[] args) { - System.loadLibrary(args[1]); doTest(new Transform()); } diff --git a/test/931-agent-thread/src/Main.java b/test/931-agent-thread/src/Main.java index 6471bc8437..a7639fbfb2 100644 --- a/test/931-agent-thread/src/Main.java +++ b/test/931-agent-thread/src/Main.java @@ -18,8 +18,6 @@ import java.util.Arrays; public class Main { public static void main(String[] args) throws Exception { - System.loadLibrary(args[1]); - testAgentThread(); System.out.println("Done"); diff --git a/test/932-transform-saves/build b/test/932-transform-saves/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/932-transform-saves/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# 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. + +./default-build "$@" --experimental agents diff --git a/test/932-transform-saves/expected.txt b/test/932-transform-saves/expected.txt new file mode 100644 index 0000000000..5097771994 --- /dev/null +++ b/test/932-transform-saves/expected.txt @@ -0,0 +1,3 @@ +hello +Goodbye +hello diff --git a/test/932-transform-saves/info.txt b/test/932-transform-saves/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/932-transform-saves/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/932-transform-saves/run b/test/932-transform-saves/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/932-transform-saves/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# 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. + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/932-transform-saves/src/Main.java b/test/932-transform-saves/src/Main.java new file mode 100644 index 0000000000..d960322071 --- /dev/null +++ b/test/932-transform-saves/src/Main.java @@ -0,0 +1,115 @@ +/* + * 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.Base64; +public class Main { + /** + * base64 encoded class/dex file for + * class Transform { + * public void sayHi() { + * System.out.println("hello"); + * } + * } + */ + private static final byte[] CLASS_BYTES_A = Base64.getDecoder().decode( + "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" + + "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" + + "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" + + "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAgAAUABgAA" + + "AAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" + + "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN"); + private static final byte[] DEX_BYTES_A = Base64.getDecoder().decode( + "ZGV4CjAzNQC6XWInnnDd1H4NdQ3P3inH8eCVmQI6W7LMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" + + "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" + + "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" + + "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" + + "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjIABWhlbGxvAANvdXQA" + + "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCAgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA="); + + /** + * base64 encoded class/dex file for + * class Transform { + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] CLASS_BYTES_B = Base64.getDecoder().decode( + "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" + + "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" + + "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" + + "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" + + "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" + + "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0="); + private static final byte[] DEX_BYTES_B = Base64.getDecoder().decode( + "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); + + public static void main(String[] args) { + doTest(new Transform()); + } + + public static void doTest(Transform t) { + // TODO We currently need to do this transform call since we don't have any way to make the + // original-dex-file a single-class dex-file letting us restore it easily. We should use the + // manipulation library that is being made when we store the original dex file. + // TODO REMOVE this theoretically does nothing but it ensures the original-dex-file we have set + // is one we can return to unaltered. + doCommonClassRedefinition(Transform.class, CLASS_BYTES_A, DEX_BYTES_A); + t.sayHi(); + + // Now turn it into DEX_BYTES_B so it says 'Goodbye' + addCommonTransformationResult("Transform", CLASS_BYTES_B, DEX_BYTES_B); + enableCommonRetransformation(true); + doCommonClassRetransformation(Transform.class); + t.sayHi(); + + // Now turn it back to normal by removing the load-hook and transforming again. + enableCommonRetransformation(false); + doCommonClassRetransformation(Transform.class); + t.sayHi(); + } + + // Transforms the class + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_bytes, + byte[] dex_bytes); + private static native void doCommonClassRetransformation(Class<?>... target); + private static native void enableCommonRetransformation(boolean enable); + private static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/932-transform-saves/src/Transform.java b/test/932-transform-saves/src/Transform.java new file mode 100644 index 0000000000..8e8af355da --- /dev/null +++ b/test/932-transform-saves/src/Transform.java @@ -0,0 +1,28 @@ +/* + * 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 Transform { + public void sayHi() { + // Use lower 'h' to make sure the string will have a different string id + // than the transformation (the transformation code is the same except + // the actual printed String, which was making the test inacurately passing + // in JIT mode when loading the string from the dex cache, as the string ids + // of the two different strings were the same). + // We know the string ids will be different because lexicographically: + // "Goodbye" < "LTransform;" < "hello". + System.out.println("hello"); + } +} diff --git a/test/933-misc-events/build b/test/933-misc-events/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/933-misc-events/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# 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. + +./default-build "$@" --experimental agents diff --git a/test/933-misc-events/expected.txt b/test/933-misc-events/expected.txt new file mode 100644 index 0000000000..024c560b26 --- /dev/null +++ b/test/933-misc-events/expected.txt @@ -0,0 +1,2 @@ +Received dump request. +Done diff --git a/test/933-misc-events/info.txt b/test/933-misc-events/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/933-misc-events/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/933-misc-events/misc_events.cc b/test/933-misc-events/misc_events.cc new file mode 100644 index 0000000000..860d4b5e16 --- /dev/null +++ b/test/933-misc-events/misc_events.cc @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <atomic> +#include <signal.h> +#include <sys/types.h> + +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test933MiscEvents { + +static std::atomic<bool> saw_dump_request(false); + +static void DumpRequestCallback(jvmtiEnv* jenv ATTRIBUTE_UNUSED) { + printf("Received dump request.\n"); + saw_dump_request.store(true, std::memory_order::memory_order_relaxed); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_testSigQuit( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); + callbacks.DataDumpRequest = DumpRequestCallback; + jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); + if (JvmtiErrorToException(env, ret)) { + return; + } + + ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_DATA_DUMP_REQUEST, + nullptr); + if (JvmtiErrorToException(env, ret)) { + return; + } + + // Send sigquit to self. + kill(getpid(), SIGQUIT); + + // Busy-wait for request. + for (;;) { + sleep(1); + if (saw_dump_request.load(std::memory_order::memory_order_relaxed)) { + break; + } + } + + ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, nullptr); + JvmtiErrorToException(env, ret); +} + +} // namespace Test933MiscEvents +} // namespace art diff --git a/test/933-misc-events/run b/test/933-misc-events/run new file mode 100755 index 0000000000..0a8d0672f6 --- /dev/null +++ b/test/933-misc-events/run @@ -0,0 +1,23 @@ +#!/bin/bash +# +# 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. + +# This test checks whether dex files can be injected into parent classloaders. App images preload +# classes, which will make the injection moot. Turn off app images to avoid the issue. + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti \ + --no-app-image diff --git a/test/933-misc-events/src/Main.java b/test/933-misc-events/src/Main.java new file mode 100644 index 0000000000..89801a3555 --- /dev/null +++ b/test/933-misc-events/src/Main.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + testSigQuit(); + + System.out.println("Done"); + } + + private static native void testSigQuit(); +} diff --git a/test/Android.bp b/test/Android.bp index be5bc59e5c..89e409223e 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -270,6 +270,7 @@ art_cc_defaults { "928-jni-table/jni_table.cc", "929-search/search.cc", "931-agent-thread/agent_thread.cc", + "933-misc-events/misc_events.cc", ], shared_libs: [ "libbase", @@ -326,6 +327,7 @@ cc_defaults { "570-checker-osr/osr.cc", "595-profile-saving/profile-saving.cc", "596-app-images/app_images.cc", + "596-monitor-inflation/monitor_inflation.cc", "597-deopt-new-string/deopt.cc", "626-const-class-linking/clear_dex_cache_types.cc", ], diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index c8e2185891..814f968bc9 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -277,41 +277,6 @@ TEST_ART_BROKEN_TARGET_TESTS := \ 147-stripped-dex-fallback \ 569-checker-pattern-replacement -# These 9** tests are not supported in current form due to linker -# restrictions. See b/31681198 -TEST_ART_BROKEN_TARGET_TESTS += \ - 901-hello-ti-agent \ - 902-hello-transformation \ - 903-hello-tagging \ - 904-object-allocation \ - 905-object-free \ - 906-iterate-heap \ - 907-get-loaded-classes \ - 908-gc-start-finish \ - 909-attach-agent \ - 910-methods \ - 911-get-stack-trace \ - 912-classes \ - 913-heaps \ - 914-hello-obsolescence \ - 915-obsolete-2 \ - 916-obsolete-jit \ - 917-fields-transformation \ - 918-fields \ - 919-obsolete-fields \ - 920-objects \ - 921-hello-failure \ - 922-properties \ - 923-monitors \ - 924-threads \ - 925-threadgroups \ - 926-multi-obsolescence \ - 927-timers \ - 928-jni-table \ - 929-search \ - 930-hello-retransform \ - 931-agent-thread \ - ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ diff --git a/test/run-test b/test/run-test index 9b178021ff..c78fa35e24 100755 --- a/test/run-test +++ b/test/run-test @@ -131,6 +131,7 @@ pic_image_suffix="" multi_image_suffix="" android_root="/system" bisection_search="no" +suspend_timeout="500000" # By default we will use optimizing. image_args="" image_suffix="" @@ -219,6 +220,10 @@ while true; do basic_verify="true" gc_stress="true" shift + elif [ "x$1" = "x--suspend-timeout" ]; then + shift + suspend_timeout="$1" + shift elif [ "x$1" = "x--image" ]; then shift image="$1" @@ -402,6 +407,9 @@ noncanonical_tmp_dir=$tmp_dir tmp_dir="`cd $oldwd ; python -c "import os; print os.path.realpath('$tmp_dir')"`" mkdir -p $tmp_dir +# Add thread suspend timeout flag +run_args="${run_args} --runtime-option -XX:ThreadSuspendTimeout=$suspend_timeout" + if [ "$basic_verify" = "true" ]; then # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests. run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify --runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0" @@ -649,6 +657,7 @@ if [ "$usage" = "yes" ]; then echo " --quiet Don't print anything except failure messages" echo " --bisection-search Perform bisection bug search." echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild." + echo " --suspend-timeout Change thread suspend timeout ms (default 500000)." ) 1>&2 # Direct to stderr so usage is not printed if --quiet is set. exit 1 fi diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc index 8799c9188b..80e1797369 100644 --- a/test/ti-agent/common_helper.cc +++ b/test/ti-agent/common_helper.cc @@ -16,14 +16,18 @@ #include "ti-agent/common_helper.h" +#include <dlfcn.h> #include <stdio.h> #include <sstream> #include <deque> +#include "android-base/stringprintf.h" #include "art_method.h" #include "jni.h" +#include "jni_internal.h" #include "openjdkjvmti/jvmti.h" #include "scoped_thread_state_change-inl.h" +#include "ScopedLocalRef.h" #include "stack.h" #include "ti-agent/common_load.h" #include "utils.h" @@ -253,11 +257,12 @@ void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env, jint* new_class_data_len, unsigned char** new_class_data) { std::string name_str(name); - if (gTransformations.find(name_str) != gTransformations.end()) { + if (gTransformations.find(name_str) != gTransformations.end() && + gTransformations[name_str].size() > 0) { CommonTransformationResult& res = gTransformations[name_str][0]; const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes; unsigned char* new_data; - jvmti_env->Allocate(desired_array.size(), &new_data); + CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data)); memcpy(new_data, desired_array.data(), desired_array.size()); *new_class_data = new_data; *new_class_data_len = desired_array.size(); @@ -324,4 +329,123 @@ jint OnLoad(JavaVM* vm, } // namespace common_retransform +static void BindMethod(jvmtiEnv* jenv, + JNIEnv* env, + jclass klass, + jmethodID method) { + char* name; + char* signature; + jvmtiError name_result = jenv->GetMethodName(method, &name, &signature, nullptr); + if (name_result != JVMTI_ERROR_NONE) { + LOG(FATAL) << "Could not get methods"; + } + + ArtMethod* m = jni::DecodeArtMethod(method); + + std::string names[2]; + { + ScopedObjectAccess soa(Thread::Current()); + names[0] = m->JniShortName(); + names[1] = m->JniLongName(); + } + for (const std::string& mangled_name : names) { + void* sym = dlsym(RTLD_DEFAULT, mangled_name.c_str()); + if (sym == nullptr) { + continue; + } + + JNINativeMethod native_method; + native_method.fnPtr = sym; + native_method.name = name; + native_method.signature = signature; + + env->RegisterNatives(klass, &native_method, 1); + + jenv->Deallocate(reinterpret_cast<unsigned char*>(name)); + jenv->Deallocate(reinterpret_cast<unsigned char*>(signature)); + return; + } + + LOG(FATAL) << "Could not find " << names[0]; +} + +static jclass FindClassWithSystemClassLoader(JNIEnv* env, const char* class_name) { + // Find the system classloader. + ScopedLocalRef<jclass> cl_klass(env, env->FindClass("java/lang/ClassLoader")); + if (cl_klass.get() == nullptr) { + return nullptr; + } + jmethodID getsystemclassloader_method = env->GetStaticMethodID(cl_klass.get(), + "getSystemClassLoader", + "()Ljava/lang/ClassLoader;"); + if (getsystemclassloader_method == nullptr) { + return nullptr; + } + ScopedLocalRef<jobject> cl(env, env->CallStaticObjectMethod(cl_klass.get(), + getsystemclassloader_method)); + if (cl.get() == nullptr) { + return nullptr; + } + + // Create a String of the name. + std::string descriptor = android::base::StringPrintf("L%s;", class_name); + std::string dot_name = DescriptorToDot(descriptor.c_str()); + ScopedLocalRef<jstring> name_str(env, env->NewStringUTF(dot_name.c_str())); + + // Call Class.forName with it. + ScopedLocalRef<jclass> c_klass(env, env->FindClass("java/lang/Class")); + if (c_klass.get() == nullptr) { + return nullptr; + } + jmethodID forname_method = env->GetStaticMethodID( + c_klass.get(), + "forName", + "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); + if (forname_method == nullptr) { + return nullptr; + } + + return reinterpret_cast<jclass>(env->CallStaticObjectMethod(c_klass.get(), + forname_method, + name_str.get(), + JNI_FALSE, + cl.get())); +} + +void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) { + // Use JNI to load the class. + ScopedLocalRef<jclass> klass(env, env->FindClass(class_name)); + if (klass.get() == nullptr) { + // We may be called with the wrong classloader. Try explicitly using the system classloader. + env->ExceptionClear(); + klass.reset(FindClassWithSystemClassLoader(env, class_name)); + if (klass.get() == nullptr) { + LOG(FATAL) << "Could not load " << class_name; + } + } + + // Use JVMTI to get the methods. + jint method_count; + jmethodID* methods; + jvmtiError methods_result = jenv->GetClassMethods(klass.get(), &method_count, &methods); + if (methods_result != JVMTI_ERROR_NONE) { + LOG(FATAL) << "Could not get methods"; + } + + // Check each method. + for (jint i = 0; i < method_count; ++i) { + jint modifiers; + jvmtiError mod_result = jenv->GetMethodModifiers(methods[i], &modifiers); + if (mod_result != JVMTI_ERROR_NONE) { + LOG(FATAL) << "Could not get methods"; + } + constexpr jint kNative = static_cast<jint>(kAccNative); + if ((modifiers & kNative) != 0) { + BindMethod(jenv, env, klass.get(), methods[i]); + } + } + + jenv->Deallocate(reinterpret_cast<unsigned char*>(methods)); +} + } // namespace art diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h index 8599fc4d6c..c60553dc5a 100644 --- a/test/ti-agent/common_helper.h +++ b/test/ti-agent/common_helper.h @@ -71,6 +71,12 @@ void SetAllCapabilities(jvmtiEnv* env); bool JvmtiErrorToException(JNIEnv* env, jvmtiError error); +// Load the class through JNI. Inspect it, find all native methods. Construct the corresponding +// mangled name, run dlsym and bind the method. +// +// This will abort on failure. +void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name); + } // namespace art #endif // ART_TEST_TI_AGENT_COMMON_HELPER_H_ diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 1b11442092..8ed8e67e42 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "common_load.h" + #include <jni.h> #include <stdio.h> // TODO I don't know? @@ -22,7 +24,6 @@ #include "art_method-inl.h" #include "base/logging.h" #include "base/macros.h" -#include "common_load.h" #include "common_helper.h" #include "901-hello-ti-agent/basics.h" @@ -32,6 +33,8 @@ namespace art { jvmtiEnv* jvmti_env; +namespace { + using OnLoad = jint (*)(JavaVM* vm, char* options, void* reserved); using OnAttach = jint (*)(JavaVM* vm, char* options, void* reserved); @@ -41,11 +44,50 @@ struct AgentLib { OnAttach attach; }; +static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread ATTRIBUTE_UNUSED) { + // Bind Main native methods. + BindFunctions(jvmti_env, jni_env, "Main"); +} + +// Install a phase callback that will bind JNI functions on VMInit. +bool InstallBindCallback(JavaVM* vm) { + // Use a new jvmtiEnv. Otherwise we might collide with table changes. + jvmtiEnv* install_env; + if (vm->GetEnv(reinterpret_cast<void**>(&install_env), JVMTI_VERSION_1_0) != 0) { + return false; + } + SetAllCapabilities(install_env); + + { + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); + callbacks.VMInit = VMInitCallback; + + jvmtiError install_error = install_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); + if (install_error != JVMTI_ERROR_NONE) { + return false; + } + } + + { + jvmtiError enable_error = install_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_VM_INIT, + nullptr); + if (enable_error != JVMTI_ERROR_NONE) { + return false; + } + } + + return true; +} + // A trivial OnLoad implementation that only initializes the global jvmti_env. static jint MinimalOnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { + if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0) != 0) { printf("Unable to get jvmti env!\n"); return 1; } @@ -55,7 +97,7 @@ static jint MinimalOnLoad(JavaVM* vm, // A list of all non-standard the agents we have for testing. All other agents will use // MinimalOnLoad. -AgentLib agents[] = { +static AgentLib agents[] = { { "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr }, { "902-hello-transformation", common_redefine::OnLoad, nullptr }, { "909-attach-agent", nullptr, Test909AttachAgent::OnAttach }, @@ -67,6 +109,7 @@ AgentLib agents[] = { { "921-hello-failure", common_retransform::OnLoad, nullptr }, { "926-multi-obsolescence", common_redefine::OnLoad, nullptr }, { "930-hello-retransform", common_retransform::OnLoad, nullptr }, + { "932-transform-saves", common_retransform::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { @@ -100,6 +143,28 @@ static void SetIsJVM(char* options) { RuntimeIsJVM = strncmp(options, "jvm", 3) == 0; } +static bool BindFunctionsAttached(JavaVM* vm, const char* class_name) { + // Get a JNIEnv. As the thread is attached, we must not destroy it. + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != 0) { + printf("Unable to get JNI env!\n"); + return false; + } + + jvmtiEnv* jenv; + if (vm->GetEnv(reinterpret_cast<void**>(&jenv), JVMTI_VERSION_1_0) != 0) { + printf("Unable to get jvmti env!\n"); + return false; + } + SetAllCapabilities(jenv); + + BindFunctions(jenv, env, class_name); + + return true; +} + +} // namespace + extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) { char* remaining_options = nullptr; char* name_option = nullptr; @@ -110,6 +175,10 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* SetIsJVM(remaining_options); + if (!InstallBindCallback(vm)) { + return 1; + } + AgentLib* lib = FindAgent(name_option); OnLoad fn = nullptr; if (lib == nullptr) { @@ -131,6 +200,9 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void printf("Unable to find agent name in options: %s\n", options); return -1; } + + BindFunctionsAttached(vm, "Main"); + AgentLib* lib = FindAgent(name_option); if (lib == nullptr) { printf("Unable to find agent named: %s, add it to the list in test/ti-agent/common_load.cc\n", diff --git a/test/ti-agent/common_load.h b/test/ti-agent/common_load.h index fac94b4c6e..d2544214ec 100644 --- a/test/ti-agent/common_load.h +++ b/test/ti-agent/common_load.h @@ -17,6 +17,7 @@ #ifndef ART_TEST_TI_AGENT_COMMON_LOAD_H_ #define ART_TEST_TI_AGENT_COMMON_LOAD_H_ +#include "jni.h" #include "openjdkjvmti/jvmti.h" namespace art { diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt index c148ad01e1..c775f98b70 100644 --- a/test/valgrind-suppressions.txt +++ b/test/valgrind-suppressions.txt @@ -46,3 +46,26 @@ fun:_ZN14UnwindMapLocal5BuildEv fun:_ZN12BacktraceMap6CreateEib } +{ + BackTraceReading64 + Memcheck:Addr8 + fun:access_mem_unrestricted + fun:_Uelf64_memory_read + fun:_Uelf64_get_load_base + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} +{ + BackTraceReading32 + Memcheck:Addr4 + fun:access_mem_unrestricted + fun:_Uelf32_memory_read + fun:_Uelf32_get_load_base + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} + diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java index 99e03e8ea7..af8a05cdbc 100644 --- a/tools/dexfuzz/src/dexfuzz/Options.java +++ b/tools/dexfuzz/src/dexfuzz/Options.java @@ -50,6 +50,7 @@ public class Options { public static String deviceName = ""; public static boolean usingSpecificDevice = false; public static int repeat = 1; + public static int divergenceRetry = 10; public static String executeDirectory = "/data/art-test"; public static String androidRoot = ""; public static String dumpMutationsFile = "mutations.dump"; @@ -118,6 +119,8 @@ public class Options { Log.always(" --repeat=<n> : Fuzz N programs, executing each one."); Log.always(" --short-timeouts : Shorten timeouts (faster; use if"); Log.always(" you want to focus on output divergences)"); + Log.always(" --divergence-retry=<n> : Number of retries when checking if test is"); + Log.always(" self-divergent. (Default: 10)"); Log.always(" --seed=<seed> : RNG seed to use"); Log.always(" --method-mutations=<n> : Maximum number of mutations to perform on each method."); Log.always(" (Default: 3)"); @@ -239,6 +242,8 @@ public class Options { maxMethods = Integer.parseInt(value); } else if (key.equals("repeat")) { repeat = Integer.parseInt(value); + } else if (key.equals("divergence-retry")) { + divergenceRetry = Integer.parseInt(value); } else if (key.equals("log")) { Log.setLoggingLevel(LogTag.valueOf(value.toUpperCase())); } else if (key.equals("likelihoods")) { @@ -360,6 +365,10 @@ public class Options { Log.error("--repeat must be at least 1!"); return false; } + if (divergenceRetry < 0) { + Log.error("--divergence-retry cannot be negative!"); + return false; + } if (usingProvidedSeed && repeat > 1) { Log.error("Cannot use --repeat with --seed"); return false; diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java index 1797d90141..ccc426c5e4 100644 --- a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java +++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java @@ -298,13 +298,13 @@ public abstract class Fuzzer { } private boolean checkGoldenExecutorForSelfDivergence(String programName) { - // Run golden executor 5 times, make sure it always produces + // Run golden executor multiple times, make sure it always produces // the same output, otherwise report that it is self-divergent. // TODO: Instead, produce a list of acceptable outputs, and see if the divergent // outputs of the backends fall within this set of outputs. String seenOutput = null; - for (int i = 0; i < 5; i++) { + for (int i = 0; i < Options.divergenceRetry + 1; i++) { goldenExecutor.reset(); goldenExecutor.execute(programName); String output = goldenExecutor.getResult().getFlattenedOutput(); diff --git a/tools/stream-trace-converter.py b/tools/stream-trace-converter.py index 951b05bf33..7e341f2bcf 100755 --- a/tools/stream-trace-converter.py +++ b/tools/stream-trace-converter.py @@ -124,12 +124,20 @@ class Rewriter: self._threads.append('%d\t%s\n' % (tid, str)) print 'New thread: %d/%s' % (tid, str) + def ProcessTraceSummary(self, input): + summaryLength = ReadIntLE(input) + str = input.read(summaryLength) + self._summary = str + print 'Summary: \"%s\"' % str + def ProcessSpecial(self, input): code = ord(input.read(1)) if code == 1: self.ProcessMethod(input) elif code == 2: self.ProcessThread(input) + elif code == 3: + self.ProcessTraceSummary(input) else: raise MyException("Unknown special!") @@ -147,9 +155,24 @@ class Rewriter: print 'Buffer underrun, file was probably truncated. Results should still be usable.' def Finalize(self, header): - header.write('*threads\n') - for t in self._threads: - header.write(t) + # If the summary is present in the input file, use it as the header except + # for the methods section which is emtpy in the input file. If not present, + # apppend header with the threads that are recorded in the input stream. + if (self._summary): + # Erase the contents that's already written earlier by PrintHeader. + header.seek(0) + header.truncate() + # Copy the lines from the input summary to the output header until + # the methods section is seen. + for line in self._summary.splitlines(True): + if line == "*methods\n": + break + else: + header.write(line) + else: + header.write('*threads\n') + for t in self._threads: + header.write(t) header.write('*methods\n') for m in self._methods: header.write(m) @@ -166,6 +189,7 @@ class Rewriter: self._methods = [] self._threads = [] + self._summary = None self.Process(input, body) self.Finalize(header) |