diff options
137 files changed, 3561 insertions, 1020 deletions
diff --git a/Android.bp b/Android.bp index b9f1db5f46..d0e22fb873 100644 --- a/Android.bp +++ b/Android.bp @@ -27,6 +27,7 @@ subdirs = [ "dexdump", "dexlayout", "dexlist", + "dexoptanalyzer", "disassembler", "imgdiag", "oatdump", diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk index e568ce283e..6de5aefc0b 100644 --- a/build/Android.common_path.mk +++ b/build/Android.common_path.mk @@ -109,6 +109,7 @@ endif ART_CORE_DEBUGGABLE_EXECUTABLES := \ dex2oat \ + dexoptanalyzer \ imgdiag \ oatdump \ patchoat \ diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c87075f51c..bc0838435c 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -28,6 +28,7 @@ GTEST_DEX_DIRECTORIES := \ DexToDexDecompiler \ ErroneousA \ ErroneousB \ + ErroneousInit \ ExceptionHandle \ GetMethodSignature \ ImageLayoutA \ @@ -87,7 +88,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 := AllFields ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode +ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit 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 @@ -100,6 +101,7 @@ ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) +ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_test_DEX_DEPS := Main ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY @@ -136,6 +138,12 @@ ART_GTEST_oat_file_assistant_test_HOST_DEPS := \ ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) +ART_GTEST_dexoptanalyzer_test_HOST_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \ + $(HOST_OUT_EXECUTABLES)/dexoptanalyzerd +ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \ + dexoptanalyzerd ART_GTEST_dex2oat_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) @@ -219,6 +227,7 @@ ART_TEST_MODULES := \ art_dexdump_tests \ art_dexlayout_tests \ art_dexlist_tests \ + art_dexoptanalyzer_tests \ art_imgdiag_tests \ art_oatdump_tests \ art_profman_tests \ @@ -614,6 +623,9 @@ ART_GTEST_jni_internal_test_DEX_DEPS := ART_GTEST_oat_file_assistant_test_DEX_DEPS := ART_GTEST_oat_file_assistant_test_HOST_DEPS := ART_GTEST_oat_file_assistant_test_TARGET_DEPS := +ART_GTEST_dexoptanalyzer_test_DEX_DEPS := +ART_GTEST_dexoptanalyzer_test_HOST_DEPS := +ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := ART_GTEST_dex2oat_test_DEX_DEPS := ART_GTEST_dex2oat_test_HOST_DEPS := ART_GTEST_dex2oat_test_TARGET_DEPS := diff --git a/build/art.go b/build/art.go index 84269c3f23..baa6e59b55 100644 --- a/build/art.go +++ b/build/art.go @@ -58,7 +58,7 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { asflags = append(asflags, "-DART_HEAP_POISONING=1") } - if !envFalse(ctx, "ART_USE_READ_BARRIER") || ctx.AConfig().ArtUseReadBarrier() { + if !envFalse(ctx, "ART_USE_READ_BARRIER") && ctx.AConfig().ArtUseReadBarrier() { // Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP. // The default is BAKER. barrierType := envDefault(ctx, "ART_READ_BARRIER_TYPE", "BAKER") diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 28c009edf0..f1123eb692 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -766,10 +766,6 @@ struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> { Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) { if (option == "none") { existing = ExperimentalFlags::kNone; - } else if (option == "agents") { - existing = existing | ExperimentalFlags::kAgents; - } else if (option == "runtime-plugins") { - existing = existing | ExperimentalFlags::kRuntimePlugins; } else { return Result::Failure(std::string("Unknown option '") + option + "'"); } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c03ffcaa31..320304873d 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -940,6 +940,31 @@ inline void CompilerDriver::CheckThreadPools() { DCHECK(single_thread_pool_ != nullptr); } +static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, + const std::vector<const DexFile*>& dex_files) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); + MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr)); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + + for (const DexFile* dex_file : dex_files) { + for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + const char* descriptor = dex_file->GetClassDescriptor(class_def); + cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); + if (cls.Get() == nullptr) { + soa.Self()->ClearException(); + } else if (&cls->GetDexFile() == dex_file) { + DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->IsCompileTimeVerified()) + << cls->PrettyClass() + << " " << cls->GetStatus(); + } + } + } +} + void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) { @@ -984,6 +1009,9 @@ void CompilerDriver::PreCompile(jobject class_loader, } if (compiler_options_->IsAnyMethodCompilationEnabled()) { + if (kIsDebugBuild) { + EnsureVerifiedOrVerifyAtRuntime(class_loader, dex_files); + } InitializeClasses(class_loader, dex_files, timings); VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false); } @@ -1949,6 +1977,31 @@ static void PopulateVerifiedMethods(const DexFile& dex_file, DCHECK(!it.HasNext()); } +static void LoadAndUpdateStatus(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + mirror::Class::Status status, + Handle<mirror::ClassLoader> class_loader, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + const char* descriptor = dex_file.GetClassDescriptor(class_def); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Handle<mirror::Class> cls(hs.NewHandle<mirror::Class>( + class_linker->FindClass(self, descriptor, class_loader))); + if (cls.Get() != nullptr) { + // Check that the class is resolved with the current dex file. We might get + // a boot image class, or a class in a different dex file for multidex, and + // we should not update the status in that case. + if (&cls->GetDexFile() == &dex_file) { + ObjectLock<mirror::Class> lock(self, cls); + mirror::Class::SetStatus(cls, status, self); + } + } else { + DCHECK(self->IsExceptionPending()); + self->ClearException(); + } +} + bool CompilerDriver::FastVerify(jobject jclass_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) { @@ -1963,12 +2016,12 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, StackHandleScope<2> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); - MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr)); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (!verifier_deps->ValidateDependencies(class_loader, soa.Self())) { return false; } + bool compiler_only_verifies = !GetCompilerOptions().IsAnyMethodCompilationEnabled(); + // We successfully validated the dependencies, now update class status // of verified classes. Note that the dependencies also record which classes // could not be fully verified; we could try again, but that would hurt verification @@ -1983,28 +2036,16 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); if (set.find(class_def.class_idx_) == set.end()) { - if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) { + if (compiler_only_verifies) { // Just update the compiled_classes_ map. The compiler doesn't need to resolve // the type. compiled_classes_.Overwrite( ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified)); } else { - // Resolve the type, so later compilation stages know they don't need to verify + // Update the class status, so later compilation stages know they don't need to verify // the class. - const char* descriptor = dex_file->GetClassDescriptor(class_def); - cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); - if (cls.Get() != nullptr) { - // Check that the class is resolved with the current dex file. We might get - // a boot image class, or a class in a different dex file for multidex, and - // we should not update the status in that case. - if (&cls->GetDexFile() == dex_file) { - ObjectLock<mirror::Class> lock(soa.Self(), cls); - mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self()); - } - } else { - DCHECK(soa.Self()->IsExceptionPending()); - soa.Self()->ClearException(); - } + LoadAndUpdateStatus( + *dex_file, class_def, mirror::Class::kStatusVerified, class_loader, soa.Self()); // Create `VerifiedMethod`s for each methods, the compiler expects one for // quickening or compiling. // Note that this means: @@ -2013,6 +2054,14 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // TODO(ngeoffray): Reconsider this once we refactor compiler filters. PopulateVerifiedMethods(*dex_file, i, verification_results_); } + } else if (!compiler_only_verifies) { + // Make sure later compilation stages know they should not try to verify + // this class again. + LoadAndUpdateStatus(*dex_file, + class_def, + mirror::Class::kStatusRetryVerificationAtRuntime, + class_loader, + soa.Self()); } } } @@ -2199,7 +2248,7 @@ class SetVerifiedClassVisitor : public CompilationVisitor { if (klass.Get() != nullptr) { // Only do this if the class is resolved. If even resolution fails, quickening will go very, // very wrong. - if (klass->IsResolved()) { + if (klass->IsResolved() && !klass->IsErroneousResolved()) { if (klass->GetStatus() < mirror::Class::kStatusVerified) { ObjectLock<mirror::Class> lock(soa.Self(), klass); // Set class status to verified. @@ -2626,7 +2675,8 @@ CompiledClass* CompilerDriver::GetCompiledClass(ClassReference ref) const { void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) { switch (status) { case mirror::Class::kStatusNotReady: - case mirror::Class::kStatusError: + case mirror::Class::kStatusErrorResolved: + case mirror::Class::kStatusErrorUnresolved: case mirror::Class::kStatusRetryVerificationAtRuntime: case mirror::Class::kStatusVerified: case mirror::Class::kStatusInitialized: diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 459aca3b38..15e4cd8e9c 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -756,7 +756,7 @@ bool ImageWriter::PruneAppImageClassInternal( bool my_early_exit = false; // Only for ourselves, ignore caller. // Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the // app image. - if (klass->GetStatus() == mirror::Class::kStatusError) { + if (klass->IsErroneous()) { result = true; } else { ObjPtr<mirror::ClassExt> ext(klass->GetExtData()); @@ -777,8 +777,8 @@ bool ImageWriter::PruneAppImageClassInternal( visited); } // Check static fields and their classes. - size_t num_static_fields = klass->NumReferenceStaticFields(); - if (num_static_fields != 0 && klass->IsResolved()) { + if (klass->IsResolved() && klass->NumReferenceStaticFields() != 0) { + size_t num_static_fields = klass->NumReferenceStaticFields(); // Presumably GC can happen when we are cross compiling, it should not cause performance // problems to do pointer size logic. MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset( @@ -1154,7 +1154,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, // Visit and assign offsets for fields and field arrays. mirror::Class* as_klass = obj->AsClass(); mirror::DexCache* dex_cache = as_klass->GetDexCache(); - DCHECK_NE(as_klass->GetStatus(), mirror::Class::kStatusError); + DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus(); if (compile_app_image_) { // Extra sanity, no boot loader classes should be left! CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass(); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index de5af97b9a..bd2c5e3bfc 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -716,7 +716,10 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { if (compiled_class != nullptr) { status = compiled_class->GetStatus(); } else if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { - status = mirror::Class::kStatusError; + // The oat class status is used only for verification of resolved classes, + // so use kStatusErrorResolved whether the class was resolved or unresolved + // during compile-time verification. + status = mirror::Class::kStatusErrorResolved; } else { status = mirror::Class::kStatusNotReady; } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 5c561f57aa..1b7431612d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1023,7 +1023,8 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, jit_class_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), constant_area_start_(-1), fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - method_address_offset_(-1) { + method_address_offset_(std::less<uint32_t>(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Use a fake return address register to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister)); } @@ -1498,8 +1499,9 @@ void InstructionCodeGeneratorX86::GenerateFPCompare(Location lhs, DCHECK(const_area->IsEmittedAtUseSite()); __ ucomisd(lhs.AsFpuRegister<XmmRegister>(), codegen_->LiteralDoubleAddress( - const_area->GetConstant()->AsDoubleConstant()->GetValue(), - const_area->GetLocations()->InAt(0).AsRegister<Register>())); + const_area->GetConstant()->AsDoubleConstant()->GetValue(), + const_area->GetBaseMethodAddress(), + const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(rhs.IsDoubleStackSlot()); __ ucomisd(lhs.AsFpuRegister<XmmRegister>(), Address(ESP, rhs.GetStackIndex())); @@ -1511,8 +1513,9 @@ void InstructionCodeGeneratorX86::GenerateFPCompare(Location lhs, DCHECK(const_area->IsEmittedAtUseSite()); __ ucomiss(lhs.AsFpuRegister<XmmRegister>(), codegen_->LiteralFloatAddress( - const_area->GetConstant()->AsFloatConstant()->GetValue(), - const_area->GetLocations()->InAt(0).AsRegister<Register>())); + const_area->GetConstant()->AsFloatConstant()->GetValue(), + const_area->GetBaseMethodAddress(), + const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(rhs.IsStackSlot()); __ ucomiss(lhs.AsFpuRegister<XmmRegister>(), Address(ESP, rhs.GetStackIndex())); @@ -2360,10 +2363,14 @@ void InstructionCodeGeneratorX86::VisitX86FPNeg(HX86FPNeg* neg) { Register constant_area = locations->InAt(1).AsRegister<Register>(); XmmRegister mask = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); if (neg->GetType() == Primitive::kPrimFloat) { - __ movss(mask, codegen_->LiteralInt32Address(INT32_C(0x80000000), constant_area)); + __ movss(mask, codegen_->LiteralInt32Address(INT32_C(0x80000000), + neg->GetBaseMethodAddress(), + constant_area)); __ xorps(out.AsFpuRegister<XmmRegister>(), mask); } else { - __ movsd(mask, codegen_->LiteralInt64Address(INT64_C(0x8000000000000000), constant_area)); + __ movsd(mask, codegen_->LiteralInt64Address(INT64_C(0x8000000000000000), + neg->GetBaseMethodAddress(), + constant_area)); __ xorpd(out.AsFpuRegister<XmmRegister>(), mask); } } @@ -3012,8 +3019,9 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { DCHECK(const_area->IsEmittedAtUseSite()); __ addss(first.AsFpuRegister<XmmRegister>(), codegen_->LiteralFloatAddress( - const_area->GetConstant()->AsFloatConstant()->GetValue(), - const_area->GetLocations()->InAt(0).AsRegister<Register>())); + const_area->GetConstant()->AsFloatConstant()->GetValue(), + const_area->GetBaseMethodAddress(), + const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(second.IsStackSlot()); __ addss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex())); @@ -3029,8 +3037,9 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { DCHECK(const_area->IsEmittedAtUseSite()); __ addsd(first.AsFpuRegister<XmmRegister>(), codegen_->LiteralDoubleAddress( - const_area->GetConstant()->AsDoubleConstant()->GetValue(), - const_area->GetLocations()->InAt(0).AsRegister<Register>())); + const_area->GetConstant()->AsDoubleConstant()->GetValue(), + const_area->GetBaseMethodAddress(), + const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(second.IsDoubleStackSlot()); __ addsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex())); @@ -3116,8 +3125,9 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { DCHECK(const_area->IsEmittedAtUseSite()); __ subss(first.AsFpuRegister<XmmRegister>(), codegen_->LiteralFloatAddress( - const_area->GetConstant()->AsFloatConstant()->GetValue(), - const_area->GetLocations()->InAt(0).AsRegister<Register>())); + const_area->GetConstant()->AsFloatConstant()->GetValue(), + const_area->GetBaseMethodAddress(), + const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(second.IsStackSlot()); __ subss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex())); @@ -3134,6 +3144,7 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { __ subsd(first.AsFpuRegister<XmmRegister>(), codegen_->LiteralDoubleAddress( const_area->GetConstant()->AsDoubleConstant()->GetValue(), + const_area->GetBaseMethodAddress(), const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(second.IsDoubleStackSlot()); @@ -3304,6 +3315,7 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) { __ mulss(first.AsFpuRegister<XmmRegister>(), codegen_->LiteralFloatAddress( const_area->GetConstant()->AsFloatConstant()->GetValue(), + const_area->GetBaseMethodAddress(), const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(second.IsStackSlot()); @@ -3322,6 +3334,7 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) { __ mulsd(first.AsFpuRegister<XmmRegister>(), codegen_->LiteralDoubleAddress( const_area->GetConstant()->AsDoubleConstant()->GetValue(), + const_area->GetBaseMethodAddress(), const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(second.IsDoubleStackSlot()); @@ -3690,6 +3703,7 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) { __ divss(first.AsFpuRegister<XmmRegister>(), codegen_->LiteralFloatAddress( const_area->GetConstant()->AsFloatConstant()->GetValue(), + const_area->GetBaseMethodAddress(), const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(second.IsStackSlot()); @@ -3706,8 +3720,9 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) { DCHECK(const_area->IsEmittedAtUseSite()); __ divsd(first.AsFpuRegister<XmmRegister>(), codegen_->LiteralDoubleAddress( - const_area->GetConstant()->AsDoubleConstant()->GetValue(), - const_area->GetLocations()->InAt(0).AsRegister<Register>())); + const_area->GetConstant()->AsDoubleConstant()->GetValue(), + const_area->GetBaseMethodAddress(), + const_area->GetLocations()->InAt(0).AsRegister<Register>())); } else { DCHECK(second.IsDoubleStackSlot()); __ divsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex())); @@ -4454,18 +4469,7 @@ void CodeGeneratorX86::GenerateMemoryBarrier(MemBarrierKind kind) { HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86::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 X86ComputeBaseMethodAddress 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 CodeGeneratorX86::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, @@ -4518,7 +4522,10 @@ Location CodeGeneratorX86::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticO __ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset)); // Bind a new fixup label at the end of the "movl" insn. uint32_t offset = invoke->GetDexCacheArrayOffset(); - __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset)); + __ Bind(NewPcRelativeDexCacheArrayPatch( + invoke->InputAt(invoke->GetSpecialInputIndex())->AsX86ComputeBaseMethodAddress(), + invoke->GetDexFileForPcRelativeDexCache(), + offset)); break; } case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { @@ -4603,31 +4610,54 @@ void CodeGeneratorX86::RecordSimplePatch() { void CodeGeneratorX86::RecordBootStringPatch(HLoadString* load_string) { DCHECK(GetCompilerOptions().IsBootImage()); - string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex().index_); + HX86ComputeBaseMethodAddress* address = nullptr; + if (GetCompilerOptions().GetCompilePic()) { + address = load_string->InputAt(0)->AsX86ComputeBaseMethodAddress(); + } else { + DCHECK_EQ(load_string->InputCount(), 0u); + } + string_patches_.emplace_back(address, + load_string->GetDexFile(), + load_string->GetStringIndex().index_); __ Bind(&string_patches_.back().label); } void CodeGeneratorX86::RecordBootTypePatch(HLoadClass* load_class) { - boot_image_type_patches_.emplace_back(load_class->GetDexFile(), + HX86ComputeBaseMethodAddress* address = nullptr; + if (GetCompilerOptions().GetCompilePic()) { + address = load_class->InputAt(0)->AsX86ComputeBaseMethodAddress(); + } else { + DCHECK_EQ(load_class->InputCount(), 0u); + } + boot_image_type_patches_.emplace_back(address, + load_class->GetDexFile(), load_class->GetTypeIndex().index_); __ Bind(&boot_image_type_patches_.back().label); } Label* CodeGeneratorX86::NewTypeBssEntryPatch(HLoadClass* load_class) { - type_bss_entry_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_); + HX86ComputeBaseMethodAddress* address = + load_class->InputAt(0)->AsX86ComputeBaseMethodAddress(); + type_bss_entry_patches_.emplace_back( + address, load_class->GetDexFile(), load_class->GetTypeIndex().index_); return &type_bss_entry_patches_.back().label; } Label* CodeGeneratorX86::NewStringBssEntryPatch(HLoadString* load_string) { DCHECK(!GetCompilerOptions().IsBootImage()); - string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex().index_); + HX86ComputeBaseMethodAddress* address = + load_string->InputAt(0)->AsX86ComputeBaseMethodAddress(); + string_patches_.emplace_back( + address, load_string->GetDexFile(), load_string->GetStringIndex().index_); return &string_patches_.back().label; } -Label* CodeGeneratorX86::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, - uint32_t element_offset) { +Label* CodeGeneratorX86::NewPcRelativeDexCacheArrayPatch( + HX86ComputeBaseMethodAddress* method_address, + const DexFile& dex_file, + uint32_t element_offset) { // Add the patch entry and bind its label at the end of the instruction. - pc_relative_dex_cache_patches_.emplace_back(dex_file, element_offset); + pc_relative_dex_cache_patches_.emplace_back(method_address, dex_file, element_offset); return &pc_relative_dex_cache_patches_.back().label; } @@ -4637,12 +4667,12 @@ constexpr uint32_t kLabelPositionToLiteralOffsetAdjustment = 4u; template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorX86::EmitPcRelativeLinkerPatches( - const ArenaDeque<PatchInfo<Label>>& infos, + const ArenaDeque<X86PcRelativePatchInfo>& infos, ArenaVector<LinkerPatch>* linker_patches) { - for (const PatchInfo<Label>& info : infos) { + for (const X86PcRelativePatchInfo& info : infos) { uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; - linker_patches->push_back( - Factory(literal_offset, &info.dex_file, GetMethodAddressOffset(), info.index)); + linker_patches->push_back(Factory( + literal_offset, &info.dex_file, GetMethodAddressOffset(info.method_address), info.index)); } } @@ -6002,13 +6032,6 @@ HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind( FALLTHROUGH_INTENDED; case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); // Note: boot image is also non-JIT. - // We disable pc-relative load when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods - // with irreducible loops. - if (GetGraph()->HasIrreducibleLoops()) { - return HLoadClass::LoadKind::kDexCacheViaMethod; - } break; case HLoadClass::LoadKind::kBootImageAddress: break; @@ -6195,13 +6218,6 @@ HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( FALLTHROUGH_INTENDED; case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); // Note: boot image is also non-JIT. - // We disable pc-relative load when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods - // with irreducible loops. - if (GetGraph()->HasIrreducibleLoops()) { - return HLoadString::LoadKind::kDexCacheViaMethod; - } break; case HLoadString::LoadKind::kBootImageAddress: break; @@ -7489,7 +7505,7 @@ void InstructionCodeGeneratorX86::VisitX86ComputeBaseMethodAddress( __ Bind(&next_instruction); // Remember this offset for later use with constant area. - codegen_->SetMethodAddressOffset(GetAssembler()->CodeSize()); + codegen_->AddMethodAddressOffset(insn, GetAssembler()->CodeSize()); // Grab the return address off the stack. __ popl(reg); @@ -7536,17 +7552,20 @@ void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromCons switch (insn->GetType()) { case Primitive::kPrimFloat: __ movss(out.AsFpuRegister<XmmRegister>(), - codegen_->LiteralFloatAddress(value->AsFloatConstant()->GetValue(), const_area)); + codegen_->LiteralFloatAddress( + value->AsFloatConstant()->GetValue(), insn->GetBaseMethodAddress(), const_area)); break; case Primitive::kPrimDouble: __ movsd(out.AsFpuRegister<XmmRegister>(), - codegen_->LiteralDoubleAddress(value->AsDoubleConstant()->GetValue(), const_area)); + codegen_->LiteralDoubleAddress( + value->AsDoubleConstant()->GetValue(), insn->GetBaseMethodAddress(), const_area)); break; case Primitive::kPrimInt: __ movl(out.AsRegister<Register>(), - codegen_->LiteralInt32Address(value->AsIntConstant()->GetValue(), const_area)); + codegen_->LiteralInt32Address( + value->AsIntConstant()->GetValue(), insn->GetBaseMethodAddress(), const_area)); break; default: @@ -7559,13 +7578,18 @@ void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromCons */ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenerator> { public: - RIPFixup(CodeGeneratorX86& codegen, size_t offset) - : codegen_(&codegen), offset_into_constant_area_(offset) {} + RIPFixup(CodeGeneratorX86& codegen, + HX86ComputeBaseMethodAddress* base_method_address, + size_t offset) + : codegen_(&codegen), + base_method_address_(base_method_address), + offset_into_constant_area_(offset) {} protected: void SetOffset(size_t offset) { offset_into_constant_area_ = offset; } CodeGeneratorX86* codegen_; + HX86ComputeBaseMethodAddress* base_method_address_; private: void Process(const MemoryRegion& region, int pos) OVERRIDE { @@ -7574,7 +7598,8 @@ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenera // The value to patch is the distance from the offset in the constant area // from the address computed by the HX86ComputeBaseMethodAddress instruction. int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_; - int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset(); + int32_t relative_position = + constant_offset - codegen_->GetMethodAddressOffset(base_method_address_); // Patch in the right value. region.StoreUnaligned<int32_t>(pos - 4, relative_position); @@ -7591,7 +7616,8 @@ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenera class JumpTableRIPFixup : public RIPFixup { public: JumpTableRIPFixup(CodeGeneratorX86& codegen, HX86PackedSwitch* switch_instr) - : RIPFixup(codegen, static_cast<size_t>(-1)), switch_instr_(switch_instr) {} + : RIPFixup(codegen, switch_instr->GetBaseMethodAddress(), static_cast<size_t>(-1)), + switch_instr_(switch_instr) {} void CreateJumpTable() { X86Assembler* assembler = codegen_->GetAssembler(); @@ -7602,7 +7628,7 @@ class JumpTableRIPFixup : public RIPFixup { // The label values in the jump table are computed relative to the // instruction addressing the constant area. - const int32_t relative_offset = codegen_->GetMethodAddressOffset(); + const int32_t relative_offset = codegen_->GetMethodAddressOffset(base_method_address_); // Populate the jump table with the correct values for the jump table. int32_t num_entries = switch_instr_->GetNumEntries(); @@ -7644,23 +7670,32 @@ void CodeGeneratorX86::Finalize(CodeAllocator* allocator) { CodeGenerator::Finalize(allocator); } -Address CodeGeneratorX86::LiteralDoubleAddress(double v, Register reg) { - AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddDouble(v)); +Address CodeGeneratorX86::LiteralDoubleAddress(double v, + HX86ComputeBaseMethodAddress* method_base, + Register reg) { + AssemblerFixup* fixup = + new (GetGraph()->GetArena()) RIPFixup(*this, method_base, __ AddDouble(v)); return Address(reg, kDummy32BitOffset, fixup); } -Address CodeGeneratorX86::LiteralFloatAddress(float v, Register reg) { - AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddFloat(v)); +Address CodeGeneratorX86::LiteralFloatAddress(float v, + HX86ComputeBaseMethodAddress* method_base, + Register reg) { + AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, method_base, __ AddFloat(v)); return Address(reg, kDummy32BitOffset, fixup); } -Address CodeGeneratorX86::LiteralInt32Address(int32_t v, Register reg) { - AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddInt32(v)); +Address CodeGeneratorX86::LiteralInt32Address(int32_t v, + HX86ComputeBaseMethodAddress* method_base, + Register reg) { + AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, method_base, __ AddInt32(v)); return Address(reg, kDummy32BitOffset, fixup); } -Address CodeGeneratorX86::LiteralInt64Address(int64_t v, Register reg) { - AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddInt64(v)); +Address CodeGeneratorX86::LiteralInt64Address(int64_t v, + HX86ComputeBaseMethodAddress* method_base, + Register reg) { + AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, method_base, __ AddInt64(v)); return Address(reg, kDummy32BitOffset, fixup); } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 9eb97658da..7350fcc48a 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -415,7 +415,9 @@ class CodeGeneratorX86 : public CodeGenerator { void RecordBootTypePatch(HLoadClass* load_class); Label* NewTypeBssEntryPatch(HLoadClass* load_class); Label* NewStringBssEntryPatch(HLoadString* load_string); - Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); + Label* NewPcRelativeDexCacheArrayPatch(HX86ComputeBaseMethodAddress* method_address, + const DexFile& dex_file, + uint32_t element_offset); Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index, Handle<mirror::String> handle); @@ -463,22 +465,22 @@ class CodeGeneratorX86 : public CodeGenerator { return isa_features_; } - void SetMethodAddressOffset(int32_t offset) { - method_address_offset_ = offset; + void AddMethodAddressOffset(HX86ComputeBaseMethodAddress* method_base, int32_t offset) { + method_address_offset_.Put(method_base->GetId(), offset); } - int32_t GetMethodAddressOffset() const { - return method_address_offset_; + int32_t GetMethodAddressOffset(HX86ComputeBaseMethodAddress* method_base) const { + return method_address_offset_.Get(method_base->GetId()); } int32_t ConstantAreaStart() const { return constant_area_start_; } - Address LiteralDoubleAddress(double v, Register reg); - Address LiteralFloatAddress(float v, Register reg); - Address LiteralInt32Address(int32_t v, Register reg); - Address LiteralInt64Address(int64_t v, Register reg); + Address LiteralDoubleAddress(double v, HX86ComputeBaseMethodAddress* method_base, Register reg); + Address LiteralFloatAddress(float v, HX86ComputeBaseMethodAddress* method_base, Register reg); + Address LiteralInt32Address(int32_t v, HX86ComputeBaseMethodAddress* method_base, Register reg); + Address LiteralInt64Address(int64_t v, HX86ComputeBaseMethodAddress* method_base, Register reg); // Load a 32-bit value into a register in the most efficient manner. void Load32BitValue(Register dest, int32_t value); @@ -603,12 +605,21 @@ class CodeGeneratorX86 : public CodeGenerator { static constexpr int32_t kDummy32BitOffset = 256; private: - Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); + struct X86PcRelativePatchInfo : PatchInfo<Label> { + X86PcRelativePatchInfo(HX86ComputeBaseMethodAddress* address, + const DexFile& target_dex_file, + uint32_t target_index) + : PatchInfo(target_dex_file, target_index), + method_address(address) {} + HX86ComputeBaseMethodAddress* method_address; + }; template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> - void EmitPcRelativeLinkerPatches(const ArenaDeque<PatchInfo<Label>>& infos, + void EmitPcRelativeLinkerPatches(const ArenaDeque<X86PcRelativePatchInfo>& infos, ArenaVector<LinkerPatch>* linker_patches); + Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); + // Labels for each block that will be compiled. Label* block_labels_; // Indexed by block id. Label frame_entry_label_; @@ -619,15 +630,15 @@ class CodeGeneratorX86 : public CodeGenerator { const X86InstructionSetFeatures& isa_features_; // PC-relative DexCache access info. - ArenaDeque<PatchInfo<Label>> pc_relative_dex_cache_patches_; + ArenaDeque<X86PcRelativePatchInfo> pc_relative_dex_cache_patches_; // Patch locations for patchoat where the linker doesn't do any other work. ArenaDeque<Label> simple_patches_; // String patch locations; type depends on configuration (app .bss or boot image PIC/non-PIC). - ArenaDeque<PatchInfo<Label>> string_patches_; + ArenaDeque<X86PcRelativePatchInfo> string_patches_; // Type patch locations for boot image; type depends on configuration (boot image PIC/non-PIC). - ArenaDeque<PatchInfo<Label>> boot_image_type_patches_; + ArenaDeque<X86PcRelativePatchInfo> boot_image_type_patches_; // Type patch locations for kBssEntry. - ArenaDeque<PatchInfo<Label>> type_bss_entry_patches_; + ArenaDeque<X86PcRelativePatchInfo> type_bss_entry_patches_; // Patches for string root accesses in JIT compiled code. ArenaDeque<PatchInfo<Label>> jit_string_patches_; @@ -642,11 +653,9 @@ class CodeGeneratorX86 : public CodeGenerator { // Fixups for jump tables that need to be patched after the constant table is generated. ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_; - // If there is a HX86ComputeBaseMethodAddress instruction in the graph - // (which shall be the sole instruction of this kind), subtracting this offset - // from the value contained in the out register of this HX86ComputeBaseMethodAddress - // instruction gives the address of the start of this method. - int32_t method_address_offset_; + // Maps a HX86ComputeBaseMethodAddress instruction id, to its offset in the + // compiled code. + ArenaSafeMap<uint32_t, int32_t> method_address_offset_; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86); }; diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 922c3bcac9..e1b7ea53b4 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -356,23 +356,28 @@ static void CreateFloatToFloat(ArenaAllocator* arena, HInvoke* invoke) { } } -static void MathAbsFP(LocationSummary* locations, +static void MathAbsFP(HInvoke* invoke, bool is64bit, X86Assembler* assembler, CodeGeneratorX86* codegen) { + LocationSummary* locations = invoke->GetLocations(); Location output = locations->Out(); DCHECK(output.IsFpuRegister()); if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) { + HX86ComputeBaseMethodAddress* method_address = + invoke->InputAt(1)->AsX86ComputeBaseMethodAddress(); DCHECK(locations->InAt(1).IsRegister()); // We also have a constant area pointer. Register constant_area = locations->InAt(1).AsRegister<Register>(); XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); if (is64bit) { - __ movsd(temp, codegen->LiteralInt64Address(INT64_C(0x7FFFFFFFFFFFFFFF), constant_area)); + __ movsd(temp, codegen->LiteralInt64Address( + INT64_C(0x7FFFFFFFFFFFFFFF), method_address, constant_area)); __ andpd(output.AsFpuRegister<XmmRegister>(), temp); } else { - __ movss(temp, codegen->LiteralInt32Address(INT32_C(0x7FFFFFFF), constant_area)); + __ movss(temp, codegen->LiteralInt32Address( + INT32_C(0x7FFFFFFF), method_address, constant_area)); __ andps(output.AsFpuRegister<XmmRegister>(), temp); } } else { @@ -396,7 +401,7 @@ void IntrinsicLocationsBuilderX86::VisitMathAbsDouble(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86::VisitMathAbsDouble(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler(), codegen_); + MathAbsFP(invoke, /* is64bit */ true, GetAssembler(), codegen_); } void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) { @@ -404,7 +409,7 @@ void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86::VisitMathAbsFloat(HInvoke* invoke) { - MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler(), codegen_); + MathAbsFP(invoke, /* is64bit */ false, GetAssembler(), codegen_); } static void CreateAbsIntLocation(ArenaAllocator* arena, HInvoke* invoke) { @@ -486,11 +491,12 @@ void IntrinsicCodeGeneratorX86::VisitMathAbsLong(HInvoke* invoke) { GenAbsLong(invoke->GetLocations(), GetAssembler()); } -static void GenMinMaxFP(LocationSummary* locations, +static void GenMinMaxFP(HInvoke* invoke, bool is_min, bool is_double, X86Assembler* assembler, CodeGeneratorX86* codegen) { + LocationSummary* locations = invoke->GetLocations(); Location op1_loc = locations->InAt(0); Location op2_loc = locations->InAt(1); Location out_loc = locations->Out(); @@ -553,12 +559,14 @@ static void GenMinMaxFP(LocationSummary* locations, __ Bind(&nan); // Do we have a constant area pointer? if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) { + HX86ComputeBaseMethodAddress* method_address = + invoke->InputAt(2)->AsX86ComputeBaseMethodAddress(); DCHECK(locations->InAt(2).IsRegister()); Register constant_area = locations->InAt(2).AsRegister<Register>(); if (is_double) { - __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, constant_area)); + __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, method_address, constant_area)); } else { - __ movss(out, codegen->LiteralInt32Address(kFloatNaN, constant_area)); + __ movss(out, codegen->LiteralInt32Address(kFloatNaN, method_address, constant_area)); } } else { if (is_double) { @@ -608,7 +616,7 @@ void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), + GenMinMaxFP(invoke, /* is_min */ true, /* is_double */ true, GetAssembler(), @@ -620,7 +628,7 @@ void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), + GenMinMaxFP(invoke, /* is_min */ true, /* is_double */ false, GetAssembler(), @@ -632,7 +640,7 @@ void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), + GenMinMaxFP(invoke, /* is_min */ false, /* is_double */ true, GetAssembler(), @@ -644,7 +652,7 @@ void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), + GenMinMaxFP(invoke, /* is_min */ false, /* is_double */ false, GetAssembler(), @@ -905,10 +913,16 @@ void IntrinsicCodeGeneratorX86::VisitMathRoundFloat(HInvoke* invoke) { __ subss(t2, t1); if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) { // Direct constant area available. + HX86ComputeBaseMethodAddress* method_address = + invoke->InputAt(1)->AsX86ComputeBaseMethodAddress(); Register constant_area = locations->InAt(1).AsRegister<Register>(); - __ comiss(t2, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(0.5f), constant_area)); + __ comiss(t2, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(0.5f), + method_address, + constant_area)); __ j(kBelow, &skip_incr); - __ addss(t1, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(1.0f), constant_area)); + __ addss(t1, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(1.0f), + method_address, + constant_area)); __ Bind(&skip_incr); } else { // No constant area: go through stack. diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h index fa479760fe..75893c3129 100644 --- a/compiler/optimizing/nodes_x86.h +++ b/compiler/optimizing/nodes_x86.h @@ -71,6 +71,10 @@ class HX86FPNeg FINAL : public HExpression<2> { SetRawInputAt(1, method_base); } + HX86ComputeBaseMethodAddress* GetBaseMethodAddress() const { + return InputAt(1)->AsX86ComputeBaseMethodAddress(); + } + DECLARE_INSTRUCTION(X86FPNeg); private: diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index 2befc8ca4e..a1c916f43a 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -84,8 +84,8 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || load_kind == HLoadClass::LoadKind::kBssEntry) { - InitializePCRelativeBasePointer(); - load_class->AddSpecialInput(base_); + HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_class); + load_class->AddSpecialInput(method_address); } } @@ -93,8 +93,8 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { HLoadString::LoadKind load_kind = load_string->GetLoadKind(); if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative || load_kind == HLoadString::LoadKind::kBssEntry) { - InitializePCRelativeBasePointer(); - load_string->AddSpecialInput(base_); + HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(load_string); + load_string->AddSpecialInput(method_address); } } @@ -132,13 +132,13 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { void VisitNeg(HNeg* neg) OVERRIDE { if (Primitive::IsFloatingPointType(neg->GetType())) { // We need to replace the HNeg with a HX86FPNeg in order to address the constant area. - InitializePCRelativeBasePointer(); + HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(neg); HGraph* graph = GetGraph(); HBasicBlock* block = neg->GetBlock(); HX86FPNeg* x86_fp_neg = new (graph->GetArena()) HX86FPNeg( neg->GetType(), neg->InputAt(0), - base_, + method_address, neg->GetDexPc()); block->ReplaceAndRemoveInstructionWith(neg, x86_fp_neg); } @@ -151,35 +151,44 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } // We need to replace the HPackedSwitch with a HX86PackedSwitch in order to // address the constant area. - InitializePCRelativeBasePointer(); + HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(switch_insn); HGraph* graph = GetGraph(); HBasicBlock* block = switch_insn->GetBlock(); HX86PackedSwitch* x86_switch = new (graph->GetArena()) HX86PackedSwitch( switch_insn->GetStartValue(), switch_insn->GetNumEntries(), switch_insn->InputAt(0), - base_, + method_address, switch_insn->GetDexPc()); block->ReplaceAndRemoveInstructionWith(switch_insn, x86_switch); } - void InitializePCRelativeBasePointer() { - // Ensure we only initialize the pointer once. - if (base_ != nullptr) { - return; + HX86ComputeBaseMethodAddress* GetPCRelativeBasePointer(HInstruction* cursor) { + bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); + if (!has_irreducible_loops) { + // Ensure we only initialize the pointer once. + if (base_ != nullptr) { + return base_; + } } // Insert the base at the start of the entry block, move it to a better // position later in MoveBaseIfNeeded(). - base_ = new (GetGraph()->GetArena()) HX86ComputeBaseMethodAddress(); - HBasicBlock* entry_block = GetGraph()->GetEntryBlock(); - entry_block->InsertInstructionBefore(base_, entry_block->GetFirstInstruction()); - DCHECK(base_ != nullptr); + HX86ComputeBaseMethodAddress* method_address = + new (GetGraph()->GetArena()) HX86ComputeBaseMethodAddress(); + if (has_irreducible_loops) { + cursor->GetBlock()->InsertInstructionBefore(method_address, cursor); + } else { + HBasicBlock* entry_block = GetGraph()->GetEntryBlock(); + entry_block->InsertInstructionBefore(method_address, entry_block->GetFirstInstruction()); + base_ = method_address; + } + return method_address; } void ReplaceInput(HInstruction* insn, HConstant* value, int input_index, bool materialize) { - InitializePCRelativeBasePointer(); + HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(insn); HX86LoadFromConstantTable* load_constant = - new (GetGraph()->GetArena()) HX86LoadFromConstantTable(base_, value); + new (GetGraph()->GetArena()) HX86LoadFromConstantTable(method_address, value); if (!materialize) { load_constant->MarkEmittedAtUseSite(); } @@ -204,9 +213,9 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { if (invoke_static_or_direct != nullptr && invoke_static_or_direct->HasPcRelativeDexCache() && !IsCallFreeIntrinsic<IntrinsicLocationsBuilderX86>(invoke, codegen_)) { - InitializePCRelativeBasePointer(); - // Add the extra parameter base_. - invoke_static_or_direct->AddSpecialInput(base_); + HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(invoke); + // Add the extra parameter. + invoke_static_or_direct->AddSpecialInput(method_address); base_added = true; } @@ -231,8 +240,8 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { if (!base_added) { DCHECK(invoke_static_or_direct != nullptr); DCHECK(!invoke_static_or_direct->HasCurrentMethodInput()); - InitializePCRelativeBasePointer(); - invoke_static_or_direct->AddSpecialInput(base_); + HX86ComputeBaseMethodAddress* method_address = GetPCRelativeBasePointer(invoke); + invoke_static_or_direct->AddSpecialInput(method_address); } break; default: @@ -243,16 +252,12 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { CodeGeneratorX86* codegen_; // The generated HX86ComputeBaseMethodAddress in the entry block needed as an - // input to the HX86LoadFromConstantTable instructions. + // input to the HX86LoadFromConstantTable instructions. Only set for + // graphs with reducible loops. HX86ComputeBaseMethodAddress* base_; }; void PcRelativeFixups::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; - } PCRelativeHandlerVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBaseIfNeeded(); diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp new file mode 100644 index 0000000000..cf4c99ec6d --- /dev/null +++ b/dexoptanalyzer/Android.bp @@ -0,0 +1,68 @@ +// +// 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. +// + +cc_defaults { + name: "dexoptanalyzer-defaults", + host_supported: true, + defaults: ["art_defaults"], + srcs: [ + "dexoptanalyzer.cc", + ], + + target: { + android: { + compile_multilib: "prefer32", + }, + }, + + include_dirs: [ + "art/cmdline", + ], + + shared_libs: [ + "libbase", + ], +} + +art_cc_binary { + name: "dexoptanalyzer", + defaults: ["dexoptanalyzer-defaults"], + shared_libs: [ + "libart", + ], +} + +art_cc_binary { + name: "dexoptanalyzerd", + defaults: [ + "dexoptanalyzer-defaults", + "art_debug_defaults", + ], + shared_libs: [ + "libartd", + ], +} + +art_cc_test { + name: "art_dexoptanalyzer_tests", + defaults: [ + "art_gtest_defaults", + ], + shared_libs: [ + "libbacktrace" + ], + srcs: ["dexoptanalyzer_test.cc"], +} diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc new file mode 100644 index 0000000000..965e4073ea --- /dev/null +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -0,0 +1,265 @@ +/* + * 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 <string> + +#include "android-base/stringprintf.h" +#include "android-base/strings.h" +#include "compiler_filter.h" +#include "dex_file.h" +#include "noop_compiler_callbacks.h" +#include "oat_file_assistant.h" +#include "os.h" +#include "runtime.h" +#include "thread-inl.h" +#include "utils.h" + +namespace art { + +// See OatFileAssistant docs for the meaning of the valid return codes. +enum ReturnCodes { + kNoDexOptNeeded = 0, + kDex2OatFromScratch = 1, + kDex2OatForBootImageOat = 2, + kDex2OatForFilterOat = 3, + kDex2OatForRelocationOat = 4, + kDex2OatForBootImageOdex = 5, + kDex2OatForFilterOdex = 6, + kDex2OatForRelocationOdex = 7, + + kErrorInvalidArguments = 101, + kErrorCannotCreateRuntime = 102, + kErrorUnknownDexOptNeeded = 103 +}; + +static int original_argc; +static char** original_argv; + +static std::string CommandLine() { + std::vector<std::string> command; + for (int i = 0; i < original_argc; ++i) { + command.push_back(original_argv[i]); + } + return android::base::Join(command, ' '); +} + +static void UsageErrorV(const char* fmt, va_list ap) { + std::string error; + android::base::StringAppendV(&error, fmt, ap); + LOG(ERROR) << error; +} + +static void UsageError(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); +} + +NO_RETURN static void Usage(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); + + UsageError("Command: %s", CommandLine().c_str()); + UsageError(" Performs a dexopt analysis on the given dex file and returns whether or not"); + UsageError(" the dex file needs to be dexopted."); + UsageError("Usage: dexoptanalyzer [options]..."); + UsageError(""); + UsageError(" --dex-file=<filename>: the dex file which should be analyzed."); + UsageError(""); + UsageError(" --isa=<string>: the instruction set for which the analysis should be performed."); + UsageError(""); + UsageError(" --compiler-filter=<string>: the target compiler filter to be used as reference"); + UsageError(" when deciding if the dex file needs to be optimized."); + UsageError(""); + UsageError(" --assume-profile-changed: assumes the profile information has changed"); + UsageError(" when deciding if the dex file needs to be optimized."); + UsageError(""); + UsageError(" --image=<filename>: optional, the image to be used to decide if the associated"); + UsageError(" oat file is up to date. Defaults to $ANDROID_ROOT/framework/boot.art."); + UsageError(" Example: --image=/system/framework/boot.art"); + UsageError(""); + UsageError(" --android-data=<directory>: optional, the directory which should be used as"); + UsageError(" android-data. By default ANDROID_DATA env variable is used."); + UsageError(""); + UsageError("Return code:"); + UsageError(" To make it easier to integrate with the internal tools this command will make"); + UsageError(" available its result (dexoptNeeded) as the exit/return code. i.e. it will not"); + UsageError(" return 0 for success and a non zero values for errors as the conventional"); + UsageError(" commands. The following return codes are possible:"); + UsageError(" kNoDexOptNeeded = 0"); + UsageError(" kDex2OatFromScratch = 1"); + UsageError(" kDex2OatForBootImageOat = 2"); + UsageError(" kDex2OatForFilterOat = 3"); + UsageError(" kDex2OatForRelocationOat = 4"); + UsageError(" kDex2OatForBootImageOdex = 5"); + UsageError(" kDex2OatForFilterOdex = 6"); + UsageError(" kDex2OatForRelocationOdex = 7"); + + UsageError(" kErrorInvalidArguments = 101"); + UsageError(" kErrorCannotCreateRuntime = 102"); + UsageError(" kErrorUnknownDexOptNeeded = 103"); + UsageError(""); + + exit(kErrorInvalidArguments); +} + +class DexoptAnalyzer FINAL { + public: + DexoptAnalyzer() : assume_profile_changed_(false) {} + + void ParseArgs(int argc, char **argv) { + original_argc = argc; + original_argv = argv; + + InitLogging(argv, Runtime::Aborter); + // Skip over the command name. + argv++; + argc--; + + if (argc == 0) { + Usage("No arguments specified"); + } + + for (int i = 0; i < argc; ++i) { + const StringPiece option(argv[i]); + if (option == "--assume-profile-changed") { + assume_profile_changed_ = true; + } else if (option.starts_with("--dex-file=")) { + dex_file_ = option.substr(strlen("--dex-file=")).ToString(); + } else if (option.starts_with("--compiler-filter=")) { + std::string filter_str = option.substr(strlen("--compiler-filter=")).ToString(); + if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &compiler_filter_)) { + Usage("Invalid compiler filter '%s'", option.data()); + } + } else if (option.starts_with("--isa=")) { + std::string isa_str = option.substr(strlen("--isa=")).ToString(); + isa_ = GetInstructionSetFromString(isa_str.c_str()); + if (isa_ == kNone) { + Usage("Invalid isa '%s'", option.data()); + } + } else if (option.starts_with("--image=")) { + image_ = option.substr(strlen("--image=")).ToString(); + } else if (option.starts_with("--android-data=")) { + // Overwrite android-data if needed (oat file assistant relies on a valid directory to + // compute dalvik-cache folder). This is mostly used in tests. + std::string new_android_data = option.substr(strlen("--android-data=")).ToString(); + setenv("ANDROID_DATA", new_android_data.c_str(), 1); + } else { + Usage("Unknown argument '%s'", option.data()); + } + } + + if (image_.empty()) { + // If we don't receive the image, try to use the default one. + // Tests may specify a different image (e.g. core image). + std::string error_msg; + image_ = GetDefaultBootImageLocation(&error_msg); + + if (image_.empty()) { + LOG(ERROR) << error_msg; + Usage("--image unspecified and ANDROID_ROOT not set or image file does not exist."); + } + } + } + + bool CreateRuntime() { + RuntimeOptions options; + // The image could be custom, so make sure we explicitly pass it. + std::string img = "-Ximage:" + image_; + options.push_back(std::make_pair(img.c_str(), nullptr)); + // The instruction set of the image should match the instruction set we will test. + const void* isa_opt = reinterpret_cast<const void*>(GetInstructionSetString(isa_)); + options.push_back(std::make_pair("imageinstructionset", isa_opt)); + // Disable libsigchain. We don't don't need it to evaluate DexOptNeeded status. + options.push_back(std::make_pair("-Xno-sig-chain", nullptr)); + // Pretend we are a compiler so that we can re-use the same infrastructure to load a different + // ISA image and minimize the amount of things that get started. + NoopCompilerCallbacks callbacks; + options.push_back(std::make_pair("compilercallbacks", &callbacks)); + // Make sure we don't attempt to relocate. The tool should only retrieve the DexOptNeeded + // status and not attempt to relocate the boot image. + options.push_back(std::make_pair("-Xnorelocate", nullptr)); + + if (!Runtime::Create(options, false)) { + LOG(ERROR) << "Unable to initialize runtime"; + return false; + } + // Runtime::Create acquired the mutator_lock_ that is normally given away when we + // Runtime::Start. Give it away now. + Thread::Current()->TransitionFromRunnableToSuspended(kNative); + + return true; + } + + int GetDexOptNeeded() { + // If the file does not exist there's nothing to do. + // This is a fast path to avoid creating the runtime (b/34385298). + if (!OS::FileExists(dex_file_.c_str())) { + return kNoDexOptNeeded; + } + if (!CreateRuntime()) { + return kErrorCannotCreateRuntime; + } + OatFileAssistant oat_file_assistant(dex_file_.c_str(), isa_, /*load_executable*/ false); + // Always treat elements of the bootclasspath as up-to-date. + // TODO(calin): this check should be in OatFileAssistant. + if (oat_file_assistant.IsInBootClassPath()) { + return kNoDexOptNeeded; + } + int dexoptNeeded = oat_file_assistant.GetDexOptNeeded( + compiler_filter_, assume_profile_changed_); + + // Convert OatFileAssitant codes to dexoptanalyzer codes. + switch (dexoptNeeded) { + case OatFileAssistant::kNoDexOptNeeded: return kNoDexOptNeeded; + case OatFileAssistant::kDex2OatFromScratch: return kDex2OatFromScratch; + case OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOat; + case OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOat; + case OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOat; + + case -OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOdex; + case -OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOdex; + case -OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOdex; + default: + LOG(ERROR) << "Unknown dexoptNeeded " << dexoptNeeded; + return kErrorUnknownDexOptNeeded; + } + } + + private: + std::string dex_file_; + InstructionSet isa_; + CompilerFilter::Filter compiler_filter_; + bool assume_profile_changed_; + std::string image_; +}; + +static int dexoptAnalyze(int argc, char** argv) { + DexoptAnalyzer analyzer; + + // Parse arguments. Argument mistakes will lead to exit(kErrorInvalidArguments) in UsageError. + analyzer.ParseArgs(argc, argv); + return analyzer.GetDexOptNeeded(); +} + +} // namespace art + +int main(int argc, char **argv) { + return art::dexoptAnalyze(argc, argv); +} diff --git a/dexoptanalyzer/dexoptanalyzer_test.cc b/dexoptanalyzer/dexoptanalyzer_test.cc new file mode 100644 index 0000000000..57d3f1f68b --- /dev/null +++ b/dexoptanalyzer/dexoptanalyzer_test.cc @@ -0,0 +1,311 @@ +/* + * 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 <gtest/gtest.h> + +#include "arch/instruction_set.h" +#include "compiler_filter.h" +#include "dexopt_test.h" + +namespace art { + +class DexoptAnalyzerTest : public DexoptTest { + protected: + std::string GetDexoptAnalyzerCmd() { + std::string file_path = GetTestAndroidRoot(); + file_path += "/bin/dexoptanalyzer"; + if (kIsDebugBuild) { + file_path += "d"; + } + EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; + return file_path; + } + + int Analyze(const std::string& dex_file, + CompilerFilter::Filter compiler_filter, + bool assume_profile_changed) { + std::string dexoptanalyzer_cmd = GetDexoptAnalyzerCmd(); + std::vector<std::string> argv_str; + argv_str.push_back(dexoptanalyzer_cmd); + argv_str.push_back("--dex-file=" + dex_file); + argv_str.push_back("--isa=" + std::string(GetInstructionSetString(kRuntimeISA))); + argv_str.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(compiler_filter)); + if (assume_profile_changed) { + argv_str.push_back("--assume-profile-changed"); + } + argv_str.push_back("--image=" + GetImageLocation()); + argv_str.push_back("--android-data=" + android_data_); + + std::string error; + return ExecAndReturnCode(argv_str, &error); + } + + int DexoptanalyzerToOatFileAssistant(int dexoptanalyzerResult) { + switch (dexoptanalyzerResult) { + case 0: return OatFileAssistant::kNoDexOptNeeded; + case 1: return OatFileAssistant::kDex2OatFromScratch; + case 2: return OatFileAssistant::kDex2OatForBootImage; + case 3: return OatFileAssistant::kDex2OatForFilter; + case 4: return OatFileAssistant::kDex2OatForRelocation; + case 5: return -OatFileAssistant::kDex2OatForBootImage; + case 6: return -OatFileAssistant::kDex2OatForFilter; + case 7: return -OatFileAssistant::kDex2OatForRelocation; + default: return dexoptanalyzerResult; + } + } + + // Verify that the output of dexoptanalyzer for the given arguments is the same + // as the output of OatFileAssistant::GetDexOptNeeded. + void Verify(const std::string& dex_file, + CompilerFilter::Filter compiler_filter, + bool assume_profile_changed = false) { + int dexoptanalyzerResult = Analyze(dex_file, compiler_filter, assume_profile_changed); + dexoptanalyzerResult = DexoptanalyzerToOatFileAssistant(dexoptanalyzerResult); + OatFileAssistant oat_file_assistant(dex_file.c_str(), kRuntimeISA, /*load_executable*/ false); + int assistantResult = oat_file_assistant.GetDexOptNeeded( + compiler_filter, assume_profile_changed); + EXPECT_EQ(assistantResult, dexoptanalyzerResult); + } +}; + +// The tests below exercise the same test case from oat_file_assistant_test.cc. + +// Case: We have a DEX file, but no OAT file for it. +TEST_F(DexoptAnalyzerTest, DexNoOat) { + std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; + Copy(GetDexSrc1(), dex_location); + + Verify(dex_location, CompilerFilter::kSpeed); + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kInterpretOnly); + Verify(dex_location, CompilerFilter::kSpeedProfile); +} + +// Case: We have a DEX file and up-to-date OAT file for it. +TEST_F(DexoptAnalyzerTest, OatUpToDate) { + std::string dex_location = GetScratchDir() + "/OatUpToDate.jar"; + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + + Verify(dex_location, CompilerFilter::kSpeed); + Verify(dex_location, CompilerFilter::kInterpretOnly); + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kEverything); +} + +// Case: We have a DEX file and speed-profile OAT file for it. +TEST_F(DexoptAnalyzerTest, ProfileOatUpToDate) { + std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar"; + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile); + + Verify(dex_location, CompilerFilter::kSpeedProfile, false); + Verify(dex_location, CompilerFilter::kInterpretOnly, false); + Verify(dex_location, CompilerFilter::kSpeedProfile, true); + Verify(dex_location, CompilerFilter::kInterpretOnly, true); +} + +// Case: We have a MultiDEX file and up-to-date OAT file for it. +TEST_F(DexoptAnalyzerTest, MultiDexOatUpToDate) { + std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar"; + Copy(GetMultiDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + + Verify(dex_location, CompilerFilter::kSpeed, false); +} + +// Case: We have a MultiDEX file where the secondary dex file is out of date. +TEST_F(DexoptAnalyzerTest, MultiDexSecondaryOutOfDate) { + std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar"; + + // Compile code for GetMultiDexSrc1. + Copy(GetMultiDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + + // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum + // is out of date. + Copy(GetMultiDexSrc2(), dex_location); + + Verify(dex_location, CompilerFilter::kSpeed, false); +} + + +// Case: We have a DEX file and an OAT file out of date with respect to the +// dex checksum. +TEST_F(DexoptAnalyzerTest, OatDexOutOfDate) { + std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar"; + + // We create a dex, generate an oat for it, then overwrite the dex with a + // different dex to make the oat out of date. + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + Copy(GetDexSrc2(), dex_location); + + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kSpeed); +} + +// Case: We have a DEX file and an OAT file out of date with respect to the +// boot image. +TEST_F(DexoptAnalyzerTest, OatImageOutOfDate) { + std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), + CompilerFilter::kSpeed, + /*relocate*/true, + /*pic*/false, + /*with_alternate_image*/true); + + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kInterpretOnly); + Verify(dex_location, CompilerFilter::kSpeed); +} + +// Case: We have a DEX file and a verify-at-runtime OAT file out of date with +// respect to the boot image. +// It shouldn't matter that the OAT file is out of date, because it is +// verify-at-runtime. +TEST_F(DexoptAnalyzerTest, OatVerifyAtRuntimeImageOutOfDate) { + std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), + CompilerFilter::kVerifyAtRuntime, + /*relocate*/true, + /*pic*/false, + /*with_alternate_image*/true); + + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kInterpretOnly); +} + +// Case: We have a DEX file and an ODEX file, but no OAT file. +TEST_F(DexoptAnalyzerTest, DexOdexNoOat) { + std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; + + Copy(GetDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kSpeed); +} + +// Case: We have a stripped DEX file and a PIC ODEX file, but no OAT file. +TEST_F(DexoptAnalyzerTest, StrippedDexOdexNoOat) { + std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/StrippedDexOdexNoOat.odex"; + + Copy(GetDexSrc1(), dex_location); + GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + + // Strip the dex file + Copy(GetStrippedDexSrc1(), dex_location); + + Verify(dex_location, CompilerFilter::kSpeed); +} + +// Case: We have a stripped DEX file, a PIC ODEX file, and an out-of-date OAT file. +TEST_F(DexoptAnalyzerTest, StrippedDexOdexOat) { + std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar"; + std::string odex_location = GetOdexDir() + "/StrippedDexOdexOat.odex"; + + // Create the oat file from a different dex file so it looks out of date. + Copy(GetDexSrc2(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + + // Create the odex file + Copy(GetDexSrc1(), dex_location); + GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + + // Strip the dex file. + Copy(GetStrippedDexSrc1(), dex_location); + + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kSpeed); + Verify(dex_location, CompilerFilter::kEverything); +} + +// Case: We have a stripped (or resource-only) DEX file, no ODEX file and no +// OAT file. Expect: The status is kNoDexOptNeeded. +TEST_F(DexoptAnalyzerTest, ResourceOnlyDex) { + std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar"; + + Copy(GetStrippedDexSrc1(), dex_location); + + Verify(dex_location, CompilerFilter::kSpeed); + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kInterpretOnly); +} + +// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and +// OAT files both have patch delta of 0. +TEST_F(DexoptAnalyzerTest, OdexOatOverlap) { + std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar"; + std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex"; + std::string oat_location = GetOdexDir() + "/OdexOatOverlap.oat"; + + Copy(GetDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + + // Create the oat file by copying the odex so they are located in the same + // place in memory. + Copy(odex_location, oat_location); + + Verify(dex_location, CompilerFilter::kSpeed); +} + +// Case: We have a DEX file and a PIC ODEX file, but no OAT file. +TEST_F(DexoptAnalyzerTest, DexPicOdexNoOat) { + std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/DexPicOdexNoOat.odex"; + + Copy(GetDexSrc1(), dex_location); + GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed); + + Verify(dex_location, CompilerFilter::kSpeed); + Verify(dex_location, CompilerFilter::kEverything); +} + +// Case: We have a DEX file and a VerifyAtRuntime ODEX file, but no OAT file.. +TEST_F(DexoptAnalyzerTest, DexVerifyAtRuntimeOdexNoOat) { + std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex"; + + Copy(GetDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerifyAtRuntime); + + Verify(dex_location, CompilerFilter::kVerifyAtRuntime); + Verify(dex_location, CompilerFilter::kSpeed); +} + +// Case: Non-standard extension for dex file. +TEST_F(DexoptAnalyzerTest, LongDexExtension) { + std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx"; + Copy(GetDexSrc1(), dex_location); + + Verify(dex_location, CompilerFilter::kSpeed); +} + +// Case: Very short, non-existent Dex location. +TEST_F(DexoptAnalyzerTest, ShortDexLocation) { + std::string dex_location = "/xx"; + + Verify(dex_location, CompilerFilter::kSpeed); +} + +} // namespace art diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 92f1e18827..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); @@ -1431,6 +1550,60 @@ class OatDumper { } 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(), 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) { @@ -1468,6 +1641,7 @@ class OatDumper { const InstructionSet instruction_set_; std::set<uintptr_t> offsets_; Disassembler* disassembler_; + Stats stats_; }; class ImageDumper { @@ -2140,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; @@ -2169,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), @@ -2367,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" @@ -2375,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/Android.bp b/runtime/Android.bp index 81f174e43b..7f985139bb 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -474,10 +474,14 @@ art_cc_library { art_cc_library { name: "libart-runtime-gtest", defaults: ["libart-gtest-defaults"], - srcs: ["common_runtime_test.cc"], + srcs: [ + "common_runtime_test.cc", + "dexopt_test.cc" + ], shared_libs: [ "libartd", "libbase", + "libbacktrace" ], } diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index b9f688de57..917db3e486 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -52,7 +52,7 @@ inline uint32_t ArtField::GetAccessFlags() { } inline MemberOffset ArtField::GetOffset() { - DCHECK(GetDeclaringClass()->IsResolved() || GetDeclaringClass()->IsErroneous()); + DCHECK(GetDeclaringClass()->IsResolved()); return MemberOffset(offset_); } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 15938c52be..a35c7ab701 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -109,8 +109,7 @@ inline uint32_t ArtMethod::GetAccessFlags() { } inline uint16_t ArtMethod::GetMethodIndex() { - DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsResolved() || - GetDeclaringClass()->IsErroneous()); + DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsResolved()); return method_index_; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 2e258beb48..02b26c6568 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -19,6 +19,7 @@ #include <algorithm> #include <deque> #include <iostream> +#include <map> #include <memory> #include <queue> #include <string> @@ -1340,7 +1341,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( // The image space is not yet added to the heap, avoid read barriers. ObjPtr<mirror::Class> klass = types[j].Read(); if (space->HasAddress(klass.Ptr())) { - DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + DCHECK(!klass->IsErroneous()) << klass->GetStatus(); auto it = new_class_set->Find(ClassTable::TableSlot(klass)); DCHECK(it != new_class_set->end()); DCHECK_EQ(it->Read(), klass); @@ -1703,7 +1704,7 @@ bool ClassLinker::AddImageSpace( for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) { ObjPtr<mirror::Class> klass = types[j].Read(); if (klass != nullptr) { - DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + DCHECK(!klass->IsErroneous()) << klass->GetStatus(); } } } else { @@ -2232,7 +2233,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, // For temporary classes we must wait for them to be retired. if (init_done_ && klass->IsTemp()) { CHECK(!klass->IsResolved()); - if (klass->IsErroneous()) { + if (klass->IsErroneousUnresolved()) { ThrowEarlierClassFailure(klass); return nullptr; } @@ -2240,10 +2241,10 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, Handle<mirror::Class> h_class(hs.NewHandle(klass)); ObjectLock<mirror::Class> lock(self, h_class); // Loop and wait for the resolving thread to retire this class. - while (!h_class->IsRetired() && !h_class->IsErroneous()) { + while (!h_class->IsRetired() && !h_class->IsErroneousUnresolved()) { lock.WaitIgnoringInterrupts(); } - if (h_class->IsErroneous()) { + if (h_class->IsErroneousUnresolved()) { ThrowEarlierClassFailure(h_class.Get()); return nullptr; } @@ -2258,7 +2259,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, static const size_t kNumYieldIterations = 1000; // How long each sleep is in us. static const size_t kSleepDurationUS = 1000; // 1 ms. - while (!klass->IsResolved() && !klass->IsErroneous()) { + while (!klass->IsResolved() && !klass->IsErroneousUnresolved()) { StackHandleScope<1> hs(self); HandleWrapperObjPtr<mirror::Class> h_class(hs.NewHandleWrapper(&klass)); { @@ -2269,7 +2270,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, // Check for circular dependencies between classes, the lock is required for SetStatus. if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) { ThrowClassCircularityError(h_class.Get()); - mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(h_class, mirror::Class::kStatusErrorUnresolved, self); return nullptr; } } @@ -2286,7 +2287,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, ++index; } - if (klass->IsErroneous()) { + if (klass->IsErroneousUnresolved()) { ThrowEarlierClassFailure(klass); return nullptr; } @@ -2636,13 +2637,30 @@ 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); + // Check to see if an exception happened during runtime callbacks. Return if so. + if (self->IsExceptionPending()) { + return nullptr; + } + 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_)) { @@ -2668,23 +2686,23 @@ 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 // notification is necessary. if (!klass->IsErroneous()) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); } return nullptr; } // 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); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); } return nullptr; } @@ -2703,13 +2721,13 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) { // Linking failed. if (!klass->IsErroneous()) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); } return nullptr; } self->AssertNoPendingException(); CHECK(h_new_class.Get() != nullptr) << descriptor; - CHECK(h_new_class->IsResolved()) << descriptor; + CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor; // Instrumentation may have updated entrypoints for all methods of all // classes. However it could not update methods of this class while we @@ -3804,7 +3822,7 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self, } // Need to grab the lock to change status. ObjectLock<mirror::Class> super_lock(self, klass); - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); return false; } @@ -3926,8 +3944,8 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass( bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status); // If the oat file says the class had an error, re-run the verifier. That way we will get a // precise error message. To ensure a rerun, test: - // oat_file_class_status == mirror::Class::kStatusError => !preverified - DCHECK(!(oat_file_class_status == mirror::Class::kStatusError) || !preverified); + // mirror::Class::IsErroneous(oat_file_class_status) => !preverified + DCHECK(!mirror::Class::IsErroneous(oat_file_class_status) || !preverified); std::string error_msg; verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure; @@ -3985,7 +4003,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass( << " because: " << error_msg; self->AssertNoPendingException(); ThrowVerifyError(klass.Get(), "%s", error_msg.c_str()); - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); } if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) { // Class is verified so we don't need to do any access check on its methods. @@ -4076,7 +4094,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, // at compile time). return false; } - if (oat_file_class_status == mirror::Class::kStatusError) { + if (mirror::Class::IsErroneous(oat_file_class_status)) { // Compile time verification failed with a hard error. This is caused by invalid instructions // in the class. These errors are unrecoverable. return false; @@ -4235,7 +4253,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& Handle<mirror::ObjectArray<mirror::Class>> h_interfaces( hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces))); if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); return nullptr; } } @@ -4450,7 +4468,8 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, return false; } - CHECK(klass->IsResolved()) << klass->PrettyClass() << ": state=" << klass->GetStatus(); + CHECK(klass->IsResolved() && !klass->IsErroneousResolved()) + << klass->PrettyClass() << ": state=" << klass->GetStatus(); if (!klass->IsVerified()) { VerifyClass(self, klass); @@ -4485,7 +4504,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, // A separate thread could have moved us all the way to initialized. A "simple" example // involves a subclass of the current class being initialized at the same time (which // will implicitly initialize the superclass, if scheduled that way). b/28254258 - DCHECK_NE(mirror::Class::kStatusError, klass->GetStatus()); + DCHECK(!klass->IsErroneous()) << klass->GetStatus(); if (klass->IsInitialized()) { return true; } @@ -4512,7 +4531,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, } if (!ValidateSuperClassDescriptors(klass)) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); return false; } self->AllowThreadSuspension(); @@ -4548,7 +4567,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, << (self->GetException() != nullptr ? self->GetException()->Dump() : ""); ObjectLock<mirror::Class> lock(self, klass); // Initialization failed because the super-class is erroneous. - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); return false; } } @@ -4579,7 +4598,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, if (!iface_initialized) { ObjectLock<mirror::Class> lock(self, klass); // Initialization failed because one of our interfaces with default methods is erroneous. - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); return false; } } @@ -4652,7 +4671,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, if (self->IsExceptionPending()) { WrapExceptionInInitializer(klass); - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); success = false; } else if (Runtime::Current()->IsTransactionAborted()) { // The exception thrown when the transaction aborted has been caught and cleared @@ -4661,7 +4680,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, << mirror::Class::PrettyDescriptor(klass.Get()) << " without exception while transaction was aborted: re-throw it now."; Runtime::Current()->ThrowTransactionAbortError(self); - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); success = false; } else { RuntimeStats* global_stats = Runtime::Current()->GetStats(); @@ -4745,7 +4764,7 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, // we were not using WaitIgnoringInterrupts), bail out. if (self->IsExceptionPending()) { WrapExceptionInInitializer(klass); - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); return false; } // Spurious wakeup? Go back to waiting. @@ -5156,7 +5175,7 @@ bool ClassLinker::LinkClass(Thread* self, klass->SetIFieldsPtrUnchecked(nullptr); if (UNLIKELY(h_new_class.Get() == nullptr)) { self->AssertPendingOOMException(); - mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); + mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); return false; } @@ -7768,7 +7787,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, } } } - DCHECK((resolved == nullptr) || resolved->IsResolved() || resolved->IsErroneous()) + DCHECK((resolved == nullptr) || resolved->IsResolved()) << resolved->PrettyDescriptor() << " " << resolved->GetStatus(); return resolved.Ptr(); } @@ -8465,71 +8484,94 @@ void ClassLinker::CleanupClassLoaders() { } } -std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_boot_classes) { - ScopedTrace trace(__PRETTY_FUNCTION__); - ScopedObjectAccess soa(Thread::Current()); - ScopedAssertNoThreadSuspension ants(__FUNCTION__); - std::set<DexCacheResolvedClasses> ret; - VLOG(class_linker) << "Collecting resolved classes"; - const uint64_t start_time = NanoTime(); - ReaderMutexLock mu(soa.Self(), *Locks::dex_lock_); - // Loop through all the dex caches and inspect resolved classes. - for (const ClassLinker::DexCacheData& data : GetDexCachesData()) { - if (soa.Self()->IsJWeakCleared(data.weak_root)) { - continue; - } - ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(data.weak_root); - if (dex_cache == nullptr) { - continue; - } - const DexFile* dex_file = dex_cache->GetDexFile(); - const std::string& location = dex_file->GetLocation(); - const size_t num_class_defs = dex_file->NumClassDefs(); - // Use the resolved types, this will miss array classes. - const size_t num_types = dex_file->NumTypeIds(); - VLOG(class_linker) << "Collecting class profile for dex file " << location - << " types=" << num_types << " class_defs=" << num_class_defs; - DexCacheResolvedClasses resolved_classes(dex_file->GetLocation(), - dex_file->GetBaseLocation(), - dex_file->GetLocationChecksum()); - size_t num_resolved = 0; - std::unordered_set<dex::TypeIndex> class_set; - CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); - for (size_t i = 0; i < num_types; ++i) { - ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(i)); - // Filter out null class loader since that is the boot class loader. - if (klass == nullptr || (ignore_boot_classes && klass->GetClassLoader() == nullptr)) { - continue; - } - ++num_resolved; - DCHECK(!klass->IsProxyClass()); - if (!klass->IsResolved()) { - DCHECK(klass->IsErroneous()); - continue; +class GetResolvedClassesVisitor : public ClassVisitor { + public: + GetResolvedClassesVisitor(std::set<DexCacheResolvedClasses>* result, bool ignore_boot_classes) + : result_(result), + ignore_boot_classes_(ignore_boot_classes), + last_resolved_classes_(result->end()), + last_dex_file_(nullptr), + vlog_is_on_(VLOG_IS_ON(class_linker)), + extra_stats_(), + last_extra_stats_(extra_stats_.end()) { } + + bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + if (!klass->IsProxyClass() && + !klass->IsArrayClass() && + klass->IsResolved() && + !klass->IsErroneousResolved() && + (!ignore_boot_classes_ || klass->GetClassLoader() != nullptr)) { + const DexFile& dex_file = klass->GetDexFile(); + if (&dex_file != last_dex_file_) { + last_dex_file_ = &dex_file; + DexCacheResolvedClasses resolved_classes(dex_file.GetLocation(), + dex_file.GetBaseLocation(), + dex_file.GetLocationChecksum()); + last_resolved_classes_ = result_->find(resolved_classes); + if (last_resolved_classes_ == result_->end()) { + last_resolved_classes_ = result_->insert(resolved_classes).first; + } } - ObjPtr<mirror::DexCache> klass_dex_cache = klass->GetDexCache(); - if (klass_dex_cache == dex_cache) { - DCHECK(klass->IsResolved()); - CHECK_LT(klass->GetDexClassDefIndex(), num_class_defs); - class_set.insert(klass->GetDexTypeIndex()); + bool added = last_resolved_classes_->AddClass(klass->GetDexTypeIndex()); + if (UNLIKELY(vlog_is_on_) && added) { + const DexCacheResolvedClasses* resolved_classes = std::addressof(*last_resolved_classes_); + if (last_extra_stats_ == extra_stats_.end() || + last_extra_stats_->first != resolved_classes) { + last_extra_stats_ = extra_stats_.find(resolved_classes); + if (last_extra_stats_ == extra_stats_.end()) { + last_extra_stats_ = + extra_stats_.emplace(resolved_classes, ExtraStats(dex_file.NumClassDefs())).first; + } + } } } + return true; + } - if (!class_set.empty()) { - auto it = ret.find(resolved_classes); - if (it != ret.end()) { - // Already have the key, union the class type indexes. - it->AddClasses(class_set.begin(), class_set.end()); - } else { - resolved_classes.AddClasses(class_set.begin(), class_set.end()); - ret.insert(resolved_classes); + void PrintStatistics() const { + if (vlog_is_on_) { + for (const DexCacheResolvedClasses& resolved_classes : *result_) { + auto it = extra_stats_.find(std::addressof(resolved_classes)); + DCHECK(it != extra_stats_.end()); + const ExtraStats& extra_stats = it->second; + LOG(INFO) << "Dex location " << resolved_classes.GetDexLocation() + << " has " << resolved_classes.GetClasses().size() << " / " + << extra_stats.number_of_class_defs_ << " resolved classes"; } } + } + + private: + struct ExtraStats { + explicit ExtraStats(uint32_t number_of_class_defs) + : number_of_class_defs_(number_of_class_defs) {} + uint32_t number_of_class_defs_; + }; + + std::set<DexCacheResolvedClasses>* result_; + bool ignore_boot_classes_; + std::set<DexCacheResolvedClasses>::iterator last_resolved_classes_; + const DexFile* last_dex_file_; - VLOG(class_linker) << "Dex location " << location << " has " << num_resolved << " / " - << num_class_defs << " resolved classes"; + // Statistics. + bool vlog_is_on_; + std::map<const DexCacheResolvedClasses*, ExtraStats> extra_stats_; + std::map<const DexCacheResolvedClasses*, ExtraStats>::iterator last_extra_stats_; +}; + +std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_boot_classes) { + ScopedTrace trace(__PRETTY_FUNCTION__); + ScopedObjectAccess soa(Thread::Current()); + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + std::set<DexCacheResolvedClasses> ret; + VLOG(class_linker) << "Collecting resolved classes"; + const uint64_t start_time = NanoTime(); + GetResolvedClassesVisitor visitor(&ret, ignore_boot_classes); + VisitClasses(&visitor); + if (VLOG_IS_ON(class_linker)) { + visitor.PrintStatistics(); + LOG(INFO) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time); } - VLOG(class_linker) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time); return ret; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 8da979b36f..5042fb7609 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_class_def 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 7b6c0dc510..e806e7d608 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -87,6 +87,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_FALSE(primitive->IsErroneous()); EXPECT_TRUE(primitive->IsLoaded()); EXPECT_TRUE(primitive->IsResolved()); + EXPECT_FALSE(primitive->IsErroneousResolved()); EXPECT_TRUE(primitive->IsVerified()); EXPECT_TRUE(primitive->IsInitialized()); EXPECT_FALSE(primitive->IsArrayInstance()); @@ -125,6 +126,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_FALSE(JavaLangObject->IsErroneous()); EXPECT_TRUE(JavaLangObject->IsLoaded()); EXPECT_TRUE(JavaLangObject->IsResolved()); + EXPECT_FALSE(JavaLangObject->IsErroneousResolved()); EXPECT_TRUE(JavaLangObject->IsVerified()); EXPECT_TRUE(JavaLangObject->IsInitialized()); EXPECT_FALSE(JavaLangObject->IsArrayInstance()); @@ -199,6 +201,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_FALSE(array->IsErroneous()); EXPECT_TRUE(array->IsLoaded()); EXPECT_TRUE(array->IsResolved()); + EXPECT_FALSE(array->IsErroneousResolved()); EXPECT_TRUE(array->IsVerified()); EXPECT_TRUE(array->IsInitialized()); EXPECT_FALSE(array->IsArrayInstance()); @@ -270,6 +273,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_TRUE(klass->GetDexCache() != nullptr); EXPECT_TRUE(klass->IsLoaded()); EXPECT_TRUE(klass->IsResolved()); + EXPECT_FALSE(klass->IsErroneousResolved()); EXPECT_FALSE(klass->IsErroneous()); EXPECT_FALSE(klass->IsArrayClass()); EXPECT_TRUE(klass->GetComponentType() == nullptr); @@ -743,6 +747,8 @@ struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>( false, "Ljava/lang/invoke/MethodHandle;") { addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, art_field_or_method_), "artFieldOrMethod"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, cached_spread_invoker_), + "cachedSpreadInvoker"); addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, handle_kind_), "handleKind"); addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, nominal_type_), "nominalType"); addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, method_type_), "type"); @@ -857,6 +863,7 @@ TEST_F(ClassLinkerTest, FindClass) { EXPECT_FALSE(MyClass->IsErroneous()); EXPECT_TRUE(MyClass->IsLoaded()); EXPECT_TRUE(MyClass->IsResolved()); + EXPECT_FALSE(MyClass->IsErroneousResolved()); EXPECT_FALSE(MyClass->IsVerified()); EXPECT_FALSE(MyClass->IsInitialized()); EXPECT_FALSE(MyClass->IsArrayInstance()); @@ -941,6 +948,47 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { array_klass); } +TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<3> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("ErroneousInit")))); + AssertNonExistentClass("LErroneousInit;"); + Handle<mirror::Class> klass = + hs.NewHandle(class_linker_->FindClass(soa.Self(), "LErroneousInit;", class_loader)); + ASSERT_OBJ_PTR_NE(klass.Get(), ObjPtr<mirror::Class>(nullptr)); + dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; + Handle<mirror::DexCache> dex_cache = hs.NewHandle(klass->GetDexCache()); + const DexFile& dex_file = klass->GetDexFile(); + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + klass.Get()); + // Zero out the resolved type and make sure LookupResolvedType still finds it. + dex_cache->SetResolvedType(type_idx, nullptr); + EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + klass.Get()); + // Force initialization to turn the class erroneous. + bool initialized = class_linker_->EnsureInitialized(soa.Self(), + klass, + /* can_init_fields */ true, + /* can_init_parents */ true); + EXPECT_FALSE(initialized); + EXPECT_TRUE(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); + // Check that the LookupResolvedType() can still find the resolved type. + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + klass.Get()); + // Zero out the resolved type and make sure LookupResolvedType() still finds it. + dex_cache->SetResolvedType(type_idx, nullptr); + EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); + EXPECT_OBJ_PTR_EQ( + class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + klass.Get()); +} + TEST_F(ClassLinkerTest, LibCore) { ScopedObjectAccess soa(Thread::Current()); ASSERT_TRUE(java_lang_dex_file_ != nullptr); diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h index f53ca4acb6..bebdf0dbfe 100644 --- a/runtime/dex_cache_resolved_classes.h +++ b/runtime/dex_cache_resolved_classes.h @@ -44,6 +44,10 @@ class DexCacheResolvedClasses { return dex_location_.compare(other.dex_location_); } + bool AddClass(dex::TypeIndex index) const { + return classes_.insert(index).second; + } + template <class InputIt> void AddClasses(InputIt begin, InputIt end) const { classes_.insert(begin, end); diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc new file mode 100644 index 0000000000..69c6151d9e --- /dev/null +++ b/runtime/dexopt_test.cc @@ -0,0 +1,236 @@ +/* + * 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 <string> +#include <vector> + +#include <backtrace/BacktraceMap.h> +#include <gtest/gtest.h> + +#include "common_runtime_test.h" +#include "compiler_callbacks.h" +#include "dex2oat_environment_test.h" +#include "dexopt_test.h" +#include "gc/space/image_space.h" +#include "mem_map.h" + +namespace art { +void DexoptTest::SetUp() { + ReserveImageSpace(); + Dex2oatEnvironmentTest::SetUp(); +} + +void DexoptTest::PreRuntimeCreate() { + std::string error_msg; + ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg; + ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg; + UnreserveImageSpace(); +} + +void DexoptTest::PostRuntimeCreate() { + ReserveImageSpace(); +} + +void DexoptTest::GenerateOatForTest(const std::string& dex_location, + const std::string& oat_location, + CompilerFilter::Filter filter, + bool relocate, + bool pic, + bool with_alternate_image) { + std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA)); + std::string dalvik_cache_tmp = dalvik_cache + ".redirected"; + + if (!relocate) { + // Temporarily redirect the dalvik cache so dex2oat doesn't find the + // relocated image file. + ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno); + } + + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + oat_location); + args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); + args.push_back("--runtime-arg"); + + // Use -Xnorelocate regardless of the relocate argument. + // We control relocation by redirecting the dalvik cache when needed + // rather than use this flag. + args.push_back("-Xnorelocate"); + + if (pic) { + args.push_back("--compile-pic"); + } + + std::string image_location = GetImageLocation(); + if (with_alternate_image) { + args.push_back("--boot-image=" + GetImageLocation2()); + } + + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + if (!relocate) { + // Restore the dalvik cache if needed. + ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno); + } + + // Verify the odex file was generated as expected. + std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(), + oat_location.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + dex_location.c_str(), + &error_msg)); + ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; + EXPECT_EQ(pic, odex_file->IsPic()); + EXPECT_EQ(filter, odex_file->GetCompilerFilter()); + + std::unique_ptr<ImageHeader> image_header( + gc::space::ImageSpace::ReadImageHeader(image_location.c_str(), + kRuntimeISA, + &error_msg)); + ASSERT_TRUE(image_header != nullptr) << error_msg; + const OatHeader& oat_header = odex_file->GetOatHeader(); + uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum(); + + if (CompilerFilter::DependsOnImageChecksum(filter)) { + if (with_alternate_image) { + EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); + } else { + EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); + } + } + + if (!with_alternate_image) { + if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) { + if (relocate) { + EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()), + oat_header.GetImageFileLocationOatDataBegin()); + EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta()); + } else { + EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()), + oat_header.GetImageFileLocationOatDataBegin()); + EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta()); + } + } + } +} + +void DexoptTest::GenerateOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter) { + GenerateOatForTest(dex_location, + odex_location, + filter, + /*relocate*/false, + /*pic*/false, + /*with_alternate_image*/false); +} + +void DexoptTest::GeneratePicOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter) { + GenerateOatForTest(dex_location, + odex_location, + filter, + /*relocate*/false, + /*pic*/true, + /*with_alternate_image*/false); +} + +void DexoptTest::GenerateOatForTest(const char* dex_location, + CompilerFilter::Filter filter, + bool relocate, + bool pic, + bool with_alternate_image) { + std::string oat_location; + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( + dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; + GenerateOatForTest(dex_location, + oat_location, + filter, + relocate, + pic, + with_alternate_image); +} + +void DexoptTest::GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) { + GenerateOatForTest(dex_location, + filter, + /*relocate*/true, + /*pic*/false, + /*with_alternate_image*/false); +} + +bool DexoptTest::PreRelocateImage(const std::string& image_location, std::string* error_msg) { + std::string image; + if (!GetCachedImageFile(image_location, &image, error_msg)) { + return false; + } + + std::string patchoat = GetAndroidRoot(); + patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat"; + + std::vector<std::string> argv; + argv.push_back(patchoat); + argv.push_back("--input-image-location=" + image_location); + argv.push_back("--output-image-file=" + image); + argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); + argv.push_back("--base-offset-delta=0x00008000"); + return Exec(argv, error_msg); +} + +void DexoptTest::ReserveImageSpace() { + MemMap::Init(); + + // Ensure a chunk of memory is reserved for the image space. + // The reservation_end includes room for the main space that has to come + // right after the image in case of the GSS collector. + uintptr_t reservation_start = ART_BASE_ADDRESS; + uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB; + + std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true)); + ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map"; + for (BacktraceMap::const_iterator it = map->begin(); + reservation_start < reservation_end && it != map->end(); ++it) { + ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end)); + reservation_start = std::max(reservation_start, it->end); + } + ReserveImageSpaceChunk(reservation_start, reservation_end); +} + +void DexoptTest::ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) { + if (start < end) { + std::string error_msg; + image_reservation_.push_back(std::unique_ptr<MemMap>( + MemMap::MapAnonymous("image reservation", + reinterpret_cast<uint8_t*>(start), end - start, + PROT_NONE, false, false, &error_msg))); + ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg; + LOG(INFO) << "Reserved space for image " << + reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" << + reinterpret_cast<void*>(image_reservation_.back()->End()); + } +} + +void DexoptTest::UnreserveImageSpace() { + image_reservation_.clear(); +} + +} // namespace art diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h new file mode 100644 index 0000000000..5f0eafd8eb --- /dev/null +++ b/runtime/dexopt_test.h @@ -0,0 +1,97 @@ +/* + * 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_DEXOPT_TEST_H_ +#define ART_RUNTIME_DEXOPT_TEST_H_ + +#include <string> +#include <vector> + +#include "dex2oat_environment_test.h" + +namespace art { + +class DexoptTest : public Dex2oatEnvironmentTest { + public: + virtual void SetUp() OVERRIDE; + + virtual void PreRuntimeCreate(); + + virtual void PostRuntimeCreate() OVERRIDE; + + // Generate an oat file for the purposes of test. + // The oat file will be generated for dex_location in the given oat_location + // with the following configuration: + // filter - controls the compilation filter + // pic - whether or not the code will be PIC + // relocate - if true, the oat file will be relocated with respect to the + // boot image. Otherwise the oat file will not be relocated. + // with_alternate_image - if true, the oat file will be generated with an + // image checksum different than the current image checksum. + void GenerateOatForTest(const std::string& dex_location, + const std::string& oat_location, + CompilerFilter::Filter filter, + bool relocate, + bool pic, + bool with_alternate_image); + + // Generate a non-PIC odex file for the purposes of test. + // The generated odex file will be un-relocated. + void GenerateOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter); + + void GeneratePicOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter); + + // Generate an oat file for the given dex location in its oat location (under + // the dalvik cache). + void GenerateOatForTest(const char* dex_location, + CompilerFilter::Filter filter, + bool relocate, + bool pic, + bool with_alternate_image); + + // Generate a standard oat file in the oat location. + void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter); + + private: + // Pre-Relocate the image to a known non-zero offset so we don't have to + // deal with the runtime randomly relocating the image by 0 and messing up + // the expected results of the tests. + bool PreRelocateImage(const std::string& image_location, std::string* error_msg); + + // Reserve memory around where the image will be loaded so other memory + // won't conflict when it comes time to load the image. + // This can be called with an already loaded image to reserve the space + // around it. + void ReserveImageSpace(); + + // Reserve a chunk of memory for the image space in the given range. + // Only has effect for chunks with a positive number of bytes. + void ReserveImageSpaceChunk(uintptr_t start, uintptr_t end); + + // Unreserve any memory reserved by ReserveImageSpace. This should be called + // before the image is loaded. + void UnreserveImageSpace(); + + std::vector<std::unique_ptr<MemMap>> image_reservation_; +}; + +} // namespace art + +#endif // ART_RUNTIME_DEXOPT_TEST_H_ diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h index 5ddb9fa27a..0471c969f9 100644 --- a/runtime/experimental_flags.h +++ b/runtime/experimental_flags.h @@ -26,8 +26,6 @@ struct ExperimentalFlags { // The actual flag values. enum { kNone = 0x0000, - kAgents = 0x0001, // 0b00000001 - kRuntimePlugins = 0x0002, // 0b00000010 kMethodHandles = 0x0004, // 0b00000100 }; @@ -67,14 +65,6 @@ struct ExperimentalFlags { inline std::ostream& operator<<(std::ostream& stream, const ExperimentalFlags& e) { bool started = false; - if (e & ExperimentalFlags::kAgents) { - stream << (started ? "|" : "") << "kAgents"; - started = true; - } - if (e & ExperimentalFlags::kRuntimePlugins) { - stream << (started ? "|" : "") << "kRuntimePlugins"; - started = true; - } if (e & ExperimentalFlags::kMethodHandles) { stream << (started ? "|" : "") << "kMethodHandles"; started = true; diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 268cca0cfd..70449797c1 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3543,8 +3543,11 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran, collector::GcType gc_type = collector_ran->GetGcType(); const double multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for // foreground. - const uint64_t adjusted_min_free = static_cast<uint64_t>(min_free_ * multiplier); - const uint64_t adjusted_max_free = static_cast<uint64_t>(max_free_ * multiplier); + // Ensure at least 2.5 MB to temporarily fix excessive GC caused by TLAB ergonomics. + const uint64_t adjusted_min_free = std::max(static_cast<uint64_t>(min_free_ * multiplier), + static_cast<uint64_t>(5 * MB / 2)); + const uint64_t adjusted_max_free = std::max(static_cast<uint64_t>(max_free_ * multiplier), + static_cast<uint64_t>(5 * MB / 2)); if (gc_type != collector::kGcTypeSticky) { // Grow the heap for non sticky GC. ssize_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated; diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index fe6a6e9140..3d3ad593b3 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -1169,7 +1169,7 @@ void Hprof::DumpHeapObject(mirror::Object* obj) { } void Hprof::DumpHeapClass(mirror::Class* klass) { - if (!klass->IsResolved() && !klass->IsErroneous()) { + if (!klass->IsResolved()) { // Class is allocated but not yet resolved: we cannot access its fields or super class. return; } diff --git a/runtime/image.cc b/runtime/image.cc index 6d8889532f..54b099eb14 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -25,7 +25,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '5', '\0' }; // ArtMethod update +const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '6', '\0' }; // Erroneous resolved class. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 4ea1130947..bbd6d352d3 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -88,11 +88,11 @@ Instrumentation::Instrumentation() } void Instrumentation::InstallStubsForClass(mirror::Class* klass) { - if (klass->IsErroneous()) { - // We can't execute code in a erroneous class: do nothing. - } else if (!klass->IsResolved()) { + if (!klass->IsResolved()) { // We need the class to be resolved to install/uninstall stubs. Otherwise its methods // could not be initialized or linked with regards to class inheritance. + } else if (klass->IsErroneousResolved()) { + // We can't execute code in a erroneous class: do nothing. } else { for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) { InstallStubsForMethod(&method); diff --git a/runtime/memory_region.cc b/runtime/memory_region.cc index a5c70c3b26..5bf0f40eff 100644 --- a/runtime/memory_region.cc +++ b/runtime/memory_region.cc @@ -33,4 +33,36 @@ void MemoryRegion::CopyFrom(size_t offset, const MemoryRegion& from) const { from.pointer(), from.size()); } +void MemoryRegion::StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) { + DCHECK_LE(value, MaxInt<uint32_t>(length)); + DCHECK_LE(length, BitSizeOf<uint32_t>()); + DCHECK_LE(bit_offset + length, size_in_bits()); + if (length == 0) { + return; + } + // Bits are stored in this order {7 6 5 4 3 2 1 0}. + // How many remaining bits in current byte is (bit_offset % kBitsPerByte) + 1. + uint8_t* out = ComputeInternalPointer<uint8_t>(bit_offset >> kBitsPerByteLog2); + auto orig_len = length; + auto orig_value = value; + uintptr_t bit_remainder = bit_offset % kBitsPerByte; + while (true) { + const uintptr_t remaining_bits = kBitsPerByte - bit_remainder; + if (length <= remaining_bits) { + // Length is smaller than all of remainder bits. + size_t mask = ((1 << length) - 1) << bit_remainder; + *out = (*out & ~mask) | (value << bit_remainder); + break; + } + // Copy remaining bits in current byte. + size_t value_mask = (1 << remaining_bits) - 1; + *out = (*out & ~(value_mask << bit_remainder)) | ((value & value_mask) << bit_remainder); + value >>= remaining_bits; + bit_remainder = 0; + length -= remaining_bits; + ++out; + } + DCHECK_EQ(LoadBits(bit_offset, orig_len), orig_value) << bit_offset << " " << orig_len; +} + } // namespace art diff --git a/runtime/memory_region.h b/runtime/memory_region.h index fe3f917c76..f55dff7a50 100644 --- a/runtime/memory_region.h +++ b/runtime/memory_region.h @@ -124,11 +124,35 @@ class MemoryRegion FINAL : public ValueObject { // The bit at the smallest offset is the least significant bit in the // loaded value. `length` must not be larger than the number of bits // contained in the return value (32). - uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { - CHECK_LE(length, sizeof(uint32_t) * kBitsPerByte); - uint32_t value = 0u; + ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { + DCHECK_LE(length, BitSizeOf<uint32_t>()); + DCHECK_LE(bit_offset + length, size_in_bits()); + if (UNLIKELY(length == 0)) { + // Do not touch any memory if the range is empty. + return 0; + } + const uint8_t* address = start() + bit_offset / kBitsPerByte; + const uint32_t shift = bit_offset & (kBitsPerByte - 1); + // Load the value (reading only the strictly needed bytes). + const uint32_t load_bit_count = shift + length; + uint32_t value = address[0] >> shift; + if (load_bit_count > 8) { + value |= static_cast<uint32_t>(address[1]) << (8 - shift); + if (load_bit_count > 16) { + value |= static_cast<uint32_t>(address[2]) << (16 - shift); + if (load_bit_count > 24) { + value |= static_cast<uint32_t>(address[3]) << (24 - shift); + if (load_bit_count > 32) { + value |= static_cast<uint32_t>(address[4]) << (32 - shift); + } + } + } + } + // Clear unwanted most significant bits. + uint32_t clear_bit_count = BitSizeOf(value) - length; + value = (value << clear_bit_count) >> clear_bit_count; for (size_t i = 0; i < length; ++i) { - value |= LoadBit(bit_offset + i) << i; + DCHECK_EQ((value >> i) & 1, LoadBit(bit_offset + i)); } return value; } @@ -137,13 +161,7 @@ class MemoryRegion FINAL : public ValueObject { // `bit_offset`. The bit at the smallest offset is the least significant // bit of the stored `value`. `value` must not be larger than `length` // bits. - void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) { - CHECK_LE(value, MaxInt<uint32_t>(length)); - for (size_t i = 0; i < length; ++i) { - bool ith_bit = value & (1 << i); - StoreBit(bit_offset + i, ith_bit); - } - } + void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length); void CopyFrom(size_t offset, const MemoryRegion& from) const; diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index a5db0c0a8a..f56226bd98 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -207,6 +207,19 @@ inline void PrimitiveArray<T>::VisitRoots(RootVisitor* visitor) { } template<typename T> +inline PrimitiveArray<T>* PrimitiveArray<T>::AllocateAndFill(Thread* self, + const T* data, + size_t length) { + StackHandleScope<1> hs(self); + Handle<PrimitiveArray<T>> arr(hs.NewHandle(PrimitiveArray<T>::Alloc(self, length))); + if (!arr.IsNull()) { + // Copy it in. Just skip if it's null + memcpy(arr->GetData(), data, sizeof(T) * length); + } + return arr.Get(); +} + +template<typename T> inline PrimitiveArray<T>* PrimitiveArray<T>::Alloc(Thread* self, size_t length) { Array* raw_array = Array::Alloc<true>(self, GetArrayClass(), diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 19d300e1f4..16cf30f1e2 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -119,6 +119,10 @@ class MANAGED PrimitiveArray : public Array { static PrimitiveArray<T>* Alloc(Thread* self, size_t length) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + static PrimitiveArray<T>* AllocateAndFill(Thread* self, const T* data, size_t length) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + + const T* GetData() const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { return reinterpret_cast<const T*>(GetRawData(sizeof(T), 0)); } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 9964b73c98..f08d4daf95 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -115,7 +115,9 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized(); if (LIKELY(class_linker_initialized)) { - if (UNLIKELY(new_status <= old_status && new_status != kStatusError && + if (UNLIKELY(new_status <= old_status && + new_status != kStatusErrorUnresolved && + new_status != kStatusErrorResolved && new_status != kStatusRetired)) { LOG(FATAL) << "Unexpected change back of class status for " << h_this->PrettyClass() << " " << old_status << " -> " << new_status; @@ -127,10 +129,12 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) { << h_this->PrettyClass() << " " << old_status << " -> " << new_status; } } - if (UNLIKELY(new_status == kStatusError)) { - CHECK_NE(h_this->GetStatus(), kStatusError) + if (UNLIKELY(IsErroneous(new_status))) { + CHECK(!h_this->IsErroneous()) << "Attempt to set as erroneous an already erroneous class " - << h_this->PrettyClass(); + << h_this->PrettyClass() + << " old_status: " << old_status << " new_status: " << new_status; + CHECK_EQ(new_status == kStatusErrorResolved, old_status >= kStatusResolved); if (VLOG_IS_ON(class_linker)) { LOG(ERROR) << "Setting " << h_this->PrettyDescriptor() << " to erroneous."; if (self->IsExceptionPending()) { @@ -177,7 +181,7 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) { // Class is a temporary one, ensure that waiters for resolution get notified of retirement // so that they can grab the new version of the class from the class linker's table. CHECK_LT(new_status, kStatusResolved) << h_this->PrettyDescriptor(); - if (new_status == kStatusRetired || new_status == kStatusError) { + if (new_status == kStatusRetired || new_status == kStatusErrorUnresolved) { h_this->NotifyAll(self); } } else { @@ -305,7 +309,7 @@ void Class::DumpClass(std::ostream& os, int flags) { } if (h_this->NumStaticFields() > 0) { os << " static fields (" << h_this->NumStaticFields() << " entries):\n"; - if (h_this->IsResolved() || h_this->IsErroneous()) { + if (h_this->IsResolved()) { for (size_t i = 0; i < h_this->NumStaticFields(); ++i) { os << StringPrintf(" %2zd: %s\n", i, ArtField::PrettyField(h_this->GetStaticField(i)).c_str()); @@ -316,7 +320,7 @@ void Class::DumpClass(std::ostream& os, int flags) { } if (h_this->NumInstanceFields() > 0) { os << " instance fields (" << h_this->NumInstanceFields() << " entries):\n"; - if (h_this->IsResolved() || h_this->IsErroneous()) { + if (h_this->IsResolved()) { for (size_t i = 0; i < h_this->NumInstanceFields(); ++i) { os << StringPrintf(" %2zd: %s\n", i, ArtField::PrettyField(h_this->GetInstanceField(i)).c_str()); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index fb2792a316..7f6aa12fdb 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -84,6 +84,13 @@ class MANAGED Class FINAL : public Object { // will be gc'ed once all refs to the class point to the newly // cloned version. // + // kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need + // to distinguish between classes that have been resolved and classes that + // have not. This is important because the const-class instruction needs to + // return a previously resolved class even if its subsequent initialization + // failed. We also need this to decide whether to wrap a previous + // initialization failure in ClassDefNotFound error or not. + // // kStatusNotReady: If a Class cannot be found in the class table by // FindClass, it allocates an new one with AllocClass in the // kStatusNotReady and calls LoadClass. Note if it does find a @@ -119,8 +126,9 @@ class MANAGED Class FINAL : public Object { // // TODO: Explain the other states enum Status { - kStatusRetired = -2, // Retired, should not be used. Use the newly cloned one instead. - kStatusError = -1, + kStatusRetired = -3, // Retired, should not be used. Use the newly cloned one instead. + kStatusErrorResolved = -2, + kStatusErrorUnresolved = -1, kStatusNotReady = 0, kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_. kStatusLoaded = 2, // DEX idx values resolved. @@ -158,8 +166,25 @@ class MANAGED Class FINAL : public Object { // Returns true if the class has failed to link. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + bool IsErroneousUnresolved() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetStatus<kVerifyFlags>() == kStatusErrorUnresolved; + } + + // Returns true if the class has failed to initialize. + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + bool IsErroneousResolved() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetStatus<kVerifyFlags>() == kStatusErrorResolved; + } + + // Returns true if the class status indicets that the class has failed to link or initialize. + static bool IsErroneous(Status status) { + return status == kStatusErrorUnresolved || status == kStatusErrorResolved; + } + + // Returns true if the class has failed to link or initialize. + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsErroneous() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() == kStatusError; + return IsErroneous(GetStatus<kVerifyFlags>()); } // Returns true if the class has been loaded. @@ -177,7 +202,8 @@ class MANAGED Class FINAL : public Object { // Returns true if the class has been linked. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsResolved() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() >= kStatusResolved; + Status status = GetStatus<kVerifyFlags>(); + return status >= kStatusResolved || status == kStatusErrorResolved; } // Returns true if the class was compile-time verified. @@ -345,7 +371,7 @@ class MANAGED Class FINAL : public Object { // be replaced with a class with the right size for embedded imt/vtable. bool IsTemp() REQUIRES_SHARED(Locks::mutator_lock_) { Status s = GetStatus(); - return s < Status::kStatusResolving && ShouldHaveEmbeddedVTable(); + return s < Status::kStatusResolving && s != kStatusErrorResolved && ShouldHaveEmbeddedVTable(); } String* GetName() REQUIRES_SHARED(Locks::mutator_lock_); // Returns the cached name. @@ -1017,7 +1043,7 @@ class MANAGED Class FINAL : public Object { // Returns the number of instance fields containing reference types. Does not count fields in any // super classes. uint32_t NumReferenceInstanceFields() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(IsResolved() || IsErroneous()); + DCHECK(IsResolved()); return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_)); } @@ -1045,7 +1071,7 @@ class MANAGED Class FINAL : public Object { // Returns the number of static fields containing reference types. uint32_t NumReferenceStaticFields() REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(IsResolved() || IsErroneous()); + DCHECK(IsResolved()); return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_)); } diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index abe999a5ac..2f26a22ca8 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -84,10 +84,12 @@ class MANAGED MethodHandle : public Object { static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); private: + // NOTE: cached_spread_invoker_ isn't used by the runtime. + HeapReference<mirror::MethodHandle> cached_spread_invoker_; HeapReference<mirror::MethodType> nominal_type_; HeapReference<mirror::MethodType> method_type_; - uint64_t art_field_or_method_; uint32_t handle_kind_; + uint64_t art_field_or_method_; private: static MemberOffset NominalTypeOffset() { diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 284d2d10c5..a8fa7db688 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -81,14 +81,12 @@ static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoa if (c != nullptr && c->IsErroneous()) { cl->ThrowEarlierClassFailure(c.Ptr()); Thread* self = soa.Self(); - ObjPtr<mirror::Class> eiie_class = - self->DecodeJObject(WellKnownClasses::java_lang_ExceptionInInitializerError)->AsClass(); ObjPtr<mirror::Class> iae_class = self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass(); ObjPtr<mirror::Class> ncdfe_class = self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass(); ObjPtr<mirror::Class> exception = self->GetException()->GetClass(); - if (exception == eiie_class || exception == iae_class || exception == ncdfe_class) { + if (exception == iae_class || exception == ncdfe_class) { self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;", c->PrettyDescriptor().c_str()); } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 62d99fb51c..111755e7a1 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -201,8 +201,12 @@ class OatFile { // A representation of an invalid OatClass, used when an OatClass can't be found. // See FindOatClass(). static OatClass Invalid() { - return OatClass(nullptr, mirror::Class::kStatusError, kOatClassNoneCompiled, 0, nullptr, - nullptr); + return OatClass(/* oat_file */ nullptr, + mirror::Class::kStatusErrorUnresolved, + kOatClassNoneCompiled, + /* bitmap_size */ 0, + /* bitmap_pointer */ nullptr, + /* methods_pointer */ nullptr); } private: diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 84eacde8b7..577200847a 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -14,23 +14,16 @@ * limitations under the License. */ -#include <algorithm> -#include <fstream> #include <string> #include <vector> #include <sys/param.h> #include "android-base/strings.h" -#include <backtrace/BacktraceMap.h> #include <gtest/gtest.h> #include "art_field-inl.h" #include "class_linker-inl.h" -#include "common_runtime_test.h" -#include "compiler_callbacks.h" -#include "dex2oat_environment_test.h" -#include "gc/space/image_space.h" -#include "mem_map.h" +#include "dexopt_test.h" #include "oat_file_assistant.h" #include "oat_file_manager.h" #include "os.h" @@ -40,242 +33,17 @@ namespace art { -class OatFileAssistantTest : public Dex2oatEnvironmentTest { - public: - virtual void SetUp() OVERRIDE { - ReserveImageSpace(); - Dex2oatEnvironmentTest::SetUp(); - } - - // Pre-Relocate the image to a known non-zero offset so we don't have to - // deal with the runtime randomly relocating the image by 0 and messing up - // the expected results of the tests. - bool PreRelocateImage(const std::string& image_location, std::string* error_msg) { - std::string image; - if (!GetCachedImageFile(image_location, &image, error_msg)) { - return false; - } - - std::string patchoat = GetAndroidRoot(); - patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat"; - - std::vector<std::string> argv; - argv.push_back(patchoat); - argv.push_back("--input-image-location=" + image_location); - argv.push_back("--output-image-file=" + image); - argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); - argv.push_back("--base-offset-delta=0x00008000"); - return Exec(argv, error_msg); - } - - virtual void PreRuntimeCreate() { - std::string error_msg; - ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg; - ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg; - UnreserveImageSpace(); - } - - virtual void PostRuntimeCreate() OVERRIDE { - ReserveImageSpace(); - } +class OatFileAssistantTest : public DexoptTest {}; - // Generate an oat file for the purposes of test. - void GenerateOatForTest(const std::string& dex_location, - const std::string& oat_location, - CompilerFilter::Filter filter, - bool relocate, - bool pic, - bool with_alternate_image) { - std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA)); - std::string dalvik_cache_tmp = dalvik_cache + ".redirected"; - - if (!relocate) { - // Temporarily redirect the dalvik cache so dex2oat doesn't find the - // relocated image file. - ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno); - } - - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + oat_location); - args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); - args.push_back("--runtime-arg"); - - // Use -Xnorelocate regardless of the relocate argument. - // We control relocation by redirecting the dalvik cache when needed - // rather than use this flag. - args.push_back("-Xnorelocate"); - - if (pic) { - args.push_back("--compile-pic"); - } - - std::string image_location = GetImageLocation(); - if (with_alternate_image) { - args.push_back("--boot-image=" + GetImageLocation2()); - } - - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - - if (!relocate) { - // Restore the dalvik cache if needed. - ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno); - } - - // Verify the odex file was generated as expected. - std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(), - oat_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_EQ(pic, odex_file->IsPic()); - EXPECT_EQ(filter, odex_file->GetCompilerFilter()); - - std::unique_ptr<ImageHeader> image_header( - gc::space::ImageSpace::ReadImageHeader(image_location.c_str(), - kRuntimeISA, - &error_msg)); - ASSERT_TRUE(image_header != nullptr) << error_msg; - const OatHeader& oat_header = odex_file->GetOatHeader(); - uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum(); - - if (CompilerFilter::DependsOnImageChecksum(filter)) { - if (with_alternate_image) { - EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); - } else { - EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum()); - } - } - - if (!with_alternate_image) { - if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) { - if (relocate) { - EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()), - oat_header.GetImageFileLocationOatDataBegin()); - EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta()); - } else { - EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()), - oat_header.GetImageFileLocationOatDataBegin()); - EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta()); - } - } - } - } - - // Generate a non-PIC odex file for the purposes of test. - // The generated odex file will be un-relocated. - void GenerateOdexForTest(const std::string& dex_location, - const std::string& odex_location, - CompilerFilter::Filter filter) { - GenerateOatForTest(dex_location, - odex_location, - filter, - /*relocate*/false, - /*pic*/false, - /*with_alternate_image*/false); - } - - void GeneratePicOdexForTest(const std::string& dex_location, - const std::string& odex_location, - CompilerFilter::Filter filter) { - GenerateOatForTest(dex_location, - odex_location, - filter, - /*relocate*/false, - /*pic*/true, - /*with_alternate_image*/false); - } - - // Generate an oat file in the oat location. - void GenerateOatForTest(const char* dex_location, - CompilerFilter::Filter filter, - bool relocate, - bool pic, - bool with_alternate_image) { - std::string oat_location; - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( - dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; - GenerateOatForTest(dex_location, - oat_location, - filter, - relocate, - pic, - with_alternate_image); - } - - // Generate a standard oat file in the oat location. - void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) { - GenerateOatForTest(dex_location, - filter, - /*relocate*/true, - /*pic*/false, - /*with_alternate_image*/false); - } - - private: - // Reserve memory around where the image will be loaded so other memory - // won't conflict when it comes time to load the image. - // This can be called with an already loaded image to reserve the space - // around it. - void ReserveImageSpace() { - MemMap::Init(); - - // Ensure a chunk of memory is reserved for the image space. - // The reservation_end includes room for the main space that has to come - // right after the image in case of the GSS collector. - uintptr_t reservation_start = ART_BASE_ADDRESS; - uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB; - - std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true)); - ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map"; - for (BacktraceMap::const_iterator it = map->begin(); - reservation_start < reservation_end && it != map->end(); ++it) { - ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end)); - reservation_start = std::max(reservation_start, it->end); - } - ReserveImageSpaceChunk(reservation_start, reservation_end); - } - - // Reserve a chunk of memory for the image space in the given range. - // Only has effect for chunks with a positive number of bytes. - void ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) { - if (start < end) { - std::string error_msg; - image_reservation_.push_back(std::unique_ptr<MemMap>( - MemMap::MapAnonymous("image reservation", - reinterpret_cast<uint8_t*>(start), end - start, - PROT_NONE, false, false, &error_msg))); - ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg; - LOG(INFO) << "Reserved space for image " << - reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" << - reinterpret_cast<void*>(image_reservation_.back()->End()); - } - } - - - // Unreserve any memory reserved by ReserveImageSpace. This should be called - // before the image is loaded. - void UnreserveImageSpace() { - image_reservation_.clear(); - } - - std::vector<std::unique_ptr<MemMap>> image_reservation_; -}; - -class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest { +class OatFileAssistantNoDex2OatTest : public DexoptTest { public: virtual void SetUpRuntimeOptions(RuntimeOptions* options) { - OatFileAssistantTest::SetUpRuntimeOptions(options); + DexoptTest::SetUpRuntimeOptions(options); options->push_back(std::make_pair("-Xnodex2oat", nullptr)); } }; + // Case: We have a DEX file, but no OAT file for it. // Expect: The status is kDex2OatNeeded. TEST_F(OatFileAssistantTest, DexNoOat) { diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 256c3a6cec..91353e2788 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -114,6 +114,21 @@ static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) { } ALWAYS_INLINE +static inline 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; +} + +ALWAYS_INLINE static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) { size_t len = strlen(src) + 1; unsigned char* buf; diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h index 21ec731ba5..4f5eb0c33f 100644 --- a/runtime/openjdkjvmti/events-inl.h +++ b/runtime/openjdkjvmti/events-inl.h @@ -37,96 +37,84 @@ static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { } } -template <typename FnType> -ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) { - if (env->event_callbacks == nullptr) { - return nullptr; - } +namespace impl { - // TODO: Add a type check. Can be done, for example, by an explicitly instantiated template - // function. +// Infrastructure to achieve type safety for event dispatch. - switch (event) { - case ArtJvmtiEvent::kVmInit: - return reinterpret_cast<FnType*>(env->event_callbacks->VMInit); - case ArtJvmtiEvent::kVmDeath: - return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath); - case ArtJvmtiEvent::kThreadStart: - return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart); - case ArtJvmtiEvent::kThreadEnd: - return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd); - case ArtJvmtiEvent::kClassFileLoadHookRetransformable: - case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable: - return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook); - case ArtJvmtiEvent::kClassLoad: - return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad); - case ArtJvmtiEvent::kClassPrepare: - return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare); - case ArtJvmtiEvent::kVmStart: - return reinterpret_cast<FnType*>(env->event_callbacks->VMStart); - case ArtJvmtiEvent::kException: - return reinterpret_cast<FnType*>(env->event_callbacks->Exception); - case ArtJvmtiEvent::kExceptionCatch: - return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch); - case ArtJvmtiEvent::kSingleStep: - return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep); - case ArtJvmtiEvent::kFramePop: - return reinterpret_cast<FnType*>(env->event_callbacks->FramePop); - case ArtJvmtiEvent::kBreakpoint: - return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint); - case ArtJvmtiEvent::kFieldAccess: - return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess); - case ArtJvmtiEvent::kFieldModification: - return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification); - case ArtJvmtiEvent::kMethodEntry: - return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry); - case ArtJvmtiEvent::kMethodExit: - return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit); - case ArtJvmtiEvent::kNativeMethodBind: - return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind); - case ArtJvmtiEvent::kCompiledMethodLoad: - return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad); - case ArtJvmtiEvent::kCompiledMethodUnload: - return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload); - case ArtJvmtiEvent::kDynamicCodeGenerated: - return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated); - case ArtJvmtiEvent::kDataDumpRequest: - return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest); - case ArtJvmtiEvent::kMonitorWait: - return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait); - case ArtJvmtiEvent::kMonitorWaited: - return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited); - case ArtJvmtiEvent::kMonitorContendedEnter: - return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter); - case ArtJvmtiEvent::kMonitorContendedEntered: - return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered); - case ArtJvmtiEvent::kResourceExhausted: - return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted); - case ArtJvmtiEvent::kGarbageCollectionStart: - return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart); - case ArtJvmtiEvent::kGarbageCollectionFinish: - return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish); - case ArtJvmtiEvent::kObjectFree: - return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree); - case ArtJvmtiEvent::kVmObjectAlloc: - return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc); - } - return nullptr; -} +#define FORALL_EVENT_TYPES(fn) \ + fn(VMInit, ArtJvmtiEvent::kVmInit) \ + fn(VMDeath, ArtJvmtiEvent::kVmDeath) \ + fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \ + fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \ + fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \ + fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \ + fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \ + fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \ + fn(VMStart, ArtJvmtiEvent::kVmStart) \ + fn(Exception, ArtJvmtiEvent::kException) \ + fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \ + fn(SingleStep, ArtJvmtiEvent::kSingleStep) \ + fn(FramePop, ArtJvmtiEvent::kFramePop) \ + fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \ + fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \ + fn(FieldModification, ArtJvmtiEvent::kFieldModification) \ + fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \ + fn(MethodExit, ArtJvmtiEvent::kMethodExit) \ + fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \ + fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \ + fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \ + fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \ + fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \ + fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \ + fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \ + fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \ + fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \ + fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \ + fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ + fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ + fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ + fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) + +template <ArtJvmtiEvent kEvent> +struct EventFnType { +}; + +#define EVENT_FN_TYPE(name, enum_name) \ +template <> \ +struct EventFnType<enum_name> { \ + using type = decltype(jvmtiEventCallbacks().name); \ +}; + +FORALL_EVENT_TYPES(EVENT_FN_TYPE) -template <typename ...Args> -inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread*, - ArtJvmtiEvent event, - Args... args ATTRIBUTE_UNUSED) const { - CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable || - event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); - LOG(FATAL) << "Incorrect arguments to ClassFileLoadHook!"; +#undef EVENT_FN_TYPE + +template <ArtJvmtiEvent kEvent> +ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env); + +#define GET_CALLBACK(name, enum_name) \ +template <> \ +ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \ + ArtJvmTiEnv* env) { \ + if (env->event_callbacks == nullptr) { \ + return nullptr; \ + } \ + return env->event_callbacks->name; \ } +FORALL_EVENT_TYPES(GET_CALLBACK) + +#undef GET_CALLBACK + +#undef FORALL_EVENT_TYPES + +} // namespace impl + +// C++ does not allow partial template function specialization. The dispatch for our separated +// ClassFileLoadHook event types is the same, so use this helper for code deduplication. // TODO Locking of some type! -template <> +template <ArtJvmtiEvent kEvent> inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, - ArtJvmtiEvent event, JNIEnv* jnienv, jclass class_being_redefined, jobject loader, @@ -136,26 +124,16 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) const { - CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable || - event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); - using FnType = void(jvmtiEnv* /* jvmti_env */, - JNIEnv* /* jnienv */, - jclass /* class_being_redefined */, - jobject /* loader */, - const char* /* name */, - jobject /* protection_domain */, - jint /* class_data_len */, - const unsigned char* /* class_data */, - jint* /* new_class_data_len */, - unsigned char** /* new_class_data */); + static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || + kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); jint current_len = class_data_len; unsigned char* current_class_data = const_cast<unsigned char*>(class_data); ArtJvmTiEnv* last_env = nullptr; for (ArtJvmTiEnv* env : envs) { - if (ShouldDispatch(event, env, thread)) { - jint new_len; - unsigned char* new_data; - FnType* callback = GetCallback<FnType>(env, event); + if (ShouldDispatch<kEvent>(env, thread)) { + jint new_len = 0; + unsigned char* new_data = nullptr; + auto callback = impl::GetCallback<kEvent>(env); callback(env, jnienv, class_being_redefined, @@ -186,28 +164,16 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, } } -template <typename ...Args> +// Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match +// exactly the argument types of the corresponding Jvmti kEvent function pointer. + +template <ArtJvmtiEvent kEvent, typename ...Args> inline void EventHandler::DispatchEvent(art::Thread* thread, - ArtJvmtiEvent event, Args... args) const { - switch (event) { - case ArtJvmtiEvent::kClassFileLoadHookRetransformable: - case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable: - return DispatchClassFileLoadHookEvent(thread, event, args...); - default: - return GenericDispatchEvent(thread, event, args...); - } -} - -// TODO Locking of some type! -template <typename ...Args> -inline void EventHandler::GenericDispatchEvent(art::Thread* thread, - ArtJvmtiEvent event, - Args... args) const { using FnType = void(jvmtiEnv*, Args...); for (ArtJvmTiEnv* env : envs) { - if (ShouldDispatch(event, env, thread)) { - FnType* callback = GetCallback<FnType>(env, event); + if (ShouldDispatch<kEvent>(env, thread)) { + FnType* callback = impl::GetCallback<kEvent>(env); if (callback != nullptr) { (*callback)(env, args...); } @@ -215,14 +181,66 @@ inline void EventHandler::GenericDispatchEvent(art::Thread* thread, } } -inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event, - ArtJvmTiEnv* env, +// C++ does not allow partial template function specialization. The dispatch for our separated +// ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper. +// The following two DispatchEvent specializations dispatch to it. +template <> +inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( + art::Thread* thread, + JNIEnv* jnienv, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data) const { + return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( + thread, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + class_data_len, + class_data, + new_class_data_len, + new_class_data); +} +template <> +inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( + art::Thread* thread, + JNIEnv* jnienv, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data) const { + return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( + thread, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + class_data_len, + class_data, + new_class_data_len, + new_class_data); +} + +template <ArtJvmtiEvent kEvent> +inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread) { - bool dispatch = env->event_masks.global_event_mask.Test(event); + bool dispatch = env->event_masks.global_event_mask.Test(kEvent); - if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) { + if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) { EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); - dispatch = mask != nullptr && mask->Test(event); + dispatch = mask != nullptr && mask->Test(kEvent); } return dispatch; } diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc index d3f8001d69..1da08a0143 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -206,13 +206,12 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { ScopedLocalRef<jclass> klass( jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass())); - handler_->DispatchEvent(self, - ArtJvmtiEvent::kVmObjectAlloc, - jni_env, - thread.get(), - object.get(), - klass.get(), - static_cast<jlong>(byte_count)); + handler_->DispatchEvent<ArtJvmtiEvent::kVmObjectAlloc>(self, + reinterpret_cast<JNIEnv*>(jni_env), + thread.get(), + object.get(), + klass.get(), + static_cast<jlong>(byte_count)); } } @@ -241,11 +240,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { finish_enabled_(false) {} void StartPause() OVERRIDE { - handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart); + handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionStart>(nullptr); } void EndPause() OVERRIDE { - handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish); + handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionFinish>(nullptr); } bool IsEnabled() { diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h index 8e246ded8e..4e20d1776b 100644 --- a/runtime/openjdkjvmti/events.h +++ b/runtime/openjdkjvmti/events.h @@ -156,9 +156,9 @@ class EventHandler { ArtJvmtiEvent event, jvmtiEventMode mode); - template <typename ...Args> + template <ArtJvmtiEvent kEvent, typename ...Args> ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const; + inline void DispatchEvent(art::Thread* thread, Args... args) const; // Tell the event handler capabilities were added/lost so it can adjust the sent events.If // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false @@ -169,8 +169,9 @@ class EventHandler { bool added); private: + template <ArtJvmtiEvent kEvent> ALWAYS_INLINE - static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread); + static inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread); ALWAYS_INLINE inline bool NeedsEventUpdate(ArtJvmTiEnv* env, @@ -181,14 +182,17 @@ class EventHandler { ALWAYS_INLINE inline void RecalculateGlobalEventMask(ArtJvmtiEvent event); - template <typename ...Args> - ALWAYS_INLINE inline void GenericDispatchEvent(art::Thread* thread, - ArtJvmtiEvent event, - Args... args) const; - template <typename ...Args> + template <ArtJvmtiEvent kEvent> ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread, - ArtJvmtiEvent event, - Args... args) const; + JNIEnv* jnienv, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data) const; void HandleEventType(ArtJvmtiEvent event, bool enable); diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc index 94cb46a428..b27c2a3834 100644 --- a/runtime/openjdkjvmti/object_tagging.cc +++ b/runtime/openjdkjvmti/object_tagging.cc @@ -207,7 +207,7 @@ void ObjectTagTable::SweepImpl(art::IsMarkedVisitor* visitor) { } void ObjectTagTable::HandleNullSweep(jlong tag) { - event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag); + event_handler_->DispatchEvent<ArtJvmtiEvent::kObjectFree>(nullptr, tag); } template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull> diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 450f75a813..b6de592142 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -31,6 +31,8 @@ #include "ti_class.h" +#include "android-base/stringprintf.h" + #include <mutex> #include <unordered_set> @@ -38,34 +40,222 @@ #include "base/macros.h" #include "class_table-inl.h" #include "class_linker.h" +#include "common_throws.h" #include "events-inl.h" #include "handle.h" #include "jni_env_ext-inl.h" #include "jni_internal.h" +#include "mirror/array-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_ext.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" +#include "ti_redefine.h" +#include "utils.h" namespace openjdkjvmti { +using android::base::StringPrintf; + +static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self, + const char* descriptor, + const std::string& orig_location, + jint final_len, + const unsigned char* final_dex_data) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + // Make the mmap + std::string error_msg; + std::unique_ptr<art::MemMap> map(Redefiner::MoveDataToMemMap(orig_location, + final_len, + final_dex_data, + &error_msg)); + if (map.get() == nullptr) { + LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg; + self->ThrowOutOfMemoryError(StringPrintf( + "Unable to allocate dex file for transformation of %s", descriptor).c_str()); + return nullptr; + } + + // Make a dex-file + if (map->Size() < sizeof(art::DexFile::Header)) { + LOG(WARNING) << "Could not read dex file header because dex_data was too short"; + art::ThrowClassFormatError(nullptr, + "Unable to read transformed dex file of %s", + descriptor); + return nullptr; + } + uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_; + std::unique_ptr<const art::DexFile> dex_file(art::DexFile::Open(map->GetName(), + checksum, + std::move(map), + /*verify*/true, + /*verify_checksum*/true, + &error_msg)); + if (dex_file.get() == nullptr) { + LOG(WARNING) << "Unable to load modified dex file for " << descriptor << ": " << error_msg; + art::ThrowClassFormatError(nullptr, + "Unable to read transformed dex file of %s because %s", + descriptor, + error_msg.c_str()); + return nullptr; + } + if (dex_file->NumClassDefs() != 1) { + LOG(WARNING) << "Dex file contains more than 1 class_def. Ignoring."; + // TODO Throw some other sort of error here maybe? + art::ThrowClassFormatError( + nullptr, + "Unable to use transformed dex file of %s because it contained too many classes", + descriptor); + return nullptr; + } + return dex_file; +} + struct ClassCallback : public art::ClassLoadCallback { + void ClassPreDefine(const char* descriptor, + art::Handle<art::mirror::Class> klass, + art::Handle<art::mirror::ClassLoader> class_loader, + const art::DexFile& initial_dex_file, + const art::DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED, + /*out*/art::DexFile const** final_dex_file, + /*out*/art::DexFile::ClassDef const** final_class_def) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + bool is_enabled = + event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookRetransformable) || + event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); + if (!is_enabled) { + return; + } + if (descriptor[0] != 'L') { + // It is a primitive or array. Just return + return; + } + std::string name(art::PrettyDescriptor(descriptor)); + + art::Thread* self = art::Thread::Current(); + art::JNIEnvExt* env = self->GetJniEnv(); + ScopedLocalRef<jobject> loader( + env, class_loader.IsNull() ? nullptr : env->AddLocalReference<jobject>(class_loader.Get())); + // Go back to native. + art::ScopedThreadSuspension sts(self, art::ThreadState::kNative); + // Call all Non-retransformable agents. + jint post_no_redefine_len = 0; + unsigned char* post_no_redefine_dex_data = nullptr; + std::unique_ptr<const unsigned char> post_no_redefine_unique_ptr(nullptr); + event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>( + self, + static_cast<JNIEnv*>(env), + static_cast<jclass>(nullptr), // The class doesn't really exist yet so send null. + loader.get(), + name.c_str(), + static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains + static_cast<jint>(initial_dex_file.Size()), + static_cast<const unsigned char*>(initial_dex_file.Begin()), + static_cast<jint*>(&post_no_redefine_len), + static_cast<unsigned char**>(&post_no_redefine_dex_data)); + if (post_no_redefine_dex_data == nullptr) { + DCHECK_EQ(post_no_redefine_len, 0); + post_no_redefine_dex_data = const_cast<unsigned char*>(initial_dex_file.Begin()); + post_no_redefine_len = initial_dex_file.Size(); + } else { + post_no_redefine_unique_ptr = std::unique_ptr<const unsigned char>(post_no_redefine_dex_data); + DCHECK_GT(post_no_redefine_len, 0); + } + // Call all retransformable agents. + jint final_len = 0; + unsigned char* final_dex_data = nullptr; + std::unique_ptr<const unsigned char> final_dex_unique_ptr(nullptr); + event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( + self, + static_cast<JNIEnv*>(env), + static_cast<jclass>(nullptr), // The class doesn't really exist yet so send null. + loader.get(), + name.c_str(), + static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains + static_cast<jint>(post_no_redefine_len), + static_cast<const unsigned char*>(post_no_redefine_dex_data), + static_cast<jint*>(&final_len), + static_cast<unsigned char**>(&final_dex_data)); + if (final_dex_data == nullptr) { + DCHECK_EQ(final_len, 0); + final_dex_data = post_no_redefine_dex_data; + final_len = post_no_redefine_len; + } else { + final_dex_unique_ptr = std::unique_ptr<const unsigned char>(final_dex_data); + DCHECK_GT(final_len, 0); + } + + if (final_dex_data != initial_dex_file.Begin()) { + LOG(WARNING) << "Changing class " << descriptor; + art::ScopedObjectAccess soa(self); + art::StackHandleScope<2> hs(self); + // Save the results of all the non-retransformable agents. + // First allocate the ClassExt + art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(self))); + // Make sure we have a ClassExt. This is fine even though we are a temporary since it will + // get copied. + if (ext.IsNull()) { + // We will just return failure if we fail to allocate + LOG(WARNING) << "Could not allocate ext-data for class '" << descriptor << "'. " + << "Aborting transformation since we will be unable to store it."; + self->AssertPendingOOMException(); + return; + } + + // Allocate the byte array to store the dex file bytes in. + art::Handle<art::mirror::ByteArray> arr(hs.NewHandle( + art::mirror::ByteArray::AllocateAndFill( + self, + reinterpret_cast<const signed char*>(post_no_redefine_dex_data), + post_no_redefine_len))); + if (arr.IsNull()) { + LOG(WARNING) << "Unable to allocate byte array for initial dex-file bytes. Aborting " + << "transformation"; + self->AssertPendingOOMException(); + return; + } + + std::unique_ptr<const art::DexFile> dex_file(MakeSingleDexFile(self, + descriptor, + initial_dex_file.GetLocation(), + final_len, + final_dex_data)); + if (dex_file.get() == nullptr) { + return; + } + + // TODO Check Redefined dex file for invariants. + LOG(WARNING) << "Dex file created by class-definition time transformation of " + << descriptor << " is not checked for all retransformation invariants."; + // TODO Put it in classpath + LOG(WARNING) << "Dex file created for class-definition time transformation of " + << descriptor << " was not added to any classpaths!"; + // Actually set the ClassExt's original bytes once we have actually succeeded. + ext->SetOriginalDexFileBytes(arr.Get()); + // Set the return values + *final_class_def = &dex_file->GetClassDef(0); + *final_dex_file = dex_file.release(); + } + } + 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())); + ScopedLocalRef<jthread> thread_jni( + thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer())); { art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); - event_handler->DispatchEvent(thread, - ArtJvmtiEvent::kClassLoad, - reinterpret_cast<JNIEnv*>(thread->GetJniEnv()), - jthread.get(), - jklass.get()); + event_handler->DispatchEvent<ArtJvmtiEvent::kClassLoad>( + thread, + static_cast<JNIEnv*>(thread->GetJniEnv()), + thread_jni.get(), + jklass.get()); } AddTempClass(thread, jklass.get()); } @@ -78,14 +268,14 @@ struct ClassCallback : public art::ClassLoadCallback { 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())); + ScopedLocalRef<jthread> thread_jni( + thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer())); art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); - event_handler->DispatchEvent(thread, - ArtJvmtiEvent::kClassPrepare, - reinterpret_cast<JNIEnv*>(thread->GetJniEnv()), - jthread.get(), - jklass.get()); + event_handler->DispatchEvent<ArtJvmtiEvent::kClassPrepare>( + thread, + static_cast<JNIEnv*>(thread->GetJniEnv()), + thread_jni.get(), + jklass.get()); } } diff --git a/runtime/openjdkjvmti/ti_dump.cc b/runtime/openjdkjvmti/ti_dump.cc index 2ee5c409f6..d9e3ef1bcf 100644 --- a/runtime/openjdkjvmti/ti_dump.cc +++ b/runtime/openjdkjvmti/ti_dump.cc @@ -48,7 +48,7 @@ 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); + event_handler->DispatchEvent<ArtJvmtiEvent::kDataDumpRequest>(nullptr); } EventHandler* event_handler = nullptr; diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc index 154406a5db..4970288250 100644 --- a/runtime/openjdkjvmti/ti_phase.cc +++ b/runtime/openjdkjvmti/ti_phase.cc @@ -64,7 +64,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { case RuntimePhase::kStart: { art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv()); + event_handler->DispatchEvent<ArtJvmtiEvent::kVmStart>(nullptr, GetJniEnv()); PhaseUtil::current_phase_ = JVMTI_PHASE_START; } break; @@ -72,17 +72,14 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { { ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread()); art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr, - ArtJvmtiEvent::kVmInit, - GetJniEnv(), - thread.get()); + event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>(nullptr, 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()); + event_handler->DispatchEvent<ArtJvmtiEvent::kVmDeath>(nullptr, GetJniEnv()); PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD; } // TODO: Block events now. diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 34efc502e1..d2ddc21cd4 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -520,28 +520,13 @@ void Redefiner::RecordFailure(jvmtiError result, result_ = result; } -// 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); - } - 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()); + return art::mirror::ByteArray::AllocateAndFill( + driver_->self_, + reinterpret_cast<const signed char*>(&original_dex_file_.At(0)), + original_dex_file_.size()); } // See if we already have one set. @@ -561,7 +546,10 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi 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!"; } - return AllocateAndFillBytes(driver_->self_, current_dex_file.Begin(), current_dex_file.Size()); + return art::mirror::ByteArray::AllocateAndFill( + driver_->self_, + reinterpret_cast<const signed char*>(current_dex_file.Begin()), + current_dex_file.Size()); } struct CallbackCtx { diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 29a7e1f3ac..fdc13eee32 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -96,6 +96,11 @@ class Redefiner { static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable); + static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location, + jint data_len, + const unsigned char* dex_data, + std::string* error_msg); + private: class ClassRedefinition { public: @@ -234,11 +239,6 @@ class Redefiner { /*out*/std::string* error_msg) REQUIRES_SHARED(art::Locks::mutator_lock_); - static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location, - jint data_len, - const unsigned char* dex_data, - std::string* error_msg); - // TODO Put on all the lock qualifiers. jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_); diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc index 9f81d6ba97..b18a5cd746 100644 --- a/runtime/openjdkjvmti/ti_thread.cc +++ b/runtime/openjdkjvmti/ti_thread.cc @@ -61,11 +61,14 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime } return self->GetJniEnv()->AddLocalReference<jthread>(self->GetPeer()); } - void Post(art::Thread* self, ArtJvmtiEvent type) REQUIRES_SHARED(art::Locks::mutator_lock_) { + template <ArtJvmtiEvent kEvent> + void Post(art::Thread* self) 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()); + event_handler->DispatchEvent<kEvent>(self, + reinterpret_cast<JNIEnv*>(self->GetJniEnv()), + thread.get()); } void ThreadStart(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { @@ -81,11 +84,11 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime } return; } - Post(self, ArtJvmtiEvent::kThreadStart); + Post<ArtJvmtiEvent::kThreadStart>(self); } void ThreadDeath(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { - Post(self, ArtJvmtiEvent::kThreadEnd); + Post<ArtJvmtiEvent::kThreadEnd>(self); } void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { @@ -93,7 +96,7 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime // 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); + Post<ArtJvmtiEvent::kThreadStart>(art::Thread::Current()); } } diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc index af4fb7187a..3c4cfea19a 100644 --- a/runtime/openjdkjvmti/transform.cc +++ b/runtime/openjdkjvmti/transform.cc @@ -68,19 +68,17 @@ jvmtiError Transformer::RetransformClassesDirect( for (ArtClassDefinition& def : *definitions) { jint new_len = -1; unsigned char* new_data = nullptr; - // Static casts are so that we get the right template initialization for the special event - // handling code required by the ClassFileLoadHooks. - gEventHandler.DispatchEvent(self, - ArtJvmtiEvent::kClassFileLoadHookRetransformable, - GetJniEnv(env), - static_cast<jclass>(def.klass), - static_cast<jobject>(def.loader), - static_cast<const char*>(def.name.c_str()), - static_cast<jobject>(def.protection_domain), - static_cast<jint>(def.dex_len), - static_cast<const unsigned char*>(def.dex_data.get()), - static_cast<jint*>(&new_len), - static_cast<unsigned char**>(&new_data)); + gEventHandler.DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>( + self, + GetJniEnv(env), + def.klass, + def.loader, + def.name.c_str(), + def.protection_domain, + def.dex_len, + static_cast<const unsigned char*>(def.dex_data.get()), + &new_len, + &new_data); def.SetNewDexData(env, new_len, new_data); } return OK; @@ -139,20 +137,6 @@ jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* return OK; } -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, diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index d1ad77c910..ccc5f7a8ee 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -599,42 +599,6 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options, args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize)); } - if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kRuntimePlugins) { - LOG(WARNING) << "Experimental runtime plugin support has been enabled. No guarantees are made " - << "about stability or usage of this plugin support. Use at your own risk. Do " - << "not attempt to write shipping code that relies on the implementation of " - << "runtime plugins."; - } else if (!args.GetOrDefault(M::Plugins).empty()) { - LOG(WARNING) << "Experimental runtime plugin support has not been enabled. Ignored options: "; - for (const auto& op : args.GetOrDefault(M::Plugins)) { - LOG(WARNING) << " -plugin:" << op.GetLibrary(); - } - } - - if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kAgents) { - LOG(WARNING) << "Experimental runtime agent support has been enabled. No guarantees are made " - << "the completeness, accuracy, reliability, or stability of the agent " - << "implementation. Use at your own risk. Do not attempt to write shipping code " - << "that relies on the implementation of any part of this api."; - } else if (!args.GetOrDefault(M::AgentLib).empty() || !args.GetOrDefault(M::AgentPath).empty()) { - LOG(WARNING) << "agent support has not been enabled. Enable experimental agent " - << " support with '-XExperimental:agent'. Ignored options are:"; - for (const auto& op : args.GetOrDefault(M::AgentLib)) { - if (op.HasArgs()) { - LOG(WARNING) << " -agentlib:" << op.GetName() << "=" << op.GetArgs(); - } else { - LOG(WARNING) << " -agentlib:" << op.GetName(); - } - } - for (const auto& op : args.GetOrDefault(M::AgentPath)) { - if (op.HasArgs()) { - LOG(WARNING) << " -agentpath:" << op.GetName() << "=" << op.GetArgs(); - } else { - LOG(WARNING) << " -agentpath:" << op.GetName(); - } - } - } - *runtime_options = std::move(args); return true; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 06cd7ff4b5..5e008a8bb8 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1069,16 +1069,13 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental); is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode); - if (experimental_flags_ & ExperimentalFlags::kRuntimePlugins) { - plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins); - } - if (experimental_flags_ & ExperimentalFlags::kAgents) { - agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath); - // TODO Add back in -agentlib - // for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) { - // agents_.push_back(lib); - // } - } + plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins); + agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath); + // TODO Add back in -agentlib + // for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) { + // agents_.push_back(lib); + // } + XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption); heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize), runtime_options.GetOrDefault(Opt::HeapGrowthLimit), 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 e580e7875e..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; @@ -99,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 8974b595de..f1e78b40e0 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_class_def 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); } diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 749a36e3b3..ad748b04d3 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -119,10 +119,10 @@ RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10) RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback) RUNTIME_OPTIONS_KEY (std::string, CpuAbiList) RUNTIME_OPTIONS_KEY (std::string, Fingerprint) -RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, agents} -RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options>, Requires -Xexperimental:agents -RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options>, Requires -Xexperimental:agents -RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library> Requires -Xexperimental:runtime-plugins +RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{...} +RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options> +RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options> +RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library> RUNTIME_OPTIONS_KEY (Unit, FullyDeoptable) // -Xfully-deoptable // Not parse-able from command line, but can be provided explicitly. diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 3c92b86208..690b069c8e 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -198,6 +198,7 @@ void StackMap::Dump(VariableIndentationOutputStream* vios, << "StackMap" << header_suffix << std::hex << " [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" << pc_offset << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding) diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 28c4b88fb0..578252181d 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -667,32 +667,7 @@ struct FieldEncoding { ALWAYS_INLINE int32_t Load(const MemoryRegion& region) const { DCHECK_LE(end_offset_, region.size_in_bits()); - const size_t bit_count = BitSize(); - if (bit_count == 0) { - // Do not touch any memory if the range is empty. - return min_value_; - } - uint8_t* address = region.start() + start_offset_ / kBitsPerByte; - const uint32_t shift = start_offset_ & (kBitsPerByte - 1); - // Load the value (reading only the strictly needed bytes). - const uint32_t load_bit_count = shift + bit_count; - uint32_t value = *address++ >> shift; - if (load_bit_count > 8) { - value |= static_cast<uint32_t>(*address++) << (8 - shift); - if (load_bit_count > 16) { - value |= static_cast<uint32_t>(*address++) << (16 - shift); - if (load_bit_count > 24) { - value |= static_cast<uint32_t>(*address++) << (24 - shift); - if (load_bit_count > 32) { - value |= static_cast<uint32_t>(*address++) << (32 - shift); - } - } - } - } - // Clear unwanted most significant bits. - uint32_t clear_bit_count = 32 - bit_count; - value = (value << clear_bit_count) >> clear_bit_count; - return value + min_value_; + return static_cast<int32_t>(region.LoadBits(start_offset_, BitSize())) + min_value_; } ALWAYS_INLINE void Store(MemoryRegion region, int32_t value) const { @@ -1182,6 +1157,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, diff --git a/runtime/utils.cc b/runtime/utils.cc index 8867743ec1..410416ea64 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -788,49 +788,58 @@ void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu) *task_cpu = strtoull(fields[36].c_str(), nullptr, 10); } -const char* GetAndroidRoot() { - const char* android_root = getenv("ANDROID_ROOT"); - if (android_root == nullptr) { - if (OS::DirectoryExists("/system")) { - android_root = "/system"; +static const char* GetAndroidDirSafe(const char* env_var, + const char* default_dir, + std::string* error_msg) { + const char* android_dir = getenv(env_var); + if (android_dir == nullptr) { + if (OS::DirectoryExists(default_dir)) { + android_dir = default_dir; } else { - LOG(FATAL) << "ANDROID_ROOT not set and /system does not exist"; - return ""; + *error_msg = StringPrintf("%s not set and %s does not exist", env_var, default_dir); + return nullptr; } } - if (!OS::DirectoryExists(android_root)) { - LOG(FATAL) << "Failed to find ANDROID_ROOT directory " << android_root; - return ""; + if (!OS::DirectoryExists(android_dir)) { + *error_msg = StringPrintf("Failed to find %s directory %s", env_var, android_dir); + return nullptr; } - return android_root; + return android_dir; } -const char* GetAndroidData() { +const char* GetAndroidDir(const char* env_var, const char* default_dir) { std::string error_msg; - const char* dir = GetAndroidDataSafe(&error_msg); + const char* dir = GetAndroidDirSafe(env_var, default_dir, &error_msg); if (dir != nullptr) { return dir; } else { LOG(FATAL) << error_msg; - return ""; + return nullptr; } } +const char* GetAndroidRoot() { + return GetAndroidDir("ANDROID_ROOT", "/system"); +} + +const char* GetAndroidRootSafe(std::string* error_msg) { + return GetAndroidDirSafe("ANDROID_ROOT", "/system", error_msg); +} + +const char* GetAndroidData() { + return GetAndroidDir("ANDROID_DATA", "/data"); +} + const char* GetAndroidDataSafe(std::string* error_msg) { - const char* android_data = getenv("ANDROID_DATA"); - if (android_data == nullptr) { - if (OS::DirectoryExists("/data")) { - android_data = "/data"; - } else { - *error_msg = "ANDROID_DATA not set and /data does not exist"; - return nullptr; - } - } - if (!OS::DirectoryExists(android_data)) { - *error_msg = StringPrintf("Failed to find ANDROID_DATA directory %s", android_data); - return nullptr; + return GetAndroidDirSafe("ANDROID_DATA", "/data", error_msg); +} + +std::string GetDefaultBootImageLocation(std::string* error_msg) { + const char* android_root = GetAndroidRootSafe(error_msg); + if (android_root == nullptr) { + return ""; } - return android_data; + return StringPrintf("%s/framework/boot.art", android_root); } void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache, diff --git a/runtime/utils.h b/runtime/utils.h index 16ef706159..9e663b31e0 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -143,12 +143,18 @@ void SetThreadName(const char* thread_name); // Find $ANDROID_ROOT, /system, or abort. const char* GetAndroidRoot(); +// Find $ANDROID_ROOT, /system, or return null. +const char* GetAndroidRootSafe(std::string* error_msg); // Find $ANDROID_DATA, /data, or abort. const char* GetAndroidData(); // Find $ANDROID_DATA, /data, or return null. const char* GetAndroidDataSafe(std::string* error_msg); +// Returns the default boot image location (ANDROID_ROOT/framework/boot.art). +// Returns an empty string if ANDROID_ROOT is not set. +std::string GetDefaultBootImageLocation(std::string* error_msg); + // Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache // could not be found. std::string GetDalvikCache(const char* subdir); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 507ea165e5..2610252aa7 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -51,7 +51,6 @@ jclass WellKnownClasses::java_lang_ClassLoader; jclass WellKnownClasses::java_lang_ClassNotFoundException; jclass WellKnownClasses::java_lang_Daemons; jclass WellKnownClasses::java_lang_Error; -jclass WellKnownClasses::java_lang_ExceptionInInitializerError; jclass WellKnownClasses::java_lang_invoke_MethodHandle; jclass WellKnownClasses::java_lang_IllegalAccessError; jclass WellKnownClasses::java_lang_NoClassDefFoundError; @@ -290,7 +289,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Object = CacheClass(env, "java/lang/Object"); java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError"); java_lang_Error = CacheClass(env, "java/lang/Error"); - java_lang_ExceptionInInitializerError = CacheClass(env, "java/lang/ExceptionInInitializerError"); java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError"); java_lang_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle"); java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index b3ce3d1597..db8a53c44c 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -61,7 +61,6 @@ struct WellKnownClasses { static jclass java_lang_ClassNotFoundException; static jclass java_lang_Daemons; static jclass java_lang_Error; - static jclass java_lang_ExceptionInInitializerError; static jclass java_lang_IllegalAccessError; static jclass java_lang_invoke_MethodHandle; static jclass java_lang_NoClassDefFoundError; diff --git a/test/008-exceptions/expected.txt b/test/008-exceptions/expected.txt index 083ecf7ced..fcf2ef49f3 100644 --- a/test/008-exceptions/expected.txt +++ b/test/008-exceptions/expected.txt @@ -1,11 +1,11 @@ Got an NPE: second throw java.lang.NullPointerException: second throw - at Main.catchAndRethrow(Main.java:77) - at Main.exceptions_007(Main.java:59) - at Main.main(Main.java:67) + at Main.catchAndRethrow(Main.java:94) + at Main.exceptions_007(Main.java:74) + at Main.main(Main.java:82) Caused by: java.lang.NullPointerException: first throw - at Main.throwNullPointerException(Main.java:84) - at Main.catchAndRethrow(Main.java:74) + at Main.throwNullPointerException(Main.java:101) + at Main.catchAndRethrow(Main.java:91) ... 2 more Static Init BadError: This is bad by convention: BadInit @@ -15,3 +15,11 @@ Static BadInitNoStringInit BadErrorNoStringInit: This is bad by convention java.lang.NoClassDefFoundError: BadInitNoStringInit BadErrorNoStringInit: This is bad by convention +BadSuperClass Static Init +BadError: This is bad by convention: BadInit +MultiDexBadInit Static Init +java.lang.Error: MultiDexBadInit +java.lang.NoClassDefFoundError: MultiDexBadInit + cause: java.lang.Error: MultiDexBadInit +java.lang.NoClassDefFoundError: MultiDexBadInit + cause: java.lang.Error: MultiDexBadInit diff --git a/test/008-exceptions/multidex.jpp b/test/008-exceptions/multidex.jpp new file mode 100644 index 0000000000..a3746f5149 --- /dev/null +++ b/test/008-exceptions/multidex.jpp @@ -0,0 +1,27 @@ +BadError: + @@com.android.jack.annotations.ForceInMainDex + class BadError +BadInit: + @@com.android.jack.annotations.ForceInMainDex + class BadInit +BadErrorNoStringInit: + @@com.android.jack.annotations.ForceInMainDex + class BadErrorNoStringInit +BadInitNoStringInit: + @@com.android.jack.annotations.ForceInMainDex + class BadInitNoStringInit +BadSuperClass: + @@com.android.jack.annotations.ForceInMainDex + class BadSuperClass +DerivedFromBadSuperClass: + @@com.android.jack.annotations.ForceInMainDex + class DerivedFromBadSuperClass +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main +MultiDexBadInit: + @@com.android.jack.annotations.ForceInMainDex + class MultiDexBadInit +MultiDexBadInitWrapper1: + @@com.android.jack.annotations.ForceInMainDex + class MultiDexBadInitWrapper1 diff --git a/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java b/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java new file mode 100644 index 0000000000..f3953bd4c7 --- /dev/null +++ b/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java @@ -0,0 +1,24 @@ +/* + * 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 MultiDexBadInitWrapper2 { + public static void setDummy(int value) { + if (doThrow) { throw new Error(); } + MultiDexBadInit.dummy = value; + } + + public static boolean doThrow = false; +} diff --git a/test/008-exceptions/src/Main.java b/test/008-exceptions/src/Main.java index b8231f12bd..74af00ccf7 100644 --- a/test/008-exceptions/src/Main.java +++ b/test/008-exceptions/src/Main.java @@ -50,6 +50,21 @@ class BadInitNoStringInit { } } +// A class that throws BadError during static initialization, serving as a super class. +class BadSuperClass { + static int dummy; + static { + System.out.println("BadSuperClass Static Init"); + if (true) { + throw new BadError("BadInit"); + } + } +} + +// A class that derives from BadSuperClass. +class DerivedFromBadSuperClass extends BadSuperClass { +} + /** * Exceptions across method calls */ @@ -63,10 +78,12 @@ public class Main { npe.printStackTrace(System.out); } } - public static void main (String args[]) { + public static void main(String args[]) { exceptions_007(); exceptionsRethrowClassInitFailure(); exceptionsRethrowClassInitFailureNoStringInit(); + exceptionsForSuperClassInitFailure(); + exceptionsInMultiDex(); } private static void catchAndRethrow() { @@ -129,4 +146,70 @@ public class Main { error.printStackTrace(System.out); } } + + private static void exceptionsForSuperClassInitFailure() { + try { + // Resolve DerivedFromBadSuperClass. + BadSuperClass.dummy = 1; + throw new IllegalStateException("Should not reach here."); + } catch (BadError e) { + System.out.println(e); + } catch (Throwable t) { + t.printStackTrace(); + } + try { + // Before splitting mirror::Class::kStatusError into + // kStatusErrorUnresolved and kStatusErrorResolved, + // this would trigger a + // CHECK(super_class->IsResolved()) + // failure in + // ClassLinker::LoadSuperAndInterfaces(). + // After the change we're getting either VerifyError + // (for Optimizing) or NoClassDefFoundError wrapping + // BadError (for interpreter or JIT). + new DerivedFromBadSuperClass(); + throw new IllegalStateException("Should not reach here."); + } catch (NoClassDefFoundError ncdfe) { + if (!(ncdfe.getCause() instanceof BadError)) { + ncdfe.getCause().printStackTrace(); + } + } catch (VerifyError e) { + } catch (Throwable t) { + t.printStackTrace(); + } + } + + private static void exceptionsInMultiDex() { + try { + MultiDexBadInit.dummy = 1; + throw new IllegalStateException("Should not reach here."); + } catch (Error e) { + System.out.println(e); + } catch (Throwable t) { + t.printStackTrace(); + } + // Before splitting mirror::Class::kStatusError into + // kStatusErrorUnresolved and kStatusErrorResolved, + // the exception from wrapper 1 would have been + // wrapped in NoClassDefFoundError but the exception + // from wrapper 2 would have been unwrapped. + try { + MultiDexBadInitWrapper1.setDummy(1); + throw new IllegalStateException("Should not reach here."); + } catch (NoClassDefFoundError ncdfe) { + System.out.println(ncdfe); + System.out.println(" cause: " + ncdfe.getCause()); + } catch (Throwable t) { + t.printStackTrace(); + } + try { + MultiDexBadInitWrapper2.setDummy(1); + throw new IllegalStateException("Should not reach here."); + } catch (NoClassDefFoundError ncdfe) { + System.out.println(ncdfe); + System.out.println(" cause: " + ncdfe.getCause()); + } catch (Throwable t) { + t.printStackTrace(); + } + } } diff --git a/test/008-exceptions/src/MultiDexBadInit.java b/test/008-exceptions/src/MultiDexBadInit.java new file mode 100644 index 0000000000..e3ebb9ca45 --- /dev/null +++ b/test/008-exceptions/src/MultiDexBadInit.java @@ -0,0 +1,25 @@ +/* + * 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 MultiDexBadInit { + static int dummy; + static { + System.out.println("MultiDexBadInit Static Init"); + if (true) { + throw new Error("MultiDexBadInit"); + } + } +} diff --git a/test/008-exceptions/src/MultiDexBadInitWrapper1.java b/test/008-exceptions/src/MultiDexBadInitWrapper1.java new file mode 100644 index 0000000000..059e6a3d7d --- /dev/null +++ b/test/008-exceptions/src/MultiDexBadInitWrapper1.java @@ -0,0 +1,24 @@ +/* + * 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 MultiDexBadInitWrapper1 { + public static void setDummy(int value) { + if (doThrow) { throw new Error(); } + MultiDexBadInit.dummy = value; + } + + public static boolean doThrow = false; +} diff --git a/test/142-classloader2/expected.txt b/test/142-classloader2/expected.txt index 86f5e220e2..056d9785de 100644 --- a/test/142-classloader2/expected.txt +++ b/test/142-classloader2/expected.txt @@ -1 +1,5 @@ +Loaded class B. +Caught VerifyError. +Loaded class B. +Caught wrapped VerifyError. Everything OK. diff --git a/test/142-classloader2/src/Main.java b/test/142-classloader2/src/Main.java index 80b00e7dd2..a0c77645a3 100644 --- a/test/142-classloader2/src/Main.java +++ b/test/142-classloader2/src/Main.java @@ -74,16 +74,25 @@ public class Main { // Try to load a dex file with bad dex code. Use new instance to force verification. try { Class<?> badClass = Main.class.getClassLoader().loadClass("B"); + System.out.println("Loaded class B."); badClass.newInstance(); - System.out.println("Should not be able to load class from bad dex file."); + System.out.println("Should not be able to instantiate B with bad dex bytecode."); } catch (VerifyError e) { + System.out.println("Caught VerifyError."); } // Make sure the same error is rethrown when reloading the bad class. try { Class<?> badClass = Main.class.getClassLoader().loadClass("B"); - System.out.println("Should not be able to load class from bad dex file."); - } catch (VerifyError e) { + System.out.println("Loaded class B."); + badClass.newInstance(); + System.out.println("Should not be able to instantiate B with bad dex bytecode."); + } catch (NoClassDefFoundError e) { + if (e.getCause() instanceof VerifyError) { + System.out.println("Caught wrapped VerifyError."); + } else { + e.printStackTrace(); + } } System.out.println("Everything OK."); diff --git a/test/154-gc-loop/expected.txt b/test/154-gc-loop/expected.txt new file mode 100644 index 0000000000..6106818c79 --- /dev/null +++ b/test/154-gc-loop/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +Finalize count too large: false diff --git a/test/154-gc-loop/heap_interface.cc b/test/154-gc-loop/heap_interface.cc new file mode 100644 index 0000000000..8d610a87fa --- /dev/null +++ b/test/154-gc-loop/heap_interface.cc @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gc/heap.h" +#include "runtime.h" + +namespace art { +namespace { + +extern "C" JNIEXPORT void JNICALL Java_Main_backgroundProcessState(JNIEnv*, jclass) { + Runtime::Current()->UpdateProcessState(kProcessStateJankImperceptible); +} + +} // namespace +} // namespace art diff --git a/test/154-gc-loop/info.txt b/test/154-gc-loop/info.txt new file mode 100644 index 0000000000..f599db1d01 --- /dev/null +++ b/test/154-gc-loop/info.txt @@ -0,0 +1 @@ +Test that GC doesn't happen too often for a few small allocations. diff --git a/test/154-gc-loop/src/Main.java b/test/154-gc-loop/src/Main.java new file mode 100644 index 0000000000..3a256c109e --- /dev/null +++ b/test/154-gc-loop/src/Main.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +import java.lang.ref.WeakReference; + +public class Main { + static final class GcWatcher { + protected void finalize() throws Throwable { + watcher = new WeakReference<GcWatcher>(new GcWatcher()); + ++finalizeCounter; + } + } + static WeakReference<GcWatcher> watcher = new WeakReference<GcWatcher>(new GcWatcher()); + static Object o = new Object(); + static int finalizeCounter = 0; + + public static void main(String[] args) { + System.loadLibrary(args[0]); + backgroundProcessState(); + try { + Runtime.getRuntime().gc(); + for (int i = 0; i < 10; ++i) { + o = new Object(); + Thread.sleep(1000); + } + } catch (Exception e) {} + System.out.println("Finalize count too large: " + + ((finalizeCounter >= 10) ? Integer.toString(finalizeCounter) : "false")); + } + + private static native void backgroundProcessState(); +} diff --git a/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali index 75344f74bf..e4bf236266 100644 --- a/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali +++ b/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali @@ -17,10 +17,9 @@ .super Ljava/lang/Object; ## CHECK-START-X86: int IrreducibleLoop.simpleLoop(int) dead_code_elimination$initial (before) -## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod ## CHECK-DAG: <<Constant:i\d+>> IntConstant 42 -## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:{{B\d+}} irreducible:true -## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:{{B\d+}} irreducible:true +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none .method public static simpleLoop(I)I .registers 3 const/16 v0, 42 diff --git a/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali b/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali index 186f0ab3e8..9b8aa510a4 100644 --- a/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali +++ b/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali @@ -17,11 +17,10 @@ .super Ljava/lang/Object; ## CHECK-START-X86: int IrreducibleLoop.simpleLoop1(int) dead_code_elimination$initial (before) -## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod ## CHECK-DAG: <<Constant:i\d+>> IntConstant 42 ## CHECK-DAG: Goto irreducible:true -## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none -## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}},<<Method>>] loop:none +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none +## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}}] loop:none .method public static simpleLoop1(I)I .registers 3 const/16 v0, 42 @@ -58,11 +57,10 @@ .end method ## CHECK-START-X86: int IrreducibleLoop.simpleLoop2(int) dead_code_elimination$initial (before) -## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod ## CHECK-DAG: <<Constant:i\d+>> IntConstant 42 ## CHECK-DAG: Goto irreducible:true -## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none -## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}},<<Method>>] loop:none +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none +## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}}] loop:none .method public static simpleLoop2(I)I .registers 3 const/16 v0, 42 diff --git a/test/900-hello-plugin/run b/test/900-hello-plugin/run index 35b08715a1..50835f89af 100755 --- a/test/900-hello-plugin/run +++ b/test/900-hello-plugin/run @@ -18,7 +18,5 @@ plugin=libartagentd.so if [[ "$@" == *"-O"* ]]; then plugin=libartagent.so fi -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --runtime-option -agentpath:${plugin}=test_900 \ +./default-run "$@" --runtime-option -agentpath:${plugin}=test_900 \ --android-runtime-option -Xplugin:${plugin} diff --git a/test/901-hello-ti-agent/run b/test/901-hello-ti-agent/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/901-hello-ti-agent/run +++ b/test/901-hello-ti-agent/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/902-hello-transformation/run b/test/902-hello-transformation/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/902-hello-transformation/run +++ b/test/902-hello-transformation/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/903-hello-tagging/run b/test/903-hello-tagging/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/903-hello-tagging/run +++ b/test/903-hello-tagging/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/904-object-allocation/run b/test/904-object-allocation/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/904-object-allocation/run +++ b/test/904-object-allocation/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/905-object-free/run b/test/905-object-free/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/905-object-free/run +++ b/test/905-object-free/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/906-iterate-heap/run b/test/906-iterate-heap/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/906-iterate-heap/run +++ b/test/906-iterate-heap/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/907-get-loaded-classes/run b/test/907-get-loaded-classes/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/907-get-loaded-classes/run +++ b/test/907-get-loaded-classes/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/908-gc-start-finish/run b/test/908-gc-start-finish/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/908-gc-start-finish/run +++ b/test/908-gc-start-finish/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run index 985341bd4f..0664592cd1 100755 --- a/test/909-attach-agent/run +++ b/test/909-attach-agent/run @@ -21,17 +21,11 @@ if [[ "$@" == *"-O"* ]]; then plugin=libopenjdkjvmti.so fi -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --android-runtime-option -Xplugin:${plugin} \ +./default-run "$@" --android-runtime-option -Xplugin:${plugin} \ --android-runtime-option -Xfully-deoptable \ --args agent:${agent}=909-attach-agent -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --android-runtime-option -Xfully-deoptable \ +./default-run "$@" --android-runtime-option -Xfully-deoptable \ --args agent:${agent}=909-attach-agent -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --args agent:${agent}=909-attach-agent +./default-run "$@" --args agent:${agent}=909-attach-agent diff --git a/test/910-methods/run b/test/910-methods/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/910-methods/run +++ b/test/910-methods/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/911-get-stack-trace/run b/test/911-get-stack-trace/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/911-get-stack-trace/run +++ b/test/911-get-stack-trace/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt index 4976553148..328216b324 100644 --- a/test/912-classes/expected.txt +++ b/test/912-classes/expected.txt @@ -29,7 +29,7 @@ int 100000 class [Ljava.lang.String; 10000 class java.lang.Object 111 class Main$TestForNonInit 11 -class Main$TestForInitFail 1001 +class Main$TestForInitFail 1011 int [] class [Ljava.lang.String; [] class java.lang.Object [] @@ -86,8 +86,6 @@ Prepare: LA; on main (cur=main) C, true Load: LC; on main Prepare: LC; on main (cur=main) -Load: LMain$1; on main -Prepare: LMain$1; on main (cur=main) C, true Load: LA; on TestRunner Prepare: LA; on TestRunner (cur=TestRunner) diff --git a/test/912-classes/run b/test/912-classes/run index 20dfc4b767..f24db40cb0 100755 --- a/test/912-classes/run +++ b/test/912-classes/run @@ -18,7 +18,5 @@ # In certain configurations, the app images may be valid even in a new classloader. Turn off # app images to avoid the issue. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti \ +./default-run "$@" --jvmti \ --no-app-image diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java index 859f80cd80..6ad23a4869 100644 --- a/test/912-classes/src/Main.java +++ b/test/912-classes/src/Main.java @@ -219,6 +219,25 @@ public class Main { } 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); @@ -247,18 +266,6 @@ public class Main { System.out.println("C, true"); Class.forName("C", true, cl5); - 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 t = new Thread(r, "TestRunner"); t.start(); t.join(); @@ -330,6 +337,8 @@ public class Main { 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/run b/test/913-heaps/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/913-heaps/run +++ b/test/913-heaps/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/914-hello-obsolescence/run b/test/914-hello-obsolescence/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/914-hello-obsolescence/run +++ b/test/914-hello-obsolescence/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/915-obsolete-2/run b/test/915-obsolete-2/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/915-obsolete-2/run +++ b/test/915-obsolete-2/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/916-obsolete-jit/run b/test/916-obsolete-jit/run index 9056211284..b6d406fd99 100755 --- a/test/916-obsolete-jit/run +++ b/test/916-obsolete-jit/run @@ -21,7 +21,5 @@ if [[ "$@" == *"--jit"* ]]; then else other_args="--jit" fi -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - ${other_args} \ +./default-run "$@" ${other_args} \ --jvmti diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/917-fields-transformation/run +++ b/test/917-fields-transformation/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/918-fields/run b/test/918-fields/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/918-fields/run +++ b/test/918-fields/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/919-obsolete-fields/run b/test/919-obsolete-fields/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/919-obsolete-fields/run +++ b/test/919-obsolete-fields/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/920-objects/run b/test/920-objects/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/920-objects/run +++ b/test/920-objects/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/921-hello-failure/run b/test/921-hello-failure/run index 3ef4832da2..8be0ed4aed 100755 --- a/test/921-hello-failure/run +++ b/test/921-hello-failure/run @@ -15,6 +15,4 @@ # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/922-properties/run b/test/922-properties/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/922-properties/run +++ b/test/922-properties/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/923-monitors/run b/test/923-monitors/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/923-monitors/run +++ b/test/923-monitors/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/924-threads/run b/test/924-threads/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/924-threads/run +++ b/test/924-threads/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/925-threadgroups/run b/test/925-threadgroups/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/925-threadgroups/run +++ b/test/925-threadgroups/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/926-multi-obsolescence/run b/test/926-multi-obsolescence/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/926-multi-obsolescence/run +++ b/test/926-multi-obsolescence/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/927-timers/run b/test/927-timers/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/927-timers/run +++ b/test/927-timers/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/928-jni-table/run b/test/928-jni-table/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/928-jni-table/run +++ b/test/928-jni-table/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/929-search/run b/test/929-search/run index 0a8d0672f6..67923a7984 100755 --- a/test/929-search/run +++ b/test/929-search/run @@ -17,7 +17,5 @@ # 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 \ +./default-run "$@" --jvmti \ --no-app-image diff --git a/test/930-hello-retransform/run b/test/930-hello-retransform/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/930-hello-retransform/run +++ b/test/930-hello-retransform/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/931-agent-thread/run b/test/931-agent-thread/run index 0a8d0672f6..67923a7984 100755 --- a/test/931-agent-thread/run +++ b/test/931-agent-thread/run @@ -17,7 +17,5 @@ # 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 \ +./default-run "$@" --jvmti \ --no-app-image diff --git a/test/932-transform-saves/run b/test/932-transform-saves/run index 4379349cb2..c6e62ae6cd 100755 --- a/test/932-transform-saves/run +++ b/test/932-transform-saves/run @@ -14,6 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -./default-run "$@" --experimental agents \ - --experimental runtime-plugins \ - --jvmti +./default-run "$@" --jvmti diff --git a/test/933-misc-events/run b/test/933-misc-events/run index 0a8d0672f6..67923a7984 100755 --- a/test/933-misc-events/run +++ b/test/933-misc-events/run @@ -17,7 +17,5 @@ # 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 \ +./default-run "$@" --jvmti \ --no-app-image diff --git a/test/934-load-transform/build b/test/934-load-transform/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/934-load-transform/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/934-load-transform/expected.txt b/test/934-load-transform/expected.txt new file mode 100644 index 0000000000..2b60207f03 --- /dev/null +++ b/test/934-load-transform/expected.txt @@ -0,0 +1 @@ +Goodbye diff --git a/test/934-load-transform/info.txt b/test/934-load-transform/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/934-load-transform/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/934-load-transform/run b/test/934-load-transform/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/934-load-transform/run @@ -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-run "$@" --jvmti diff --git a/test/934-load-transform/src/Main.java b/test/934-load-transform/src/Main.java new file mode 100644 index 0000000000..0b7f26890f --- /dev/null +++ b/test/934-load-transform/src/Main.java @@ -0,0 +1,66 @@ +/* + * 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("Goodbye"); + * } + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" + + "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" + + "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" + + "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" + + "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" + + "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); + + public static void main(String[] args) { + doTest(); + } + + public static void doTest() { + addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES); + enableCommonRetransformation(true); + new Transform().sayHi(); + } + + // Transforms the class + 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/934-load-transform/src/Transform.java b/test/934-load-transform/src/Transform.java new file mode 100644 index 0000000000..f624c3ac99 --- /dev/null +++ b/test/934-load-transform/src/Transform.java @@ -0,0 +1,21 @@ +/* + * 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() { + throw new Error("Should not be called!"); + } +} diff --git a/test/935-non-retransformable/build b/test/935-non-retransformable/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/935-non-retransformable/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/935-non-retransformable/expected.txt b/test/935-non-retransformable/expected.txt new file mode 100644 index 0000000000..ccd50a66a0 --- /dev/null +++ b/test/935-non-retransformable/expected.txt @@ -0,0 +1,6 @@ +Hello +Hello +Goodbye +Hello +Hello +Goodbye diff --git a/test/935-non-retransformable/info.txt b/test/935-non-retransformable/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/935-non-retransformable/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/935-non-retransformable/run b/test/935-non-retransformable/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/935-non-retransformable/run @@ -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-run "$@" --jvmti diff --git a/test/935-non-retransformable/src/Main.java b/test/935-non-retransformable/src/Main.java new file mode 100644 index 0000000000..d9cc329ed9 --- /dev/null +++ b/test/935-non-retransformable/src/Main.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; +import java.util.Base64; + +public class Main { + + /** + * base64 encoded class/dex file for + * class Transform { + * public void sayHi() { + * System.out.println("Hello"); + * } + * public void sayGoodbye() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAHwoABwAQCQARABIIABMKABQAFQgAFgcAFwcAGAEABjxpbml0PgEAAygpVgEABENv" + + "ZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEACnNheUdvb2RieWUBAApTb3VyY2VGaWxlAQAO" + + "VHJhbnNmb3JtLmphdmEMAAgACQcAGQwAGgAbAQAFSGVsbG8HABwMAB0AHgEAB0dvb2RieWUBAAlU" + + "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" + + "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" + + "YXZhL2xhbmcvU3RyaW5nOylWACAABgAHAAAAAAADAAAACAAJAAEACgAAAB0AAQABAAAABSq3AAGx" + + "AAAAAQALAAAABgABAAAAAQABAAwACQABAAoAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAsAAAAK" + + "AAIAAAADAAgABAABAA0ACQABAAoAAAAlAAIAAQAAAAmyAAISBbYABLEAAAABAAsAAAAKAAIAAAAG" + + "AAgABwABAA4AAAACAA8="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQDpaN+7jX/ZLl9Jr0HAEV7nqL1YDuakKakgAwAAcAAAAHhWNBIAAAAAAAAAAIACAAAQ" + + "AAAAcAAAAAYAAACwAAAAAgAAAMgAAAABAAAA4AAAAAUAAADoAAAAAQAAABABAADwAQAAMAEAAJYB" + + "AACeAQAApwEAAK4BAAC7AQAA0gEAAOYBAAD6AQAADgIAAB4CAAAhAgAAJQIAADkCAAA+AgAARwIA" + + "AFMCAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABQAAAAAAAAAKAAAABQAAAJABAAAEAAEA" + + "DAAAAAAAAAAAAAAAAAAAAA4AAAAAAAAADwAAAAEAAQANAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAA" + + "AAAACAAAAAAAAABrAgAAAAAAAAEAAQABAAAAWgIAAAQAAABwEAQAAAAOAAMAAQACAAAAXwIAAAkA" + + "AABiAAAAGwEBAAAAbiADABAADgAAAAMAAQACAAAAZQIAAAkAAABiAAAAGwECAAAAbiADABAADgAA" + + "AAEAAAADAAY8aW5pdD4AB0dvb2RieWUABUhlbGxvAAtMVHJhbnNmb3JtOwAVTGphdmEvaW8vUHJp" + + "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" + + "bGFuZy9TeXN0ZW07AA5UcmFuc2Zvcm0uamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4yMgAD" + + "b3V0AAdwcmludGxuAApzYXlHb29kYnllAAVzYXlIaQABAAcOAAYABw6HAAMABw6HAAAAAQIAgIAE" + + "sAIBAcgCAQHsAgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEAAAAHAAAAACAAAABgAAALAAAAADAAAA" + + "AgAAAMgAAAAEAAAAAQAAAOAAAAAFAAAABQAAAOgAAAAGAAAAAQAAABABAAABIAAAAwAAADABAAAB" + + "EAAAAQAAAJABAAACIAAAEAAAAJYBAAADIAAAAwAAAFoCAAAAIAAAAQAAAGsCAAAAEAAAAQAAAIAC" + + "AAA="); + + public static void main(String[] args) { + doTest(); + } + + public static void doTest() { + addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES); + enableCommonRetransformation(true); + // Actually load the class. + Transform t = new Transform(); + try { + // Call functions with reflection. Since the sayGoodbye function does not exist in the + // LTransform; when we compile this for the first time we need to use reflection. + Method hi = Transform.class.getMethod("sayHi"); + Method bye = Transform.class.getMethod("sayGoodbye"); + hi.invoke(t); + t.sayHi(); + bye.invoke(t); + // Make sure we don't get called for transformation again. + addCommonTransformationResult("Transform", new byte[0], new byte[0]); + doCommonClassRetransformation(Transform.class); + hi.invoke(t); + t.sayHi(); + bye.invoke(t); + } catch (Exception e) { + System.out.println("Unexpected error occured! " + e.toString()); + e.printStackTrace(); + } + } + + // Transforms the class + private static native void doCommonClassRetransformation(Class<?>... klasses); + 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/935-non-retransformable/src/Transform.java b/test/935-non-retransformable/src/Transform.java new file mode 100644 index 0000000000..f624c3ac99 --- /dev/null +++ b/test/935-non-retransformable/src/Transform.java @@ -0,0 +1,21 @@ +/* + * 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() { + throw new Error("Should not be called!"); + } +} diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt index 7540ef74f3..05b80e78a7 100644 --- a/test/957-methodhandle-transforms/expected.txt +++ b/test/957-methodhandle-transforms/expected.txt @@ -16,3 +16,46 @@ target: target, 42, 56 fallback: fallback, 42, 56 target: target, 42, 56 target: target, 42, 56 +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:43 +a: a, b:43 +a: a, b:43 +a: a, b:43 +a: a, b:43 +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:true, c: false +a: a, b:true, c: false +a: a, b:1, c: 2, d: 3, e: 4, f:5, g: 6.0, h: 7.0 +a: a, b:1, c: 2, d: 3, e: 4, f:5, g: 6.0, h: 7.0 +a: a, b:1, c: 2, d: 51, e: 52, f:53.0, g: 54.0 +a: a, b:1, c: 2, d: 51, e: 52, f:53.0, g: 54.0 +a: a, b:1, c: 2, d: 3, e: 4, f:5.0, g:6.0 +a: a, b:1, c: 2, d: 3, e: 4, f:5.0, g:6.0 +a: a, b:1, c: 2, d: 3, e: 4.0, f:5.0 +a: a, b:1, c: 2, d: 3, e: 4.0, f:5.0 +a: a, b:1, c: 2, d: 3.0, e: 4.0 +a: a, b:1, c: 2, d: 3.0, e: 4.0 +a: a, b:1.0, c: 2.0, d: 3.0 +a: a, b:1.0, c: 2.0, d: 3.0 +a: a, b:1.0, c: 2.0 +a: a, b:1.0, c: 2.0 +a: a, b:b, c: c +a: a, b:b, c: c +a: a, b:43 +a: a, b:b, c: c +a: a, b:true, c: false +a: a, b:1, c: 2 +a: a, b:a, c: b +a: a, b:3, c: 4 +a: a, b:42, c: 43 +a: a, b:100, c: 99 +a: a, b:8.9, c: 9.1 +a: a, b:6.7, c: 7.8 diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java index eebf55fb61..4035857b9a 100644 --- a/test/957-methodhandle-transforms/src/Main.java +++ b/test/957-methodhandle-transforms/src/Main.java @@ -34,6 +34,10 @@ public class Main { testFilterReturnValue(); testPermuteArguments(); testInvokers(); + testSpreaders_reference(); + testSpreaders_primitive(); + testInvokeWithArguments(); + testAsCollector(); } public static void testThrowException() throws Throwable { @@ -921,6 +925,455 @@ public class Main { } } + public static int spreadReferences(String a, String b, String c) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c); + return 42; + } + + public static int spreadReferences_Unbox(String a, int b) { + System.out.println("a: " + a + ", b:" + b); + return 43; + } + + public static void testSpreaders_reference() throws Throwable { + MethodType methodType = MethodType.methodType(int.class, + new Class<?>[] { String.class, String.class, String.class }); + MethodHandle delegate = MethodHandles.lookup().findStatic( + Main.class, "spreadReferences", methodType); + + // Basic checks on array lengths. + // + // Array size = 0 + MethodHandle mhAsSpreader = delegate.asSpreader(String[].class, 0); + int ret = (int) mhAsSpreader.invoke("a", "b", "c", new String[] {}); + assertEquals(42, ret); + // Array size = 1 + mhAsSpreader = delegate.asSpreader(String[].class, 1); + ret = (int) mhAsSpreader.invoke("a", "b", new String[] { "c" }); + assertEquals(42, ret); + // Array size = 2 + mhAsSpreader = delegate.asSpreader(String[].class, 2); + ret = (int) mhAsSpreader.invoke("a", new String[] { "b", "c" }); + assertEquals(42, ret); + // Array size = 3 + mhAsSpreader = delegate.asSpreader(String[].class, 3); + ret = (int) mhAsSpreader.invoke(new String[] { "a", "b", "c"}); + assertEquals(42, ret); + + // Exception case, array size = 4 is illegal. + try { + delegate.asSpreader(String[].class, 4); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Exception case, calling with an arg of the wrong size. + // Array size = 3 + mhAsSpreader = delegate.asSpreader(String[].class, 3); + try { + ret = (int) mhAsSpreader.invoke(new String[] { "a", "b"}); + } catch (IllegalArgumentException expected) { + } + + // Various other hijinks, pass as Object[] arrays, Object etc. + mhAsSpreader = delegate.asSpreader(Object[].class, 2); + ret = (int) mhAsSpreader.invoke("a", new String[] { "b", "c" }); + assertEquals(42, ret); + + mhAsSpreader = delegate.asSpreader(Object[].class, 2); + ret = (int) mhAsSpreader.invoke("a", new Object[] { "b", "c" }); + assertEquals(42, ret); + + mhAsSpreader = delegate.asSpreader(Object[].class, 2); + ret = (int) mhAsSpreader.invoke("a", (Object) new Object[] { "b", "c" }); + assertEquals(42, ret); + + // Test implicit unboxing. + MethodType methodType2 = MethodType.methodType(int.class, + new Class<?>[] { String.class, int.class }); + MethodHandle delegate2 = MethodHandles.lookup().findStatic( + Main.class, "spreadReferences_Unbox", methodType2); + + // .. with an Integer[] array. + mhAsSpreader = delegate2.asSpreader(Integer[].class, 1); + ret = (int) mhAsSpreader.invoke("a", new Integer[] { 43 }); + assertEquals(43, ret); + + // .. with an Integer[] array declared as an Object[] argument type. + mhAsSpreader = delegate2.asSpreader(Object[].class, 1); + ret = (int) mhAsSpreader.invoke("a", new Integer[] { 43 }); + assertEquals(43, ret); + + // .. with an Object[] array. + mhAsSpreader = delegate2.asSpreader(Object[].class, 1); + ret = (int) mhAsSpreader.invoke("a", new Object[] { Integer.valueOf(43)}); + assertEquals(43, ret); + + // -- Part 2-- + // Run a subset of these tests on MethodHandles.spreadInvoker, which only accepts + // a trailing argument type of Object[]. + MethodHandle spreadInvoker = MethodHandles.spreadInvoker(methodType2, 1); + ret = (int) spreadInvoker.invoke(delegate2, "a", new Object[] { Integer.valueOf(43)}); + assertEquals(43, ret); + + ret = (int) spreadInvoker.invoke(delegate2, "a", new Integer[] { 43 }); + assertEquals(43, ret); + + // NOTE: Annoyingly, the second argument here is leadingArgCount and not + // arrayLength. + spreadInvoker = MethodHandles.spreadInvoker(methodType, 3); + ret = (int) spreadInvoker.invoke(delegate, "a", "b", "c", new String[] {}); + assertEquals(42, ret); + + spreadInvoker = MethodHandles.spreadInvoker(methodType, 0); + ret = (int) spreadInvoker.invoke(delegate, new String[] { "a", "b", "c" }); + assertEquals(42, ret); + + // Exact invokes: Double check that the expected parameter type is + // Object[] and not T[]. + try { + spreadInvoker.invokeExact(delegate, new String[] { "a", "b", "c" }); + fail(); + } catch (WrongMethodTypeException expected) { + } + + ret = (int) spreadInvoker.invoke(delegate, new Object[] { "a", "b", "c" }); + assertEquals(42, ret); + } + + public static int spreadBoolean(String a, Boolean b, boolean c) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c); + return 44; + } + + public static int spreadByte(String a, Byte b, byte c, + short d, int e, long f, float g, double h) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c + + ", d: " + d + ", e: " + e + ", f:" + f + ", g: " + g + + ", h: " + h); + return 45; + } + + public static int spreadChar(String a, Character b, char c, + int d, long e, float f, double g) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c + + ", d: " + d + ", e: " + e + ", f:" + f + ", g: " + g); + return 46; + } + + public static int spreadShort(String a, Short b, short c, + int d, long e, float f, double g) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c + + ", d: " + d + ", e: " + e + ", f:" + f + ", g:" + g); + return 47; + } + + public static int spreadInt(String a, Integer b, int c, + long d, float e, double f) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c + + ", d: " + d + ", e: " + e + ", f:" + f); + return 48; + } + + public static int spreadLong(String a, Long b, long c, float d, double e) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c + + ", d: " + d + ", e: " + e); + return 49; + } + + public static int spreadFloat(String a, Float b, float c, double d) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c + ", d: " + d); + return 50; + } + + public static int spreadDouble(String a, Double b, double c) { + System.out.println("a: " + a + ", b:" + b + ", c: " + c); + return 51; + } + + public static void testSpreaders_primitive() throws Throwable { + // boolean[] + // --------------------- + MethodType type = MethodType.methodType(int.class, + new Class<?>[] { String.class, Boolean.class, boolean.class }); + MethodHandle delegate = MethodHandles.lookup().findStatic( + Main.class, "spreadBoolean", type); + + MethodHandle spreader = delegate.asSpreader(boolean[].class, 2); + int ret = (int) spreader.invokeExact("a", new boolean[] { true, false }); + assertEquals(44, ret); + ret = (int) spreader.invoke("a", new boolean[] { true, false }); + assertEquals(44, ret); + + // boolean can't be cast to String (the first argument to the method). + try { + delegate.asSpreader(boolean[].class, 3); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // int can't be cast to boolean to supply the last argument to the method. + try { + delegate.asSpreader(int[].class, 1); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // byte[] + // --------------------- + type = MethodType.methodType(int.class, + new Class<?>[] { + String.class, Byte.class, byte.class, + short.class, int.class, long.class, + float.class, double.class }); + delegate = MethodHandles.lookup().findStatic(Main.class, "spreadByte", type); + + spreader = delegate.asSpreader(byte[].class, 7); + ret = (int) spreader.invokeExact("a", + new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }); + assertEquals(45, ret); + ret = (int) spreader.invoke("a", + new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }); + assertEquals(45, ret); + + // char[] + // --------------------- + type = MethodType.methodType(int.class, + new Class<?>[] { + String.class, Character.class,char.class, + int.class, long.class, float.class, double.class }); + delegate = MethodHandles.lookup().findStatic(Main.class, "spreadChar", type); + + spreader = delegate.asSpreader(char[].class, 6); + ret = (int) spreader.invokeExact("a", + new char[] { '1', '2', '3', '4', '5', '6' }); + assertEquals(46, ret); + ret = (int) spreader.invokeExact("a", + new char[] { '1', '2', '3', '4', '5', '6' }); + assertEquals(46, ret); + + // short[] + // --------------------- + type = MethodType.methodType(int.class, + new Class<?>[] { + String.class, Short.class, short.class, + int.class, long.class, float.class, double.class }); + delegate = MethodHandles.lookup().findStatic(Main.class, "spreadShort", type); + + spreader = delegate.asSpreader(short[].class, 6); + ret = (int) spreader.invokeExact("a", + new short[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }); + assertEquals(47, ret); + ret = (int) spreader.invoke("a", + new short[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }); + assertEquals(47, ret); + + // int[] + // --------------------- + type = MethodType.methodType(int.class, + new Class<?>[] { + String.class, Integer.class, int.class, + long.class, float.class, double.class }); + delegate = MethodHandles.lookup().findStatic(Main.class, "spreadInt", type); + + spreader = delegate.asSpreader(int[].class, 5); + ret = (int) spreader.invokeExact("a", new int[] { 1, 2, 3, 4, 5 }); + assertEquals(48, ret); + ret = (int) spreader.invokeExact("a", new int[] { 1, 2, 3, 4, 5 }); + assertEquals(48, ret); + + // long[] + // --------------------- + type = MethodType.methodType(int.class, + new Class<?>[] { + String.class, Long.class, long.class, float.class, double.class }); + delegate = MethodHandles.lookup().findStatic(Main.class, "spreadLong", type); + + spreader = delegate.asSpreader(long[].class, 4); + ret = (int) spreader.invokeExact("a", + new long[] { 0x1, 0x2, 0x3, 0x4 }); + assertEquals(49, ret); + ret = (int) spreader.invoke("a", + new long[] { 0x1, 0x2, 0x3, 0x4 }); + assertEquals(49, ret); + + // float[] + // --------------------- + type = MethodType.methodType(int.class, + new Class<?>[] { + String.class, Float.class, float.class, double.class }); + delegate = MethodHandles.lookup().findStatic(Main.class, "spreadFloat", type); + + spreader = delegate.asSpreader(float[].class, 3); + ret = (int) spreader.invokeExact("a", + new float[] { 1.0f, 2.0f, 3.0f }); + assertEquals(50, ret); + ret = (int) spreader.invokeExact("a", + new float[] { 1.0f, 2.0f, 3.0f }); + assertEquals(50, ret); + + // double[] + // --------------------- + type = MethodType.methodType(int.class, + new Class<?>[] { String.class, Double.class, double.class }); + delegate = MethodHandles.lookup().findStatic(Main.class, "spreadDouble", type); + + spreader = delegate.asSpreader(double[].class, 2); + ret = (int) spreader.invokeExact("a", new double[] { 1.0, 2.0 }); + assertEquals(51, ret); + ret = (int) spreader.invokeExact("a", new double[] { 1.0, 2.0 }); + assertEquals(51, ret); + } + + public static void testInvokeWithArguments() throws Throwable { + MethodType methodType = MethodType.methodType(int.class, + new Class<?>[] { String.class, String.class, String.class }); + MethodHandle handle = MethodHandles.lookup().findStatic( + Main.class, "spreadReferences", methodType); + + Object ret = handle.invokeWithArguments(new Object[] { "a", "b", "c"}); + assertEquals(42, (int) ret); + handle.invokeWithArguments(new String[] { "a", "b", "c" }); + assertEquals(42, (int) ret); + + // Pass in an array that's too small. Should throw an IAE. + try { + handle.invokeWithArguments(new Object[] { "a", "b" }); + fail(); + } catch (IllegalArgumentException expected) { + } catch (WrongMethodTypeException expected) { + } + + // Test implicit unboxing. + MethodType methodType2 = MethodType.methodType(int.class, + new Class<?>[] { String.class, int.class }); + MethodHandle handle2 = MethodHandles.lookup().findStatic( + Main.class, "spreadReferences_Unbox", methodType2); + + ret = (int) handle2.invokeWithArguments(new Object[] { "a", 43 }); + assertEquals(43, (int) ret); + } + + public static int collectBoolean(String a, boolean[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 44; + } + + public static int collectByte(String a, byte[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 45; + } + + public static int collectChar(String a, char[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 46; + } + + public static int collectShort(String a, short[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 47; + } + + public static int collectInt(String a, int[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 48; + } + + public static int collectLong(String a, long[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 49; + } + + public static int collectFloat(String a, float[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 50; + } + + public static int collectDouble(String a, double[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 51; + } + + public static int collectCharSequence(String a, CharSequence[] b) { + System.out.println("a: " + a + ", b:" + b[0] + ", c: " + b[1]); + return 99; + } + + public static void testAsCollector() throws Throwable { + // Reference arrays. + // ------------------- + MethodHandle trailingRef = MethodHandles.lookup().findStatic( + Main.class, "collectCharSequence", + MethodType.methodType(int.class, String.class, CharSequence[].class)); + + // int[] is not convertible to CharSequence[].class. + try { + trailingRef.asCollector(int[].class, 1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // Object[] is not convertible to CharSequence[].class. + try { + trailingRef.asCollector(Object[].class, 1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // String[].class is convertible to CharSequence.class + MethodHandle collector = trailingRef.asCollector(String[].class, 2); + assertEquals(99, (int) collector.invoke("a", "b", "c")); + + // Too few arguments should fail with a WMTE. + try { + collector.invoke("a", "b"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Too many arguments should fail with a WMTE. + try { + collector.invoke("a", "b", "c", "d"); + fail(); + } catch (WrongMethodTypeException expected) { + } + + // Sanity checks on other array types. + + MethodHandle target = MethodHandles.lookup().findStatic( + Main.class, "collectBoolean", + MethodType.methodType(int.class, String.class, boolean[].class)); + assertEquals(44, (int) target.asCollector(boolean[].class, 2).invoke("a", true, false)); + + target = MethodHandles.lookup().findStatic(Main.class, "collectByte", + MethodType.methodType(int.class, String.class, byte[].class)); + assertEquals(45, (int) target.asCollector(byte[].class, 2).invoke("a", (byte) 1, (byte) 2)); + + target = MethodHandles.lookup().findStatic(Main.class, "collectChar", + MethodType.methodType(int.class, String.class, char[].class)); + assertEquals(46, (int) target.asCollector(char[].class, 2).invoke("a", 'a', 'b')); + + target = MethodHandles.lookup().findStatic(Main.class, "collectShort", + MethodType.methodType(int.class, String.class, short[].class)); + assertEquals(47, (int) target.asCollector(short[].class, 2).invoke("a", (short) 3, (short) 4)); + + target = MethodHandles.lookup().findStatic(Main.class, "collectInt", + MethodType.methodType(int.class, String.class, int[].class)); + assertEquals(48, (int) target.asCollector(int[].class, 2).invoke("a", 42, 43)); + + target = MethodHandles.lookup().findStatic(Main.class, "collectLong", + MethodType.methodType(int.class, String.class, long[].class)); + assertEquals(49, (int) target.asCollector(long[].class, 2).invoke("a", 100, 99)); + + target = MethodHandles.lookup().findStatic(Main.class, "collectFloat", + MethodType.methodType(int.class, String.class, float[].class)); + assertEquals(50, (int) target.asCollector(float[].class, 2).invoke("a", 8.9f, 9.1f)); + + target = MethodHandles.lookup().findStatic(Main.class, "collectDouble", + MethodType.methodType(int.class, String.class, double[].class)); + assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8)); + } + public static void fail() { System.out.println("FAIL"); Thread.dumpStack(); @@ -931,6 +1384,10 @@ public class Main { Thread.dumpStack(); } + public static void assertEquals(int i1, int i2) { + if (i1 != i2) throw new AssertionError("Expected: " + i1 + " was " + i2); + } + public static void assertEquals(String s1, String s2) { if (s1 == s2) { return; diff --git a/test/Android.bp b/test/Android.bp index 89e409223e..287df1375e 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -173,12 +173,13 @@ art_cc_library { whole_static_libs: [ "libart-compiler-gtest", "libart-runtime-gtest", - "libgtest", + "libgtest" ], shared_libs: [ "libartd", "libartd-compiler", "libbase", + "libbacktrace" ], target: { android: { @@ -317,6 +318,7 @@ cc_defaults { "141-class-unload/jni_unload.cc", "148-multithread-gc-annotations/gc_coverage.cc", "149-suspend-all-stress/suspend_all.cc", + "154-gc-loop/heap_interface.cc", "454-get-vreg/get_vreg_jni.cc", "457-regs/regs_jni.cc", "461-get-reference-vreg/get_reference_vreg_jni.cc", diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 814f968bc9..b4c6b455a2 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -379,6 +379,7 @@ TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := # slows down allocations significantly which these tests do a lot. TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \ 137-cfi \ + 154-gc-loop \ 908-gc-start-finish \ 913-heaps \ 961-default-iface-resolution-gen \ @@ -511,10 +512,13 @@ TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS := # Known broken tests for the interpreter. # CFI unwinding expects managed frames. # 629 requires compilation. +# 934 and 935 are broken due to the PreDefine hook not yet inserting them into the classpath. This should be fixed shortly TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \ 137-cfi \ 554-jit-profile-file \ - 629-vdex-speed + 629-vdex-speed \ + 934-load-transform \ + 935-non-retransformable \ ifneq (,$(filter interpreter,$(COMPILER_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -536,17 +540,22 @@ TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := # flaky as JIT tests. This should be fixed once b/33630159 or b/33616143 are # resolved but until then just disable them. Test 916 already checks this # feature for JIT use cases in a way that is resilient to the jit frames. +# 912: b/34655682 +# 934 and 935 are broken due to the PreDefine hook not yet inserting them into the classpath. This should be fixed shortly TEST_ART_BROKEN_JIT_RUN_TESTS := \ 137-cfi \ 629-vdex-speed \ 902-hello-transformation \ 904-object-allocation \ 906-iterate-heap \ + 912-classes \ 914-hello-obsolescence \ 915-obsolete-2 \ 917-fields-transformation \ 919-obsolete-fields \ 926-multi-obsolescence \ + 934-load-transform \ + 935-non-retransformable \ ifneq (,$(filter jit,$(COMPILER_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ diff --git a/test/ErroneousInit/ErroneousInit.java b/test/ErroneousInit/ErroneousInit.java new file mode 100644 index 0000000000..67b7b204dc --- /dev/null +++ b/test/ErroneousInit/ErroneousInit.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class ErroneousInit { + static { + if (true) { + throw new Error(); + } + } +} diff --git a/test/run-test b/test/run-test index c78fa35e24..a228789701 100755 --- a/test/run-test +++ b/test/run-test @@ -408,7 +408,9 @@ 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 [ ! "$runtime" = "jvm" ]; then + run_args="${run_args} --runtime-option -XX:ThreadSuspendTimeout=$suspend_timeout" +fi if [ "$basic_verify" = "true" ]; then # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests. diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc index 80e1797369..330f7e12eb 100644 --- a/test/ti-agent/common_helper.cc +++ b/test/ti-agent/common_helper.cc @@ -329,6 +329,38 @@ jint OnLoad(JavaVM* vm, } // namespace common_retransform +namespace common_transform { + +using art::common_retransform::CommonClassFileLoadHookRetransformable; + +// Get all capabilities except those related to retransformation. +jint OnLoad(JavaVM* vm, + char* options ATTRIBUTE_UNUSED, + void* reserved ATTRIBUTE_UNUSED) { + if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { + printf("Unable to get jvmti env!\n"); + return 1; + } + // Don't set the retransform caps + jvmtiCapabilities caps; + jvmti_env->GetPotentialCapabilities(&caps); + caps.can_retransform_classes = 0; + caps.can_retransform_any_class = 0; + jvmti_env->AddCapabilities(&caps); + + // Use the same callback as the retransform test. + jvmtiEventCallbacks cb; + memset(&cb, 0, sizeof(cb)); + cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable; + if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) { + printf("Unable to set class file load hook cb!\n"); + return 1; + } + return 0; +} + +} // namespace common_transform + static void BindMethod(jvmtiEnv* jenv, JNIEnv* env, jclass klass, diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h index c60553dc5a..031850147e 100644 --- a/test/ti-agent/common_helper.h +++ b/test/ti-agent/common_helper.h @@ -27,10 +27,15 @@ namespace common_redefine { jint OnLoad(JavaVM* vm, char* options, void* reserved); } // namespace common_redefine + namespace common_retransform { jint OnLoad(JavaVM* vm, char* options, void* reserved); } // namespace common_retransform +namespace common_transform { +jint OnLoad(JavaVM* vm, char* options, void* reserved); +} // namespace common_transform + extern bool RuntimeIsJVM; diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 8ed8e67e42..f5074519b8 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -110,6 +110,8 @@ static AgentLib agents[] = { { "926-multi-obsolescence", common_redefine::OnLoad, nullptr }, { "930-hello-retransform", common_retransform::OnLoad, nullptr }, { "932-transform-saves", common_retransform::OnLoad, nullptr }, + { "934-load-transform", common_retransform::OnLoad, nullptr }, + { "935-non-retransformable", common_transform::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { |