Snap for 9554547 from 0992003fecb94552d355b50040cb4b2936c3de3d to mainline-networking-release
Change-Id: Id7e7c5ad33ac30192625fce681eafcfd1b1c7d06
diff --git a/dex2oat/dex2oat_cts_test.cc b/dex2oat/dex2oat_cts_test.cc
index 41c7015..2541264 100644
--- a/dex2oat/dex2oat_cts_test.cc
+++ b/dex2oat/dex2oat_cts_test.cc
@@ -14,11 +14,54 @@
* limitations under the License.
*/
+#include "base/file_utils.h"
#include "dex2oat_environment_test.h"
namespace art {
-class Dex2oatCtsTest : public Dex2oatEnvironmentTest {};
+class Dex2oatCtsTest : public CommonArtTest, public Dex2oatScratchDirs {
+ public:
+ void SetUp() override {
+ CommonArtTest::SetUp();
+ Dex2oatScratchDirs::SetUp(android_data_);
+ }
+
+ void TearDown() override {
+ Dex2oatScratchDirs::TearDown();
+ CommonArtTest::TearDown();
+ }
+
+ protected:
+ // Stripped down counterpart to Dex2oatEnvironmentTest::Dex2Oat that only adds
+ // enough arguments for our purposes.
+ int Dex2Oat(const std::vector<std::string>& dex2oat_args,
+ std::string* output,
+ std::string* error_msg) {
+ // This command line should work regardless of bitness, ISA, etc.
+ std::vector<std::string> argv = {std::string(kAndroidArtApexDefaultPath) + "/bin/dex2oat"};
+ argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end());
+
+ // We must set --android-root.
+ const char* android_root = getenv("ANDROID_ROOT");
+ CHECK(android_root != nullptr);
+ argv.push_back("--android-root=" + std::string(android_root));
+
+ // We need dex2oat to actually log things.
+ auto post_fork_fn = []() { return setenv("ANDROID_LOG_TAGS", "*:d", 1) == 0; };
+ ForkAndExecResult res = ForkAndExec(argv, post_fork_fn, output);
+ if (res.stage != ForkAndExecResult::kFinished) {
+ *error_msg = strerror(errno);
+ ::testing::AssertionFailure() << "Failed to finish dex2oat invocation: " << *error_msg;
+ }
+
+ if (!res.StandardSuccess()) {
+ // We cannot use ASSERT_TRUE since the method returns an int and not void.
+ ::testing::AssertionFailure() << "dex2oat fork/exec failed: " << *error_msg;
+ }
+
+ return res.status_code;
+ }
+};
// Run dex2oat with --enable-palette-compilation-hooks to force calls to
// PaletteNotify{Start,End}Dex2oatCompilation.
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index 6124ed9..bd06043 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -139,6 +139,8 @@
static void TearDownAndroidDataDir(const std::string& android_data, bool fail_on_error);
+ static void ClearDirectory(const char* dirpath, bool recursive = true);
+
// Get the names of the libcore modules.
virtual std::vector<std::string> GetLibCoreModuleNames() const;
@@ -231,8 +233,6 @@
std::unique_ptr<const DexFile> LoadExpectSingleDexFile(const char* location);
- void ClearDirectory(const char* dirpath, bool recursive = true);
-
// Open a file (allows reading of framework jars).
std::vector<std::unique_ptr<const DexFile>> OpenDexFiles(const char* filename);
diff --git a/libartpalette/apex/palette_test.cc b/libartpalette/apex/palette_test.cc
index 853dc29..0bcc093 100644
--- a/libartpalette/apex/palette_test.cc
+++ b/libartpalette/apex/palette_test.cc
@@ -21,7 +21,6 @@
#include <sys/syscall.h>
#include <unistd.h>
-#include "common_runtime_test.h"
#include "gtest/gtest.h"
namespace {
@@ -70,14 +69,32 @@
#endif
}
-class PaletteClientJniTest : public art::CommonRuntimeTest {};
-
-TEST_F(PaletteClientJniTest, JniInvocation) {
+TEST_F(PaletteClientTest, JniInvocation) {
+#ifndef ART_TARGET_ANDROID
+ // On host we need to set up a boot classpath and pass it in here. Let's not
+ // bother since this test is only for native API coverage on target.
+ GTEST_SKIP() << "Will only spin up a VM on Android";
+#else
bool enabled;
EXPECT_EQ(PALETTE_STATUS_OK, PaletteShouldReportJniInvocations(&enabled));
- JNIEnv* env = art::Thread::Current()->GetJniEnv();
+ JavaVMInitArgs vm_args;
+ JavaVMOption options[] = {
+ {.optionString = "-verbose:jni", .extraInfo = nullptr},
+ };
+ vm_args.version = JNI_VERSION_1_6;
+ vm_args.nOptions = std::size(options);
+ vm_args.options = options;
+ vm_args.ignoreUnrecognized = JNI_TRUE;
+
+ JavaVM* jvm = nullptr;
+ JNIEnv* env = nullptr;
+ EXPECT_EQ(JNI_OK, JNI_CreateJavaVM(&jvm, &env, &vm_args));
ASSERT_NE(nullptr, env);
+
PaletteNotifyBeginJniInvocation(env);
PaletteNotifyEndJniInvocation(env);
+
+ EXPECT_EQ(JNI_OK, jvm->DestroyJavaVM());
+#endif
}
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 964b7f3..e867c4f 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -41,21 +41,17 @@
static constexpr bool kDebugArgs = false;
-// Test class that provides some helpers to set a test up for compilation using dex2oat.
-class Dex2oatEnvironmentTest : public CommonRuntimeTest {
+class Dex2oatScratchDirs {
public:
- void SetUp() override {
- CommonRuntimeTest::SetUp();
- const ArtDexFileLoader dex_file_loader;
-
+ void SetUp(const std::string& android_data) {
// Create a scratch directory to work from.
// Get the realpath of the android data. The oat dir should always point to real location
// when generating oat files in dalvik-cache. This avoids complicating the unit tests
// when matching the expected paths.
- UniqueCPtr<const char[]> android_data_real(realpath(android_data_.c_str(), nullptr));
+ UniqueCPtr<const char[]> android_data_real(realpath(android_data.c_str(), nullptr));
ASSERT_TRUE(android_data_real != nullptr)
- << "Could not get the realpath of the android data" << android_data_ << strerror(errno);
+ << "Could not get the realpath of the android data" << android_data << strerror(errno);
scratch_dir_.assign(android_data_real.get());
scratch_dir_ += "/Dex2oatEnvironmentTest";
@@ -67,6 +63,41 @@
odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA));
ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
+ }
+
+ void TearDown() {
+ CommonArtTest::ClearDirectory(odex_dir_.c_str());
+ ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
+
+ CommonArtTest::ClearDirectory(odex_oat_dir_.c_str());
+ ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
+
+ CommonArtTest::ClearDirectory(scratch_dir_.c_str());
+ ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
+ }
+
+ // Scratch directory, for dex and odex files (oat files will go in the
+ // dalvik cache).
+ const std::string& GetScratchDir() const { return scratch_dir_; }
+
+ // Odex directory is the subdirectory in the scratch directory where odex
+ // files should be located.
+ const std::string& GetOdexDir() const { return odex_dir_; }
+
+ private:
+ std::string scratch_dir_;
+ std::string odex_oat_dir_;
+ std::string odex_dir_;
+};
+
+// Test class that provides some helpers to set a test up for compilation using dex2oat.
+class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTest {
+ public:
+ void SetUp() override {
+ CommonRuntimeTest::SetUp();
+ Dex2oatScratchDirs::SetUp(android_data_);
+
+ const ArtDexFileLoader dex_file_loader;
// Verify the environment is as we expect
std::vector<uint32_t> checksums;
@@ -122,15 +153,7 @@
}
void TearDown() override {
- ClearDirectory(odex_dir_.c_str());
- ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
-
- ClearDirectory(odex_oat_dir_.c_str());
- ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
-
- ClearDirectory(scratch_dir_.c_str());
- ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
-
+ Dex2oatScratchDirs::TearDown();
CommonRuntimeTest::TearDown();
}
@@ -165,18 +188,6 @@
return GetTestDexFileName("Nested");
}
- // Scratch directory, for dex and odex files (oat files will go in the
- // dalvik cache).
- const std::string& GetScratchDir() const {
- return scratch_dir_;
- }
-
- // Odex directory is the subdirectory in the scratch directory where odex
- // files should be located.
- const std::string& GetOdexDir() const {
- return odex_dir_;
- }
-
int Dex2Oat(const std::vector<std::string>& dex2oat_args,
std::string* output,
std::string* error_msg) {
@@ -232,11 +243,6 @@
return res.status_code;
}
-
- private:
- std::string scratch_dir_;
- std::string odex_oat_dir_;
- std::string odex_dir_;
};
} // namespace art
diff --git a/runtime/gc/collector/mark_compact-inl.h b/runtime/gc/collector/mark_compact-inl.h
index d6919aa..57517d5 100644
--- a/runtime/gc/collector/mark_compact-inl.h
+++ b/runtime/gc/collector/mark_compact-inl.h
@@ -276,7 +276,7 @@
stack_high_addr = reinterpret_cast<char*>(stack_low_addr) + self->GetStackSize();
}
if (root < stack_low_addr || root > stack_high_addr) {
- auto ret = updated_roots_.insert(root);
+ auto ret = updated_roots_->insert(root);
DCHECK(ret.second) << "root=" << root << " old_ref=" << old_ref
<< " stack_low_addr=" << stack_low_addr
<< " stack_high_addr=" << stack_high_addr;
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 38cbcb7..f262b66 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -243,6 +243,7 @@
gc_barrier_(0),
mark_stack_lock_("mark compact mark stack lock", kMarkSweepMarkStackLock),
bump_pointer_space_(heap->GetBumpPointerSpace()),
+ moving_space_bitmap_(bump_pointer_space_->GetMarkBitmap()),
moving_to_space_fd_(kFdUnused),
moving_from_space_fd_(kFdUnused),
uffd_(kFdUnused),
@@ -250,9 +251,12 @@
compaction_in_progress_count_(0),
compacting_(false),
uffd_initialized_(false),
- uffd_minor_fault_supported_(GetUffdAndMinorFault().second),
+ uffd_minor_fault_supported_(false),
minor_fault_initialized_(false),
map_linear_alloc_shared_(false) {
+ if (kIsDebugBuild) {
+ updated_roots_.reset(new std::unordered_set<void*>());
+ }
// TODO: Depending on how the bump-pointer space move is implemented. If we
// switch between two virtual memories each time, then we will have to
// initialize live_words_bitmap_ accordingly.
@@ -435,13 +439,8 @@
// in these spaces. This card-table will eventually be used to track
// mutations while concurrent marking is going on.
card_table->ClearCardRange(space->Begin(), space->Limit());
- if (space == bump_pointer_space_) {
- // It is OK to clear the bitmap with mutators running since the only
- // place it is read is VisitObjects which has exclusion with this GC.
- moving_space_bitmap_ = bump_pointer_space_->GetMarkBitmap();
- moving_space_bitmap_->Clear();
- } else {
- CHECK(space == heap_->GetNonMovingSpace());
+ if (space != bump_pointer_space_) {
+ CHECK_EQ(space, heap_->GetNonMovingSpace());
non_moving_space_ = space;
non_moving_space_bitmap_ = space->GetMarkBitmap();
}
@@ -449,6 +448,28 @@
}
}
+void MarkCompact::MarkZygoteLargeObjects() {
+ Thread* self = thread_running_gc_;
+ DCHECK_EQ(self, Thread::Current());
+ space::LargeObjectSpace* const los = heap_->GetLargeObjectsSpace();
+ if (los != nullptr) {
+ // Pick the current live bitmap (mark bitmap if swapped).
+ accounting::LargeObjectBitmap* const live_bitmap = los->GetLiveBitmap();
+ accounting::LargeObjectBitmap* const mark_bitmap = los->GetMarkBitmap();
+ // Walk through all of the objects and explicitly mark the zygote ones so they don't get swept.
+ std::pair<uint8_t*, uint8_t*> range = los->GetBeginEndAtomic();
+ live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(range.first),
+ reinterpret_cast<uintptr_t>(range.second),
+ [mark_bitmap, los, self](mirror::Object* obj)
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (los->IsZygoteLargeObject(self, obj)) {
+ mark_bitmap->Set(obj);
+ }
+ });
+ }
+}
+
void MarkCompact::InitializePhase() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
mark_stack_ = heap_->GetMarkStack();
@@ -3456,6 +3477,7 @@
DCHECK_EQ(thread_running_gc_, Thread::Current());
WriterMutexLock mu(thread_running_gc_, *Locks::heap_bitmap_lock_);
BindAndResetBitmaps();
+ MarkZygoteLargeObjects();
MarkRoots(
static_cast<VisitRootFlags>(kVisitRootFlagAllRoots | kVisitRootFlagStartLoggingNewRoots));
MarkReachableObjects();
@@ -3626,8 +3648,15 @@
<< " doesn't belong to any of the spaces and large object space doesn't exist";
accounting::LargeObjectBitmap* los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
DCHECK(los_bitmap->HasAddress(obj));
- return kParallel ? !los_bitmap->AtomicTestAndSet(obj)
- : !los_bitmap->Set(obj);
+ if (kParallel) {
+ los_bitmap->AtomicTestAndSet(obj);
+ } else {
+ los_bitmap->Set(obj);
+ }
+ // We only have primitive arrays in large object space. So there is no
+ // reason to push into mark-stack.
+ DCHECK(obj->IsString() || (obj->IsArrayInstance() && !obj->IsObjectArray()));
+ return false;
}
}
@@ -3752,6 +3781,11 @@
info_map_.MadviseDontNeedAndZero();
live_words_bitmap_->ClearBitmap();
+ // TODO: We can clear this bitmap right before compaction pause. But in that
+ // case we need to ensure that we don't assert on this bitmap afterwards.
+ // Also, we would still need to clear it here again as we may have to use the
+ // bitmap for black-allocations (see UpdateMovingSpaceBlackAllocations()).
+ moving_space_bitmap_->Clear();
if (UNLIKELY(is_zygote && IsValidFd(uffd_))) {
heap_->DeleteThreadPool();
@@ -3762,7 +3796,9 @@
}
CHECK(mark_stack_->IsEmpty()); // Ensure that the mark stack is empty.
mark_stack_->Reset();
- updated_roots_.clear();
+ if (kIsDebugBuild && updated_roots_.get() != nullptr) {
+ updated_roots_->clear();
+ }
class_after_obj_ordered_map_.clear();
delete[] moving_pages_status_;
linear_alloc_arenas_.clear();
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 498a56d..78ee5c5 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -510,6 +510,9 @@
// the lowest-address obj is compacted.
void UpdateClassAfterObjMap();
+ void MarkZygoteLargeObjects() REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
// Buffers, one per worker thread + gc-thread, to be used when
// kObjPtrPoisoning == true as in that case we can't have the buffer on the
// stack. The first page of the buffer is assigned to
@@ -536,7 +539,7 @@
// GC-root is updated twice.
// TODO: Must be replaced with an efficient mechanism eventually. Or ensure
// that double updation doesn't happen in the first place.
- std::unordered_set<void*> updated_roots_;
+ std::unique_ptr<std::unordered_set<void*>> updated_roots_;
MemMap from_space_map_;
MemMap shadow_to_space_map_;
// Any array of live-bytes in logical chunks of kOffsetChunkSize size
@@ -628,11 +631,11 @@
size_t last_checked_reclaim_page_idx_;
uint8_t* last_reclaimed_page_;
- // The main space bitmap
- accounting::ContinuousSpaceBitmap* moving_space_bitmap_;
- accounting::ContinuousSpaceBitmap* non_moving_space_bitmap_;
space::ContinuousSpace* non_moving_space_;
space::BumpPointerSpace* const bump_pointer_space_;
+ // The main space bitmap
+ accounting::ContinuousSpaceBitmap* const moving_space_bitmap_;
+ accounting::ContinuousSpaceBitmap* non_moving_space_bitmap_;
Thread* thread_running_gc_;
// Array of pages' compaction status.
Atomic<PageState>* moving_pages_status_;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 2cb1bf1..c450676 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -417,7 +417,18 @@
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
}
+
LOG(INFO) << "Using " << foreground_collector_type_ << " GC.";
+ if (!gUseUserfaultfd) {
+ // This ensures that userfaultfd syscall is done before any seccomp filter is installed.
+ // TODO(b/266731037): Remove this when we no longer need to collect metric on userfaultfd
+ // support.
+ auto [uffd_supported, minor_fault_supported] = collector::MarkCompact::GetUffdAndMinorFault();
+ // The check is just to ensure that compiler doesn't eliminate the function call above.
+ // Userfaultfd support is certain to be there if its minor-fault feature is supported.
+ CHECK_IMPLIES(minor_fault_supported, uffd_supported);
+ }
+
if (gUseReadBarrier) {
CHECK_EQ(foreground_collector_type_, kCollectorTypeCC);
CHECK_EQ(background_collector_type_, kCollectorTypeCCBackground);
@@ -2521,7 +2532,7 @@
new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space_);
CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table";
- if (collector_type_ != kCollectorTypeCC) {
+ if (collector_type_ != kCollectorTypeCC && collector_type_ != kCollectorTypeCMC) {
// Set all the cards in the mod-union table since we don't know which objects contain references
// to large objects.
mod_union_table->SetCards();
@@ -2533,10 +2544,10 @@
mod_union_table->ProcessCards();
mod_union_table->ClearTable();
- // For CC we never collect zygote large objects. This means we do not need to set the cards for
- // the zygote mod-union table and we can also clear all of the existing image mod-union tables.
- // The existing mod-union tables are only for image spaces and may only reference zygote and
- // image objects.
+ // For CC and CMC we never collect zygote large objects. This means we do not need to set the
+ // cards for the zygote mod-union table and we can also clear all of the existing image
+ // mod-union tables. The existing mod-union tables are only for image spaces and may only
+ // reference zygote and image objects.
for (auto& pair : mod_union_tables_) {
CHECK(pair.first->IsImageSpace());
CHECK(!pair.first->AsImageSpace()->GetImageHeader().IsAppImage());
diff --git a/test/924-threads/src/art/Test924.java b/test/924-threads/src/art/Test924.java
index e97c9c6..3555720 100644
--- a/test/924-threads/src/art/Test924.java
+++ b/test/924-threads/src/art/Test924.java
@@ -337,6 +337,19 @@
}
}
+ private static List<String> filterForThread(Object[] thread_messages, String thread_name) {
+ List<String> messageListForThread = new ArrayList<String>();
+
+ for (int i = 0; i < thread_messages.length; i++) {
+ String message = (String)thread_messages[i];
+ if (message.startsWith("Thread(" + thread_name + ")")) {
+ messageListForThread.add(message);
+ }
+ }
+
+ return messageListForThread;
+ }
+
private static void doTestEvents() throws Exception {
enableThreadEvents(true);
@@ -354,21 +367,24 @@
}
}
};
- Thread t = new Thread(r, "EventTestThread");
+ String thread_name = "EventTestThread";
+ Thread t = new Thread(r, thread_name);
System.out.println("Constructed thread");
Thread.yield();
Thread.sleep(100);
- System.out.println(Arrays.toString(getThreadEventMessages()));
+
+ // Check that there are no events related to EventTestThread that we just created.
+ System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
t.start();
cdl1.await();
- System.out.println(Arrays.toString(getThreadEventMessages()));
+ System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
cdl2.countDown();
t.join();
- System.out.println(Arrays.toString(getThreadEventMessages()));
+ System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
System.out.println("Thread joined");