Add -XX:MadviseRandomAccess option

If enabled, ART will advise random access to the kernel for files
that are thought to have such access patterns.

Bug: 67772594
Test: verify that -XX:MadviseRandomAccess:true is passed to runtime init

(cherry picked from commit 087f2046dfdf41646c740a05004b4d40cbd99b11)

Change-Id: I76a5f62846d563a4f2cf25e47dbd320464aee8c1
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index b3c3aa0..781ddd7 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -77,9 +77,6 @@
 // For debugging, Open will print DlOpen error message if set to true.
 static constexpr bool kPrintDlOpenErrorMessage = false;
 
-// If true, we advise the kernel about dex file mem map accesses.
-static constexpr bool kMadviseDexFileAccesses = true;
-
 // Note for OatFileBase and descendents:
 //
 // These are used in OatFile::Open to try all our loaders.
@@ -1651,20 +1648,19 @@
 
 // Madvise the dex file based on the state we are moving to.
 void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) {
-  const bool low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode();
+  Runtime* const runtime = Runtime::Current();
+  const bool low_ram = runtime->GetHeap()->IsLowMemoryMode();
   // TODO: Also do madvise hints for non low ram devices.
-  if (!kMadviseDexFileAccesses || !low_ram) {
+  if (!low_ram) {
     return;
   }
-  if (state == MadviseState::kMadviseStateAtLoad) {
-    if (low_ram) {
-      // Default every dex file to MADV_RANDOM when its loaded by default for low ram devices.
-      // Other devices have enough page cache to get performance benefits from loading more pages
-      // into the page cache.
-      MadviseLargestPageAlignedRegion(dex_file.Begin(),
-                                      dex_file.Begin() + dex_file.Size(),
-                                      MADV_RANDOM);
-    }
+  if (state == MadviseState::kMadviseStateAtLoad && runtime->MAdviseRandomAccess()) {
+    // Default every dex file to MADV_RANDOM when its loaded by default for low ram devices.
+    // Other devices have enough page cache to get performance benefits from loading more pages
+    // into the page cache.
+    MadviseLargestPageAlignedRegion(dex_file.Begin(),
+                                    dex_file.Begin() + dex_file.Size(),
+                                    MADV_RANDOM);
   }
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
   if (oat_dex_file != nullptr) {
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 9888186..9841a95 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -159,6 +159,10 @@
           .WithType<bool>()
           .WithValueMap({{"false", false}, {"true", true}})
           .IntoKey(M::DumpNativeStackOnSigQuit)
+      .Define("-XX:MadviseRandomAccess:_")
+          .WithType<bool>()
+          .WithValueMap({{"false", false}, {"true", true}})
+          .IntoKey(M::MadviseRandomAccess)
       .Define("-Xusejit:_")
           .WithType<bool>()
           .WithValueMap({{"false", false}, {"true", true}})
@@ -717,6 +721,7 @@
   UsageMessage(stream, "  -XX:LargeObjectSpace={disabled,map,freelist}\n");
   UsageMessage(stream, "  -XX:LargeObjectThreshold=N\n");
   UsageMessage(stream, "  -XX:DumpNativeStackOnSigQuit=booleanvalue\n");
+  UsageMessage(stream, "  -XX:MadviseRandomAccess:booleanvalue\n");
   UsageMessage(stream, "  -XX:SlowDebug={false,true}\n");
   UsageMessage(stream, "  -Xmethod-trace\n");
   UsageMessage(stream, "  -Xmethod-trace-file:filename");
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a4ed21e..8eb4a07 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1143,6 +1143,7 @@
   zygote_max_failed_boots_ = runtime_options.GetOrDefault(Opt::ZygoteMaxFailedBoots);
   experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
   is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
+  madvise_random_access_ = runtime_options.GetOrDefault(Opt::MadviseRandomAccess);
 
   plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins);
   agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 399e1c1..9f79a01 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -682,6 +682,12 @@
     return result;
   }
 
+  // Whether or not we use MADV_RANDOM on files that are thought to have random access patterns.
+  // This is beneficial for low RAM devices since it reduces page cache thrashing.
+  bool MAdviseRandomAccess() const {
+    return madvise_random_access_;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -916,6 +922,10 @@
   // Whether or not we are on a low RAM device.
   bool is_low_memory_mode_;
 
+  // Whether or not we use MADV_RANDOM on files that are thought to have random access patterns.
+  // This is beneficial for low RAM devices since it reduces page cache thrashing.
+  bool madvise_random_access_;
+
   // Whether the application should run in safe mode, that is, interpreter only.
   bool safe_mode_;
 
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index cafae22..2e03562 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -70,6 +70,7 @@
 RUNTIME_OPTIONS_KEY (bool,                EnableHSpaceCompactForOOM,      true)
 RUNTIME_OPTIONS_KEY (bool,                UseJitCompilation,              false)
 RUNTIME_OPTIONS_KEY (bool,                DumpNativeStackOnSigQuit,       true)
+RUNTIME_OPTIONS_KEY (bool,                MadviseRandomAccess,            false)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITCompileThreshold)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITWarmupThreshold)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITOsrThreshold)