Add madvising of .odex, .vdex and .art files
1. When mmaping {.art, .odex, .vdex} files madvise them to MADV_WILLNEED
2. Add system properties to limit the madvise size/range
Test: presubmit
Bug: 178853586
Change-Id: I14afc7cc038ebbf6bba5a393ef222050284dd86d
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 838960c..7191525 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -111,6 +111,9 @@
},
{
"name": "art-run-test-047-returns[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-048-reflect-v8[com.google.android.art.apex]"
}
],
"presubmit": [
@@ -1261,6 +1264,9 @@
"name": "art-run-test-818-clinit-nterp"
},
{
+ "name": "art-run-test-821-madvise-willneed"
+ },
+ {
"name": "art-run-test-963-default-range-smali"
}
]
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 1dee6a6..1627d78 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -964,6 +964,18 @@
return MemMap::Invalid();
}
+ Runtime* runtime = Runtime::Current();
+ // The runtime might not be available at this point if we're running
+ // dex2oat or oatdump.
+ if (runtime != nullptr) {
+ size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeArt();
+ Runtime::MadviseFileForRange(madvise_size_limit,
+ temp_map.Size(),
+ temp_map.Begin(),
+ temp_map.End(),
+ image_filename);
+ }
+
if (is_compressed) {
memcpy(map.Begin(), &image_header, sizeof(ImageHeader));
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 887c992..e33053f 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1693,6 +1693,17 @@
reservation,
error_msg);
if (with_dlopen != nullptr) {
+ Runtime* runtime = Runtime::Current();
+ // The runtime might not be available at this point if we're running
+ // dex2oat or oatdump.
+ if (runtime != nullptr) {
+ size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex();
+ Runtime::MadviseFileForRange(madvise_size_limit,
+ with_dlopen->Size(),
+ with_dlopen->Begin(),
+ with_dlopen->End(),
+ oat_location);
+ }
return with_dlopen;
}
if (kPrintDlOpenErrorMessage) {
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 2a5bc9f..1cb8e9f 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -233,6 +233,15 @@
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(M::MadviseRandomAccess)
+ .Define("-XMadviseWillNeedVdexFileSize:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::MadviseWillNeedVdexFileSize)
+ .Define("-XMadviseWillNeedOdexFileSize:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::MadviseWillNeedOdexFileSize)
+ .Define("-XMadviseWillNeedArtFileSize:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::MadviseWillNeedArtFileSize)
.Define("-Xusejit:_")
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index fea03c8..8877c54 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -284,6 +284,9 @@
experimental_flags_(ExperimentalFlags::kNone),
oat_file_manager_(nullptr),
is_low_memory_mode_(false),
+ madvise_willneed_vdex_filesize_(0),
+ madvise_willneed_odex_filesize_(0),
+ madvise_willneed_art_filesize_(0),
safe_mode_(false),
hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
core_platform_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
@@ -1387,6 +1390,9 @@
experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
madvise_random_access_ = runtime_options.GetOrDefault(Opt::MadviseRandomAccess);
+ madvise_willneed_vdex_filesize_ = runtime_options.GetOrDefault(Opt::MadviseWillNeedVdexFileSize);
+ madvise_willneed_odex_filesize_ = runtime_options.GetOrDefault(Opt::MadviseWillNeedOdexFileSize);
+ madvise_willneed_art_filesize_ = runtime_options.GetOrDefault(Opt::MadviseWillNeedArtFileSize);
jni_ids_indirection_ = runtime_options.GetOrDefault(Opt::OpaqueJniIds);
automatically_set_jni_ids_indirection_ =
@@ -3128,4 +3134,50 @@
}
}
+void Runtime::MadviseFileForRange(size_t madvise_size_limit_bytes,
+ size_t map_size_bytes,
+ const uint8_t* map_begin,
+ const uint8_t* map_end,
+ const std::string& file_name) {
+ // Ideal blockTransferSize for madvising files (128KiB)
+ static constexpr size_t kIdealIoTransferSizeBytes = 128*1024;
+
+ size_t target_size_bytes = std::min<size_t>(map_size_bytes, madvise_size_limit_bytes);
+
+ if (target_size_bytes > 0) {
+ ScopedTrace madvising_trace("madvising "
+ + file_name
+ + " size="
+ + std::to_string(target_size_bytes));
+
+ // Based on requested size (target_size_bytes)
+ const uint8_t* target_pos = map_begin + target_size_bytes;
+
+ // Clamp endOfFile if its past map_end
+ if (target_pos < map_end) {
+ target_pos = map_end;
+ }
+
+ // Madvise the whole file up to target_pos in chunks of
+ // kIdealIoTransferSizeBytes (to MADV_WILLNEED)
+ // Note:
+ // madvise(MADV_WILLNEED) will prefetch max(fd readahead size, optimal
+ // block size for device) per call, hence the need for chunks. (128KB is a
+ // good default.)
+ for (const uint8_t* madvise_start = map_begin;
+ madvise_start < target_pos;
+ madvise_start += kIdealIoTransferSizeBytes) {
+ void* madvise_addr = const_cast<void*>(reinterpret_cast<const void*>(madvise_start));
+ size_t madvise_length = std::min(kIdealIoTransferSizeBytes,
+ static_cast<size_t>(target_pos - madvise_start));
+ int status = madvise(madvise_addr, madvise_length, MADV_WILLNEED);
+ // In case of error we stop madvising rest of the file
+ if (status < 0) {
+ LOG(ERROR) << "Failed to madvise file:" << file_name << " for size:" << map_size_bytes;
+ break;
+ }
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c9142f6..43a8454 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -885,6 +885,18 @@
return madvise_random_access_;
}
+ size_t GetMadviseWillNeedSizeVdex() const {
+ return madvise_willneed_vdex_filesize_;
+ }
+
+ size_t GetMadviseWillNeedSizeOdex() const {
+ return madvise_willneed_odex_filesize_;
+ }
+
+ size_t GetMadviseWillNeedSizeArt() const {
+ return madvise_willneed_art_filesize_;
+ }
+
const std::string& GetJdwpOptions() {
return jdwp_options_;
}
@@ -969,6 +981,12 @@
void RequestMetricsReport(bool synchronous = true);
+ static void MadviseFileForRange(size_t madvise_size_limit_bytes,
+ size_t map_size_bytes,
+ const uint8_t* map_begin,
+ const uint8_t* map_end,
+ const std::string& file_name);
+
private:
static void InitPlatformSignalHandlers();
@@ -1232,6 +1250,18 @@
// This is beneficial for low RAM devices since it reduces page cache thrashing.
bool madvise_random_access_;
+ // Limiting size (in bytes) for applying MADV_WILLNEED on vdex files
+ // A 0 for this will turn off madvising to MADV_WILLNEED
+ size_t madvise_willneed_vdex_filesize_;
+
+ // Limiting size (in bytes) for applying MADV_WILLNEED on odex files
+ // A 0 for this will turn off madvising to MADV_WILLNEED
+ size_t madvise_willneed_odex_filesize_;
+
+ // Limiting size (in bytes) for applying MADV_WILLNEED on art files
+ // A 0 for this will turn off madvising to MADV_WILLNEED
+ size_t madvise_willneed_art_filesize_;
+
// 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 b58d924..ea6b6e1 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -79,6 +79,9 @@
RUNTIME_OPTIONS_KEY (bool, UseProfiledJitCompilation, false)
RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true)
RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false)
+RUNTIME_OPTIONS_KEY (unsigned int, MadviseWillNeedVdexFileSize, 0)
+RUNTIME_OPTIONS_KEY (unsigned int, MadviseWillNeedOdexFileSize, 0)
+RUNTIME_OPTIONS_KEY (unsigned int, MadviseWillNeedArtFileSize, 0)
RUNTIME_OPTIONS_KEY (JniIdType, OpaqueJniIds, JniIdType::kDefault) // -Xopaque-jni-ids:{true, false, swapable}
RUNTIME_OPTIONS_KEY (bool, AutoPromoteOpaqueJniIds, true) // testing use only. -Xauto-promote-opaque-jni-ids:{true, false}
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold)
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 70fde27..1f2d4e5 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -198,8 +198,20 @@
if (!writable) {
vdex->AllowWriting(false);
+ Runtime* runtime = Runtime::Current();
+ // The runtime might not be available at this point if we're running
+ // dex2oat or oatdump.
+ if (runtime != nullptr) {
+ size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeVdex();
+ Runtime::MadviseFileForRange(madvise_size_limit,
+ vdex->Size(),
+ vdex->Begin(),
+ vdex->End(),
+ vdex_filename);
+ }
}
+
return vdex;
}
diff --git a/test/821-madvise-willneed/Android.bp b/test/821-madvise-willneed/Android.bp
new file mode 100644
index 0000000..86e883c
--- /dev/null
+++ b/test/821-madvise-willneed/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `821-madvise-willneed`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-821-madvise-willneed",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-821-madvise-willneed-expected-stdout",
+ ":art-run-test-821-madvise-willneed-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-821-madvise-willneed-expected-stdout",
+ out: ["art-run-test-821-madvise-willneed-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-821-madvise-willneed-expected-stderr",
+ out: ["art-run-test-821-madvise-willneed-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/821-madvise-willneed/expected-stderr.txt b/test/821-madvise-willneed/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/821-madvise-willneed/expected-stderr.txt
diff --git a/test/821-madvise-willneed/expected-stdout.txt b/test/821-madvise-willneed/expected-stdout.txt
new file mode 100644
index 0000000..af5626b
--- /dev/null
+++ b/test/821-madvise-willneed/expected-stdout.txt
@@ -0,0 +1 @@
+Hello, world!
diff --git a/test/821-madvise-willneed/info.txt b/test/821-madvise-willneed/info.txt
new file mode 100644
index 0000000..cea36a0
--- /dev/null
+++ b/test/821-madvise-willneed/info.txt
@@ -0,0 +1 @@
+Verify that passing madvise size limits argument to ART does not cause a crash
diff --git a/test/821-madvise-willneed/run b/test/821-madvise-willneed/run
new file mode 100644
index 0000000..2c3917f
--- /dev/null
+++ b/test/821-madvise-willneed/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.i
+
+# Load and run HelloWorld after madvising odex, vdex, art files to 100MB size
+# limit
+exec ${RUN} "${@}" --runtime-option -XMadviseWillNeedVdexFileSize:104857600 \
+ --runtime-option -XMadviseWillNeedOdexFileSize:104857600 \
+ --runtime-option -XMadviseWillNeedArtFileSize:104857600
diff --git a/test/821-madvise-willneed/src/Main.java b/test/821-madvise-willneed/src/Main.java
new file mode 100644
index 0000000..cc8c521
--- /dev/null
+++ b/test/821-madvise-willneed/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 9564041..ce959ce 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1380,5 +1380,10 @@
"bug": "b/175435088",
"description": ["Test is time-sensitive and fails on gcstress and",
"interpreter-access-checks configurations."]
+ },
+ {
+ "tests": ["821-madvise-willneed"],
+ "variant": "jvm",
+ "description": ["Adding custom madvise flags. Not to be tested on default jvm"]
}
]