From 24e04aa68c575d349eac0d9d09aab9bd3106ef94 Mon Sep 17 00:00:00 2001 From: Jean Christophe Beyler Date: Fri, 12 Sep 2014 12:03:25 -0700 Subject: ART: Allow the execution to stop if the compilation fails via an option The current implementation continues the execution of the application if dex2oat fails by relying on the interpreter. This patch adds a -Xno-dex-file-fallback option to stop the default behavior. This can be used two-fold. First, one can enforce that a runtime only starts with a boot image. A follow-up patch will ensure that dex2oat (for apps) and patchoat in general request that mode and close gracefully otherwise. Second, this can be used for testing and debugging purposes, as it ensures that compiler failures & aborts are not silently ignored. Add testing. Bug: 19100590 Change-Id: Iaf07b5ccf00942ca8a8ec8687599320a3ddbc089 Signed-off-by: Jean Christophe Beyler --- cmdline/cmdline_parser_test.cc | 1 + runtime/class_linker.cc | 11 +++-- runtime/parsed_options.cc | 4 ++ runtime/runtime.cc | 11 ++++- runtime/runtime.h | 8 ++++ runtime/runtime_options.def | 2 +- test/116-nodex2oat/run | 2 +- test/118-noimage-dex2oat/expected.txt | 2 + test/118-noimage-dex2oat/run | 6 ++- test/119-noimage-patchoat/check | 20 +++++++++ test/119-noimage-patchoat/expected.txt | 3 ++ test/119-noimage-patchoat/run | 6 ++- test/134-nodex2oat-nofallback/check | 32 +++++++++++++++ test/134-nodex2oat-nofallback/expected.txt | 64 +++++++++++++++++++++++++++++ test/134-nodex2oat-nofallback/info.txt | 2 + test/134-nodex2oat-nofallback/run | 20 +++++++++ test/134-nodex2oat-nofallback/src/Main.java | 36 ++++++++++++++++ test/Android.run-test.mk | 3 +- 18 files changed, 224 insertions(+), 9 deletions(-) create mode 100755 test/119-noimage-patchoat/check create mode 100755 test/134-nodex2oat-nofallback/check create mode 100644 test/134-nodex2oat-nofallback/expected.txt create mode 100644 test/134-nodex2oat-nofallback/info.txt create mode 100755 test/134-nodex2oat-nofallback/run create mode 100644 test/134-nodex2oat-nofallback/src/Main.java diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 288f7ac..b740b41 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -202,6 +202,7 @@ TEST_F(CmdlineParserTest, TestSimpleSuccesses) { EXPECT_SINGLE_PARSE_VALUE(false, "-XX:DisableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM); EXPECT_SINGLE_PARSE_VALUE(0.5, "-XX:HeapTargetUtilization=0.5", M::HeapTargetUtilization); EXPECT_SINGLE_PARSE_VALUE(5u, "-XX:ParallelGCThreads=5", M::ParallelGCThreads); + EXPECT_SINGLE_PARSE_EXISTS("-Xno-dex-file-fallback", M::NoDexFileFallback); } // TEST_F TEST_F(CmdlineParserTest, TestSimpleFailures) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index ee66b49..3278751 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1008,10 +1008,15 @@ bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_ // Failed, bail. if (open_oat_file.get() == nullptr) { - std::string error_msg; // dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress. - DexFile::Open(dex_location, dex_location, &error_msg, dex_files); - error_msgs->push_back(error_msg); + if (Runtime::Current()->IsDexFileFallbackEnabled()) { + std::string error_msg; + if (!DexFile::Open(dex_location, dex_location, &error_msg, dex_files)) { + error_msgs->push_back(error_msg); + } + } else { + error_msgs->push_back("Fallback mode disabled, skipping dex files."); + } return false; } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 99369ca..c0c7baa 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -241,6 +241,8 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xzygote-max-failed-boots=_") .WithType() .IntoKey(M::ZygoteMaxFailedBoots) + .Define("-Xno-dex-file-fallback") + .IntoKey(M::NoDexFileFallback) .Ignore({ "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa", "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_", @@ -631,6 +633,8 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -X[no]relocate\n"); UsageMessage(stream, " -X[no]dex2oat (Whether to invoke dex2oat on the application)\n"); UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n"); + UsageMessage(stream, " -Xno-dex-file-fallback " + "(Don't fall back to dex files without oat files)\n"); UsageMessage(stream, "\n"); UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f38f65e..1af08c1 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -169,6 +169,7 @@ Runtime::Runtime() dump_gc_performance_on_shutdown_(false), preinitialization_transaction_(nullptr), verify_(false), + allow_dex_file_fallback_(true), target_sdk_version_(0), implicit_null_checks_(false), implicit_so_checks_(false), @@ -380,7 +381,9 @@ bool Runtime::Create(const RuntimeOptions& options, bool ignore_unrecognized) { InitLogging(NULL); // Calls Locks::Init() as a side effect. instance_ = new Runtime; if (!instance_->Init(options, ignore_unrecognized)) { - delete instance_; + // TODO: Currently deleting the instance will abort the runtime on destruction. Now This will + // leak memory, instead. Fix the destructor. b/19100793. + // delete instance_; instance_ = NULL; return false; } @@ -762,6 +765,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) intern_table_ = new InternTable; verify_ = runtime_options.GetOrDefault(Opt::Verify); + allow_dex_file_fallback_ = !runtime_options.Exists(Opt::NoDexFileFallback); if (runtime_options.GetOrDefault(Opt::Interpret)) { GetInstrumentation()->ForceInterpretOnly(); @@ -800,6 +804,11 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM), runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs)); + if (heap_->GetImageSpace() == nullptr && !allow_dex_file_fallback_) { + LOG(ERROR) << "Dex file fallback disabled, cannot continue without image."; + return false; + } + dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); if (runtime_options.Exists(Opt::JdwpOptions)) { diff --git a/runtime/runtime.h b/runtime/runtime.h index fb9ca40..944c8bd 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -505,6 +505,10 @@ class Runtime { return verify_; } + bool IsDexFileFallbackEnabled() const { + return allow_dex_file_fallback_; + } + bool RunningOnValgrind() const { return running_on_valgrind_; } @@ -668,6 +672,10 @@ class Runtime { // If false, verification is disabled. True by default. bool verify_; + // If true, the runtime may use dex files directly with the interpreter if an oat file is not + // available/usable. + bool allow_dex_file_fallback_; + // Specifies target SDK version to allow workarounds for certain API levels. int32_t target_sdk_version_; diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 71a0152..d9cc4d4 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -115,6 +115,6 @@ RUNTIME_OPTIONS_KEY (void (*)(int32_t status), \ // Runtime::Abort. RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullptr) RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 1) - +RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback) #undef RUNTIME_OPTIONS_KEY diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run index 9e5c7dd..2cdb3f7 100755 --- a/test/116-nodex2oat/run +++ b/test/116-nodex2oat/run @@ -24,7 +24,7 @@ if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then exit 1 fi -# Make sure we can run without an oat file, +# Make sure we can run without an oat file. echo "Run -Xnodex2oat" ${RUN} ${flags} --runtime-option -Xnodex2oat diff --git a/test/118-noimage-dex2oat/expected.txt b/test/118-noimage-dex2oat/expected.txt index bcb695d..0103e89 100644 --- a/test/118-noimage-dex2oat/expected.txt +++ b/test/118-noimage-dex2oat/expected.txt @@ -1,6 +1,8 @@ Run -Xnoimage-dex2oat Has image is false, is image dex2oat enabled is false, is BOOTCLASSPATH on disk is false. testB18485243 PASS +Run -Xnoimage-dex2oat -Xno-dex-file-fallback +Failed to initialize runtime (check log for details) Run -Ximage-dex2oat Has image is true, is image dex2oat enabled is true, is BOOTCLASSPATH on disk is true. testB18485243 PASS diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run index 2037797..4b1d0ce 100644 --- a/test/118-noimage-dex2oat/run +++ b/test/118-noimage-dex2oat/run @@ -46,10 +46,14 @@ bpath="${bpath}:${framework}/bouncycastle${bpath_suffix}.jar" bpath_arg="--runtime-option -Xbootclasspath:${bpath}" -# Make sure we can run without an oat file, +# Make sure we can run without an oat file. echo "Run -Xnoimage-dex2oat" ${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat +# Make sure we cannot run without an oat file without fallback. +echo "Run -Xnoimage-dex2oat -Xno-dex-file-fallback" +${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat --runtime-option -Xno-dex-file-fallback + # Make sure we can run with the oat file. echo "Run -Ximage-dex2oat" ${RUN} ${flags} ${bpath_arg} --runtime-option -Ximage-dex2oat diff --git a/test/119-noimage-patchoat/check b/test/119-noimage-patchoat/check new file mode 100755 index 0000000..7b47ac1 --- /dev/null +++ b/test/119-noimage-patchoat/check @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2014 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. + +# Strip the process pids and line numbers from exact error messages. +sed -e 's/^art E.*\] //' "$2" > "$2.tmp" + +diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null diff --git a/test/119-noimage-patchoat/expected.txt b/test/119-noimage-patchoat/expected.txt index e864268..3d2b4f1 100644 --- a/test/119-noimage-patchoat/expected.txt +++ b/test/119-noimage-patchoat/expected.txt @@ -1,5 +1,8 @@ Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false Has image is false, is image dex2oat enabled is false. +Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -Xno-dex-file-fallback +Dex file fallback disabled, cannot continue without image. +Failed to initialize runtime (check log for details) Run -Ximage-dex2oat Has image is true, is image dex2oat enabled is true. Run default diff --git a/test/119-noimage-patchoat/run b/test/119-noimage-patchoat/run index c409cbb..02a64f8 100644 --- a/test/119-noimage-patchoat/run +++ b/test/119-noimage-patchoat/run @@ -29,10 +29,14 @@ else false_bin="/system/bin/false" fi -# Make sure we can run without an image file, +# Make sure we can run without an image file. echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false" ${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xpatchoat:${false_bin} +# Make sure we cannot run without an image file without fallback. +echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -Xno-dex-file-fallback" +${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xpatchoat:${false_bin} --runtime-option -Xno-dex-file-fallback + # Make sure we can run with the image file. echo "Run -Ximage-dex2oat" ${RUN} ${flags} ${BPATH} --runtime-option -Ximage-dex2oat diff --git a/test/134-nodex2oat-nofallback/check b/test/134-nodex2oat-nofallback/check new file mode 100755 index 0000000..d929c8f --- /dev/null +++ b/test/134-nodex2oat-nofallback/check @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Copyright (C) 2014 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. + +# The head of the log. +HEAD=$(head -n 1 "$2") +EXPECTED_HEAD="Unable to locate class 'Main'" + +# Content somewhere inside the output. +grep 'Suppressed: java.io.IOException: Fallback mode disabled, skipping dex files.' "$2" >/dev/null +MSG_FOUND=$? + +if [[ "$HEAD" != "$EXPECTED_HEAD" || $MSG_FOUND -ne 0 ]] ; then + # Print out the log and return with error. + cat "$2" + exit 1 +fi + +# Success. +exit 0 \ No newline at end of file diff --git a/test/134-nodex2oat-nofallback/expected.txt b/test/134-nodex2oat-nofallback/expected.txt new file mode 100644 index 0000000..9b6c846 --- /dev/null +++ b/test/134-nodex2oat-nofallback/expected.txt @@ -0,0 +1,64 @@ +# This file isn't used, but gives an example of the output we expect. +Unable to locate class 'Main' +java.lang.ClassNotFoundException: Didn't find class "Main" on path: DexPathList[[zip file "/tmp/user/test-12345/134-nodex2oat-nofallback.jar"],nativeLibraryDirectories=[/ssd2/aosp-master3/out/host/linux-x86/lib]] + at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) + at java.lang.ClassLoader.loadClass(ClassLoader.java:511) + at java.lang.ClassLoader.loadClass(ClassLoader.java:469) + Suppressed: java.io.IOException: Fallback mode disabled, skipping dex files. + at dalvik.system.DexFile.openDexFileNative(Native Method) + at dalvik.system.DexFile.openDexFile(DexFile.java:295) + at dalvik.system.DexFile.(DexFile.java:80) + at dalvik.system.DexFile.(DexFile.java:59) + at dalvik.system.DexPathList.loadDexFile(DexPathList.java:262) + at dalvik.system.DexPathList.makeDexElements(DexPathList.java:231) + at dalvik.system.DexPathList.(DexPathList.java:109) + at dalvik.system.BaseDexClassLoader.(BaseDexClassLoader.java:48) + at dalvik.system.PathClassLoader.(PathClassLoader.java:38) + at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:128) + at java.lang.ClassLoader.access$000(ClassLoader.java:65) + at java.lang.ClassLoader$SystemClassLoader.(ClassLoader.java:81) + at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:137) + Caused by: java.io.IOException: Failed to open oat file from dex location '/tmp/user/test-12345/134-nodex2oat-nofallback.jar' + ... 13 more + Caused by: java.io.IOException: Failed to open oat file from /tmp/user/test-12345/x86/134-nodex2oat-nofallback.odex (error Failed to open oat filename for reading: No such file or directory) (no dalvik_cache availible) and relocation failed. + ... 13 more + Caused by: java.io.IOException + ... 13 more + Suppressed: java.lang.ClassNotFoundException: Main + at java.lang.Class.classForName(Native Method) + at java.lang.BootClassLoader.findClass(ClassLoader.java:781) + at java.lang.BootClassLoader.loadClass(ClassLoader.java:841) + at java.lang.ClassLoader.loadClass(ClassLoader.java:504) + ... 1 more + Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available +java.lang.ClassNotFoundException: Didn't find class "Main" on path: DexPathList[[zip file "/tmp/user/test-12345/134-nodex2oat-nofallback.jar"],nativeLibraryDirectories=[/ssd2/aosp-master3/out/host/linux-x86/lib]] + at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) + at java.lang.ClassLoader.loadClass(ClassLoader.java:511) + at java.lang.ClassLoader.loadClass(ClassLoader.java:469) + Suppressed: java.io.IOException: Fallback mode disabled, skipping dex files. + at dalvik.system.DexFile.openDexFileNative(Native Method) + at dalvik.system.DexFile.openDexFile(DexFile.java:295) + at dalvik.system.DexFile.(DexFile.java:80) + at dalvik.system.DexFile.(DexFile.java:59) + at dalvik.system.DexPathList.loadDexFile(DexPathList.java:262) + at dalvik.system.DexPathList.makeDexElements(DexPathList.java:231) + at dalvik.system.DexPathList.(DexPathList.java:109) + at dalvik.system.BaseDexClassLoader.(BaseDexClassLoader.java:48) + at dalvik.system.PathClassLoader.(PathClassLoader.java:38) + at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:128) + at java.lang.ClassLoader.access$000(ClassLoader.java:65) + at java.lang.ClassLoader$SystemClassLoader.(ClassLoader.java:81) + at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:137) + Caused by: java.io.IOException: Failed to open oat file from dex location '/tmp/user/test-12345/134-nodex2oat-nofallback.jar' + ... 13 more + Caused by: java.io.IOException: Failed to open oat file from /tmp/user/test-12345/x86/134-nodex2oat-nofallback.odex (error Failed to open oat filename for reading: No such file or directory) (no dalvik_cache availible) and relocation failed. + ... 13 more + Caused by: java.io.IOException: + ... 13 more + Suppressed: java.lang.ClassNotFoundException: Main + at java.lang.Class.classForName(Native Method) + at java.lang.BootClassLoader.findClass(ClassLoader.java:781) + at java.lang.BootClassLoader.loadClass(ClassLoader.java:841) + at java.lang.ClassLoader.loadClass(ClassLoader.java:504) + ... 1 more + Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available diff --git a/test/134-nodex2oat-nofallback/info.txt b/test/134-nodex2oat-nofallback/info.txt new file mode 100644 index 0000000..3004729 --- /dev/null +++ b/test/134-nodex2oat-nofallback/info.txt @@ -0,0 +1,2 @@ +Test that disables dex2oat'ing the application, and disable fallback. This is derived from test +116. It needs it separate test as it needs a custom check script. \ No newline at end of file diff --git a/test/134-nodex2oat-nofallback/run b/test/134-nodex2oat-nofallback/run new file mode 100755 index 0000000..38b4adb --- /dev/null +++ b/test/134-nodex2oat-nofallback/run @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2014 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. + +flags="${@}" + +# Make sure we cannot run without an oat file without fallback. +${RUN} ${flags} --runtime-option -Xnodex2oat --runtime-option -Xno-dex-file-fallback diff --git a/test/134-nodex2oat-nofallback/src/Main.java b/test/134-nodex2oat-nofallback/src/Main.java new file mode 100644 index 0000000..37ac9d5 --- /dev/null +++ b/test/134-nodex2oat-nofallback/src/Main.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 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( + "Has oat is " + hasOat() + ", is dex2oat enabled is " + isDex2OatEnabled() + "."); + + if (hasOat() && !isDex2OatEnabled()) { + throw new Error("Application with dex2oat disabled runs with an oat file"); + } else if (!hasOat() && isDex2OatEnabled()) { + throw new Error("Application with dex2oat enabled runs without an oat file"); + } + } + + static { + System.loadLibrary("arttest"); + } + + private native static boolean hasOat(); + + private native static boolean isDex2OatEnabled(); +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index c8e0ec5..8e4b46b 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -181,7 +181,8 @@ TEST_ART_TIMING_SENSITIVE_RUN_TESTS := # Note 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild. TEST_ART_BROKEN_PREBUILD_RUN_TESTS := \ 116-nodex2oat \ - 118-noimage-dex2oat + 118-noimage-dex2oat \ + 134-nodex2oat-nofallback ifneq (,$(filter prebuild,$(PREBUILD_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),prebuild, \ -- cgit v1.1