diff options
-rw-r--r-- | CleanSpec.mk | 2 | ||||
-rw-r--r-- | compiler/debug/dwarf/dwarf_test.cc | 12 | ||||
-rw-r--r-- | libartbase/base/common_art_test.cc | 2 | ||||
-rw-r--r-- | oatdump/oatdump.cc | 51 | ||||
-rw-r--r-- | oatdump/oatdump_app_test.cc | 12 | ||||
-rw-r--r-- | oatdump/oatdump_test.h | 16 | ||||
-rw-r--r-- | openjdkjvmti/ti_thread.cc | 33 | ||||
-rw-r--r-- | runtime/class_linker.cc | 14 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 4 | ||||
-rw-r--r-- | runtime/runtime.cc | 47 | ||||
-rw-r--r-- | runtime/runtime.h | 4 | ||||
-rw-r--r-- | runtime/runtime_options.def | 1 | ||||
-rw-r--r-- | runtime/thread.cc | 42 | ||||
-rw-r--r-- | runtime/thread.h | 2 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 4 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 2 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/src/Main.java | 2 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/src/art/Test1919.java | 14 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/vminit.cc | 9 | ||||
-rw-r--r-- | test/ProfileTestMultiDex/Second.java | 8 | ||||
-rw-r--r-- | tools/luci/config/cr-buildbucket.cfg | 2 |
21 files changed, 227 insertions, 56 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index f621bcb9aa..55c7285811 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -58,6 +58,8 @@ $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libandroidicu.so) $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libpac.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest*/art_libdexfile_support_tests/dex_file_supp_test) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest*/) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest*/) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc index 212fd63efa..5491596dd8 100644 --- a/compiler/debug/dwarf/dwarf_test.cc +++ b/compiler/debug/dwarf/dwarf_test.cc @@ -73,13 +73,11 @@ TEST_F(DwarfTest, DebugFrame) { opcodes.SameValue(reg); DW_CHECK_NEXT("DW_CFA_same_value: r6 (esi)"); opcodes.Offset(Reg(0x3F), -offset); - // Bad register likely means that it does not exist on x86, - // but we want to test high register numbers anyway. - DW_CHECK_NEXT("DW_CFA_offset: bad register: r63 at cfa-40000"); + DW_CHECK_NEXT("DW_CFA_offset: r63 at cfa-40000"); opcodes.Offset(Reg(0x40), -offset); - DW_CHECK_NEXT("DW_CFA_offset_extended: bad register: r64 at cfa-40000"); + DW_CHECK_NEXT("DW_CFA_offset_extended: r64 at cfa-40000"); opcodes.Offset(Reg(0x40), offset); - DW_CHECK_NEXT("DW_CFA_offset_extended_sf: bad register: r64 at cfa+40000"); + DW_CHECK_NEXT("DW_CFA_offset_extended_sf: r64 at cfa+40000"); opcodes.ValOffset(reg, -offset); DW_CHECK_NEXT("DW_CFA_val_offset: r6 (esi) at cfa-40000"); opcodes.ValOffset(reg, offset); @@ -131,7 +129,7 @@ TEST_F(DwarfTest, DebugFrame) { CheckObjdumpOutput(is64bit, "-W"); } -TEST_F(DwarfTest, DebugFrame64) { +TEST_F(DwarfTest, DISABLED_DebugFrame64) { constexpr bool is64bit = true; DebugFrameOpCodeWriter<> initial_opcodes; WriteCIE(is64bit, Reg(16), initial_opcodes, &debug_frame_data_); @@ -184,7 +182,7 @@ TEST_F(DwarfTest, x86_64_RegisterMapping) { CheckObjdumpOutput(is64bit, "-W"); } -TEST_F(DwarfTest, DebugLine) { +TEST_F(DwarfTest, DISABLED_DebugLine) { const bool is64bit = false; const int code_factor_bits = 1; DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits); diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc index 18e1bf0dc5..916f072799 100644 --- a/libartbase/base/common_art_test.cc +++ b/libartbase/base/common_art_test.cc @@ -243,7 +243,7 @@ std::string CommonArtTestImpl::GetAndroidToolsDir(const std::string& subdir1, std::string CommonArtTestImpl::GetAndroidHostToolsDir() { return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host", - "x86_64-linux-glibc2.15", + "x86_64-linux-glibc2.17", "x86_64-linux"); } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 14ff1aed9d..35f8ee2ce2 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1619,6 +1619,24 @@ class OatDumper { } } + std::pair<const uint8_t*, const uint8_t*> GetBootImageLiveObjectsDataRange(gc::Heap* heap) const + REQUIRES_SHARED(Locks::mutator_lock_) { + const std::vector<gc::space::ImageSpace*>& boot_image_spaces = heap->GetBootImageSpaces(); + const ImageHeader& main_header = boot_image_spaces[0]->GetImageHeader(); + ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = + ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast( + main_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kBootImageLiveObjects)); + DCHECK(boot_image_live_objects != nullptr); + DCHECK(heap->ObjectIsInBootImageSpace(boot_image_live_objects)); + const uint8_t* boot_image_live_objects_address = + reinterpret_cast<const uint8_t*>(boot_image_live_objects.Ptr()); + uint32_t begin_offset = mirror::ObjectArray<mirror::Object>::OffsetOfElement(0).Uint32Value(); + uint32_t end_offset = mirror::ObjectArray<mirror::Object>::OffsetOfElement( + boot_image_live_objects->GetLength()).Uint32Value(); + return std::make_pair(boot_image_live_objects_address + begin_offset, + boot_image_live_objects_address + end_offset); + } + void DumpDataBimgRelRoEntries(std::ostream& os) { os << ".data.bimg.rel.ro: "; if (oat_file_.GetBootImageRelocations().empty()) { @@ -1632,28 +1650,39 @@ class OatDumper { const std::vector<gc::space::ImageSpace*>& boot_image_spaces = runtime->GetHeap()->GetBootImageSpaces(); ScopedObjectAccess soa(Thread::Current()); + auto live_objects = GetBootImageLiveObjectsDataRange(runtime->GetHeap()); + const uint8_t* live_objects_begin = live_objects.first; + const uint8_t* live_objects_end = live_objects.second; for (const uint32_t& object_offset : oat_file_.GetBootImageRelocations()) { uint32_t entry_index = &object_offset - oat_file_.GetBootImageRelocations().data(); uint32_t entry_offset = entry_index * sizeof(oat_file_.GetBootImageRelocations()[0]); os << StringPrintf(" 0x%x: 0x%08x", entry_offset, object_offset); - uint8_t* object = boot_image_spaces[0]->Begin() + object_offset; + uint8_t* address = boot_image_spaces[0]->Begin() + object_offset; bool found = false; for (gc::space::ImageSpace* space : boot_image_spaces) { - uint64_t local_offset = object - space->Begin(); + uint64_t local_offset = address - space->Begin(); if (local_offset < space->GetImageHeader().GetImageSize()) { if (space->GetImageHeader().GetObjectsSection().Contains(local_offset)) { - ObjPtr<mirror::Object> o = reinterpret_cast<mirror::Object*>(object); - if (o->IsString()) { - os << " String: " << o->AsString()->ToModifiedUtf8(); - } else if (o->IsClass()) { - os << " Class: " << o->AsClass()->PrettyDescriptor(); - } else { - os << StringPrintf(" 0x%08x %s", + if (address >= live_objects_begin && address < live_objects_end) { + size_t index = + (address - live_objects_begin) / sizeof(mirror::HeapReference<mirror::Object>); + os << StringPrintf(" 0x%08x BootImageLiveObject[%zu]", object_offset, - o->GetClass()->PrettyDescriptor().c_str()); + index); + } else { + ObjPtr<mirror::Object> o = reinterpret_cast<mirror::Object*>(address); + if (o->IsString()) { + os << " String: " << o->AsString()->ToModifiedUtf8(); + } else if (o->IsClass()) { + os << " Class: " << o->AsClass()->PrettyDescriptor(); + } else { + os << StringPrintf(" 0x%08x %s", + object_offset, + o->GetClass()->PrettyDescriptor().c_str()); + } } } else if (space->GetImageHeader().GetMethodsSection().Contains(local_offset)) { - ArtMethod* m = reinterpret_cast<ArtMethod*>(object); + ArtMethod* m = reinterpret_cast<ArtMethod*>(address); os << " ArtMethod: " << m->PrettyMethod(); } else { os << StringPrintf(" 0x%08x <unexpected section in %s>", diff --git a/oatdump/oatdump_app_test.cc b/oatdump/oatdump_app_test.cc index 2b04a0d295..83e5f51d41 100644 --- a/oatdump/oatdump_app_test.cc +++ b/oatdump/oatdump_app_test.cc @@ -28,4 +28,16 @@ TEST_F(OatDumpTest, TestAppWithBootImageStatic) { ASSERT_TRUE(Exec(kStatic, kModeOatWithBootImage, {}, kListAndCode)); } +TEST_F(OatDumpTest, TestAppImageWithBootImage) { + const std::string app_image_arg = "--app-image-file=" + GetAppImageName(); + ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M", app_image_arg})); + ASSERT_TRUE(Exec(kDynamic, kModeAppImage, {}, kListAndCode)); +} +TEST_F(OatDumpTest, TestAppImageWithBootImageStatic) { + TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); + const std::string app_image_arg = "--app-image-file=" + GetAppImageName(); + ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M", app_image_arg})); + ASSERT_TRUE(Exec(kStatic, kModeAppImage, {}, kListAndCode)); +} + } // namespace art diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h index 3ead8de905..c4f29677a9 100644 --- a/oatdump/oatdump_test.h +++ b/oatdump/oatdump_test.h @@ -92,6 +92,7 @@ class OatDumpTest : public CommonRuntimeTest { kModeOat, kModeCoreOat, kModeOatWithBootImage, + kModeAppImage, kModeArt, kModeSymbolize, }; @@ -108,6 +109,10 @@ class OatDumpTest : public CommonRuntimeTest { return "ProfileTestMultiDex"; } + std::string GetAppImageName() { + return tmp_dir_ + "/" + GetAppBaseName() + ".art"; + } + std::string GetAppOdexName() { return tmp_dir_ + "/" + GetAppBaseName() + ".odex"; } @@ -200,6 +205,17 @@ class OatDumpTest : public CommonRuntimeTest { exec_argv.push_back("--instruction-set=" + std::string( GetInstructionSetString(kRuntimeISA))); exec_argv.push_back("--oat-file=" + GetAppOdexName()); + } else if (mode == kModeAppImage) { + exec_argv.push_back("--runtime-arg"); + exec_argv.push_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames())); + exec_argv.push_back("--runtime-arg"); + exec_argv.push_back( + GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations())); + exec_argv.push_back("--image=" + GetCoreArtLocation()); + exec_argv.push_back("--instruction-set=" + std::string( + GetInstructionSetString(kRuntimeISA))); + exec_argv.push_back("--app-oat=" + GetAppOdexName()); + exec_argv.push_back("--app-image=" + GetAppImageName()); } else if (mode == kModeCoreOat) { exec_argv.push_back("--oat-file=" + core_oat_location_); } else { diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index 051db4c67e..c6798bb1c9 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -47,6 +47,7 @@ #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" +#include "mirror/throwable.h" #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_utf_chars.h" #include "obj_ptr.h" @@ -83,6 +84,17 @@ struct ThreadCallback : public art::ThreadLifecycleCallback { } void ThreadStart(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) { + // Needs to be checked first because we might start these threads before we actually send the + // VMInit event. + if (self->IsSystemDaemon()) { + // System daemon threads are things like the finalizer or gc thread. It would be dangerous to + // allow agents to get in the way of these threads starting up. These threads include things + // like the HeapTaskDaemon and the finalizer daemon. + // + // This event can happen during the time before VMInit or just after zygote fork. Since the + // second is hard to distinguish we unfortunately cannot really check the state here. + return; + } if (!started) { // Runtime isn't started. We only expect at most the signal handler or JIT threads to be // started here. @@ -132,16 +144,35 @@ void ThreadUtil::VMInitEventSent() { gThreadCallback.Post<ArtJvmtiEvent::kThreadStart>(art::Thread::Current()); } + +static void WaitForSystemDaemonStart(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { + { + art::ScopedThreadStateChange strc(self, art::kNative); + JNIEnv* jni = self->GetJniEnv(); + jni->CallStaticVoidMethod(art::WellKnownClasses::java_lang_Daemons, + art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart); + } + if (self->IsExceptionPending()) { + LOG(WARNING) << "Exception occurred when waiting for system daemons to start: " + << self->GetException()->Dump(); + self->ClearException(); + } +} + void ThreadUtil::CacheData() { // We must have started since it is now safe to cache our data; gThreadCallback.started = true; - art::ScopedObjectAccess soa(art::Thread::Current()); + art::Thread* self = art::Thread::Current(); + art::ScopedObjectAccess soa(self); art::ObjPtr<art::mirror::Class> thread_class = soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread); CHECK(thread_class != nullptr); context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;"); CHECK(context_class_loader_ != nullptr); + // Now wait for all required system threads to come up before allowing the rest of loading to + // continue. + WaitForSystemDaemonStart(self); } void ThreadUtil::Unregister() { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 65fe4e4194..89c6a0b717 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4635,6 +4635,20 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); // In case we run without an image there won't be a backing oat file. if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) { + if (!kIsDebugBuild && klass->GetClassLoader() == nullptr) { + // For boot classpath classes in the case we're not using a default boot image: + // we don't have the infrastructure yet to query verification data on individual + // boot vdex files, so it's simpler for now to consider all boot classpath classes + // verified. This should be taken into account when measuring boot time and app + // startup compare to the (current) production system where both: + // 1) updatable boot classpath classes, and + // 2) classes in /system referencing updatable classes + // will be verified at runtime. + if (!Runtime::Current()->IsUsingDefaultBootImageLocation()) { + oat_file_class_status = ClassStatus::kVerified; + return true; + } + } return false; } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index e644c04d96..6423f3b21b 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -219,9 +219,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xjnitrace:_") .WithType<std::string>() .IntoKey(M::JniTrace) - .Define("-Xpatchoat:_") - .WithType<std::string>() - .IntoKey(M::PatchOat) .Define({"-Xrelocate", "-Xnorelocate"}) .WithValues({true, false}) .IntoKey(M::Relocate) @@ -736,7 +733,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xcompiler:filename\n"); UsageMessage(stream, " -Xcompiler-option dex2oat-option\n"); UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n"); - UsageMessage(stream, " -Xpatchoat:filename (obsolete, ignored)\n"); UsageMessage(stream, " -Xusejit:booleanvalue\n"); UsageMessage(stream, " -Xjitinitialsize:N\n"); UsageMessage(stream, " -Xjitmaxsize:N\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1465b14d44..aa3578015c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -869,14 +869,21 @@ bool Runtime::Start() { GetInstructionSetString(kRuntimeISA)); } - // Send the initialized phase event. Send it before starting daemons, as otherwise - // sending thread events becomes complicated. + StartDaemonThreads(); + + // Make sure the environment is still clean (no lingering local refs from starting daemon + // threads). { ScopedObjectAccess soa(self); - callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit); + self->GetJniEnv()->AssertLocalsEmpty(); } - StartDaemonThreads(); + // Send the initialized phase event. Send it after starting the Daemon threads so that agents + // cannot delay the daemon threads from starting forever. + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit); + } { ScopedObjectAccess soa(self); @@ -1006,9 +1013,9 @@ void Runtime::StartDaemonThreads() { VLOG(startup) << "Runtime::StartDaemonThreads exiting"; } -static size_t OpenDexFiles(ArrayRef<const std::string> dex_filenames, - ArrayRef<const std::string> dex_locations, - std::vector<std::unique_ptr<const DexFile>>* dex_files) { +static size_t OpenBootDexFiles(ArrayRef<const std::string> dex_filenames, + ArrayRef<const std::string> dex_locations, + std::vector<std::unique_ptr<const DexFile>>* dex_files) { DCHECK(dex_files != nullptr) << "OpenDexFiles: out-param is nullptr"; size_t failure_count = 0; const ArtDexFileLoader dex_file_loader; @@ -1021,9 +1028,15 @@ static size_t OpenDexFiles(ArrayRef<const std::string> dex_filenames, LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; continue; } + // In the case we're not using the default boot image, we don't have support yet + // on reading vdex files of boot classpath. So just assume all boot classpath + // dex files have been verified (this should always be the case as the default boot + // image has been generated at build time). + bool verify = Runtime::Current()->IsVerificationEnabled() && + (kIsDebugBuild || Runtime::Current()->IsUsingDefaultBootImageLocation()); if (!dex_file_loader.Open(dex_filename, dex_location, - Runtime::Current()->IsVerificationEnabled(), + verify, kVerifyChecksum, &error_msg, dex_files)) { @@ -1124,6 +1137,12 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { runtime_options.GetOrDefault(Opt::StackDumpLockProfThreshold)); image_location_ = runtime_options.GetOrDefault(Opt::Image); + { + std::string error_msg; + is_using_default_boot_image_location_ = + (image_location_.compare(GetDefaultBootImageLocation(&error_msg)) == 0); + } + SetInstructionSet(runtime_options.GetOrDefault(Opt::ImageInstructionSet)); boot_class_path_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath); boot_class_path_locations_ = runtime_options.ReleaseOrDefault(Opt::BootClassPathLocations); @@ -1480,9 +1499,9 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { if (runtime_options.Exists(Opt::BootClassPathDexList)) { extra_boot_class_path.swap(*runtime_options.GetOrDefault(Opt::BootClassPathDexList)); } else { - OpenDexFiles(ArrayRef<const std::string>(GetBootClassPath()).SubArray(start), - ArrayRef<const std::string>(GetBootClassPathLocations()).SubArray(start), - &extra_boot_class_path); + OpenBootDexFiles(ArrayRef<const std::string>(GetBootClassPath()).SubArray(start), + ArrayRef<const std::string>(GetBootClassPathLocations()).SubArray(start), + &extra_boot_class_path); } class_linker_->AddExtraBootDexFiles(self, std::move(extra_boot_class_path)); } @@ -1498,9 +1517,9 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { if (runtime_options.Exists(Opt::BootClassPathDexList)) { boot_class_path.swap(*runtime_options.GetOrDefault(Opt::BootClassPathDexList)); } else { - OpenDexFiles(ArrayRef<const std::string>(GetBootClassPath()), - ArrayRef<const std::string>(GetBootClassPathLocations()), - &boot_class_path); + OpenBootDexFiles(ArrayRef<const std::string>(GetBootClassPath()), + ArrayRef<const std::string>(GetBootClassPathLocations()), + &boot_class_path); } if (!class_linker_->InitWithoutImage(std::move(boot_class_path), &error_msg)) { LOG(ERROR) << "Could not initialize without image: " << error_msg; diff --git a/runtime/runtime.h b/runtime/runtime.h index f550b84583..ace0eea139 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -186,8 +186,7 @@ class Runtime { } bool IsUsingDefaultBootImageLocation() const { - std::string error_msg; - return GetImageLocation().compare(GetDefaultBootImageLocation(&error_msg)) == 0; + return is_using_default_boot_image_location_; } // Starts a runtime, which may cause threads to be started and code to run. @@ -915,6 +914,7 @@ class Runtime { std::vector<std::string> compiler_options_; std::vector<std::string> image_compiler_options_; std::string image_location_; + bool is_using_default_boot_image_location_; std::vector<std::string> boot_class_path_; std::vector<std::string> boot_class_path_locations_; diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 222c821df9..9b4aa0fc74 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -88,7 +88,6 @@ RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \ RUNTIME_OPTIONS_KEY (std::vector<std::string>, \ PropertiesList) // -D<whatever> -D<whatever> ... RUNTIME_OPTIONS_KEY (std::string, JniTrace) -RUNTIME_OPTIONS_KEY (std::string, PatchOat) RUNTIME_OPTIONS_KEY (bool, Relocate, kDefaultMustRelocate) RUNTIME_OPTIONS_KEY (bool, ImageDex2Oat, true) RUNTIME_OPTIONS_KEY (bool, Interpret, false) // -Xint diff --git a/runtime/thread.cc b/runtime/thread.cc index 4828aaef2c..8890a30c10 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -304,18 +304,27 @@ void Thread::Park(bool is_absolute, int64_t time) { if (old_state == kNoPermit) { // no permit was available. block thread until later. Runtime::Current()->GetRuntimeCallbacks()->ThreadParkStart(is_absolute, time); - int result = 0; bool timed_out = false; if (!is_absolute && time == 0) { // Thread.getState() is documented to return waiting for untimed parks. ScopedThreadSuspension sts(this, ThreadState::kWaiting); DCHECK_EQ(NumberOfHeldMutexes(), 0u); - result = futex(tls32_.park_state_.Address(), + int result = futex(tls32_.park_state_.Address(), FUTEX_WAIT_PRIVATE, /* sleep if val = */ kNoPermitWaiterWaiting, /* timeout */ nullptr, nullptr, 0); + // This errno check must happen before the scope is closed, to ensure that + // no destructors (such as ScopedThreadSuspension) overwrite errno. + if (result == -1) { + switch (errno) { + case EAGAIN: + FALLTHROUGH_INTENDED; + case EINTR: break; // park() is allowed to spuriously return + default: PLOG(FATAL) << "Failed to park"; + } + } } else if (time > 0) { // Only actually suspend and futex_wait if we're going to wait for some // positive amount of time - the kernel will reject negative times with @@ -325,6 +334,7 @@ void Thread::Park(bool is_absolute, int64_t time) { ScopedThreadSuspension sts(this, ThreadState::kTimedWaiting); DCHECK_EQ(NumberOfHeldMutexes(), 0u); timespec timespec; + int result = 0; if (is_absolute) { // Time is millis when scheduled for an absolute time timespec.tv_nsec = (time % 1000) * 1000000; @@ -350,15 +360,17 @@ void Thread::Park(bool is_absolute, int64_t time) { nullptr, 0); } - } - if (result == -1) { - switch (errno) { - case ETIMEDOUT: - timed_out = true; - FALLTHROUGH_INTENDED; - case EAGAIN: - case EINTR: break; // park() is allowed to spuriously return - default: PLOG(FATAL) << "Failed to park"; + // This errno check must happen before the scope is closed, to ensure that + // no destructors (such as ScopedThreadSuspension) overwrite errno. + if (result == -1) { + switch (errno) { + case ETIMEDOUT: + timed_out = true; + FALLTHROUGH_INTENDED; + case EAGAIN: + case EINTR: break; // park() is allowed to spuriously return + default: PLOG(FATAL) << "Failed to park"; + } } } // Mark as no longer waiting, and consume permit if there is one. @@ -4258,4 +4270,12 @@ int Thread::GetNativePriority() { return priority; } +bool Thread::IsSystemDaemon() const { + if (GetPeer() == nullptr) { + return false; + } + return jni::DecodeArtField( + WellKnownClasses::java_lang_Thread_systemDaemon)->GetBoolean(GetPeer()); +} + } // namespace art diff --git a/runtime/thread.h b/runtime/thread.h index 7a14fd7b48..ec276b59f5 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1230,6 +1230,8 @@ class Thread { return this == jit_sensitive_thread_; } + bool IsSystemDaemon() const REQUIRES_SHARED(Locks::mutator_lock_); + // Returns true if StrictMode events are traced for the current thread. static bool IsSensitiveThread() { if (is_sensitive_thread_hook_ != nullptr) { diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 467860011c..db90ae82cf 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -92,6 +92,7 @@ jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass; jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init; jmethodID WellKnownClasses::java_lang_Daemons_start; jmethodID WellKnownClasses::java_lang_Daemons_stop; +jmethodID WellKnownClasses::java_lang_Daemons_waitForDaemonStart; jmethodID WellKnownClasses::java_lang_Double_valueOf; jmethodID WellKnownClasses::java_lang_Float_valueOf; jmethodID WellKnownClasses::java_lang_Integer_valueOf; @@ -132,6 +133,7 @@ jfieldID WellKnownClasses::java_lang_Thread_lock; jfieldID WellKnownClasses::java_lang_Thread_name; jfieldID WellKnownClasses::java_lang_Thread_priority; jfieldID WellKnownClasses::java_lang_Thread_nativePeer; +jfieldID WellKnownClasses::java_lang_Thread_systemDaemon; jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart; jfieldID WellKnownClasses::java_lang_ThreadGroup_groups; jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups; @@ -351,6 +353,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V"); java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V"); + java_lang_Daemons_waitForDaemonStart = CacheMethod(env, java_lang_Daemons, true, "waitForDaemonStart", "()V"); java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;"); java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;"); @@ -385,6 +388,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;"); java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I"); java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J"); + java_lang_Thread_systemDaemon = CacheField(env, java_lang_Thread, false, "systemDaemon", "Z"); java_lang_Thread_unparkedBeforeStart = CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z"); java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;"); java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 872b562bd5..3c5144fbd5 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -101,6 +101,7 @@ struct WellKnownClasses { static jmethodID java_lang_ClassNotFoundException_init; static jmethodID java_lang_Daemons_start; static jmethodID java_lang_Daemons_stop; + static jmethodID java_lang_Daemons_waitForDaemonStart; static jmethodID java_lang_Double_valueOf; static jmethodID java_lang_Float_valueOf; static jmethodID java_lang_Integer_valueOf; @@ -141,6 +142,7 @@ struct WellKnownClasses { static jfieldID java_lang_Thread_name; static jfieldID java_lang_Thread_priority; static jfieldID java_lang_Thread_nativePeer; + static jfieldID java_lang_Thread_systemDaemon; static jfieldID java_lang_Thread_unparkedBeforeStart; static jfieldID java_lang_ThreadGroup_groups; static jfieldID java_lang_ThreadGroup_ngroups; diff --git a/test/1919-vminit-thread-start-timing/src/Main.java b/test/1919-vminit-thread-start-timing/src/Main.java index 65781b8484..41baf241d8 100644 --- a/test/1919-vminit-thread-start-timing/src/Main.java +++ b/test/1919-vminit-thread-start-timing/src/Main.java @@ -15,7 +15,7 @@ */ public class Main { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { art.Test1919.run(); } } diff --git a/test/1919-vminit-thread-start-timing/src/art/Test1919.java b/test/1919-vminit-thread-start-timing/src/art/Test1919.java index f6b770f7cf..26ff49fffc 100644 --- a/test/1919-vminit-thread-start-timing/src/art/Test1919.java +++ b/test/1919-vminit-thread-start-timing/src/art/Test1919.java @@ -19,12 +19,20 @@ package art; public class Test1919 { public static final boolean PRINT_ALL_THREADS = false; - public static void run() { + public static void run() throws Exception { + Thread testing_thread = getTestingThread(); + // TODO(b/124284724): For unknown reasons the testing thread will sometimes SEGV after the test + // has otherwise completed successfully. This has only been observed on the release version of + // art (libart.so) and I haven't had any luck reproing it. I assume it has something to do with + // racing between the DetachCurrentThread and shutdown but I'm not sure. Since the runtime + // normally never shuts down anyway for now I'll just ensure everything gets cleaned up early to + // prevent the problem from showing up. + testing_thread.join(); for (Event e : getEvents()) { if (e.thr != null) { if (PRINT_ALL_THREADS || e.thr.equals(Thread.currentThread()) || - e.thr.getName().equals("JVMTI_THREAD-Test1919")) { + e.thr.equals(testing_thread)) { System.out.println(e.name + ": " + e.thr.getName()); } } @@ -52,4 +60,6 @@ public class Test1919 { public static native String[] getEventNames(); public static native Thread[] getEventThreads(); + + public static native Thread getTestingThread(); } diff --git a/test/1919-vminit-thread-start-timing/vminit.cc b/test/1919-vminit-thread-start-timing/vminit.cc index 109c61f05c..ddf6649769 100644 --- a/test/1919-vminit-thread-start-timing/vminit.cc +++ b/test/1919-vminit-thread-start-timing/vminit.cc @@ -45,6 +45,8 @@ struct EventList { std::vector<EventData> events; }; +// The thread we started for testing. +static jthread the_thread; static void EnableEvent(jvmtiEnv* env, jvmtiEvent evt) { jvmtiError error = env->SetEventNotificationMode(JVMTI_ENABLE, evt, nullptr); @@ -93,6 +95,9 @@ static void CreateAgentThread(jvmtiEnv* jvmti, JNIEnv* env) { env->CallNonvirtualVoidMethod(thread.get(), thread_klass.get(), initID, thread_name.get()); CHECK(!env->ExceptionCheck()); + // Set the_thread. + the_thread = static_cast<jthread>(env->NewGlobalRef(thread.get())); + // Run agent thread. CheckJvmtiError(jvmti, jvmti->RunAgentThread(thread.get(), Test1919AgentThread, @@ -188,5 +193,9 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1919_getEventThreads(JNIE return ret; } +extern "C" JNIEXPORT jthread JNICALL Java_art_Test1919_getTestingThread(JNIEnv*, jclass) { + return the_thread; +} + } // namespace Test1919VMInitThreadStart } // namespace art diff --git a/test/ProfileTestMultiDex/Second.java b/test/ProfileTestMultiDex/Second.java index 4b3c7a479b..9f5dc66742 100644 --- a/test/ProfileTestMultiDex/Second.java +++ b/test/ProfileTestMultiDex/Second.java @@ -30,3 +30,11 @@ class SubC extends Super { int getValue() { return 24; } } +class TestIntrinsicOatdump { + Integer valueOf(int i) { + // ProfileTestMultiDex is used also for testing oatdump for apps. + // This is a regression test that oatdump can handle .data.bimg.rel.ro + // entries pointing to the middle of the "boot image live objects" array. + return Integer.valueOf(i); + } +} diff --git a/tools/luci/config/cr-buildbucket.cfg b/tools/luci/config/cr-buildbucket.cfg index 8df8433f75..5ace19bbf0 100644 --- a/tools/luci/config/cr-buildbucket.cfg +++ b/tools/luci/config/cr-buildbucket.cfg @@ -29,7 +29,7 @@ buckets { builder_defaults { dimensions: "pool:luci.art.ci" service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com" - execution_timeout_secs: 10800 # 3h + execution_timeout_secs: 108000 # 30h swarming_tags: "vpython:native-python-wrapper" build_numbers: YES # Some builders require specific hardware, so we make the assignment in bots.cfg |