diff options
26 files changed, 282 insertions, 114 deletions
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 606302bd78..03c94a45e3 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -104,7 +104,7 @@ bool VerificationResults::IsClassRejected(ClassReference ref) { bool VerificationResults::IsCandidateForCompilation(MethodReference&, const uint32_t access_flags) { - if (!compiler_options_->IsCompilationEnabled()) { + if (!compiler_options_->IsBytecodeCompilationEnabled()) { return false; } // Don't compile class initializers unless kEverything. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 4d622bdddf..4594e2cc42 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -553,8 +553,8 @@ static void CompileMethod(Thread* self, MethodReference method_ref(&dex_file, method_idx); if ((access_flags & kAccNative) != 0) { - // Are we interpreting only and have support for generic JNI down calls? - if (!driver->GetCompilerOptions().IsCompilationEnabled() && + // Are we extracting only and have support for generic JNI down calls? + if (!driver->GetCompilerOptions().IsJniCompilationEnabled() && InstructionSetHasGenericJniStub(driver->GetInstructionSet())) { // Leaving this empty will trigger the generic JNI version } else { diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index c67ab6ef14..6d4455e65c 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -89,8 +89,12 @@ class CompilerOptions FINAL { return compiler_filter_ == CompilerFilter::kVerifyAtRuntime; } - bool IsCompilationEnabled() const { - return CompilerFilter::IsCompilationEnabled(compiler_filter_); + bool IsBytecodeCompilationEnabled() const { + return CompilerFilter::IsBytecodeCompilationEnabled(compiler_filter_); + } + + bool IsJniCompilationEnabled() const { + return CompilerFilter::IsJniCompilationEnabled(compiler_filter_); } bool IsVerificationEnabled() const { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index cb274dcc09..9f6f4530c7 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1450,25 +1450,6 @@ class Dex2Oat FINAL { class_linker->RegisterDexFile(*dex_file, Runtime::Current()->GetLinearAlloc()))); } - /* - * If we're not in interpret-only or verify-none or verify-at-runtime or verify-profile mode, - * go ahead and compile small applications. Don't bother to check if we're doing the image. - */ - if (!IsBootImage() && - compiler_options_->IsCompilationEnabled() && - compiler_kind_ == Compiler::kQuick) { - size_t num_methods = 0; - for (size_t i = 0; i != dex_files_.size(); ++i) { - const DexFile* dex_file = dex_files_[i]; - CHECK(dex_file != nullptr); - num_methods += dex_file->NumMethodIds(); - } - if (num_methods <= compiler_options_->GetNumDexMethodsThreshold()) { - compiler_options_->SetCompilerFilter(CompilerFilter::kSpeed); - VLOG(compiler) << "Below method threshold, compiling anyways"; - } - } - return true; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 26885ac731..d2e23aac15 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1348,7 +1348,8 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { // The image space is not yet added to the heap, avoid read barriers. mirror::Class* klass = types[j].Read(); - if (klass != nullptr) { + // There may also be boot image classes, + if (space->HasAddress(klass)) { DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); // Update the class loader from the one in the image class loader to the one that loaded // the app image. @@ -1387,6 +1388,9 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( VLOG(image) << PrettyMethod(&m); } } + } else { + DCHECK(klass == nullptr || heap->ObjectIsInBootImageSpace(klass)) + << klass << " " << PrettyClass(klass); } } } @@ -1394,10 +1398,10 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { // The image space is not yet added to the heap, avoid read barriers. mirror::Class* klass = types[j].Read(); - if (klass != nullptr) { + if (space->HasAddress(klass)) { DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); if (kIsDebugBuild) { - if (new_class_set != nullptr) { + if (new_class_set != nullptr) { auto it = new_class_set->Find(GcRoot<mirror::Class>(klass)); DCHECK(it != new_class_set->end()); DCHECK_EQ(it->Read(), klass); @@ -1662,6 +1666,10 @@ bool ClassLinker::AddImageSpace( // resolve the same way, simply flatten the hierarchy in the way the resolution order would be, // and check that the dex file names are the same. for (mirror::ClassLoader* image_class_loader : image_class_loaders) { + if (IsBootClassLoader(soa, image_class_loader)) { + // The dex cache can reference types from the boot class loader. + continue; + } std::list<mirror::String*> image_dex_file_names; std::string temp_error_msg; if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) { diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc index d617caf78c..dc197c1066 100644 --- a/runtime/compiler_filter.cc +++ b/runtime/compiler_filter.cc @@ -20,7 +20,7 @@ namespace art { -bool CompilerFilter::IsCompilationEnabled(Filter filter) { +bool CompilerFilter::IsBytecodeCompilationEnabled(Filter filter) { switch (filter) { case CompilerFilter::kVerifyNone: case CompilerFilter::kVerifyAtRuntime: @@ -39,6 +39,25 @@ bool CompilerFilter::IsCompilationEnabled(Filter filter) { UNREACHABLE(); } +bool CompilerFilter::IsJniCompilationEnabled(Filter filter) { + switch (filter) { + case CompilerFilter::kVerifyNone: + case CompilerFilter::kVerifyAtRuntime: return false; + + case CompilerFilter::kVerifyProfile: + case CompilerFilter::kInterpretOnly: + case CompilerFilter::kSpaceProfile: + case CompilerFilter::kSpace: + case CompilerFilter::kBalanced: + case CompilerFilter::kTime: + case CompilerFilter::kSpeedProfile: + case CompilerFilter::kSpeed: + case CompilerFilter::kEverythingProfile: + case CompilerFilter::kEverything: return true; + } + UNREACHABLE(); +} + bool CompilerFilter::IsVerificationEnabled(Filter filter) { switch (filter) { case CompilerFilter::kVerifyNone: diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h index 6289d8a22c..65d1d987a7 100644 --- a/runtime/compiler_filter.h +++ b/runtime/compiler_filter.h @@ -30,10 +30,10 @@ class CompilerFilter FINAL { // Note: Order here matters. Later filter choices are considered "as good // as" earlier filter choices. enum Filter { - kVerifyNone, // Skip verification and compile nothing except JNI stubs. - kVerifyAtRuntime, // Only compile JNI stubs and verify at runtime. - kVerifyProfile, // Verify only the classes in the profile. - kInterpretOnly, // Verify, and compile only JNI stubs. + kVerifyNone, // Skip verification but mark all classes as verified anyway. + kVerifyAtRuntime, // Delay verication to runtime, do not compile anything. + kVerifyProfile, // Verify only the classes in the profile, compile only JNI stubs. + kInterpretOnly, // Verify everything, compile only JNI stubs. kTime, // Compile methods, but minimize compilation time. kSpaceProfile, // Maximize space savings based on profile. kSpace, // Maximize space savings. @@ -45,8 +45,12 @@ class CompilerFilter FINAL { }; // Returns true if an oat file with this compiler filter contains - // compiled executable code. - static bool IsCompilationEnabled(Filter filter); + // compiled executable code for bytecode. + static bool IsBytecodeCompilationEnabled(Filter filter); + + // Returns true if an oat file with this compiler filter contains + // compiled executable code for JNI methods. + static bool IsJniCompilationEnabled(Filter filter); // Returns true if this compiler filter requires running verification. static bool IsVerificationEnabled(Filter filter); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index d8325525ac..1fb8b407ec 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -582,7 +582,7 @@ class UpdateEntryPointsClassVisitor : public ClassVisitor { if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) && !m.IsNative() && !m.IsProxyMethod()) { - instrumentation_->UpdateMethodsCode(&m, GetQuickToInterpreterBridge()); + instrumentation_->UpdateMethodsCodeFromDebugger(&m, GetQuickToInterpreterBridge()); } } return true; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 34bc458575..61119f8499 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -687,8 +687,7 @@ void Instrumentation::ResetQuickAllocEntryPoints() { } } -void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_code) { - DCHECK(method->GetDeclaringClass()->IsResolved()); +void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code) { const void* new_quick_code; if (LIKELY(!instrumentation_stubs_installed_)) { new_quick_code = quick_code; @@ -710,6 +709,18 @@ void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_cod UpdateEntrypoints(method, new_quick_code); } +void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_code) { + DCHECK(method->GetDeclaringClass()->IsResolved()); + UpdateMethodsCodeImpl(method, quick_code); +} + +void Instrumentation::UpdateMethodsCodeFromDebugger(ArtMethod* method, const void* quick_code) { + // When debugger attaches, we may update the entry points of all methods of a class + // to the interpreter bridge. A method's declaring class might not be in resolved + // state yet in that case. + UpdateMethodsCodeImpl(method, quick_code); +} + bool Instrumentation::AddDeoptimizedMethod(ArtMethod* method) { if (IsDeoptimizedMethod(method)) { // Already in the map. Return. diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index a4c3d41537..ce6ead453f 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -227,6 +227,10 @@ class Instrumentation { void UpdateMethodsCode(ArtMethod* method, const void* quick_code) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_); + // Update the code of a method respecting any installed stubs from debugger. + void UpdateMethodsCodeFromDebugger(ArtMethod* method, const void* quick_code) + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_); + // Get the quick code for the given method. More efficient than asking the class linker as it // will short-cut to GetCode if instrumentation and static method resolution stubs aren't // installed. @@ -493,6 +497,9 @@ class Instrumentation { SHARED_REQUIRES(Locks::mutator_lock_, deoptimized_methods_lock_); bool IsDeoptimizedMethodsEmpty() const SHARED_REQUIRES(Locks::mutator_lock_, deoptimized_methods_lock_); + void UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code) + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_); + // Have we hijacked ArtMethod::code_ so that it calls instrumentation/interpreter code? bool instrumentation_stubs_installed_; diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 1d0e600e4d..8c42b3abce 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -484,6 +484,36 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive self->PopShadowFrame(); } +static bool IsStringInit(const Instruction* instr, ArtMethod* caller) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (instr->Opcode() == Instruction::INVOKE_DIRECT || + instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) { + // Instead of calling ResolveMethod() which has suspend point and can trigger + // GC, look up the callee method symbolically. + uint16_t callee_method_idx = (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ? + instr->VRegB_3rc() : instr->VRegB_35c(); + const DexFile* dex_file = caller->GetDexFile(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(callee_method_idx); + const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_); + const char* method_name = dex_file->GetMethodName(method_id); + // Compare method's class name and method name against string init. + // It's ok since it's not allowed to create your own java/lang/String. + // TODO: verify that assumption. + if ((strcmp(class_name, "Ljava/lang/String;") == 0) && + (strcmp(method_name, "<init>") == 0)) { + return true; + } + } + return false; +} + +static int16_t GetReceiverRegisterForStringInit(const Instruction* instr) { + DCHECK(instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE || + instr->Opcode() == Instruction::INVOKE_DIRECT); + return (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ? + instr->VRegC_3rc() : instr->VRegC_35c(); +} + void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, bool from_code, @@ -519,22 +549,38 @@ void EnterInterpreterFromDeoptimize(Thread* self, // TODO: should be tested more once b/17586779 is fixed. const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]); if (instr->IsInvoke()) { + if (IsStringInit(instr, shadow_frame->GetMethod())) { + uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr); + // Move the StringFactory.newStringFromChars() result into the register representing + // "this object" when invoking the string constructor in the original dex instruction. + // Also move the result into all aliases. + DCHECK(value.GetL()->IsString()); + SetStringInitValueToAllAliases(shadow_frame, this_obj_vreg, value); + // Calling string constructor in the original dex code doesn't generate a result value. + value.SetJ(0); + } new_dex_pc = dex_pc + instr->SizeInCodeUnits(); } else if (instr->Opcode() == Instruction::NEW_INSTANCE) { // It's possible to deoptimize at a NEW_INSTANCE dex instruciton that's for a // java string, which is turned into a call into StringFactory.newEmptyString(); + // Move the StringFactory.newEmptyString() result into the destination register. + DCHECK(value.GetL()->IsString()); + shadow_frame->SetVRegReference(instr->VRegA_21c(), value.GetL()); + // new-instance doesn't generate a result value. + value.SetJ(0); + // Skip the dex instruction since we essentially come back from an invocation. + new_dex_pc = dex_pc + instr->SizeInCodeUnits(); if (kIsDebugBuild) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + // This is a suspend point. But it's ok since value has been set into shadow_frame. mirror::Class* klass = class_linker->ResolveType( instr->VRegB_21c(), shadow_frame->GetMethod()); DCHECK(klass->IsStringClass()); } - // Skip the dex instruction since we essentially come back from an invocation. - new_dex_pc = dex_pc + instr->SizeInCodeUnits(); } else { - DCHECK(false) << "Unexpected instruction opcode " << instr->Opcode() - << " at dex_pc " << dex_pc - << " of method: " << PrettyMethod(shadow_frame->GetMethod(), false); + CHECK(false) << "Unexpected instruction opcode " << instr->Opcode() + << " at dex_pc " << dex_pc + << " of method: " << PrettyMethod(shadow_frame->GetMethod(), false); } } else { // Nothing to do, the dex_pc is the one at which the code requested diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 12d70c5244..53d5e43989 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -540,6 +540,30 @@ void ArtInterpreterToCompiledCodeBridge(Thread* self, result, method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty()); } +void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, + uint16_t this_obj_vreg, + JValue result) + SHARED_REQUIRES(Locks::mutator_lock_) { + Object* existing = shadow_frame->GetVRegReference(this_obj_vreg); + if (existing == nullptr) { + // If it's null, we come from compiled code that was deoptimized. Nothing to do, + // as the compiler verified there was no alias. + // Set the new string result of the StringFactory. + shadow_frame->SetVRegReference(this_obj_vreg, result.GetL()); + return; + } + // Set the string init result into all aliases. + for (uint32_t i = 0, e = shadow_frame->NumberOfVRegs(); i < e; ++i) { + if (shadow_frame->GetVRegReference(i) == existing) { + DCHECK_EQ(shadow_frame->GetVRegReference(i), + reinterpret_cast<mirror::Object*>(shadow_frame->GetVReg(i))); + shadow_frame->SetVRegReference(i, result.GetL()); + DCHECK_EQ(shadow_frame->GetVRegReference(i), + reinterpret_cast<mirror::Object*>(shadow_frame->GetVReg(i))); + } + } +} + template <bool is_range, bool do_assignability_check, size_t kVarArgMax> @@ -739,24 +763,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, } if (string_init && !self->IsExceptionPending()) { - mirror::Object* existing = shadow_frame.GetVRegReference(string_init_vreg_this); - if (existing == nullptr) { - // If it's null, we come from compiled code that was deoptimized. Nothing to do, - // as the compiler verified there was no alias. - // Set the new string result of the StringFactory. - shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL()); - } else { - // Replace the fake string that was allocated with the StringFactory result. - for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { - if (shadow_frame.GetVRegReference(i) == existing) { - DCHECK_EQ(shadow_frame.GetVRegReference(i), - reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i))); - shadow_frame.SetVRegReference(i, result->GetL()); - DCHECK_EQ(shadow_frame.GetVRegReference(i), - reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i))); - } - } - } + SetStringInitValueToAllAliases(&shadow_frame, string_init_vreg_this, *result); } return !self->IsExceptionPending(); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 69376fd7a1..cc470f372b 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -1021,6 +1021,12 @@ void ArtInterpreterToCompiledCodeBridge(Thread* self, ShadowFrame* shadow_frame, JValue* result); +// Set string value created from StringFactory.newStringFromXXX() into all aliases of +// StringFactory.newEmptyString(). +void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, + uint16_t this_obj_vreg, + JValue result); + // Explicitly instantiate all DoInvoke functions. #define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \ template SHARED_REQUIRES(Locks::mutator_lock_) \ diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index b0a786e9e3..c5d3ccd748 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -35,6 +35,8 @@ namespace art { // with all profile savers running at the same time. static constexpr const uint64_t kMinSavePeriodNs = MsToNs(20 * 1000); // 20 seconds static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds +// Minimum number of JIT samples during launch to include a method into the profile. +static constexpr const size_t kStartupMethodSamples = 1; static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10; static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10; @@ -108,7 +110,7 @@ void ProfileSaver::Run() { } total_ms_of_sleep_ += kSaveResolvedClassesDelayMs; } - FetchAndCacheResolvedClasses(); + FetchAndCacheResolvedClassesAndMethods(); // Loop for the profiled methods. while (!ShuttingDown(self)) { @@ -204,11 +206,48 @@ ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& f return &info_it->second; } -void ProfileSaver::FetchAndCacheResolvedClasses() { +// Get resolved methods that have a profile info or more than kStartupMethodSamples samples. +// Excludes native methods and classes in the boot image. +class GetMethodsVisitor : public ClassVisitor { + public: + explicit GetMethodsVisitor(std::vector<MethodReference>* methods) : methods_(methods) {} + + virtual bool operator()(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) { + if (Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { + return true; + } + for (ArtMethod& method : klass->GetMethods(sizeof(void*))) { + if (!method.IsNative()) { + if (method.GetCounter() >= kStartupMethodSamples || + method.GetProfilingInfo(sizeof(void*)) != nullptr) { + // Have samples, add to profile. + const DexFile* dex_file = method.GetInterfaceMethodIfProxy(sizeof(void*))->GetDexFile(); + methods_->push_back(MethodReference(dex_file, method.GetDexMethodIndex())); + } + } + } + return true; + } + + private: + std::vector<MethodReference>* const methods_; +}; + +void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { ScopedTrace trace(__PRETTY_FUNCTION__); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); std::set<DexCacheResolvedClasses> resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/ true); + + std::vector<MethodReference> methods; + { + ScopedTrace trace2("Get hot methods"); + GetMethodsVisitor visitor(&methods); + ScopedObjectAccess soa(Thread::Current()); + class_linker->VisitClasses(&visitor); + VLOG(profiler) << "Methods with samples greater than " + << kStartupMethodSamples << " = " << methods.size(); + } MutexLock mu(Thread::Current(), *Locks::profiler_lock_); uint64_t total_number_of_profile_entries_cached = 0; @@ -216,11 +255,16 @@ void ProfileSaver::FetchAndCacheResolvedClasses() { std::set<DexCacheResolvedClasses> resolved_classes_for_location; const std::string& filename = it.first; const std::set<std::string>& locations = it.second; - + std::vector<MethodReference> methods_for_location; + for (const MethodReference& ref : methods) { + if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) { + methods_for_location.push_back(ref); + } + } for (const DexCacheResolvedClasses& classes : resolved_classes) { if (locations.find(classes.GetBaseLocation()) != locations.end()) { - VLOG(profiler) << "Added classes for location " << classes.GetBaseLocation() - << " (" << classes.GetDexLocation() << ")"; + VLOG(profiler) << "Added " << classes.GetClasses().size() << " classes for location " + << classes.GetBaseLocation() << " (" << classes.GetDexLocation() << ")"; resolved_classes_for_location.insert(classes); } else { VLOG(profiler) << "Location not found " << classes.GetBaseLocation() @@ -228,7 +272,7 @@ void ProfileSaver::FetchAndCacheResolvedClasses() { } } ProfileCompilationInfo* info = GetCachedProfiledInfo(filename); - info->AddMethodsAndClasses(std::vector<MethodReference>(), resolved_classes_for_location); + info->AddMethodsAndClasses(methods_for_location, resolved_classes_for_location); total_number_of_profile_entries_cached += resolved_classes_for_location.size(); } max_number_of_profile_entries_cached_ = std::max( diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index c6da95931c..9c6d0fa1cf 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -95,9 +95,9 @@ class ProfileSaver { // If no entry exists, a new empty one will be created, added to the cache and // then returned. ProfileCompilationInfo* GetCachedProfiledInfo(const std::string& filename); - // Fetches the current resolved classes from the ClassLinker and stores them - // in the profile_cache_ for later save. - void FetchAndCacheResolvedClasses(); + // Fetches the current resolved classes and methods from the ClassLinker and stores them in the + // profile_cache_ for later save. + void FetchAndCacheResolvedClassesAndMethods(); static bool MaybeRecordDexUseInternal( const std::string& dex_location, diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 2da3d8479c..2894b68f03 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -148,9 +148,7 @@ inline void DexCache::FixupStrings(GcRoot<mirror::String>* dest, const Visitor& for (size_t i = 0, count = NumStrings(); i < count; ++i) { mirror::String* source = src[i].Read<kReadBarrierOption>(); mirror::String* new_source = visitor(source); - if (source != new_source) { - dest[i] = GcRoot<mirror::String>(new_source); - } + dest[i] = GcRoot<mirror::String>(new_source); } } @@ -160,9 +158,7 @@ inline void DexCache::FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visi for (size_t i = 0, count = NumResolvedTypes(); i < count; ++i) { mirror::Class* source = src[i].Read<kReadBarrierOption>(); mirror::Class* new_source = visitor(source); - if (source != new_source) { - dest[i] = GcRoot<mirror::Class>(new_source); - } + dest[i] = GcRoot<mirror::Class>(new_source); } } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index a64a6dfb3d..28fd9b98cd 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -153,7 +153,7 @@ bool OatFileAssistant::OdexFileCompilerFilterIsOkay(CompilerFilter::Filter targe } OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target) { - bool compilation_desired = CompilerFilter::IsCompilationEnabled(target); + bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target); // See if the oat file is in good shape as is. bool oat_okay = OatFileCompilerFilterIsOkay(target); @@ -573,7 +573,7 @@ bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) { CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter(); - if (CompilerFilter::IsCompilationEnabled(current_compiler_filter)) { + if (CompilerFilter::IsBytecodeCompilationEnabled(current_compiler_filter)) { if (!file.IsPic()) { const ImageInfo* image_info = GetImageInfo(); if (image_info == nullptr) { diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 78ed5893de..ccab48cdc5 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -233,7 +233,7 @@ class OatFileAssistantTest : public CommonRuntimeTest { EXPECT_TRUE(odex_file->HasPatchInfo()); EXPECT_EQ(filter, odex_file->GetCompilerFilter()); - if (CompilerFilter::IsCompilationEnabled(filter)) { + if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) { const std::vector<gc::space::ImageSpace*> image_spaces = runtime->GetHeap()->GetBootImageSpaces(); ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr); diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc index 1f33651243..ba71dc3c6e 100644 --- a/runtime/openjdkjvm/OpenjdkJvm.cc +++ b/runtime/openjdkjvm/OpenjdkJvm.cc @@ -58,11 +58,6 @@ #include <sys/socket.h> #include <sys/ioctl.h> -#ifdef __ANDROID__ -// This function is provided by android linker. -extern "C" void android_update_LD_LIBRARY_PATH(const char* ld_library_path); -#endif // __ANDROID__ - #undef LOG_TAG #define LOG_TAG "artopenjdk" @@ -324,22 +319,6 @@ JNIEXPORT __attribute__((noreturn)) void JVM_Exit(jint status) { exit(status); } -static void SetLdLibraryPath(JNIEnv* env, jstring javaLdLibraryPath) { -#ifdef __ANDROID__ - if (javaLdLibraryPath != nullptr) { - ScopedUtfChars ldLibraryPath(env, javaLdLibraryPath); - if (ldLibraryPath.c_str() != nullptr) { - android_update_LD_LIBRARY_PATH(ldLibraryPath.c_str()); - } - } - -#else - LOG(WARNING) << "android_update_LD_LIBRARY_PATH not found; .so dependencies will not work!"; - UNUSED(javaLdLibraryPath, env); -#endif -} - - JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, jobject javaLoader, @@ -349,17 +328,6 @@ JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, return NULL; } - int32_t target_sdk_version = art::Runtime::Current()->GetTargetSdkVersion(); - - // Starting with N nativeLoad uses classloader local - // linker namespace instead of global LD_LIBRARY_PATH - // (23 is Marshmallow). This call is here to preserve - // backwards compatibility for the apps targeting sdk - // version <= 23 - if (target_sdk_version == 0) { - SetLdLibraryPath(env, javaLibrarySearchPath); - } - std::string error_msg; { art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM(); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 2b96328f80..b2be770bf7 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -4652,7 +4652,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) << " from other class " << GetDeclaringClass(); - return; + // Keep hunting for possible hard fails. } } diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc index 0dab4007a7..c6a2e9a5a8 100644 --- a/test/117-nopatchoat/nopatchoat.cc +++ b/test/117-nopatchoat/nopatchoat.cc @@ -55,7 +55,7 @@ class NoPatchoatTest { const OatFile* oat_file = oat_dex_file->GetOatFile(); return !oat_file->IsPic() - && CompilerFilter::IsCompilationEnabled(oat_file->GetCompilerFilter()); + && CompilerFilter::IsBytecodeCompilationEnabled(oat_file->GetCompilerFilter()); } }; diff --git a/test/597-deopt-new-string/src/Main.java b/test/597-deopt-new-string/src/Main.java index 1224e407b0..e78f0d36ec 100644 --- a/test/597-deopt-new-string/src/Main.java +++ b/test/597-deopt-new-string/src/Main.java @@ -48,7 +48,12 @@ public class Main implements Runnable { throw new Error(); } char[] arr = {'a', 'b', 'c'}; - return new String(arr, 0, arr.length); + String str = new String(arr, 0, arr.length); + if (!str.equals("abc")) { + System.out.println("Failure 1! " + str); + System.exit(0); + } + return str; } public void run() { @@ -68,7 +73,11 @@ public class Main implements Runnable { } else { // This thread keeps doing new String() from a char array. while (!done) { - $noinline$run0(); + String str = $noinline$run0(); + if (!str.equals("abc")) { + System.out.println("Failure 2! " + str); + System.exit(0); + } } } } diff --git a/test/600-verifier-fails/expected.txt b/test/600-verifier-fails/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/600-verifier-fails/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/600-verifier-fails/info.txt b/test/600-verifier-fails/info.txt new file mode 100644 index 0000000000..478dd9bceb --- /dev/null +++ b/test/600-verifier-fails/info.txt @@ -0,0 +1,4 @@ +The situation in this test was discovered by running dexfuzz on +another fuzzingly random generated Java test. The soft verification +fail (on the final field modification) should not hide the hard +verification fail (on the type mismatch) to avoid a crash later on. diff --git a/test/600-verifier-fails/smali/sput.smali b/test/600-verifier-fails/smali/sput.smali new file mode 100644 index 0000000000..87f3799e02 --- /dev/null +++ b/test/600-verifier-fails/smali/sput.smali @@ -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 public LA; +.super Ljava/lang/Object; + +.method public foo(I)V +.registers 2 + sput v1, LMain;->staticField:Ljava/lang/String; + return-void +.end method diff --git a/test/600-verifier-fails/src/Main.java b/test/600-verifier-fails/src/Main.java new file mode 100644 index 0000000000..ba4cc31ad7 --- /dev/null +++ b/test/600-verifier-fails/src/Main.java @@ -0,0 +1,30 @@ +/* + * 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; + +public class Main { + + public static final String staticField = null; + + public static void main(String[] args) throws Exception { + try { + Class<?> a = Class.forName("A"); + } catch (java.lang.VerifyError e) { + System.out.println("passed"); + } + } +} |