summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CleanSpec.mk2
-rw-r--r--compiler/debug/dwarf/dwarf_test.cc12
-rw-r--r--libartbase/base/common_art_test.cc2
-rw-r--r--oatdump/oatdump.cc51
-rw-r--r--oatdump/oatdump_app_test.cc12
-rw-r--r--oatdump/oatdump_test.h16
-rw-r--r--openjdkjvmti/ti_thread.cc33
-rw-r--r--runtime/class_linker.cc14
-rw-r--r--runtime/parsed_options.cc4
-rw-r--r--runtime/runtime.cc47
-rw-r--r--runtime/runtime.h4
-rw-r--r--runtime/runtime_options.def1
-rw-r--r--runtime/thread.cc42
-rw-r--r--runtime/thread.h2
-rw-r--r--runtime/well_known_classes.cc4
-rw-r--r--runtime/well_known_classes.h2
-rw-r--r--test/1919-vminit-thread-start-timing/src/Main.java2
-rw-r--r--test/1919-vminit-thread-start-timing/src/art/Test1919.java14
-rw-r--r--test/1919-vminit-thread-start-timing/vminit.cc9
-rw-r--r--test/ProfileTestMultiDex/Second.java8
-rw-r--r--tools/luci/config/cr-buildbucket.cfg2
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