summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmdline/cmdline_types.h46
-rw-r--r--compiler/Android.bp4
-rw-r--r--compiler/driver/compiler_options.cc131
-rw-r--r--compiler/driver/compiler_options.h11
-rw-r--r--compiler/driver/compiler_options_map-inl.h159
-rw-r--r--compiler/driver/compiler_options_map-storage.h48
-rw-r--r--compiler/driver/compiler_options_map.def60
-rw-r--r--compiler/driver/compiler_options_map.h45
-rw-r--r--compiler/driver/simple_compiler_options_map.h64
-rw-r--r--compiler/jit/jit_compiler.cc21
-rw-r--r--compiler/utils/arm/jni_macro_assembler_arm_vixl.cc15
-rw-r--r--compiler/utils/arm64/jni_macro_assembler_arm64.cc15
-rw-r--r--compiler/utils/test_dex_file_builder.h9
-rw-r--r--dex2oat/Android.bp5
-rw-r--r--dex2oat/dex2oat.cc395
-rw-r--r--dex2oat/dex2oat_image_test.cc11
-rw-r--r--dex2oat/dex2oat_options.cc268
-rw-r--r--dex2oat/dex2oat_options.def90
-rw-r--r--dex2oat/dex2oat_options.h76
-rw-r--r--dex2oat/dex2oat_test.cc5
-rw-r--r--dex2oat/linker/oat_writer.cc64
-rw-r--r--dex2oat/linker/oat_writer_test.cc26
-rw-r--r--dexdump/dexdump.cc5
-rw-r--r--dexlayout/dexlayout.cc19
-rw-r--r--dexlayout/dexlayout_test.cc11
-rw-r--r--dexlist/dexlist.cc3
-rw-r--r--dexoptanalyzer/dexoptanalyzer.cc33
-rw-r--r--openjdkjvmti/events.cc6
-rw-r--r--openjdkjvmti/fixed_up_dex_file.cc3
-rw-r--r--openjdkjvmti/ti_class.cc13
-rw-r--r--openjdkjvmti/ti_method.cc5
-rw-r--r--openjdkjvmti/ti_redefine.cc17
-rw-r--r--openjdkjvmti/ti_search.cc3
-rw-r--r--openjdkjvmti/ti_stack.cc2
-rw-r--r--profman/profman.cc23
-rw-r--r--runtime/Android.bp3
-rw-r--r--runtime/base/bit_struct.h290
-rw-r--r--runtime/base/bit_struct_detail.h90
-rw-r--r--runtime/base/bit_struct_test.cc269
-rw-r--r--runtime/base/bit_utils.h122
-rw-r--r--runtime/base/bit_utils_test.cc91
-rw-r--r--runtime/base/file_magic.cc4
-rw-r--r--runtime/base/file_magic.h1
-rw-r--r--runtime/base/variant_map.h8
-rw-r--r--runtime/class_linker.cc15
-rw-r--r--runtime/class_linker_test.cc12
-rw-r--r--runtime/class_loader_context.cc18
-rw-r--r--runtime/class_loader_context_test.cc5
-rw-r--r--runtime/common_runtime_test.cc5
-rw-r--r--runtime/dex2oat_environment_test.h18
-rw-r--r--runtime/dex_file.cc490
-rw-r--r--runtime/dex_file.h444
-rw-r--r--runtime/dex_file_loader.cc484
-rw-r--r--runtime/dex_file_loader.h200
-rw-r--r--runtime/dex_file_test.cc75
-rw-r--r--runtime/dex_file_verifier_test.cc6
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc15
-rw-r--r--runtime/gc/space/image_space.cc9
-rw-r--r--runtime/indirect_reference_table.cc2
-rw-r--r--runtime/indirect_reference_table.h2
-rw-r--r--runtime/java_vm_ext.cc60
-rw-r--r--runtime/java_vm_ext.h6
-rw-r--r--runtime/java_vm_ext_test.cc46
-rw-r--r--runtime/jit/jit_code_cache.cc7
-rw-r--r--runtime/jit/profile_compilation_info.cc5
-rw-r--r--runtime/jit/profile_saver.cc14
-rw-r--r--runtime/native/dalvik_system_DexFile.cc13
-rw-r--r--runtime/native/dalvik_system_VMDebug.cc22
-rw-r--r--runtime/native/java_lang_VMClassLoader.cc3
-rw-r--r--runtime/native_dex_file.cc56
-rw-r--r--runtime/native_dex_file.h61
-rw-r--r--runtime/oat_file.cc194
-rw-r--r--runtime/oat_file.h12
-rw-r--r--runtime/oat_file_assistant.cc83
-rw-r--r--runtime/oat_file_assistant.h9
-rw-r--r--runtime/oat_file_assistant_test.cc120
-rw-r--r--runtime/oat_file_manager.cc14
-rw-r--r--runtime/parsed_options.cc3
-rw-r--r--runtime/runtime.cc3
-rw-r--r--runtime/runtime_options.def2
-rw-r--r--runtime/utils.cc3
-rw-r--r--runtime/vdex_file.cc19
-rw-r--r--test/1914-get-local-instance/expected.txt3
-rw-r--r--test/1914-get-local-instance/src/art/Test1914.java30
-rw-r--r--test/1929-exception-catch-exception/build2
-rw-r--r--test/1938-transform-abstract-single-impl/expected.txt4
-rw-r--r--test/1938-transform-abstract-single-impl/info.txt2
-rwxr-xr-xtest/1938-transform-abstract-single-impl/run17
-rw-r--r--test/1938-transform-abstract-single-impl/src/Main.java100
-rw-r--r--test/1938-transform-abstract-single-impl/src/art/Redefinition.java91
-rw-r--r--test/1939-proxy-frames/expected.txt8
-rw-r--r--test/1939-proxy-frames/info.txt2
-rw-r--r--test/1939-proxy-frames/local_instance.cc66
-rwxr-xr-xtest/1939-proxy-frames/run18
-rw-r--r--test/1939-proxy-frames/src/Main.java21
-rw-r--r--test/1939-proxy-frames/src/art/Breakpoint.java202
-rw-r--r--test/1939-proxy-frames/src/art/Locals.java121
-rw-r--r--test/1939-proxy-frames/src/art/StackTrace.java68
-rw-r--r--test/1939-proxy-frames/src/art/Suspension.java30
-rw-r--r--test/1939-proxy-frames/src/art/Test1939.java175
-rwxr-xr-xtest/450-checker-types/build2
-rwxr-xr-xtest/458-checker-instruct-simplification/build2
-rwxr-xr-xtest/463-checker-boolean-simplifier/build2
-rw-r--r--test/476-checker-ctor-fence-redun-elim/build2
-rw-r--r--test/482-checker-loop-back-edge-use/build2
-rw-r--r--test/484-checker-register-hints/build2
-rwxr-xr-xtest/530-checker-lse/build2
-rw-r--r--test/549-checker-types-merge/build2
-rwxr-xr-xtest/565-checker-doublenegbitwise/build2
-rw-r--r--test/565-checker-rotate/build2
-rw-r--r--test/566-checker-signum/build2
-rw-r--r--test/567-checker-compare/build2
-rw-r--r--test/570-checker-osr/build2
-rwxr-xr-xtest/586-checker-null-array-get/build2
-rwxr-xr-xtest/593-checker-boolean-2-integral-conv/build2
-rw-r--r--test/597-deopt-invoke-stub/run7
-rw-r--r--test/611-checker-simplify-if/build2
-rw-r--r--test/618-checker-induction/build2
-rw-r--r--test/910-methods/build2
-rw-r--r--test/910-methods/check2
-rw-r--r--test/911-get-stack-trace/build2
-rw-r--r--test/913-heaps/build2
-rw-r--r--test/983-source-transform-verify/source_transform.cc17
-rw-r--r--test/Android.bp1
-rwxr-xr-xtest/etc/run-test-jar4
-rw-r--r--test/knownfailures.json9
-rwxr-xr-xtest/testrunner/run_build_test_target.py4
-rwxr-xr-xtest/testrunner/testrunner.py18
-rw-r--r--tools/ahat/Android.mk20
-rw-r--r--tools/ahat/etc/L.hprof (renamed from tools/ahat/test-dump/L.hprof)bin11930828 -> 11930828 bytes
-rw-r--r--tools/ahat/etc/O.hprof (renamed from tools/ahat/test-dump/O.hprof)bin5921872 -> 5921872 bytes
-rw-r--r--tools/ahat/etc/README.txt9
-rw-r--r--tools/ahat/etc/RI.hprof (renamed from tools/ahat/test-dump/RI.hprof)bin1819716 -> 1819716 bytes
-rw-r--r--tools/ahat/etc/ahat-tests.mf (renamed from tools/ahat/test/manifest.txt)0
-rw-r--r--tools/ahat/etc/ahat.mf (renamed from tools/ahat/src/manifest.txt)0
-rw-r--r--tools/ahat/etc/style.css (renamed from tools/ahat/src/style.css)0
-rw-r--r--tools/ahat/etc/test-dump.pro (renamed from tools/ahat/test-dump/config.pro)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/AhatHandler.java (renamed from tools/ahat/src/AhatHandler.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/AhatHttpHandler.java (renamed from tools/ahat/src/AhatHttpHandler.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/BitmapHandler.java (renamed from tools/ahat/src/BitmapHandler.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/Column.java (renamed from tools/ahat/src/Column.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/Doc.java (renamed from tools/ahat/src/Doc.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/DocString.java (renamed from tools/ahat/src/DocString.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/DominatedList.java (renamed from tools/ahat/src/DominatedList.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/HeapTable.java (renamed from tools/ahat/src/HeapTable.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/HtmlDoc.java (renamed from tools/ahat/src/HtmlDoc.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/HtmlEscaper.java (renamed from tools/ahat/src/HtmlEscaper.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/Main.java (renamed from tools/ahat/src/Main.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/Menu.java (renamed from tools/ahat/src/Menu.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/ObjectHandler.java (renamed from tools/ahat/src/ObjectHandler.java)57
-rw-r--r--tools/ahat/src/main/com/android/ahat/ObjectsHandler.java (renamed from tools/ahat/src/ObjectsHandler.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/OverviewHandler.java (renamed from tools/ahat/src/OverviewHandler.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/Query.java (renamed from tools/ahat/src/Query.java)4
-rw-r--r--tools/ahat/src/main/com/android/ahat/RootedHandler.java (renamed from tools/ahat/src/RootedHandler.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/SiteHandler.java (renamed from tools/ahat/src/SiteHandler.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/SitePrinter.java (renamed from tools/ahat/src/SitePrinter.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/SizeTable.java (renamed from tools/ahat/src/SizeTable.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/StaticHandler.java (renamed from tools/ahat/src/StaticHandler.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/SubsetSelector.java (renamed from tools/ahat/src/SubsetSelector.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/Summarizer.java (renamed from tools/ahat/src/Summarizer.java)8
-rw-r--r--tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java (renamed from tools/ahat/src/dominators/DominatorsComputation.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java (renamed from tools/ahat/src/heapdump/AhatArrayInstance.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java (renamed from tools/ahat/src/heapdump/AhatClassInstance.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java (renamed from tools/ahat/src/heapdump/AhatClassObj.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatField.java (renamed from tools/ahat/src/heapdump/AhatField.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java (renamed from tools/ahat/src/heapdump/AhatHeap.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java (renamed from tools/ahat/src/heapdump/AhatInstance.java)23
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderClassObj.java (renamed from tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderInstance.java (renamed from tools/ahat/src/heapdump/AhatPlaceHolderInstance.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java (renamed from tools/ahat/src/heapdump/AhatSnapshot.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Diff.java (renamed from tools/ahat/src/heapdump/Diff.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java (renamed from tools/ahat/src/heapdump/DiffFields.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java (renamed from tools/ahat/src/heapdump/Diffable.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java (renamed from tools/ahat/src/heapdump/DiffedFieldValue.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/DominatorReferenceIterator.java (renamed from tools/ahat/src/heapdump/DominatorReferenceIterator.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Field.java (renamed from tools/ahat/src/heapdump/Field.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java (renamed from tools/ahat/src/heapdump/FieldValue.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java (renamed from tools/ahat/src/heapdump/HprofFormatException.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Instances.java (renamed from tools/ahat/src/heapdump/Instances.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Parser.java (renamed from tools/ahat/src/heapdump/Parser.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java (renamed from tools/ahat/src/heapdump/PathElement.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Reference.java (renamed from tools/ahat/src/heapdump/Reference.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/RootType.java (renamed from tools/ahat/src/heapdump/RootType.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Site.java (renamed from tools/ahat/src/heapdump/Site.java)2
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Size.java (renamed from tools/ahat/src/heapdump/Size.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/SkipNullsIterator.java (renamed from tools/ahat/src/heapdump/SkipNullsIterator.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Sort.java (renamed from tools/ahat/src/heapdump/Sort.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java (renamed from tools/ahat/src/heapdump/SuperRoot.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Type.java (renamed from tools/ahat/src/heapdump/Type.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Value.java (renamed from tools/ahat/src/heapdump/Value.java)0
-rw-r--r--tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java (renamed from tools/ahat/src/proguard/ProguardMap.java)0
-rw-r--r--tools/ahat/src/test-dump/Main.java (renamed from tools/ahat/test-dump/Main.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/DiffFieldsTest.java (renamed from tools/ahat/test/DiffFieldsTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/DiffTest.java (renamed from tools/ahat/test/DiffTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/DominatorsTest.java (renamed from tools/ahat/test/DominatorsTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/HtmlEscaperTest.java (renamed from tools/ahat/test/HtmlEscaperTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/InstanceTest.java (renamed from tools/ahat/test/InstanceTest.java)4
-rw-r--r--tools/ahat/src/test/com/android/ahat/NativeAllocationTest.java (renamed from tools/ahat/test/NativeAllocationTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/ObjectHandlerTest.java (renamed from tools/ahat/test/ObjectHandlerTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/OverviewHandlerTest.java (renamed from tools/ahat/test/OverviewHandlerTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/PerformanceTest.java (renamed from tools/ahat/test/PerformanceTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/ProguardMapTest.java (renamed from tools/ahat/test/ProguardMapTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/QueryTest.java (renamed from tools/ahat/test/QueryTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/RootedHandlerTest.java (renamed from tools/ahat/test/RootedHandlerTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/SiteHandlerTest.java (renamed from tools/ahat/test/SiteHandlerTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/SiteTest.java (renamed from tools/ahat/test/SiteTest.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/TestDump.java (renamed from tools/ahat/test/TestDump.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/TestHandler.java (renamed from tools/ahat/test/TestHandler.java)0
-rw-r--r--tools/ahat/src/test/com/android/ahat/Tests.java (renamed from tools/ahat/test/Tests.java)0
-rw-r--r--tools/ahat/test-dump/README.txt7
-rw-r--r--tools/art24
-rw-r--r--tools/libjdwp_art_failures.txt29
212 files changed, 4836 insertions, 1557 deletions
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 521156a319..87bf1c4d43 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -290,26 +290,42 @@ struct CmdlineType<double> : CmdlineTypeParser<double> {
static const char* Name() { return "double"; }
};
+template <typename T>
+static inline CmdlineParseResult<T> ParseNumeric(const std::string& str) {
+ static_assert(sizeof(T) < sizeof(long long int), // NOLINT [runtime/int] [4]
+ "Current support is restricted.");
+
+ const char* begin = str.c_str();
+ char* end;
+
+ // Parse into a larger type (long long) because we can't use strtoul
+ // since it silently converts negative values into unsigned long and doesn't set errno.
+ errno = 0;
+ long long int result = strtoll(begin, &end, 10); // NOLINT [runtime/int] [4]
+ if (begin == end || *end != '\0' || errno == EINVAL) {
+ return CmdlineParseResult<T>::Failure("Failed to parse integer from " + str);
+ } else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
+ result < std::numeric_limits<T>::min() || result > std::numeric_limits<T>::max()) {
+ return CmdlineParseResult<T>::OutOfRange(
+ "Failed to parse integer from " + str + "; out of range");
+ }
+
+ return CmdlineParseResult<T>::Success(static_cast<T>(result));
+}
+
template <>
struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
Result Parse(const std::string& str) {
- const char* begin = str.c_str();
- char* end;
+ return ParseNumeric<unsigned int>(str);
+ }
- // Parse into a larger type (long long) because we can't use strtoul
- // since it silently converts negative values into unsigned long and doesn't set errno.
- errno = 0;
- long long int result = strtoll(begin, &end, 10); // NOLINT [runtime/int] [4]
- if (begin == end || *end != '\0' || errno == EINVAL) {
- return Result::Failure("Failed to parse integer from " + str);
- } else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
- result < std::numeric_limits<int>::min()
- || result > std::numeric_limits<unsigned int>::max() || result < 0) {
- return Result::OutOfRange(
- "Failed to parse integer from " + str + "; out of unsigned int range");
- }
+ static const char* Name() { return "unsigned integer"; }
+};
- return Result::Success(static_cast<unsigned int>(result));
+template <>
+struct CmdlineType<int> : CmdlineTypeParser<int> {
+ Result Parse(const std::string& str) {
+ return ParseNumeric<int>(str);
}
static const char* Name() { return "unsigned integer"; }
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 59ca4c7abf..1e4cdf2bd5 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -193,6 +193,10 @@ art_cc_defaults {
"liblzma",
],
include_dirs: ["art/disassembler"],
+ header_libs: [
+ "art_cmdlineparser_headers", // For compiler_options.
+ ],
+
export_include_dirs: ["."],
}
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 538845de19..b6cedff28a 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -18,7 +18,13 @@
#include <fstream>
+#include "android-base/stringprintf.h"
+
+#include "base/variant_map.h"
+#include "cmdline_parser.h"
+#include "compiler_options_map-inl.h"
#include "runtime.h"
+#include "simple_compiler_options_map.h"
namespace art {
@@ -71,115 +77,50 @@ bool CompilerOptions::EmitRunTimeChecksInDebugMode() const {
(kIsTargetBuild || IsCoreImage() || Runtime::Current()->UseJitCompilation());
}
-void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--huge-method-max", &huge_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseLargeMethodMax(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--large-method-max", &large_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseSmallMethodMax(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--small-method-max", &small_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseTinyMethodMax(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--tiny-method-max", &tiny_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseNumDexMethods(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--num-dex-methods", &num_dex_methods_threshold_, Usage);
-}
-
-void CompilerOptions::ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--inline-max-code-units", &inline_max_code_units_, Usage);
-}
-
-void CompilerOptions::ParseDumpInitFailures(const StringPiece& option,
- UsageFn Usage ATTRIBUTE_UNUSED) {
- DCHECK(option.starts_with("--dump-init-failures="));
- std::string file_name = option.substr(strlen("--dump-init-failures=")).data();
- init_failure_output_.reset(new std::ofstream(file_name));
+bool CompilerOptions::ParseDumpInitFailures(const std::string& option, std::string* error_msg) {
+ init_failure_output_.reset(new std::ofstream(option));
if (init_failure_output_.get() == nullptr) {
- LOG(ERROR) << "Failed to allocate ofstream";
+ *error_msg = "Failed to construct std::ofstream";
+ return false;
} else if (init_failure_output_->fail()) {
- LOG(ERROR) << "Failed to open " << file_name << " for writing the initialization "
- << "failures.";
+ *error_msg = android::base::StringPrintf(
+ "Failed to open %s for writing the initialization failures.", option.c_str());
init_failure_output_.reset();
+ return false;
}
+ return true;
}
-void CompilerOptions::ParseRegisterAllocationStrategy(const StringPiece& option,
- UsageFn Usage) {
- DCHECK(option.starts_with("--register-allocation-strategy="));
- StringPiece choice = option.substr(strlen("--register-allocation-strategy=")).data();
- if (choice == "linear-scan") {
+bool CompilerOptions::ParseRegisterAllocationStrategy(const std::string& option,
+ std::string* error_msg) {
+ if (option == "linear-scan") {
register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorLinearScan;
- } else if (choice == "graph-color") {
+ } else if (option == "graph-color") {
register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorGraphColor;
} else {
- Usage("Unrecognized register allocation strategy. Try linear-scan, or graph-color.");
+ *error_msg = "Unrecognized register allocation strategy. Try linear-scan, or graph-color.";
+ return false;
}
+ return true;
}
-bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usage) {
- if (option.starts_with("--compiler-filter=")) {
- const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
- if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, &compiler_filter_)) {
- Usage("Unknown --compiler-filter value %s", compiler_filter_string);
- }
- } else if (option == "--compile-pic") {
- compile_pic_ = true;
- } else if (option.starts_with("--huge-method-max=")) {
- ParseHugeMethodMax(option, Usage);
- } else if (option.starts_with("--large-method-max=")) {
- ParseLargeMethodMax(option, Usage);
- } else if (option.starts_with("--small-method-max=")) {
- ParseSmallMethodMax(option, Usage);
- } else if (option.starts_with("--tiny-method-max=")) {
- ParseTinyMethodMax(option, Usage);
- } else if (option.starts_with("--num-dex-methods=")) {
- ParseNumDexMethods(option, Usage);
- } else if (option.starts_with("--inline-max-code-units=")) {
- ParseInlineMaxCodeUnits(option, Usage);
- } else if (option == "--generate-debug-info" || option == "-g") {
- generate_debug_info_ = true;
- } else if (option == "--no-generate-debug-info") {
- generate_debug_info_ = false;
- } else if (option == "--generate-mini-debug-info") {
- generate_mini_debug_info_ = true;
- } else if (option == "--no-generate-mini-debug-info") {
- generate_mini_debug_info_ = false;
- } else if (option == "--generate-build-id") {
- generate_build_id_ = true;
- } else if (option == "--no-generate-build-id") {
- generate_build_id_ = false;
- } else if (option == "--debuggable") {
- debuggable_ = true;
- } else if (option.starts_with("--top-k-profile-threshold=")) {
- ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold_, Usage);
- } else if (option == "--abort-on-hard-verifier-error") {
- abort_on_hard_verifier_failure_ = true;
- } else if (option == "--no-abort-on-hard-verifier-error") {
- abort_on_hard_verifier_failure_ = false;
- } else if (option.starts_with("--dump-init-failures=")) {
- ParseDumpInitFailures(option, Usage);
- } else if (option.starts_with("--dump-cfg=")) {
- dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
- } else if (option == "--dump-cfg-append") {
- dump_cfg_append_ = true;
- } else if (option.starts_with("--register-allocation-strategy=")) {
- ParseRegisterAllocationStrategy(option, Usage);
- } else if (option.starts_with("--verbose-methods=")) {
- // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
- // conditional on having verbose methods.
- gLogVerbosity.compiler = false;
- Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
- } else {
- // Option not recognized.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+bool CompilerOptions::ParseCompilerOptions(const std::vector<std::string>& options,
+ bool ignore_unrecognized,
+ std::string* error_msg) {
+ auto parser = CreateSimpleParser(ignore_unrecognized);
+ CmdlineResult parse_result = parser.Parse(options);
+ if (!parse_result.IsSuccess()) {
+ *error_msg = parse_result.GetMessage();
return false;
}
- return true;
+
+ SimpleParseArgumentMap args = parser.ReleaseArgumentsMap();
+ return ReadCompilerOptions(args, this, error_msg);
}
+#pragma GCC diagnostic pop
+
} // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index a9372c4844..311dbd569e 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -231,7 +231,9 @@ class CompilerOptions FINAL {
return no_inline_from_;
}
- bool ParseCompilerOption(const StringPiece& option, UsageFn Usage);
+ bool ParseCompilerOptions(const std::vector<std::string>& options,
+ bool ignore_unrecognized,
+ std::string* error_msg);
void SetNonPic() {
compile_pic_ = false;
@@ -258,7 +260,7 @@ class CompilerOptions FINAL {
}
private:
- void ParseDumpInitFailures(const StringPiece& option, UsageFn Usage);
+ bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
void ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage);
void ParseNumDexMethods(const StringPiece& option, UsageFn Usage);
@@ -266,7 +268,7 @@ class CompilerOptions FINAL {
void ParseSmallMethodMax(const StringPiece& option, UsageFn Usage);
void ParseLargeMethodMax(const StringPiece& option, UsageFn Usage);
void ParseHugeMethodMax(const StringPiece& option, UsageFn Usage);
- void ParseRegisterAllocationStrategy(const StringPiece& option, UsageFn Usage);
+ bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
CompilerFilter::Filter compiler_filter_;
size_t huge_method_threshold_;
@@ -327,6 +329,9 @@ class CompilerOptions FINAL {
friend class CommonCompilerTest;
friend class verifier::VerifierDepsTest;
+ template <class Base>
+ friend bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string* error_msg);
+
DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
};
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
new file mode 100644
index 0000000000..9cb818a270
--- /dev/null
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
+
+#include "compiler_options_map.h"
+
+#include <memory>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+
+#include "base/macros.h"
+#include "cmdline_parser.h"
+#include "compiler_options.h"
+
+namespace art {
+
+template <class Base>
+inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string* error_msg) {
+ if (map.Exists(Base::CompilerFilter)) {
+ CompilerFilter::Filter compiler_filter;
+ if (!CompilerFilter::ParseCompilerFilter(map.Get(Base::CompilerFilter)->c_str(),
+ &compiler_filter)) {
+ *error_msg = android::base::StringPrintf("Unknown --compiler-filter value %s",
+ map.Get(Base::CompilerFilter)->c_str());
+ return false;
+ }
+ options->SetCompilerFilter(compiler_filter);
+ }
+ if (map.Exists(Base::PIC)) {
+ options->compile_pic_ = true;
+ }
+ map.AssignIfExists(Base::HugeMethodMaxThreshold, &options->huge_method_threshold_);
+ map.AssignIfExists(Base::LargeMethodMaxThreshold, &options->large_method_threshold_);
+ map.AssignIfExists(Base::SmallMethodMaxThreshold, &options->small_method_threshold_);
+ map.AssignIfExists(Base::TinyMethodMaxThreshold, &options->tiny_method_threshold_);
+ map.AssignIfExists(Base::NumDexMethodsThreshold, &options->num_dex_methods_threshold_);
+ map.AssignIfExists(Base::InlineMaxCodeUnitsThreshold, &options->inline_max_code_units_);
+ map.AssignIfExists(Base::GenerateDebugInfo, &options->generate_debug_info_);
+ map.AssignIfExists(Base::GenerateMiniDebugInfo, &options->generate_mini_debug_info_);
+ map.AssignIfExists(Base::GenerateBuildID, &options->generate_build_id_);
+ if (map.Exists(Base::Debuggable)) {
+ options->debuggable_ = true;
+ }
+ map.AssignIfExists(Base::TopKProfileThreshold, &options->top_k_profile_threshold_);
+ map.AssignIfExists(Base::AbortOnHardVerifierFailure, &options->abort_on_hard_verifier_failure_);
+ if (map.Exists(Base::DumpInitFailures)) {
+ if (!options->ParseDumpInitFailures(*map.Get(Base::DumpInitFailures), error_msg)) {
+ return false;
+ }
+ }
+ map.AssignIfExists(Base::DumpCFG, &options->dump_cfg_file_name_);
+ if (map.Exists(Base::DumpCFGAppend)) {
+ options->dump_cfg_append_ = true;
+ }
+ if (map.Exists(Base::RegisterAllocationStrategy)) {
+ if (!options->ParseRegisterAllocationStrategy(*map.Get(Base::DumpInitFailures), error_msg)) {
+ return false;
+ }
+ }
+ map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_);
+
+ return true;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+template <typename Map, typename Builder>
+inline void AddCompilerOptionsArgumentParserOptions(Builder& b) {
+ b.
+ Define("--compiler-filter=_")
+ .template WithType<std::string>()
+ .IntoKey(Map::CompilerFilter)
+
+ .Define("--compile-pic")
+ .IntoKey(Map::PIC)
+
+ .Define("--huge-method-max=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::HugeMethodMaxThreshold)
+ .Define("--large-method-max=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::LargeMethodMaxThreshold)
+ .Define("--small-method-max=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::SmallMethodMaxThreshold)
+ .Define("--tiny-method-max=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::TinyMethodMaxThreshold)
+ .Define("--num-dex-methods=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::NumDexMethodsThreshold)
+ .Define("--inline-max-code-units=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::InlineMaxCodeUnitsThreshold)
+
+ .Define({"--generate-debug-info", "-g", "--no-generate-debug-info"})
+ .WithValues({true, true, false})
+ .IntoKey(Map::GenerateDebugInfo)
+ .Define({"--generate-mini-debug-info", "--no-generate-mini-debug-info"})
+ .WithValues({true, false})
+ .IntoKey(Map::GenerateMiniDebugInfo)
+
+ .Define({"--generate-build-id", "--no-generate-build-id"})
+ .WithValues({true, false})
+ .IntoKey(Map::GenerateBuildID)
+
+ .Define("--debuggable")
+ .IntoKey(Map::Debuggable)
+
+ .Define("--top-k-profile-threshold=_")
+ .template WithType<double>().WithRange(0.0, 100.0)
+ .IntoKey(Map::TopKProfileThreshold)
+
+ .Define({"--abort-on-hard-verifier-error", "--no-abort-on-hard-verifier-error"})
+ .WithValues({true, false})
+ .IntoKey(Map::AbortOnHardVerifierFailure)
+
+ .Define("--dump-init-failures=_")
+ .template WithType<std::string>()
+ .IntoKey(Map::DumpInitFailures)
+
+ .Define("--dump-cfg=_")
+ .template WithType<std::string>()
+ .IntoKey(Map::DumpCFG)
+ .Define("--dump-cfg-append")
+ .IntoKey(Map::DumpCFGAppend)
+
+ .Define("--register-allocation-strategy=_")
+ .template WithType<std::string>()
+ .IntoKey(Map::RegisterAllocationStrategy)
+
+ .Define("--verbose-methods=_")
+ .template WithType<ParseStringList<','>>()
+ .IntoKey(Map::VerboseMethods);
+}
+
+#pragma GCC diagnostic pop
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
diff --git a/compiler/driver/compiler_options_map-storage.h b/compiler/driver/compiler_options_map-storage.h
new file mode 100644
index 0000000000..756598de05
--- /dev/null
+++ b/compiler/driver/compiler_options_map-storage.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+
+// Assumes:
+// * #include "compiler_options_map.h"
+// * namespace art
+//
+// Usage:
+// #define COMPILER_OPTIONS_MAP_TYPE TheTypeOfTheMap
+// #define COMPILER_OPTIONS_MAP_KEY_TYPE TheTypeOfTheMapsKey
+// #include "driver/compiler_options_map-storage.h
+
+#ifndef COMPILER_OPTIONS_MAP_TYPE
+#error "Expected COMPILER_OPTIONS_MAP_TYPE"
+#endif
+
+#ifndef COMPILER_OPTIONS_MAP_KEY_TYPE
+#error "Expected COMPILER_OPTIONS_MAP_KEY_TYPE"
+#endif
+
+#define COMPILER_OPTIONS_KEY(Type, Name, ...) \
+ template <typename Base, template <typename TV> class KeyType> \
+ const KeyType<Type> CompilerOptionsMap<Base, KeyType>::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4]
+#include <driver/compiler_options_map.def>
+
+template struct CompilerOptionsMap<COMPILER_OPTIONS_MAP_TYPE, COMPILER_OPTIONS_MAP_KEY_TYPE>;
+
+#undef COMPILER_OPTIONS_MAP_TYPE
+#undef COMPILER_OPTIONS_MAP_KEY_TYPE
+
+#endif // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+#undef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_ // Guard is only for cpplint
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
new file mode 100644
index 0000000000..570bc5aca7
--- /dev/null
+++ b/compiler/driver/compiler_options_map.def
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef COMPILER_OPTIONS_KEY
+#error "Please #define COMPILER_OPTIONS_KEY before #including this file"
+#define COMPILER_OPTIONS_KEY(...) // Don't display errors in this file in IDEs.
+#endif
+
+// This file defines the list of keys for CompilerOptionsMap.
+// These can be used with CompilerOptionsMap.Get/Set/etc, once that template class has been
+// instantiated.
+//
+// Column Descriptions:
+// <<Type>> <<Key Name>> (<<Default Value>>)
+//
+// Default values are only used by Map::GetOrDefault(K<T>).
+// If a default value is omitted here, T{} is used as the default value, which is
+// almost-always the value of the type as if it was memset to all 0.
+//
+// Please keep the columns aligned if possible when adding new rows.
+//
+
+// Parse-able keys from the command line.
+
+// TODO: Add type parser.
+COMPILER_OPTIONS_KEY (std::string, CompilerFilter)
+COMPILER_OPTIONS_KEY (Unit, PIC)
+COMPILER_OPTIONS_KEY (unsigned int, HugeMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, LargeMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, SmallMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, TinyMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, NumDexMethodsThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, InlineMaxCodeUnitsThreshold)
+COMPILER_OPTIONS_KEY (bool, GenerateDebugInfo)
+COMPILER_OPTIONS_KEY (bool, GenerateMiniDebugInfo)
+COMPILER_OPTIONS_KEY (bool, GenerateBuildID)
+COMPILER_OPTIONS_KEY (Unit, Debuggable)
+COMPILER_OPTIONS_KEY (double, TopKProfileThreshold)
+COMPILER_OPTIONS_KEY (bool, AbortOnHardVerifierFailure)
+COMPILER_OPTIONS_KEY (std::string, DumpInitFailures)
+COMPILER_OPTIONS_KEY (std::string, DumpCFG)
+COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend)
+// TODO: Add type parser.
+COMPILER_OPTIONS_KEY (std::string, RegisterAllocationStrategy)
+COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods)
+
+#undef COMPILER_OPTIONS_KEY
diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h
new file mode 100644
index 0000000000..b9bc8b6ea1
--- /dev/null
+++ b/compiler/driver/compiler_options_map.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
+
+#include <string>
+#include <vector>
+
+#include "base/variant_map.h"
+#include "cmdline_types.h"
+
+namespace art {
+
+// Defines a type-safe heterogeneous key->value map. This is to be used as the base for
+// an extended map.
+template <typename Base, template <typename TV> class KeyType>
+struct CompilerOptionsMap : VariantMap<Base, KeyType> {
+ // Make the next many usages of Key slightly shorter to type.
+ template <typename TValue>
+ using Key = KeyType<TValue>;
+
+ // List of key declarations, shorthand for 'static const Key<T> Name'
+#define COMPILER_OPTIONS_KEY(Type, Name, ...) static const Key<Type> (Name);
+#include "compiler_options_map.def"
+};
+
+#undef DECLARE_KEY
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
diff --git a/compiler/driver/simple_compiler_options_map.h b/compiler/driver/simple_compiler_options_map.h
new file mode 100644
index 0000000000..3860da9f66
--- /dev/null
+++ b/compiler/driver/simple_compiler_options_map.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// This file declares a completion of the CompilerOptionsMap and should be included into a
+// .cc file, only.
+
+#ifndef ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
+#define ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
+
+#include <memory>
+
+#include "compiler_options_map-inl.h"
+#include "base/variant_map.h"
+
+namespace art {
+
+template <typename TValue>
+struct SimpleParseArgumentMapKey : VariantMapKey<TValue> {
+ SimpleParseArgumentMapKey() {}
+ explicit SimpleParseArgumentMapKey(TValue default_value)
+ : VariantMapKey<TValue>(std::move(default_value)) {}
+ // Don't ODR-use constexpr default values, which means that Struct::Fields
+ // that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
+};
+
+struct SimpleParseArgumentMap : CompilerOptionsMap<SimpleParseArgumentMap,
+ SimpleParseArgumentMapKey> {
+ // This 'using' line is necessary to inherit the variadic constructor.
+ using CompilerOptionsMap<SimpleParseArgumentMap, SimpleParseArgumentMapKey>::CompilerOptionsMap;
+};
+
+#define COMPILER_OPTIONS_MAP_TYPE SimpleParseArgumentMap
+#define COMPILER_OPTIONS_MAP_KEY_TYPE SimpleParseArgumentMapKey
+#include "compiler_options_map-storage.h"
+
+using Parser = CmdlineParser<SimpleParseArgumentMap, SimpleParseArgumentMapKey>;
+
+static inline Parser CreateSimpleParser(bool ignore_unrecognized) {
+ std::unique_ptr<Parser::Builder> parser_builder =
+ std::unique_ptr<Parser::Builder>(new Parser::Builder());
+
+ AddCompilerOptionsArgumentParserOptions<SimpleParseArgumentMap>(*parser_builder);
+
+ parser_builder->IgnoreUnrecognized(ignore_unrecognized);
+
+ return parser_builder->Build();
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 511a44af04..5c89869e00 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -78,21 +78,16 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou
}
}
-// Callers of this method assume it has NO_RETURN.
-NO_RETURN static void Usage(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- std::string error;
- android::base::StringAppendV(&error, fmt, ap);
- LOG(FATAL) << error;
- va_end(ap);
- exit(EXIT_FAILURE);
-}
-
JitCompiler::JitCompiler() {
compiler_options_.reset(new CompilerOptions());
- for (const std::string& argument : Runtime::Current()->GetCompilerOptions()) {
- compiler_options_->ParseCompilerOption(argument, Usage);
+ {
+ std::string error_msg;
+ if (!compiler_options_->ParseCompilerOptions(Runtime::Current()->GetCompilerOptions(),
+ true /* ignore_unrecognized */,
+ &error_msg)) {
+ LOG(FATAL) << error_msg;
+ UNREACHABLE();
+ }
}
// JIT is never PIC, no matter what the runtime compiler options specify.
compiler_options_->SetNonPic();
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index edb3292ea7..0bae4d4b69 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -164,6 +164,21 @@ void ArmVIXLJNIMacroAssembler::RemoveFrame(size_t frame_size,
// AAPCS calling convention.
DCHECK_NE(core_spill_mask & (1 << MR), 0)
<< "core_spill_mask should contain Marking Register R" << MR;
+
+ // The following condition is a compile-time one, so it does not have a run-time cost.
+ if (kIsDebugBuild) {
+ // The following condition is a run-time one; it is executed after the
+ // previous compile-time test, to avoid penalizing non-debug builds.
+ if (emit_run_time_checks_in_debug_mode_) {
+ // Emit a run-time check verifying that the Marking Register is up-to-date.
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ // Ensure we are not clobbering a callee-save register that was restored before.
+ DCHECK_EQ(core_spill_mask & (1 << temp.GetCode()), 0)
+ << "core_spill_mask hould not contain scratch register R" << temp.GetCode();
+ asm_.GenerateMarkingRegisterCheck(temp);
+ }
+ }
}
}
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index 43c0eff8fc..573bb6d4be 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -788,6 +788,21 @@ void Arm64JNIMacroAssembler::RemoveFrame(size_t frame_size,
// AAPCS64 calling convention.
DCHECK(core_reg_list.IncludesAliasOf(mr))
<< "core_reg_list should contain Marking Register X" << mr.GetCode();
+
+ // The following condition is a compile-time one, so it does not have a run-time cost.
+ if (kIsDebugBuild) {
+ // The following condition is a run-time one; it is executed after the
+ // previous compile-time test, to avoid penalizing non-debug builds.
+ if (emit_run_time_checks_in_debug_mode_) {
+ // Emit a run-time check verifying that the Marking Register is up-to-date.
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ Register temp = temps.AcquireW();
+ // Ensure we are not clobbering a callee-save register that was restored before.
+ DCHECK(!core_reg_list.IncludesAliasOf(temp.X()))
+ << "core_reg_list should not contain scratch register X" << temp.GetCode();
+ asm_.GenerateMarkingRegisterCheck(temp);
+ }
+ }
}
}
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index 9ba3903033..e6501e0b83 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -26,7 +26,8 @@
#include "base/bit_utils.h"
#include "base/logging.h"
-#include "dex_file.h"
+#include "dex_file_loader.h"
+#include "native_dex_file.h"
namespace art {
@@ -88,8 +89,8 @@ class TestDexFileBuilder {
} header_data;
std::memset(header_data.data, 0, sizeof(header_data.data));
DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data);
- std::copy_n(DexFile::kDexMagic, 4u, header->magic_);
- std::copy_n(DexFile::kDexMagicVersions[0], 4u, header->magic_ + 4u);
+ std::copy_n(NativeDexFile::kDexMagic, 4u, header->magic_);
+ std::copy_n(NativeDexFile::kDexMagicVersions[0], 4u, header->magic_ + 4u);
header->header_size_ = sizeof(DexFile::Header);
header->endian_tag_ = DexFile::kDexEndianConstant;
header->link_size_ = 0u; // Unused.
@@ -231,7 +232,7 @@ class TestDexFileBuilder {
static constexpr bool kVerify = false;
static constexpr bool kVerifyChecksum = false;
std::string error_msg;
- std::unique_ptr<const DexFile> dex_file(DexFile::Open(
+ std::unique_ptr<const DexFile> dex_file(DexFileLoader::Open(
&dex_file_data_[0],
dex_file_data_.size(),
dex_location,
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index c9125df8f4..a93b0e7f0c 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -91,7 +91,10 @@ cc_defaults {
name: "dex2oat-defaults",
host_supported: true,
defaults: ["art_defaults"],
- srcs: ["dex2oat.cc"],
+ srcs: [
+ "dex2oat_options.cc",
+ "dex2oat.cc",
+ ],
target: {
android: {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 7b4653107f..528cf3a0a7 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -20,11 +20,13 @@
#include <sys/stat.h>
#include "base/memory_tool.h"
+#include <forward_list>
#include <fstream>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
+#include <type_traits>
#include <unordered_set>
#include <vector>
@@ -50,16 +52,19 @@
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "class_loader_context.h"
+#include "cmdline_parser.h"
#include "compiler.h"
#include "compiler_callbacks.h"
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/verification_results.h"
+#include "dex2oat_options.h"
#include "dex2oat_return_codes.h"
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
+#include "driver/compiler_options_map-inl.h"
#include "elf_file.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
@@ -235,6 +240,13 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" --oat-fd=<number>: specifies the oat output destination via a file descriptor.");
UsageError(" Example: --oat-fd=6");
UsageError("");
+ UsageError(" --input-vdex-fd=<number>: specifies the vdex input source via a file descriptor.");
+ UsageError(" Example: --input-vdex-fd=6");
+ UsageError("");
+ UsageError(" --output-vdex-fd=<number>: specifies the vdex output destination via a file");
+ UsageError(" descriptor.");
+ UsageError(" Example: --output-vdex-fd=6");
+ UsageError("");
UsageError(" --oat-location=<oat-name>: specifies a symbolic name for the file corresponding");
UsageError(" to the file descriptor specified by --oat-fd.");
UsageError(" Example: --oat-location=/data/dalvik-cache/system@app@Calculator.apk.oat");
@@ -659,76 +671,27 @@ class Dex2Oat FINAL {
std::string error_msg;
};
- void ParseZipFd(const StringPiece& option) {
- ParseUintOption(option, "--zip-fd", &zip_fd_, Usage);
- }
-
- void ParseInputVdexFd(const StringPiece& option) {
- // Note that the input vdex fd might be -1.
- ParseIntOption(option, "--input-vdex-fd", &input_vdex_fd_, Usage);
- }
-
- void ParseOutputVdexFd(const StringPiece& option) {
- ParseUintOption(option, "--output-vdex-fd", &output_vdex_fd_, Usage);
- }
-
- void ParseOatFd(const StringPiece& option) {
- ParseUintOption(option, "--oat-fd", &oat_fd_, Usage);
- }
-
- void ParseFdForCollection(const StringPiece& option,
- const char* arg_name,
- std::vector<uint32_t>* fds) {
- uint32_t fd;
- ParseUintOption(option, arg_name, &fd, Usage);
- fds->push_back(fd);
- }
-
- void ParseJ(const StringPiece& option) {
- ParseUintOption(option, "-j", &thread_count_, Usage, /* is_long_option */ false);
- }
-
- void ParseBase(const StringPiece& option) {
- DCHECK(option.starts_with("--base="));
- const char* image_base_str = option.substr(strlen("--base=")).data();
+ void ParseBase(const std::string& option) {
char* end;
- image_base_ = strtoul(image_base_str, &end, 16);
- if (end == image_base_str || *end != '\0') {
+ image_base_ = strtoul(option.c_str(), &end, 16);
+ if (end == option.c_str() || *end != '\0') {
Usage("Failed to parse hexadecimal value for option %s", option.data());
}
}
- void ParseInstructionSet(const StringPiece& option) {
- DCHECK(option.starts_with("--instruction-set="));
- StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
- // StringPiece is not necessarily zero-terminated, so need to make a copy and ensure it.
- std::unique_ptr<char[]> buf(new char[instruction_set_str.length() + 1]);
- strncpy(buf.get(), instruction_set_str.data(), instruction_set_str.length());
- buf.get()[instruction_set_str.length()] = 0;
- instruction_set_ = GetInstructionSetFromString(buf.get());
- // arm actually means thumb2.
- if (instruction_set_ == InstructionSet::kArm) {
- instruction_set_ = InstructionSet::kThumb2;
- }
- }
-
bool VerifyProfileData() {
return profile_compilation_info_->VerifyProfileData(dex_files_);
}
- void ParseInstructionSetVariant(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--instruction-set-variant="));
- StringPiece str = option.substr(strlen("--instruction-set-variant=")).data();
+ void ParseInstructionSetVariant(const std::string& option, ParserOptions* parser_options) {
instruction_set_features_ = InstructionSetFeatures::FromVariant(
- instruction_set_, str.as_string(), &parser_options->error_msg);
+ instruction_set_, option, &parser_options->error_msg);
if (instruction_set_features_.get() == nullptr) {
Usage("%s", parser_options->error_msg.c_str());
}
}
- void ParseInstructionSetFeatures(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--instruction-set-features="));
- StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
+ void ParseInstructionSetFeatures(const std::string& option, ParserOptions* parser_options) {
if (instruction_set_features_ == nullptr) {
instruction_set_features_ = InstructionSetFeatures::FromVariant(
instruction_set_, "default", &parser_options->error_msg);
@@ -738,38 +701,9 @@ class Dex2Oat FINAL {
}
}
instruction_set_features_ =
- instruction_set_features_->AddFeaturesFromString(str.as_string(),
- &parser_options->error_msg);
+ instruction_set_features_->AddFeaturesFromString(option, &parser_options->error_msg);
if (instruction_set_features_ == nullptr) {
- Usage("Error parsing '%s': %s", option.data(), parser_options->error_msg.c_str());
- }
- }
-
- void ParseCompilerBackend(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--compiler-backend="));
- parser_options->requested_specific_compiler = true;
- StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data();
- if (backend_str == "Quick") {
- compiler_kind_ = Compiler::kQuick;
- } else if (backend_str == "Optimizing") {
- compiler_kind_ = Compiler::kOptimizing;
- } else {
- Usage("Unknown compiler backend: %s", backend_str.data());
- }
- }
-
- void ParseImageFormat(const StringPiece& option) {
- const StringPiece substr("--image-format=");
- DCHECK(option.starts_with(substr));
- const StringPiece format_str = option.substr(substr.length());
- if (format_str == "lz4") {
- image_storage_mode_ = ImageHeader::kStorageModeLZ4;
- } else if (format_str == "lz4hc") {
- image_storage_mode_ = ImageHeader::kStorageModeLZ4HC;
- } else if (format_str == "uncompressed") {
- image_storage_mode_ = ImageHeader::kStorageModeUncompressed;
- } else {
- Usage("Unknown image format: %s", format_str.data());
+ Usage("Error parsing '%s': %s", option.c_str(), parser_options->error_msg.c_str());
}
}
@@ -1092,23 +1026,20 @@ class Dex2Oat FINAL {
base_symbol_oat = base_symbol_oat.substr(0, last_symbol_oat_slash + 1);
}
- const size_t num_expanded_files = 2 + (base_symbol_oat.empty() ? 0 : 1);
- char_backing_storage_.reserve((dex_locations_.size() - 1) * num_expanded_files);
-
// Now create the other names. Use a counted loop to skip the first one.
for (size_t i = 1; i < dex_locations_.size(); ++i) {
// TODO: Make everything properly std::string.
std::string image_name = CreateMultiImageName(dex_locations_[i], prefix, infix, ".art");
- char_backing_storage_.push_back(base_img + image_name);
- image_filenames_.push_back((char_backing_storage_.end() - 1)->c_str());
+ char_backing_storage_.push_front(base_img + image_name);
+ image_filenames_.push_back(char_backing_storage_.front().c_str());
std::string oat_name = CreateMultiImageName(dex_locations_[i], prefix, infix, ".oat");
- char_backing_storage_.push_back(base_oat + oat_name);
- oat_filenames_.push_back((char_backing_storage_.end() - 1)->c_str());
+ char_backing_storage_.push_front(base_oat + oat_name);
+ oat_filenames_.push_back(char_backing_storage_.front().c_str());
if (!base_symbol_oat.empty()) {
- char_backing_storage_.push_back(base_symbol_oat + oat_name);
- oat_unstripped_.push_back((char_backing_storage_.end() - 1)->c_str());
+ char_backing_storage_.push_front(base_symbol_oat + oat_name);
+ oat_unstripped_.push_back(char_backing_storage_.front().c_str());
}
}
}
@@ -1173,6 +1104,43 @@ class Dex2Oat FINAL {
kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
}
+ // This simple forward is here so the string specializations below don't look out of place.
+ template <typename T, typename U>
+ void AssignIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<T>& key,
+ U* out) {
+ map.AssignIfExists(key, out);
+ }
+
+ // Specializations to handle const char* vs std::string.
+ void AssignIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<std::string>& key,
+ const char** out) {
+ if (map.Exists(key)) {
+ char_backing_storage_.push_front(std::move(*map.Get(key)));
+ *out = char_backing_storage_.front().c_str();
+ }
+ }
+ void AssignIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<std::vector<std::string>>& key,
+ std::vector<const char*>* out) {
+ if (map.Exists(key)) {
+ for (auto& val : *map.Get(key)) {
+ char_backing_storage_.push_front(std::move(val));
+ out->push_back(char_backing_storage_.front().c_str());
+ }
+ }
+ }
+
+ template <typename T>
+ void AssignTrueIfExists(Dex2oatArgumentMap& map,
+ const Dex2oatArgumentMap::Key<T>& key,
+ bool* out) {
+ if (map.Exists(key)) {
+ *out = true;
+ }
+ }
+
// Parse the arguments from the command line. In case of an unrecognized option or impossible
// values/combinations, a usage error will be displayed and exit() is called. Thus, if the method
// returns, arguments have been successfully parsed.
@@ -1182,159 +1150,104 @@ class Dex2Oat FINAL {
InitLogging(argv, Runtime::Abort);
- // Skip over argv[0].
- argv++;
- argc--;
+ compiler_options_.reset(new CompilerOptions());
- if (argc == 0) {
- Usage("No arguments specified");
+ using M = Dex2oatArgumentMap;
+ std::string error_msg;
+ std::unique_ptr<M> args_uptr = M::Parse(argc, const_cast<const char**>(argv), &error_msg);
+ if (args_uptr == nullptr) {
+ Usage("Failed to parse command line: %s", error_msg.c_str());
+ UNREACHABLE();
}
+ M& args = *args_uptr;
+
std::unique_ptr<ParserOptions> parser_options(new ParserOptions());
- compiler_options_.reset(new CompilerOptions());
- for (int i = 0; i < argc; i++) {
- const StringPiece option(argv[i]);
- const bool log_options = false;
- if (log_options) {
- LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
- }
- if (option.starts_with("--dex-file=")) {
- dex_filenames_.push_back(option.substr(strlen("--dex-file=")).data());
- } else if (option.starts_with("--dex-location=")) {
- dex_locations_.push_back(option.substr(strlen("--dex-location=")).data());
- } else if (option.starts_with("--zip-fd=")) {
- ParseZipFd(option);
- } else if (option.starts_with("--zip-location=")) {
- zip_location_ = option.substr(strlen("--zip-location=")).data();
- } else if (option.starts_with("--input-vdex-fd=")) {
- ParseInputVdexFd(option);
- } else if (option.starts_with("--input-vdex=")) {
- input_vdex_ = option.substr(strlen("--input-vdex=")).data();
- } else if (option.starts_with("--output-vdex=")) {
- output_vdex_ = option.substr(strlen("--output-vdex=")).data();
- } else if (option.starts_with("--output-vdex-fd=")) {
- ParseOutputVdexFd(option);
- } else if (option.starts_with("--oat-file=")) {
- oat_filenames_.push_back(option.substr(strlen("--oat-file=")).data());
- } else if (option.starts_with("--oat-symbols=")) {
- parser_options->oat_symbols.push_back(option.substr(strlen("--oat-symbols=")).data());
- } else if (option.starts_with("--oat-fd=")) {
- ParseOatFd(option);
- } else if (option.starts_with("--oat-location=")) {
- oat_location_ = option.substr(strlen("--oat-location=")).data();
- } else if (option == "--watch-dog") {
- parser_options->watch_dog_enabled = true;
- } else if (option == "--no-watch-dog") {
- parser_options->watch_dog_enabled = false;
- } else if (option.starts_with("--watchdog-timeout=")) {
- ParseIntOption(option,
- "--watchdog-timeout",
- &parser_options->watch_dog_timeout_in_ms,
- Usage);
- } else if (option.starts_with("-j")) {
- ParseJ(option);
- } else if (option.starts_with("--image=")) {
- image_filenames_.push_back(option.substr(strlen("--image=")).data());
- } else if (option.starts_with("--image-classes=")) {
- image_classes_filename_ = option.substr(strlen("--image-classes=")).data();
- } else if (option.starts_with("--image-classes-zip=")) {
- image_classes_zip_filename_ = option.substr(strlen("--image-classes-zip=")).data();
- } else if (option.starts_with("--image-format=")) {
- ParseImageFormat(option);
- } else if (option.starts_with("--compiled-classes=")) {
- compiled_classes_filename_ = option.substr(strlen("--compiled-classes=")).data();
- } else if (option.starts_with("--compiled-classes-zip=")) {
- compiled_classes_zip_filename_ = option.substr(strlen("--compiled-classes-zip=")).data();
- } else if (option.starts_with("--compiled-methods=")) {
- compiled_methods_filename_ = option.substr(strlen("--compiled-methods=")).data();
- } else if (option.starts_with("--compiled-methods-zip=")) {
- compiled_methods_zip_filename_ = option.substr(strlen("--compiled-methods-zip=")).data();
- } else if (option.starts_with("--run-passes=")) {
- passes_to_run_filename_ = option.substr(strlen("--run-passes=")).data();
- } else if (option.starts_with("--base=")) {
- ParseBase(option);
- } else if (option.starts_with("--boot-image=")) {
- parser_options->boot_image_filename = option.substr(strlen("--boot-image=")).data();
- } else if (option.starts_with("--android-root=")) {
- android_root_ = option.substr(strlen("--android-root=")).data();
- } else if (option.starts_with("--instruction-set=")) {
- ParseInstructionSet(option);
- } else if (option.starts_with("--instruction-set-variant=")) {
- ParseInstructionSetVariant(option, parser_options.get());
- } else if (option.starts_with("--instruction-set-features=")) {
- ParseInstructionSetFeatures(option, parser_options.get());
- } else if (option.starts_with("--compiler-backend=")) {
- ParseCompilerBackend(option, parser_options.get());
- } else if (option.starts_with("--profile-file=")) {
- profile_file_ = option.substr(strlen("--profile-file=")).ToString();
- } else if (option.starts_with("--profile-file-fd=")) {
- ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage);
- } else if (option == "--host") {
- is_host_ = true;
- } else if (option == "--runtime-arg") {
- if (++i >= argc) {
- Usage("Missing required argument for --runtime-arg");
- }
- if (log_options) {
- LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
- }
- runtime_args_.push_back(argv[i]);
- } else if (option == "--dump-timing") {
- dump_timing_ = true;
- } else if (option == "--dump-passes") {
- dump_passes_ = true;
- } else if (option == "--dump-stats") {
- dump_stats_ = true;
- } else if (option == "--avoid-storing-invocation") {
- avoid_storing_invocation_ = true;
- } else if (option.starts_with("--swap-file=")) {
- swap_file_name_ = option.substr(strlen("--swap-file=")).data();
- } else if (option.starts_with("--swap-fd=")) {
- ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
- } else if (option.starts_with("--swap-dex-size-threshold=")) {
- ParseUintOption(option,
- "--swap-dex-size-threshold",
- &min_dex_file_cumulative_size_for_swap_,
- Usage);
- } else if (option.starts_with("--swap-dex-count-threshold=")) {
- ParseUintOption(option,
- "--swap-dex-count-threshold",
- &min_dex_files_for_swap_,
- Usage);
- } else if (option.starts_with("--very-large-app-threshold=")) {
- ParseUintOption(option,
- "--very-large-app-threshold",
- &very_large_threshold_,
- Usage);
- } else if (option.starts_with("--app-image-file=")) {
- app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
- } else if (option.starts_with("--app-image-fd=")) {
- ParseUintOption(option, "--app-image-fd", &app_image_fd_, Usage);
- } else if (option == "--multi-image") {
- multi_image_ = true;
- } else if (option.starts_with("--no-inline-from=")) {
- no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data();
- } else if (option == "--force-determinism") {
- if (!SupportsDeterministicCompilation()) {
- Usage("Option --force-determinism requires read barriers or a CMS/MS garbage collector");
- }
- force_determinism_ = true;
- } else if (option.starts_with("--classpath-dir=")) {
- classpath_dir_ = option.substr(strlen("--classpath-dir=")).data();
- } else if (option.starts_with("--class-loader-context=")) {
- class_loader_context_ = ClassLoaderContext::Create(
- option.substr(strlen("--class-loader-context=")).data());
- if (class_loader_context_ == nullptr) {
- Usage("Option --class-loader-context has an incorrect format: %s", option.data());
- }
- } else if (option.starts_with("--dirty-image-objects=")) {
- dirty_image_objects_filename_ = option.substr(strlen("--dirty-image-objects=")).data();
- } else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
- Usage("Unknown argument %s", option.data());
+ AssignIfExists(args, M::DexFiles, &dex_filenames_);
+ AssignIfExists(args, M::DexLocations, &dex_locations_);
+ AssignIfExists(args, M::OatFiles, &oat_filenames_);
+ AssignIfExists(args, M::OatSymbols, &parser_options->oat_symbols);
+ AssignIfExists(args, M::ImageFilenames, &image_filenames_);
+ AssignIfExists(args, M::ZipFd, &zip_fd_);
+ AssignIfExists(args, M::ZipLocation, &zip_location_);
+ AssignIfExists(args, M::InputVdexFd, &input_vdex_fd_);
+ AssignIfExists(args, M::OutputVdexFd, &output_vdex_fd_);
+ AssignIfExists(args, M::InputVdex, &input_vdex_);
+ AssignIfExists(args, M::OutputVdex, &output_vdex_);
+ AssignIfExists(args, M::OatFd, &oat_fd_);
+ AssignIfExists(args, M::OatLocation, &oat_location_);
+ AssignIfExists(args, M::Watchdog, &parser_options->watch_dog_enabled);
+ AssignIfExists(args, M::WatchdogTimeout, &parser_options->watch_dog_timeout_in_ms);
+ AssignIfExists(args, M::Threads, &thread_count_);
+ AssignIfExists(args, M::ImageClasses, &image_classes_filename_);
+ AssignIfExists(args, M::ImageClassesZip, &image_classes_zip_filename_);
+ AssignIfExists(args, M::CompiledClasses, &compiled_classes_filename_);
+ AssignIfExists(args, M::CompiledClassesZip, &compiled_classes_zip_filename_);
+ AssignIfExists(args, M::CompiledMethods, &compiled_methods_filename_);
+ AssignIfExists(args, M::CompiledMethodsZip, &compiled_methods_zip_filename_);
+ AssignIfExists(args, M::Passes, &passes_to_run_filename_);
+ AssignIfExists(args, M::BootImage, &parser_options->boot_image_filename);
+ AssignIfExists(args, M::AndroidRoot, &android_root_);
+ AssignIfExists(args, M::Profile, &profile_file_);
+ AssignIfExists(args, M::ProfileFd, &profile_file_fd_);
+ AssignIfExists(args, M::RuntimeOptions, &runtime_args_);
+ AssignIfExists(args, M::SwapFile, &swap_file_name_);
+ AssignIfExists(args, M::SwapFileFd, &swap_fd_);
+ AssignIfExists(args, M::SwapDexSizeThreshold, &min_dex_file_cumulative_size_for_swap_);
+ AssignIfExists(args, M::SwapDexCountThreshold, &min_dex_files_for_swap_);
+ AssignIfExists(args, M::VeryLargeAppThreshold, &very_large_threshold_);
+ AssignIfExists(args, M::AppImageFile, &app_image_file_name_);
+ AssignIfExists(args, M::AppImageFileFd, &app_image_fd_);
+ AssignIfExists(args, M::NoInlineFrom, &no_inline_from_string_);
+ AssignIfExists(args, M::ClasspathDir, &classpath_dir_);
+ AssignIfExists(args, M::DirtyImageObjects, &dirty_image_objects_filename_);
+ AssignIfExists(args, M::ImageFormat, &image_storage_mode_);
+
+ AssignIfExists(args, M::Backend, &compiler_kind_);
+ parser_options->requested_specific_compiler = args.Exists(M::Backend);
+
+ AssignIfExists(args, M::TargetInstructionSet, &instruction_set_);
+ // arm actually means thumb2.
+ if (instruction_set_ == InstructionSet::kArm) {
+ instruction_set_ = InstructionSet::kThumb2;
+ }
+
+ AssignTrueIfExists(args, M::Host, &is_host_);
+ AssignTrueIfExists(args, M::DumpTiming, &dump_timing_);
+ AssignTrueIfExists(args, M::DumpPasses, &dump_passes_);
+ AssignTrueIfExists(args, M::DumpStats, &dump_stats_);
+ AssignTrueIfExists(args, M::AvoidStoringInvocation, &avoid_storing_invocation_);
+ AssignTrueIfExists(args, M::MultiImage, &multi_image_);
+
+ if (args.Exists(M::ForceDeterminism)) {
+ if (!SupportsDeterministicCompilation()) {
+ Usage("Option --force-determinism requires read barriers or a CMS/MS garbage collector");
+ }
+ force_determinism_ = true;
+ }
+
+ if (args.Exists(M::Base)) {
+ ParseBase(*args.Get(M::Base));
+ }
+ if (args.Exists(M::TargetInstructionSetVariant)) {
+ ParseInstructionSetVariant(*args.Get(M::TargetInstructionSetVariant), parser_options.get());
+ }
+ if (args.Exists(M::TargetInstructionSetFeatures)) {
+ ParseInstructionSetFeatures(*args.Get(M::TargetInstructionSetFeatures), parser_options.get());
+ }
+ if (args.Exists(M::ClassLoaderContext)) {
+ class_loader_context_ = ClassLoaderContext::Create(*args.Get(M::ClassLoaderContext));
+ if (class_loader_context_ == nullptr) {
+ Usage("Option --class-loader-context has an incorrect format: %s",
+ args.Get(M::ClassLoaderContext)->c_str());
}
}
+ if (!ReadCompilerOptions(args, compiler_options_.get(), &error_msg)) {
+ Usage(error_msg.c_str());
+ }
+
ProcessOptions(parser_options.get());
// Insert some compiler things.
@@ -2931,7 +2844,7 @@ class Dex2Oat FINAL {
std::unordered_map<const DexFile*, size_t> dex_file_oat_index_map_;
// Backing storage.
- std::vector<std::string> char_backing_storage_;
+ std::forward_list<std::string> char_backing_storage_;
// See CompilerOptions.force_determinism_.
bool force_determinism_;
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 1f644c15dc..ae7ebe2da1 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -28,6 +28,7 @@
#include "base/macros.h"
#include "base/unix_file/fd_file.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "jit/profile_compilation_info.h"
#include "method_reference.h"
#include "runtime.h"
@@ -62,7 +63,11 @@ class Dex2oatImageTest : public CommonRuntimeTest {
for (const std::string& dex : GetLibCoreDexFileNames()) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
- CHECK(DexFile::Open(dex.c_str(), dex, /*verify_checksum*/ false, &error_msg, &dex_files))
+ CHECK(DexFileLoader::Open(dex.c_str(),
+ dex,
+ /*verify_checksum*/ false,
+ &error_msg,
+ &dex_files))
<< error_msg;
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
@@ -328,8 +333,8 @@ TEST_F(Dex2oatImageTest, TestModesAndFilters) {
profile_file.Close();
std::cout << "Profile sizes " << profile_sizes << std::endl;
// Since there is some difference between profile vs image + methods due to layout, check that
- // the range is within expected margins (+-5%).
- const double kRatio = 0.95;
+ // the range is within expected margins (+-10%).
+ const double kRatio = 0.90;
EXPECT_LE(profile_sizes.art_size * kRatio, compiled_methods_sizes.art_size);
// TODO(mathieuc): Find a reliable way to check compiled code. b/63746626
// EXPECT_LE(profile_sizes.oat_size * kRatio, compiled_methods_sizes.oat_size);
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
new file mode 100644
index 0000000000..43e6c4d02f
--- /dev/null
+++ b/dex2oat/dex2oat_options.cc
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "dex2oat_options.h"
+
+#include <memory>
+
+#include "cmdline_parser.h"
+#include "driver/compiler_options_map-inl.h"
+
+namespace art {
+
+template<>
+struct CmdlineType<InstructionSet> : CmdlineTypeParser<InstructionSet> {
+ Result Parse(const std::string& option) {
+ InstructionSet set = GetInstructionSetFromString(option.c_str());
+ if (set == kNone) {
+ return Result::Failure(std::string("Not a valid instruction set: '") + option + "'");
+ }
+ return Result::Success(set);
+ }
+
+ static const char* Name() { return "InstructionSet"; }
+};
+
+#define COMPILER_OPTIONS_MAP_TYPE Dex2oatArgumentMap
+#define COMPILER_OPTIONS_MAP_KEY_TYPE Dex2oatArgumentMapKey
+#include "driver/compiler_options_map-storage.h"
+
+// Specify storage for the Dex2oatOptions keys.
+
+#define DEX2OAT_OPTIONS_KEY(Type, Name, ...) \
+ const Dex2oatArgumentMap::Key<Type> Dex2oatArgumentMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4]
+#include "dex2oat_options.def"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+using M = Dex2oatArgumentMap;
+using Parser = CmdlineParser<Dex2oatArgumentMap, Dex2oatArgumentMap::Key>;
+using Builder = Parser::Builder;
+
+static void AddInputMappings(Builder& builder) {
+ builder.
+ Define("--dex-file=_")
+ .WithType<std::vector<std::string>>().AppendValues()
+ .IntoKey(M::DexFiles)
+ .Define("--dex-location=_")
+ .WithType<std::vector<std::string>>().AppendValues()
+ .IntoKey(M::DexLocations)
+ .Define("--zip-fd=_")
+ .WithType<int>()
+ .IntoKey(M::ZipFd)
+ .Define("--zip-location=_")
+ .WithType<std::string>()
+ .IntoKey(M::ZipLocation)
+ .Define("--boot-image=_")
+ .WithType<std::string>()
+ .IntoKey(M::BootImage);
+}
+
+static void AddGeneratedArtifactMappings(Builder& builder) {
+ builder.
+ Define("--input-vdex-fd=_")
+ .WithType<int>()
+ .IntoKey(M::InputVdexFd)
+ .Define("--input-vdex=_")
+ .WithType<std::string>()
+ .IntoKey(M::InputVdex)
+ .Define("--output-vdex-fd=_")
+ .WithType<int>()
+ .IntoKey(M::OutputVdexFd)
+ .Define("--output-vdex=_")
+ .WithType<std::string>()
+ .IntoKey(M::OutputVdex)
+ .Define("--oat-file=_")
+ .WithType<std::vector<std::string>>().AppendValues()
+ .IntoKey(M::OatFiles)
+ .Define("--oat-symbols=_")
+ .WithType<std::vector<std::string>>().AppendValues()
+ .IntoKey(M::OatSymbols)
+ .Define("--oat-fd=_")
+ .WithType<int>()
+ .IntoKey(M::OatFd)
+ .Define("--oat-location=_")
+ .WithType<std::string>()
+ .IntoKey(M::OatLocation);
+}
+
+static void AddImageMappings(Builder& builder) {
+ builder.
+ Define("--image=_")
+ .WithType<std::vector<std::string>>().AppendValues()
+ .IntoKey(M::ImageFilenames)
+ .Define("--image-classes=_")
+ .WithType<std::string>()
+ .IntoKey(M::ImageClasses)
+ .Define("--image-classes-zip=_")
+ .WithType<std::string>()
+ .IntoKey(M::ImageClassesZip)
+ .Define("--base=_")
+ .WithType<std::string>()
+ .IntoKey(M::Base)
+ .Define("--app-image-file=_")
+ .WithType<std::string>()
+ .IntoKey(M::AppImageFile)
+ .Define("--app-image-fd=_")
+ .WithType<int>()
+ .IntoKey(M::AppImageFileFd)
+ .Define("--multi-image")
+ .IntoKey(M::MultiImage)
+ .Define("--dirty-image-objects=_")
+ .WithType<std::string>()
+ .IntoKey(M::DirtyImageObjects)
+ .Define("--image-format=_")
+ .WithType<ImageHeader::StorageMode>()
+ .WithValueMap({{"lz4", ImageHeader::kStorageModeLZ4},
+ {"lz4hc", ImageHeader::kStorageModeLZ4HC},
+ {"uncompressed", ImageHeader::kStorageModeUncompressed}})
+ .IntoKey(M::ImageFormat);
+}
+
+static void AddSwapMappings(Builder& builder) {
+ builder.
+ Define("--swap-file=_")
+ .WithType<std::string>()
+ .IntoKey(M::SwapFile)
+ .Define("--swap-fd=_")
+ .WithType<int>()
+ .IntoKey(M::SwapFileFd)
+ .Define("--swap-dex-size-threshold=_")
+ .WithType<unsigned int>()
+ .IntoKey(M::SwapDexSizeThreshold)
+ .Define("--swap-dex-count-threshold=_")
+ .WithType<unsigned int>()
+ .IntoKey(M::SwapDexCountThreshold);
+}
+
+static void AddCompilerMappings(Builder& builder) {
+ builder.
+ Define("--compiled-classes=_")
+ .WithType<std::string>()
+ .IntoKey(M::CompiledClasses)
+ .Define("--compiled-classes-zip=_")
+ .WithType<std::string>()
+ .IntoKey(M::CompiledClassesZip)
+ .Define("--compiled-methods=_")
+ .WithType<std::string>()
+ .IntoKey(M::CompiledMethods)
+ .Define("--compiled-methods-zip=_")
+ .WithType<std::string>()
+ .IntoKey(M::CompiledMethodsZip)
+ .Define("--run-passes=_")
+ .WithType<std::string>()
+ .IntoKey(M::Passes)
+ .Define("--profile-file=_")
+ .WithType<std::string>()
+ .IntoKey(M::Profile)
+ .Define("--profile-file-fd=_")
+ .WithType<int>()
+ .IntoKey(M::ProfileFd)
+ .Define("--no-inline-from=_")
+ .WithType<std::string>()
+ .IntoKey(M::NoInlineFrom);
+}
+
+static void AddTargetMappings(Builder& builder) {
+ builder.
+ Define("--instruction-set=_")
+ .WithType<InstructionSet>()
+ .IntoKey(M::TargetInstructionSet)
+ .Define("--instruction-set-variant=_")
+ .WithType<std::string>()
+ .IntoKey(M::TargetInstructionSetVariant)
+ .Define("--instruction-set-features=_")
+ .WithType<std::string>()
+ .IntoKey(M::TargetInstructionSetFeatures);
+}
+
+static Parser CreateArgumentParser() {
+ std::unique_ptr<Builder> parser_builder = std::unique_ptr<Builder>(new Builder());
+
+ AddInputMappings(*parser_builder);
+ AddGeneratedArtifactMappings(*parser_builder);
+ AddImageMappings(*parser_builder);
+ AddSwapMappings(*parser_builder);
+ AddCompilerMappings(*parser_builder);
+ AddTargetMappings(*parser_builder);
+
+ parser_builder->
+ Define({"--watch-dog", "--no-watch-dog"})
+ .WithValues({true, false})
+ .IntoKey(M::Watchdog)
+ .Define("--watchdog-timeout=_")
+ .WithType<int>()
+ .IntoKey(M::WatchdogTimeout)
+ .Define("-j_")
+ .WithType<unsigned int>()
+ .IntoKey(M::Threads)
+ .Define("--android-root=_")
+ .WithType<std::string>()
+ .IntoKey(M::AndroidRoot)
+ .Define("--compiler-backend=_")
+ .WithType<Compiler::Kind>()
+ .WithValueMap({{"Quick", Compiler::Kind::kQuick},
+ {"Optimizing", Compiler::Kind::kOptimizing}})
+ .IntoKey(M::Backend)
+ .Define("--host")
+ .IntoKey(M::Host)
+ .Define("--dump-timing")
+ .IntoKey(M::DumpTiming)
+ .Define("--dump-passes")
+ .IntoKey(M::DumpPasses)
+ .Define("--dump-stats")
+ .IntoKey(M::DumpStats)
+ .Define("--avoid-storing-invocation")
+ .IntoKey(M::AvoidStoringInvocation)
+ .Define("--very-large-app-threshold=_")
+ .WithType<unsigned int>()
+ .IntoKey(M::VeryLargeAppThreshold)
+ .Define("--force-determinism")
+ .IntoKey(M::ForceDeterminism)
+ .Define("--classpath-dir=_")
+ .WithType<std::string>()
+ .IntoKey(M::ClasspathDir)
+ .Define("--class-loader-context=_")
+ .WithType<std::string>()
+ .IntoKey(M::ClassLoaderContext)
+ .Define("--runtime-arg _")
+ .WithType<std::vector<std::string>>().AppendValues()
+ .IntoKey(M::RuntimeOptions);
+
+ AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder);
+
+ parser_builder->IgnoreUnrecognized(false);
+
+ return parser_builder->Build();
+}
+
+#pragma GCC diagnostic pop
+
+std::unique_ptr<Dex2oatArgumentMap> Dex2oatArgumentMap::Parse(int argc,
+ const char** argv,
+ std::string* error_msg) {
+ Parser parser = CreateArgumentParser();
+ CmdlineResult parse_result = parser.Parse(argv, argc);
+ if (!parse_result.IsSuccess()) {
+ *error_msg = parse_result.GetMessage();
+ return nullptr;
+ }
+
+ return std::unique_ptr<Dex2oatArgumentMap>(new Dex2oatArgumentMap(parser.ReleaseArgumentsMap()));
+}
+
+} // namespace art
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
new file mode 100644
index 0000000000..83a3035ed5
--- /dev/null
+++ b/dex2oat/dex2oat_options.def
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef DEX2OAT_OPTIONS_KEY
+#error "Please #define DEX2OAT_OPTIONS_KEY before #including this file"
+#define DEX2OAT_OPTIONS_KEY(...) // Don't display errors in this file in IDEs.
+#endif
+
+// This file defines the list of keys for Dex2oatOptions.
+// These can be used with Dex2oatOptions.Get/Set/etc, for example:
+// Dex2oatOptions opt; bool* dex2oat_enabled = opt.Get(Dex2oatOptions::Dex2Oat);
+//
+// Column Descriptions:
+// <<Type>> <<Key Name>> <<Default Value>>
+//
+// Default values are only used by Map::GetOrDefault(K<T>).
+// If a default value is omitted here, T{} is used as the default value, which is
+// almost-always the value of the type as if it was memset to all 0.
+//
+// Please keep the columns aligned if possible when adding new rows.
+//
+
+// Parse-able keys from the command line.
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>, DexFiles)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>, DexLocations)
+DEX2OAT_OPTIONS_KEY (int, ZipFd)
+DEX2OAT_OPTIONS_KEY (std::string, ZipLocation)
+DEX2OAT_OPTIONS_KEY (int, InputVdexFd)
+DEX2OAT_OPTIONS_KEY (std::string, InputVdex)
+DEX2OAT_OPTIONS_KEY (int, OutputVdexFd)
+DEX2OAT_OPTIONS_KEY (std::string, OutputVdex)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>, OatFiles)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>, OatSymbols)
+DEX2OAT_OPTIONS_KEY (int, OatFd)
+DEX2OAT_OPTIONS_KEY (std::string, OatLocation)
+DEX2OAT_OPTIONS_KEY (bool, Watchdog)
+DEX2OAT_OPTIONS_KEY (int, WatchdogTimeout)
+DEX2OAT_OPTIONS_KEY (unsigned int, Threads)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>, ImageFilenames)
+DEX2OAT_OPTIONS_KEY (std::string, ImageClasses)
+DEX2OAT_OPTIONS_KEY (std::string, ImageClassesZip)
+DEX2OAT_OPTIONS_KEY (ImageHeader::StorageMode, ImageFormat)
+DEX2OAT_OPTIONS_KEY (std::string, CompiledClasses)
+DEX2OAT_OPTIONS_KEY (std::string, CompiledClassesZip)
+DEX2OAT_OPTIONS_KEY (std::string, CompiledMethods)
+DEX2OAT_OPTIONS_KEY (std::string, CompiledMethodsZip)
+DEX2OAT_OPTIONS_KEY (std::string, Passes)
+DEX2OAT_OPTIONS_KEY (std::string, Base) // TODO: Hex string parsing.
+DEX2OAT_OPTIONS_KEY (std::string, BootImage)
+DEX2OAT_OPTIONS_KEY (std::string, AndroidRoot)
+DEX2OAT_OPTIONS_KEY (InstructionSet, TargetInstructionSet)
+DEX2OAT_OPTIONS_KEY (std::string, TargetInstructionSetVariant)
+DEX2OAT_OPTIONS_KEY (std::string, TargetInstructionSetFeatures)
+DEX2OAT_OPTIONS_KEY (Compiler::Kind, Backend)
+DEX2OAT_OPTIONS_KEY (std::string, Profile)
+DEX2OAT_OPTIONS_KEY (int, ProfileFd)
+DEX2OAT_OPTIONS_KEY (Unit, Host)
+DEX2OAT_OPTIONS_KEY (Unit, DumpTiming)
+DEX2OAT_OPTIONS_KEY (Unit, DumpPasses)
+DEX2OAT_OPTIONS_KEY (Unit, DumpStats)
+DEX2OAT_OPTIONS_KEY (Unit, AvoidStoringInvocation)
+DEX2OAT_OPTIONS_KEY (std::string, SwapFile)
+DEX2OAT_OPTIONS_KEY (int, SwapFileFd)
+DEX2OAT_OPTIONS_KEY (unsigned int, SwapDexSizeThreshold)
+DEX2OAT_OPTIONS_KEY (unsigned int, SwapDexCountThreshold)
+DEX2OAT_OPTIONS_KEY (unsigned int, VeryLargeAppThreshold)
+DEX2OAT_OPTIONS_KEY (std::string, AppImageFile)
+DEX2OAT_OPTIONS_KEY (int, AppImageFileFd)
+DEX2OAT_OPTIONS_KEY (Unit, MultiImage)
+DEX2OAT_OPTIONS_KEY (std::string, NoInlineFrom)
+DEX2OAT_OPTIONS_KEY (Unit, ForceDeterminism)
+DEX2OAT_OPTIONS_KEY (std::string, ClasspathDir)
+DEX2OAT_OPTIONS_KEY (std::string, ClassLoaderContext)
+DEX2OAT_OPTIONS_KEY (std::string, DirtyImageObjects)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>, RuntimeOptions)
+
+#undef DEX2OAT_OPTIONS_KEY
diff --git a/dex2oat/dex2oat_options.h b/dex2oat/dex2oat_options.h
new file mode 100644
index 0000000000..a4c718625f
--- /dev/null
+++ b/dex2oat/dex2oat_options.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_DEX2OAT_DEX2OAT_OPTIONS_H_
+#define ART_DEX2OAT_DEX2OAT_OPTIONS_H_
+
+#include <cstdio>
+#include <string>
+#include <vector>
+
+#include "base/variant_map.h"
+#include "cmdline_types.h" // TODO: don't need to include this file here
+#include "compiler.h"
+#include "driver/compiler_options_map.h"
+#include "image.h"
+
+namespace art {
+
+template <typename TVariantMap,
+ template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser;
+
+// Define a key that is usable with a Dex2oatArgumentMap.
+// This key will *not* work with other subtypes of VariantMap.
+template <typename TValue>
+struct Dex2oatArgumentMapKey : VariantMapKey<TValue> {
+ Dex2oatArgumentMapKey() {}
+ explicit Dex2oatArgumentMapKey(TValue default_value)
+ : VariantMapKey<TValue>(std::move(default_value)) {}
+ // Don't ODR-use constexpr default values, which means that Struct::Fields
+ // that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
+};
+
+// Defines a type-safe heterogeneous key->value map.
+// Use the VariantMap interface to look up or to store a Dex2oatArgumentMapKey,Value pair.
+//
+// Example:
+// auto map = Dex2oatArgumentMap();
+// map.Set(Dex2oatArgumentMap::ZipFd, -1);
+// int *target_utilization = map.Get(Dex2oatArgumentMap::ZipFd);
+//
+struct Dex2oatArgumentMap : CompilerOptionsMap<Dex2oatArgumentMap, Dex2oatArgumentMapKey> {
+ // This 'using' line is necessary to inherit the variadic constructor.
+ using CompilerOptionsMap<Dex2oatArgumentMap, Dex2oatArgumentMapKey>::CompilerOptionsMap;
+
+ static std::unique_ptr<Dex2oatArgumentMap> Parse(int argc,
+ const char** argv,
+ std::string* error_msg);
+
+ // Make the next many usages of Key slightly shorter to type.
+ template <typename TValue>
+ using Key = Dex2oatArgumentMapKey<TValue>;
+
+ // List of key declarations, shorthand for 'static const Key<T> Name'
+#define DEX2OAT_OPTIONS_KEY(Type, Name, ...) static const Key<Type> (Name);
+#include "dex2oat_options.def"
+};
+
+extern template struct CompilerOptionsMap<Dex2oatArgumentMap, Dex2oatArgumentMapKey>;
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DEX2OAT_OPTIONS_H_
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 5bf35139cb..1b731fc7f6 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -33,6 +33,7 @@
#include "dex2oat_environment_test.h"
#include "dex2oat_return_codes.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "jit/profile_compilation_info.h"
#include "oat.h"
#include "oat_file.h"
@@ -677,7 +678,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
const char* location = dex_location.c_str();
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- ASSERT_TRUE(DexFile::Open(location, location, true, &error_msg, &dex_files));
+ ASSERT_TRUE(DexFileLoader::Open(location, location, true, &error_msg, &dex_files));
EXPECT_EQ(dex_files.size(), 1U);
std::unique_ptr<const DexFile>& dex_file = dex_files[0];
GenerateProfile(profile_location,
@@ -811,7 +812,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
const char* location = dex_location.c_str();
std::vector<std::unique_ptr<const DexFile>> dex_files;
- ASSERT_TRUE(DexFile::Open(location, location, true, &error_msg, &dex_files));
+ ASSERT_TRUE(DexFileLoader::Open(location, location, true, &error_msg, &dex_files));
EXPECT_EQ(dex_files.size(), 1U);
std::unique_ptr<const DexFile>& old_dex_file = dex_files[0];
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index dfbe31a548..05af442a3a 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -34,6 +34,7 @@
#include "debug/method_debug_info.h"
#include "dex/verification_results.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "dex_file_types.h"
#include "dexlayout.h"
#include "driver/compiler_driver-inl.h"
@@ -52,6 +53,7 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
+#include "native_dex_file.h"
#include "oat_quick_method_header.h"
#include "os.h"
#include "safe_map.h"
@@ -415,7 +417,7 @@ bool OatWriter::AddDexFileSource(const char* filename,
if (fd.Fd() == -1) {
PLOG(ERROR) << "Failed to read magic number from dex file: '" << filename << "'";
return false;
- } else if (IsDexMagic(magic)) {
+ } else if (DexFileLoader::IsValidMagic(magic)) {
// The file is open for reading, not writing, so it's OK to let the File destructor
// close it without checking for explicit Close(), so pass checkUsage = false.
raw_dex_files_.emplace_back(new File(fd.Release(), location, /* checkUsage */ false));
@@ -447,13 +449,13 @@ bool OatWriter::AddZippedDexFilesSource(File&& zip_fd,
return false;
}
for (size_t i = 0; ; ++i) {
- std::string entry_name = DexFile::GetMultiDexClassesDexName(i);
+ std::string entry_name = DexFileLoader::GetMultiDexClassesDexName(i);
std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg));
if (entry == nullptr) {
break;
}
zipped_dex_files_.push_back(std::move(entry));
- zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location));
+ zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location));
const char* full_location = zipped_dex_file_locations_.back().c_str();
oat_dex_files_.emplace_back(full_location,
DexFileSource(zipped_dex_files_.back().get()),
@@ -478,12 +480,13 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file,
LOG(ERROR) << "Unexpected number of dex files in vdex " << location;
return false;
}
- if (!DexFile::IsMagicValid(current_dex_data)) {
+
+ if (!DexFileLoader::IsValidMagic(current_dex_data)) {
LOG(ERROR) << "Invalid magic in vdex file created from " << location;
return false;
}
// We used `zipped_dex_file_locations_` to keep the strings in memory.
- zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location));
+ zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location));
const char* full_location = zipped_dex_file_locations_.back().c_str();
oat_dex_files_.emplace_back(full_location,
DexFileSource(current_dex_data),
@@ -3107,11 +3110,12 @@ bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) {
}
bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* location) {
- if (!DexFile::IsMagicValid(raw_header)) {
+ const bool valid_native_dex_magic = NativeDexFile::IsMagicValid(raw_header);
+ if (!valid_native_dex_magic) {
LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location;
return false;
}
- if (!DexFile::IsVersionValid(raw_header)) {
+ if (!NativeDexFile::IsVersionValid(raw_header)) {
LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location;
return false;
}
@@ -3242,12 +3246,12 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil
LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg;
return false;
}
- dex_file = DexFile::Open(location,
- zip_entry->GetCrc32(),
- std::move(mem_map),
- /* verify */ true,
- /* verify_checksum */ true,
- &error_msg);
+ dex_file = DexFileLoader::Open(location,
+ zip_entry->GetCrc32(),
+ std::move(mem_map),
+ /* verify */ true,
+ /* verify_checksum */ true,
+ &error_msg);
} else if (oat_dex_file->source_.IsRawFile()) {
File* raw_file = oat_dex_file->source_.GetRawFile();
int dup_fd = dup(raw_file->Fd());
@@ -3255,7 +3259,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil
PLOG(ERROR) << "Failed to dup dex file descriptor (" << raw_file->Fd() << ") at " << location;
return false;
}
- dex_file = DexFile::OpenDex(dup_fd, location, /* verify_checksum */ true, &error_msg);
+ dex_file = DexFileLoader::OpenDex(dup_fd, location, /* verify_checksum */ true, &error_msg);
} else {
// The source data is a vdex file.
CHECK(oat_dex_file->source_.IsRawData())
@@ -3267,14 +3271,14 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil
DCHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file->GetLocation()));
const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file);
// Since the source may have had its layout changed, or may be quickened, don't verify it.
- dex_file = DexFile::Open(raw_dex_file,
- header->file_size_,
- location,
- oat_dex_file->dex_file_location_checksum_,
- nullptr,
- /* verify */ false,
- /* verify_checksum */ false,
- &error_msg);
+ dex_file = DexFileLoader::Open(raw_dex_file,
+ header->file_size_,
+ location,
+ oat_dex_file->dex_file_location_checksum_,
+ nullptr,
+ /* verify */ false,
+ /* verify_checksum */ false,
+ &error_msg);
}
if (dex_file == nullptr) {
LOG(ERROR) << "Failed to open dex file for layout: " << error_msg;
@@ -3532,14 +3536,14 @@ bool OatWriter::OpenDexFiles(
}
// Now, open the dex file.
- dex_files.emplace_back(DexFile::Open(raw_dex_file,
- oat_dex_file.dex_file_size_,
- oat_dex_file.GetLocation(),
- oat_dex_file.dex_file_location_checksum_,
- /* oat_dex_file */ nullptr,
- verify,
- verify,
- &error_msg));
+ dex_files.emplace_back(DexFileLoader::Open(raw_dex_file,
+ oat_dex_file.dex_file_size_,
+ oat_dex_file.GetLocation(),
+ oat_dex_file.dex_file_location_checksum_,
+ /* oat_dex_file */ nullptr,
+ verify,
+ verify,
+ &error_msg));
if (dex_files.back() == nullptr) {
LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation()
<< " Error: " << error_msg;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index d89d9f07b2..a19057a0ed 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -26,6 +26,7 @@
#include "compiled_method-inl.h"
#include "compiler.h"
#include "debug/method_debug_info.h"
+#include "dex_file_loader.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/verification_results.h"
#include "driver/compiler_driver.h"
@@ -48,16 +49,6 @@
namespace art {
namespace linker {
-NO_RETURN static void Usage(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- std::string error;
- android::base::StringAppendV(&error, fmt, ap);
- LOG(FATAL) << error;
- va_end(ap);
- UNREACHABLE();
-}
-
class OatTest : public CommonCompilerTest {
protected:
static const bool kCompile = false; // DISABLED_ due to the time to compile libcore
@@ -101,8 +92,11 @@ class OatTest : public CommonCompilerTest {
insn_features_ = InstructionSetFeatures::FromVariant(insn_set, "default", error_msg);
ASSERT_TRUE(insn_features_ != nullptr) << *error_msg;
compiler_options_.reset(new CompilerOptions);
- for (const std::string& option : compiler_options) {
- compiler_options_->ParseCompilerOption(option, Usage);
+ if (!compiler_options_->ParseCompilerOptions(compiler_options,
+ false /* ignore_unrecognized */,
+ error_msg)) {
+ LOG(FATAL) << *error_msg;
+ UNREACHABLE();
}
verification_results_.reset(new VerificationResults(compiler_options_.get()));
callbacks_.reset(new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp));
@@ -752,14 +746,14 @@ void OatTest::TestZipFileInput(bool verify) {
ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
&opened_dex_file1->GetHeader(),
dex_file1_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+ ASSERT_EQ(DexFileLoader::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
opened_dex_file1->GetLocation());
ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
&opened_dex_file2->GetHeader(),
dex_file2_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+ ASSERT_EQ(DexFileLoader::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
opened_dex_file2->GetLocation());
}
}
@@ -801,14 +795,14 @@ void OatTest::TestZipFileInput(bool verify) {
ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
&opened_dex_file1->GetHeader(),
dex_file1_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+ ASSERT_EQ(DexFileLoader::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
opened_dex_file1->GetLocation());
ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
&opened_dex_file2->GetHeader(),
dex_file2_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+ ASSERT_EQ(DexFileLoader::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
opened_dex_file2->GetLocation());
}
}
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 7599d230d2..3648a3edd0 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -45,6 +45,7 @@
#include "android-base/stringprintf.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "dex_file_types.h"
#include "dex_instruction-inl.h"
#include "dexdump_cfg.h"
@@ -1825,7 +1826,7 @@ static void processDexFile(const char* fileName,
fputs("Opened '", gOutFile);
fputs(fileName, gOutFile);
if (n > 1) {
- fprintf(gOutFile, ":%s", DexFile::GetMultiDexClassesDexName(i).c_str());
+ fprintf(gOutFile, ":%s", DexFileLoader::GetMultiDexClassesDexName(i).c_str());
}
fprintf(gOutFile, "', DEX version '%.3s'\n", pDexFile->GetHeader().magic_ + 4);
}
@@ -1882,7 +1883,7 @@ int processFile(const char* fileName) {
const bool kVerifyChecksum = !gOptions.ignoreBadChecksum;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) {
+ if (!DexFileLoader::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) {
// Display returned error message to user. Note that this error behavior
// differs from the error messages shown by the original Dalvik dexdump.
fputs(error_msg.c_str(), stderr);
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 095c960bc0..ade00723fd 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -35,6 +35,7 @@
#include "dex_file-inl.h"
#include "dex_file_layout.h"
+#include "dex_file_loader.h"
#include "dex_file_types.h"
#include "dex_file_verifier.h"
#include "dex_instruction-inl.h"
@@ -1929,14 +1930,14 @@ void DexLayout::OutputDexFile(const DexFile* dex_file) {
// Verify the output dex file's structure for debug builds.
if (kIsDebugBuild) {
std::string location = "memory mapped file for " + dex_file_location;
- std::unique_ptr<const DexFile> output_dex_file(DexFile::Open(mem_map_->Begin(),
- mem_map_->Size(),
- location,
- header_->Checksum(),
- /*oat_dex_file*/ nullptr,
- /*verify*/ true,
- /*verify_checksum*/ false,
- &error_msg));
+ std::unique_ptr<const DexFile> output_dex_file(DexFileLoader::Open(mem_map_->Begin(),
+ mem_map_->Size(),
+ location,
+ header_->Checksum(),
+ /*oat_dex_file*/ nullptr,
+ /*verify*/ true,
+ /*verify_checksum*/ false,
+ &error_msg));
DCHECK(output_dex_file != nullptr) << "Failed to re-open output file:" << error_msg;
}
// Do IR-level comparison between input and output. This check ignores potential differences
@@ -1998,7 +1999,7 @@ int DexLayout::ProcessFile(const char* file_name) {
const bool verify_checksum = !options_.ignore_bad_checksum_;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- if (!DexFile::Open(file_name, file_name, verify_checksum, &error_msg, &dex_files)) {
+ if (!DexFileLoader::Open(file_name, file_name, verify_checksum, &error_msg, &dex_files)) {
// Display returned error message to user. Note that this error behavior
// differs from the error messages shown by the original Dalvik dexdump.
fputs(error_msg.c_str(), stderr);
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 336eb5fbcc..f8fa893069 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -24,6 +24,7 @@
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "exec_utils.h"
#include "jit/profile_compilation_info.h"
#include "utils.h"
@@ -322,11 +323,11 @@ class DexLayoutTest : public CommonRuntimeTest {
const std::string& dex_location) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
- bool result = DexFile::Open(input_dex.c_str(),
- input_dex,
- false,
- &error_msg,
- &dex_files);
+ bool result = DexFileLoader::Open(input_dex.c_str(),
+ input_dex,
+ false,
+ &error_msg,
+ &dex_files);
ASSERT_TRUE(result) << error_msg;
ASSERT_GE(dex_files.size(), 1u);
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index 6a1e22a525..e5870522a3 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "mem_map.h"
#include "runtime.h"
@@ -178,7 +179,7 @@ static int processFile(const char* fileName) {
static constexpr bool kVerifyChecksum = true;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) {
+ if (!DexFileLoader::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) {
fputs(error_msg.c_str(), stderr);
fputc('\n', stderr);
return -1;
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 51a67ca45e..08d38d5925 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -97,6 +97,10 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --android-data=<directory>: optional, the directory which should be used as");
UsageError(" android-data. By default ANDROID_DATA env variable is used.");
UsageError("");
+ UsageError(" --oat-fd=number: file descriptor of the oat file which should be analyzed");
+ UsageError("");
+ UsageError(" --vdex-fd=number: file descriptor of the vdex file corresponding to the oat file");
+ UsageError("");
UsageError(" --downgrade: optional, if the purpose of dexopt is to downgrade the dex file");
UsageError(" By default, dexopt considers upgrade case.");
UsageError("");
@@ -167,6 +171,10 @@ class DexoptAnalyzer FINAL {
setenv("ANDROID_DATA", new_android_data.c_str(), 1);
} else if (option.starts_with("--downgrade")) {
downgrade_ = true;
+ } else if (option.starts_with("--oat-fd")) {
+ oat_fd_ = std::stoi(option.substr(strlen("--oat-fd=")).ToString(), nullptr, 0);
+ } else if (option.starts_with("--vdex-fd")) {
+ vdex_fd_ = std::stoi(option.substr(strlen("--vdex-fd=")).ToString(), nullptr, 0);
} else { Usage("Unknown argument '%s'", option.data()); }
}
@@ -181,6 +189,12 @@ class DexoptAnalyzer FINAL {
Usage("--image unspecified and ANDROID_ROOT not set or image file does not exist.");
}
}
+ if (oat_fd_ > 0 && vdex_fd_ < 0) {
+ Usage("A valid --vdex-fd must also be provided with --oat-fd.");
+ }
+ if (oat_fd_ < 0 && vdex_fd_ > 0) {
+ Usage("A valid --oat-fd must also be provided with --vdex-fd.");
+ }
}
bool CreateRuntime() {
@@ -223,15 +237,26 @@ class DexoptAnalyzer FINAL {
}
std::unique_ptr<Runtime> runtime(Runtime::Current());
- OatFileAssistant oat_file_assistant(dex_file_.c_str(), isa_, /*load_executable*/ false);
+ std::unique_ptr<OatFileAssistant> oat_file_assistant;
+ if (oat_fd_ != -1 && vdex_fd_ != -1) {
+ oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(),
+ isa_,
+ false /*load_executable*/,
+ vdex_fd_,
+ oat_fd_);
+ } else {
+ oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(),
+ isa_,
+ false /*load_executable*/);
+ }
// Always treat elements of the bootclasspath as up-to-date.
// TODO(calin): this check should be in OatFileAssistant.
- if (oat_file_assistant.IsInBootClassPath()) {
+ if (oat_file_assistant->IsInBootClassPath()) {
return kNoDexOptNeeded;
}
// TODO(calin): Pass the class loader context as an argument to dexoptanalyzer. b/62269291.
- int dexoptNeeded = oat_file_assistant.GetDexOptNeeded(
+ int dexoptNeeded = oat_file_assistant->GetDexOptNeeded(
compiler_filter_, assume_profile_changed_, downgrade_);
// Convert OatFileAssitant codes to dexoptanalyzer codes.
@@ -258,6 +283,8 @@ class DexoptAnalyzer FINAL {
bool assume_profile_changed_;
bool downgrade_;
std::string image_;
+ int oat_fd_ = -1;
+ int vdex_fd_ = -1;
};
static int dexoptAnalyze(int argc, char** argv) {
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 32dd69e0e6..0282fbce1f 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -842,6 +842,12 @@ void EventHandler::HandleLocalAccessCapabilityAdded() {
bool operator()(art::ObjPtr<art::mirror::Class> klass)
OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ if (!klass->IsLoaded()) {
+ // Skip classes that aren't loaded since they might not have fully allocated and initialized
+ // their methods. Furthemore since the jvmti-plugin must have been loaded by this point
+ // these methods will definitately be using debuggable code.
+ return true;
+ }
for (auto& m : klass->GetMethods(art::kRuntimePointerSize)) {
const void* code = m.GetEntryPointFromQuickCompiledCode();
if (m.IsNative() || m.IsProxyMethod()) {
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 5bfa5ca491..c4988695f1 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -30,6 +30,7 @@
*/
#include "fixed_up_dex_file.h"
+#include "dex_file_loader.h"
#include "dex_file-inl.h"
// Runtime includes.
@@ -68,7 +69,7 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi
data.resize(original.Size());
memcpy(data.data(), original.Begin(), original.Size());
std::string error;
- std::unique_ptr<const art::DexFile> new_dex_file(art::DexFile::Open(
+ std::unique_ptr<const art::DexFile> new_dex_file(art::DexFileLoader::Open(
data.data(),
data.size(),
/*location*/"Unquickening_dexfile.dex",
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index daf4a8b7f2..5f29416134 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -43,6 +43,7 @@
#include "class_table-inl.h"
#include "common_throws.h"
#include "dex_file_annotations.h"
+#include "dex_file_loader.h"
#include "events-inl.h"
#include "fixed_up_dex_file.h"
#include "gc/heap-visit-objects-inl.h"
@@ -106,12 +107,12 @@ static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self,
}
uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_;
std::string map_name = map->GetName();
- std::unique_ptr<const art::DexFile> dex_file(art::DexFile::Open(map_name,
- checksum,
- std::move(map),
- /*verify*/true,
- /*verify_checksum*/true,
- &error_msg));
+ std::unique_ptr<const art::DexFile> dex_file(art::DexFileLoader::Open(map_name,
+ checksum,
+ std::move(map),
+ /*verify*/true,
+ /*verify_checksum*/true,
+ &error_msg));
if (dex_file.get() == nullptr) {
LOG(WARNING) << "Unable to load modified dex file for " << descriptor << ": " << error_msg;
art::ThrowClassFormatError(nullptr,
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index f05977a4b1..50402a04a9 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -572,8 +572,9 @@ class CommonLocalVariableClosure : public art::Closure {
return;
}
art::ArtMethod* method = visitor.GetMethod();
- if (method->IsNative()) {
- // TODO We really should support get/set for non-shadow frames.
+ // Native and 'art' proxy methods don't have registers.
+ if (method->IsNative() || method->IsProxyMethod()) {
+ // TODO It might be useful to fake up support for get at least on proxy frames.
result_ = ERR(OPAQUE_FRAME);
return;
} else if (method->GetCodeItem()->registers_size_ <= slot_) {
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 815caeb9cb..53abfbca00 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -44,6 +44,7 @@
#include "class_linker-inl.h"
#include "debugger.h"
#include "dex_file.h"
+#include "dex_file_loader.h"
#include "dex_file_types.h"
#include "events-inl.h"
#include "gc/allocation_listener.h"
@@ -425,12 +426,12 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition
return ERR(INVALID_CLASS_FORMAT);
}
uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_;
- std::unique_ptr<const art::DexFile> dex_file(art::DexFile::Open(map->GetName(),
- checksum,
- std::move(map),
- /*verify*/true,
- /*verify_checksum*/true,
- error_msg_));
+ std::unique_ptr<const art::DexFile> dex_file(art::DexFileLoader::Open(map->GetName(),
+ checksum,
+ std::move(map),
+ /*verify*/true,
+ /*verify_checksum*/true,
+ error_msg_));
if (dex_file.get() == nullptr) {
os << "Unable to load modified dex file for " << def.GetName() << ": " << *error_msg_;
*error_msg_ = os.str();
@@ -1403,7 +1404,9 @@ void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class>
method.SetNotIntrinsic();
// Notify the jit that this method is redefined.
art::jit::Jit* jit = driver_->runtime_->GetJit();
- if (jit != nullptr) {
+ // Non-invokable methods don't have any JIT data associated with them so we don't need to tell
+ // the jit about them.
+ if (jit != nullptr && method.IsInvokable()) {
jit->GetCodeCache()->NotifyMethodRedefined(&method);
}
}
diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
index 25bc5d6eb3..bafc8552b1 100644
--- a/openjdkjvmti/ti_search.cc
+++ b/openjdkjvmti/ti_search.cc
@@ -39,6 +39,7 @@
#include "base/macros.h"
#include "class_linker.h"
#include "dex_file.h"
+#include "dex_file_loader.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/object.h"
@@ -226,7 +227,7 @@ jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_U
std::string error_msg;
std::vector<std::unique_ptr<const art::DexFile>> dex_files;
- if (!art::DexFile::Open(segment, segment, true, &error_msg, &dex_files)) {
+ if (!art::DexFileLoader::Open(segment, segment, true, &error_msg, &dex_files)) {
LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg;
return ERR(ILLEGAL_ARGUMENT);
}
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index d4cc42ae70..e0c139954d 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -789,7 +789,7 @@ jvmtiError StackUtil::GetFrameLocation(jvmtiEnv* env ATTRIBUTE_UNUSED,
}
*method_ptr = art::jni::EncodeArtMethod(closure.method);
- if (closure.method->IsNative()) {
+ if (closure.method->IsNative() || closure.method->IsProxyMethod()) {
*location_ptr = -1;
} else {
if (closure.dex_pc == art::dex::kDexNoIndex) {
diff --git a/profman/profman.cc b/profman/profman.cc
index 9b4f5794b7..8ccf7b4c1d 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -39,6 +39,7 @@
#include "boot_image_profile.h"
#include "bytecode_utils.h"
#include "dex_file.h"
+#include "dex_file_loader.h"
#include "dex_file_types.h"
#include "jit/profile_compilation_info.h"
#include "profile_assistant.h"
@@ -328,21 +329,21 @@ class ProfMan FINAL {
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files_for_location;
if (use_apk_fd_list) {
- if (DexFile::OpenZip(apks_fd_[i],
- dex_locations_[i],
- kVerifyChecksum,
- &error_msg,
- &dex_files_for_location)) {
+ if (DexFileLoader::OpenZip(apks_fd_[i],
+ dex_locations_[i],
+ kVerifyChecksum,
+ &error_msg,
+ &dex_files_for_location)) {
} else {
LOG(WARNING) << "OpenZip failed for '" << dex_locations_[i] << "' " << error_msg;
continue;
}
} else {
- if (DexFile::Open(apk_files_[i].c_str(),
- dex_locations_[i],
- kVerifyChecksum,
- &error_msg,
- &dex_files_for_location)) {
+ if (DexFileLoader::Open(apk_files_[i].c_str(),
+ dex_locations_[i],
+ kVerifyChecksum,
+ &error_msg,
+ &dex_files_for_location)) {
} else {
LOG(WARNING) << "Open failed for '" << dex_locations_[i] << "' " << error_msg;
continue;
@@ -795,7 +796,7 @@ class ProfMan FINAL {
const DexFile* dex_file = class_ref.dex_file;
const auto& dex_resolved_classes = resolved_class_set.emplace(
dex_file->GetLocation(),
- dex_file->GetBaseLocation(),
+ DexFileLoader::GetBaseLocation(dex_file->GetLocation()),
dex_file->GetLocationChecksum(),
dex_file->NumMethodIds());
dex_resolved_classes.first->AddClass(class_ref.TypeIndex());
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 711bc65892..ed9906a5ec 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -55,6 +55,7 @@ cc_defaults {
"compiler_filter.cc",
"debugger.cc",
"dex_file.cc",
+ "dex_file_loader.cc",
"dex_file_annotations.cc",
"dex_file_layout.cc",
"dex_file_tracking_registrar.cc",
@@ -152,6 +153,7 @@ cc_defaults {
"mirror/throwable.cc",
"monitor.cc",
"native_bridge_art_interface.cc",
+ "native_dex_file.cc",
"native_stack_dump.cc",
"native/dalvik_system_DexFile.cc",
"native/dalvik_system_VMDebug.cc",
@@ -530,6 +532,7 @@ art_cc_test {
"barrier_test.cc",
"base/arena_allocator_test.cc",
"base/bit_field_test.cc",
+ "base/bit_struct_test.cc",
"base/bit_utils_test.cc",
"base/bit_vector_test.cc",
"base/hash_set_test.cc",
diff --git a/runtime/base/bit_struct.h b/runtime/base/bit_struct.h
new file mode 100644
index 0000000000..1f86ee1917
--- /dev/null
+++ b/runtime/base/bit_struct.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_BASE_BIT_STRUCT_H_
+#define ART_RUNTIME_BASE_BIT_STRUCT_H_
+
+#include "bit_struct_detail.h"
+#include "bit_utils.h"
+
+//
+// Zero-cost, type-safe, well-defined "structs" of bit fields.
+//
+// ---------------------------------------------
+// Usage example:
+// ---------------------------------------------
+//
+// // Definition for type 'Example'
+// BITSTRUCT_DEFINE_START(Example, 10)
+// BitStructUint<0, 2> u2; // Every field must be a BitStruct[*].
+// BitStructInt<2, 7> i7;
+// BitStructUint<9, 1> i1;
+// BITSTRUCT_DEFINE_END(Example);
+//
+// Would define a bit struct with this layout:
+// <- 1 -> <-- 7 --> <- 2 ->
+// +--------+---------------+-----+
+// | i1 | i7 | u2 +
+// +--------+---------------+-----+
+// 10 9 2 0
+//
+// // Read-write just like regular values.
+// Example ex;
+// ex.u2 = 3;
+// ex.i7 = -25;
+// ex.i1 = true;
+// size_t u2 = ex.u2;
+// int i7 = ex.i7;
+// bool i1 = ex.i1;
+//
+// // It's packed down to the smallest # of machine words.
+// assert(sizeof(Example) == 2);
+// // The exact bit pattern is well-defined by the template parameters.
+// uint16_t cast = *reinterpret_cast<uint16_t*>(ex);
+// assert(cast == ((3) | (0b100111 << 2) | (true << 9);
+//
+// ---------------------------------------------
+// Why not just use C++ bitfields?
+// ---------------------------------------------
+//
+// The layout is implementation-defined.
+// We do not know whether the fields are packed left-to-right or
+// right-to-left, so it makes it useless when the memory layout needs to be
+// precisely controlled.
+//
+// ---------------------------------------------
+// More info:
+// ---------------------------------------------
+// Currently uintmax_t is the largest supported underlying storage type,
+// all (kBitOffset + kBitWidth) must fit into BitSizeOf<uintmax_t>();
+//
+// Using BitStruct[U]int will automatically select an underlying type
+// that's the smallest to fit your (offset + bitwidth).
+//
+// BitStructNumber can be used to manually select an underlying type.
+//
+// BitStructField can be used with custom standard-layout structs,
+// thus allowing for arbitrary nesting of bit structs.
+//
+namespace art {
+// Zero-cost wrapper around a struct 'T', allowing it to be stored as a bitfield
+// at offset 'kBitOffset' and width 'kBitWidth'.
+// The storage is plain unsigned int, whose size is the smallest required to fit
+// 'kBitOffset + kBitWidth'. All operations to this become BitFieldExtract/BitFieldInsert
+// operations to the underlying uint.
+//
+// Field memory representation:
+//
+// MSB <-- width --> LSB
+// +--------+------------+--------+
+// | ?????? | u bitfield | ?????? +
+// +--------+------------+--------+
+// offset 0
+//
+// Reading/writing the bitfield (un)packs it into a temporary T:
+//
+// MSB <-- width --> LSB
+// +-----------------+------------+
+// | 0.............0 | T bitfield |
+// +-----------------+------------+
+// 0
+//
+// It's the responsibility of the StorageType to ensure the bit representation
+// of T can be represented by kBitWidth.
+template <typename T,
+ size_t kBitOffset,
+ size_t kBitWidth = BitStructSizeOf<T>(),
+ typename StorageType = typename detail::MinimumTypeUnsignedHelper<kBitOffset + kBitWidth>::type>
+struct BitStructField {
+ static_assert(std::is_standard_layout<T>::value, "T must be standard layout");
+
+ operator T() const {
+ return Get();
+ }
+
+ // Exclude overload when T==StorageType.
+ template <typename _ = void,
+ typename = std::enable_if_t<std::is_same<T, StorageType>::value, _>>
+ explicit operator StorageType() const {
+ return GetStorage();
+ }
+
+ BitStructField& operator=(T value) {
+ return Assign(*this, value);
+ }
+
+ static constexpr size_t BitStructSizeOf() {
+ return kBitWidth;
+ }
+
+ protected:
+ template <typename T2>
+ T2& Assign(T2& what, T value) {
+ // Since C++ doesn't allow the type of operator= to change out
+ // in the subclass, reimplement operator= in each subclass
+ // manually and call this helper function.
+ static_assert(std::is_base_of<BitStructField, T2>::value, "T2 must inherit BitStructField");
+ what.Set(value);
+ return what;
+ }
+
+ T Get() const {
+ ValueStorage vs;
+ vs.pod_.val_ = GetStorage();
+ return vs.value_;
+ }
+
+ void Set(T value) {
+ ValueStorage value_as_storage;
+ value_as_storage.value_ = value;
+
+ storage_.pod_.val_ = BitFieldInsert(storage_.pod_.val_,
+ value_as_storage.pod_.val_,
+ kBitOffset,
+ kBitWidth);
+ }
+
+ private:
+ StorageType GetStorage() const {
+ return BitFieldExtract(storage_.pod_.val_, kBitOffset, kBitWidth);
+ }
+
+ // Underlying value must be wrapped in a separate standard-layout struct.
+ // See below for more details.
+ struct PodWrapper {
+ StorageType val_;
+ };
+
+ union ValueStorage {
+ // Safely alias pod_ and value_ together.
+ //
+ // See C++ 9.5.1 [class.union]:
+ // If a standard-layout union contains several standard-layout structs that share a common
+ // initial sequence ... it is permitted to inspect the common initial sequence of any of
+ // standard-layout struct members.
+ PodWrapper pod_;
+ T value_;
+ } storage_;
+
+ // Future work: In theory almost non-standard layout can be supported here,
+ // assuming they don't rely on the address of (this).
+ // We just have to use memcpy since the union-aliasing would not work.
+};
+
+// Base class for number-like BitStruct fields.
+// T is the type to store in as a bit field.
+// kBitOffset, kBitWidth define the position and length of the bitfield.
+//
+// (Common usage should be BitStructInt, BitStructUint -- this
+// intermediate template allows a user-defined integer to be used.)
+template <typename T, size_t kBitOffset, size_t kBitWidth>
+struct BitStructNumber : public BitStructField<T, kBitOffset, kBitWidth, /*StorageType*/T> {
+ using StorageType = T;
+
+ BitStructNumber& operator=(T value) {
+ return BaseType::Assign(*this, value);
+ }
+
+ /*implicit*/ operator T() const {
+ return Get();
+ }
+
+ explicit operator bool() const {
+ return static_cast<bool>(Get());
+ }
+
+ BitStructNumber& operator++() {
+ *this = Get() + 1u;
+ return *this;
+ }
+
+ StorageType operator++(int) {
+ return Get() + 1u;
+ }
+
+ BitStructNumber& operator--() {
+ *this = Get() - 1u;
+ return *this;
+ }
+
+ StorageType operator--(int) {
+ return Get() - 1u;
+ }
+
+ private:
+ using BaseType = BitStructField<T, kBitOffset, kBitWidth, /*StorageType*/T>;
+ using BaseType::Get;
+};
+
+// Create a BitStruct field which uses the smallest underlying int storage type,
+// in order to be large enough to fit (kBitOffset + kBitWidth).
+//
+// Values are sign-extended when they are read out.
+template <size_t kBitOffset, size_t kBitWidth>
+using BitStructInt =
+ BitStructNumber<typename detail::MinimumTypeHelper<int, kBitOffset + kBitWidth>::type,
+ kBitOffset,
+ kBitWidth>;
+
+// Create a BitStruct field which uses the smallest underlying uint storage type,
+// in order to be large enough to fit (kBitOffset + kBitWidth).
+//
+// Values are zero-extended when they are read out.
+template <size_t kBitOffset, size_t kBitWidth>
+using BitStructUint =
+ BitStructNumber<typename detail::MinimumTypeHelper<unsigned int, kBitOffset + kBitWidth>::type,
+ kBitOffset,
+ kBitWidth>;
+
+// Start a definition for a bitstruct.
+// A bitstruct is defined to be a union with a common initial subsequence
+// that we call 'DefineBitStructSize<bitwidth>'.
+//
+// See top of file for usage example.
+//
+// This marker is required by the C++ standard in order to
+// have a "common initial sequence".
+//
+// See C++ 9.5.1 [class.union]:
+// If a standard-layout union contains several standard-layout structs that share a common
+// initial sequence ... it is permitted to inspect the common initial sequence of any of
+// standard-layout struct members.
+#define BITSTRUCT_DEFINE_START(name, bitwidth) \
+ union name { \
+ art::detail::DefineBitStructSize<(bitwidth)> _; \
+ static constexpr size_t BitStructSizeOf() { return (bitwidth); }
+
+// End the definition of a bitstruct, and insert a sanity check
+// to ensure that the bitstruct did not exceed the specified size.
+//
+// See top of file for usage example.
+#define BITSTRUCT_DEFINE_END(name) \
+ }; /* NOLINT [readability/braces] [4] */ \
+ static_assert(art::detail::ValidateBitStructSize<name>(), \
+ #name "bitsize incorrect: " \
+ "did you insert extra fields that weren't BitStructX, " \
+ "and does the size match the sum of the field widths?")
+
+// Determine the minimal bit size for a user-defined type T.
+// Used by BitStructField to determine how small a custom type is.
+template <typename T>
+static constexpr size_t BitStructSizeOf() {
+ return T::BitStructSizeOf();
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_BIT_STRUCT_H_
diff --git a/runtime/base/bit_struct_detail.h b/runtime/base/bit_struct_detail.h
new file mode 100644
index 0000000000..9f629c0970
--- /dev/null
+++ b/runtime/base/bit_struct_detail.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
+#define ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
+
+#include "bit_utils.h"
+#include "globals.h"
+
+#include <type_traits>
+
+// Implementation details for bit_struct.h
+// Not intended to be used stand-alone.
+
+namespace art {
+
+template <typename T>
+static constexpr size_t BitStructSizeOf();
+
+namespace detail {
+ // Select the smallest uintX_t that will fit kBitSize bits.
+ template <size_t kBitSize>
+ struct MinimumTypeUnsignedHelper {
+ using type =
+ typename std::conditional<kBitSize == 0, void,
+ typename std::conditional<kBitSize <= 8, uint8_t,
+ typename std::conditional<kBitSize <= 16, uint16_t,
+ typename std::conditional<kBitSize <= 32, uint32_t,
+ typename std::conditional<kBitSize <= 64, uint64_t,
+ typename std::conditional<kBitSize <= BitSizeOf<uintmax_t>(), uintmax_t,
+ void>::type>::type>::type>::type>::type>::type;
+ };
+
+ // Select the smallest [u]intX_t that will fit kBitSize bits.
+ // Automatically picks intX_t or uintX_t based on the sign-ness of T.
+ template <typename T, size_t kBitSize>
+ struct MinimumTypeHelper {
+ using type_unsigned = typename MinimumTypeUnsignedHelper<kBitSize>::type;
+
+ using type =
+ typename std::conditional</* if */ std::is_signed<T>::value,
+ /* then */ typename std::make_signed<type_unsigned>::type,
+ /* else */ type_unsigned>::type;
+ };
+
+ // Ensure the minimal type storage for 'T' matches its declared BitStructSizeOf.
+ // Nominally used by the BITSTRUCT_DEFINE_END macro.
+ template <typename T>
+ static constexpr bool ValidateBitStructSize() {
+ const size_t kBitStructSizeOf = BitStructSizeOf<T>();
+ const size_t kExpectedSize = (BitStructSizeOf<T>() < kBitsPerByte)
+ ? kBitsPerByte
+ : RoundUpToPowerOfTwo(kBitStructSizeOf);
+
+ // Ensure no extra fields were added in between START/END.
+ const size_t kActualSize = sizeof(T) * kBitsPerByte;
+ return kExpectedSize == kActualSize;
+ }
+
+ // Denotes the beginning of a bit struct.
+ //
+ // This marker is required by the C++ standard in order to
+ // have a "common initial sequence".
+ //
+ // See C++ 9.5.1 [class.union]:
+ // If a standard-layout union contains several standard-layout structs that share a common
+ // initial sequence ... it is permitted to inspect the common initial sequence of any of
+ // standard-layout struct members.
+ template <size_t kSize>
+ struct DefineBitStructSize {
+ private:
+ typename MinimumTypeUnsignedHelper<kSize>::type _;
+ };
+} // namespace detail
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
diff --git a/runtime/base/bit_struct_test.cc b/runtime/base/bit_struct_test.cc
new file mode 100644
index 0000000000..872ada324c
--- /dev/null
+++ b/runtime/base/bit_struct_test.cc
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "bit_struct.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+// A copy of detail::ValidateBitStructSize that uses EXPECT for a more
+// human-readable message.
+template <typename T>
+static constexpr bool ValidateBitStructSize(const char* name) {
+ const size_t kBitStructSizeOf = BitStructSizeOf<T>();
+ const size_t kExpectedSize = (BitStructSizeOf<T>() < kBitsPerByte)
+ ? kBitsPerByte
+ : RoundUpToPowerOfTwo(kBitStructSizeOf);
+
+ // Ensure no extra fields were added in between START/END.
+ const size_t kActualSize = sizeof(T) * kBitsPerByte;
+ EXPECT_EQ(kExpectedSize, kActualSize) << name;
+ return true;
+}
+
+#define VALIDATE_BITSTRUCT_SIZE(type) ValidateBitStructSize<type>(#type)
+
+TEST(BitStructs, MinimumType) {
+ EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<1>::type));
+ EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<2>::type));
+ EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<3>::type));
+ EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<8>::type));
+ EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<9>::type));
+ EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<10>::type));
+ EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<15>::type));
+ EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<16>::type));
+ EXPECT_EQ(4u, sizeof(typename detail::MinimumTypeUnsignedHelper<17>::type));
+ EXPECT_EQ(4u, sizeof(typename detail::MinimumTypeUnsignedHelper<32>::type));
+ EXPECT_EQ(8u, sizeof(typename detail::MinimumTypeUnsignedHelper<33>::type));
+ EXPECT_EQ(8u, sizeof(typename detail::MinimumTypeUnsignedHelper<64>::type));
+}
+
+template <typename T>
+size_t AsUint(const T& value) {
+ size_t uint_value = 0;
+ memcpy(&uint_value, &value, sizeof(value));
+ return uint_value;
+}
+
+struct CustomBitStruct {
+ CustomBitStruct() = default;
+ explicit CustomBitStruct(int8_t data) : data(data) {}
+
+ static constexpr size_t BitStructSizeOf() {
+ return 4;
+ }
+
+ int8_t data;
+};
+
+template <typename T>
+void ZeroInitialize(T& value) {
+ memset(&value, 0, sizeof(T));
+ // TODO: replace with value initialization
+}
+
+TEST(BitStructs, Custom) {
+ CustomBitStruct expected(0b1111);
+
+ BitStructField<CustomBitStruct, /*lsb*/4, /*width*/4> f;
+ ZeroInitialize(f);
+
+ EXPECT_EQ(1u, sizeof(f));
+
+ f = CustomBitStruct(0b1111);
+
+ CustomBitStruct read_out = f;
+ EXPECT_EQ(read_out.data, 0b1111);
+
+ EXPECT_EQ(AsUint(f), 0b11110000u);
+}
+
+BITSTRUCT_DEFINE_START(TestTwoCustom, /* size */ 8)
+ BitStructField<CustomBitStruct, /*lsb*/0, /*width*/4> f4_a;
+ BitStructField<CustomBitStruct, /*lsb*/4, /*width*/4> f4_b;
+BITSTRUCT_DEFINE_END(TestTwoCustom);
+
+TEST(BitStructs, TwoCustom) {
+ EXPECT_EQ(sizeof(TestTwoCustom), 1u);
+
+ VALIDATE_BITSTRUCT_SIZE(TestTwoCustom);
+
+ TestTwoCustom cst;
+ ZeroInitialize(cst);
+
+ // Test the write to most-significant field doesn't clobber least-significant.
+ cst.f4_a = CustomBitStruct(0b0110);
+ cst.f4_b = CustomBitStruct(0b0101);
+
+ int8_t read_out = static_cast<CustomBitStruct>(cst.f4_a).data;
+ int8_t read_out_b = static_cast<CustomBitStruct>(cst.f4_b).data;
+
+ EXPECT_EQ(0b0110, static_cast<int>(read_out));
+ EXPECT_EQ(0b0101, static_cast<int>(read_out_b));
+
+ EXPECT_EQ(AsUint(cst), 0b01010110u);
+
+ // Test write to least-significant field doesn't clobber most-significant.
+ cst.f4_a = CustomBitStruct(0);
+
+ read_out = static_cast<CustomBitStruct>(cst.f4_a).data;
+ read_out_b = static_cast<CustomBitStruct>(cst.f4_b).data;
+
+ EXPECT_EQ(0b0, static_cast<int>(read_out));
+ EXPECT_EQ(0b0101, static_cast<int>(read_out_b));
+
+ EXPECT_EQ(AsUint(cst), 0b01010000u);
+}
+
+TEST(BitStructs, Number) {
+ BitStructNumber<uint16_t, /*lsb*/4, /*width*/4> bsn;
+ ZeroInitialize(bsn);
+ EXPECT_EQ(2u, sizeof(bsn));
+
+ bsn = 0b1111;
+
+ uint32_t read_out = static_cast<uint32_t>(bsn);
+ uint32_t read_out_impl = bsn;
+
+ EXPECT_EQ(read_out, read_out_impl);
+ EXPECT_EQ(read_out, 0b1111u);
+ EXPECT_EQ(AsUint(bsn), 0b11110000u);
+}
+
+BITSTRUCT_DEFINE_START(TestBitStruct, /* size */ 8)
+ BitStructInt</*lsb*/0, /*width*/3> i3;
+ BitStructUint</*lsb*/3, /*width*/4> u4;
+
+ BitStructUint</*lsb*/0, /*width*/7> alias_all;
+BITSTRUCT_DEFINE_END(TestBitStruct);
+
+TEST(BitStructs, Test1) {
+ {
+ // Check minimal size selection is correct.
+ BitStructInt</*lsb*/0, /*width*/3> i3;
+ BitStructUint</*lsb*/3, /*width*/4> u4;
+
+ BitStructUint</*lsb*/0, /*width*/7> alias_all;
+
+ EXPECT_EQ(1u, sizeof(i3));
+ EXPECT_EQ(1u, sizeof(u4));
+ EXPECT_EQ(1u, sizeof(alias_all));
+ }
+ TestBitStruct tst;
+ ZeroInitialize(tst);
+
+ // Check minimal size selection is correct.
+ EXPECT_EQ(1u, sizeof(TestBitStruct));
+ EXPECT_EQ(1u, sizeof(tst._));
+ EXPECT_EQ(1u, sizeof(tst.i3));
+ EXPECT_EQ(1u, sizeof(tst.u4));
+ EXPECT_EQ(1u, sizeof(tst.alias_all));
+
+ // Check operator assignment.
+ tst.i3 = -1;
+ tst.u4 = 0b1010;
+
+ // Check implicit operator conversion.
+ int8_t read_i3 = tst.i3;
+ uint8_t read_u4 = tst.u4;
+
+ // Ensure read-out values were correct.
+ EXPECT_EQ(static_cast<int8_t>(-1), read_i3);
+ EXPECT_EQ(0b1010, read_u4);
+
+ // Ensure aliasing is working.
+ EXPECT_EQ(0b1010111, static_cast<uint8_t>(tst.alias_all));
+
+ // Ensure the bit pattern is correct.
+ EXPECT_EQ(0b1010111u, AsUint(tst));
+
+ // Math operator checks
+ {
+ // In-place
+ ++tst.u4;
+ EXPECT_EQ(static_cast<uint8_t>(0b1011), static_cast<uint8_t>(tst.u4));
+ --tst.u4;
+ EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
+
+ // Copy
+ uint8_t read_and_convert = tst.u4++;
+ EXPECT_EQ(static_cast<uint8_t>(0b1011), read_and_convert);
+ EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
+ read_and_convert = tst.u4--;
+ EXPECT_EQ(static_cast<uint8_t>(0b1001), read_and_convert);
+ EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
+
+ // Check boolean operator conversion.
+ tst.u4 = 0b1010;
+ EXPECT_TRUE(static_cast<bool>(tst.u4));
+ bool succ = tst.u4 ? true : false;
+ EXPECT_TRUE(succ);
+
+ tst.u4 = 0;
+ EXPECT_FALSE(static_cast<bool>(tst.u4));
+
+/*
+ // Disabled: Overflow is caught by the BitFieldInsert DCHECKs.
+ // Check overflow for uint.
+ tst.u4 = 0b1111;
+ ++tst.u4;
+ EXPECT_EQ(static_cast<uint8_t>(0), static_cast<uint8_t>(tst.u4));
+*/
+ }
+}
+
+BITSTRUCT_DEFINE_START(MixedSizeBitStruct, /* size */ 32)
+ BitStructUint</*lsb*/0, /*width*/3> u3;
+ BitStructUint</*lsb*/3, /*width*/10> u10;
+ BitStructUint</*lsb*/13, /*width*/19> u19;
+
+ BitStructUint</*lsb*/0, /*width*/32> alias_all;
+BITSTRUCT_DEFINE_END(MixedSizeBitStruct);
+
+// static_assert(sizeof(MixedSizeBitStruct) == sizeof(uint32_t), "TestBitStructs#MixedSize");
+
+TEST(BitStructs, Mixed) {
+ EXPECT_EQ(4u, sizeof(MixedSizeBitStruct));
+
+ MixedSizeBitStruct tst;
+ ZeroInitialize(tst);
+
+ // Check operator assignment.
+ tst.u3 = 0b111u;
+ tst.u10 = 0b1111010100u;
+ tst.u19 = 0b1010101010101010101u;
+
+ // Check implicit operator conversion.
+ uint8_t read_u3 = tst.u3;
+ uint16_t read_u10 = tst.u10;
+ uint32_t read_u19 = tst.u19;
+
+ // Ensure read-out values were correct.
+ EXPECT_EQ(0b111u, read_u3);
+ EXPECT_EQ(0b1111010100u, read_u10);
+ EXPECT_EQ(0b1010101010101010101u, read_u19);
+
+ uint32_t read_all = tst.alias_all;
+
+ // Ensure aliasing is working.
+ EXPECT_EQ(0b10101010101010101011111010100111u, read_all);
+
+ // Ensure the bit pattern is correct.
+ EXPECT_EQ(0b10101010101010101011111010100111u, AsUint(tst));
+}
+
+} // namespace art
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index 87dac0261e..da3c7048b6 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -371,6 +371,128 @@ inline static uint64_t ReverseBits64(uint64_t opnd) {
return opnd;
}
+// Create a mask for the least significant "bits"
+// The returned value is always unsigned to prevent undefined behavior for bitwise ops.
+//
+// Given 'bits',
+// Returns:
+// <--- bits --->
+// +-----------------+------------+
+// | 0 ............0 | 1.....1 |
+// +-----------------+------------+
+// msb lsb
+template <typename T = size_t>
+inline static constexpr std::make_unsigned_t<T> MaskLeastSignificant(size_t bits) {
+ DCHECK_GE(BitSizeOf<T>(), bits) << "Bits out of range for type T";
+ using unsigned_T = std::make_unsigned_t<T>;
+ if (bits >= BitSizeOf<T>()) {
+ return std::numeric_limits<unsigned_T>::max();
+ } else {
+ return static_cast<unsigned_T>((1 << bits) - 1);
+ }
+}
+
+// Clears the bitfield starting at the least significant bit "lsb" with a bitwidth of 'width'.
+// (Equivalent of ARM BFC instruction).
+//
+// Given:
+// <-- width -->
+// +--------+------------+--------+
+// | ABC... | bitfield | XYZ... +
+// +--------+------------+--------+
+// lsb 0
+// Returns:
+// <-- width -->
+// +--------+------------+--------+
+// | ABC... | 0........0 | XYZ... +
+// +--------+------------+--------+
+// lsb 0
+template <typename T>
+inline static constexpr T BitFieldClear(T value, size_t lsb, size_t width) {
+ DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value";
+ const auto val = static_cast<std::make_unsigned_t<T>>(value);
+ const auto mask = MaskLeastSignificant<T>(width);
+
+ return static_cast<T>(val & ~(mask << lsb));
+}
+
+// Inserts the contents of 'data' into bitfield of 'value' starting
+// at the least significant bit "lsb" with a bitwidth of 'width'.
+// Note: data must be within range of [MinInt(width), MaxInt(width)].
+// (Equivalent of ARM BFI instruction).
+//
+// Given (data):
+// <-- width -->
+// +--------+------------+--------+
+// | ABC... | bitfield | XYZ... +
+// +--------+------------+--------+
+// lsb 0
+// Returns:
+// <-- width -->
+// +--------+------------+--------+
+// | ABC... | 0...data | XYZ... +
+// +--------+------------+--------+
+// lsb 0
+
+template <typename T, typename T2>
+inline static constexpr T BitFieldInsert(T value, T2 data, size_t lsb, size_t width) {
+ DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value";
+ if (width != 0u) {
+ DCHECK_GE(MaxInt<T2>(width), data) << "Data out of range [too large] for bitwidth";
+ DCHECK_LE(MinInt<T2>(width), data) << "Data out of range [too small] for bitwidth";
+ } else {
+ DCHECK_EQ(static_cast<T2>(0), data) << "Data out of range [nonzero] for bitwidth 0";
+ }
+ const auto data_mask = MaskLeastSignificant<T2>(width);
+ const auto value_cleared = BitFieldClear(value, lsb, width);
+
+ return static_cast<T>(value_cleared | ((data & data_mask) << lsb));
+}
+
+// Extracts the bitfield starting at the least significant bit "lsb" with a bitwidth of 'width'.
+// Signed types are sign-extended during extraction. (Equivalent of ARM UBFX/SBFX instruction).
+//
+// Given:
+// <-- width -->
+// +--------+-------------+-------+
+// | | bitfield | +
+// +--------+-------------+-------+
+// lsb 0
+// (Unsigned) Returns:
+// <-- width -->
+// +----------------+-------------+
+// | 0... 0 | bitfield |
+// +----------------+-------------+
+// 0
+// (Signed) Returns:
+// <-- width -->
+// +----------------+-------------+
+// | S... S | bitfield |
+// +----------------+-------------+
+// 0
+// where S is the highest bit in 'bitfield'.
+template <typename T>
+inline static constexpr T BitFieldExtract(T value, size_t lsb, size_t width) {
+ DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value";
+ const auto val = static_cast<std::make_unsigned_t<T>>(value);
+
+ const T bitfield_unsigned =
+ static_cast<T>((val >> lsb) & MaskLeastSignificant<T>(width));
+ if (std::is_signed<T>::value) {
+ // Perform sign extension
+ if (width == 0) { // Avoid underflow.
+ return static_cast<T>(0);
+ } else if (bitfield_unsigned & (1 << (width - 1))) { // Detect if sign bit was set.
+ // MSB <width> LSB
+ // 0b11111...100...000000
+ const auto ones_negmask = ~MaskLeastSignificant<T>(width);
+ return static_cast<T>(bitfield_unsigned | ones_negmask);
+ }
+ }
+ // Skip sign extension.
+ return bitfield_unsigned;
+}
+
} // namespace art
#endif // ART_RUNTIME_BASE_BIT_UTILS_H_
diff --git a/runtime/base/bit_utils_test.cc b/runtime/base/bit_utils_test.cc
index c96c6dc933..0276d8ded2 100644
--- a/runtime/base/bit_utils_test.cc
+++ b/runtime/base/bit_utils_test.cc
@@ -345,6 +345,97 @@ static_assert(IsAbsoluteUint<32, int64_t>(std::numeric_limits<uint32_t>::max()),
"TestIsAbsoluteUint64#27");
static_assert(!IsAbsoluteUint<32, int64_t>(kUint32MaxPlus1), "TestIsAbsoluteUint64#28");
+static_assert(MaskLeastSignificant(0) == 0b0, "TestMaskLeastSignificant#1");
+static_assert(MaskLeastSignificant(1) == 0b1, "TestMaskLeastSignificant#2");
+static_assert(MaskLeastSignificant(2) == 0b11, "TestMaskLeastSignificant#3");
+static_assert(MaskLeastSignificant<uint8_t>(8) == 0xFF, "TestMaskLeastSignificant#4");
+static_assert(MaskLeastSignificant<int8_t>(8) == 0xFF, "TestMaskLeastSignificant#5");
+
+static_assert(BitFieldClear(0xFF, /*lsb*/0, /*width*/0) == 0xFF, "TestBitFieldClear#1");
+static_assert(BitFieldClear(std::numeric_limits<uint32_t>::max(), /*lsb*/0, /*width*/32) == 0x0,
+ "TestBitFieldClear#2");
+static_assert(BitFieldClear(std::numeric_limits<int32_t>::max(), /*lsb*/0, /*width*/32) == 0x0,
+ "TestBitFieldClear#3");
+static_assert(BitFieldClear(0xFF, /*lsb*/0, /*width*/2) == 0b11111100, "TestBitFieldClear#4");
+static_assert(BitFieldClear(0xFF, /*lsb*/0, /*width*/3) == 0b11111000, "TestBitFieldClear#5");
+static_assert(BitFieldClear(0xFF, /*lsb*/1, /*width*/3) == 0b11110001, "TestBitFieldClear#6");
+static_assert(BitFieldClear(0xFF, /*lsb*/2, /*width*/3) == 0b11100011, "TestBitFieldClear#7");
+
+static_assert(BitFieldExtract(0xFF, /*lsb*/0, /*width*/0) == 0x0, "TestBitFieldExtract#1");
+static_assert(BitFieldExtract(std::numeric_limits<uint32_t>::max(), /*lsb*/0, /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldExtract#2");
+static_assert(BitFieldExtract(std::numeric_limits<int32_t>::max(), /*lsb*/0, /*width*/32)
+ == std::numeric_limits<int32_t>::max(),
+ "TestBitFieldExtract#3");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/0, /*width*/2) == 0b00000011,
+ "TestBitFieldExtract#4");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/0, /*width*/3) == 0b00000111,
+ "TestBitFieldExtract#5");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/1, /*width*/3) == 0b00000111,
+ "TestBitFieldExtract#6");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/2, /*width*/3) == 0b00000111,
+ "TestBitFieldExtract#7");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/3, /*width*/3) == 0b00000111,
+ "TestBitFieldExtract#8");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/8, /*width*/3) == 0b00000000,
+ "TestBitFieldExtract#9");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/7, /*width*/3) == 0b00000001,
+ "TestBitFieldExtract#10");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/6, /*width*/3) == 0b00000011,
+ "TestBitFieldExtract#11");
+static_assert(BitFieldExtract(0xFF, /*lsb*/0, /*width*/2) == -1, "TestBitFieldExtract#12");
+static_assert(BitFieldExtract(0xFF, /*lsb*/0, /*width*/3) == -1, "TestBitFieldExtract#13");
+static_assert(BitFieldExtract(0xFF, /*lsb*/1, /*width*/3) == -1, "TestBitFieldExtract#14");
+static_assert(BitFieldExtract(0xFF, /*lsb*/2, /*width*/3) == -1, "TestBitFieldExtract#15");
+static_assert(BitFieldExtract(0xFF, /*lsb*/3, /*width*/3) == -1, "TestBitFieldExtract#16");
+static_assert(BitFieldExtract(0xFF, /*lsb*/8, /*width*/3) == 0b00000000, "TestBitFieldExtract#17");
+static_assert(BitFieldExtract(0xFF, /*lsb*/7, /*width*/3) == 0b00000001, "TestBitFieldExtract#18");
+static_assert(BitFieldExtract(0xFF, /*lsb*/6, /*width*/3) == 0b00000011, "TestBitFieldExtract#19");
+static_assert(BitFieldExtract(static_cast<uint8_t>(0b01101010), /*lsb*/2, /*width*/4)
+ == 0b00001010,
+ "TestBitFieldExtract#20");
+static_assert(BitFieldExtract(static_cast<int8_t>(0b01101010), /*lsb*/2, /*width*/4)
+ == static_cast<int8_t>(0b11111010),
+ "TestBitFieldExtract#21");
+
+static_assert(BitFieldInsert(0xFF, /*data*/0x0, /*lsb*/0, /*width*/0) == 0xFF,
+ "TestBitFieldInsert#1");
+static_assert(BitFieldInsert(std::numeric_limits<uint32_t>::max(),
+ /*data*/std::numeric_limits<uint32_t>::max(),
+ /*lsb*/0,
+ /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldInsert#2");
+static_assert(BitFieldInsert(std::numeric_limits<int32_t>::max(),
+ /*data*/std::numeric_limits<uint32_t>::max(),
+ /*lsb*/0,
+ /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldInsert#3");
+static_assert(BitFieldInsert(0u,
+ /*data*/std::numeric_limits<uint32_t>::max(),
+ /*lsb*/0,
+ /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldInsert#4");
+static_assert(BitFieldInsert(-(-0),
+ /*data*/std::numeric_limits<uint32_t>::max(),
+ /*lsb*/0,
+ /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldInsert#5");
+static_assert(BitFieldInsert(0x00, /*data*/0b11u, /*lsb*/0, /*width*/2) == 0b00000011,
+ "TestBitFieldInsert#6");
+static_assert(BitFieldInsert(0x00, /*data*/0b111u, /*lsb*/0, /*width*/3) == 0b00000111,
+ "TestBitFieldInsert#7");
+static_assert(BitFieldInsert(0x00, /*data*/0b111u, /*lsb*/1, /*width*/3) == 0b00001110,
+ "TestBitFieldInsert#8");
+static_assert(BitFieldInsert(0x00, /*data*/0b111u, /*lsb*/2, /*width*/3) == 0b00011100,
+ "TestBitFieldInsert#9");
+static_assert(BitFieldInsert(0b01011100, /*data*/0b1101u, /*lsb*/4, /*width*/4) == 0b11011100,
+ "TestBitFieldInsert#10");
+
template <typename Container>
void CheckElements(const std::initializer_list<uint32_t>& expected, const Container& elements) {
auto expected_it = expected.begin();
diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc
index 568a7ae5d6..30b4f0559d 100644
--- a/runtime/base/file_magic.cc
+++ b/runtime/base/file_magic.cc
@@ -55,8 +55,4 @@ bool IsZipMagic(uint32_t magic) {
('K' == ((magic >> 8) & 0xff)));
}
-bool IsDexMagic(uint32_t magic) {
- return DexFile::IsMagicValid(reinterpret_cast<const uint8_t*>(&magic));
-}
-
} // namespace art
diff --git a/runtime/base/file_magic.h b/runtime/base/file_magic.h
index 4b5d2f5a48..1c9effdb50 100644
--- a/runtime/base/file_magic.h
+++ b/runtime/base/file_magic.h
@@ -29,7 +29,6 @@ File OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_
// Check whether the given magic matches a known file type.
bool IsZipMagic(uint32_t magic);
-bool IsDexMagic(uint32_t magic);
} // namespace art
diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h
index d87df8710c..71a1018dea 100644
--- a/runtime/base/variant_map.h
+++ b/runtime/base/variant_map.h
@@ -237,6 +237,14 @@ struct VariantMap {
return (ptr == nullptr) ? key.CreateDefaultValue() : *ptr;
}
+ template <typename T, typename U>
+ void AssignIfExists(const TKey<T>& key, U* out) {
+ DCHECK(out != nullptr);
+ if (Exists(key)) {
+ *out = std::move(*Get(key));
+ }
+ }
+
private:
// TODO: move to detail, or make it more generic like a ScopeGuard(function)
template <typename TValue>
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 8999e17cf1..fe91272ef7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -51,6 +51,7 @@
#include "compiler_callbacks.h"
#include "debugger.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "experimental_flags.h"
@@ -2866,6 +2867,11 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void*
return true;
}
+ if (Thread::Current()->IsAsyncExceptionPending()) {
+ // Force use of interpreter to handle async-exceptions
+ return true;
+ }
+
if (runtime->IsJavaDebuggable()) {
// For simplicity, we ignore precompiled code and go to the interpreter
// assuming we don't already have jitted code.
@@ -8709,10 +8715,11 @@ class GetResolvedClassesVisitor : public ClassVisitor {
const DexFile& dex_file = klass->GetDexFile();
if (&dex_file != last_dex_file_) {
last_dex_file_ = &dex_file;
- DexCacheResolvedClasses resolved_classes(dex_file.GetLocation(),
- dex_file.GetBaseLocation(),
- dex_file.GetLocationChecksum(),
- dex_file.NumMethodIds());
+ DexCacheResolvedClasses resolved_classes(
+ dex_file.GetLocation(),
+ DexFileLoader::GetBaseLocation(dex_file.GetLocation()),
+ dex_file.GetLocationChecksum(),
+ dex_file.NumMethodIds());
last_resolved_classes_ = result_->find(resolved_classes);
if (last_resolved_classes_ == result_->end()) {
last_resolved_classes_ = result_->insert(resolved_classes).first;
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index f887b8ed42..3d9fd59e0b 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -26,7 +26,7 @@
#include "base/enums.h"
#include "class_linker-inl.h"
#include "common_runtime_test.h"
-#include "dex_file.h"
+#include "native_dex_file.h"
#include "dex_file_types.h"
#include "entrypoints/entrypoint_utils-inl.h"
#include "experimental_flags.h"
@@ -1462,11 +1462,11 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) {
dex_cache->SetLocation(location.Get());
const DexFile* old_dex_file = dex_cache->GetDexFile();
- std::unique_ptr<DexFile> dex_file(new DexFile(old_dex_file->Begin(),
- old_dex_file->Size(),
- location->ToModifiedUtf8(),
- 0u,
- nullptr));
+ std::unique_ptr<DexFile> dex_file(new NativeDexFile(old_dex_file->Begin(),
+ old_dex_file->Size(),
+ location->ToModifiedUtf8(),
+ 0u,
+ nullptr));
{
WriterMutexLock mu(soa.Self(), *Locks::dex_lock_);
// Check that inserting with a UTF16 name works.
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 2282da048f..167533d68a 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -25,6 +25,7 @@
#include "class_linker.h"
#include "class_loader_utils.h"
#include "dex_file.h"
+#include "dex_file_loader.h"
#include "handle_scope-inl.h"
#include "jni_internal.h"
#include "oat_file_assistant.h"
@@ -227,11 +228,11 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla
std::string error_msg;
// When opening the dex files from the context we expect their checksum to match their
// contents. So pass true to verify_checksum.
- if (!DexFile::Open(location.c_str(),
- location.c_str(),
- /*verify_checksum*/ true,
- &error_msg,
- &info.opened_dex_files)) {
+ if (!DexFileLoader::Open(location.c_str(),
+ location.c_str(),
+ /*verify_checksum*/ true,
+ &error_msg,
+ &info.opened_dex_files)) {
// If we fail to open the dex file because it's been stripped, try to open the dex file
// from its corresponding oat file.
// This could happen when we need to recompile a pre-build whose dex code has been stripped.
@@ -282,7 +283,7 @@ bool ClassLoaderContext::RemoveLocationsFromClassPaths(
std::set<std::string> canonical_locations;
for (const std::string& location : locations) {
- canonical_locations.insert(DexFile::GetDexCanonicalLocation(location.c_str()));
+ canonical_locations.insert(DexFileLoader::GetDexCanonicalLocation(location.c_str()));
}
bool removed_locations = false;
for (ClassLoaderInfo& info : class_loader_chain_) {
@@ -292,7 +293,7 @@ bool ClassLoaderContext::RemoveLocationsFromClassPaths(
info.classpath.end(),
[canonical_locations](const std::string& location) {
return ContainsElement(canonical_locations,
- DexFile::GetDexCanonicalLocation(location.c_str()));
+ DexFileLoader::GetDexCanonicalLocation(location.c_str()));
});
info.classpath.erase(kept_it, info.classpath.end());
if (initial_size != info.classpath.size()) {
@@ -340,7 +341,8 @@ std::string ClassLoaderContext::EncodeContext(const std::string& base_dir,
if (for_dex2oat) {
// dex2oat only needs the base location. It cannot accept multidex locations.
// So ensure we only add each file once.
- bool new_insert = seen_locations.insert(dex_file->GetBaseLocation()).second;
+ bool new_insert = seen_locations.insert(
+ DexFileLoader::GetBaseLocation(dex_file->GetLocation())).second;
if (!new_insert) {
continue;
}
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index ae3dcecb4a..be6acde4a9 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -100,12 +100,13 @@ class ClassLoaderContextTest : public CommonRuntimeTest {
info.opened_dex_files[cur_open_dex_index++];
std::unique_ptr<const DexFile>& expected_dex_file = (*all_dex_files)[k];
- std::string expected_location = expected_dex_file->GetBaseLocation();
+ std::string expected_location =
+ DexFileLoader::GetBaseLocation(expected_dex_file->GetLocation());
UniqueCPtr<const char[]> expected_real_location(
realpath(expected_location.c_str(), nullptr));
ASSERT_TRUE(expected_real_location != nullptr) << expected_location;
expected_location.assign(expected_real_location.get());
- expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation());
+ expected_location += DexFileLoader::GetMultiDexSuffix(expected_dex_file->GetLocation());
ASSERT_EQ(expected_location, opened_dex_file->GetLocation());
ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 29b376a21c..0c2e49010e 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -34,6 +34,7 @@
#include "class_linker.h"
#include "compiler_callbacks.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "gc/heap.h"
#include "gc_root-inl.h"
#include "gtest/gtest.h"
@@ -372,7 +373,7 @@ std::unique_ptr<const DexFile> CommonRuntimeTestImpl::LoadExpectSingleDexFile(
std::string error_msg;
MemMap::Init();
static constexpr bool kVerifyChecksum = true;
- if (!DexFile::Open(location, location, kVerifyChecksum, &error_msg, &dex_files)) {
+ if (!DexFileLoader::Open(location, location, kVerifyChecksum, &error_msg, &dex_files)) {
LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
UNREACHABLE();
} else {
@@ -571,7 +572,7 @@ std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTestImpl::OpenTestDexFi
static constexpr bool kVerifyChecksum = true;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- bool success = DexFile::Open(
+ bool success = DexFileLoader::Open(
filename.c_str(), filename.c_str(), kVerifyChecksum, &error_msg, &dex_files);
CHECK(success) << "Failed to open '" << filename << "': " << error_msg;
for (auto& dex_file : dex_files) {
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 93daa45519..a9bb95480e 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -26,6 +26,7 @@
#include "base/stl_util.h"
#include "common_runtime_test.h"
#include "compiler_callbacks.h"
+#include "dex_file_loader.h"
#include "exec_utils.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
@@ -71,7 +72,8 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest {
<< "Expected dex file to be at: " << GetDexSrc1();
ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
<< "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
- ASSERT_FALSE(DexFile::GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg))
+ ASSERT_FALSE(
+ DexFileLoader::GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg))
<< "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
<< "Expected dex file to be at: " << GetDexSrc2();
@@ -80,13 +82,19 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest {
// GetMultiDexSrc1, but a different secondary dex checksum.
static constexpr bool kVerifyChecksum = true;
std::vector<std::unique_ptr<const DexFile>> multi1;
- ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(),
- GetMultiDexSrc1().c_str(), kVerifyChecksum, &error_msg, &multi1)) << error_msg;
+ ASSERT_TRUE(DexFileLoader::Open(GetMultiDexSrc1().c_str(),
+ GetMultiDexSrc1().c_str(),
+ kVerifyChecksum,
+ &error_msg,
+ &multi1)) << error_msg;
ASSERT_GT(multi1.size(), 1u);
std::vector<std::unique_ptr<const DexFile>> multi2;
- ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(),
- GetMultiDexSrc2().c_str(), kVerifyChecksum, &error_msg, &multi2)) << error_msg;
+ ASSERT_TRUE(DexFileLoader::Open(GetMultiDexSrc2().c_str(),
+ GetMultiDexSrc2().c_str(),
+ kVerifyChecksum,
+ &error_msg,
+ &multi2)) << error_msg;
ASSERT_GT(multi2.size(), 1u);
ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 2e776b0e61..f6b3428208 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -23,7 +23,6 @@
#include <string.h>
#include <sys/file.h>
#include <sys/mman.h> // For the PROT_* and MAP_* constants.
-#include <sys/stat.h>
#include <zlib.h>
#include <memory>
@@ -33,19 +32,17 @@
#include "android-base/stringprintf.h"
#include "base/enums.h"
-#include "base/file_magic.h"
#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/systrace.h"
-#include "base/unix_file/fd_file.h"
#include "dex_file-inl.h"
-#include "dex_file_verifier.h"
+#include "dex_file_loader.h"
#include "jvalue.h"
#include "leb128.h"
+#include "mem_map.h"
+#include "native_dex_file.h"
#include "os.h"
#include "utf-inl.h"
#include "utils.h"
-#include "zip_archive.h"
namespace art {
@@ -56,22 +53,6 @@ static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex
static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong");
static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial");
-static constexpr OatDexFile* kNoOatDexFile = nullptr;
-
-const char* DexFile::kClassesDex = "classes.dex";
-
-const uint8_t DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
-const uint8_t DexFile::kDexMagicVersions[DexFile::kNumDexVersions][DexFile::kDexVersionLen] = {
- {'0', '3', '5', '\0'},
- // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex
- // files with that version number would erroneously be accepted and run.
- {'0', '3', '7', '\0'},
- // Dex version 038: Android "O".
- {'0', '3', '8', '\0'},
- // Dex verion 039: Beyond Android "O".
- {'0', '3', '9', '\0'},
-};
-
uint32_t DexFile::CalculateChecksum() const {
const uint32_t non_sum = OFFSETOF_MEMBER(DexFile::Header, signature_);
const uint8_t* non_sum_ptr = Begin() + non_sum;
@@ -83,55 +64,6 @@ struct DexFile::AnnotationValue {
uint8_t type_;
};
-bool DexFile::GetMultiDexChecksums(const char* filename,
- std::vector<uint32_t>* checksums,
- std::string* error_msg) {
- CHECK(checksums != nullptr);
- uint32_t magic;
-
- File fd = OpenAndReadMagic(filename, &magic, error_msg);
- if (fd.Fd() == -1) {
- DCHECK(!error_msg->empty());
- return false;
- }
- if (IsZipMagic(magic)) {
- std::unique_ptr<ZipArchive> zip_archive(
- ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
- if (zip_archive.get() == nullptr) {
- *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
- error_msg->c_str());
- return false;
- }
-
- uint32_t i = 0;
- std::string zip_entry_name = GetMultiDexClassesDexName(i++);
- std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
- if (zip_entry.get() == nullptr) {
- *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
- zip_entry_name.c_str(), error_msg->c_str());
- return false;
- }
-
- do {
- checksums->push_back(zip_entry->GetCrc32());
- zip_entry_name = DexFile::GetMultiDexClassesDexName(i++);
- zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
- } while (zip_entry.get() != nullptr);
- return true;
- }
- if (IsDexMagic(magic)) {
- std::unique_ptr<const DexFile> dex_file(
- DexFile::OpenFile(fd.Release(), filename, false, false, error_msg));
- if (dex_file.get() == nullptr) {
- return false;
- }
- checksums->push_back(dex_file->GetHeader().checksum_);
- return true;
- }
- *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
- return false;
-}
-
int DexFile::GetPermissions() const {
if (mem_map_.get() == nullptr) {
return 0;
@@ -162,367 +94,6 @@ bool DexFile::DisableWrite() const {
}
}
-
-std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) {
- ScopedTrace trace(std::string("Open dex file from RAM ") + location);
- return OpenCommon(base,
- size,
- location,
- location_checksum,
- oat_dex_file,
- verify,
- verify_checksum,
- error_msg);
-}
-
-std::unique_ptr<const DexFile> DexFile::Open(const std::string& location,
- uint32_t location_checksum,
- std::unique_ptr<MemMap> map,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) {
- ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
- CHECK(map.get() != nullptr);
-
- if (map->Size() < sizeof(DexFile::Header)) {
- *error_msg = StringPrintf(
- "DexFile: failed to open dex file '%s' that is too short to have a header",
- location.c_str());
- return nullptr;
- }
-
- std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
- map->Size(),
- location,
- location_checksum,
- kNoOatDexFile,
- verify,
- verify_checksum,
- error_msg);
- if (dex_file != nullptr) {
- dex_file->mem_map_ = std::move(map);
- }
- return dex_file;
-}
-
-bool DexFile::Open(const char* filename,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) {
- ScopedTrace trace(std::string("Open dex file ") + std::string(location));
- DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
- uint32_t magic;
- File fd = OpenAndReadMagic(filename, &magic, error_msg);
- if (fd.Fd() == -1) {
- DCHECK(!error_msg->empty());
- return false;
- }
- if (IsZipMagic(magic)) {
- return DexFile::OpenZip(fd.Release(), location, verify_checksum, error_msg, dex_files);
- }
- if (IsDexMagic(magic)) {
- std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.Release(),
- location,
- /* verify */ true,
- verify_checksum,
- error_msg));
- if (dex_file.get() != nullptr) {
- dex_files->push_back(std::move(dex_file));
- return true;
- } else {
- return false;
- }
- }
- *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
- return false;
-}
-
-std::unique_ptr<const DexFile> DexFile::OpenDex(int fd,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg) {
- ScopedTrace trace("Open dex file " + std::string(location));
- return OpenFile(fd, location, true /* verify */, verify_checksum, error_msg);
-}
-
-bool DexFile::OpenZip(int fd,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) {
- ScopedTrace trace("Dex file open Zip " + std::string(location));
- DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr";
- std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
- if (zip_archive.get() == nullptr) {
- DCHECK(!error_msg->empty());
- return false;
- }
- return DexFile::OpenAllDexFilesFromZip(*zip_archive,
- location,
- verify_checksum,
- error_msg,
- dex_files);
-}
-
-std::unique_ptr<const DexFile> DexFile::OpenFile(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) {
- ScopedTrace trace(std::string("Open dex file ") + std::string(location));
- CHECK(!location.empty());
- std::unique_ptr<MemMap> map;
- {
- File delayed_close(fd, /* check_usage */ false);
- struct stat sbuf;
- memset(&sbuf, 0, sizeof(sbuf));
- if (fstat(fd, &sbuf) == -1) {
- *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
- strerror(errno));
- return nullptr;
- }
- if (S_ISDIR(sbuf.st_mode)) {
- *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
- return nullptr;
- }
- size_t length = sbuf.st_size;
- map.reset(MemMap::MapFile(length,
- PROT_READ,
- MAP_PRIVATE,
- fd,
- 0,
- /*low_4gb*/false,
- location.c_str(),
- error_msg));
- if (map == nullptr) {
- DCHECK(!error_msg->empty());
- return nullptr;
- }
- }
-
- if (map->Size() < sizeof(DexFile::Header)) {
- *error_msg = StringPrintf(
- "DexFile: failed to open dex file '%s' that is too short to have a header",
- location.c_str());
- return nullptr;
- }
-
- const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
-
- std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
- map->Size(),
- location,
- dex_header->checksum_,
- kNoOatDexFile,
- verify,
- verify_checksum,
- error_msg);
- if (dex_file != nullptr) {
- dex_file->mem_map_ = std::move(map);
- }
-
- return dex_file;
-}
-
-std::unique_ptr<const DexFile> DexFile::OpenOneDexFileFromZip(const ZipArchive& zip_archive,
- const char* entry_name,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg,
- ZipOpenErrorCode* error_code) {
- ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
- CHECK(!location.empty());
- std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
- if (zip_entry == nullptr) {
- *error_code = ZipOpenErrorCode::kEntryNotFound;
- return nullptr;
- }
- if (zip_entry->GetUncompressedLength() == 0) {
- *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
- *error_code = ZipOpenErrorCode::kDexFileError;
- return nullptr;
- }
-
- std::unique_ptr<MemMap> map;
- if (zip_entry->IsUncompressed()) {
- if (!zip_entry->IsAlignedTo(alignof(Header))) {
- // Do not mmap unaligned ZIP entries because
- // doing so would fail dex verification which requires 4 byte alignment.
- LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
- << "please zipalign to " << alignof(Header) << " bytes. "
- << "Falling back to extracting file.";
- } else {
- // Map uncompressed files within zip as file-backed to avoid a dirty copy.
- map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
- if (map == nullptr) {
- LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
- << "is your ZIP file corrupted? Falling back to extraction.";
- // Try again with Extraction which still has a chance of recovery.
- }
- }
- }
-
- if (map == nullptr) {
- // Default path for compressed ZIP entries,
- // and fallback for stored ZIP entries.
- map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
- }
-
- if (map == nullptr) {
- *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
- error_msg->c_str());
- *error_code = ZipOpenErrorCode::kExtractToMemoryError;
- return nullptr;
- }
- VerifyResult verify_result;
- std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
- map->Size(),
- location,
- zip_entry->GetCrc32(),
- kNoOatDexFile,
- /* verify */ true,
- verify_checksum,
- error_msg,
- &verify_result);
- if (dex_file == nullptr) {
- if (verify_result == VerifyResult::kVerifyNotAttempted) {
- *error_code = ZipOpenErrorCode::kDexFileError;
- } else {
- *error_code = ZipOpenErrorCode::kVerifyError;
- }
- return nullptr;
- }
- dex_file->mem_map_ = std::move(map);
- if (!dex_file->DisableWrite()) {
- *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
- *error_code = ZipOpenErrorCode::kMakeReadOnlyError;
- return nullptr;
- }
- CHECK(dex_file->IsReadOnly()) << location;
- if (verify_result != VerifyResult::kVerifySucceeded) {
- *error_code = ZipOpenErrorCode::kVerifyError;
- return nullptr;
- }
- *error_code = ZipOpenErrorCode::kNoError;
- return dex_file;
-}
-
-// Technically we do not have a limitation with respect to the number of dex files that can be in a
-// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
-// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
-// seems an excessive number.
-static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
-
-bool DexFile::OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) {
- ScopedTrace trace("Dex file open from Zip " + std::string(location));
- DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
- ZipOpenErrorCode error_code;
- std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
- kClassesDex,
- location,
- verify_checksum,
- error_msg,
- &error_code));
- if (dex_file.get() == nullptr) {
- return false;
- } else {
- // Had at least classes.dex.
- dex_files->push_back(std::move(dex_file));
-
- // Now try some more.
-
- // We could try to avoid std::string allocations by working on a char array directly. As we
- // do not expect a lot of iterations, this seems too involved and brittle.
-
- for (size_t i = 1; ; ++i) {
- std::string name = GetMultiDexClassesDexName(i);
- std::string fake_location = GetMultiDexLocation(i, location.c_str());
- std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
- name.c_str(),
- fake_location,
- verify_checksum,
- error_msg,
- &error_code));
- if (next_dex_file.get() == nullptr) {
- if (error_code != ZipOpenErrorCode::kEntryNotFound) {
- LOG(WARNING) << "Zip open failed: " << *error_msg;
- }
- break;
- } else {
- dex_files->push_back(std::move(next_dex_file));
- }
-
- if (i == kWarnOnManyDexFilesThreshold) {
- LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
- << " dex files. Please consider coalescing and shrinking the number to "
- " avoid runtime overhead.";
- }
-
- if (i == std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "Overflow in number of dex files!";
- break;
- }
- }
-
- return true;
- }
-}
-
-std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- VerifyResult* verify_result) {
- if (verify_result != nullptr) {
- *verify_result = VerifyResult::kVerifyNotAttempted;
- }
- std::unique_ptr<DexFile> dex_file(new DexFile(base,
- size,
- location,
- location_checksum,
- oat_dex_file));
- if (dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
- error_msg->c_str());
- return nullptr;
- }
- if (!dex_file->Init(error_msg)) {
- dex_file.reset();
- return nullptr;
- }
- if (verify && !DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- location.c_str(),
- verify_checksum,
- error_msg)) {
- if (verify_result != nullptr) {
- *verify_result = VerifyResult::kVerifyFailed;
- }
- return nullptr;
- }
- if (verify_result != nullptr) {
- *verify_result = VerifyResult::kVerifySucceeded;
- }
- return dex_file;
-}
-
DexFile::DexFile(const uint8_t* base,
size_t size,
const std::string& location,
@@ -569,7 +140,7 @@ bool DexFile::Init(std::string* error_msg) {
}
bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
- if (!IsMagicValid(header_->magic_)) {
+ if (!IsMagicValid()) {
std::ostringstream oss;
oss << "Unrecognized magic number in " << GetLocation() << ":"
<< " " << header_->magic_[0]
@@ -579,7 +150,7 @@ bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
*error_msg = oss.str();
return false;
}
- if (!IsVersionValid(header_->magic_)) {
+ if (!IsVersionValid()) {
std::ostringstream oss;
oss << "Unrecognized version number in " << GetLocation() << ":"
<< " " << header_->magic_[4]
@@ -619,22 +190,8 @@ void DexFile::InitializeSectionsFromMapList() {
}
}
-bool DexFile::IsMagicValid(const uint8_t* magic) {
- return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
-}
-
-bool DexFile::IsVersionValid(const uint8_t* magic) {
- const uint8_t* version = &magic[sizeof(kDexMagic)];
- for (uint32_t i = 0; i < kNumDexVersions; i++) {
- if (memcmp(version, kDexMagicVersions[i], kDexVersionLen) == 0) {
- return true;
- }
- }
- return false;
-}
-
uint32_t DexFile::Header::GetVersion() const {
- const char* version = reinterpret_cast<const char*>(&magic_[sizeof(kDexMagic)]);
+ const char* version = reinterpret_cast<const char*>(&magic_[kDexMagicSize]);
return atoi(version);
}
@@ -1218,41 +775,6 @@ bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) {
}
}
-bool DexFile::IsMultiDexLocation(const char* location) {
- return strrchr(location, kMultiDexSeparator) != nullptr;
-}
-
-std::string DexFile::GetMultiDexClassesDexName(size_t index) {
- if (index == 0) {
- return "classes.dex";
- } else {
- return StringPrintf("classes%zu.dex", index + 1);
- }
-}
-
-std::string DexFile::GetMultiDexLocation(size_t index, const char* dex_location) {
- if (index == 0) {
- return dex_location;
- } else {
- return StringPrintf("%s" kMultiDexSeparatorString "classes%zu.dex", dex_location, index + 1);
- }
-}
-
-std::string DexFile::GetDexCanonicalLocation(const char* dex_location) {
- CHECK_NE(dex_location, static_cast<const char*>(nullptr));
- std::string base_location = GetBaseLocation(dex_location);
- const char* suffix = dex_location + base_location.size();
- DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
- UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr));
- if (path != nullptr && path.get() != base_location) {
- return std::string(path.get()) + suffix;
- } else if (suffix[0] == 0) {
- return base_location;
- } else {
- return dex_location;
- }
-}
-
// Read a signed integer. "zwidth" is the zero-based byte count.
int32_t DexFile::ReadSignedInt(const uint8_t* ptr, int zwidth) {
int32_t val = 0;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 9c5fd10a36..5759684c55 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -39,34 +39,28 @@ class Signature;
class StringPiece;
class ZipArchive;
+// Dex file is the API that exposes native dex files (ordinary dex files) and CompactDex.
+// Originally, the dex file format used by ART was mostly the same as APKs. The only change was
+// quickened opcodes and layout optimizations.
+// Since ART needs to support both native dex files and CompactDex files, the DexFile interface
+// provides an abstraction to facilitate this.
class DexFile {
public:
+ // Number of bytes in the dex file magic.
+ static constexpr size_t kDexMagicSize = 4;
+ static constexpr size_t kDexVersionLen = 4;
+
// First Dex format version supporting default methods.
static const uint32_t kDefaultMethodsVersion = 37;
// First Dex format version enforcing class definition ordering rules.
static const uint32_t kClassDefinitionOrderEnforcedVersion = 37;
- static const uint8_t kDexMagic[];
- static constexpr size_t kNumDexVersions = 4;
- static constexpr size_t kDexVersionLen = 4;
- static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
-
static constexpr size_t kSha1DigestSize = 20;
static constexpr uint32_t kDexEndianConstant = 0x12345678;
- // name of the DexFile entry within a zip archive
- static const char* kClassesDex;
-
// The value of an invalid index.
static const uint16_t kDexNoIndex16 = 0xFFFF;
- // The separator character in MultiDex locations.
- static constexpr char kMultiDexSeparator = '!';
-
- // A string version of the previous. This is a define so that we can merge string literals in the
- // preprocessor.
- #define kMultiDexSeparatorString "!"
-
// Raw header_item.
struct Header {
uint8_t magic_[8];
@@ -433,57 +427,6 @@ class DexFile {
struct AnnotationValue;
- // Returns the checksums of a file for comparison with GetLocationChecksum().
- // For .dex files, this is the single header checksum.
- // For zip files, this is the zip entry CRC32 checksum for classes.dex and
- // each additional multidex entry classes2.dex, classes3.dex, etc.
- // Return true if the checksums could be found, false otherwise.
- static bool GetMultiDexChecksums(const char* filename,
- std::vector<uint32_t>* checksums,
- std::string* error_msg);
-
- // Check whether a location denotes a multidex dex file. This is a very simple check: returns
- // whether the string contains the separator character.
- static bool IsMultiDexLocation(const char* location);
-
- // Opens .dex file, backed by existing memory
- static std::unique_ptr<const DexFile> Open(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg);
-
- // Opens .dex file that has been memory-mapped by the caller.
- static std::unique_ptr<const DexFile> Open(const std::string& location,
- uint32_t location_checkum,
- std::unique_ptr<MemMap> mem_map,
- bool verify,
- bool verify_checksum,
- std::string* error_msg);
-
- // Opens all .dex files found in the file, guessing the container format based on file extension.
- static bool Open(const char* filename,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files);
-
- // Open a single dex file from an fd. This function closes the fd.
- static std::unique_ptr<const DexFile> OpenDex(int fd,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg);
-
- // Opens dex files from within a .jar, .zip, or .apk file
- static bool OpenZip(int fd,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files);
-
// Closes a .dex file.
virtual ~DexFile();
@@ -491,38 +434,6 @@ class DexFile {
return location_;
}
- // For normal dex files, location and base location coincide. If a dex file is part of a multidex
- // archive, the base location is the name of the originating jar/apk, stripped of any internal
- // classes*.dex path.
- static std::string GetBaseLocation(const char* location) {
- const char* pos = strrchr(location, kMultiDexSeparator);
- if (pos == nullptr) {
- return location;
- } else {
- return std::string(location, pos - location);
- }
- }
-
- static std::string GetBaseLocation(const std::string& location) {
- return GetBaseLocation(location.c_str());
- }
-
- // Returns the '!classes*.dex' part of the dex location. Returns an empty
- // string if there is no multidex suffix for the given location.
- // The kMultiDexSeparator is included in the returned suffix.
- static std::string GetMultiDexSuffix(const std::string& location) {
- size_t pos = location.rfind(kMultiDexSeparator);
- if (pos == std::string::npos) {
- return "";
- } else {
- return location.substr(pos);
- }
- }
-
- std::string GetBaseLocation() const {
- return GetBaseLocation(location_);
- }
-
// For DexFiles directly from .dex files, this is the checksum from the DexFile::Header.
// For DexFiles opened from a zip files, this will be the ZipEntry CRC32 of classes.dex.
uint32_t GetLocationChecksum() const {
@@ -540,10 +451,10 @@ class DexFile {
}
// Returns true if the byte string points to the magic value.
- static bool IsMagicValid(const uint8_t* magic);
+ virtual bool IsMagicValid() const = 0;
// Returns true if the byte string after the magic is the correct value.
- static bool IsVersionValid(const uint8_t* magic);
+ virtual bool IsVersionValid() const = 0;
// Returns the number of string identifiers in the .dex file.
size_t NumStringIds() const {
@@ -733,11 +644,10 @@ class DexFile {
const TypeList* GetInterfacesList(const ClassDef& class_def) const {
if (class_def.interfaces_off_ == 0) {
- return nullptr;
- } else {
- const uint8_t* addr = begin_ + class_def.interfaces_off_;
- return reinterpret_cast<const TypeList*>(addr);
+ return nullptr;
}
+ const uint8_t* addr = begin_ + class_def.interfaces_off_;
+ return reinterpret_cast<const TypeList*>(addr);
}
uint32_t NumMethodHandles() const {
@@ -760,11 +670,7 @@ class DexFile {
// Returns a pointer to the raw memory mapped class_data_item
const uint8_t* GetClassData(const ClassDef& class_def) const {
- if (class_def.class_data_off_ == 0) {
- return nullptr;
- } else {
- return begin_ + class_def.class_data_off_;
- }
+ return (class_def.class_data_off_ == 0) ? nullptr : begin_ + class_def.class_data_off_;
}
//
@@ -772,10 +678,9 @@ class DexFile {
DCHECK_LT(code_off, size_) << "Code item offset larger then maximum allowed offset";
if (code_off == 0) {
return nullptr; // native or abstract method
- } else {
- const uint8_t* addr = begin_ + code_off;
- return reinterpret_cast<const CodeItem*>(addr);
}
+ const uint8_t* addr = begin_ + code_off;
+ return reinterpret_cast<const CodeItem*>(addr);
}
const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const;
@@ -820,20 +725,13 @@ class DexFile {
const char* GetShorty(uint32_t proto_idx) const;
const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
- if (proto_id.parameters_off_ == 0) {
- return nullptr;
- } else {
- const uint8_t* addr = begin_ + proto_id.parameters_off_;
- return reinterpret_cast<const TypeList*>(addr);
- }
+ return (proto_id.parameters_off_ == 0)
+ ? nullptr
+ : reinterpret_cast<const TypeList*>(begin_ + proto_id.parameters_off_);
}
const uint8_t* GetEncodedStaticFieldValuesArray(const ClassDef& class_def) const {
- if (class_def.static_values_off_ == 0) {
- return 0;
- } else {
- return begin_ + class_def.static_values_off_;
- }
+ return (class_def.static_values_off_ == 0) ? 0 : begin_ + class_def.static_values_off_;
}
const uint8_t* GetCallSiteEncodedValuesArray(const CallSiteIdItem& call_site_id) const {
@@ -860,27 +758,18 @@ class DexFile {
// Check that the offset is in bounds.
// Note that although the specification says that 0 should be used if there
// is no debug information, some applications incorrectly use 0xFFFFFFFF.
- if (code_item->debug_info_off_ == 0 || code_item->debug_info_off_ >= size_) {
- return nullptr;
- } else {
- return begin_ + code_item->debug_info_off_;
- }
+ const uint32_t debug_info_off = code_item->debug_info_off_;
+ return (debug_info_off == 0 || debug_info_off >= size_) ? nullptr : begin_ + debug_info_off;
}
struct PositionInfo {
- PositionInfo()
- : address_(0),
- line_(0),
- source_file_(nullptr),
- prologue_end_(false),
- epilogue_begin_(false) {
- }
+ PositionInfo() = default;
- uint32_t address_; // In 16-bit code units.
- uint32_t line_; // Source code line number starting at 1.
- const char* source_file_; // nullptr if the file from ClassDef still applies.
- bool prologue_end_;
- bool epilogue_begin_;
+ uint32_t address_ = 0; // In 16-bit code units.
+ uint32_t line_ = 0; // Source code line number starting at 1.
+ const char* source_file_ = nullptr; // nullptr if the file from ClassDef still applies.
+ bool prologue_end_ = false;
+ bool epilogue_begin_ = false;
};
// Callback for "new position table entry".
@@ -888,23 +777,15 @@ class DexFile {
typedef bool (*DexDebugNewPositionCb)(void* context, const PositionInfo& entry);
struct LocalInfo {
- LocalInfo()
- : name_(nullptr),
- descriptor_(nullptr),
- signature_(nullptr),
- start_address_(0),
- end_address_(0),
- reg_(0),
- is_live_(false) {
- }
-
- const char* name_; // E.g., list. It can be nullptr if unknown.
- const char* descriptor_; // E.g., Ljava/util/LinkedList;
- const char* signature_; // E.g., java.util.LinkedList<java.lang.Integer>
- uint32_t start_address_; // PC location where the local is first defined.
- uint32_t end_address_; // PC location where the local is no longer defined.
- uint16_t reg_; // Dex register which stores the values.
- bool is_live_; // Is the local defined and live.
+ LocalInfo() = default;
+
+ const char* name_ = nullptr; // E.g., list. It can be nullptr if unknown.
+ const char* descriptor_ = nullptr; // E.g., Ljava/util/LinkedList;
+ const char* signature_ = nullptr; // E.g., java.util.LinkedList<java.lang.Integer>
+ uint32_t start_address_ = 0; // PC location where the local is first defined.
+ uint32_t end_address_ = 0; // PC location where the local is no longer defined.
+ uint16_t reg_ = 0; // Dex register which stores the values.
+ bool is_live_ = false; // Is the local defined and live.
};
// Callback for "new locals table entry".
@@ -913,98 +794,82 @@ class DexFile {
static bool LineNumForPcCb(void* context, const PositionInfo& entry);
const AnnotationsDirectoryItem* GetAnnotationsDirectory(const ClassDef& class_def) const {
- if (class_def.annotations_off_ == 0) {
- return nullptr;
- } else {
- return reinterpret_cast<const AnnotationsDirectoryItem*>(begin_ + class_def.annotations_off_);
- }
+ return (class_def.annotations_off_ == 0)
+ ? nullptr
+ : reinterpret_cast<const AnnotationsDirectoryItem*>(begin_ + class_def.annotations_off_);
}
const AnnotationSetItem* GetClassAnnotationSet(const AnnotationsDirectoryItem* anno_dir) const {
- if (anno_dir->class_annotations_off_ == 0) {
- return nullptr;
- } else {
- return reinterpret_cast<const AnnotationSetItem*>(begin_ + anno_dir->class_annotations_off_);
- }
+ return (anno_dir->class_annotations_off_ == 0)
+ ? nullptr
+ : reinterpret_cast<const AnnotationSetItem*>(begin_ + anno_dir->class_annotations_off_);
}
const FieldAnnotationsItem* GetFieldAnnotations(const AnnotationsDirectoryItem* anno_dir) const {
- if (anno_dir->fields_size_ == 0) {
- return nullptr;
- } else {
- return reinterpret_cast<const FieldAnnotationsItem*>(&anno_dir[1]);
- }
+ return (anno_dir->fields_size_ == 0)
+ ? nullptr
+ : reinterpret_cast<const FieldAnnotationsItem*>(&anno_dir[1]);
}
const MethodAnnotationsItem* GetMethodAnnotations(const AnnotationsDirectoryItem* anno_dir)
const {
if (anno_dir->methods_size_ == 0) {
return nullptr;
- } else {
- // Skip past the header and field annotations.
- const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]);
- addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem);
- return reinterpret_cast<const MethodAnnotationsItem*>(addr);
}
+ // Skip past the header and field annotations.
+ const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]);
+ addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem);
+ return reinterpret_cast<const MethodAnnotationsItem*>(addr);
}
const ParameterAnnotationsItem* GetParameterAnnotations(const AnnotationsDirectoryItem* anno_dir)
const {
if (anno_dir->parameters_size_ == 0) {
return nullptr;
- } else {
- // Skip past the header, field annotations, and method annotations.
- const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]);
- addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem);
- addr += anno_dir->methods_size_ * sizeof(MethodAnnotationsItem);
- return reinterpret_cast<const ParameterAnnotationsItem*>(addr);
}
+ // Skip past the header, field annotations, and method annotations.
+ const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]);
+ addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem);
+ addr += anno_dir->methods_size_ * sizeof(MethodAnnotationsItem);
+ return reinterpret_cast<const ParameterAnnotationsItem*>(addr);
}
const AnnotationSetItem* GetFieldAnnotationSetItem(const FieldAnnotationsItem& anno_item) const {
uint32_t offset = anno_item.annotations_off_;
- if (offset == 0) {
- return nullptr;
- } else {
- return reinterpret_cast<const AnnotationSetItem*>(begin_ + offset);
- }
+ return (offset == 0)
+ ? nullptr
+ : reinterpret_cast<const AnnotationSetItem*>(begin_ + offset);
}
const AnnotationSetItem* GetMethodAnnotationSetItem(const MethodAnnotationsItem& anno_item)
const {
uint32_t offset = anno_item.annotations_off_;
- if (offset == 0) {
- return nullptr;
- } else {
- return reinterpret_cast<const AnnotationSetItem*>(begin_ + offset);
- }
+ return (offset == 0)
+ ? nullptr
+ : reinterpret_cast<const AnnotationSetItem*>(begin_ + offset);
}
const AnnotationSetRefList* GetParameterAnnotationSetRefList(
const ParameterAnnotationsItem* anno_item) const {
uint32_t offset = anno_item->annotations_off_;
- if (offset == 0) {
- return nullptr;
- }
- return reinterpret_cast<const AnnotationSetRefList*>(begin_ + offset);
+ return (offset == 0)
+ ? nullptr
+ : reinterpret_cast<const AnnotationSetRefList*>(begin_ + offset);
}
const AnnotationItem* GetAnnotationItem(const AnnotationSetItem* set_item, uint32_t index) const {
DCHECK_LE(index, set_item->size_);
uint32_t offset = set_item->entries_[index];
- if (offset == 0) {
- return nullptr;
- } else {
- return reinterpret_cast<const AnnotationItem*>(begin_ + offset);
- }
+ return (offset == 0)
+ ? nullptr
+ : reinterpret_cast<const AnnotationItem*>(begin_ + offset);
}
const AnnotationSetItem* GetSetRefItemItem(const AnnotationSetRefItem* anno_item) const {
uint32_t offset = anno_item->annotations_off_;
- if (offset == 0) {
- return nullptr;
- }
- return reinterpret_cast<const AnnotationSetItem*>(begin_ + offset);
+ return (offset == 0)
+ ? nullptr
+ : reinterpret_cast<const AnnotationSetItem*>(begin_ + offset);
}
// Debug info opcodes and constants
@@ -1065,29 +930,6 @@ class DexFile {
return size_;
}
- // Return the name of the index-th classes.dex in a multidex zip file. This is classes.dex for
- // index == 0, and classes{index + 1}.dex else.
- static std::string GetMultiDexClassesDexName(size_t index);
-
- // Return the (possibly synthetic) dex location for a multidex entry. This is dex_location for
- // index == 0, and dex_location + multi-dex-separator + GetMultiDexClassesDexName(index) else.
- static std::string GetMultiDexLocation(size_t index, const char* dex_location);
-
- // Returns the canonical form of the given dex location.
- //
- // There are different flavors of "dex locations" as follows:
- // the file name of a dex file:
- // The actual file path that the dex file has on disk.
- // dex_location:
- // This acts as a key for the class linker to know which dex file to load.
- // It may correspond to either an old odex file or a particular dex file
- // inside an oat file. In the first case it will also match the file name
- // of the dex file. In the second case (oat) it will include the file name
- // and possibly some multidex annotation to uniquely identify it.
- // canonical_dex_location:
- // the dex_location where it's file name part has been made canonical.
- static std::string GetDexCanonicalLocation(const char* dex_location);
-
const OatDexFile* GetOatDexFile() const {
return oat_dex_file_;
}
@@ -1113,64 +955,7 @@ class DexFile {
// Returns a human-readable form of the type at an index.
std::string PrettyType(dex::TypeIndex type_idx) const;
- private:
- static std::unique_ptr<const DexFile> OpenFile(int fd,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg);
-
- enum class ZipOpenErrorCode { // private
- kNoError,
- kEntryNotFound,
- kExtractToMemoryError,
- kDexFileError,
- kMakeReadOnlyError,
- kVerifyError
- };
-
- // Open all classesXXX.dex files from a zip archive.
- static bool OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files);
-
- // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
- // return.
- static std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const ZipArchive& zip_archive,
- const char* entry_name,
- const std::string& location,
- bool verify_checksum,
- std::string* error_msg,
- ZipOpenErrorCode* error_code);
-
- enum class VerifyResult { // private
- kVerifyNotAttempted,
- kVerifySucceeded,
- kVerifyFailed
- };
-
- static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- VerifyResult* verify_result = nullptr);
-
-
- // Opens a .dex file at the given address, optionally backed by a MemMap
- static std::unique_ptr<const DexFile> OpenMemory(const uint8_t* dex_file,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- std::unique_ptr<MemMap> mem_map,
- const OatDexFile* oat_dex_file,
- std::string* error_msg);
-
+ protected:
DexFile(const uint8_t* base,
size_t size,
const std::string& location,
@@ -1241,9 +1026,9 @@ class DexFile {
// null.
mutable const OatDexFile* oat_dex_file_;
+ friend class DexFileLoader;
friend class DexFileVerifierTest;
friend class OatWriter;
- ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor
};
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
@@ -1252,7 +1037,7 @@ std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
class DexFileParameterIterator {
public:
DexFileParameterIterator(const DexFile& dex_file, const DexFile::ProtoId& proto_id)
- : dex_file_(dex_file), size_(0), pos_(0) {
+ : dex_file_(dex_file) {
type_list_ = dex_file_.GetProtoParameters(proto_id);
if (type_list_ != nullptr) {
size_ = type_list_->Size();
@@ -1269,9 +1054,9 @@ class DexFileParameterIterator {
}
private:
const DexFile& dex_file_;
- const DexFile::TypeList* type_list_;
- uint32_t size_;
- uint32_t pos_;
+ const DexFile::TypeList* type_list_ = nullptr;
+ uint32_t size_ = 0;
+ uint32_t pos_ = 0;
DISALLOW_IMPLICIT_CONSTRUCTORS(DexFileParameterIterator);
};
@@ -1298,13 +1083,12 @@ class Signature : public ValueObject {
Signature(const DexFile* dex, const DexFile::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) {
}
- Signature() : dex_file_(nullptr), proto_id_(nullptr) {
- }
+ Signature() = default;
friend class DexFile;
- const DexFile* const dex_file_;
- const DexFile::ProtoId* const proto_id_;
+ const DexFile* const dex_file_ = nullptr;
+ const DexFile::ProtoId* const proto_id_ = nullptr;
};
std::ostream& operator<<(std::ostream& os, const Signature& sig);
@@ -1583,44 +1367,44 @@ class CallSiteArrayValueIterator : public EncodedArrayValueIterator {
std::ostream& operator<<(std::ostream& os, const CallSiteArrayValueIterator::ValueType& code);
class CatchHandlerIterator {
- public:
- CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address);
+ public:
+ CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address);
- CatchHandlerIterator(const DexFile::CodeItem& code_item,
- const DexFile::TryItem& try_item);
+ CatchHandlerIterator(const DexFile::CodeItem& code_item,
+ const DexFile::TryItem& try_item);
- explicit CatchHandlerIterator(const uint8_t* handler_data) {
- Init(handler_data);
- }
+ explicit CatchHandlerIterator(const uint8_t* handler_data) {
+ Init(handler_data);
+ }
- dex::TypeIndex GetHandlerTypeIndex() const {
- return handler_.type_idx_;
- }
- uint32_t GetHandlerAddress() const {
- return handler_.address_;
- }
- void Next();
- bool HasNext() const {
- return remaining_count_ != -1 || catch_all_;
- }
- // End of this set of catch blocks, convenience method to locate next set of catch blocks
- const uint8_t* EndDataPointer() const {
- CHECK(!HasNext());
- return current_data_;
- }
+ dex::TypeIndex GetHandlerTypeIndex() const {
+ return handler_.type_idx_;
+ }
+ uint32_t GetHandlerAddress() const {
+ return handler_.address_;
+ }
+ void Next();
+ bool HasNext() const {
+ return remaining_count_ != -1 || catch_all_;
+ }
+ // End of this set of catch blocks, convenience method to locate next set of catch blocks
+ const uint8_t* EndDataPointer() const {
+ CHECK(!HasNext());
+ return current_data_;
+ }
- private:
- void Init(const DexFile::CodeItem& code_item, int32_t offset);
- void Init(const uint8_t* handler_data);
-
- struct CatchHandlerItem {
- dex::TypeIndex type_idx_; // type index of the caught exception type
- uint32_t address_; // handler address
- } handler_;
- const uint8_t* current_data_; // the current handler in dex file.
- int32_t remaining_count_; // number of handlers not read.
- bool catch_all_; // is there a handler that will catch all exceptions in case
- // that all typed handler does not match.
+ private:
+ void Init(const DexFile::CodeItem& code_item, int32_t offset);
+ void Init(const uint8_t* handler_data);
+
+ struct CatchHandlerItem {
+ dex::TypeIndex type_idx_; // type index of the caught exception type
+ uint32_t address_; // handler address
+ } handler_;
+ const uint8_t* current_data_; // the current handler in dex file.
+ int32_t remaining_count_; // number of handlers not read.
+ bool catch_all_; // is there a handler that will catch all exceptions in case
+ // that all typed handler does not match.
};
} // namespace art
diff --git a/runtime/dex_file_loader.cc b/runtime/dex_file_loader.cc
new file mode 100644
index 0000000000..3ccb755f58
--- /dev/null
+++ b/runtime/dex_file_loader.cc
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "dex_file_loader.h"
+
+#include <sys/mman.h> // For the PROT_* and MAP_* constants.
+#include <sys/stat.h>
+
+#include "android-base/stringprintf.h"
+
+#include "base/file_magic.h"
+#include "base/stl_util.h"
+#include "base/systrace.h"
+#include "base/unix_file/fd_file.h"
+#include "dex_file.h"
+#include "dex_file_verifier.h"
+#include "native_dex_file.h"
+#include "zip_archive.h"
+
+namespace art {
+
+using android::base::StringPrintf;
+
+static constexpr OatDexFile* kNoOatDexFile = nullptr;
+
+
+bool DexFileLoader::IsValidMagic(uint32_t magic) {
+ return IsValidMagic(reinterpret_cast<uint8_t*>(&magic));
+}
+
+bool DexFileLoader::IsValidMagic(const uint8_t* magic) {
+ return NativeDexFile::IsMagicValid(magic);
+}
+
+bool DexFileLoader::GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg) {
+ CHECK(checksums != nullptr);
+ uint32_t magic;
+
+ File fd = OpenAndReadMagic(filename, &magic, error_msg);
+ if (fd.Fd() == -1) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ if (IsZipMagic(magic)) {
+ std::unique_ptr<ZipArchive> zip_archive(
+ ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
+ if (zip_archive.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
+ error_msg->c_str());
+ return false;
+ }
+
+ uint32_t i = 0;
+ std::string zip_entry_name = GetMultiDexClassesDexName(i++);
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
+ if (zip_entry.get() == nullptr) {
+ *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
+ zip_entry_name.c_str(), error_msg->c_str());
+ return false;
+ }
+
+ do {
+ checksums->push_back(zip_entry->GetCrc32());
+ zip_entry_name = GetMultiDexClassesDexName(i++);
+ zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
+ } while (zip_entry.get() != nullptr);
+ return true;
+ }
+ if (IsValidMagic(magic)) {
+ std::unique_ptr<const DexFile> dex_file(
+ OpenFile(fd.Release(), filename, false, false, error_msg));
+ if (dex_file == nullptr) {
+ return false;
+ }
+ checksums->push_back(dex_file->GetHeader().checksum_);
+ return true;
+ }
+ *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
+ return false;
+}
+
+bool DexFileLoader::IsMultiDexLocation(const char* location) {
+ return strrchr(location, kMultiDexSeparator) != nullptr;
+}
+
+std::string DexFileLoader::GetMultiDexClassesDexName(size_t index) {
+ return (index == 0) ? "classes.dex" : StringPrintf("classes%zu.dex", index + 1);
+}
+
+std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) {
+ return (index == 0)
+ ? dex_location
+ : StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1);
+}
+
+std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) {
+ CHECK_NE(dex_location, static_cast<const char*>(nullptr));
+ std::string base_location = GetBaseLocation(dex_location);
+ const char* suffix = dex_location + base_location.size();
+ DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
+ UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr));
+ if (path != nullptr && path.get() != base_location) {
+ return std::string(path.get()) + suffix;
+ } else if (suffix[0] == 0) {
+ return base_location;
+ } else {
+ return dex_location;
+ }
+}
+
+std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg) {
+ ScopedTrace trace(std::string("Open dex file from RAM ") + location);
+ return OpenCommon(base,
+ size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ verify,
+ verify_checksum,
+ error_msg);
+}
+
+std::unique_ptr<const DexFile> DexFileLoader::Open(const std::string& location,
+ uint32_t location_checksum,
+ std::unique_ptr<MemMap> map,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg) {
+ ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
+ CHECK(map.get() != nullptr);
+
+ if (map->Size() < sizeof(DexFile::Header)) {
+ *error_msg = StringPrintf(
+ "DexFile: failed to open dex file '%s' that is too short to have a header",
+ location.c_str());
+ return nullptr;
+ }
+
+ std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
+ map->Size(),
+ location,
+ location_checksum,
+ kNoOatDexFile,
+ verify,
+ verify_checksum,
+ error_msg);
+ if (dex_file != nullptr) {
+ dex_file->mem_map_ = std::move(map);
+ }
+ return dex_file;
+}
+
+bool DexFileLoader::Open(const char* filename,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ ScopedTrace trace(std::string("Open dex file ") + std::string(location));
+ DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
+ uint32_t magic;
+ File fd = OpenAndReadMagic(filename, &magic, error_msg);
+ if (fd.Fd() == -1) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ if (IsZipMagic(magic)) {
+ return OpenZip(fd.Release(), location, verify_checksum, error_msg, dex_files);
+ }
+ if (IsValidMagic(magic)) {
+ std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(),
+ location,
+ /* verify */ true,
+ verify_checksum,
+ error_msg));
+ if (dex_file.get() != nullptr) {
+ dex_files->push_back(std::move(dex_file));
+ return true;
+ } else {
+ return false;
+ }
+ }
+ *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
+ return false;
+}
+
+std::unique_ptr<const DexFile> DexFileLoader::OpenDex(int fd,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg) {
+ ScopedTrace trace("Open dex file " + std::string(location));
+ return OpenFile(fd, location, true /* verify */, verify_checksum, error_msg);
+}
+
+bool DexFileLoader::OpenZip(int fd,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ ScopedTrace trace("Dex file open Zip " + std::string(location));
+ DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr";
+ std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
+ if (zip_archive.get() == nullptr) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ return OpenAllDexFilesFromZip(*zip_archive, location, verify_checksum, error_msg, dex_files);
+}
+
+std::unique_ptr<const DexFile> DexFileLoader::OpenFile(int fd,
+ const std::string& location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg) {
+ ScopedTrace trace(std::string("Open dex file ") + std::string(location));
+ CHECK(!location.empty());
+ std::unique_ptr<MemMap> map;
+ {
+ File delayed_close(fd, /* check_usage */ false);
+ struct stat sbuf;
+ memset(&sbuf, 0, sizeof(sbuf));
+ if (fstat(fd, &sbuf) == -1) {
+ *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
+ strerror(errno));
+ return nullptr;
+ }
+ if (S_ISDIR(sbuf.st_mode)) {
+ *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
+ return nullptr;
+ }
+ size_t length = sbuf.st_size;
+ map.reset(MemMap::MapFile(length,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+ 0,
+ /*low_4gb*/false,
+ location.c_str(),
+ error_msg));
+ if (map == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+ }
+
+ if (map->Size() < sizeof(DexFile::Header)) {
+ *error_msg = StringPrintf(
+ "DexFile: failed to open dex file '%s' that is too short to have a header",
+ location.c_str());
+ return nullptr;
+ }
+
+ const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(map->Begin());
+
+ std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
+ map->Size(),
+ location,
+ dex_header->checksum_,
+ kNoOatDexFile,
+ verify,
+ verify_checksum,
+ error_msg);
+ if (dex_file != nullptr) {
+ dex_file->mem_map_ = std::move(map);
+ }
+
+ return dex_file;
+}
+
+std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
+ const ZipArchive& zip_archive,
+ const char* entry_name,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
+ ZipOpenErrorCode* error_code) {
+ ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
+ CHECK(!location.empty());
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
+ if (zip_entry == nullptr) {
+ *error_code = ZipOpenErrorCode::kEntryNotFound;
+ return nullptr;
+ }
+ if (zip_entry->GetUncompressedLength() == 0) {
+ *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
+ *error_code = ZipOpenErrorCode::kDexFileError;
+ return nullptr;
+ }
+
+ std::unique_ptr<MemMap> map;
+ if (zip_entry->IsUncompressed()) {
+ if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) {
+ // Do not mmap unaligned ZIP entries because
+ // doing so would fail dex verification which requires 4 byte alignment.
+ LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
+ << "please zipalign to " << alignof(DexFile::Header) << " bytes. "
+ << "Falling back to extracting file.";
+ } else {
+ // Map uncompressed files within zip as file-backed to avoid a dirty copy.
+ map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
+ if (map == nullptr) {
+ LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
+ << "is your ZIP file corrupted? Falling back to extraction.";
+ // Try again with Extraction which still has a chance of recovery.
+ }
+ }
+ }
+
+ if (map == nullptr) {
+ // Default path for compressed ZIP entries,
+ // and fallback for stored ZIP entries.
+ map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
+ }
+
+ if (map == nullptr) {
+ *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
+ error_msg->c_str());
+ *error_code = ZipOpenErrorCode::kExtractToMemoryError;
+ return nullptr;
+ }
+ VerifyResult verify_result;
+ std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
+ map->Size(),
+ location,
+ zip_entry->GetCrc32(),
+ kNoOatDexFile,
+ /* verify */ true,
+ verify_checksum,
+ error_msg,
+ &verify_result);
+ if (dex_file == nullptr) {
+ if (verify_result == VerifyResult::kVerifyNotAttempted) {
+ *error_code = ZipOpenErrorCode::kDexFileError;
+ } else {
+ *error_code = ZipOpenErrorCode::kVerifyError;
+ }
+ return nullptr;
+ }
+ dex_file->mem_map_ = std::move(map);
+ if (!dex_file->DisableWrite()) {
+ *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
+ *error_code = ZipOpenErrorCode::kMakeReadOnlyError;
+ return nullptr;
+ }
+ CHECK(dex_file->IsReadOnly()) << location;
+ if (verify_result != VerifyResult::kVerifySucceeded) {
+ *error_code = ZipOpenErrorCode::kVerifyError;
+ return nullptr;
+ }
+ *error_code = ZipOpenErrorCode::kNoError;
+ return dex_file;
+}
+
+// Technically we do not have a limitation with respect to the number of dex files that can be in a
+// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
+// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
+// seems an excessive number.
+static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
+
+bool DexFileLoader::OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ ScopedTrace trace("Dex file open from Zip " + std::string(location));
+ DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
+ ZipOpenErrorCode error_code;
+ std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
+ kClassesDex,
+ location,
+ verify_checksum,
+ error_msg,
+ &error_code));
+ if (dex_file.get() == nullptr) {
+ return false;
+ } else {
+ // Had at least classes.dex.
+ dex_files->push_back(std::move(dex_file));
+
+ // Now try some more.
+
+ // We could try to avoid std::string allocations by working on a char array directly. As we
+ // do not expect a lot of iterations, this seems too involved and brittle.
+
+ for (size_t i = 1; ; ++i) {
+ std::string name = GetMultiDexClassesDexName(i);
+ std::string fake_location = GetMultiDexLocation(i, location.c_str());
+ std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
+ name.c_str(),
+ fake_location,
+ verify_checksum,
+ error_msg,
+ &error_code));
+ if (next_dex_file.get() == nullptr) {
+ if (error_code != ZipOpenErrorCode::kEntryNotFound) {
+ LOG(WARNING) << "Zip open failed: " << *error_msg;
+ }
+ break;
+ } else {
+ dex_files->push_back(std::move(next_dex_file));
+ }
+
+ if (i == kWarnOnManyDexFilesThreshold) {
+ LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
+ << " dex files. Please consider coalescing and shrinking the number to "
+ " avoid runtime overhead.";
+ }
+
+ if (i == std::numeric_limits<size_t>::max()) {
+ LOG(ERROR) << "Overflow in number of dex files!";
+ break;
+ }
+ }
+
+ return true;
+ }
+}
+
+std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ VerifyResult* verify_result) {
+ if (verify_result != nullptr) {
+ *verify_result = VerifyResult::kVerifyNotAttempted;
+ }
+ std::unique_ptr<DexFile> dex_file;
+ if (NativeDexFile::IsMagicValid(base)) {
+ dex_file.reset(new NativeDexFile(base, size, location, location_checksum, oat_dex_file));
+ } else {
+ return nullptr;
+ }
+ if (dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
+ error_msg->c_str());
+ return nullptr;
+ }
+ if (!dex_file->Init(error_msg)) {
+ dex_file.reset();
+ return nullptr;
+ }
+ if (verify && !DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ location.c_str(),
+ verify_checksum,
+ error_msg)) {
+ if (verify_result != nullptr) {
+ *verify_result = VerifyResult::kVerifyFailed;
+ }
+ return nullptr;
+ }
+ if (verify_result != nullptr) {
+ *verify_result = VerifyResult::kVerifySucceeded;
+ }
+ return dex_file;
+}
+
+} // namespace art
diff --git a/runtime/dex_file_loader.h b/runtime/dex_file_loader.h
new file mode 100644
index 0000000000..61b5c71726
--- /dev/null
+++ b/runtime/dex_file_loader.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_DEX_FILE_LOADER_H_
+#define ART_RUNTIME_DEX_FILE_LOADER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace art {
+
+class DexFile;
+class MemMap;
+class OatDexFile;
+class ZipArchive;
+
+// Class that is used to open dex files and deal with corresponding multidex and location logic.
+class DexFileLoader {
+ public:
+ // name of the DexFile entry within a zip archive
+ static constexpr const char* kClassesDex = "classes.dex";
+
+ // The separator character in MultiDex locations.
+ static constexpr char kMultiDexSeparator = '!';
+
+ // Return true if the magic is valid for dex or cdex.
+ static bool IsValidMagic(uint32_t magic);
+ static bool IsValidMagic(const uint8_t* magic);
+
+ // Returns the checksums of a file for comparison with GetLocationChecksum().
+ // For .dex files, this is the single header checksum.
+ // For zip files, this is the zip entry CRC32 checksum for classes.dex and
+ // each additional multidex entry classes2.dex, classes3.dex, etc.
+ // Return true if the checksums could be found, false otherwise.
+ static bool GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg);
+
+ // Check whether a location denotes a multidex dex file. This is a very simple check: returns
+ // whether the string contains the separator character.
+ static bool IsMultiDexLocation(const char* location);
+
+ // Opens .dex file, backed by existing memory
+ static std::unique_ptr<const DexFile> Open(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg);
+
+ // Opens .dex file that has been memory-mapped by the caller.
+ static std::unique_ptr<const DexFile> Open(const std::string& location,
+ uint32_t location_checkum,
+ std::unique_ptr<MemMap> mem_map,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg);
+
+ // Opens all .dex files found in the file, guessing the container format based on file extension.
+ static bool Open(const char* filename,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
+
+ // Open a single dex file from an fd. This function closes the fd.
+ static std::unique_ptr<const DexFile> OpenDex(int fd,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg);
+
+ // Opens dex files from within a .jar, .zip, or .apk file
+ static bool OpenZip(int fd,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
+
+ // Return the name of the index-th classes.dex in a multidex zip file. This is classes.dex for
+ // index == 0, and classes{index + 1}.dex else.
+ static std::string GetMultiDexClassesDexName(size_t index);
+
+ // Return the (possibly synthetic) dex location for a multidex entry. This is dex_location for
+ // index == 0, and dex_location + multi-dex-separator + GetMultiDexClassesDexName(index) else.
+ static std::string GetMultiDexLocation(size_t index, const char* dex_location);
+
+ // Returns the canonical form of the given dex location.
+ //
+ // There are different flavors of "dex locations" as follows:
+ // the file name of a dex file:
+ // The actual file path that the dex file has on disk.
+ // dex_location:
+ // This acts as a key for the class linker to know which dex file to load.
+ // It may correspond to either an old odex file or a particular dex file
+ // inside an oat file. In the first case it will also match the file name
+ // of the dex file. In the second case (oat) it will include the file name
+ // and possibly some multidex annotation to uniquely identify it.
+ // canonical_dex_location:
+ // the dex_location where it's file name part has been made canonical.
+ static std::string GetDexCanonicalLocation(const char* dex_location);
+
+ // For normal dex files, location and base location coincide. If a dex file is part of a multidex
+ // archive, the base location is the name of the originating jar/apk, stripped of any internal
+ // classes*.dex path.
+ static std::string GetBaseLocation(const char* location) {
+ const char* pos = strrchr(location, kMultiDexSeparator);
+ return (pos == nullptr) ? location : std::string(location, pos - location);
+ }
+
+ static std::string GetBaseLocation(const std::string& location) {
+ return GetBaseLocation(location.c_str());
+ }
+
+ // Returns the '!classes*.dex' part of the dex location. Returns an empty
+ // string if there is no multidex suffix for the given location.
+ // The kMultiDexSeparator is included in the returned suffix.
+ static std::string GetMultiDexSuffix(const std::string& location) {
+ size_t pos = location.rfind(kMultiDexSeparator);
+ return (pos == std::string::npos) ? std::string() : location.substr(pos);
+ }
+
+ private:
+ static std::unique_ptr<const DexFile> OpenFile(int fd,
+ const std::string& location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg);
+
+ enum class ZipOpenErrorCode {
+ kNoError,
+ kEntryNotFound,
+ kExtractToMemoryError,
+ kDexFileError,
+ kMakeReadOnlyError,
+ kVerifyError
+ };
+
+ // Open all classesXXX.dex files from a zip archive.
+ static bool OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
+
+ // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
+ // return.
+ static std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const ZipArchive& zip_archive,
+ const char* entry_name,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
+ ZipOpenErrorCode* error_code);
+
+ enum class VerifyResult { // private
+ kVerifyNotAttempted,
+ kVerifySucceeded,
+ kVerifyFailed
+ };
+
+ static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ VerifyResult* verify_result = nullptr);
+
+
+ // Opens a .dex file at the given address, optionally backed by a MemMap
+ static std::unique_ptr<const DexFile> OpenMemory(const uint8_t* dex_file,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ std::unique_ptr<MemMap> mem_map,
+ const OatDexFile* oat_dex_file,
+ std::string* error_msg);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_DEX_FILE_LOADER_H_
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 67cd42803d..b3011379c6 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -24,6 +24,7 @@
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "mem_map.h"
#include "os.h"
#include "scoped_thread_state_change-inl.h"
@@ -235,7 +236,7 @@ static bool OpenDexFilesBase64(const char* base64,
ScopedObjectAccess soa(Thread::Current());
static constexpr bool kVerifyChecksum = true;
std::vector<std::unique_ptr<const DexFile>> tmp;
- bool success = DexFile::Open(location, location, kVerifyChecksum, error_msg, &tmp);
+ bool success = DexFileLoader::Open(location, location, kVerifyChecksum, error_msg, &tmp);
if (success) {
for (std::unique_ptr<const DexFile>& dex_file : tmp) {
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
@@ -274,12 +275,12 @@ static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base
/* reuse */ false,
&error_message));
memcpy(region->Begin(), dex_bytes.data(), dex_bytes.size());
- std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
- location_checksum,
- std::move(region),
- /* verify */ true,
- /* verify_checksum */ true,
- &error_message));
+ std::unique_ptr<const DexFile> dex_file(DexFileLoader::Open(location,
+ location_checksum,
+ std::move(region),
+ /* verify */ true,
+ /* verify_checksum */ true,
+ &error_message));
if (expect_success) {
CHECK(dex_file != nullptr) << error_message;
} else {
@@ -365,7 +366,7 @@ TEST_F(DexFileTest, Version40Rejected) {
static constexpr bool kVerifyChecksum = true;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- ASSERT_FALSE(DexFile::Open(location, location, kVerifyChecksum, &error_msg, &dex_files));
+ ASSERT_FALSE(DexFileLoader::Open(location, location, kVerifyChecksum, &error_msg, &dex_files));
}
TEST_F(DexFileTest, Version41Rejected) {
@@ -377,7 +378,7 @@ TEST_F(DexFileTest, Version41Rejected) {
static constexpr bool kVerifyChecksum = true;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- ASSERT_FALSE(DexFile::Open(location, location, kVerifyChecksum, &error_msg, &dex_files));
+ ASSERT_FALSE(DexFileLoader::Open(location, location, kVerifyChecksum, &error_msg, &dex_files));
}
TEST_F(DexFileTest, ZeroLengthDexRejected) {
@@ -389,7 +390,7 @@ TEST_F(DexFileTest, ZeroLengthDexRejected) {
static constexpr bool kVerifyChecksum = true;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- ASSERT_FALSE(DexFile::Open(location, location, kVerifyChecksum, &error_msg, &dex_files));
+ ASSERT_FALSE(DexFileLoader::Open(location, location, kVerifyChecksum, &error_msg, &dex_files));
}
TEST_F(DexFileTest, GetLocationChecksum) {
@@ -402,7 +403,9 @@ TEST_F(DexFileTest, GetChecksum) {
std::vector<uint32_t> checksums;
ScopedObjectAccess soa(Thread::Current());
std::string error_msg;
- EXPECT_TRUE(DexFile::GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), &checksums, &error_msg))
+ EXPECT_TRUE(DexFileLoader::GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(),
+ &checksums,
+ &error_msg))
<< error_msg;
ASSERT_EQ(1U, checksums.size());
EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]);
@@ -412,18 +415,18 @@ TEST_F(DexFileTest, GetMultiDexChecksums) {
std::string error_msg;
std::vector<uint32_t> checksums;
std::string multidex_file = GetTestDexFileName("MultiDex");
- EXPECT_TRUE(DexFile::GetMultiDexChecksums(multidex_file.c_str(),
- &checksums,
- &error_msg)) << error_msg;
+ EXPECT_TRUE(DexFileLoader::GetMultiDexChecksums(multidex_file.c_str(),
+ &checksums,
+ &error_msg)) << error_msg;
std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex");
ASSERT_EQ(2U, dexes.size());
ASSERT_EQ(2U, checksums.size());
- EXPECT_EQ(dexes[0]->GetLocation(), DexFile::GetMultiDexLocation(0, multidex_file.c_str()));
+ EXPECT_EQ(dexes[0]->GetLocation(), DexFileLoader::GetMultiDexLocation(0, multidex_file.c_str()));
EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]);
- EXPECT_EQ(dexes[1]->GetLocation(), DexFile::GetMultiDexLocation(1, multidex_file.c_str()));
+ EXPECT_EQ(dexes[1]->GetLocation(), DexFileLoader::GetMultiDexLocation(1, multidex_file.c_str()));
EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]);
}
@@ -625,20 +628,20 @@ TEST_F(DexFileTest, FindFieldId) {
}
TEST_F(DexFileTest, GetMultiDexClassesDexName) {
- ASSERT_EQ("classes.dex", DexFile::GetMultiDexClassesDexName(0));
- ASSERT_EQ("classes2.dex", DexFile::GetMultiDexClassesDexName(1));
- ASSERT_EQ("classes3.dex", DexFile::GetMultiDexClassesDexName(2));
- ASSERT_EQ("classes100.dex", DexFile::GetMultiDexClassesDexName(99));
+ ASSERT_EQ("classes.dex", DexFileLoader::GetMultiDexClassesDexName(0));
+ ASSERT_EQ("classes2.dex", DexFileLoader::GetMultiDexClassesDexName(1));
+ ASSERT_EQ("classes3.dex", DexFileLoader::GetMultiDexClassesDexName(2));
+ ASSERT_EQ("classes100.dex", DexFileLoader::GetMultiDexClassesDexName(99));
}
TEST_F(DexFileTest, GetMultiDexLocation) {
std::string dex_location_str = "/system/app/framework.jar";
const char* dex_location = dex_location_str.c_str();
- ASSERT_EQ("/system/app/framework.jar", DexFile::GetMultiDexLocation(0, dex_location));
+ ASSERT_EQ("/system/app/framework.jar", DexFileLoader::GetMultiDexLocation(0, dex_location));
ASSERT_EQ("/system/app/framework.jar!classes2.dex",
- DexFile::GetMultiDexLocation(1, dex_location));
+ DexFileLoader::GetMultiDexLocation(1, dex_location));
ASSERT_EQ("/system/app/framework.jar!classes101.dex",
- DexFile::GetMultiDexLocation(100, dex_location));
+ DexFileLoader::GetMultiDexLocation(100, dex_location));
}
TEST_F(DexFileTest, GetDexCanonicalLocation) {
@@ -646,28 +649,30 @@ TEST_F(DexFileTest, GetDexCanonicalLocation) {
UniqueCPtr<const char[]> dex_location_real(realpath(file.GetFilename().c_str(), nullptr));
std::string dex_location(dex_location_real.get());
- ASSERT_EQ(dex_location, DexFile::GetDexCanonicalLocation(dex_location.c_str()));
- std::string multidex_location = DexFile::GetMultiDexLocation(1, dex_location.c_str());
- ASSERT_EQ(multidex_location, DexFile::GetDexCanonicalLocation(multidex_location.c_str()));
+ ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location.c_str()));
+ std::string multidex_location = DexFileLoader::GetMultiDexLocation(1, dex_location.c_str());
+ ASSERT_EQ(multidex_location, DexFileLoader::GetDexCanonicalLocation(multidex_location.c_str()));
std::string dex_location_sym = dex_location + "symlink";
ASSERT_EQ(0, symlink(dex_location.c_str(), dex_location_sym.c_str()));
- ASSERT_EQ(dex_location, DexFile::GetDexCanonicalLocation(dex_location_sym.c_str()));
+ ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location_sym.c_str()));
- std::string multidex_location_sym = DexFile::GetMultiDexLocation(1, dex_location_sym.c_str());
- ASSERT_EQ(multidex_location, DexFile::GetDexCanonicalLocation(multidex_location_sym.c_str()));
+ std::string multidex_location_sym = DexFileLoader::GetMultiDexLocation(
+ 1, dex_location_sym.c_str());
+ ASSERT_EQ(multidex_location,
+ DexFileLoader::GetDexCanonicalLocation(multidex_location_sym.c_str()));
ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
}
TEST(DexFileUtilsTest, GetBaseLocationAndMultiDexSuffix) {
- EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar"));
- EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar!classes2.dex"));
- EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar!classes8.dex"));
- EXPECT_EQ("", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar"));
- EXPECT_EQ("!classes2.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar!classes2.dex"));
- EXPECT_EQ("!classes8.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar!classes8.dex"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar!classes2.dex"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar!classes8.dex"));
+ EXPECT_EQ("", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar"));
+ EXPECT_EQ("!classes2.dex", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar!classes2.dex"));
+ EXPECT_EQ("!classes8.dex", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar!classes8.dex"));
}
TEST_F(DexFileTest, ZipOpenClassesPresent) {
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 21de059797..af2d53585e 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -27,8 +27,10 @@
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "dex_file_types.h"
#include "leb128.h"
+#include "native_dex_file.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "utils.h"
@@ -55,7 +57,7 @@ static void FixUpChecksum(uint8_t* dex_file) {
class DexFileVerifierTest : public CommonRuntimeTest {
protected:
DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) {
- return new DexFile(dex_bytes, length, "tmp", 0, nullptr);
+ return new NativeDexFile(dex_bytes, length, "tmp", 0, nullptr);
}
void VerifyModification(const char* dex_file_base64_content,
@@ -112,7 +114,7 @@ static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
// read dex file
ScopedObjectAccess soa(Thread::Current());
std::vector<std::unique_ptr<const DexFile>> tmp;
- bool success = DexFile::Open(location, location, true, error_msg, &tmp);
+ bool success = DexFileLoader::Open(location, location, true, error_msg, &tmp);
CHECK(success) << *error_msg;
EXPECT_EQ(1U, tmp.size());
std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 813a264ed9..ea7a83c75e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2185,20 +2185,11 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod**
// Generic JNI trampoline at this stage; instead, method's
// annotations' classes are looked up in the bootstrap class
// loader's resolved types (which won't trigger an exception).
+ CHECK(!self->IsExceptionPending());
bool critical_native = called->IsAnnotatedWithCriticalNative();
- // ArtMethod::IsAnnotatedWithCriticalNative should not throw
- // an exception; clear it if it happened anyway.
- // TODO: Revisit this code path and turn this into a CHECK(!self->IsExceptionPending()).
- if (self->IsExceptionPending()) {
- self->ClearException();
- }
+ CHECK(!self->IsExceptionPending());
bool fast_native = called->IsAnnotatedWithFastNative();
- // ArtMethod::IsAnnotatedWithFastNative should not throw
- // an exception; clear it if it happened anyway.
- // TODO: Revisit this code path and turn this into a CHECK(!self->IsExceptionPending()).
- if (self->IsExceptionPending()) {
- self->ClearException();
- }
+ CHECK(!self->IsExceptionPending());
bool normal_native = !critical_native && !fast_native;
// Restore the initial ArtMethod pointer at `*sp`.
*sp = called;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 732c707670..f0eada3cb4 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -35,6 +35,7 @@
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
+#include "dex_file_loader.h"
#include "exec_utils.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "image-inl.h"
@@ -1829,12 +1830,12 @@ bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg
// Skip multidex locations - These will be checked when we visit their
// corresponding primary non-multidex location.
- if (DexFile::IsMultiDexLocation(dex_file_location.c_str())) {
+ if (DexFileLoader::IsMultiDexLocation(dex_file_location.c_str())) {
continue;
}
std::vector<uint32_t> checksums;
- if (!DexFile::GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) {
+ if (!DexFileLoader::GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) {
*error_msg = StringPrintf("ValidateOatFile failed to get checksums of dex file '%s' "
"referenced by oat file %s: %s",
dex_file_location.c_str(),
@@ -1855,7 +1856,9 @@ bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg
// Verify checksums for any related multidex entries.
for (size_t i = 1; i < checksums.size(); i++) {
- std::string multi_dex_location = DexFile::GetMultiDexLocation(i, dex_file_location.c_str());
+ std::string multi_dex_location = DexFileLoader::GetMultiDexLocation(
+ i,
+ dex_file_location.c_str());
const OatFile::OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(),
nullptr,
error_msg);
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 2dd4db3895..2c8ec47492 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -511,7 +511,7 @@ bool IndirectReferenceTable::EnsureFreeCapacity(size_t free_capacity, std::strin
return true;
}
-size_t IndirectReferenceTable::FreeCapacity() {
+size_t IndirectReferenceTable::FreeCapacity() const {
return max_entries_ - segment_state_.top_index;
}
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 7daf01ce61..6675099523 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -293,7 +293,7 @@ class IndirectReferenceTable {
REQUIRES_SHARED(Locks::mutator_lock_);
// See implementation of EnsureFreeCapacity. We'll only state here how much is trivially free,
// without recovering holes. Thus this is a conservative estimate.
- size_t FreeCapacity() REQUIRES_SHARED(Locks::mutator_lock_);
+ size_t FreeCapacity() const;
// Note IrtIterator does not have a read barrier as it's used to visit roots.
IrtIterator begin() {
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 5a1605323e..73746e18ef 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -28,6 +28,8 @@
#include "check_jni.h"
#include "dex_file-inl.h"
#include "fault_handler.h"
+#include "gc/allocation_record.h"
+#include "gc/heap.h"
#include "gc_root-inl.h"
#include "indirect_reference_table-inl.h"
#include "jni_internal.h"
@@ -468,7 +470,11 @@ JavaVMExt::JavaVMExt(Runtime* runtime,
weak_globals_add_condition_("weak globals add condition",
(CHECK(Locks::jni_weak_globals_lock_ != nullptr),
*Locks::jni_weak_globals_lock_)),
- env_hooks_() {
+ env_hooks_(),
+ enable_allocation_tracking_delta_(
+ runtime_options.GetOrDefault(RuntimeArgumentMap::GlobalRefAllocStackTraceLimit)),
+ allocation_tracking_enabled_(false),
+ old_allocation_tracking_state_(false) {
functions = unchecked_functions_;
SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni));
}
@@ -583,18 +589,55 @@ bool JavaVMExt::ShouldTrace(ArtMethod* method) {
return true;
}
+void JavaVMExt::CheckGlobalRefAllocationTracking() {
+ if (LIKELY(enable_allocation_tracking_delta_ == 0)) {
+ return;
+ }
+ size_t simple_free_capacity = globals_.FreeCapacity();
+ if (UNLIKELY(simple_free_capacity <= enable_allocation_tracking_delta_)) {
+ if (!allocation_tracking_enabled_) {
+ LOG(WARNING) << "Global reference storage appears close to exhaustion, program termination "
+ << "may be imminent. Enabling allocation tracking to improve abort diagnostics. "
+ << "This will result in program slow-down.";
+
+ old_allocation_tracking_state_ = runtime_->GetHeap()->IsAllocTrackingEnabled();
+ if (!old_allocation_tracking_state_) {
+ // Need to be guaranteed suspended.
+ ScopedObjectAccess soa(Thread::Current());
+ ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative);
+ gc::AllocRecordObjectMap::SetAllocTrackingEnabled(true);
+ }
+ allocation_tracking_enabled_ = true;
+ }
+ } else {
+ if (UNLIKELY(allocation_tracking_enabled_)) {
+ if (!old_allocation_tracking_state_) {
+ // Need to be guaranteed suspended.
+ ScopedObjectAccess soa(Thread::Current());
+ ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative);
+ gc::AllocRecordObjectMap::SetAllocTrackingEnabled(false);
+ }
+ allocation_tracking_enabled_ = false;
+ }
+ }
+}
+
jobject JavaVMExt::AddGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) {
// Check for null after decoding the object to handle cleared weak globals.
if (obj == nullptr) {
return nullptr;
}
- WriterMutexLock mu(self, *Locks::jni_globals_lock_);
+ IndirectRef ref;
std::string error_msg;
- IndirectRef ref = globals_.Add(kIRTFirstSegment, obj, &error_msg);
+ {
+ WriterMutexLock mu(self, *Locks::jni_globals_lock_);
+ ref = globals_.Add(kIRTFirstSegment, obj, &error_msg);
+ }
if (UNLIKELY(ref == nullptr)) {
LOG(FATAL) << error_msg;
UNREACHABLE();
}
+ CheckGlobalRefAllocationTracking();
return reinterpret_cast<jobject>(ref);
}
@@ -625,11 +668,14 @@ void JavaVMExt::DeleteGlobalRef(Thread* self, jobject obj) {
if (obj == nullptr) {
return;
}
- WriterMutexLock mu(self, *Locks::jni_globals_lock_);
- if (!globals_.Remove(kIRTFirstSegment, obj)) {
- LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
- << "failed to find entry";
+ {
+ WriterMutexLock mu(self, *Locks::jni_globals_lock_);
+ if (!globals_.Remove(kIRTFirstSegment, obj)) {
+ LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
+ << "failed to find entry";
+ }
}
+ CheckGlobalRefAllocationTracking();
}
void JavaVMExt::DeleteWeakGlobalRef(Thread* self, jweak obj) {
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index b767b199f0..0510d6ab75 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -211,6 +211,8 @@ class JavaVMExt : public JavaVM {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::jni_weak_globals_lock_);
+ void CheckGlobalRefAllocationTracking();
+
Runtime* const runtime_;
// Used for testing. By default, we'll LOG(FATAL) the reason.
@@ -247,6 +249,10 @@ class JavaVMExt : public JavaVM {
// TODO Maybe move this to Runtime.
std::vector<GetEnvHook> env_hooks_;
+ size_t enable_allocation_tracking_delta_;
+ std::atomic<bool> allocation_tracking_enabled_;
+ std::atomic<bool> old_allocation_tracking_state_;
+
DISALLOW_COPY_AND_ASSIGN(JavaVMExt);
};
diff --git a/runtime/java_vm_ext_test.cc b/runtime/java_vm_ext_test.cc
index 2cbfa81b91..a15ec56274 100644
--- a/runtime/java_vm_ext_test.cc
+++ b/runtime/java_vm_ext_test.cc
@@ -19,6 +19,7 @@
#include <pthread.h>
#include "common_runtime_test.h"
+#include "gc/heap.h"
#include "java_vm_ext.h"
#include "runtime.h"
@@ -134,4 +135,49 @@ TEST_F(JavaVmExtTest, DetachCurrentThread) {
EXPECT_EQ(JNI_ERR, err);
}
+class JavaVmExtStackTraceTest : public JavaVmExtTest {
+ protected:
+ void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+ options->emplace_back("-XX:GlobalRefAllocStackTraceLimit=50000", nullptr);
+ }
+};
+
+TEST_F(JavaVmExtStackTraceTest, TestEnableDisable) {
+ ASSERT_FALSE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
+
+ JNIEnv* env;
+ jint ok = vm_->AttachCurrentThread(&env, nullptr);
+ ASSERT_EQ(JNI_OK, ok);
+
+ std::vector<jobject> global_refs_;
+ jobject local_ref = env->NewStringUTF("Dummy");
+ for (size_t i = 0; i < 2000; ++i) {
+ global_refs_.push_back(env->NewGlobalRef(local_ref));
+ }
+
+ EXPECT_TRUE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
+
+ for (jobject global_ref : global_refs_) {
+ env->DeleteGlobalRef(global_ref);
+ }
+
+ EXPECT_FALSE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
+
+ global_refs_.clear();
+ for (size_t i = 0; i < 2000; ++i) {
+ global_refs_.push_back(env->NewGlobalRef(local_ref));
+ }
+
+ EXPECT_TRUE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
+
+ for (jobject global_ref : global_refs_) {
+ env->DeleteGlobalRef(global_ref);
+ }
+
+ EXPECT_FALSE(Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
+
+ ok = vm_->DetachCurrentThread();
+ EXPECT_EQ(JNI_OK, ok);
+}
+
} // namespace art
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index e122c6da20..47615f56fe 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -26,6 +26,7 @@
#include "base/time_utils.h"
#include "cha.h"
#include "debugger_interface.h"
+#include "dex_file_loader.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/bitmap-inl.h"
#include "gc/scoped_gc_critical_section.h"
@@ -1350,7 +1351,8 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca
for (const ProfilingInfo* info : profiling_infos_) {
ArtMethod* method = info->GetMethod();
const DexFile* dex_file = method->GetDexFile();
- if (!ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) {
+ const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
+ if (!ContainsElement(dex_base_locations, base_location)) {
// Skip dex files which are not profiled.
continue;
}
@@ -1404,7 +1406,8 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca
is_missing_types = true;
continue;
}
- if (ContainsElement(dex_base_locations, class_dex_file->GetBaseLocation())) {
+ if (ContainsElement(dex_base_locations,
+ DexFileLoader::GetBaseLocation(class_dex_file->GetLocation()))) {
// Only consider classes from the same apk (including multidex).
profile_classes.emplace_back(/*ProfileMethodInfo::ProfileClassReference*/
class_dex_file, type_index);
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index d14c94aafe..19501de81b 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -40,6 +40,7 @@
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
+#include "dex_file_loader.h"
#include "jit/profiling_info.h"
#include "os.h"
#include "safe_map.h"
@@ -1537,7 +1538,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>*
os << dex_data->profile_key;
} else {
// Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
- std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_data->profile_key);
+ std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex_data->profile_key);
os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
}
os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
@@ -1696,7 +1697,7 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd,
const uint16_t kFavorSplit = 2;
for (uint16_t i = 0; i < number_of_dex_files; i++) {
- std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str());
+ std::string dex_location = DexFileLoader::GetMultiDexLocation(i, base_dex_location.c_str());
std::string profile_key = GetProfileDexFileKey(dex_location);
for (uint16_t m = 0; m < number_of_methods; m++) {
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 2bf8d8b8f8..01853de403 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -31,6 +31,7 @@
#include "base/time_utils.h"
#include "class_table-inl.h"
#include "compiler_filter.h"
+#include "dex_file_loader.h"
#include "dex_reference_collection.h"
#include "gc/collector_type.h"
#include "gc/gc_cause.h"
@@ -414,7 +415,8 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) {
const std::set<std::string>& locations = it.second;
for (const auto& pair : hot_methods.GetMap()) {
const DexFile* const dex_file = pair.first;
- if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
+ const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
+ if (locations.find(base_location) != locations.end()) {
const MethodReferenceCollection::IndexVector& indices = pair.second;
uint8_t flags = Hotness::kFlagHot;
flags |= startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup;
@@ -427,7 +429,8 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) {
}
for (const auto& pair : sampled_methods.GetMap()) {
const DexFile* const dex_file = pair.first;
- if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
+ const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
+ if (locations.find(base_location) != locations.end()) {
const MethodReferenceCollection::IndexVector& indices = pair.second;
cached_info->AddMethodsForDex(startup ? Hotness::kFlagStartup : Hotness::kFlagPostStartup,
dex_file,
@@ -437,14 +440,15 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) {
}
for (const auto& pair : resolved_classes.GetMap()) {
const DexFile* const dex_file = pair.first;
- if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
+ const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
+ if (locations.find(base_location) != locations.end()) {
const TypeReferenceCollection::IndexVector& classes = pair.second;
VLOG(profiler) << "Added " << classes.size() << " classes for location "
- << dex_file->GetBaseLocation()
+ << base_location
<< " (" << dex_file->GetLocation() << ")";
cached_info->AddClassesForDex(dex_file, classes.begin(), classes.end());
} else {
- VLOG(profiler) << "Location not found " << dex_file->GetBaseLocation()
+ VLOG(profiler) << "Location not found " << base_location
<< " (" << dex_file->GetLocation() << ")";
}
}
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index d40e6d94c9..e75d097220 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -26,6 +26,7 @@
#include "common_throws.h"
#include "compiler_filter.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "jni_internal.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
@@ -185,12 +186,12 @@ static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem
dex_mem_map->Begin(),
dex_mem_map->End());
std::string error_message;
- std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
- 0,
- std::move(dex_mem_map),
- /* verify */ true,
- /* verify_location */ true,
- &error_message));
+ std::unique_ptr<const DexFile> dex_file(DexFileLoader::Open(location,
+ 0,
+ std::move(dex_mem_map),
+ /* verify */ true,
+ /* verify_location */ true,
+ &error_message));
if (dex_file == nullptr) {
ScopedObjectAccess soa(env);
ThrowWrappedIOException("%s", error_message.c_str());
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 3357fa7a45..70dd5cb56d 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -95,10 +95,10 @@ static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize,
}
static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename,
- jobject javaFd, jint bufferSize, jint flags,
+ jint javaFd, jint bufferSize, jint flags,
jboolean samplingEnabled, jint intervalUs,
jboolean streamingOutput) {
- int originalFd = jniGetFDFromFileDescriptor(env, javaFd);
+ int originalFd = javaFd;
if (originalFd < 0) {
return;
}
@@ -224,9 +224,9 @@ static jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) {
* Cause "hprof" data to be dumped. We can throw an IOException if an
* error occurs during file handling.
*/
-static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jobject javaFd) {
+static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jint javaFd) {
// Only one of these may be null.
- if (javaFilename == nullptr && javaFd == nullptr) {
+ if (javaFilename == nullptr && javaFd < 0) {
ScopedObjectAccess soa(env);
ThrowNullPointerException("fileName == null && fd == null");
return;
@@ -243,15 +243,7 @@ static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, job
filename = "[fd]";
}
- int fd = -1;
- if (javaFd != nullptr) {
- fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (fd < 0) {
- ScopedObjectAccess soa(env);
- ThrowRuntimeException("Invalid file descriptor");
- return;
- }
- }
+ int fd = javaFd;
hprof::DumpHeap(filename.c_str(), fd, false);
}
@@ -537,7 +529,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
NATIVE_METHOD(VMDebug, crash, "()V"),
- NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"),
+ NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),
NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
@@ -557,7 +549,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"),
NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"),
NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"),
- NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;IIIZIZ)V"),
NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"),
NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"),
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 4034e8c837..413149c510 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -17,6 +17,7 @@
#include "java_lang_VMClassLoader.h"
#include "class_linker.h"
+#include "dex_file_loader.h"
#include "jni_internal.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
@@ -135,7 +136,7 @@ static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) {
const DexFile* dex_file = path[i];
// For multidex locations, e.g., x.jar!classes2.dex, we want to look into x.jar.
- const std::string& location(dex_file->GetBaseLocation());
+ const std::string location(DexFileLoader::GetBaseLocation(dex_file->GetLocation()));
ScopedLocalRef<jstring> javaPath(env, env->NewStringUTF(location.c_str()));
if (javaPath.get() == nullptr) {
diff --git a/runtime/native_dex_file.cc b/runtime/native_dex_file.cc
new file mode 100644
index 0000000000..9a93696f1b
--- /dev/null
+++ b/runtime/native_dex_file.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "native_dex_file.h"
+
+namespace art {
+
+const uint8_t NativeDexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
+const uint8_t NativeDexFile::kDexMagicVersions[NativeDexFile::kNumDexVersions]
+ [NativeDexFile::kDexVersionLen] = {
+ {'0', '3', '5', '\0'},
+ // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex
+ // files with that version number would erroneously be accepted and run.
+ {'0', '3', '7', '\0'},
+ // Dex version 038: Android "O" and beyond.
+ {'0', '3', '8', '\0'},
+ // Dex verion 039: Beyond Android "O".
+ {'0', '3', '9', '\0'},
+};
+
+bool NativeDexFile::IsMagicValid(const uint8_t* magic) {
+ return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
+}
+
+bool NativeDexFile::IsVersionValid(const uint8_t* magic) {
+ const uint8_t* version = &magic[sizeof(kDexMagic)];
+ for (uint32_t i = 0; i < kNumDexVersions; i++) {
+ if (memcmp(version, kDexMagicVersions[i], kDexVersionLen) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool NativeDexFile::IsMagicValid() const {
+ return IsMagicValid(header_->magic_);
+}
+
+bool NativeDexFile::IsVersionValid() const {
+ return IsVersionValid(header_->magic_);
+}
+
+} // namespace art
diff --git a/runtime/native_dex_file.h b/runtime/native_dex_file.h
new file mode 100644
index 0000000000..8f09e6d7fc
--- /dev/null
+++ b/runtime/native_dex_file.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_DEX_FILE_H_
+#define ART_RUNTIME_NATIVE_DEX_FILE_H_
+
+#include <iosfwd>
+
+#include "dex_file.h"
+
+namespace art {
+
+class OatDexFile;
+
+// Native (ordinary) dex file. This is the format that is packaged in APKs and produced by tools.
+class NativeDexFile : public DexFile {
+ public:
+ static const uint8_t kDexMagic[kDexMagicSize];
+ static constexpr size_t kNumDexVersions = 4;
+ static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
+
+ // Returns true if the byte string points to the magic value.
+ static bool IsMagicValid(const uint8_t* magic);
+ virtual bool IsMagicValid() const OVERRIDE;
+
+ // Returns true if the byte string after the magic is the correct value.
+ static bool IsVersionValid(const uint8_t* magic);
+ virtual bool IsVersionValid() const OVERRIDE;
+
+ private:
+ NativeDexFile(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file)
+ : DexFile(base, size, location, location_checksum, oat_dex_file) {}
+
+ friend class DexFileLoader;
+ friend class DexFileVerifierTest;
+
+ ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor
+
+ DISALLOW_COPY_AND_ASSIGN(NativeDexFile);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_DEX_FILE_H_
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 3f4cb942fc..ab820fb37f 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -26,6 +26,7 @@
#include <cstring>
#include <sstream>
#include <type_traits>
+#include <sys/stat.h>
// dlopen_ext support from bionic.
#ifdef ART_TARGET_ANDROID
@@ -41,6 +42,7 @@
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
#include "dex_file_types.h"
+#include "dex_file_loader.h"
#include "elf_file.h"
#include "elf_utils.h"
#include "gc_root.h"
@@ -48,6 +50,7 @@
#include "mem_map.h"
#include "mirror/class.h"
#include "mirror/object-inl.h"
+#include "native_dex_file.h"
#include "oat.h"
#include "oat_file-inl.h"
#include "oat_file_manager.h"
@@ -105,6 +108,19 @@ class OatFileBase : public OatFile {
const char* abs_dex_location,
std::string* error_msg);
+ template <typename kOatFileBaseSubType>
+ static OatFileBase* OpenOatFile(int vdex_fd,
+ int oat_fd,
+ const std::string& vdex_filename,
+ const std::string& oat_filename,
+ uint8_t* requested_base,
+ uint8_t* oat_file_begin,
+ bool writable,
+ bool executable,
+ bool low_4gb,
+ const char* abs_dex_location,
+ std::string* error_msg);
+
protected:
OatFileBase(const std::string& filename, bool executable) : OatFile(filename, executable) {}
@@ -118,6 +134,12 @@ class OatFileBase : public OatFile {
bool low_4gb,
std::string* error_msg);
+ bool LoadVdex(int vdex_fd,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ std::string* error_msg);
+
virtual bool Load(const std::string& elf_filename,
uint8_t* oat_file_begin,
bool writable,
@@ -125,6 +147,13 @@ class OatFileBase : public OatFile {
bool low_4gb,
std::string* error_msg) = 0;
+ virtual bool Load(int oat_fd,
+ uint8_t* oat_file_begin,
+ bool writable,
+ bool executable,
+ bool low_4gb,
+ std::string* error_msg) = 0;
+
bool ComputeFields(uint8_t* requested_base,
const std::string& file_path,
std::string* error_msg);
@@ -192,6 +221,46 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename,
return ret.release();
}
+template <typename kOatFileBaseSubType>
+OatFileBase* OatFileBase::OpenOatFile(int vdex_fd,
+ int oat_fd,
+ const std::string& vdex_location,
+ const std::string& oat_location,
+ uint8_t* requested_base,
+ uint8_t* oat_file_begin,
+ bool writable,
+ bool executable,
+ bool low_4gb,
+ const char* abs_dex_location,
+ std::string* error_msg) {
+ std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(oat_location, executable));
+
+ if (kIsVdexEnabled && !ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) {
+ return nullptr;
+ }
+
+ if (!ret->Load(oat_fd,
+ oat_file_begin,
+ writable,
+ executable,
+ low_4gb,
+ error_msg)) {
+ return nullptr;
+ }
+
+ if (!ret->ComputeFields(requested_base, oat_location, error_msg)) {
+ return nullptr;
+ }
+
+ ret->PreSetup(oat_location);
+
+ if (!ret->Setup(abs_dex_location, error_msg)) {
+ return nullptr;
+ }
+
+ return ret.release();
+}
+
bool OatFileBase::LoadVdex(const std::string& vdex_filename,
bool writable,
bool low_4gb,
@@ -206,6 +275,33 @@ bool OatFileBase::LoadVdex(const std::string& vdex_filename,
return true;
}
+bool OatFileBase::LoadVdex(int vdex_fd,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ std::string* error_msg) {
+ if (vdex_fd != -1) {
+ struct stat s;
+ int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd, &s));
+ if (rc == -1) {
+ PLOG(WARNING) << "Failed getting length of vdex file";
+ } else {
+ vdex_ = VdexFile::Open(vdex_fd,
+ s.st_size,
+ vdex_filename,
+ writable,
+ low_4gb,
+ false /* unquicken */,
+ error_msg);
+ if (vdex_.get() == nullptr) {
+ *error_msg = "Failed opening vdex file.";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
bool OatFileBase::ComputeFields(uint8_t* requested_base,
const std::string& file_path,
std::string* error_msg) {
@@ -458,7 +554,9 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
}
const uint8_t* dex_file_pointer = DexBegin() + dex_file_offset;
- if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) {
+
+ const bool valid_magic = NativeDexFile::IsMagicValid(dex_file_pointer);
+ if (UNLIKELY(!valid_magic)) {
*error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid "
"dex file magic '%s'",
GetLocation().c_str(),
@@ -467,7 +565,7 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
dex_file_pointer);
return false;
}
- if (UNLIKELY(!DexFile::IsVersionValid(dex_file_pointer))) {
+ if (UNLIKELY(!NativeDexFile::IsVersionValid(dex_file_pointer))) {
*error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid "
"dex file version '%s'",
GetLocation().c_str(),
@@ -611,7 +709,8 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
reinterpret_cast<const DexFile::Header*>(dex_file_pointer)->method_ids_size_);
}
- std::string canonical_location = DexFile::GetDexCanonicalLocation(dex_file_location.c_str());
+ std::string canonical_location =
+ DexFileLoader::GetDexCanonicalLocation(dex_file_location.c_str());
// Create the OatDexFile and add it to the owning container.
OatDexFile* oat_dex_file = new OatDexFile(this,
@@ -712,6 +811,10 @@ class DlOpenOatFile FINAL : public OatFileBase {
bool low_4gb,
std::string* error_msg) OVERRIDE;
+ bool Load(int, uint8_t*, bool, bool, bool, std::string*) {
+ return false;
+ }
+
// Ask the linker where it mmaped the file and notify our mmap wrapper of the regions.
void PreSetup(const std::string& elf_filename) OVERRIDE;
@@ -973,6 +1076,13 @@ class ElfOatFile FINAL : public OatFileBase {
bool low_4gb,
std::string* error_msg) OVERRIDE;
+ bool Load(int oat_fd,
+ uint8_t* oat_file_begin, // Override where the file is loaded to if not null
+ bool writable,
+ bool executable,
+ bool low_4gb,
+ std::string* error_msg) OVERRIDE;
+
void PreSetup(const std::string& elf_filename ATTRIBUTE_UNUSED) OVERRIDE {
}
@@ -1065,6 +1175,31 @@ bool ElfOatFile::Load(const std::string& elf_filename,
error_msg);
}
+bool ElfOatFile::Load(int oat_fd,
+ uint8_t* oat_file_begin, // Override where the file is loaded to if not null
+ bool writable,
+ bool executable,
+ bool low_4gb,
+ std::string* error_msg) {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ if (oat_fd != -1) {
+ std::unique_ptr<File> file = std::make_unique<File>(oat_fd, false);
+ file->DisableAutoClose();
+ if (file == nullptr) {
+ *error_msg = StringPrintf("Failed to open oat filename for reading: %s",
+ strerror(errno));
+ return false;
+ }
+ return ElfOatFile::ElfFileOpen(file.get(),
+ oat_file_begin,
+ writable,
+ executable,
+ low_4gb,
+ error_msg);
+ }
+ return false;
+}
+
bool ElfOatFile::ElfFileOpen(File* file,
uint8_t* oat_file_begin,
bool writable,
@@ -1096,8 +1231,8 @@ std::string OatFile::ResolveRelativeEncodedDexLocation(
const char* abs_dex_location, const std::string& rel_dex_location) {
if (abs_dex_location != nullptr && rel_dex_location[0] != '/') {
// Strip :classes<N>.dex used for secondary multidex files.
- std::string base = DexFile::GetBaseLocation(rel_dex_location);
- std::string multidex_suffix = DexFile::GetMultiDexSuffix(rel_dex_location);
+ std::string base = DexFileLoader::GetBaseLocation(rel_dex_location);
+ std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(rel_dex_location);
// Check if the base is a suffix of the provided abs_dex_location.
std::string target_suffix = "/" + base;
@@ -1194,6 +1329,33 @@ OatFile* OatFile::Open(const std::string& oat_filename,
return with_internal;
}
+OatFile* OatFile::Open(int vdex_fd,
+ int oat_fd,
+ const std::string& oat_location,
+ uint8_t* requested_base,
+ uint8_t* oat_file_begin,
+ bool executable,
+ bool low_4gb,
+ const char* abs_dex_location,
+ std::string* error_msg) {
+ CHECK(!oat_location.empty()) << oat_location;
+
+ std::string vdex_location = GetVdexFilename(oat_location);
+
+ OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(vdex_fd,
+ oat_fd,
+ vdex_location,
+ oat_location,
+ requested_base,
+ oat_file_begin,
+ false /* writable */,
+ executable,
+ low_4gb,
+ abs_dex_location,
+ error_msg);
+ return with_internal;
+}
+
OatFile* OatFile::OpenWritable(File* file,
const std::string& location,
const char* abs_dex_location,
@@ -1324,7 +1486,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
oat_dex_file = secondary_lb->second; // May be null.
} else {
// We haven't seen this dex_location before, we must check the canonical location.
- std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location);
+ std::string dex_canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location);
if (dex_canonical_location != dex_location) {
StringPiece canonical_key(dex_canonical_location);
auto canonical_it = oat_dex_files_.find(canonical_key);
@@ -1342,7 +1504,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
if (oat_dex_file == nullptr) {
if (error_msg != nullptr) {
- std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location);
+ std::string dex_canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location);
*error_msg = "Failed to find OatDexFile for DexFile " + std::string(dex_location)
+ " (canonical path " + dex_canonical_location + ") in OatFile " + GetLocation();
}
@@ -1352,7 +1514,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
if (dex_location_checksum != nullptr &&
oat_dex_file->GetDexFileLocationChecksum() != *dex_location_checksum) {
if (error_msg != nullptr) {
- std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location);
+ std::string dex_canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location);
std::string checksum = StringPrintf("0x%08x", oat_dex_file->GetDexFileLocationChecksum());
std::string required_checksum = StringPrintf("0x%08x", *dex_location_checksum);
*error_msg = "OatDexFile for DexFile " + std::string(dex_location)
@@ -1408,14 +1570,14 @@ std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* err
ScopedTrace trace(__PRETTY_FUNCTION__);
static constexpr bool kVerify = false;
static constexpr bool kVerifyChecksum = false;
- return DexFile::Open(dex_file_pointer_,
- FileSize(),
- dex_file_location_,
- dex_file_location_checksum_,
- this,
- kVerify,
- kVerifyChecksum,
- error_msg);
+ return DexFileLoader::Open(dex_file_pointer_,
+ FileSize(),
+ dex_file_location_,
+ dex_file_location_checksum_,
+ this,
+ kVerify,
+ kVerifyChecksum,
+ error_msg);
}
uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 04cb3a0a6e..7d4e6dfd6b 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -89,6 +89,18 @@ class OatFile {
const char* abs_dex_location,
std::string* error_msg);
+ // Similar to OatFile::Open(const std::string...), but accepts input vdex and
+ // odex files as file descriptors.
+ static OatFile* Open(int vdex_fd,
+ int oat_fd,
+ const std::string& oat_location,
+ uint8_t* requested_base,
+ uint8_t* oat_file_begin,
+ bool executable,
+ bool low_4gb,
+ const char* abs_dex_location,
+ std::string* error_msg);
+
// Open an oat file from an already opened File.
// Does not use dlopen underneath so cannot be used for runtime use
// where relocations may be required. Currently used from
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f3a0725f79..a7fe9b1205 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -27,6 +27,7 @@
#include "base/stl_util.h"
#include "class_linker.h"
#include "compiler_filter.h"
+#include "dex_file_loader.h"
#include "exec_utils.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
@@ -69,7 +70,9 @@ std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStat
OatFileAssistant::OatFileAssistant(const char* dex_location,
const InstructionSet isa,
- bool load_executable)
+ bool load_executable,
+ int vdex_fd,
+ int oat_fd)
: isa_(isa),
load_executable_(load_executable),
odex_(this, /*is_oat_location*/ false),
@@ -109,7 +112,7 @@ OatFileAssistant::OatFileAssistant(const char* dex_location,
std::string error_msg;
std::string odex_file_name;
if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) {
- odex_.Reset(odex_file_name);
+ odex_.Reset(odex_file_name, vdex_fd, oat_fd);
} else {
LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
}
@@ -132,7 +135,7 @@ OatFileAssistant::OatFileAssistant(const char* dex_location,
LOG(WARNING) << "Failed to determine dex file parent directory: " << dex_location_;
} else {
std::string parent = dex_location_.substr(0, pos);
- if (access(parent.c_str(), W_OK) == 0) {
+ if (access(parent.c_str(), W_OK) == 0 || oat_fd > 0) {
dex_parent_writable_ = true;
} else {
VLOG(oat) << "Dex parent of " << dex_location_ << " is not writable: " << strerror(errno);
@@ -349,7 +352,7 @@ bool OatFileAssistant::LoadDexFiles(
// Load the rest of the multidex entries
for (size_t i = 1;; i++) {
- std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location.c_str());
+ std::string multidex_dex_location = DexFileLoader::GetMultiDexLocation(i, dex_location.c_str());
oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
if (oat_dex_file == nullptr) {
// There are no more multidex entries to load.
@@ -401,7 +404,7 @@ bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* er
uint32_t expected_checksum = (*required_dex_checksums)[i];
uint32_t actual_checksum = file.GetLocationChecksum(i);
if (expected_checksum != actual_checksum) {
- std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str());
*error_msg = StringPrintf("Dex checksum does not match for dex: %s."
"Expected: %u, actual: %u",
dex.c_str(),
@@ -430,7 +433,7 @@ bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* err
}
for (uint32_t i = 0; i < number_of_dex_files; i++) {
- std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str());
uint32_t expected_checksum = (*required_dex_checksums)[i];
const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr);
if (oat_dex_file == nullptr) {
@@ -863,9 +866,9 @@ const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
required_dex_checksums_found_ = false;
cached_required_dex_checksums_.clear();
std::string error_msg;
- if (DexFile::GetMultiDexChecksums(dex_location_.c_str(),
- &cached_required_dex_checksums_,
- &error_msg)) {
+ if (DexFileLoader::GetMultiDexChecksums(dex_location_.c_str(),
+ &cached_required_dex_checksums_,
+ &error_msg)) {
required_dex_checksums_found_ = true;
has_original_dex_files_ = true;
} else {
@@ -879,7 +882,7 @@ const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
if (odex_file != nullptr) {
required_dex_checksums_found_ = true;
for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) {
- std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str());
const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr);
if (odex_dex_file == nullptr) {
required_dex_checksums_found_ = false;
@@ -1016,11 +1019,28 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
// Check to see if there is a vdex file we can make use of.
std::string error_msg;
std::string vdex_filename = GetVdexFilename(filename_);
- std::unique_ptr<VdexFile> vdex = VdexFile::Open(vdex_filename,
- /*writeable*/false,
- /*low_4gb*/false,
- /*unquicken*/false,
- &error_msg);
+ std::unique_ptr<VdexFile> vdex;
+ if (vdex_fd_ == -1) {
+ vdex = VdexFile::Open(vdex_filename,
+ false /*writeable*/,
+ false /*low_4gb*/,
+ false /*unquicken*/,
+ &error_msg);
+ } else {
+ struct stat s;
+ int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd_, &s));
+ if (rc == -1) {
+ PLOG(WARNING) << "Failed getting length of vdex file";
+ } else {
+ vdex = VdexFile::Open(vdex_fd_,
+ s.st_size,
+ vdex_filename,
+ false /*writable*/,
+ false /*low_4gb*/,
+ false /* unquicken */,
+ &error_msg);
+ }
+ }
if (vdex == nullptr) {
status_ = kOatCannotOpen;
VLOG(oat) << "unable to open vdex file " << vdex_filename << ": " << error_msg;
@@ -1095,14 +1115,26 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
load_attempted_ = true;
if (filename_provided_) {
std::string error_msg;
- file_.reset(OatFile::Open(filename_.c_str(),
- filename_.c_str(),
- nullptr,
- nullptr,
- oat_file_assistant_->load_executable_,
- /*low_4gb*/false,
- oat_file_assistant_->dex_location_.c_str(),
- &error_msg));
+ if (oat_fd_ != -1 && vdex_fd_ != -1) {
+ file_.reset(OatFile::Open(vdex_fd_,
+ oat_fd_,
+ filename_.c_str(),
+ nullptr,
+ nullptr,
+ oat_file_assistant_->load_executable_,
+ false /* low_4gb */,
+ oat_file_assistant_->dex_location_.c_str(),
+ &error_msg));
+ } else {
+ file_.reset(OatFile::Open(filename_.c_str(),
+ filename_.c_str(),
+ nullptr,
+ nullptr,
+ oat_file_assistant_->load_executable_,
+ false /* low_4gb */,
+ oat_file_assistant_->dex_location_.c_str(),
+ &error_msg));
+ }
if (file_.get() == nullptr) {
VLOG(oat) << "OatFileAssistant test for existing oat file "
<< filename_ << ": " << error_msg;
@@ -1169,9 +1201,12 @@ void OatFileAssistant::OatFileInfo::Reset() {
status_attempted_ = false;
}
-void OatFileAssistant::OatFileInfo::Reset(const std::string& filename) {
+void OatFileAssistant::OatFileInfo::Reset(const std::string& filename, int vdex_fd,
+ int oat_fd) {
filename_provided_ = true;
filename_ = filename;
+ vdex_fd_ = vdex_fd;
+ oat_fd_ = oat_fd;
Reset();
}
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6dc3c197b2..0f74ca4b02 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -121,7 +121,9 @@ class OatFileAssistant {
// executable code for this dex location.
OatFileAssistant(const char* dex_location,
const InstructionSet isa,
- bool load_executable);
+ bool load_executable,
+ int vdex_fd = -1,
+ int oat_fd = -1);
~OatFileAssistant();
@@ -349,7 +351,7 @@ class OatFileAssistant {
// Clear any cached information and switch to getting info about the oat
// file with the given filename.
- void Reset(const std::string& filename);
+ void Reset(const std::string& filename, int vdex_fd = -1, int oat_fd = -1);
// Release the loaded oat file for runtime use.
// Returns null if the oat file hasn't been loaded or is out of date.
@@ -386,6 +388,9 @@ class OatFileAssistant {
bool filename_provided_ = false;
std::string filename_;
+ int oat_fd_ = -1;
+ int vdex_fd_ = -1;
+
bool load_attempted_ = false;
std::unique_ptr<OatFile> file_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 3ecd1b516d..d99036df7e 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -20,6 +20,7 @@
#include <string>
#include <vector>
+#include <fcntl.h>
#include <gtest/gtest.h>
@@ -222,6 +223,125 @@ TEST_F(OatFileAssistantTest, OatUpToDate) {
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
+// Case: Passing valid file descriptors of updated odex/vdex filesalong with
+// the dex file.
+// Expect: The status is kNoDexOptNeeded.
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithFd) {
+ std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+ std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
+ std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ odex_location.c_str(),
+ CompilerFilter::kSpeed,
+ true,
+ false,
+ false);
+
+ android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY));
+ android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY));
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ kRuntimeISA,
+ false,
+ vdex_fd.get(),
+ odex_fd.get());
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract));
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
+ EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: Passing valid odex fd, however, invalid fd for vdex with
+// the dex file.
+// Expect: The status is kDex2oatFromScratch.
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
+ std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+ std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ odex_location.c_str(),
+ CompilerFilter::kSpeed,
+ true,
+ false,
+ false);
+
+ android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY));
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ kRuntimeISA,
+ false,
+ -1,
+ odex_fd.get());
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
+}
+
+// Case: Passing valid vdex fd, however, invalid fd for odex with
+// the dex file.
+// Expect: The status is kDex2oatFromScratch.
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) {
+ std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+ std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
+ std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ odex_location.c_str(),
+ CompilerFilter::kSpeed,
+ true,
+ false,
+ false);
+
+ android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY));
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ kRuntimeISA,
+ false,
+ vdex_fd.get(),
+ -1);
+ // Even though the vdex file is up to date, because we don't have the oat
+ // file, we can't know that the vdex depends on the boot image and is up to
+ // date with respect to the boot image. Instead we must assume the vdex file
+ // depends on the boot image and is out of date with respect to the boot
+ // image.
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+ EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
+}
+
+// Case: Passing invalid vdex and odex fd with the dex file.
+// Expect: The status is kDex2oatFromScratch.
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) {
+ std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ kRuntimeISA,
+ false,
+ -1,
+ -1);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
+}
+
// Case: We have a DEX file and up-to-date OAT file for it. We load the dex file
// via a symlink.
// Expect: The status is kNoDexOptNeeded.
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 7cabae55e4..1e7cf723dc 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -31,6 +31,7 @@
#include "class_linker.h"
#include "class_loader_context.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "dex_file_tracking_registrar.h"
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
@@ -94,7 +95,7 @@ const OatFile* OatFileManager::FindOpenedOatFileFromDexLocation(
for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles();
for (const OatDexFile* oat_dex_file : oat_dex_files) {
- if (DexFile::GetBaseLocation(oat_dex_file->GetDexFileLocation()) == dex_base_location) {
+ if (DexFileLoader::GetBaseLocation(oat_dex_file->GetDexFileLocation()) == dex_base_location) {
return oat_file.get();
}
}
@@ -435,8 +436,13 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
// Update the oat file on disk if we can, based on the --compiler-filter
// option derived from the current runtime options.
// This may fail, but that's okay. Best effort is all that matters here.
- switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false,
- context.get(),
+ // TODO(calin): b/64530081 b/66984396. Pass a null context to verify and compile
+ // secondary dex files in isolation (and avoid to extract/verify the main apk
+ // if it's in the class path). Note this trades correctness for performance
+ // since the resulting slow down is unacceptable in some cases until b/64530081
+ // is fixed.
+ switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false,
+ /*class_loader_context*/ nullptr,
/*out*/ &error_msg)) {
case OatFileAssistant::kUpdateFailed:
LOG(WARNING) << error_msg;
@@ -591,7 +597,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
if (oat_file_assistant.HasOriginalDexFiles()) {
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
static constexpr bool kVerifyChecksum = true;
- if (!DexFile::Open(
+ if (!DexFileLoader::Open(
dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) {
LOG(WARNING) << error_msg;
error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 1d524fd5e6..9888186ed0 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -310,6 +310,9 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.Define("-XX:ThreadSuspendTimeout=_") // in ms
.WithType<MillisecondsToNanoseconds>() // store as ns
.IntoKey(M::ThreadSuspendTimeout)
+ .Define("-XX:GlobalRefAllocStackTraceLimit=_") // Number of free slots to enable tracing.
+ .WithType<unsigned int>()
+ .IntoKey(M::GlobalRefAllocStackTraceLimit)
.Define("-XX:SlowDebug=_")
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7c05cb6174..a4ed21e450 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -67,6 +67,7 @@
#include "class_linker-inl.h"
#include "compiler_callbacks.h"
#include "debugger.h"
+#include "dex_file_loader.h"
#include "elf_file.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "experimental_flags.h"
@@ -1020,7 +1021,7 @@ static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames,
LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
continue;
}
- if (!DexFile::Open(dex_filename, dex_location, kVerifyChecksum, &error_msg, dex_files)) {
+ if (!DexFileLoader::Open(dex_filename, dex_location, kVerifyChecksum, &error_msg, dex_files)) {
LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
++failure_count;
}
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 78a60faa3a..cafae22e8c 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -145,4 +145,6 @@ RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullpt
RUNTIME_OPTIONS_KEY (bool, SlowDebug, false)
+RUNTIME_OPTIONS_KEY (unsigned int, GlobalRefAllocStackTraceLimit, 0) // 0 = off
+
#undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/utils.cc b/runtime/utils.cc
index b72dec62bd..1f6bd742b6 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -48,6 +48,7 @@
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "dex_file-inl.h"
+#include "dex_file_loader.h"
#include "dex_instruction.h"
#include "oat_quick_method_header.h"
#include "os.h"
@@ -858,7 +859,7 @@ bool GetDalvikCacheFilename(const char* location, const char* cache_location,
!android::base::EndsWith(location, ".art") &&
!android::base::EndsWith(location, ".oat")) {
cache_file += "/";
- cache_file += DexFile::kClassesDex;
+ cache_file += DexFileLoader::kClassesDex;
}
std::replace(cache_file.begin(), cache_file.end(), '/', '@');
*filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index b95522062e..55bc9ecac5 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -25,6 +25,7 @@
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "dex_file.h"
+#include "dex_file_loader.h"
#include "dex_to_dex_decompiler.h"
namespace art {
@@ -151,15 +152,15 @@ bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_
size_t size = reinterpret_cast<const DexFile::Header*>(dex_file_start)->file_size_;
// TODO: Supply the location information for a vdex file.
static constexpr char kVdexLocation[] = "";
- std::string location = DexFile::GetMultiDexLocation(i, kVdexLocation);
- std::unique_ptr<const DexFile> dex(DexFile::Open(dex_file_start,
- size,
- location,
- GetLocationChecksum(i),
- nullptr /*oat_dex_file*/,
- false /*verify*/,
- false /*verify_checksum*/,
- error_msg));
+ std::string location = DexFileLoader::GetMultiDexLocation(i, kVdexLocation);
+ std::unique_ptr<const DexFile> dex(DexFileLoader::Open(dex_file_start,
+ size,
+ location,
+ GetLocationChecksum(i),
+ nullptr /*oat_dex_file*/,
+ false /*verify*/,
+ false /*verify_checksum*/,
+ error_msg));
if (dex == nullptr) {
return false;
}
diff --git a/test/1914-get-local-instance/expected.txt b/test/1914-get-local-instance/expected.txt
index 4117942392..09f0df1937 100644
--- a/test/1914-get-local-instance/expected.txt
+++ b/test/1914-get-local-instance/expected.txt
@@ -10,3 +10,6 @@ Running public void art.Test1914$TargetClass.InstanceMethod(java.lang.Runnable)
Running public native void art.Test1914$TargetClass.NativeInstanceMethod(java.lang.Runnable) with "GetThis" on remote thread.
"GetThis" on public native void art.Test1914$TargetClass.NativeInstanceMethod(java.lang.Runnable) got value: TargetClass("NativeInstanceMethodObject")
Value is 'TargetClass("NativeInstanceMethodObject")' (class: class art.Test1914$TargetClass)
+Running public abstract void art.Test1914$Foo.InterfaceProxyMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public abstract void art.Test1914$Foo.InterfaceProxyMethod(java.lang.Runnable) got value: Proxy for [interface art.Test1914$Foo]
+ Value is 'Proxy for [interface art.Test1914$Foo]' (class: PROXY CLASS)
diff --git a/test/1914-get-local-instance/src/art/Test1914.java b/test/1914-get-local-instance/src/art/Test1914.java
index c09f519db8..e47f9cbf38 100644
--- a/test/1914-get-local-instance/src/art/Test1914.java
+++ b/test/1914-get-local-instance/src/art/Test1914.java
@@ -18,7 +18,9 @@ package art;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.nio.ByteBuffer;
import java.util.concurrent.Semaphore;
import java.util.Arrays;
@@ -35,7 +37,7 @@ public class Test1914 {
public static void reportValue(Object val) {
System.out.println("\tValue is '" + val + "' (class: "
- + (val != null ? val.getClass() : "NULL") + ")");
+ + (val != null ? (val instanceof Proxy ? "PROXY CLASS" : val.getClass()) : "NULL") + ")");
}
public static void StaticMethod(Runnable safepoint) {
@@ -151,7 +153,10 @@ public class Test1914 {
private StackTrace.StackFrameData findStackFrame(Thread thr) {
for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
- if (frame.method.equals(target)) {
+ if (frame.method.equals(target) ||
+ (frame.method.getName().equals(target.getName()) &&
+ Arrays.deepEquals(frame.method.getParameterTypes(), target.getParameterTypes()) &&
+ ((Method)frame.method).getReturnType().equals(target.getReturnType()))) {
return frame;
}
}
@@ -163,6 +168,25 @@ public class Test1914 {
return klass.getDeclaredMethod(name, Runnable.class);
}
+ public static interface Foo {
+ public void InterfaceProxyMethod(Runnable r);
+ }
+
+ public static Object getProxyObject(final Class... k) {
+ return Proxy.newProxyInstance(
+ Test1914.class.getClassLoader(),
+ k,
+ (p, m, a) -> {
+ if (m.getName().equals("toString")) {
+ return "Proxy for " + Arrays.toString(k);
+ } else {
+ ((Runnable)a[0]).run();
+ reportValue(p);
+ return null;
+ }
+ });
+ }
+
public static void run() throws Exception {
Locals.EnableLocalVariableAccess();
final TestCase[] MAIN_TEST_CASES = new TestCase[] {
@@ -172,6 +196,8 @@ public class Test1914 {
getMethod(TargetClass.class, "InstanceMethod")),
new TestCase(new TargetClass("NativeInstanceMethodObject"),
getMethod(TargetClass.class, "NativeInstanceMethod")),
+ new TestCase(getProxyObject(Foo.class),
+ getMethod(Foo.class, "InterfaceProxyMethod")),
};
for (TestCase t: MAIN_TEST_CASES) {
diff --git a/test/1929-exception-catch-exception/build b/test/1929-exception-catch-exception/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/1929-exception-catch-exception/build
+++ b/test/1929-exception-catch-exception/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/1938-transform-abstract-single-impl/expected.txt b/test/1938-transform-abstract-single-impl/expected.txt
new file mode 100644
index 0000000000..6a06f9baab
--- /dev/null
+++ b/test/1938-transform-abstract-single-impl/expected.txt
@@ -0,0 +1,4 @@
+JNI_OnLoad called
+Running sayHi() - hello
+redefining TransformAbstract
+Running sayHi() - Goodbye
diff --git a/test/1938-transform-abstract-single-impl/info.txt b/test/1938-transform-abstract-single-impl/info.txt
new file mode 100644
index 0000000000..5df8306981
--- /dev/null
+++ b/test/1938-transform-abstract-single-impl/info.txt
@@ -0,0 +1,2 @@
+Tests that single-implementation abstract methods don't crash the runtime when
+their declaring class is redefined.
diff --git a/test/1938-transform-abstract-single-impl/run b/test/1938-transform-abstract-single-impl/run
new file mode 100755
index 0000000000..adb1a1c507
--- /dev/null
+++ b/test/1938-transform-abstract-single-impl/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --no-app-image
diff --git a/test/1938-transform-abstract-single-impl/src/Main.java b/test/1938-transform-abstract-single-impl/src/Main.java
new file mode 100644
index 0000000000..7ac2172eb4
--- /dev/null
+++ b/test/1938-transform-abstract-single-impl/src/Main.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import art.Redefinition;
+import java.util.Base64;
+public class Main {
+ static abstract class TransformAbstract {
+ public abstract void doSayHi();
+
+ public void sayHi() {
+ System.out.println("hello");
+ }
+ }
+
+ static final class TransformConcrete extends TransformAbstract {
+ public final void doSayHi() {
+ System.out.print("Running sayHi() - ");
+ sayHi();
+ }
+ }
+
+ public static native void ensureJitCompiled(Class k, String m);
+
+ /**
+ * base64 encoded class/dex file for
+ * static abstract class TransformAbstract {
+ * public abstract void doSayHi();
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAIQoABgAPCQAQABEIABIKABMAFAcAFgcAGQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAdkb1NheUhpAQAFc2F5SGkBAApTb3VyY2VGaWxlAQAJTWFpbi5q" +
+ "YXZhDAAHAAgHABoMABsAHAEAB0dvb2RieWUHAB0MAB4AHwcAIAEAFk1haW4kVHJhbnNmb3JtQWJz" +
+ "dHJhY3QBABFUcmFuc2Zvcm1BYnN0cmFjdAEADElubmVyQ2xhc3NlcwEAEGphdmEvbGFuZy9PYmpl" +
+ "Y3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2" +
+ "YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAARNYWlu" +
+ "BCAABQAGAAAAAAADAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAHAQB" +
+ "AAsACAAAAAEADAAIAAEACQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEACgAAAAoAAgAAAB8ACAAg" +
+ "AAIADQAAAAIADgAYAAAACgABAAUAFQAXBAg=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCQkoTiKzIz0l96rtsnUxdY4Kwx+YINWFHEAwAAcAAAAHhWNBIAAAAAAAAAAAADAAAV" +
+ "AAAAcAAAAAkAAADEAAAAAgAAAOgAAAABAAAAAAEAAAUAAAAIAQAAAQAAADABAAB0AgAAUAEAAKoB" +
+ "AACyAQAAuwEAANUBAADdAQAAAQIAACECAAA4AgAATAIAAGACAAB0AgAAfwIAAJICAACVAgAAmQIA" +
+ "AKYCAACvAgAAtQIAALoCAADDAgAAygIAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAA" +
+ "DAAAAAwAAAAIAAAAAAAAAA0AAAAIAAAApAEAAAcABAARAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAT" +
+ "AAAABAABABIAAAAFAAAAAAAAAAAAAAAABAAABQAAAAAAAAAKAAAAlAEAAOwCAAAAAAAAAgAAANwC" +
+ "AADiAgAAAQABAAEAAADRAgAABAAAAHAQBAAAAA4AAwABAAIAAADWAgAACAAAAGIAAAAaAQEAbiAD" +
+ "ABAADgBQAQAAAAAAAAAAAAAAAAAAAQAAAAYABjxpbml0PgAHR29vZGJ5ZQAYTE1haW4kVHJhbnNm" +
+ "b3JtQWJzdHJhY3Q7AAZMTWFpbjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsA" +
+ "HkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJM" +
+ "amF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07" +
+ "AAlNYWluLmphdmEAEVRyYW5zZm9ybUFic3RyYWN0AAFWAAJWTAALYWNjZXNzRmxhZ3MAB2RvU2F5" +
+ "SGkABG5hbWUAA291dAAHcHJpbnRsbgAFc2F5SGkABXZhbHVlABwABw4AHwAHDngAAgIBFBgBAgMC" +
+ "DiQIBBAXCwAAAQIAgIAE3AIBgQgAAQH0AgAAEAAAAAAAAAABAAAAAAAAAAEAAAAVAAAAcAAAAAIA" +
+ "AAAJAAAAxAAAAAMAAAACAAAA6AAAAAQAAAABAAAAAAEAAAUAAAAFAAAACAEAAAYAAAABAAAAMAEA" +
+ "AAMQAAABAAAAUAEAAAEgAAACAAAAXAEAAAYgAAABAAAAlAEAAAEQAAABAAAApAEAAAIgAAAVAAAA" +
+ "qgEAAAMgAAACAAAA0QIAAAQgAAACAAAA3AIAAAAgAAABAAAA7AIAAAAQAAABAAAAAAMAAA==");
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+
+ ensureJitCompiled(TransformAbstract.class, "sayHi");
+ ensureJitCompiled(TransformConcrete.class, "doSayHi");
+
+ TransformAbstract t1 = new TransformConcrete();
+ t1.doSayHi();
+
+ assertSingleImplementation(TransformAbstract.class, "doSayHi", true);
+
+ System.out.println("redefining TransformAbstract");
+ Redefinition.doCommonClassRedefinition(TransformAbstract.class, CLASS_BYTES, DEX_BYTES);
+
+ t1.doSayHi();
+ }
+
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
+ }
+ }
+}
diff --git a/test/1938-transform-abstract-single-impl/src/art/Redefinition.java b/test/1938-transform-abstract-single-impl/src/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/1938-transform-abstract-single-impl/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
+
+ public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = dex_file_bytes;
+ }
+ }
+
+ // A set of possible test configurations. Test should set this if they need to.
+ // This must be kept in sync with the defines in ti-agent/common_helper.cc
+ public static enum Config {
+ COMMON_REDEFINE(0),
+ COMMON_RETRANSFORM(1),
+ COMMON_TRANSFORM(2);
+
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
+ }
+
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
+ }
+
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]),
+ dex_files.toArray(new byte[0][]));
+ }
+
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(d.target.getCanonicalName(),
+ d.class_file_bytes,
+ d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+ byte[][] classfiles,
+ byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/1939-proxy-frames/expected.txt b/test/1939-proxy-frames/expected.txt
new file mode 100644
index 0000000000..a4c97c9bbe
--- /dev/null
+++ b/test/1939-proxy-frames/expected.txt
@@ -0,0 +1,8 @@
+Running public abstract void art.Test1939$Foo.InterfaceProxyMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public abstract void art.Test1939$Foo.InterfaceProxyMethod(java.lang.Runnable) got value: Proxy for [interface art.Test1939$Foo]
+Running public abstract void art.Test1939$Foo.InterfaceProxyMethod(java.lang.Runnable) with "GetLocalReference0" on remote thread.
+"GetLocalReference0" on public abstract void art.Test1939$Foo.InterfaceProxyMethod(java.lang.Runnable) failed due to JVMTI_ERROR_OPAQUE_FRAME
+Running public abstract void art.Test1939$Foo.InterfaceProxyMethod(java.lang.Runnable) with "GetProxyFrameLocation" on remote thread.
+"GetProxyFrameLocation" on public abstract void art.Test1939$Foo.InterfaceProxyMethod(java.lang.Runnable) got value: -1
+Running public abstract void art.Test1939$Foo.InterfaceProxyMethod(java.lang.Runnable) with "GetProxyFrameMethod" on remote thread.
+"GetProxyFrameMethod" on public abstract void art.Test1939$Foo.InterfaceProxyMethod(java.lang.Runnable) got value: public final void $Proxy0.InterfaceProxyMethod(java.lang.Runnable)
diff --git a/test/1939-proxy-frames/info.txt b/test/1939-proxy-frames/info.txt
new file mode 100644
index 0000000000..9fc3d62cd6
--- /dev/null
+++ b/test/1939-proxy-frames/info.txt
@@ -0,0 +1,2 @@
+Test for jvmti get local instance
+
diff --git a/test/1939-proxy-frames/local_instance.cc b/test/1939-proxy-frames/local_instance.cc
new file mode 100644
index 0000000000..dc833bfd90
--- /dev/null
+++ b/test/1939-proxy-frames/local_instance.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1939ProxyFrames {
+
+extern "C" JNIEXPORT jobject Java_art_Test1939_GetFrameMethod(JNIEnv* env,
+ jclass,
+ jthread thr,
+ jint depth) {
+ jmethodID m = nullptr;
+ jlong loc = -1;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetFrameLocation(thr, depth, &m, &loc))) {
+ return nullptr;
+ }
+ jclass klass = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetMethodDeclaringClass(m, &klass))) {
+ return nullptr;
+ }
+ jobject res = env->ToReflectedMethod(klass, m, false);
+ env->DeleteLocalRef(klass);
+ return res;
+}
+
+extern "C" JNIEXPORT jlong Java_art_Test1939_GetFrameLocation(JNIEnv* env,
+ jclass,
+ jthread thr,
+ jint depth) {
+ jmethodID m = nullptr;
+ jlong loc = -1;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetFrameLocation(thr, depth, &m, &loc));
+ return loc;
+}
+
+} // namespace Test1939ProxyFrames
+} // namespace art
+
diff --git a/test/1939-proxy-frames/run b/test/1939-proxy-frames/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/1939-proxy-frames/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1939-proxy-frames/src/Main.java b/test/1939-proxy-frames/src/Main.java
new file mode 100644
index 0000000000..85cab34d53
--- /dev/null
+++ b/test/1939-proxy-frames/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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) throws Exception {
+ art.Test1939.run();
+ }
+}
diff --git a/test/1939-proxy-frames/src/art/Breakpoint.java b/test/1939-proxy-frames/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1939-proxy-frames/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1939-proxy-frames/src/art/Locals.java b/test/1939-proxy-frames/src/art/Locals.java
new file mode 100644
index 0000000000..22e21be398
--- /dev/null
+++ b/test/1939-proxy-frames/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1939-proxy-frames/src/art/StackTrace.java b/test/1939-proxy-frames/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1939-proxy-frames/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1939-proxy-frames/src/art/Suspension.java b/test/1939-proxy-frames/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1939-proxy-frames/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1939-proxy-frames/src/art/Test1939.java b/test/1939-proxy-frames/src/art/Test1939.java
new file mode 100644
index 0000000000..83d0d2ca4b
--- /dev/null
+++ b/test/1939-proxy-frames/src/art/Test1939.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Test1939 {
+ public static interface SafepointFunction {
+ public void invoke(
+ Thread thread,
+ Method target,
+ int depth) throws Exception;
+ }
+
+ public static interface GetterFunction {
+ public Object GetVar(Thread t, int depth);
+ }
+
+ public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, int depth) {
+ try {
+ Object res = get.GetVar(t, depth);
+ System.out.println(this + " on " + method + " got value: " + res);
+ } catch (Exception e) {
+ System.out.println(this + " on " + method + " failed due to " + e.getMessage());
+ }
+ }
+ public String toString() {
+ return "\"Get" + type + "\"";
+ }
+ };
+ }
+
+ public static class TestCase {
+ public final Object thiz;
+ public final Method target;
+
+ public TestCase(Method target) {
+ this(null, target);
+ }
+ public TestCase(Object thiz, Method target) {
+ this.thiz = thiz;
+ this.target = target;
+ }
+
+ public static class ThreadPauser implements Runnable {
+ public final Semaphore sem_wakeup_main;
+ public final Semaphore sem_wait;
+
+ public ThreadPauser() {
+ sem_wakeup_main = new Semaphore(0);
+ sem_wait = new Semaphore(0);
+ }
+
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
+ }
+
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public void exec(final SafepointFunction safepoint) throws Exception {
+ System.out.println("Running " + target + " with " + safepoint + " on remote thread.");
+ final ThreadPauser pause = new ThreadPauser();
+ Thread remote = new Thread(
+ () -> {
+ try {
+ target.invoke(thiz, pause);
+ } catch (Exception e) {
+ throw new Error("Error invoking remote thread " + Thread.currentThread(), e);
+ }
+ },
+ "remote thread for " + target + " with " + safepoint);
+ remote.start();
+ pause.waitForOtherThreadToPause();
+ try {
+ Suspension.suspend(remote);
+ StackTrace.StackFrameData frame = findStackFrame(remote);
+ safepoint.invoke(remote, target, frame.depth);
+ } finally {
+ Suspension.resume(remote);
+ pause.wakeupOtherThread();
+ remote.join();
+ }
+ }
+
+ private StackTrace.StackFrameData findStackFrame(Thread thr) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target) ||
+ (frame.method.getName().equals(target.getName()) &&
+ Arrays.deepEquals(frame.method.getParameterTypes(), target.getParameterTypes()) &&
+ ((Method)frame.method).getReturnType().equals(target.getReturnType()))) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+ }
+
+ public static Method getMethod(Class<?> klass, String name) throws Exception {
+ return klass.getDeclaredMethod(name, Runnable.class);
+ }
+
+ public static interface Foo {
+ public void InterfaceProxyMethod(Runnable r);
+ }
+
+ public static Object getProxyObject(final Class... k) {
+ return Proxy.newProxyInstance(
+ Test1939.class.getClassLoader(),
+ k,
+ (p, m, a) -> {
+ if (m.getName().equals("toString")) {
+ return "Proxy for " + Arrays.toString(k);
+ } else {
+ ((Runnable)a[0]).run();
+ return null;
+ }
+ });
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ TestCase test = new TestCase(
+ getProxyObject(Foo.class), getMethod(Foo.class, "InterfaceProxyMethod"));
+ test.exec(NamedGet("This", Locals::GetLocalInstance));
+ test.exec(NamedGet("LocalReference0", (t, d) -> Locals.GetLocalVariableObject(t, d, 0)));
+ test.exec(NamedGet("ProxyFrameLocation", (t, d) -> Long.valueOf(GetFrameLocation(t, d))));
+ test.exec(NamedGet("ProxyFrameMethod", Test1939::GetFrameMethod));
+ }
+
+ public static native long GetFrameLocation(Thread thr, int depth);
+ public static native Executable GetFrameMethod(Thread thr, int depth);
+}
+
diff --git a/test/450-checker-types/build b/test/450-checker-types/build
index 947ec9a560..3721955670 100755
--- a/test/450-checker-types/build
+++ b/test/450-checker-types/build
@@ -21,6 +21,6 @@ export USE_JACK=false
export DESUGAR=false
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/458-checker-instruct-simplification/build b/test/458-checker-instruct-simplification/build
index 947ec9a560..3721955670 100755
--- a/test/458-checker-instruct-simplification/build
+++ b/test/458-checker-instruct-simplification/build
@@ -21,6 +21,6 @@ export USE_JACK=false
export DESUGAR=false
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/463-checker-boolean-simplifier/build b/test/463-checker-boolean-simplifier/build
index 947ec9a560..3721955670 100755
--- a/test/463-checker-boolean-simplifier/build
+++ b/test/463-checker-boolean-simplifier/build
@@ -21,6 +21,6 @@ export USE_JACK=false
export DESUGAR=false
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/476-checker-ctor-fence-redun-elim/build b/test/476-checker-ctor-fence-redun-elim/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/476-checker-ctor-fence-redun-elim/build
+++ b/test/476-checker-ctor-fence-redun-elim/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/482-checker-loop-back-edge-use/build b/test/482-checker-loop-back-edge-use/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/482-checker-loop-back-edge-use/build
+++ b/test/482-checker-loop-back-edge-use/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/484-checker-register-hints/build b/test/484-checker-register-hints/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/484-checker-register-hints/build
+++ b/test/484-checker-register-hints/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/530-checker-lse/build b/test/530-checker-lse/build
index 42b99ad9f8..10ffcc537d 100755
--- a/test/530-checker-lse/build
+++ b/test/530-checker-lse/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/549-checker-types-merge/build b/test/549-checker-types-merge/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/549-checker-types-merge/build
+++ b/test/549-checker-types-merge/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/565-checker-doublenegbitwise/build b/test/565-checker-doublenegbitwise/build
index 947ec9a560..3721955670 100755
--- a/test/565-checker-doublenegbitwise/build
+++ b/test/565-checker-doublenegbitwise/build
@@ -21,6 +21,6 @@ export USE_JACK=false
export DESUGAR=false
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/565-checker-rotate/build b/test/565-checker-rotate/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/565-checker-rotate/build
+++ b/test/565-checker-rotate/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/566-checker-signum/build b/test/566-checker-signum/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/566-checker-signum/build
+++ b/test/566-checker-signum/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/567-checker-compare/build b/test/567-checker-compare/build
index 1d269dcdb2..10ffcc537d 100644
--- a/test/567-checker-compare/build
+++ b/test/567-checker-compare/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/570-checker-osr/build b/test/570-checker-osr/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/570-checker-osr/build
+++ b/test/570-checker-osr/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/586-checker-null-array-get/build b/test/586-checker-null-array-get/build
index 947ec9a560..3721955670 100755
--- a/test/586-checker-null-array-get/build
+++ b/test/586-checker-null-array-get/build
@@ -21,6 +21,6 @@ export USE_JACK=false
export DESUGAR=false
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/593-checker-boolean-2-integral-conv/build b/test/593-checker-boolean-2-integral-conv/build
index 947ec9a560..3721955670 100755
--- a/test/593-checker-boolean-2-integral-conv/build
+++ b/test/593-checker-boolean-2-integral-conv/build
@@ -21,6 +21,6 @@ export USE_JACK=false
export DESUGAR=false
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/597-deopt-invoke-stub/run b/test/597-deopt-invoke-stub/run
index bc04498bfe..53b7c4cc71 100644
--- a/test/597-deopt-invoke-stub/run
+++ b/test/597-deopt-invoke-stub/run
@@ -14,5 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# We want to run in debuggable mode and compiled.
-exec ${RUN} --jit -Xcompiler-option --debuggable "${@}"
+# In order to test deoptimizing at quick-to-interpreter bridge,
+# we want to run in debuggable mode with jit compilation.
+# We also bump up the jit threshold to 10 to make sure that the method
+# that should be interpreted is not compiled.
+exec ${RUN} --jit --runtime-option -Xjitthreshold:10000 -Xcompiler-option --debuggable "${@}"
diff --git a/test/611-checker-simplify-if/build b/test/611-checker-simplify-if/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/611-checker-simplify-if/build
+++ b/test/611-checker-simplify-if/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/618-checker-induction/build b/test/618-checker-induction/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/618-checker-induction/build
+++ b/test/618-checker-induction/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/910-methods/build b/test/910-methods/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/910-methods/build
+++ b/test/910-methods/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/910-methods/check b/test/910-methods/check
index f9552ada26..e6f7d7773f 100644
--- a/test/910-methods/check
+++ b/test/910-methods/check
@@ -19,7 +19,7 @@ if [[ "$USE_JACK" == true ]]; then
patch -p0 expected.txt < expected_jack.diff
fi
-if [[ "$DX" == 'd8' ]]; then
+if [[ "$USE_D8" == true ]]; then
patch -p0 expected.txt < expected_d8.diff
fi
diff --git a/test/911-get-stack-trace/build b/test/911-get-stack-trace/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/911-get-stack-trace/build
+++ b/test/911-get-stack-trace/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/913-heaps/build b/test/913-heaps/build
index 42b99ad9f8..10ffcc537d 100644
--- a/test/913-heaps/build
+++ b/test/913-heaps/build
@@ -15,6 +15,6 @@
# limitations under the License.
# See b/65168732
-export DX=$ANDROID_HOST_OUT/bin/dx
+export USE_D8=false
./default-build "$@"
diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc
index 570ade364d..ef67acec98 100644
--- a/test/983-source-transform-verify/source_transform.cc
+++ b/test/983-source-transform-verify/source_transform.cc
@@ -29,6 +29,7 @@
#include "base/macros.h"
#include "bytecode_utils.h"
#include "dex_file.h"
+#include "dex_file_loader.h"
#include "dex_instruction.h"
#include "jit/jit.h"
#include "native_stack_dump.h"
@@ -66,14 +67,14 @@ void JNICALL CheckDexFileHook(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
return;
}
std::string error;
- std::unique_ptr<const DexFile> dex(DexFile::Open(class_data,
- class_data_len,
- "fake_location.dex",
- /*location_checksum*/ 0,
- /*oat_dex_file*/ nullptr,
- /*verify*/ true,
- /*verify_checksum*/ true,
- &error));
+ std::unique_ptr<const DexFile> dex(DexFileLoader::Open(class_data,
+ class_data_len,
+ "fake_location.dex",
+ /*location_checksum*/ 0,
+ /*oat_dex_file*/ nullptr,
+ /*verify*/ true,
+ /*verify_checksum*/ true,
+ &error));
if (dex.get() == nullptr) {
std::cout << "Failed to verify dex file for " << name << " because " << error << std::endl;
return;
diff --git a/test/Android.bp b/test/Android.bp
index 31474d5107..b737345729 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -277,6 +277,7 @@ art_cc_defaults {
"1930-monitor-info/monitor.cc",
"1932-monitor-events-misc/monitor_misc.cc",
"1934-jvmti-signal-thread/signal_threads.cc",
+ "1939-proxy-frames/local_instance.cc",
],
shared_libs: [
"libbase",
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index d37e6bc4f3..9e1afc8cb5 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -71,9 +71,9 @@ VDEX_FILTER=""
PROFILE="n"
RANDOM_PROFILE="n"
# The normal dex2oat timeout.
-DEX2OAT_TIMEOUT="60"
+DEX2OAT_TIMEOUT="300" # 5 mins
# The *hard* timeout where we really start trying to kill the dex2oat.
-DEX2OAT_RT_TIMEOUT="90"
+DEX2OAT_RT_TIMEOUT="360" # 6 mins
# if "y", set -Xstacktracedir and inform the test of its location. When
# this is set, stack trace dumps (from signal 3) will be written to a file
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 229d618a2d..47b2f22a3c 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1,5 +1,12 @@
[
{
+ "tests": [ "1939-proxy-frames", "1914-get-local-instance" ],
+ "description": ["Test 1939 & 1914 seems to consistently fail in gcstress on 64 bit with",
+ "a proxy this object having no associated class!"],
+ "variant": "gcstress",
+ "bug": "http://b/67679263"
+ },
+ {
"tests": "1934-jvmti-signal-thread",
"description": ["Disables 1934-jvmti-signal-thread in tracing configurations"],
"variant": "trace | stream",
@@ -237,7 +244,7 @@
},
{
"tests": "597-deopt-invoke-stub",
- "variant": "interp-ac | interpreter | optimizing | trace | stream",
+ "variant": "speed-profile | interp-ac | interpreter | optimizing | trace | stream",
"description": ["This test expects JIT compilation and no AOT for",
"testing deoptimizing at quick-to-interpreter bridge."]
},
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 49444d42cb..492b792239 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -96,7 +96,9 @@ if target.has_key('run-test'):
run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
'art/test/testrunner/testrunner.py')]
run_test_command += target.get('run-test', [])
- run_test_command += ['-j', str(n_threads)]
+ # Let testrunner compute concurrency based on #cpus.
+ # b/65822340
+ # run_test_command += ['-j', str(n_threads)]
run_test_command += ['-b']
run_test_command += ['--host']
run_test_command += ['--verbose']
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 929fca6b5d..ca29d0a484 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -237,6 +237,7 @@ def setup_test_env():
n_thread = get_default_threads('target')
else:
n_thread = get_default_threads('host')
+ print_text("Concurrency: " + str(n_thread) + "\n")
global semaphore
semaphore = threading.Semaphore(n_thread)
@@ -480,7 +481,7 @@ def run_test(command, test, test_variant, test_name):
if test_passed:
print_test_info(test_name, 'PASS')
else:
- failed_tests.append((test_name, script_output))
+ failed_tests.append((test_name, str(command) + "\n" + script_output))
if not env.ART_TEST_KEEP_GOING:
stop_testrunner = True
print_test_info(test_name, 'FAIL', ('%s\n%s') % (
@@ -535,10 +536,17 @@ def print_test_info(test_name, result, failed_test_info=""):
total_test_count)
if result == 'FAIL' or result == 'TIMEOUT':
- info += ('%s %s %s\n') % (
- progress_info,
- test_name,
- COLOR_ERROR + result + COLOR_NORMAL)
+ if not verbose:
+ info += ('%s %s %s\n') % (
+ progress_info,
+ test_name,
+ COLOR_ERROR + result + COLOR_NORMAL)
+ else:
+ info += ('%s %s %s\n%s\n') % (
+ progress_info,
+ test_name,
+ COLOR_ERROR + result + COLOR_NORMAL,
+ failed_test_info)
else:
result_text = ''
if result == 'PASS':
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index cf31e2e5bd..5eccba1327 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -20,9 +20,9 @@ include art/build/Android.common_path.mk
# --- ahat.jar ----------------
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAR_MANIFEST := src/manifest.txt
-LOCAL_JAVA_RESOURCE_FILES := $(LOCAL_PATH)/src/style.css
+LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
+LOCAL_JAR_MANIFEST := etc/ahat.mf
+LOCAL_JAVA_RESOURCE_FILES := $(LOCAL_PATH)/etc/style.css
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := ahat
@@ -49,9 +49,9 @@ ifneq ($(EMMA_INSTRUMENT),true)
include $(CLEAR_VARS)
LOCAL_MODULE := ahat-test-dump
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, test-dump)
+LOCAL_SRC_FILES := $(call all-java-files-under, src/test-dump)
LOCAL_PROGUARD_ENABLED := obfuscation
-LOCAL_PROGUARD_FLAG_FILES := test-dump/config.pro
+LOCAL_PROGUARD_FLAG_FILES := etc/test-dump.pro
include $(BUILD_JAVA_LIBRARY)
# Determine the location of the test-dump.jar, test-dump.hprof, and proguard
@@ -87,15 +87,15 @@ $(AHAT_TEST_DUMP_BASE_HPROF): $(AHAT_TEST_DUMP_JAR) $(AHAT_TEST_DUMP_DEPENDENCIE
# --- ahat-tests.jar --------------
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, test)
-LOCAL_JAR_MANIFEST := test/manifest.txt
+LOCAL_SRC_FILES := $(call all-java-files-under, src/test)
+LOCAL_JAR_MANIFEST := etc/ahat-tests.mf
LOCAL_JAVA_RESOURCE_FILES := \
$(AHAT_TEST_DUMP_HPROF) \
$(AHAT_TEST_DUMP_BASE_HPROF) \
$(AHAT_TEST_DUMP_PROGUARD_MAP) \
- $(LOCAL_PATH)/test-dump/L.hprof \
- $(LOCAL_PATH)/test-dump/O.hprof \
- $(LOCAL_PATH)/test-dump/RI.hprof
+ $(LOCAL_PATH)/etc/L.hprof \
+ $(LOCAL_PATH)/etc/O.hprof \
+ $(LOCAL_PATH)/etc/RI.hprof
LOCAL_STATIC_JAVA_LIBRARIES := ahat junit-host
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE_TAGS := tests
diff --git a/tools/ahat/test-dump/L.hprof b/tools/ahat/etc/L.hprof
index 1acdf7965d..1acdf7965d 100644
--- a/tools/ahat/test-dump/L.hprof
+++ b/tools/ahat/etc/L.hprof
Binary files differ
diff --git a/tools/ahat/test-dump/O.hprof b/tools/ahat/etc/O.hprof
index d474c6c6b4..d474c6c6b4 100644
--- a/tools/ahat/test-dump/O.hprof
+++ b/tools/ahat/etc/O.hprof
Binary files differ
diff --git a/tools/ahat/etc/README.txt b/tools/ahat/etc/README.txt
new file mode 100644
index 0000000000..e9b5b22dae
--- /dev/null
+++ b/tools/ahat/etc/README.txt
@@ -0,0 +1,9 @@
+L.hprof
+ A version of the test-dump hprof generated on Android L, with one of the
+ ROOT_DEBUGGER records manually changed to a ROOT_FINALIZING record.
+
+O.hprof
+ A version of the test-dump hprof generated on Android O.
+
+RI.hprof
+ A version of the test-dump hprof generated on the reference implementation.
diff --git a/tools/ahat/test-dump/RI.hprof b/tools/ahat/etc/RI.hprof
index 9482542a7f..9482542a7f 100644
--- a/tools/ahat/test-dump/RI.hprof
+++ b/tools/ahat/etc/RI.hprof
Binary files differ
diff --git a/tools/ahat/test/manifest.txt b/tools/ahat/etc/ahat-tests.mf
index af17fadded..af17fadded 100644
--- a/tools/ahat/test/manifest.txt
+++ b/tools/ahat/etc/ahat-tests.mf
diff --git a/tools/ahat/src/manifest.txt b/tools/ahat/etc/ahat.mf
index 1753406e6e..1753406e6e 100644
--- a/tools/ahat/src/manifest.txt
+++ b/tools/ahat/etc/ahat.mf
diff --git a/tools/ahat/src/style.css b/tools/ahat/etc/style.css
index 47fae1d551..47fae1d551 100644
--- a/tools/ahat/src/style.css
+++ b/tools/ahat/etc/style.css
diff --git a/tools/ahat/test-dump/config.pro b/tools/ahat/etc/test-dump.pro
index 284e4b8621..284e4b8621 100644
--- a/tools/ahat/test-dump/config.pro
+++ b/tools/ahat/etc/test-dump.pro
diff --git a/tools/ahat/src/AhatHandler.java b/tools/ahat/src/main/com/android/ahat/AhatHandler.java
index d4b4d1b107..d4b4d1b107 100644
--- a/tools/ahat/src/AhatHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/AhatHandler.java
diff --git a/tools/ahat/src/AhatHttpHandler.java b/tools/ahat/src/main/com/android/ahat/AhatHttpHandler.java
index 1d05a66653..1d05a66653 100644
--- a/tools/ahat/src/AhatHttpHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/AhatHttpHandler.java
diff --git a/tools/ahat/src/BitmapHandler.java b/tools/ahat/src/main/com/android/ahat/BitmapHandler.java
index 836aef67b8..836aef67b8 100644
--- a/tools/ahat/src/BitmapHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/BitmapHandler.java
diff --git a/tools/ahat/src/Column.java b/tools/ahat/src/main/com/android/ahat/Column.java
index 819e586ef9..819e586ef9 100644
--- a/tools/ahat/src/Column.java
+++ b/tools/ahat/src/main/com/android/ahat/Column.java
diff --git a/tools/ahat/src/Doc.java b/tools/ahat/src/main/com/android/ahat/Doc.java
index 5a70c4c74b..5a70c4c74b 100644
--- a/tools/ahat/src/Doc.java
+++ b/tools/ahat/src/main/com/android/ahat/Doc.java
diff --git a/tools/ahat/src/DocString.java b/tools/ahat/src/main/com/android/ahat/DocString.java
index 76e9e80d46..76e9e80d46 100644
--- a/tools/ahat/src/DocString.java
+++ b/tools/ahat/src/main/com/android/ahat/DocString.java
diff --git a/tools/ahat/src/DominatedList.java b/tools/ahat/src/main/com/android/ahat/DominatedList.java
index 75133b2184..75133b2184 100644
--- a/tools/ahat/src/DominatedList.java
+++ b/tools/ahat/src/main/com/android/ahat/DominatedList.java
diff --git a/tools/ahat/src/HeapTable.java b/tools/ahat/src/main/com/android/ahat/HeapTable.java
index b04f2aebf7..b04f2aebf7 100644
--- a/tools/ahat/src/HeapTable.java
+++ b/tools/ahat/src/main/com/android/ahat/HeapTable.java
diff --git a/tools/ahat/src/HtmlDoc.java b/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
index 5a22fc75fe..5a22fc75fe 100644
--- a/tools/ahat/src/HtmlDoc.java
+++ b/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
diff --git a/tools/ahat/src/HtmlEscaper.java b/tools/ahat/src/main/com/android/ahat/HtmlEscaper.java
index 75a68277d3..75a68277d3 100644
--- a/tools/ahat/src/HtmlEscaper.java
+++ b/tools/ahat/src/main/com/android/ahat/HtmlEscaper.java
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/main/com/android/ahat/Main.java
index 31c485d851..31c485d851 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/main/com/android/ahat/Main.java
diff --git a/tools/ahat/src/Menu.java b/tools/ahat/src/main/com/android/ahat/Menu.java
index 6d38dc5731..6d38dc5731 100644
--- a/tools/ahat/src/Menu.java
+++ b/tools/ahat/src/main/com/android/ahat/Menu.java
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/main/com/android/ahat/ObjectHandler.java
index 79f8b76c92..bfd5d5cacd 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/ObjectHandler.java
@@ -19,6 +19,7 @@ package com.android.ahat;
import com.android.ahat.heapdump.AhatArrayInstance;
import com.android.ahat.heapdump.AhatClassInstance;
import com.android.ahat.heapdump.AhatClassObj;
+import com.android.ahat.heapdump.AhatHeap;
import com.android.ahat.heapdump.AhatInstance;
import com.android.ahat.heapdump.AhatSnapshot;
import com.android.ahat.heapdump.DiffFields;
@@ -66,7 +67,10 @@ class ObjectHandler implements AhatHandler {
doc.big(Summarizer.summarize(inst));
printAllocationSite(doc, query, inst);
- printGcRootPath(doc, query, inst);
+
+ if (!inst.isUnreachable()) {
+ printGcRootPath(doc, query, inst);
+ }
doc.section("Object Info");
AhatClassObj cls = inst.getClassObj();
@@ -257,23 +261,54 @@ class ObjectHandler implements AhatHandler {
if (bitmap != null) {
doc.section("Bitmap Image");
doc.println(DocString.image(
- DocString.formattedUri("bitmap?id=%d", bitmap.getId()), "bitmap image"));
+ DocString.formattedUri("bitmap?id=0x%x", bitmap.getId()), "bitmap image"));
}
}
private void printGcRootPath(Doc doc, Query query, AhatInstance inst) {
doc.section("Sample Path from GC Root");
List<PathElement> path = inst.getPathFromGcRoot();
- doc.table(new Column(""), new Column("Path Element"));
- doc.row(DocString.text("(rooted)"),
- DocString.link(DocString.uri("root"), DocString.text("ROOT")));
- for (PathElement element : path) {
- DocString label = DocString.text("→ ");
- label.append(Summarizer.summarize(element.instance));
- label.append(element.field);
- doc.row(DocString.text(element.isDominator ? "(dominator)" : ""), label);
+
+ // Add a dummy PathElement as a marker for the root.
+ final PathElement root = new PathElement(null, null);
+ path.add(0, root);
+
+ HeapTable.TableConfig<PathElement> table = new HeapTable.TableConfig<PathElement>() {
+ public String getHeapsDescription() {
+ return "Bytes Retained by Heap (Dominators Only)";
+ }
+
+ public long getSize(PathElement element, AhatHeap heap) {
+ if (element == root) {
+ return heap.getSize().getSize();
+ }
+ if (element.isDominator) {
+ return element.instance.getRetainedSize(heap).getSize();
+ }
+ return 0;
+ }
+
+ public List<HeapTable.ValueConfig<PathElement>> getValueConfigs() {
+ HeapTable.ValueConfig<PathElement> value = new HeapTable.ValueConfig<PathElement>() {
+ public String getDescription() {
+ return "Path Element";
+ }
+
+ public DocString render(PathElement element) {
+ if (element == root) {
+ return DocString.link(DocString.uri("rooted"), DocString.text("ROOT"));
+ } else {
+ DocString label = DocString.text("→ ");
+ label.append(Summarizer.summarize(element.instance));
+ label.append(element.field);
+ return label;
+ }
+ }
+ };
+ return Collections.singletonList(value);
+ }
};
- doc.end();
+ HeapTable.render(doc, query, DOMINATOR_PATH_ID, table, mSnapshot, path);
}
public void printDominatedObjects(Doc doc, Query query, AhatInstance inst) {
diff --git a/tools/ahat/src/ObjectsHandler.java b/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java
index 1a8f018bd5..1a8f018bd5 100644
--- a/tools/ahat/src/ObjectsHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java
diff --git a/tools/ahat/src/OverviewHandler.java b/tools/ahat/src/main/com/android/ahat/OverviewHandler.java
index c9f84259a9..c9f84259a9 100644
--- a/tools/ahat/src/OverviewHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/OverviewHandler.java
diff --git a/tools/ahat/src/Query.java b/tools/ahat/src/main/com/android/ahat/Query.java
index f910608771..9c2783c081 100644
--- a/tools/ahat/src/Query.java
+++ b/tools/ahat/src/main/com/android/ahat/Query.java
@@ -65,7 +65,7 @@ class Query {
*/
public long getLong(String name, long defaultValue) {
String value = get(name, null);
- return value == null ? defaultValue : Long.parseLong(value);
+ return value == null ? defaultValue : Long.decode(value);
}
/**
@@ -73,7 +73,7 @@ class Query {
*/
public int getInt(String name, int defaultValue) {
String value = get(name, null);
- return value == null ? defaultValue : Integer.parseInt(value);
+ return value == null ? defaultValue : Integer.decode(value);
}
/**
diff --git a/tools/ahat/src/RootedHandler.java b/tools/ahat/src/main/com/android/ahat/RootedHandler.java
index 26451a3963..26451a3963 100644
--- a/tools/ahat/src/RootedHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/RootedHandler.java
diff --git a/tools/ahat/src/SiteHandler.java b/tools/ahat/src/main/com/android/ahat/SiteHandler.java
index 543eaa376a..543eaa376a 100644
--- a/tools/ahat/src/SiteHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/SiteHandler.java
diff --git a/tools/ahat/src/SitePrinter.java b/tools/ahat/src/main/com/android/ahat/SitePrinter.java
index 32037f4414..32037f4414 100644
--- a/tools/ahat/src/SitePrinter.java
+++ b/tools/ahat/src/main/com/android/ahat/SitePrinter.java
diff --git a/tools/ahat/src/SizeTable.java b/tools/ahat/src/main/com/android/ahat/SizeTable.java
index 46e395669f..46e395669f 100644
--- a/tools/ahat/src/SizeTable.java
+++ b/tools/ahat/src/main/com/android/ahat/SizeTable.java
diff --git a/tools/ahat/src/StaticHandler.java b/tools/ahat/src/main/com/android/ahat/StaticHandler.java
index 4a68f1c12f..4a68f1c12f 100644
--- a/tools/ahat/src/StaticHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/StaticHandler.java
diff --git a/tools/ahat/src/SubsetSelector.java b/tools/ahat/src/main/com/android/ahat/SubsetSelector.java
index 79399c178b..79399c178b 100644
--- a/tools/ahat/src/SubsetSelector.java
+++ b/tools/ahat/src/main/com/android/ahat/SubsetSelector.java
diff --git a/tools/ahat/src/Summarizer.java b/tools/ahat/src/main/com/android/ahat/Summarizer.java
index 50b2e4b3b3..ae0776ab0b 100644
--- a/tools/ahat/src/Summarizer.java
+++ b/tools/ahat/src/main/com/android/ahat/Summarizer.java
@@ -51,7 +51,9 @@ class Summarizer {
}
// Annotate unreachable objects as such.
- if (!inst.isReachable()) {
+ if (inst.isWeaklyReachable()) {
+ formatted.append("weak ");
+ } else if (inst.isUnreachable()) {
formatted.append("unreachable ");
}
@@ -65,7 +67,7 @@ class Summarizer {
// Don't make links to placeholder objects.
formatted.append(linkText);
} else {
- URI objTarget = DocString.formattedUri("object?id=%d", inst.getId());
+ URI objTarget = DocString.formattedUri("object?id=0x%x", inst.getId());
formatted.appendLink(objTarget, linkText);
}
@@ -100,7 +102,7 @@ class Summarizer {
AhatInstance bitmap = inst.getAssociatedBitmapInstance();
String thumbnail = "";
if (bitmap != null) {
- URI uri = DocString.formattedUri("bitmap?id=%d", bitmap.getId());
+ URI uri = DocString.formattedUri("bitmap?id=0x%x", bitmap.getId());
formatted.appendThumbnail(uri, "bitmap image");
}
return formatted;
diff --git a/tools/ahat/src/dominators/DominatorsComputation.java b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java
index 58b7b59f9a..58b7b59f9a 100644
--- a/tools/ahat/src/dominators/DominatorsComputation.java
+++ b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java
diff --git a/tools/ahat/src/heapdump/AhatArrayInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java
index 50a4805bed..50a4805bed 100644
--- a/tools/ahat/src/heapdump/AhatArrayInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java
diff --git a/tools/ahat/src/heapdump/AhatClassInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
index 94efa5049f..94efa5049f 100644
--- a/tools/ahat/src/heapdump/AhatClassInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
diff --git a/tools/ahat/src/heapdump/AhatClassObj.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java
index be0f71306e..be0f71306e 100644
--- a/tools/ahat/src/heapdump/AhatClassObj.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java
diff --git a/tools/ahat/src/heapdump/AhatField.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatField.java
index a25ee2869d..a25ee2869d 100644
--- a/tools/ahat/src/heapdump/AhatField.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatField.java
diff --git a/tools/ahat/src/heapdump/AhatHeap.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java
index b8897a182c..b8897a182c 100644
--- a/tools/ahat/src/heapdump/AhatHeap.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java
diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
index c04448728f..cb2d738f23 100644
--- a/tools/ahat/src/heapdump/AhatInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
@@ -136,13 +136,28 @@ public abstract class AhatInstance implements Diffable<AhatInstance>,
}
/**
- * Returns whether this object is strongly-reachable.
+ * Returns true if this object is strongly-reachable.
*/
- public boolean isReachable() {
+ public boolean isStronglyReachable() {
return mImmediateDominator != null;
}
/**
+ * Returns true if this object is reachable only through a
+ * soft/weak/phantom/finalizer reference.
+ */
+ public boolean isWeaklyReachable() {
+ return !isStronglyReachable() && mNextInstanceToGcRoot != null;
+ }
+
+ /**
+ * Returns true if this object is completely unreachable.
+ */
+ public boolean isUnreachable() {
+ return !isStronglyReachable() && !isWeaklyReachable();
+ }
+
+ /**
* Returns the heap that this instance is allocated on.
*/
public AhatHeap getHeap() {
@@ -499,6 +514,10 @@ public abstract class AhatInstance implements Diffable<AhatInstance>,
} else {
if (ref.ref.mSoftReverseReferences == null) {
ref.ref.mSoftReverseReferences = new ArrayList<AhatInstance>();
+ if (ref.ref.mNextInstanceToGcRoot == null) {
+ ref.ref.mNextInstanceToGcRoot = ref.src;
+ ref.ref.mNextInstanceToGcRootField = ref.field;
+ }
}
ref.ref.mSoftReverseReferences.add(ref.src);
}
diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderClassObj.java
index 07f5b50012..07f5b50012 100644
--- a/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderClassObj.java
diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderInstance.java
index 884940370d..884940370d 100644
--- a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatPlaceHolderInstance.java
diff --git a/tools/ahat/src/heapdump/AhatSnapshot.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java
index 945966cec7..945966cec7 100644
--- a/tools/ahat/src/heapdump/AhatSnapshot.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java
diff --git a/tools/ahat/src/heapdump/Diff.java b/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java
index 98c7e58d56..98c7e58d56 100644
--- a/tools/ahat/src/heapdump/Diff.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java
diff --git a/tools/ahat/src/heapdump/DiffFields.java b/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java
index e3c671fe21..e3c671fe21 100644
--- a/tools/ahat/src/heapdump/DiffFields.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java
diff --git a/tools/ahat/src/heapdump/Diffable.java b/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java
index 53442c857e..53442c857e 100644
--- a/tools/ahat/src/heapdump/Diffable.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java
diff --git a/tools/ahat/src/heapdump/DiffedFieldValue.java b/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java
index 3cd273ed98..3cd273ed98 100644
--- a/tools/ahat/src/heapdump/DiffedFieldValue.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java
diff --git a/tools/ahat/src/heapdump/DominatorReferenceIterator.java b/tools/ahat/src/main/com/android/ahat/heapdump/DominatorReferenceIterator.java
index 0b99e496cc..0b99e496cc 100644
--- a/tools/ahat/src/heapdump/DominatorReferenceIterator.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/DominatorReferenceIterator.java
diff --git a/tools/ahat/src/heapdump/Field.java b/tools/ahat/src/main/com/android/ahat/heapdump/Field.java
index dff401796a..dff401796a 100644
--- a/tools/ahat/src/heapdump/Field.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Field.java
diff --git a/tools/ahat/src/heapdump/FieldValue.java b/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java
index 20e6da7271..20e6da7271 100644
--- a/tools/ahat/src/heapdump/FieldValue.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java
diff --git a/tools/ahat/src/heapdump/HprofFormatException.java b/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java
index 0e128cd50a..0e128cd50a 100644
--- a/tools/ahat/src/heapdump/HprofFormatException.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java
diff --git a/tools/ahat/src/heapdump/Instances.java b/tools/ahat/src/main/com/android/ahat/heapdump/Instances.java
index 085144650f..085144650f 100644
--- a/tools/ahat/src/heapdump/Instances.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Instances.java
diff --git a/tools/ahat/src/heapdump/Parser.java b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
index 756b7d2554..756b7d2554 100644
--- a/tools/ahat/src/heapdump/Parser.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
diff --git a/tools/ahat/src/heapdump/PathElement.java b/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java
index 196a24628c..196a24628c 100644
--- a/tools/ahat/src/heapdump/PathElement.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java
diff --git a/tools/ahat/src/heapdump/Reference.java b/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java
index 980f2780b6..980f2780b6 100644
--- a/tools/ahat/src/heapdump/Reference.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java
diff --git a/tools/ahat/src/heapdump/RootType.java b/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java
index af552ea2c9..af552ea2c9 100644
--- a/tools/ahat/src/heapdump/RootType.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java
diff --git a/tools/ahat/src/heapdump/Site.java b/tools/ahat/src/main/com/android/ahat/heapdump/Site.java
index 821493f1be..523550ad2c 100644
--- a/tools/ahat/src/heapdump/Site.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Site.java
@@ -186,7 +186,7 @@ public class Site implements Diffable<Site> {
// Add all reachable objects allocated at this site.
for (AhatInstance inst : mObjects) {
- if (inst.isReachable()) {
+ if (inst.isStronglyReachable()) {
AhatHeap heap = inst.getHeap();
Size size = inst.getSize();
ObjectsInfo info = getObjectsInfo(heap, inst.getClassObj());
diff --git a/tools/ahat/src/heapdump/Size.java b/tools/ahat/src/main/com/android/ahat/heapdump/Size.java
index 7c8db900df..7c8db900df 100644
--- a/tools/ahat/src/heapdump/Size.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Size.java
diff --git a/tools/ahat/src/heapdump/SkipNullsIterator.java b/tools/ahat/src/main/com/android/ahat/heapdump/SkipNullsIterator.java
index e99fe5e8ea..e99fe5e8ea 100644
--- a/tools/ahat/src/heapdump/SkipNullsIterator.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/SkipNullsIterator.java
diff --git a/tools/ahat/src/heapdump/Sort.java b/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java
index efe0d6b59b..efe0d6b59b 100644
--- a/tools/ahat/src/heapdump/Sort.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java
diff --git a/tools/ahat/src/heapdump/SuperRoot.java b/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java
index a2adbd2808..a2adbd2808 100644
--- a/tools/ahat/src/heapdump/SuperRoot.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java
diff --git a/tools/ahat/src/heapdump/Type.java b/tools/ahat/src/main/com/android/ahat/heapdump/Type.java
index 726bc47cf2..726bc47cf2 100644
--- a/tools/ahat/src/heapdump/Type.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Type.java
diff --git a/tools/ahat/src/heapdump/Value.java b/tools/ahat/src/main/com/android/ahat/heapdump/Value.java
index 01fd25057d..01fd25057d 100644
--- a/tools/ahat/src/heapdump/Value.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Value.java
diff --git a/tools/ahat/src/proguard/ProguardMap.java b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java
index 50c110aad4..50c110aad4 100644
--- a/tools/ahat/src/proguard/ProguardMap.java
+++ b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/src/test-dump/Main.java
index 333d28c214..333d28c214 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/src/test-dump/Main.java
diff --git a/tools/ahat/test/DiffFieldsTest.java b/tools/ahat/src/test/com/android/ahat/DiffFieldsTest.java
index 19399757a6..19399757a6 100644
--- a/tools/ahat/test/DiffFieldsTest.java
+++ b/tools/ahat/src/test/com/android/ahat/DiffFieldsTest.java
diff --git a/tools/ahat/test/DiffTest.java b/tools/ahat/src/test/com/android/ahat/DiffTest.java
index 585f29ae61..585f29ae61 100644
--- a/tools/ahat/test/DiffTest.java
+++ b/tools/ahat/src/test/com/android/ahat/DiffTest.java
diff --git a/tools/ahat/test/DominatorsTest.java b/tools/ahat/src/test/com/android/ahat/DominatorsTest.java
index 0424e10dc8..0424e10dc8 100644
--- a/tools/ahat/test/DominatorsTest.java
+++ b/tools/ahat/src/test/com/android/ahat/DominatorsTest.java
diff --git a/tools/ahat/test/HtmlEscaperTest.java b/tools/ahat/src/test/com/android/ahat/HtmlEscaperTest.java
index a36db356f5..a36db356f5 100644
--- a/tools/ahat/test/HtmlEscaperTest.java
+++ b/tools/ahat/src/test/com/android/ahat/HtmlEscaperTest.java
diff --git a/tools/ahat/test/InstanceTest.java b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
index 49a21e2d70..a4908fd0ab 100644
--- a/tools/ahat/test/InstanceTest.java
+++ b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
@@ -214,7 +214,9 @@ public class InstanceTest {
// reference as having a non-null referent.
TestDump dump = TestDump.getTestDump();
AhatInstance ref = dump.getDumpedAhatInstance("aSoftReference");
- assertNotNull(ref.getReferent());
+ AhatInstance referent = ref.getReferent();
+ assertNotNull(referent);
+ assertTrue(referent.isWeaklyReachable());
}
@Test
diff --git a/tools/ahat/test/NativeAllocationTest.java b/tools/ahat/src/test/com/android/ahat/NativeAllocationTest.java
index 7436be8311..7436be8311 100644
--- a/tools/ahat/test/NativeAllocationTest.java
+++ b/tools/ahat/src/test/com/android/ahat/NativeAllocationTest.java
diff --git a/tools/ahat/test/ObjectHandlerTest.java b/tools/ahat/src/test/com/android/ahat/ObjectHandlerTest.java
index 1b8a781e0c..1b8a781e0c 100644
--- a/tools/ahat/test/ObjectHandlerTest.java
+++ b/tools/ahat/src/test/com/android/ahat/ObjectHandlerTest.java
diff --git a/tools/ahat/test/OverviewHandlerTest.java b/tools/ahat/src/test/com/android/ahat/OverviewHandlerTest.java
index c2f773b64b..c2f773b64b 100644
--- a/tools/ahat/test/OverviewHandlerTest.java
+++ b/tools/ahat/src/test/com/android/ahat/OverviewHandlerTest.java
diff --git a/tools/ahat/test/PerformanceTest.java b/tools/ahat/src/test/com/android/ahat/PerformanceTest.java
index e13974bb6f..e13974bb6f 100644
--- a/tools/ahat/test/PerformanceTest.java
+++ b/tools/ahat/src/test/com/android/ahat/PerformanceTest.java
diff --git a/tools/ahat/test/ProguardMapTest.java b/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java
index ad40f45665..ad40f45665 100644
--- a/tools/ahat/test/ProguardMapTest.java
+++ b/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java
diff --git a/tools/ahat/test/QueryTest.java b/tools/ahat/src/test/com/android/ahat/QueryTest.java
index 5bcf8eafc3..5bcf8eafc3 100644
--- a/tools/ahat/test/QueryTest.java
+++ b/tools/ahat/src/test/com/android/ahat/QueryTest.java
diff --git a/tools/ahat/test/RootedHandlerTest.java b/tools/ahat/src/test/com/android/ahat/RootedHandlerTest.java
index f325b8e9a7..f325b8e9a7 100644
--- a/tools/ahat/test/RootedHandlerTest.java
+++ b/tools/ahat/src/test/com/android/ahat/RootedHandlerTest.java
diff --git a/tools/ahat/test/SiteHandlerTest.java b/tools/ahat/src/test/com/android/ahat/SiteHandlerTest.java
index 37596be8bb..37596be8bb 100644
--- a/tools/ahat/test/SiteHandlerTest.java
+++ b/tools/ahat/src/test/com/android/ahat/SiteHandlerTest.java
diff --git a/tools/ahat/test/SiteTest.java b/tools/ahat/src/test/com/android/ahat/SiteTest.java
index dc0fe08297..dc0fe08297 100644
--- a/tools/ahat/test/SiteTest.java
+++ b/tools/ahat/src/test/com/android/ahat/SiteTest.java
diff --git a/tools/ahat/test/TestDump.java b/tools/ahat/src/test/com/android/ahat/TestDump.java
index a0d1021ef1..a0d1021ef1 100644
--- a/tools/ahat/test/TestDump.java
+++ b/tools/ahat/src/test/com/android/ahat/TestDump.java
diff --git a/tools/ahat/test/TestHandler.java b/tools/ahat/src/test/com/android/ahat/TestHandler.java
index 859e39a688..859e39a688 100644
--- a/tools/ahat/test/TestHandler.java
+++ b/tools/ahat/src/test/com/android/ahat/TestHandler.java
diff --git a/tools/ahat/test/Tests.java b/tools/ahat/src/test/com/android/ahat/Tests.java
index 0e7043291d..0e7043291d 100644
--- a/tools/ahat/test/Tests.java
+++ b/tools/ahat/src/test/com/android/ahat/Tests.java
diff --git a/tools/ahat/test-dump/README.txt b/tools/ahat/test-dump/README.txt
deleted file mode 100644
index e7ea584b26..0000000000
--- a/tools/ahat/test-dump/README.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-
-Main.java - A program used to generate a heap dump used for tests.
-L.hprof - A version of the test dump generated on Android L,
- with one of the ROOT_DEBUGGER records manually changed to a
- ROOT_FINALIZING record.
-O.hprof - A version of the test dump generated on Android O.
-RI.hprof - A version of the test dump generated on the reference implementation.
diff --git a/tools/art b/tools/art
index 15993ddaa2..80a80908e4 100644
--- a/tools/art
+++ b/tools/art
@@ -220,6 +220,11 @@ function detect_boot_image_location() {
echo "$image_location"
}
+# If android logging is not explicitly set, only print warnings and errors.
+if [ -z "$ANDROID_LOG_TAGS" ]; then
+ ANDROID_LOG_TAGS='*:w'
+fi
+
# Runs dalvikvm, returns its exit code.
# (Oat directories are cleaned up in between runs)
function run_art() {
@@ -229,15 +234,16 @@ function run_art() {
# First cleanup any left-over 'oat' files from the last time dalvikvm was run.
cleanup_oat_directory_for_classpath "$@"
# Run dalvikvm.
- verbose_run ANDROID_DATA=$ANDROID_DATA \
- ANDROID_ROOT=$ANDROID_ROOT \
- LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
- PATH=$ANDROID_ROOT/bin:$PATH \
- LD_USE_LOAD_BIAS=1 \
- $LAUNCH_WRAPPER $ART_BINARY_PATH $lib \
- -XXlib:$LIBART \
- -Xnorelocate \
- -Ximage:"$image_location" \
+ verbose_run ANDROID_DATA="$ANDROID_DATA" \
+ ANDROID_ROOT="$ANDROID_ROOT" \
+ LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \
+ PATH="$ANDROID_ROOT/bin:$PATH" \
+ LD_USE_LOAD_BIAS=1 \
+ ANDROID_LOG_TAGS="$ANDROID_LOG_TAGS" \
+ $LAUNCH_WRAPPER $ART_BINARY_PATH $lib \
+ -XXlib:"$LIBART" \
+ -Xnorelocate \
+ -Ximage:"$image_location" \
"$@"
ret=$?
diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt
index 6b5daec5a7..8d67c45cce 100644
--- a/tools/libjdwp_art_failures.txt
+++ b/tools/libjdwp_art_failures.txt
@@ -48,7 +48,7 @@
name: "org.apache.harmony.jpda.tests.jdwp.Events.VMDeath002Test#testVMDeathRequest"
},
{
- description: "Test fails with INTERNAL error due to proxy frame!",
+ description: "Test fails with OPAQUE_FRAME error due to attempting a GetLocalReference on a proxy frame instead of GetLocalInstance!",
result: EXEC_FAILED,
bug: 66903662,
name: "org.apache.harmony.jpda.tests.jdwp.StackFrame.ProxyThisObjectTest#testThisObject"
@@ -71,6 +71,33 @@
"org.apache.harmony.jpda.tests.jdwp.EventModifiers.InstanceOnlyModifierTest#testMethodExit",
"org.apache.harmony.jpda.tests.jdwp.EventModifiers.InstanceOnlyModifierTest#testMethodExitWithReturnValue" ]
},
+/* TODO Investigate these failures more closely */
+{
+ description: "Tests that fail when run on the chromium buildbots against the prebuilt libjdwp.so in certain configurations",
+ result: EXEC_FAILED,
+ bug: 67497270,
+ names: [
+ "org.apache.harmony.jpda.tests.jdwp.Events.CombinedEvents003Test#testCombinedEvents003_01",
+ "org.apache.harmony.jpda.tests.jdwp.Events.CombinedEventsTest#testCombinedEvents_01",
+ "org.apache.harmony.jpda.tests.jdwp.Events.CombinedEventsTest#testCombinedEvents_02",
+ "org.apache.harmony.jpda.tests.jdwp.Events.CombinedEventsTest#testCombinedEvents_03",
+ "org.apache.harmony.jpda.tests.jdwp.Events.CombinedEventsTest#testCombinedEvents_04",
+ "org.apache.harmony.jpda.tests.jdwp.Events.CombinedEventsTest#testCombinedEvents_06",
+ "org.apache.harmony.jpda.tests.jdwp.Events.VMDeathTest#testVMDeathEvent",
+ "org.apache.harmony.jpda.tests.jdwp.MultiSession.ClassPrepareTest#testClassPrepare001",
+ "org.apache.harmony.jpda.tests.jdwp.MultiSession.ExceptionTest#testException001",
+ "org.apache.harmony.jpda.tests.jdwp.MultiSession.FieldAccessTest#testFieldAccess001",
+ "org.apache.harmony.jpda.tests.jdwp.MultiSession.FieldModificationTest#testFieldModification001",
+ "org.apache.harmony.jpda.tests.jdwp.MultiSession.SingleStepTest#testSingleStep001",
+ "org.apache.harmony.jpda.tests.jdwp.MultiSession.VMDeathTest#testVMDeathRequest",
+ "org.apache.harmony.jpda.tests.jdwp.ReferenceType.SignatureWithGenericTest#testSignatureWithGeneric001",
+ "org.apache.harmony.jpda.tests.jdwp.StackFrame.GetValues002Test#testGetValues005_Int2",
+ "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.SetDefaultStratumTest#testSetDefaultStratum001",
+ "org.apache.harmony.jpda.tests.jdwp.ThreadReference.StatusTest#testStatus001",
+ "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.AllClassesTest#testAllClasses002",
+ "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.AllClassesWithGenericTest#testAllClassesWithGeneric001"
+ ]
+},
/* TODO Categorize these failures more. */
{
description: "Tests that fail on both ART and RI. These tests are likely incorrect",