Use cmdline gc-type and phenotype to decide which GC to run

Bug: 242553398
Bug: 160737021
Test: manual with android builds and module installations
Change-Id: Icfc25058e669406a0c3eb178376db8041e11c27b
diff --git a/build/art.go b/build/art.go
index 970af8a..cc53719 100644
--- a/build/art.go
+++ b/build/art.go
@@ -70,10 +70,11 @@
 		if !ctx.Config().IsEnvFalse("ART_USE_GENERATIONAL_CC") {
 			cflags = append(cflags, "-DART_USE_GENERATIONAL_CC=1")
 		}
-		// For now force CC as we don't want to make userfaultfd GC the default.
-		// Eventually, make it such that we force CC only if ART_USE_READ_BARRIER
-		// was set to true explicitly during build time.
-		cflags = append(cflags, "-DART_FORCE_USE_READ_BARRIER=1")
+		// Force CC only if ART_USE_READ_BARRIER was set to true explicitly during
+		// build time.
+		if ctx.Config().IsEnvTrue("ART_USE_READ_BARRIER") {
+			cflags = append(cflags, "-DART_FORCE_USE_READ_BARRIER=1")
+		}
 		tlab = true
 	} else if gcType == "CMC" {
 		tlab = true
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index d73418b..71e5a13 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -16,10 +16,13 @@
 
 #include "mark_compact-inl.h"
 
+#include "android-base/file.h"
+#include "android-base/properties.h"
 #include "base/quasi_atomic.h"
 #include "base/systrace.h"
 #include "base/utils.h"
 #include "gc/accounting/mod_union_table-inl.h"
+#include "gc/collector_type.h"
 #include "gc/reference_processor.h"
 #include "gc/space/bump_pointer_space.h"
 #include "gc/task_processor.h"
@@ -36,6 +39,7 @@
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
+
 #include <fstream>
 #include <numeric>
 
@@ -58,6 +62,12 @@
 #endif  // __NR_userfaultfd
 #endif  // __BIONIC__
 
+namespace {
+
+using ::android::base::GetBoolProperty;
+
+}
+
 namespace art {
 
 // We require MREMAP_DONTUNMAP functionality of the mremap syscall, which was
@@ -82,11 +92,28 @@
 // userfaultfd is enabled.
 static const bool gKernelHasFaultRetry = kIsTargetAndroid || IsKernelVersionAtLeast(5, 7);
 
-#ifndef ART_FORCE_USE_READ_BARRIER
-static bool ShouldUseUserfaultfd() {
-#if !defined(__linux__)
-  return false;
-#endif
+// The other cases are defined as constexpr in runtime/read_barrier_config.h
+#if !defined(ART_FORCE_USE_READ_BARRIER) && defined(ART_USE_READ_BARRIER)
+// Returns collector type asked to be used on the cmdline.
+static gc::CollectorType FetchCmdlineGcType() {
+  std::string argv;
+  gc::CollectorType gc_type = gc::CollectorType::kCollectorTypeNone;
+  if (android::base::ReadFileToString("/proc/self/cmdline", &argv)) {
+    if (argv.find("-Xgc:CMC") != std::string::npos) {
+      gc_type = gc::CollectorType::kCollectorTypeCMC;
+    } else if (argv.find("-Xgc:CC") != std::string::npos) {
+      gc_type = gc::CollectorType::kCollectorTypeCC;
+    }
+  }
+  return gc_type;
+}
+
+static bool SysPropSaysUffdGc() {
+  return GetBoolProperty("persist.device_config.runtime_native_boot.enable_uffd_gc",
+                         GetBoolProperty("ro.dalvik.vm.enable_uffd_gc", false));
+}
+
+static bool KernelSupportsUffd() {
   int fd = syscall(__NR_userfaultfd, O_CLOEXEC | UFFD_USER_MODE_ONLY);
   // On non-android devices we may not have the kernel patches that restrict
   // userfaultfd to user mode. But that is not a security concern as we are
@@ -101,15 +128,26 @@
     return false;
   }
 }
-#endif
 
-// The other cases are defined as a constexpr in runtime/read_barrier_config.h
-#ifndef ART_FORCE_USE_READ_BARRIER
-const bool gUseReadBarrier = (kUseBakerReadBarrier || kUseTableLookupReadBarrier)
-                             && !ShouldUseUserfaultfd();
-#ifdef ART_DEFAULT_GC_TYPE_IS_CMC
-const bool gUseUserfaultfd = !gUseReadBarrier;
+static bool ShouldUseUserfaultfd() {
+  static_assert(kUseBakerReadBarrier || kUseTableLookupReadBarrier);
+#ifdef __linux__
+  // Use CMC/CC if that is being explicitly asked for on cmdline. Otherwise,
+  // always use CC on host. On target, use CMC only if system properties says so
+  // and the kernel supports it.
+  gc::CollectorType gc_type = FetchCmdlineGcType();
+  return gc_type == gc::CollectorType::kCollectorTypeCMC ||
+         (gc_type == gc::CollectorType::kCollectorTypeNone &&
+          kIsTargetAndroid &&
+          SysPropSaysUffdGc() &&
+          KernelSupportsUffd());
+#else
+  return false;
 #endif
+}
+
+const bool gUseUserfaultfd = ShouldUseUserfaultfd();
+const bool gUseReadBarrier = !gUseUserfaultfd;
 #endif
 
 namespace gc {
diff --git a/runtime/read_barrier_config.h b/runtime/read_barrier_config.h
index 53274ea..876e3d7 100644
--- a/runtime/read_barrier_config.h
+++ b/runtime/read_barrier_config.h
@@ -71,16 +71,24 @@
 static constexpr bool kUseTableLookupReadBarrier = false;
 #endif
 
+// Only if read-barrier isn't forced (see build/art.go) but is selected, that we need
+// to see if we support userfaultfd GC. All the other cases can be constexpr here.
 #ifdef ART_FORCE_USE_READ_BARRIER
 constexpr bool gUseReadBarrier = kUseBakerReadBarrier || kUseTableLookupReadBarrier;
 constexpr bool gUseUserfaultfd = !gUseReadBarrier;
+static_assert(!gUseUserfaultfd);
 #else
-extern const bool gUseReadBarrier;
+#ifndef ART_USE_READ_BARRIER
+constexpr bool gUseReadBarrier = false;
 #ifdef ART_DEFAULT_GC_TYPE_IS_CMC
-extern const bool gUseUserfaultfd;
+constexpr bool gUseUserfaultfd = true;
 #else
 constexpr bool gUseUserfaultfd = false;
 #endif
+#else
+extern const bool gUseReadBarrier;
+extern const bool gUseUserfaultfd;
+#endif
 #endif
 
 // Disabled for performance reasons.