Merge "Added a new mutator called NewInstanceChanger."
diff --git a/Android.bp b/Android.bp
index d0e22fb..0ce7916 100644
--- a/Android.bp
+++ b/Android.bp
@@ -31,10 +31,13 @@
"disassembler",
"imgdiag",
"oatdump",
+ "openjdkjvm",
+ "openjdkjvmti",
"patchoat",
"profman",
"runtime",
"sigchainlib",
+ "simulator",
"test",
"tools/cpp-define-generator",
"tools/dmtracedump",
diff --git a/benchmark/micro-native/micro_native.cc b/benchmark/micro-native/micro_native.cc
index d366d9d..dffbf3b 100644
--- a/benchmark/micro-native/micro_native.cc
+++ b/benchmark/micro-native/micro_native.cc
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <stdio.h>
#include <jni.h>
+#include <stdio.h>
#ifndef NATIVE_METHOD
#define NATIVE_METHOD(className, functionName, signature) \
diff --git a/build/Android.bp b/build/Android.bp
index c5ff486..d617116 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -99,6 +99,15 @@
// Bug: 15446488. We don't omit the frame pointer to work around
// clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress.
"-fno-omit-frame-pointer",
+ // The build assumes that all our x86/x86_64 hosts (such as buildbots and developer
+ // desktops) support at least sse4.2/popcount. This firstly implies that the ART
+ // runtime binary itself may exploit these features. Secondly, this implies that
+ // the ART runtime passes these feature flags to dex2oat and JIT by calling the
+ // method InstructionSetFeatures::FromCppDefines(). Since invoking dex2oat directly
+ // does not pick up these flags, cross-compiling from a x86/x86_64 host to a
+ // x86/x86_64 target should not be affected.
+ "-msse4.2",
+ "-mpopcnt",
],
host_ldlibs: [
"-ldl",
diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk
index 66ac897..247f4e3 100644
--- a/build/Android.cpplint.mk
+++ b/build/Android.cpplint.mk
@@ -18,11 +18,13 @@
ART_CPPLINT := $(LOCAL_PATH)/tools/cpplint.py
ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf
-ART_CPPLINT_FLAGS := --root=$(TOP)
+# Use `pwd` instead of $TOP for root, $TOP is always . and --root doesn't seem
+# to work with a relative path (b/34787652).
+ART_CPPLINT_FLAGS := --root=`pwd`
ART_CPPLINT_QUIET := --quiet
ART_CPPLINT_INGORED := \
runtime/elf.h \
- runtime/openjdkjvmti/include/jvmti.h
+ openjdkjvmti/include/jvmti.h
# This:
# 1) Gets a list of all .h & .cc files in the art directory.
diff --git a/build/art.go b/build/art.go
index 6c9aa89..19b39cd 100644
--- a/build/art.go
+++ b/build/art.go
@@ -153,6 +153,11 @@
cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta)
cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta)
+ if len(ctx.AConfig().SanitizeHost()) > 0 && !envFalse(ctx, "ART_ENABLE_ADDRESS_SANITIZER") {
+ // We enable full sanitization on the host by default.
+ cflags = append(cflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
+ }
+
return cflags
}
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h
index 32480dd..804727b 100644
--- a/cmdline/cmdline_parser.h
+++ b/cmdline/cmdline_parser.h
@@ -19,20 +19,20 @@
#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
-#include "cmdline/detail/cmdline_parser_detail.h"
-#include "cmdline/detail/cmdline_parse_argument_detail.h"
#include "cmdline/detail/cmdline_debug_detail.h"
+#include "cmdline/detail/cmdline_parse_argument_detail.h"
+#include "cmdline/detail/cmdline_parser_detail.h"
-#include "cmdline_type_parser.h"
-#include "token_range.h"
-#include "cmdline_types.h"
-#include "cmdline_result.h"
#include "cmdline_parse_result.h"
+#include "cmdline_result.h"
+#include "cmdline_type_parser.h"
+#include "cmdline_types.h"
+#include "token_range.h"
#include "runtime/base/variant_map.h"
-#include <vector>
#include <memory>
+#include <vector>
namespace art {
// Build a parser for command line arguments with a small domain specific language.
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index d957869..3c4f376 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -15,14 +15,16 @@
*/
#include "cmdline_parser.h"
-#include "runtime/runtime_options.h"
-#include "runtime/parsed_options.h"
-#include "utils.h"
#include <numeric>
+
#include "gtest/gtest.h"
+
#include "runtime/experimental_flags.h"
+#include "runtime/parsed_options.h"
#include "runtime/runtime.h"
+#include "runtime/runtime_options.h"
+#include "utils.h"
#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
reinterpret_cast<void*>(nullptr));
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 4de8a48..20c4a7e 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -20,9 +20,9 @@
#include <list>
-#include "memory_representation.h"
-#include "detail/cmdline_debug_detail.h"
#include "cmdline_type_parser.h"
+#include "detail/cmdline_debug_detail.h"
+#include "memory_representation.h"
#include "android-base/strings.h"
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
index da03c21..ceb6fa7 100644
--- a/cmdline/detail/cmdline_parse_argument_detail.h
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -17,13 +17,13 @@
#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
#define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
-#include <type_traits>
#include <assert.h>
-#include <functional>
-#include <vector>
#include <algorithm>
-#include <numeric>
+#include <functional>
#include <memory>
+#include <numeric>
+#include <type_traits>
+#include <vector>
#include "android-base/strings.h"
diff --git a/cmdline/detail/cmdline_parser_detail.h b/cmdline/detail/cmdline_parser_detail.h
index 24dbca2..118628f 100644
--- a/cmdline/detail/cmdline_parser_detail.h
+++ b/cmdline/detail/cmdline_parser_detail.h
@@ -17,8 +17,8 @@
#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
#define ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
-#include <string>
#include <sstream>
+#include <string>
#include <vector>
namespace art {
diff --git a/cmdline/memory_representation.h b/cmdline/memory_representation.h
index 2619c31..8db68bc 100644
--- a/cmdline/memory_representation.h
+++ b/cmdline/memory_representation.h
@@ -17,9 +17,9 @@
#ifndef ART_CMDLINE_MEMORY_REPRESENTATION_H_
#define ART_CMDLINE_MEMORY_REPRESENTATION_H_
-#include <string>
#include <assert.h>
#include <ostream>
+#include <string>
#include "base/bit_utils.h"
diff --git a/cmdline/token_range.h b/cmdline/token_range.h
index c22d6c8..642bb1d 100644
--- a/cmdline/token_range.h
+++ b/cmdline/token_range.h
@@ -18,10 +18,10 @@
#define ART_CMDLINE_TOKEN_RANGE_H_
#include <assert.h>
-#include <vector>
-#include <string>
#include <algorithm>
#include <memory>
+#include <string>
+#include <vector>
#include "android-base/strings.h"
diff --git a/compiler/Android.bp b/compiler/Android.bp
index b721d21..f11d256 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -423,8 +423,11 @@
},
},
+ header_libs: ["libart_simulator_headers"],
+
shared_libs: [
"libartd-compiler",
+ "libartd-simulator-container",
"libvixld-arm",
"libvixld-arm64",
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index c754e55..5347e7f 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -17,9 +17,9 @@
#ifndef ART_COMPILER_CFI_TEST_H_
#define ART_COMPILER_CFI_TEST_H_
-#include <vector>
#include <memory>
#include <sstream>
+#include <vector>
#include "arch/instruction_set.h"
#include "base/enums.h"
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 07bfe31..a9a718f 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -28,8 +28,8 @@
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "interpreter/interpreter.h"
-#include "mirror/class_loader.h"
#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
#include "oat_quick_method_header.h"
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 761e9e1..97127f5 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -17,8 +17,8 @@
#ifndef ART_COMPILER_COMPILED_METHOD_H_
#define ART_COMPILER_COMPILED_METHOD_H_
-#include <memory>
#include <iosfwd>
+#include <memory>
#include <string>
#include <vector>
diff --git a/compiler/compiler.h b/compiler/compiler.h
index ba89cb1..6c542c8 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -17,8 +17,8 @@
#ifndef ART_COMPILER_COMPILER_H_
#define ART_COMPILER_COMPILER_H_
-#include "dex_file.h"
#include "base/mutex.h"
+#include "dex_file.h"
#include "os.h"
namespace art {
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
index e2f0a65..e1f538d 100644
--- a/compiler/debug/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -17,13 +17,14 @@
#ifndef ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
#define ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
-#include <cstring>
#include <dirent.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <cstring>
#include <memory>
#include <set>
-#include <stdio.h>
#include <string>
-#include <sys/types.h>
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 88426a3..1ef3ba7 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -19,16 +19,17 @@
#include "class_linker.h"
#include "compiler/common_compiler_test.h"
#include "compiler/compiled_method.h"
-#include "compiler/driver/compiler_options.h"
#include "compiler/driver/compiler_driver.h"
+#include "compiler/driver/compiler_options.h"
#include "compiler_callbacks.h"
#include "dex_file.h"
#include "handle_scope-inl.h"
-#include "verifier/method_verifier-inl.h"
#include "mirror/class_loader.h"
#include "runtime.h"
-#include "thread.h"
#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+#include "verifier/method_verifier-inl.h"
+#include "verifier/verifier_deps.h"
namespace art {
@@ -39,6 +40,11 @@
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
compiler_options_->boot_image_ = false;
compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken);
+ // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
+ // the results for all the dex files, not just the results for the current dex file.
+ Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
+ new verifier::VerifierDeps(GetDexFiles(class_loader)));
+ compiler_driver_->SetDexFilesForOatFile(GetDexFiles(class_loader));
compiler_driver_->CompileAll(class_loader, GetDexFiles(class_loader), &timings);
}
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index 2572291..54ddc21 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -21,8 +21,8 @@
#include "base/enums.h"
#include "class_linker-inl.h"
#include "dex_file-inl.h"
-#include "dex_instruction.h"
#include "dex_instruction-inl.h"
+#include "dex_instruction.h"
#include "dex_instruction_utils.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
@@ -145,9 +145,8 @@
DCHECK_EQ(invoke_direct->VRegC_35c(),
method->GetCodeItem()->registers_size_ - method->GetCodeItem()->ins_size_);
uint32_t method_index = invoke_direct->VRegB_35c();
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- ArtMethod* target_method =
- method->GetDexCache()->GetResolvedMethod(method_index, pointer_size);
+ ArtMethod* target_method = Runtime::Current()->GetClassLinker()->LookupResolvedMethod(
+ method_index, method->GetDexCache(), method->GetClassLoader());
if (kIsDebugBuild && target_method != nullptr) {
CHECK(!target_method->IsStatic());
CHECK(target_method->IsConstructor());
diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc
index b1006b2..c7e9f4f 100644
--- a/compiler/dex/quick_compiler_callbacks.cc
+++ b/compiler/dex/quick_compiler_callbacks.cc
@@ -16,8 +16,9 @@
#include "quick_compiler_callbacks.h"
-#include "verifier/method_verifier-inl.h"
+#include "driver/compiler_driver.h"
#include "verification_results.h"
+#include "verifier/method_verifier-inl.h"
namespace art {
@@ -33,4 +34,17 @@
}
}
+bool QuickCompilerCallbacks::CanAssumeVerified(ClassReference ref) {
+ // If we don't have class unloading enabled in the compiler, we will never see class that were
+ // previously verified. Return false to avoid overhead from the lookup in the compiler driver.
+ if (!does_class_unloading_) {
+ return false;
+ }
+ DCHECK(compiler_driver_ != nullptr);
+ // In the case of the quicken filter: avoiding verification of quickened instructions, which the
+ // verifier doesn't currently support.
+ // In the case of the verify filter, avoiding verifiying twice.
+ return compiler_driver_->CanAssumeVerified(ref);
+}
+
} // namespace art
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index a3a6c09..578aff4 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -22,6 +22,7 @@
namespace art {
+class CompilerDriver;
class VerificationResults;
class QuickCompilerCallbacks FINAL : public CompilerCallbacks {
@@ -53,8 +54,19 @@
verification_results_ = verification_results;
}
+ bool CanAssumeVerified(ClassReference ref) OVERRIDE;
+
+ void SetDoesClassUnloading(bool does_class_unloading, CompilerDriver* compiler_driver)
+ OVERRIDE {
+ does_class_unloading_ = does_class_unloading;
+ compiler_driver_ = compiler_driver;
+ DCHECK(!does_class_unloading || compiler_driver_ != nullptr);
+ }
+
private:
VerificationResults* verification_results_ = nullptr;
+ bool does_class_unloading_ = false;
+ CompilerDriver* compiler_driver_ = nullptr;
std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
};
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index beb3439..cfb56e3 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -22,8 +22,8 @@
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "runtime.h"
-#include "thread.h"
#include "thread-current-inl.h"
+#include "thread.h"
#include "utils/atomic_dex_ref_map-inl.h"
#include "verified_method.h"
#include "verifier/method_verifier-inl.h"
@@ -46,10 +46,6 @@
void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
DCHECK(method_verifier != nullptr);
- if (!compiler_options_->IsAnyCompilationEnabled()) {
- // Verified methods are only required for quickening and compilation.
- return;
- }
MethodReference ref = method_verifier->GetMethodReference();
std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier));
if (verified_method == nullptr) {
@@ -104,7 +100,6 @@
const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
const VerifiedMethod* ret = nullptr;
- DCHECK(compiler_options_->IsAnyCompilationEnabled());
if (atomic_verified_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index), &ret)) {
return ret;
}
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index bbd28b2..2ec2af5 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "compiled_method_storage.h"
+
#include <gtest/gtest.h>
-#include "compiled_method_storage.h"
#include "compiled_method.h"
#include "compiler_driver.h"
#include "compiler_options.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index cf04e41..037e458 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -16,9 +16,9 @@
#include "compiler_driver.h"
+#include <unistd.h>
#include <unordered_set>
#include <vector>
-#include <unistd.h>
#ifndef __APPLE__
#include <malloc.h> // For mallinfo
@@ -303,7 +303,6 @@
timings_logger_(timer),
compiler_context_(nullptr),
support_boot_image_fixup_(true),
- dex_files_for_oat_file_(nullptr),
compiled_method_storage_(swap_fd),
profile_compilation_info_(profile_compilation_info),
max_arena_alloc_(0),
@@ -409,7 +408,7 @@
ClassName, MethodName, Signature) \
SetupIntrinsic(soa.Self(), Intrinsics::k##Name, InvokeType, ClassName, MethodName, Signature);
#include "intrinsics_list.h"
-INTRINSICS_LIST(SETUP_INTRINSICS)
+ INTRINSICS_LIST(SETUP_INTRINSICS)
#undef INTRINSICS_LIST
#undef SETUP_INTRINSICS
}
@@ -924,8 +923,11 @@
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
- LOG(FATAL) << "Had a hard failure verifying all classes, and was asked to abort in such "
- << "situations. Please check the log.";
+ // Avoid dumping threads. Even if we shut down the thread pools, there will still be three
+ // instances of this thread's stack.
+ LOG(FATAL_WITHOUT_ABORT) << "Had a hard failure verifying all classes, and was asked to abort "
+ << "in such situations. Please check the log.";
+ abort();
}
if (compiler_options_->IsAnyCompilationEnabled()) {
@@ -1912,8 +1914,8 @@
TimingLogger* timings) {
verifier::VerifierDeps* verifier_deps =
Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
- // If there is an existing `VerifierDeps`, try to use it for fast verification.
- if (verifier_deps == nullptr) {
+ // If there exist VerifierDeps that aren't the ones we just created to output, use them to verify.
+ if (verifier_deps == nullptr || verifier_deps->OutputOnly()) {
return false;
}
TimingLogger::ScopedTiming t("Fast Verify", timings);
@@ -1933,14 +1935,12 @@
// time. So instead we assume these classes still need to be verified at
// runtime.
for (const DexFile* dex_file : dex_files) {
- // Fetch the list of unverified classes and turn it into a set for faster
- // lookups.
- const std::vector<dex::TypeIndex>& unverified_classes =
+ // Fetch the list of unverified classes.
+ const std::set<dex::TypeIndex>& unverified_classes =
verifier_deps->GetUnverifiedClasses(*dex_file);
- std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
- if (set.find(class_def.class_idx_) == set.end()) {
+ if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) {
if (compiler_only_verifies) {
// Just update the compiled_classes_ map. The compiler doesn't need to resolve
// the type.
@@ -1980,13 +1980,6 @@
void CompilerDriver::Verify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
- // Always add the dex files to compiled_classes_. This happens for all compiler filters.
- for (const DexFile* dex_file : dex_files) {
- if (!compiled_classes_.HaveDexFile(dex_file)) {
- compiled_classes_.AddDexFile(dex_file, dex_file->NumClassDefs());
- }
- }
-
if (FastVerify(jclass_loader, dex_files, timings)) {
return;
}
@@ -1996,14 +1989,16 @@
// non boot image compilation. The verifier will need it to record the new dependencies.
// Then dex2oat can update the vdex file with these new dependencies.
if (!GetCompilerOptions().IsBootImage()) {
+ // Dex2oat creates the verifier deps.
// Create the main VerifierDeps, and set it to this thread.
- verifier::VerifierDeps* verifier_deps = new verifier::VerifierDeps(dex_files);
- Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps);
+ verifier::VerifierDeps* verifier_deps =
+ Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
+ CHECK(verifier_deps != nullptr);
Thread::Current()->SetVerifierDeps(verifier_deps);
// Create per-thread VerifierDeps to avoid contention on the main one.
// We will merge them after verification.
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
- worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files));
+ worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files_for_oat_file_));
}
}
@@ -2028,7 +2023,7 @@
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
worker->GetThread()->SetVerifierDeps(nullptr);
- verifier_deps->MergeWith(*thread_deps, dex_files);;
+ verifier_deps->MergeWith(*thread_deps, dex_files_for_oat_file_);
delete thread_deps;
}
Thread::Current()->SetVerifierDeps(nullptr);
@@ -2331,10 +2326,8 @@
// a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity
// checks in Thread::AssertThreadSuspensionIsAllowable.
Runtime* const runtime = Runtime::Current();
- Transaction transaction;
-
// Run the class initializer in transaction mode.
- runtime->EnterTransactionMode(&transaction);
+ runtime->EnterTransactionMode(is_app_image, klass.Get());
bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true,
true);
// TODO we detach transaction from runtime to indicate we quit the transactional
@@ -2343,7 +2336,11 @@
{
ScopedAssertNoThreadSuspension ants("Transaction end");
- runtime->ExitTransactionMode();
+
+ if (success) {
+ runtime->ExitTransactionMode();
+ DCHECK(!runtime->IsActiveTransaction());
+ }
if (!success) {
CHECK(soa.Self()->IsExceptionPending());
@@ -2357,7 +2354,7 @@
*file_log << exception->Dump() << "\n";
}
soa.Self()->ClearException();
- transaction.Rollback();
+ runtime->RollbackAllTransactions();
CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
} else if (is_boot_image) {
// For boot image, we want to put the updated status in the oat class since we can't
@@ -2450,7 +2447,8 @@
bool ResolveTypesOfMethods(Thread* self, ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
- auto rtn_type = m->GetReturnType(true); // return value is discarded because resolve will be done internally.
+ // Return value of ResolveReturnType() is discarded because resolve will be done internally.
+ ObjPtr<mirror::Class> rtn_type = m->ResolveReturnType();
if (rtn_type == nullptr) {
self->ClearException();
return false;
@@ -2459,7 +2457,7 @@
if (types != nullptr) {
for (uint32_t i = 0; i < types->Size(); ++i) {
dex::TypeIndex param_type_idx = types->GetTypeItem(i).type_idx_;
- auto param_type = m->GetClassFromTypeIndex(param_type_idx, true);
+ ObjPtr<mirror::Class> param_type = m->ResolveClassFromTypeIndex(param_type_idx);
if (param_type == nullptr) {
self->ClearException();
return false;
@@ -2696,7 +2694,14 @@
: profile_compilation_info_->DumpInfo(&dex_files));
}
- DCHECK(current_dex_to_dex_methods_ == nullptr);
+ current_dex_to_dex_methods_ = nullptr;
+ Thread* const self = Thread::Current();
+ {
+ // Clear in case we aren't the first call to Compile.
+ MutexLock mu(self, dex_to_dex_references_lock_);
+ dex_to_dex_references_.clear();
+ }
+
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
CompileDexFile(class_loader,
@@ -2715,7 +2720,7 @@
{
// From this point on, we shall not modify dex_to_dex_references_, so
// just grab a reference to it that we use without holding the mutex.
- MutexLock lock(Thread::Current(), dex_to_dex_references_lock_);
+ MutexLock lock(self, dex_to_dex_references_lock_);
dex_to_dex_references = ArrayRef<DexFileMethodSet>(dex_to_dex_references_);
}
for (const auto& method_set : dex_to_dex_references) {
@@ -2874,9 +2879,9 @@
bool CompilerDriver::GetCompiledClass(ClassReference ref, mirror::Class::Status* status) const {
DCHECK(status != nullptr);
// The table doesn't know if something wasn't inserted. For this case it will return
- // kStatusNotReady. To handle this, just assume anything not verified is not compiled.
+ // kStatusNotReady. To handle this, just assume anything we didn't try to verify is not compiled.
if (!compiled_classes_.Get(DexFileReference(ref.first, ref.second), status) ||
- *status < mirror::Class::kStatusVerified) {
+ *status < mirror::Class::kStatusRetryVerificationAtRuntime) {
return false;
}
return true;
@@ -2903,7 +2908,17 @@
do {
DexFileReference dex_ref(ref.first, ref.second);
mirror::Class::Status existing = mirror::Class::kStatusNotReady;
- CHECK(compiled_classes_.Get(dex_ref, &existing)) << dex_ref.dex_file->GetLocation();
+ if (!compiled_classes_.Get(dex_ref, &existing)) {
+ // Probably a uses library class, bail.
+ if (kIsDebugBuild) {
+ // Check to make sure it's not a dex file for an oat file we are compiling since these
+ // should always succeed. These do not include classes in for used libraries.
+ for (const DexFile* dex_file : GetDexFilesForOatFile()) {
+ CHECK_NE(dex_ref.dex_file, dex_file) << dex_ref.dex_file->GetLocation();
+ }
+ }
+ return;
+ }
if (existing >= status) {
// Existing status is already better than we expect, break.
break;
@@ -3016,4 +3031,19 @@
single_thread_pool_.reset();
}
+void CompilerDriver::SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) {
+ dex_files_for_oat_file_ = dex_files;
+ for (const DexFile* dex_file : dex_files) {
+ if (!compiled_classes_.HaveDexFile(dex_file)) {
+ compiled_classes_.AddDexFile(dex_file, dex_file->NumClassDefs());
+ }
+ }
+}
+
+bool CompilerDriver::CanAssumeVerified(ClassReference ref) const {
+ mirror::Class::Status existing = mirror::Class::kStatusNotReady;
+ compiled_classes_.Get(DexFileReference(ref.first, ref.second), &existing);
+ return existing >= mirror::Class::kStatusVerified;
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 93234cb..11808c1 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -22,6 +22,8 @@
#include <unordered_set>
#include <vector>
+#include "android-base/strings.h"
+
#include "arch/instruction_set.h"
#include "base/array_ref.h"
#include "base/bit_utils.h"
@@ -32,8 +34,8 @@
#include "dex_file.h"
#include "dex_file_types.h"
#include "driver/compiled_method_storage.h"
-#include "jit/profile_compilation_info.h"
#include "invoke_type.h"
+#include "jit/profile_compilation_info.h"
#include "method_reference.h"
#include "mirror/class.h" // For mirror::Class::Status.
#include "os.h"
@@ -103,15 +105,11 @@
~CompilerDriver();
// Set dex files that will be stored in the oat file after being compiled.
- void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) {
- dex_files_for_oat_file_ = &dex_files;
- }
+ void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files);
// Get dex file that will be stored in the oat file after being compiled.
ArrayRef<const DexFile* const> GetDexFilesForOatFile() const {
- return (dex_files_for_oat_file_ != nullptr)
- ? ArrayRef<const DexFile* const>(*dex_files_for_oat_file_)
- : ArrayRef<const DexFile* const>();
+ return ArrayRef<const DexFile* const>(dex_files_for_oat_file_);
}
void CompileAll(jobject class_loader,
@@ -381,6 +379,16 @@
return profile_compilation_info_;
}
+ bool CanAssumeVerified(ClassReference ref) const;
+
+ // Is `boot_image_filename` the name of a core image (small boot
+ // image used for ART testing only)?
+ static bool IsCoreImageFilename(const std::string& boot_image_filename) {
+ // TODO: This is under-approximating...
+ return android::base::EndsWith(boot_image_filename, "core.art")
+ || android::base::EndsWith(boot_image_filename, "core-optimizing.art");
+ }
+
private:
void PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
@@ -532,7 +540,7 @@
bool support_boot_image_fixup_;
// List of dex files that will be stored in the oat file.
- const std::vector<const DexFile*>* dex_files_for_oat_file_;
+ std::vector<const DexFile*> dex_files_for_oat_file_;
CompiledMethodStorage compiled_method_storage_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index b4ad325..392d57c 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -23,16 +23,17 @@
#include "art_method-inl.h"
#include "class_linker-inl.h"
#include "common_compiler_test.h"
+#include "compiler_callbacks.h"
#include "dex_file.h"
#include "dex_file_types.h"
#include "gc/heap.h"
+#include "handle_scope-inl.h"
+#include "jit/profile_compilation_info.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
-#include "handle_scope-inl.h"
-#include "jit/profile_compilation_info.h"
+#include "mirror/object_array-inl.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
@@ -42,7 +43,9 @@
void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
- compiler_driver_->CompileAll(class_loader, GetDexFiles(class_loader), &timings);
+ dex_files_ = GetDexFiles(class_loader);
+ compiler_driver_->SetDexFilesForOatFile(dex_files_);;
+ compiler_driver_->CompileAll(class_loader, dex_files_, &timings);
t.NewTiming("MakeAllExecutable");
MakeAllExecutable(class_loader);
}
@@ -95,6 +98,7 @@
JNIEnv* env_;
jclass class_;
jmethodID mid_;
+ std::vector<const DexFile*> dex_files_;
};
// Disabled due to 10 second runtime on host
@@ -118,10 +122,12 @@
EXPECT_TRUE(type != nullptr) << "type_idx=" << i
<< " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i)));
}
- EXPECT_EQ(dex.NumMethodIds(), dex_cache->NumResolvedMethods());
+ EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods()
+ || dex.NumMethodIds() == dex_cache->NumResolvedMethods());
auto* cl = Runtime::Current()->GetClassLinker();
auto pointer_size = cl->GetImagePointerSize();
for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
+ // FIXME: This is outdated for hash-based method array.
ArtMethod* method = dex_cache->GetResolvedMethod(i, pointer_size);
EXPECT_TRUE(method != nullptr) << "method_idx=" << i
<< " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i))
@@ -133,6 +139,7 @@
EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields()
|| dex.NumFieldIds() == dex_cache->NumResolvedFields());
for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
+ // FIXME: This is outdated for hash-based field array.
ArtField* field = dex_cache->GetResolvedField(i, cl->GetImagePointerSize());
EXPECT_TRUE(field != nullptr) << "field_idx=" << i
<< " " << dex.GetFieldDeclaringClassDescriptor(dex.GetFieldId(i))
@@ -360,6 +367,49 @@
CheckVerifiedClass(class_loader, "LSecond;");
}
+// Test that a class of status kStatusRetryVerificationAtRuntime is indeed recorded that way in the
+// driver.
+// Test that checks that classes can be assumed as verified if unloading mode is enabled and
+// the class status is at least verified.
+TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) {
+ Thread* const self = Thread::Current();
+ jobject class_loader;
+ std::vector<const DexFile*> dex_files;
+ const DexFile* dex_file = nullptr;
+ {
+ ScopedObjectAccess soa(self);
+ class_loader = LoadDex("ProfileTestMultiDex");
+ ASSERT_NE(class_loader, nullptr);
+ dex_files = GetDexFiles(class_loader);
+ ASSERT_GT(dex_files.size(), 0u);
+ dex_file = dex_files.front();
+ }
+ compiler_driver_->SetDexFilesForOatFile(dex_files);
+ callbacks_->SetDoesClassUnloading(true, compiler_driver_.get());
+ ClassReference ref(dex_file, 0u);
+ // Test that the status is read from the compiler driver as expected.
+ for (size_t i = mirror::Class::kStatusRetryVerificationAtRuntime;
+ i < mirror::Class::kStatusMax;
+ ++i) {
+ const mirror::Class::Status expected_status = static_cast<mirror::Class::Status>(i);
+ // Skip unsupported status that are not supposed to be ever recorded.
+ if (expected_status == mirror::Class::kStatusVerifyingAtRuntime ||
+ expected_status == mirror::Class::kStatusInitializing) {
+ continue;
+ }
+ compiler_driver_->RecordClassStatus(ref, expected_status);
+ mirror::Class::Status status = {};
+ ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status));
+ EXPECT_EQ(status, expected_status);
+
+ // Check that we can assume verified if we are a status that is at least verified.
+ if (status >= mirror::Class::kStatusVerified) {
+ // Check that the class can be assumed as verified in the compiler driver.
+ EXPECT_TRUE(callbacks_->CanAssumeVerified(ref)) << status;
+ }
+ }
+}
+
// TODO: need check-cast test (when stub complete & we can throw/catch
} // namespace art
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 76f0ae9..538845d 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -18,6 +18,8 @@
#include <fstream>
+#include "runtime.h"
+
namespace art {
CompilerOptions::CompilerOptions()
@@ -30,6 +32,7 @@
inline_max_code_units_(kUnsetInlineMaxCodeUnits),
no_inline_from_(nullptr),
boot_image_(false),
+ core_image_(false),
app_image_(false),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
debuggable_(false),
@@ -55,6 +58,19 @@
// because we don't want to include the PassManagerOptions definition from the header file.
}
+bool CompilerOptions::EmitRunTimeChecksInDebugMode() const {
+ // Run-time checks (e.g. Marking Register checks) are only emitted
+ // in debug mode, and
+ // - when running on device; or
+ // - when running on host, but only
+ // - when compiling the core image (which is used only for testing); or
+ // - when JIT compiling (only relevant for non-native methods).
+ // This is to prevent these checks from being emitted into pre-opted
+ // boot image or apps, as these are compiled with dex2oatd.
+ return kIsDebugBuild &&
+ (kIsTargetBuild || IsCoreImage() || Runtime::Current()->UseJitCompilation());
+}
+
void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
ParseUintOption(option, "--huge-method-max", &huge_method_threshold_, Usage);
}
@@ -144,6 +160,8 @@
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=")) {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index b99263d..1e05c4e 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -161,6 +161,9 @@
return generate_mini_debug_info_;
}
+ // Should run-time checks be emitted in debug mode?
+ bool EmitRunTimeChecksInDebugMode() const;
+
bool GetGenerateBuildId() const {
return generate_build_id_;
}
@@ -177,10 +180,19 @@
return implicit_suspend_checks_;
}
+ // Are we compiling a boot image?
bool IsBootImage() const {
return boot_image_;
}
+ // Are we compiling a core image (small boot image only used for ART testing)?
+ bool IsCoreImage() const {
+ // Ensure that `core_image_` => `boot_image_`.
+ DCHECK(!core_image_ || boot_image_);
+ return core_image_;
+ }
+
+ // Are we compiling an app image?
bool IsAppImage() const {
return app_image_;
}
@@ -266,6 +278,7 @@
const std::vector<const DexFile*>* no_inline_from_;
bool boot_image_;
+ bool core_image_;
bool app_image_;
// When using a profile file only the top K% of the profiled samples will be compiled.
double top_k_profile_threshold_;
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index fa2d78d..984e9ee 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -18,9 +18,9 @@
#include "base/unix_file/fd_file.h"
#include "common_compiler_test.h"
+#include "elf_builder.h"
#include "elf_file.h"
#include "elf_file_impl.h"
-#include "elf_builder.h"
#include "elf_writer_quick.h"
#include "oat.h"
#include "utils.h"
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index 0b3ca69..f759aa5 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -21,19 +21,19 @@
#include "base/enums.h"
#include "class_linker.h"
#include "common_runtime_test.h"
-#include "dex_file.h"
#include "dex_file-inl.h"
+#include "dex_file.h"
#include "gtest/gtest.h"
+#include "handle_scope-inl.h"
#include "leb128.h"
#include "mirror/class-inl.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
#include "mirror/stack_trace_element.h"
#include "oat_quick_method_header.h"
#include "optimizing/stack_map_stream.h"
#include "runtime-inl.h"
#include "scoped_thread_state_change-inl.h"
-#include "handle_scope-inl.h"
#include "thread.h"
namespace art {
diff --git a/compiler/image_test.h b/compiler/image_test.h
index 6c3a89b..daa4b11 100644
--- a/compiler/image_test.h
+++ b/compiler/image_test.h
@@ -28,8 +28,8 @@
#include "art_method-inl.h"
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
-#include "compiler_callbacks.h"
#include "common_compiler_test.h"
+#include "compiler_callbacks.h"
#include "debug/method_debug_info.h"
#include "dex/quick_compiler_callbacks.h"
#include "driver/compiler_options.h"
@@ -214,7 +214,8 @@
/*compile_app_image*/false,
storage_mode,
oat_filename_vector,
- dex_file_to_oat_index_map));
+ dex_file_to_oat_index_map,
+ /*dirty_image_objects*/nullptr));
{
{
jobject class_loader = nullptr;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f92bf95..115e722 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -16,9 +16,9 @@
#include "image_writer.h"
-#include <sys/stat.h>
#include <lz4.h>
#include <lz4hc.h>
+#include <sys/stat.h>
#include <memory>
#include <numeric>
@@ -43,8 +43,8 @@
#include "gc/accounting/heap_bitmap.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/collector/concurrent_copying.h"
-#include "gc/heap.h"
#include "gc/heap-visit-objects-inl.h"
+#include "gc/heap.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
#include "gc/verification.h"
@@ -59,8 +59,8 @@
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
#include "mirror/class_loader.h"
-#include "mirror/dex_cache.h"
#include "mirror/dex_cache-inl.h"
+#include "mirror/dex_cache.h"
#include "mirror/executable.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
@@ -579,7 +579,12 @@
}
}
- if (klass->GetStatus() == Class::kStatusInitialized) {
+ // Move known dirty objects into their own sections. This includes:
+ // - classes with dirty static fields.
+ if (dirty_image_objects_ != nullptr &&
+ dirty_image_objects_->find(klass->PrettyDescriptor()) != dirty_image_objects_->end()) {
+ bin = kBinKnownDirty;
+ } else if (klass->GetStatus() == Class::kStatusInitialized) {
bin = kBinClassInitialized;
// If the class's static fields are all final, put it into a separate bin
@@ -770,18 +775,18 @@
*result_ = true;
}
- // Record the object visited in case of circular reference.
- visited_->emplace(ref);
if (ref->IsClass()) {
*result_ = *result_ ||
image_writer_->PruneAppImageClassInternal(ref->AsClass(), early_exit_, visited_);
} else {
+ // Record the object visited in case of circular reference.
+ visited_->emplace(ref);
*result_ = *result_ ||
image_writer_->PruneAppImageClassInternal(klass, early_exit_, visited_);
ref->VisitReferences(*this, *this);
+ // Clean up before exit for next call of this function.
+ visited_->erase(ref);
}
- // Clean up before exit for next call of this function.
- visited_->erase(ref);
}
ALWAYS_INLINE void operator() (ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
@@ -894,7 +899,7 @@
&my_early_exit,
visited);
// Remove the class if the dex file is not in the set of dex files. This happens for classes that
- // are from uses library if there is no profile. b/30688277
+ // are from uses-library if there is no profile. b/30688277
mirror::DexCache* dex_cache = klass->GetDexCache();
if (dex_cache != nullptr) {
result = result ||
@@ -1023,41 +1028,58 @@
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
- ArtMethod* resolution_method = runtime->GetResolutionMethod();
const DexFile& dex_file = *dex_cache->GetDexFile();
// Prune methods.
- ArtMethod** resolved_methods = dex_cache->GetResolvedMethods();
- for (size_t i = 0, num = dex_cache->NumResolvedMethods(); i != num; ++i) {
- ArtMethod* method =
- mirror::DexCache::GetElementPtrSize(resolved_methods, i, target_ptr_size_);
- DCHECK(method != nullptr) << "Expected resolution method instead of null method";
+ mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
+ dex::TypeIndex last_class_idx; // Initialized to invalid index.
+ ObjPtr<mirror::Class> last_class = nullptr;
+ for (size_t i = 0, num = dex_cache->GetDexFile()->NumMethodIds(); i != num; ++i) {
+ uint32_t slot_idx = dex_cache->MethodSlotIndex(i);
+ auto pair =
+ mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_);
+ uint32_t stored_index = pair.index;
+ ArtMethod* method = pair.object;
+ if (method != nullptr && i > stored_index) {
+ continue; // Already checked.
+ }
// Check if the referenced class is in the image. Note that we want to check the referenced
// class rather than the declaring class to preserve the semantics, i.e. using a MethodId
// results in resolving the referenced class and that can for example throw OOME.
- ObjPtr<mirror::Class> referencing_class = class_linker->LookupResolvedType(
- dex_file,
- dex_file.GetMethodId(i).class_idx_,
- dex_cache,
- class_loader);
- // Copied methods may be held live by a class which was not an image class but have a
- // declaring class which is an image class. Set it to the resolution method to be safe and
- // prevent dangling pointers.
- if (method->IsCopied() || !KeepClass(referencing_class)) {
- mirror::DexCache::SetElementPtrSize(resolved_methods,
- i,
- resolution_method,
- target_ptr_size_);
- } else if (kIsDebugBuild) {
- // Check that the class is still in the classes table.
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- CHECK(class_linker->ClassInClassTable(referencing_class)) << "Class "
- << Class::PrettyClass(referencing_class) << " not in class linker table";
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(i);
+ if (method_id.class_idx_ != last_class_idx) {
+ last_class_idx = method_id.class_idx_;
+ last_class = class_linker->LookupResolvedType(
+ dex_file, last_class_idx, dex_cache, class_loader);
+ if (last_class != nullptr && !KeepClass(last_class)) {
+ last_class = nullptr;
+ }
+ }
+ if (method == nullptr || i < stored_index) {
+ if (last_class != nullptr) {
+ const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
+ Signature signature = dex_file.GetMethodSignature(method_id);
+ if (last_class->IsInterface()) {
+ method = last_class->FindInterfaceMethod(name, signature, target_ptr_size_);
+ } else {
+ method = last_class->FindClassMethod(name, signature, target_ptr_size_);
+ }
+ if (method != nullptr) {
+ // If the referenced class is in the image, the defining class must also be there.
+ DCHECK(KeepClass(method->GetDeclaringClass()));
+ dex_cache->SetResolvedMethod(i, method, target_ptr_size_);
+ }
+ }
+ } else {
+ DCHECK_EQ(i, stored_index);
+ if (last_class == nullptr) {
+ dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
+ }
}
}
// Prune fields and make the contents of the field array deterministic.
mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
- dex::TypeIndex last_class_idx; // Initialized to invalid index.
- ObjPtr<mirror::Class> last_class = nullptr;
+ last_class_idx = dex::TypeIndex(); // Initialized to invalid index.
+ last_class = nullptr;
for (size_t i = 0, end = dex_file.NumFieldIds(); i < end; ++i) {
uint32_t slot_idx = dex_cache->FieldSlotIndex(i);
auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_);
@@ -1136,9 +1158,22 @@
Thread* self = Thread::Current();
ScopedAssertNoThreadSuspension sa(__FUNCTION__);
- // Clear class table strong roots so that dex caches can get pruned. We require pruning the class
- // path dex caches.
- class_linker->ClearClassTableStrongRoots();
+ // Prune uses-library dex caches. Only prune the uses-library dex caches since we want to make
+ // sure the other ones don't get unloaded before the OatWriter runs.
+ class_linker->VisitClassTables(
+ [&](ClassTable* table) REQUIRES_SHARED(Locks::mutator_lock_) {
+ table->RemoveStrongRoots(
+ [&](GcRoot<mirror::Object> root) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> obj = root.Read();
+ if (obj->IsDexCache()) {
+ // Return true if the dex file is not one of the ones in the map.
+ return dex_file_oat_index_map_.find(obj->AsDexCache()->GetDexFile()) ==
+ dex_file_oat_index_map_.end();
+ }
+ // Return false to avoid removing.
+ return false;
+ });
+ });
// Remove the undesired classes from the class roots.
ObjPtr<mirror::ClassLoader> class_loader;
@@ -1651,6 +1686,10 @@
runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs);
image_methods_[ImageHeader::kSaveEverythingMethod] =
runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything);
+ image_methods_[ImageHeader::kSaveEverythingMethodForClinit] =
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit);
+ image_methods_[ImageHeader::kSaveEverythingMethodForSuspendCheck] =
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck);
// Visit image methods first to have the main runtime methods in the first image.
for (auto* m : image_methods_) {
CHECK(m != nullptr);
@@ -2401,17 +2440,19 @@
orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache),
fixup_visitor);
}
- ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
+ mirror::MethodDexCacheType* orig_methods = orig_dex_cache->GetResolvedMethods();
if (orig_methods != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(),
NativeLocationInImage(orig_methods),
PointerSize::k64);
- ArtMethod** copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache);
+ mirror::MethodDexCacheType* copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) {
- ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_);
+ mirror::MethodDexCachePair orig_pair =
+ mirror::DexCache::GetNativePairPtrSize(orig_methods, i, target_ptr_size_);
// NativeLocationInImage also handles runtime methods since these have relocation info.
- ArtMethod* copy = NativeLocationInImage(orig);
- mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_);
+ mirror::MethodDexCachePair copy_pair(NativeLocationInImage(orig_pair.object),
+ orig_pair.index);
+ mirror::DexCache::SetNativePairPtrSize(copy_methods, i, copy_pair, target_ptr_size_);
}
}
mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields();
@@ -2552,7 +2593,8 @@
CopyReference(copy->GetDeclaringClassAddressWithoutBarrier(), orig->GetDeclaringClassUnchecked());
- ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_);
+ mirror::MethodDexCacheType* orig_resolved_methods =
+ orig->GetDexCacheResolvedMethods(target_ptr_size_);
copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
@@ -2741,7 +2783,8 @@
bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
- const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map)
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+ const std::unordered_set<std::string>* dirty_image_objects)
: compiler_driver_(compiler_driver),
global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
image_objects_offset_begin_(0),
@@ -2753,7 +2796,8 @@
clean_methods_(0u),
image_storage_mode_(image_storage_mode),
oat_filenames_(oat_filenames),
- dex_file_oat_index_map_(dex_file_oat_index_map) {
+ dex_file_oat_index_map_(dex_file_oat_index_map),
+ dirty_image_objects_(dirty_image_objects) {
CHECK_NE(image_begin, 0U);
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index ee6fc1d..866e204 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -22,10 +22,10 @@
#include <cstddef>
#include <memory>
+#include <ostream>
#include <set>
#include <stack>
#include <string>
-#include <ostream>
#include "art_method.h"
#include "base/bit_utils.h"
@@ -40,8 +40,8 @@
#include "lock_word.h"
#include "mem_map.h"
#include "mirror/dex_cache.h"
-#include "obj_ptr.h"
#include "oat_file.h"
+#include "obj_ptr.h"
#include "os.h"
#include "safe_map.h"
#include "utils.h"
@@ -75,7 +75,8 @@
bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
- const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map);
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+ const std::unordered_set<std::string>* dirty_image_objects);
bool PrepareImageAddressSpace();
@@ -159,6 +160,7 @@
// Classify different kinds of bins that objects end up getting packed into during image writing.
// Ordered from dirtiest to cleanest (until ArtMethods).
enum Bin {
+ kBinKnownDirty, // Known dirty objects from --dirty-image-objects list
kBinMiscDirty, // Dex caches, object locks, etc...
kBinClassVerified, // Class verified, but initializers haven't been run
// Unknown mix of clean/dirty:
@@ -599,6 +601,9 @@
// Map of dex files to the indexes of oat files that they were compiled into.
const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_;
+ // Set of objects known to be dirty in the image. Can be nullptr if there are none.
+ const std::unordered_set<std::string>* dirty_image_objects_;
+
class ComputeLazyFieldsForClassesVisitor;
class FixupClassVisitor;
class FixupRootVisitor;
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index f0f24d3..1e1838e 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -19,9 +19,9 @@
#include "base/mutex.h"
#include "compiled_method.h"
-#include "jit_logger.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
+#include "jit_logger.h"
namespace art {
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc
index 3f29ae5..7e1ad9f 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.cc
+++ b/compiler/jni/quick/arm/calling_convention_arm.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "base/logging.h"
#include "calling_convention_arm.h"
+
+#include "base/logging.h"
#include "handle_scope-inl.h"
#include "utils/arm/managed_register_arm.h"
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index e086455..292ce10 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "base/logging.h"
#include "calling_convention_arm64.h"
+
+#include "base/logging.h"
#include "handle_scope-inl.h"
#include "utils/arm64/managed_register_arm64.h"
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 68ec7bd..e7e4647 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -17,36 +17,36 @@
#include "jni_compiler.h"
#include <algorithm>
+#include <fstream>
#include <ios>
#include <memory>
#include <vector>
-#include <fstream>
#include "art_method.h"
#include "base/arena_allocator.h"
#include "base/enums.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "memory_region.h"
#include "calling_convention.h"
#include "class_linker.h"
#include "compiled_method.h"
+#include "debug/dwarf/debug_frame_opcode_writer.h"
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "jni_env_ext.h"
-#include "debug/dwarf/debug_frame_opcode_writer.h"
+#include "memory_region.h"
+#include "thread.h"
+#include "utils.h"
+#include "utils/arm/managed_register_arm.h"
+#include "utils/arm64/managed_register_arm64.h"
#include "utils/assembler.h"
#include "utils/jni_macro_assembler.h"
#include "utils/managed_register.h"
-#include "utils/arm/managed_register_arm.h"
-#include "utils/arm64/managed_register_arm64.h"
#include "utils/mips/managed_register_mips.h"
#include "utils/mips64/managed_register_mips64.h"
#include "utils/x86/managed_register_x86.h"
-#include "utils.h"
-#include "thread.h"
#define __ jni_asm->
@@ -219,7 +219,9 @@
// Assembler that holds generated instructions
std::unique_ptr<JNIMacroAssembler<kPointerSize>> jni_asm =
GetMacroAssembler<kPointerSize>(&arena, instruction_set, instruction_set_features);
- jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GenerateAnyDebugInfo());
+ const CompilerOptions& compiler_options = driver->GetCompilerOptions();
+ jni_asm->cfi().SetEnabled(compiler_options.GenerateAnyDebugInfo());
+ jni_asm->SetEmitRunTimeChecksInDebugMode(compiler_options.EmitRunTimeChecksInDebugMode());
// Offsets into data structures
// TODO: if cross compiling these offsets are for the host not the target
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index 18d6b9a..2ac2a1d 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -22,8 +22,8 @@
#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "lock_word.h"
-#include "mirror/object.h"
#include "mirror/array-inl.h"
+#include "mirror/object.h"
#include "read_barrier.h"
#include "utils/arm/assembler_arm_vixl.h"
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
index 52e27af..fe76dfe 100644
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "linker/arm/relative_patcher_thumb2.h"
+
#include "base/casts.h"
#include "linker/relative_patcher_test.h"
-#include "linker/arm/relative_patcher_thumb2.h"
#include "lock_word.h"
#include "mirror/array-inl.h"
#include "mirror/object.h"
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 38c732b..db829f3 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -25,8 +25,8 @@
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "linker/output_stream.h"
#include "lock_word.h"
-#include "mirror/object.h"
#include "mirror/array-inl.h"
+#include "mirror/object.h"
#include "oat.h"
#include "oat_quick_method_header.h"
#include "read_barrier.h"
diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc
index 5d02d44..d6919e9 100644
--- a/compiler/linker/arm64/relative_patcher_arm64_test.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "linker/arm64/relative_patcher_arm64.h"
+
#include "base/casts.h"
#include "linker/relative_patcher_test.h"
-#include "linker/arm64/relative_patcher_arm64.h"
#include "lock_word.h"
#include "mirror/array-inl.h"
#include "mirror/object.h"
diff --git a/compiler/linker/mips/relative_patcher_mips.h b/compiler/linker/mips/relative_patcher_mips.h
index 0b74bd3..d6eda34 100644
--- a/compiler/linker/mips/relative_patcher_mips.h
+++ b/compiler/linker/mips/relative_patcher_mips.h
@@ -17,8 +17,8 @@
#ifndef ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_
#define ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_
-#include "linker/relative_patcher.h"
#include "arch/mips/instruction_set_features_mips.h"
+#include "linker/relative_patcher.h"
namespace art {
namespace linker {
diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
index d1a75e2..586e2aa 100644
--- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "linker/relative_patcher_test.h"
#include "linker/mips/relative_patcher_mips.h"
+#include "linker/relative_patcher_test.h"
namespace art {
namespace linker {
diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc
index 2f7a075..ebe5406 100644
--- a/compiler/linker/mips/relative_patcher_mips_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "linker/relative_patcher_test.h"
#include "linker/mips/relative_patcher_mips.h"
+#include "linker/relative_patcher_test.h"
+
namespace art {
namespace linker {
diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc
index a5f494d..4edcae7 100644
--- a/compiler/linker/mips64/relative_patcher_mips64_test.cc
+++ b/compiler/linker/mips64/relative_patcher_mips64_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "linker/relative_patcher_test.h"
#include "linker/mips64/relative_patcher_mips64.h"
+#include "linker/relative_patcher_test.h"
+
namespace art {
namespace linker {
diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc
index e9e242b..4ae75d6 100644
--- a/compiler/linker/multi_oat_relative_patcher.cc
+++ b/compiler/linker/multi_oat_relative_patcher.cc
@@ -16,9 +16,9 @@
#include "multi_oat_relative_patcher.h"
-#include "globals.h"
#include "base/bit_utils.h"
#include "base/logging.h"
+#include "globals.h"
namespace art {
namespace linker {
diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc
index 615b2b9..e967901 100644
--- a/compiler/linker/multi_oat_relative_patcher_test.cc
+++ b/compiler/linker/multi_oat_relative_patcher_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "multi_oat_relative_patcher.h"
+
#include "compiled_method.h"
#include "gtest/gtest.h"
-#include "multi_oat_relative_patcher.h"
#include "vector_output_stream.h"
namespace art {
diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc
index 09fef29..87cb100 100644
--- a/compiler/linker/output_stream_test.cc
+++ b/compiler/linker/output_stream_test.cc
@@ -17,8 +17,8 @@
#include "file_output_stream.h"
#include "vector_output_stream.h"
-#include "base/unix_file/fd_file.h"
#include "base/logging.h"
+#include "base/unix_file/fd_file.h"
#include "buffered_output_stream.h"
#include "common_runtime_test.h"
diff --git a/compiler/linker/vector_output_stream.h b/compiler/linker/vector_output_stream.h
index 3210143..a9b93e7 100644
--- a/compiler/linker/vector_output_stream.h
+++ b/compiler/linker/vector_output_stream.h
@@ -19,8 +19,8 @@
#include "output_stream.h"
-#include <string>
#include <string.h>
+#include <string>
#include <vector>
namespace art {
diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/compiler/linker/x86/relative_patcher_x86_test.cc
index 0bd9de8..4f74cee 100644
--- a/compiler/linker/x86/relative_patcher_x86_test.cc
+++ b/compiler/linker/x86/relative_patcher_x86_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "linker/relative_patcher_test.h"
#include "linker/x86/relative_patcher_x86.h"
+#include "linker/relative_patcher_test.h"
+
namespace art {
namespace linker {
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
index 6d6bb40..ae17aa7 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
+++ b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "linker/relative_patcher_test.h"
#include "linker/x86_64/relative_patcher_x86_64.h"
+#include "linker/relative_patcher_test.h"
+
namespace art {
namespace linker {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index f8bb417..d7e3a28 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1116,6 +1116,7 @@
const std::vector<const DexFile*>* dex_files)
: OatDexMethodVisitor(writer, offset),
pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())),
+ class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr),
dex_files_(dex_files),
class_linker_(Runtime::Current()->GetClassLinker()) {}
@@ -1131,10 +1132,7 @@
if (!IsImageClass()) {
return true;
}
- ScopedObjectAccessUnchecked soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache = hs.NewHandle(
- class_linker_->FindDexCache(Thread::Current(), *dex_file));
+ ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), *dex_file);
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_);
if (klass != nullptr) {
@@ -1182,36 +1180,36 @@
++method_offsets_index_;
}
- // Unchecked as we hold mutator_lock_ on entry.
- ScopedObjectAccessUnchecked soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker_->FindDexCache(
- Thread::Current(), *dex_file_)));
+ Thread* self = Thread::Current();
+ ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(self, *dex_file_);
ArtMethod* method;
if (writer_->HasBootImage()) {
const InvokeType invoke_type = it.GetMethodInvokeType(
dex_file_->GetClassDef(class_def_index_));
+ // Unchecked as we hold mutator_lock_ on entry.
+ ScopedObjectAccessUnchecked soa(self);
+ StackHandleScope<1> hs(self);
method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
*dex_file_,
it.GetMemberIndex(),
- dex_cache,
+ hs.NewHandle(dex_cache),
ScopedNullHandle<mirror::ClassLoader>(),
nullptr,
invoke_type);
if (method == nullptr) {
LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: "
<< dex_file_->PrettyMethod(it.GetMemberIndex(), true);
- soa.Self()->AssertPendingException();
- mirror::Throwable* exc = soa.Self()->GetException();
+ self->AssertPendingException();
+ mirror::Throwable* exc = self->GetException();
std::string dump = exc->Dump();
LOG(FATAL) << dump;
UNREACHABLE();
}
} else {
- // Should already have been resolved by the compiler, just peek into the dex cache.
+ // Should already have been resolved by the compiler.
// It may not be resolved if the class failed to verify, in this case, don't set the
- // entrypoint. This is not fatal since the dex cache will contain a resolution method.
- method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), pointer_size_);
+ // entrypoint. This is not fatal since we shall use a resolution method.
+ method = class_linker_->LookupResolvedMethod(it.GetMemberIndex(), dex_cache, class_loader_);
}
if (method != nullptr &&
compiled_method != nullptr &&
@@ -1252,6 +1250,7 @@
private:
const PointerSize pointer_size_;
+ ObjPtr<mirror::ClassLoader> class_loader_;
const std::vector<const DexFile*>* dex_files_;
ClassLinker* const class_linker_;
std::vector<std::pair<ArtMethod*, ArtMethod*>> methods_to_process_;
@@ -1283,9 +1282,12 @@
bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE
REQUIRES_SHARED(Locks::mutator_lock_) {
OatDexMethodVisitor::StartClass(dex_file, class_def_index);
- if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
- dex_cache_ = class_linker_->FindDexCache(Thread::Current(), *dex_file);
- DCHECK(dex_cache_ != nullptr);
+ if (writer_->GetCompilerDriver()->GetCompilerOptions().IsAotCompilationEnabled()) {
+ // Only need to set the dex cache if we have compilation. Other modes might have unloaded it.
+ if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
+ dex_cache_ = class_linker_->FindDexCache(Thread::Current(), *dex_file);
+ DCHECK(dex_cache_ != nullptr);
+ }
}
return true;
}
@@ -1471,7 +1473,8 @@
ObjPtr<mirror::DexCache> dex_cache =
(dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache(
Thread::Current(), *ref.dex_file);
- ArtMethod* method = dex_cache->GetResolvedMethod(ref.dex_method_index, pointer_size_);
+ ArtMethod* method =
+ class_linker_->LookupResolvedMethod(ref.dex_method_index, dex_cache, class_loader_);
CHECK(method != nullptr);
return method;
}
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index f3ecdf0..2f96cfa 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -20,8 +20,8 @@
#include "base/arena_containers.h"
#include "induction_var_range.h"
-#include "side_effects_analysis.h"
#include "nodes.h"
+#include "side_effects_analysis.h"
namespace art {
@@ -1121,6 +1121,66 @@
}
}
+ void VisitRem(HRem* instruction) OVERRIDE {
+ HInstruction* left = instruction->GetLeft();
+ HInstruction* right = instruction->GetRight();
+
+ // Handle 'i % CONST' format expression in array index, e.g:
+ // array[i % 20];
+ if (right->IsIntConstant()) {
+ int32_t right_const = std::abs(right->AsIntConstant()->GetValue());
+ if (right_const == 0) {
+ return;
+ }
+ // The sign of divisor CONST doesn't affect the sign final value range.
+ // For example:
+ // if (i > 0) {
+ // array[i % 10]; // index value range [0, 9]
+ // array[i % -10]; // index value range [0, 9]
+ // }
+ ValueRange* right_range = new (GetGraph()->GetArena()) ValueRange(
+ GetGraph()->GetArena(),
+ ValueBound(nullptr, 1 - right_const),
+ ValueBound(nullptr, right_const - 1));
+
+ ValueRange* left_range = LookupValueRange(left, left->GetBlock());
+ if (left_range != nullptr) {
+ right_range = left_range->Narrow(right_range);
+ }
+ AssignRange(instruction->GetBlock(), instruction, right_range);
+ return;
+ }
+
+ // Handle following pattern:
+ // i0 NullCheck
+ // i1 ArrayLength[i0]
+ // i2 DivByZeroCheck [i1] <-- right
+ // i3 Rem [i5, i2] <-- we are here.
+ // i4 BoundsCheck [i3,i1]
+ if (right->IsDivZeroCheck()) {
+ // if array_length can pass div-by-zero check,
+ // array_length must be > 0.
+ right = right->AsDivZeroCheck()->InputAt(0);
+ }
+
+ // Handle 'i % array.length' format expression in array index, e.g:
+ // array[(i+7) % array.length];
+ if (right->IsArrayLength()) {
+ ValueBound lower = ValueBound::Min(); // ideally, lower should be '1-array_length'.
+ ValueBound upper = ValueBound(right, -1); // array_length - 1
+ ValueRange* right_range = new (GetGraph()->GetArena()) ValueRange(
+ GetGraph()->GetArena(),
+ lower,
+ upper);
+ ValueRange* left_range = LookupValueRange(left, left->GetBlock());
+ if (left_range != nullptr) {
+ right_range = left_range->Narrow(right_range);
+ }
+ AssignRange(instruction->GetBlock(), instruction, right_range);
+ return;
+ }
+ }
+
void VisitNewArray(HNewArray* new_array) OVERRIDE {
HInstruction* len = new_array->GetLength();
if (!len->IsIntConstant()) {
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index a949c33..2aaf058 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "base/arena_allocator.h"
#include "bounds_check_elimination.h"
+
+#include "base/arena_allocator.h"
#include "builder.h"
#include "gvn.h"
#include "induction_var_analysis.h"
@@ -950,4 +951,152 @@
ASSERT_TRUE(IsRemoved(bounds_check6));
}
+// int[] array = new int[10];
+// for (int i=0; i<200; i++) {
+// array[i%10] = 10; // Can eliminate
+// array[i%1] = 10; // Can eliminate
+// array[i%200] = 10; // Cannot eliminate
+// array[i%-10] = 10; // Can eliminate
+// array[i%array.length] = 10; // Can eliminate
+// array[param_i%10] = 10; // Can't eliminate, when param_i < 0
+// }
+TEST_F(BoundsCheckEliminationTest, ModArrayBoundsElimination) {
+ HBasicBlock* entry = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry);
+ graph_->SetEntryBlock(entry);
+ HInstruction* param_i = new (&allocator_)
+ HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
+ entry->AddInstruction(param_i);
+
+ HInstruction* constant_0 = graph_->GetIntConstant(0);
+ HInstruction* constant_1 = graph_->GetIntConstant(1);
+ HInstruction* constant_10 = graph_->GetIntConstant(10);
+ HInstruction* constant_200 = graph_->GetIntConstant(200);
+ HInstruction* constant_minus_10 = graph_->GetIntConstant(-10);
+
+ HBasicBlock* block = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(block);
+ entry->AddSuccessor(block);
+ // We pass a bogus constant for the class to avoid mocking one.
+ HInstruction* new_array = new (&allocator_) HNewArray(constant_10, constant_10, 0);
+ block->AddInstruction(new_array);
+ block->AddInstruction(new (&allocator_) HGoto());
+
+ HBasicBlock* loop_header = new (&allocator_) HBasicBlock(graph_);
+ HBasicBlock* loop_body = new (&allocator_) HBasicBlock(graph_);
+ HBasicBlock* exit = new (&allocator_) HBasicBlock(graph_);
+
+ graph_->AddBlock(loop_header);
+ graph_->AddBlock(loop_body);
+ graph_->AddBlock(exit);
+ block->AddSuccessor(loop_header);
+ loop_header->AddSuccessor(exit); // true successor
+ loop_header->AddSuccessor(loop_body); // false successor
+ loop_body->AddSuccessor(loop_header);
+
+ HPhi* phi = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt);
+ HInstruction* cmp = new (&allocator_) HGreaterThanOrEqual(phi, constant_200);
+ HInstruction* if_inst = new (&allocator_) HIf(cmp);
+ loop_header->AddPhi(phi);
+ loop_header->AddInstruction(cmp);
+ loop_header->AddInstruction(if_inst);
+ phi->AddInput(constant_0);
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // LOOP BODY:
+ // array[i % 10] = 10;
+ HRem* i_mod_10 = new (&allocator_) HRem(Primitive::kPrimInt, phi, constant_10, 0);
+ HBoundsCheck* bounds_check_i_mod_10 = new (&allocator_) HBoundsCheck(i_mod_10, constant_10, 0);
+ HInstruction* array_set = new (&allocator_) HArraySet(
+ new_array, bounds_check_i_mod_10, constant_10, Primitive::kPrimInt, 0);
+ loop_body->AddInstruction(i_mod_10);
+ loop_body->AddInstruction(bounds_check_i_mod_10);
+ loop_body->AddInstruction(array_set);
+
+ // array[i % 1] = 10;
+ HRem* i_mod_1 = new (&allocator_) HRem(Primitive::kPrimInt, phi, constant_1, 0);
+ HBoundsCheck* bounds_check_i_mod_1 = new (&allocator_) HBoundsCheck(i_mod_1, constant_10, 0);
+ array_set = new (&allocator_) HArraySet(
+ new_array, bounds_check_i_mod_1, constant_10, Primitive::kPrimInt, 0);
+ loop_body->AddInstruction(i_mod_1);
+ loop_body->AddInstruction(bounds_check_i_mod_1);
+ loop_body->AddInstruction(array_set);
+
+ // array[i % 200] = 10;
+ HRem* i_mod_200 = new (&allocator_) HRem(Primitive::kPrimInt, phi, constant_1, 0);
+ HBoundsCheck* bounds_check_i_mod_200 = new (&allocator_) HBoundsCheck(i_mod_200, constant_10, 0);
+ array_set = new (&allocator_) HArraySet(
+ new_array, bounds_check_i_mod_200, constant_10, Primitive::kPrimInt, 0);
+ loop_body->AddInstruction(i_mod_200);
+ loop_body->AddInstruction(bounds_check_i_mod_200);
+ loop_body->AddInstruction(array_set);
+
+ // array[i % -10] = 10;
+ HRem* i_mod_minus_10 = new (&allocator_) HRem(Primitive::kPrimInt, phi, constant_minus_10, 0);
+ HBoundsCheck* bounds_check_i_mod_minus_10 = new (&allocator_) HBoundsCheck(
+ i_mod_minus_10, constant_10, 0);
+ array_set = new (&allocator_) HArraySet(
+ new_array, bounds_check_i_mod_minus_10, constant_10, Primitive::kPrimInt, 0);
+ loop_body->AddInstruction(i_mod_minus_10);
+ loop_body->AddInstruction(bounds_check_i_mod_minus_10);
+ loop_body->AddInstruction(array_set);
+
+ // array[i%array.length] = 10;
+ HNullCheck* null_check = new (&allocator_) HNullCheck(new_array, 0);
+ HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
+ HRem* i_mod_array_length = new (&allocator_) HRem(Primitive::kPrimInt, phi, array_length, 0);
+ HBoundsCheck* bounds_check_i_mod_array_len = new (&allocator_) HBoundsCheck(
+ i_mod_array_length, array_length, 0);
+ array_set = new (&allocator_) HArraySet(
+ null_check, bounds_check_i_mod_array_len, constant_10, Primitive::kPrimInt, 0);
+ loop_body->AddInstruction(null_check);
+ loop_body->AddInstruction(array_length);
+ loop_body->AddInstruction(i_mod_array_length);
+ loop_body->AddInstruction(bounds_check_i_mod_array_len);
+ loop_body->AddInstruction(array_set);
+
+ // array[param_i % 10] = 10;
+ HRem* param_i_mod_10 = new (&allocator_) HRem(Primitive::kPrimInt, param_i, constant_10, 0);
+ HBoundsCheck* bounds_check_param_i_mod_10 = new (&allocator_) HBoundsCheck(
+ param_i_mod_10, constant_10, 0);
+ array_set = new (&allocator_) HArraySet(
+ new_array, bounds_check_param_i_mod_10, constant_10, Primitive::kPrimInt, 0);
+ loop_body->AddInstruction(param_i_mod_10);
+ loop_body->AddInstruction(bounds_check_param_i_mod_10);
+ loop_body->AddInstruction(array_set);
+
+ // array[param_i%array.length] = 10;
+ null_check = new (&allocator_) HNullCheck(new_array, 0);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
+ HRem* param_i_mod_array_length = new (&allocator_) HRem(
+ Primitive::kPrimInt, param_i, array_length, 0);
+ HBoundsCheck* bounds_check_param_i_mod_array_len = new (&allocator_) HBoundsCheck(
+ param_i_mod_array_length, array_length, 0);
+ array_set = new (&allocator_) HArraySet(
+ null_check, bounds_check_param_i_mod_array_len, constant_10, Primitive::kPrimInt, 0);
+ loop_body->AddInstruction(null_check);
+ loop_body->AddInstruction(array_length);
+ loop_body->AddInstruction(param_i_mod_array_length);
+ loop_body->AddInstruction(bounds_check_param_i_mod_array_len);
+ loop_body->AddInstruction(array_set);
+
+ // i++;
+ HInstruction* add = new (&allocator_) HAdd(Primitive::kPrimInt, phi, constant_1);
+ loop_body->AddInstruction(add);
+ loop_body->AddInstruction(new (&allocator_) HGoto());
+ phi->AddInput(add);
+ //////////////////////////////////////////////////////////////////////////////////
+
+ exit->AddInstruction(new (&allocator_) HExit());
+
+ RunBCE();
+
+ ASSERT_TRUE(IsRemoved(bounds_check_i_mod_10));
+ ASSERT_TRUE(IsRemoved(bounds_check_i_mod_1));
+ ASSERT_TRUE(IsRemoved(bounds_check_i_mod_200));
+ ASSERT_TRUE(IsRemoved(bounds_check_i_mod_minus_10));
+ ASSERT_TRUE(IsRemoved(bounds_check_i_mod_array_len));
+ ASSERT_FALSE(IsRemoved(bounds_check_param_i_mod_10));
+}
+
} // namespace art
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 3a4c9db..43429cf 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -20,14 +20,14 @@
#include "base/arena_containers.h"
#include "base/arena_object.h"
#include "block_builder.h"
-#include "dex_file.h"
#include "dex_file-inl.h"
+#include "dex_file.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
#include "instruction_builder.h"
+#include "nodes.h"
#include "optimizing_compiler_stats.h"
#include "primitive.h"
-#include "nodes.h"
#include "ssa_builder.h"
namespace art {
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 2872cf7..d7d0fff 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -57,8 +57,8 @@
#include "mirror/reference.h"
#include "mirror/string.h"
#include "parallel_move_resolver.h"
-#include "ssa_liveness_analysis.h"
#include "scoped_thread_state_change-inl.h"
+#include "ssa_liveness_analysis.h"
#include "thread-current-inl.h"
#include "utils/assembler.h"
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 73202b4..51a0bae 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -446,6 +446,16 @@
return GetFrameSize() == (CallPushesPC() ? GetWordSize() : 0);
}
+ static int8_t GetInt8ValueOf(HConstant* constant) {
+ DCHECK(constant->IsIntConstant());
+ return constant->AsIntConstant()->GetValue();
+ }
+
+ static int16_t GetInt16ValueOf(HConstant* constant) {
+ DCHECK(constant->IsIntConstant());
+ return constant->AsIntConstant()->GetValue();
+ }
+
static int32_t GetInt32ValueOf(HConstant* constant) {
if (constant->IsIntConstant()) {
return constant->AsIntConstant()->GetValue();
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 7e5b1a0..3be774a 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -29,9 +29,9 @@
#include "intrinsics.h"
#include "intrinsics_arm64.h"
#include "linker/arm64/relative_patcher_arm64.h"
+#include "lock_word.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
-#include "lock_word.h"
#include "offsets.h"
#include "thread.h"
#include "utils/arm64/assembler_arm64.h"
@@ -1595,6 +1595,8 @@
__ Str(wzr, MemOperand(sp, GetStackOffsetOfShouldDeoptimizeFlag()));
}
}
+
+ MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void CodeGeneratorARM64::GenerateFrameExit() {
@@ -3587,6 +3589,7 @@
}
if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
if (!codegen_->GoesToNextBlock(block, successor)) {
__ B(codegen_->GetLabelOf(successor));
@@ -4391,6 +4394,7 @@
void InstructionCodeGeneratorARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) {
@@ -4459,6 +4463,8 @@
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
+
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
@@ -4626,6 +4632,7 @@
void InstructionCodeGeneratorARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
codegen_->GenerateInvokePolymorphicCall(invoke);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeMethodPatch(
@@ -4801,27 +4808,37 @@
DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
return;
}
- // Ensure that between the BLR (emitted by GenerateStaticOrDirectCall) and RecordPcInfo there
- // are no pools emitted.
- EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
- LocationSummary* locations = invoke->GetLocations();
- codegen_->GenerateStaticOrDirectCall(
- invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
+ {
+ // Ensure that between the BLR (emitted by GenerateStaticOrDirectCall) and RecordPcInfo there
+ // are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
+ LocationSummary* locations = invoke->GetLocations();
+ codegen_->GenerateStaticOrDirectCall(
+ invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
+ }
+
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
return;
}
- // Ensure that between the BLR (emitted by GenerateVirtualCall) and RecordPcInfo there
- // are no pools emitted.
- EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
- codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
- DCHECK(!codegen_->IsLeafMethod());
+ {
+ // Ensure that between the BLR (emitted by GenerateVirtualCall) and RecordPcInfo there
+ // are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
+ codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
+ DCHECK(!codegen_->IsLeafMethod());
+ }
+
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind(
@@ -4895,6 +4912,7 @@
HLoadClass::LoadKind load_kind = cls->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
codegen_->GenerateLoadClassRuntimeCall(cls);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -4995,6 +5013,7 @@
} else {
__ Bind(slow_path->GetExitLabel());
}
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
}
@@ -5113,6 +5132,7 @@
codegen_->AddSlowPath(slow_path);
__ Cbz(out.X(), slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
return;
}
case HLoadString::LoadKind::kJitTableAddress: {
@@ -5137,6 +5157,7 @@
__ Mov(calling_convention.GetRegisterAt(0).W(), load->GetStringIndex().index_);
codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) {
@@ -5164,6 +5185,7 @@
} else {
CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
}
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void LocationsBuilderARM64::VisitMul(HMul* mul) {
@@ -5260,6 +5282,7 @@
CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
@@ -5296,6 +5319,7 @@
codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
}
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void LocationsBuilderARM64::VisitNot(HNot* instruction) {
@@ -5644,6 +5668,7 @@
return;
}
GenerateSuspendCheck(instruction, nullptr);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void LocationsBuilderARM64::VisitThrow(HThrow* instruction) {
@@ -6021,6 +6046,7 @@
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -6074,22 +6100,25 @@
obj.GetCode());
vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data);
- EmissionCheckScope guard(GetVIXLAssembler(),
- (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
- vixl::aarch64::Label return_address;
- __ adr(lr, &return_address);
- __ Bind(cbnz_label);
- __ cbnz(mr, static_cast<int64_t>(0)); // Placeholder, patched at link-time.
- static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
- "Field LDR must be 1 instruction (4B) before the return address label; "
- " 2 instructions (8B) for heap poisoning.");
- Register ref_reg = RegisterFrom(ref, Primitive::kPrimNot);
- __ ldr(ref_reg, MemOperand(base.X(), offset));
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
+ {
+ EmissionCheckScope guard(GetVIXLAssembler(),
+ (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
+ vixl::aarch64::Label return_address;
+ __ adr(lr, &return_address);
+ __ Bind(cbnz_label);
+ __ cbnz(mr, static_cast<int64_t>(0)); // Placeholder, patched at link-time.
+ static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
+ "Field LDR must be 1 instruction (4B) before the return address label; "
+ " 2 instructions (8B) for heap poisoning.");
+ Register ref_reg = RegisterFrom(ref, Primitive::kPrimNot);
+ __ ldr(ref_reg, MemOperand(base.X(), offset));
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
+ __ Bind(&return_address);
}
- GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
- __ Bind(&return_address);
+ MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__, /* temp_loc */ LocationFrom(ip1));
return;
}
@@ -6158,19 +6187,22 @@
vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data);
__ Add(temp.X(), obj.X(), Operand(data_offset));
- EmissionCheckScope guard(GetVIXLAssembler(),
- (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
- vixl::aarch64::Label return_address;
- __ adr(lr, &return_address);
- __ Bind(cbnz_label);
- __ cbnz(mr, static_cast<int64_t>(0)); // Placeholder, patched at link-time.
- static_assert(BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
- "Array LDR must be 1 instruction (4B) before the return address label; "
- " 2 instructions (8B) for heap poisoning.");
- __ ldr(ref_reg, MemOperand(temp.X(), index_reg.X(), LSL, scale_factor));
- DCHECK(!needs_null_check); // The thunk cannot handle the null check.
- GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
- __ Bind(&return_address);
+ {
+ EmissionCheckScope guard(GetVIXLAssembler(),
+ (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
+ vixl::aarch64::Label return_address;
+ __ adr(lr, &return_address);
+ __ Bind(cbnz_label);
+ __ cbnz(mr, static_cast<int64_t>(0)); // Placeholder, patched at link-time.
+ static_assert(BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
+ "Array LDR must be 1 instruction (4B) before the return address label; "
+ " 2 instructions (8B) for heap poisoning.");
+ __ ldr(ref_reg, MemOperand(temp.X(), index_reg.X(), LSL, scale_factor));
+ DCHECK(!needs_null_check); // The thunk cannot handle the null check.
+ GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
+ __ Bind(&return_address);
+ }
+ MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__, /* temp_loc */ LocationFrom(ip1));
return;
}
@@ -6247,6 +6279,7 @@
GenerateRawReferenceLoad(
instruction, ref, obj, offset, index, scale_factor, needs_null_check, use_load_acquire);
__ Bind(slow_path->GetExitLabel());
+ MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void CodeGeneratorARM64::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
@@ -6303,6 +6336,7 @@
// Fast path: the GC is not marking: nothing to do (the field is
// up-to-date, and we don't need to load the reference).
__ Bind(slow_path->GetExitLabel());
+ MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
void CodeGeneratorARM64::GenerateRawReferenceLoad(HInstruction* instruction,
@@ -6381,6 +6415,19 @@
GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
}
+void CodeGeneratorARM64::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
+ // The following condition is a compile-time one, so it does not have a run-time cost.
+ if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && 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 (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp = temp_loc.IsValid() ? WRegisterFrom(temp_loc) : temps.AcquireW();
+ GetAssembler()->GenerateMarkingRegisterCheck(temp, code);
+ }
+ }
+}
+
void CodeGeneratorARM64::GenerateReadBarrierSlow(HInstruction* instruction,
Location out,
Location ref,
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 584eead..c339209 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -687,6 +687,22 @@
bool needs_null_check,
bool use_load_acquire);
+ // Emit code checking the status of the Marking Register, and
+ // aborting the program if MR does not match the value stored in the
+ // art::Thread object. Code is only emitted in debug mode and if
+ // CompilerOptions::EmitRunTimeChecksInDebugMode returns true.
+ //
+ // Argument `code` is used to identify the different occurrences of
+ // MaybeGenerateMarkingRegisterCheck in the code generator, and is
+ // passed to the BRK instruction.
+ //
+ // If `temp_loc` is a valid location, it is expected to be a
+ // register and will be used as a temporary to generate code;
+ // otherwise, a temporary will be fetched from the core register
+ // scratch pool.
+ virtual void MaybeGenerateMarkingRegisterCheck(int code,
+ Location temp_loc = Location::NoLocation());
+
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
//
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 430cdde..3d45dd3 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -94,6 +94,9 @@
// The reserved entrypoint register for link-time generated thunks.
const vixl32::Register kBakerCcEntrypointRegister = r4;
+// Using a base helps identify when we hit Marking Register check breakpoints.
+constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
+
#ifdef __
#error "ARM Codegen VIXL macro-assembler macro already defined."
#endif
@@ -2690,6 +2693,8 @@
__ Mov(temp, 0);
GetAssembler()->StoreToOffset(kStoreWord, temp, sp, GetStackOffsetOfShouldDeoptimizeFlag());
}
+
+ MaybeGenerateMarkingRegisterCheck(/* code */ 1);
}
void CodeGeneratorARMVIXL::GenerateFrameExit() {
@@ -2938,6 +2943,7 @@
}
if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 2);
}
if (!codegen_->GoesToNextBlock(block, successor)) {
__ B(codegen_->GetLabelOf(successor));
@@ -3655,6 +3661,7 @@
void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 3);
}
void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
@@ -3685,12 +3692,15 @@
DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 4);
return;
}
LocationSummary* locations = invoke->GetLocations();
codegen_->GenerateStaticOrDirectCall(
invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
+
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 5);
}
void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
@@ -3709,11 +3719,14 @@
void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 6);
return;
}
codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
DCHECK(!codegen_->IsLeafMethod());
+
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 7);
}
void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
@@ -3790,6 +3803,8 @@
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
}
+
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 8);
}
void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
@@ -3798,6 +3813,7 @@
void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
codegen_->GenerateInvokePolymorphicCall(invoke);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 9);
}
void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
@@ -5329,6 +5345,7 @@
codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
}
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10);
}
void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
@@ -5348,6 +5365,7 @@
codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
DCHECK(!codegen_->IsLeafMethod());
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11);
}
void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
@@ -6965,6 +6983,7 @@
return;
}
GenerateSuspendCheck(instruction, nullptr);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
}
void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
@@ -7326,6 +7345,7 @@
HLoadClass::LoadKind load_kind = cls->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
codegen_->GenerateLoadClassRuntimeCall(cls);
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -7405,6 +7425,7 @@
} else {
__ Bind(slow_path->GetExitLabel());
}
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
}
}
@@ -7528,6 +7549,7 @@
codegen_->AddSlowPath(slow_path);
__ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
return;
}
case HLoadString::LoadKind::kJitTableAddress: {
@@ -7548,6 +7570,7 @@
__ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16);
}
static int32_t GetExceptionTlsOffset() {
@@ -8146,6 +8169,7 @@
} else {
CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
}
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
}
void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
@@ -8647,6 +8671,7 @@
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
+ codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18);
}
void CodeGeneratorARMVIXL::MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations) {
@@ -8711,31 +8736,34 @@
base.GetCode(), obj.GetCode(), narrow);
vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
- vixl::EmissionCheckScope guard(
- GetVIXLAssembler(),
- (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
- vixl32::Label return_address;
- EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
- __ cmp(mr, Operand(0));
- EmitPlaceholderBne(this, bne_label);
- ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
- __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, MemOperand(base, offset));
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- // Note: We need a specific width for the unpoisoning NEG.
- if (kPoisonHeapReferences) {
- if (narrow) {
- // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
- __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
- } else {
- __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
+ {
+ vixl::EmissionCheckScope guard(
+ GetVIXLAssembler(),
+ (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
+ vixl32::Label return_address;
+ EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
+ __ cmp(mr, Operand(0));
+ EmitPlaceholderBne(this, bne_label);
+ ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
+ __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, MemOperand(base, offset));
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
}
+ // Note: We need a specific width for the unpoisoning NEG.
+ if (kPoisonHeapReferences) {
+ if (narrow) {
+ // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
+ __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
+ } else {
+ __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
+ }
+ }
+ __ Bind(&return_address);
+ DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
+ narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
+ : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
}
- __ Bind(&return_address);
- DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
- narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
- : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
+ MaybeGenerateMarkingRegisterCheck(/* code */ 19, /* temp_loc */ LocationFrom(ip));
return;
}
@@ -8796,23 +8824,26 @@
vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
__ Add(data_reg, obj, Operand(data_offset));
- vixl::EmissionCheckScope guard(
- GetVIXLAssembler(),
- (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
- vixl32::Label return_address;
- EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
- __ cmp(mr, Operand(0));
- EmitPlaceholderBne(this, bne_label);
- ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
- __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
- DCHECK(!needs_null_check); // The thunk cannot handle the null check.
- // Note: We need a Wide NEG for the unpoisoning.
- if (kPoisonHeapReferences) {
- __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
+ {
+ vixl::EmissionCheckScope guard(
+ GetVIXLAssembler(),
+ (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
+ vixl32::Label return_address;
+ EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
+ __ cmp(mr, Operand(0));
+ EmitPlaceholderBne(this, bne_label);
+ ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
+ __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
+ DCHECK(!needs_null_check); // The thunk cannot handle the null check.
+ // Note: We need a Wide NEG for the unpoisoning.
+ if (kPoisonHeapReferences) {
+ __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
+ }
+ __ Bind(&return_address);
+ DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
+ BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
}
- __ Bind(&return_address);
- DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
- BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
+ MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
return;
}
@@ -8866,6 +8897,7 @@
// Fast path: the GC is not marking: just load the reference.
GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
__ Bind(slow_path->GetExitLabel());
+ MaybeGenerateMarkingRegisterCheck(/* code */ 21);
}
void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
@@ -8920,6 +8952,7 @@
// Fast path: the GC is not marking: nothing to do (the field is
// up-to-date, and we don't need to load the reference).
__ Bind(slow_path->GetExitLabel());
+ MaybeGenerateMarkingRegisterCheck(/* code */ 22);
}
void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction,
@@ -8981,6 +9014,20 @@
GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
}
+void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
+ // The following condition is a compile-time one, so it does not have a run-time cost.
+ if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && 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 (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register temp = temp_loc.IsValid() ? RegisterFrom(temp_loc) : temps.Acquire();
+ GetAssembler()->GenerateMarkingRegisterCheck(temp,
+ kMarkingRegisterCheckBreakCodeBaseCode + code);
+ }
+ }
+}
+
void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
Location out,
Location ref,
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 01cf287..5feb33b 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -22,8 +22,8 @@
#include "common_arm.h"
#include "driver/compiler_options.h"
#include "nodes.h"
-#include "string_reference.h"
#include "parallel_move_resolver.h"
+#include "string_reference.h"
#include "type_reference.h"
#include "utils/arm/assembler_arm_vixl.h"
@@ -661,6 +661,28 @@
ScaleFactor scale_factor,
bool needs_null_check);
+ // Emit code checking the status of the Marking Register, and
+ // aborting the program if MR does not match the value stored in the
+ // art::Thread object. Code is only emitted in debug mode and if
+ // CompilerOptions::EmitRunTimeChecksInDebugMode returns true.
+ //
+ // Argument `code` is used to identify the different occurrences of
+ // MaybeGenerateMarkingRegisterCheck in the code generator, and is
+ // used together with kMarkingRegisterCheckBreakCodeBaseCode to
+ // create the value passed to the BKPT instruction. Note that unlike
+ // in the ARM64 code generator, where `__LINE__` is passed as `code`
+ // argument to
+ // CodeGeneratorARM64::MaybeGenerateMarkingRegisterCheck, we cannot
+ // realistically do that here, as Encoding T1 for the BKPT
+ // instruction only accepts 8-bit immediate values.
+ //
+ // If `temp_loc` is a valid location, it is expected to be a
+ // register and will be used as a temporary to generate code;
+ // otherwise, a temporary will be fetched from the core register
+ // scratch pool.
+ virtual void MaybeGenerateMarkingRegisterCheck(int code,
+ Location temp_loc = Location::NoLocation());
+
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
//
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index f422b9f..9095ecd 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -15,7 +15,9 @@
*/
#include "code_generator_arm64.h"
+
#include "mirror/array-inl.h"
+#include "mirror/string.h"
using namespace vixl::aarch64; // NOLINT(build/namespaces)
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index 14782d7..e7aec76 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -15,7 +15,9 @@
*/
#include "code_generator_x86.h"
+
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace x86 {
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 246044e..c7ee81c 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -15,7 +15,9 @@
*/
#include "code_generator_x86_64.h"
+
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace x86_64 {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index af0e646..99b7793 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -24,9 +24,9 @@
#include "gc/accounting/card_table.h"
#include "intrinsics.h"
#include "intrinsics_x86.h"
+#include "lock_word.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
-#include "lock_word.h"
#include "thread.h"
#include "utils/assembler.h"
#include "utils/stack_checks.h"
@@ -4956,8 +4956,8 @@
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
if (value.IsConstant()) {
- int16_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
- __ movw(Address(base, offset), Immediate(v));
+ __ movw(Address(base, offset),
+ Immediate(CodeGenerator::GetInt16ValueOf(value.GetConstant())));
} else {
__ movw(Address(base, offset), value.AsRegister<Register>());
}
@@ -5404,7 +5404,7 @@
if (value.IsRegister()) {
__ movb(address, value.AsRegister<ByteRegister>());
} else {
- __ movb(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ __ movb(address, Immediate(CodeGenerator::GetInt8ValueOf(value.GetConstant())));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
@@ -5417,7 +5417,7 @@
if (value.IsRegister()) {
__ movw(address, value.AsRegister<Register>());
} else {
- __ movw(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ __ movw(address, Immediate(CodeGenerator::GetInt16ValueOf(value.GetConstant())));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 86f6d51..8283887 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4425,8 +4425,8 @@
case Primitive::kPrimBoolean:
case Primitive::kPrimByte: {
if (value.IsConstant()) {
- int8_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
- __ movb(Address(base, offset), Immediate(v));
+ __ movb(Address(base, offset),
+ Immediate(CodeGenerator::GetInt8ValueOf(value.GetConstant())));
} else {
__ movb(Address(base, offset), value.AsRegister<CpuRegister>());
}
@@ -4436,8 +4436,8 @@
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
if (value.IsConstant()) {
- int16_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
- __ movw(Address(base, offset), Immediate(v));
+ __ movw(Address(base, offset),
+ Immediate(CodeGenerator::GetInt16ValueOf(value.GetConstant())));
} else {
__ movw(Address(base, offset), value.AsRegister<CpuRegister>());
}
@@ -4861,7 +4861,7 @@
if (value.IsRegister()) {
__ movb(address, value.AsRegister<CpuRegister>());
} else {
- __ movb(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ __ movb(address, Immediate(CodeGenerator::GetInt8ValueOf(value.GetConstant())));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
@@ -4875,7 +4875,7 @@
__ movw(address, value.AsRegister<CpuRegister>());
} else {
DCHECK(value.IsConstant()) << value;
- __ movw(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ __ movw(address, Immediate(CodeGenerator::GetInt16ValueOf(value.GetConstant())));
}
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index 1b38acd..aa4f5da 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -28,6 +28,7 @@
#include "arch/x86/instruction_set_features_x86.h"
#include "arch/x86/registers_x86.h"
#include "arch/x86_64/instruction_set_features_x86_64.h"
+#include "code_simulator.h"
#include "code_simulator_container.h"
#include "common_compiler_test.h"
#include "graph_checker.h"
@@ -78,6 +79,21 @@
};
#ifdef ART_ENABLE_CODEGEN_arm
+// Special ARM code generator for codegen testing in a limited code
+// generation environment (i.e. with no runtime support).
+//
+// Note: If we want to exercise certains HIR constructions
+// (e.g. reference field load in Baker read barrier configuration) in
+// codegen tests in the future, we should also:
+// - save the Thread Register (R9) and possibly the Marking Register
+// (R8) before entering the generated function (both registers are
+// callee-save in AAPCS);
+// - set these registers to meaningful values before or upon entering
+// the generated function (so that generated code using them is
+// correct);
+// - restore their original values before leaving the generated
+// function.
+
// Provide our own codegen, that ensures the C calling conventions
// are preserved. Currently, ART and C do not match as R4 is caller-save
// in ART, and callee-save in C. Alternatively, we could use or write
@@ -99,6 +115,50 @@
blocked_core_registers_[arm::R6] = false;
blocked_core_registers_[arm::R7] = false;
}
+
+ void MaybeGenerateMarkingRegisterCheck(int code ATTRIBUTE_UNUSED,
+ Location temp_loc ATTRIBUTE_UNUSED) OVERRIDE {
+ // When turned on, the marking register checks in
+ // CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck expects the
+ // Thread Register and the Marking Register to be set to
+ // meaningful values. This is not the case in codegen testing, so
+ // just disable them entirely here (by doing nothing in this
+ // method).
+ }
+};
+#endif
+
+#ifdef ART_ENABLE_CODEGEN_arm64
+// Special ARM64 code generator for codegen testing in a limited code
+// generation environment (i.e. with no runtime support).
+//
+// Note: If we want to exercise certains HIR constructions
+// (e.g. reference field load in Baker read barrier configuration) in
+// codegen tests in the future, we should also:
+// - save the Thread Register (X19) and possibly the Marking Register
+// (X20) before entering the generated function (both registers are
+// callee-save in AAPCS64);
+// - set these registers to meaningful values before or upon entering
+// the generated function (so that generated code using them is
+// correct);
+// - restore their original values before leaving the generated
+// function.
+class TestCodeGeneratorARM64 : public arm64::CodeGeneratorARM64 {
+ public:
+ TestCodeGeneratorARM64(HGraph* graph,
+ const Arm64InstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options)
+ : arm64::CodeGeneratorARM64(graph, isa_features, compiler_options) {}
+
+ void MaybeGenerateMarkingRegisterCheck(int codem ATTRIBUTE_UNUSED,
+ Location temp_loc ATTRIBUTE_UNUSED) OVERRIDE {
+ // When turned on, the marking register checks in
+ // CodeGeneratorARM64::MaybeGenerateMarkingRegisterCheck expect the
+ // Thread Register and the Marking Register to be set to
+ // meaningful values. This is not the case in codegen testing, so
+ // just disable them entirely here (by doing nothing in this
+ // method).
+ }
};
#endif
@@ -262,7 +322,8 @@
bool has_result,
Expected expected) {
CompilerOptions compiler_options;
- std::unique_ptr<CodeGenerator> codegen(target_config.CreateCodeGenerator(graph, compiler_options));
+ std::unique_ptr<CodeGenerator> codegen(target_config.CreateCodeGenerator(graph,
+ compiler_options));
RunCode(codegen.get(), graph, hook_before_codegen, has_result, expected);
}
@@ -279,9 +340,8 @@
CodeGenerator* create_codegen_arm64(HGraph* graph, const CompilerOptions& compiler_options) {
std::unique_ptr<const Arm64InstructionSetFeatures> features_arm64(
Arm64InstructionSetFeatures::FromCppDefines());
- return new (graph->GetArena()) arm64::CodeGeneratorARM64(graph,
- *features_arm64.get(),
- compiler_options);
+ return new (graph->GetArena())
+ TestCodeGeneratorARM64(graph, *features_arm64.get(), compiler_options);
}
#endif
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index 8fcceed..e354654 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -17,8 +17,8 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
#define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
-#include "instruction_simplifier_shared.h"
#include "debug/dwarf/register.h"
+#include "instruction_simplifier_shared.h"
#include "locations.h"
#include "nodes.h"
#include "utils/arm/constants_arm.h"
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index fdd77e7..96fa540 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "dead_code_elimination.h"
+
#include "arch/x86/instruction_set_features_x86.h"
#include "code_generator_x86.h"
-#include "dead_code_elimination.h"
#include "driver/compiler_options.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc
index 04789d9..bbd28f5 100644
--- a/compiler/optimizing/find_loops_test.cc
+++ b/compiler/optimizing/find_loops_test.cc
@@ -20,8 +20,8 @@
#include "dex_instruction.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-#include "ssa_liveness_analysis.h"
#include "pretty_printer.h"
+#include "ssa_liveness_analysis.h"
#include "gtest/gtest.h"
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index aea901d..327e11f 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -17,8 +17,8 @@
#include "graph_checker.h"
#include <algorithm>
-#include <string>
#include <sstream>
+#include <string>
#include "android-base/stringprintf.h"
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index f8d37bd..e1ed7f6 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "gvn.h"
+
#include "base/arena_allocator.h"
#include "builder.h"
-#include "gvn.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index ab1772b..0b980f5 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -151,6 +151,16 @@
}
/**
+ * Checks if the given phi instruction has been classified as anything by
+ * induction variable analysis. Returns false for anything that cannot be
+ * classified statically, such as reductions or other complex cycles.
+ */
+ bool IsClassified(HPhi* phi) const {
+ HLoopInformation* lp = phi->GetBlock()->GetLoopInformation(); // closest enveloping loop
+ return (lp != nullptr) && (induction_analysis_->LookupInfo(lp, phi) != nullptr);
+ }
+
+ /**
* Checks if header logic of a loop terminates. Sets trip-count tc if known.
*/
bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const;
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 67d2093..2b82b33 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include "induction_var_range.h"
+
#include "base/arena_allocator.h"
#include "builder.h"
#include "induction_var_analysis.h"
-#include "induction_var_range.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 18390cc..0141c26 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -23,8 +23,8 @@
#include "constant_folding.h"
#include "dead_code_elimination.h"
#include "dex/inline_method_analyser.h"
-#include "dex/verified_method.h"
#include "dex/verification_results.h"
+#include "dex/verified_method.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
@@ -38,10 +38,10 @@
#include "optimizing_compiler.h"
#include "reference_type_propagation.h"
#include "register_allocator_linear_scan.h"
+#include "scoped_thread_state_change-inl.h"
#include "sharpening.h"
#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
-#include "scoped_thread_state_change-inl.h"
#include "thread.h"
namespace art {
@@ -1948,7 +1948,7 @@
declared_rti.IsStrictSupertypeOf(actual_rti);
}
-ReferenceTypeInfo HInliner::GetClassRTI(mirror::Class* klass) {
+ReferenceTypeInfo HInliner::GetClassRTI(ObjPtr<mirror::Class> klass) {
return ReferenceTypePropagation::IsAdmissible(klass)
? ReferenceTypeInfo::Create(handles_->NewHandle(klass))
: graph_->GetInexactObjectRti();
@@ -1976,9 +1976,8 @@
++param_idx, ++input_idx) {
HInstruction* input = invoke_instruction->InputAt(input_idx);
if (input->GetType() == Primitive::kPrimNot) {
- mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex(
- param_list->GetTypeItem(param_idx).type_idx_,
- /* resolve */ false);
+ ObjPtr<mirror::Class> param_cls = resolved_method->LookupResolvedClassFromTypeIndex(
+ param_list->GetTypeItem(param_idx).type_idx_);
if (IsReferenceTypeRefinement(GetClassRTI(param_cls),
/* declared_can_be_null */ true,
input)) {
@@ -2027,7 +2026,7 @@
// TODO: we could be more precise by merging the phi inputs but that requires
// some functionality from the reference type propagation.
DCHECK(return_replacement->IsPhi());
- mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */);
+ ObjPtr<mirror::Class> cls = resolved_method->LookupResolvedReturnType();
return_replacement->SetReferenceTypeInfo(GetClassRTI(cls));
}
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 67476b6..c4b3a32 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -19,8 +19,8 @@
#include "dex_file_types.h"
#include "invoke_type.h"
-#include "optimization.h"
#include "jit/profile_compilation_info.h"
+#include "optimization.h"
namespace art {
@@ -207,7 +207,7 @@
// Creates an instance of ReferenceTypeInfo from `klass` if `klass` is
// admissible (see ReferenceTypePropagation::IsAdmissible for details).
// Otherwise returns inexact Object RTI.
- ReferenceTypeInfo GetClassRTI(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ ReferenceTypeInfo GetClassRTI(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 8054140..143c77f 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -23,8 +23,8 @@
#include "driver/compiler_options.h"
#include "imtable-inl.h"
#include "quicken_info.h"
-#include "sharpening.h"
#include "scoped_thread_state_change-inl.h"
+#include "sharpening.h"
namespace art {
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 5a83df3..2a9b9f5 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -21,8 +21,8 @@
#include "base/arena_object.h"
#include "block_builder.h"
#include "dex_file_types.h"
-#include "driver/compiler_driver.h"
#include "driver/compiler_driver-inl.h"
+#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
#include "mirror/dex_cache.h"
#include "nodes.h"
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index f2a8cc0..02cfbbc 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -21,8 +21,8 @@
#include "escape.h"
#include "intrinsics.h"
#include "mirror/class-inl.h"
-#include "sharpening.h"
#include "scoped_thread_state_change-inl.h"
+#include "sharpening.h"
namespace art {
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index a025fb1..a32d0ce 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "instruction_simplifier_arm.h"
+
#include "code_generator.h"
#include "common_arm.h"
-#include "instruction_simplifier_arm.h"
#include "instruction_simplifier_shared.h"
#include "mirror/array-inl.h"
#include "mirror/string.h"
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index b664d41..7bdeef5 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -39,7 +39,7 @@
case Intrinsics::k ## Name: \
return IsStatic;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
}
@@ -55,7 +55,7 @@
case Intrinsics::k ## Name: \
return NeedsEnvironmentOrCache;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
}
@@ -71,7 +71,7 @@
case Intrinsics::k ## Name: \
return SideEffects;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
}
@@ -87,7 +87,7 @@
case Intrinsics::k ## Name: \
return Exceptions;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
}
@@ -172,7 +172,7 @@
os << # Name; \
break;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef STATIC_INTRINSICS_LIST
#undef VIRTUAL_INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 9da5a7f..6411e82 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -63,7 +63,7 @@
Visit ## Name(invoke); \
return;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
@@ -77,7 +77,7 @@
virtual void Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
}
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
diff --git a/compiler/optimizing/intrinsics_arm64.h b/compiler/optimizing/intrinsics_arm64.h
index ff59ce9..5a6d180 100644
--- a/compiler/optimizing/intrinsics_arm64.h
+++ b/compiler/optimizing/intrinsics_arm64.h
@@ -47,7 +47,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
@@ -72,7 +72,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
diff --git a/compiler/optimizing/intrinsics_arm_vixl.h b/compiler/optimizing/intrinsics_arm_vixl.h
index 023cba1..a4a2830 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.h
+++ b/compiler/optimizing/intrinsics_arm_vixl.h
@@ -36,7 +36,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
@@ -63,7 +63,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 4cea6df..2669d97 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -22,6 +22,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "intrinsics.h"
#include "mirror/array-inl.h"
+#include "mirror/object_array-inl.h"
#include "mirror/string.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
diff --git a/compiler/optimizing/intrinsics_mips.h b/compiler/optimizing/intrinsics_mips.h
index eaadad2..05d1aa2 100644
--- a/compiler/optimizing/intrinsics_mips.h
+++ b/compiler/optimizing/intrinsics_mips.h
@@ -39,7 +39,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
@@ -64,7 +64,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index d785567..74be954 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -22,6 +22,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "intrinsics.h"
#include "mirror/array-inl.h"
+#include "mirror/object_array-inl.h"
#include "mirror/string.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
diff --git a/compiler/optimizing/intrinsics_mips64.h b/compiler/optimizing/intrinsics_mips64.h
index 179627a..6880a25 100644
--- a/compiler/optimizing/intrinsics_mips64.h
+++ b/compiler/optimizing/intrinsics_mips64.h
@@ -39,7 +39,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
@@ -64,7 +64,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
diff --git a/compiler/optimizing/intrinsics_x86.h b/compiler/optimizing/intrinsics_x86.h
index 3743cb1..22f11b1 100644
--- a/compiler/optimizing/intrinsics_x86.h
+++ b/compiler/optimizing/intrinsics_x86.h
@@ -39,7 +39,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
@@ -64,7 +64,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
diff --git a/compiler/optimizing/intrinsics_x86_64.h b/compiler/optimizing/intrinsics_x86_64.h
index 97404aa..4b28788 100644
--- a/compiler/optimizing/intrinsics_x86_64.h
+++ b/compiler/optimizing/intrinsics_x86_64.h
@@ -39,7 +39,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
@@ -64,7 +64,7 @@
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
void Visit ## Name(HInvoke* invoke) OVERRIDE;
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 8d15f78..8967d7c 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "licm.h"
+
#include "base/arena_allocator.h"
#include "builder.h"
-#include "licm.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index 86fb8e0..a2c1794 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -466,6 +466,11 @@
CreateReferenceInfoForReferenceType(new_instance);
}
+ void VisitNewArray(HNewArray* new_array) OVERRIDE {
+ // Any references appearing in the ref_info_array_ so far cannot alias with new_array.
+ CreateReferenceInfoForReferenceType(new_array);
+ }
+
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) OVERRIDE {
CreateReferenceInfoForReferenceType(instruction);
}
@@ -478,6 +483,22 @@
CreateReferenceInfoForReferenceType(instruction);
}
+ void VisitInvokeUnresolved(HInvokeUnresolved* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitInvokePolymorphic(HInvokePolymorphic* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitLoadString(HLoadString* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitPhi(HPhi* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
void VisitParameterValue(HParameterValue* instruction) OVERRIDE {
CreateReferenceInfoForReferenceType(instruction);
}
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 211528b..fddda3d 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include "load_store_analysis.h"
#include "load_store_elimination.h"
#include "escape.h"
+#include "load_store_analysis.h"
#include "side_effects_analysis.h"
#include <iostream>
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index a9fe209..40fe35b 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -18,8 +18,8 @@
#include <type_traits>
-#include "nodes.h"
#include "code_generator.h"
+#include "nodes.h"
namespace art {
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 422e58d..027ba77 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -16,9 +16,9 @@
#include "loop_optimization.h"
-#include "arch/instruction_set.h"
#include "arch/arm/instruction_set_features_arm.h"
#include "arch/arm64/instruction_set_features_arm64.h"
+#include "arch/instruction_set.h"
#include "arch/mips/instruction_set_features_mips.h"
#include "arch/mips64/instruction_set_features_mips64.h"
#include "arch/x86/instruction_set_features_x86.h"
@@ -40,6 +40,8 @@
instruction->RemoveAsUserOfAllInputs();
instruction->RemoveEnvironmentUsers();
instruction->GetBlock()->RemoveInstructionOrPhi(instruction, /*ensure_safety=*/ false);
+ RemoveEnvironmentUses(instruction);
+ ResetEnvironmentInputRecords(instruction);
}
// Detect a goto block and sets succ to the single successor.
@@ -254,6 +256,35 @@
return false;
}
+// Detect reductions of the following forms,
+// under assumption phi has only *one* use:
+// x = x_phi + ..
+// x = x_phi - ..
+// x = max(x_phi, ..)
+// x = min(x_phi, ..)
+static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) {
+ if (reduction->IsAdd()) {
+ return reduction->InputAt(0) == phi || reduction->InputAt(1) == phi;
+ } else if (reduction->IsSub()) {
+ return reduction->InputAt(0) == phi;
+ } else if (reduction->IsInvokeStaticOrDirect()) {
+ switch (reduction->AsInvokeStaticOrDirect()->GetIntrinsic()) {
+ case Intrinsics::kMathMinIntInt:
+ case Intrinsics::kMathMinLongLong:
+ case Intrinsics::kMathMinFloatFloat:
+ case Intrinsics::kMathMinDoubleDouble:
+ case Intrinsics::kMathMaxIntInt:
+ case Intrinsics::kMathMaxLongLong:
+ case Intrinsics::kMathMaxFloatFloat:
+ case Intrinsics::kMathMaxDoubleDouble:
+ return reduction->InputAt(0) == phi || reduction->InputAt(1) == phi;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
// Test vector restrictions.
static bool HasVectorRestrictions(uint64_t restrictions, uint64_t tested) {
return (restrictions & tested) != 0;
@@ -267,8 +298,22 @@
return instruction;
}
+// Check that instructions from the induction sets are fully removed: have no uses
+// and no other instructions use them.
+static bool CheckInductionSetFullyRemoved(ArenaSet<HInstruction*>* iset) {
+ for (HInstruction* instr : *iset) {
+ if (instr->GetBlock() != nullptr ||
+ !instr->GetUses().empty() ||
+ !instr->GetEnvUses().empty() ||
+ HasEnvironmentUsedByOthers(instr)) {
+ return false;
+ }
+ }
+ return true;
+}
+
//
-// Class methods.
+// Public methods.
//
HLoopOptimization::HLoopOptimization(HGraph* graph,
@@ -282,7 +327,7 @@
top_loop_(nullptr),
last_loop_(nullptr),
iset_(nullptr),
- induction_simplication_count_(0),
+ reductions_(nullptr),
simplified_(false),
vector_length_(0),
vector_refs_(nullptr),
@@ -316,6 +361,10 @@
last_loop_ = top_loop_ = nullptr;
}
+//
+// Loop setup and traversal.
+//
+
void HLoopOptimization::LocalRun() {
// Build the linear order using the phase-local allocator. This step enables building
// a loop hierarchy that properly reflects the outer-inner and previous-next relation.
@@ -334,17 +383,21 @@
// should use the global allocator.
if (top_loop_ != nullptr) {
ArenaSet<HInstruction*> iset(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+ ArenaSafeMap<HInstruction*, HInstruction*> reds(
+ std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
ArenaSet<ArrayReference> refs(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
ArenaSafeMap<HInstruction*, HInstruction*> map(
std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
// Attach.
iset_ = &iset;
+ reductions_ = &reds;
vector_refs_ = &refs;
vector_map_ = ↦
// Traverse.
TraverseLoopsInnerToOuter(top_loop_);
// Detach.
iset_ = nullptr;
+ reductions_ = nullptr;
vector_refs_ = nullptr;
vector_map_ = nullptr;
}
@@ -397,16 +450,12 @@
}
}
-void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
+bool HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
+ bool changed = false;
for ( ; node != nullptr; node = node->next) {
- // Visit inner loops first.
- uint32_t current_induction_simplification_count = induction_simplication_count_;
- if (node->inner != nullptr) {
- TraverseLoopsInnerToOuter(node->inner);
- }
- // Recompute induction information of this loop if the induction
- // of any inner loop has been simplified.
- if (current_induction_simplification_count != induction_simplication_count_) {
+ // Visit inner loops first. Recompute induction information for this
+ // loop if the induction of any inner loop has changed.
+ if (TraverseLoopsInnerToOuter(node->inner)) {
induction_range_.ReVisit(node->loop_info);
}
// Repeat simplifications in the loop-body until no more changes occur.
@@ -416,12 +465,14 @@
simplified_ = false;
SimplifyInduction(node);
SimplifyBlocks(node);
+ changed = simplified_ || changed;
} while (simplified_);
// Optimize inner loop.
if (node->inner == nullptr) {
- OptimizeInnerLoop(node);
+ changed = OptimizeInnerLoop(node) || changed;
}
}
+ return changed;
}
//
@@ -438,17 +489,18 @@
// for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->AsPhi();
- iset_->clear(); // prepare phi induction
if (TrySetPhiInduction(phi, /*restrict_uses*/ true) &&
TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ false)) {
// Note that it's ok to have replaced uses after the loop with the last value, without
// being able to remove the cycle. Environment uses (which are the reason we may not be
- // able to remove the cycle) within the loop will still hold the right value.
+ // able to remove the cycle) within the loop will still hold the right value. We must
+ // have tried first, however, to replace outside uses.
if (CanRemoveCycle()) {
+ simplified_ = true;
for (HInstruction* i : *iset_) {
RemoveFromCycle(i);
}
- simplified_ = true;
+ DCHECK(CheckInductionSetFullyRemoved(iset_));
}
}
}
@@ -491,21 +543,20 @@
}
}
-void HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
+bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
HBasicBlock* header = node->loop_info->GetHeader();
HBasicBlock* preheader = node->loop_info->GetPreHeader();
// Ensure loop header logic is finite.
int64_t trip_count = 0;
if (!induction_range_.IsFinite(node->loop_info, &trip_count)) {
- return;
+ return false;
}
-
// Ensure there is only a single loop-body (besides the header).
HBasicBlock* body = nullptr;
for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
if (it.Current() != header) {
if (body != nullptr) {
- return;
+ return false;
}
body = it.Current();
}
@@ -513,27 +564,27 @@
CHECK(body != nullptr);
// Ensure there is only a single exit point.
if (header->GetSuccessors().size() != 2) {
- return;
+ return false;
}
HBasicBlock* exit = (header->GetSuccessors()[0] == body)
? header->GetSuccessors()[1]
: header->GetSuccessors()[0];
// Ensure exit can only be reached by exiting loop.
if (exit->GetPredecessors().size() != 1) {
- return;
+ return false;
}
// Detect either an empty loop (no side effects other than plain iteration) or
// a trivial loop (just iterating once). Replace subsequent index uses, if any,
// with the last value and remove the loop, possibly after unrolling its body.
- HInstruction* phi = header->GetFirstPhi();
- iset_->clear(); // prepare phi induction
- if (TrySetSimpleLoopHeader(header)) {
+ HPhi* main_phi = nullptr;
+ if (TrySetSimpleLoopHeader(header, &main_phi)) {
bool is_empty = IsEmptyBody(body);
- if ((is_empty || trip_count == 1) &&
- TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ true)) {
+ if (reductions_->empty() && // TODO: possible with some effort
+ (is_empty || trip_count == 1) &&
+ TryAssignLastValue(node->loop_info, main_phi, preheader, /*collect_loop_uses*/ true)) {
if (!is_empty) {
// Unroll the loop-body, which sees initial value of the index.
- phi->ReplaceWith(phi->InputAt(0));
+ main_phi->ReplaceWith(main_phi->InputAt(0));
preheader->MergeInstructionsWith(body);
}
body->DisconnectAndDelete();
@@ -546,21 +597,20 @@
preheader->AddDominatedBlock(exit);
exit->SetDominator(preheader);
RemoveLoop(node); // update hierarchy
- return;
+ return true;
}
}
-
// Vectorize loop, if possible and valid.
- if (kEnableVectorization) {
- iset_->clear(); // prepare phi induction
- if (TrySetSimpleLoopHeader(header) &&
- ShouldVectorize(node, body, trip_count) &&
- TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ true)) {
- Vectorize(node, body, exit, trip_count);
- graph_->SetHasSIMD(true); // flag SIMD usage
- return;
- }
+ if (kEnableVectorization &&
+ TrySetSimpleLoopHeader(header, &main_phi) &&
+ reductions_->empty() && // TODO: possible with some effort
+ ShouldVectorize(node, body, trip_count) &&
+ TryAssignLastValue(node->loop_info, main_phi, preheader, /*collect_loop_uses*/ true)) {
+ Vectorize(node, body, exit, trip_count);
+ graph_->SetHasSIMD(true); // flag SIMD usage
+ return true;
}
+ return false;
}
//
@@ -601,6 +651,8 @@
// aliased, as well as the property that references either point to the same
// array or to two completely disjoint arrays, i.e., no partial aliasing.
// Other than a few simply heuristics, no detailed subscript analysis is done.
+ // The scan over references also finds a suitable dynamic loop peeling candidate.
+ const ArrayReference* candidate = nullptr;
for (auto i = vector_refs_->begin(); i != vector_refs_->end(); ++i) {
for (auto j = i; ++j != vector_refs_->end(); ) {
if (i->type == j->type && (i->lhs || j->lhs)) {
@@ -636,7 +688,7 @@
}
// Consider dynamic loop peeling for alignment.
- SetPeelingCandidate(trip_count);
+ SetPeelingCandidate(candidate, trip_count);
// Success!
return true;
@@ -659,14 +711,15 @@
bool needs_cleanup = trip_count == 0 || (trip_count % chunk) != 0;
// Adjust vector bookkeeping.
- iset_->clear(); // prepare phi induction
- bool is_simple_loop_header = TrySetSimpleLoopHeader(header); // fills iset_
+ HPhi* main_phi = nullptr;
+ bool is_simple_loop_header = TrySetSimpleLoopHeader(header, &main_phi); // refills sets
DCHECK(is_simple_loop_header);
vector_header_ = header;
vector_body_ = block;
- // Generate dynamic loop peeling trip count, if needed:
- // ptc = <peeling-needed-for-candidate>
+ // Generate dynamic loop peeling trip count, if needed, under the assumption
+ // that the Android runtime guarantees at least "component size" alignment:
+ // ptc = (ALIGN - (&a[initial] % ALIGN)) / type-size
HInstruction* ptc = nullptr;
if (vector_peeling_candidate_ != nullptr) {
DCHECK_LT(vector_length_, trip_count) << "dynamic peeling currently requires known trip count";
@@ -755,6 +808,7 @@
while (!header->GetFirstInstruction()->IsGoto()) {
header->RemoveInstruction(header->GetFirstInstruction());
}
+
// Update loop hierarchy: the old header now resides in the same outer loop
// as the old preheader. Note that we don't bother putting sequential
// loops back in the hierarchy at this point.
@@ -821,13 +875,12 @@
vector_index_ = new (global_allocator_) HAdd(induc_type, vector_index_, step);
Insert(vector_body_, vector_index_);
}
- // Finalize phi for the loop index.
+ // Finalize phi inputs for the loop index.
phi->AddInput(lo);
phi->AddInput(vector_index_);
vector_index_ = phi;
}
-// TODO: accept reductions at left-hand-side, mixed-type store idioms, etc.
bool HLoopOptimization::VectorizeDef(LoopNode* node,
HInstruction* instruction,
bool generate_code) {
@@ -1098,7 +1151,7 @@
case kArm:
case kThumb2:
// Allow vectorization for all ARM devices, because Android assumes that
- // ARM 32-bit always supports advanced SIMD.
+ // ARM 32-bit always supports advanced SIMD (64-bit SIMD).
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -1117,7 +1170,7 @@
return false;
case kArm64:
// Allow vectorization for all ARM devices, because Android assumes that
- // ARMv8 AArch64 always supports advanced SIMD.
+ // ARMv8 AArch64 always supports advanced SIMD (128-bit SIMD).
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -1142,7 +1195,7 @@
}
case kX86:
case kX86_64:
- // Allow vectorization for SSE4-enabled X86 devices only (128-bit vectors).
+ // Allow vectorization for SSE4.1-enabled X86 devices only (128-bit SIMD).
if (features->AsX86InstructionSetFeatures()->HasSSE4_1()) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -1290,8 +1343,6 @@
global_allocator_, base, opa, type, vector_length_, is_string_char_at);
}
// Known dynamically enforced alignment?
- // TODO: detect offset + constant differences.
- // TODO: long run, static alignment analysis?
if (vector_peeling_candidate_ != nullptr &&
vector_peeling_candidate_->base == base &&
vector_peeling_candidate_->offset == offset) {
@@ -1566,9 +1617,11 @@
return true;
}
-void HLoopOptimization::SetPeelingCandidate(int64_t trip_count ATTRIBUTE_UNUSED) {
+void HLoopOptimization::SetPeelingCandidate(const ArrayReference* candidate,
+ int64_t trip_count ATTRIBUTE_UNUSED) {
// Current heuristic: none.
// TODO: implement
+ vector_peeling_candidate_ = candidate;
}
uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) {
@@ -1596,13 +1649,17 @@
//
bool HLoopOptimization::TrySetPhiInduction(HPhi* phi, bool restrict_uses) {
+ // Start with empty phi induction.
+ iset_->clear();
+
// Special case Phis that have equivalent in a debuggable setup. Our graph checker isn't
// smart enough to follow strongly connected components (and it's probably not worth
// it to make it so). See b/33775412.
if (graph_->IsDebuggable() && phi->HasEquivalentPhi()) {
return false;
}
- DCHECK(iset_->empty());
+
+ // Lookup phi induction cycle.
ArenaSet<HInstruction*>* set = induction_range_.LookupCycle(phi);
if (set != nullptr) {
for (HInstruction* i : *set) {
@@ -1614,6 +1671,7 @@
} else if (!i->IsRemovable()) {
return false;
} else if (i != phi && restrict_uses) {
+ // Deal with regular uses.
for (const HUseListNode<HInstruction*>& use : i->GetUses()) {
if (set->find(use.GetUser()) == set->end()) {
return false;
@@ -1627,17 +1685,65 @@
return false;
}
-// Find: phi: Phi(init, addsub)
-// s: SuspendCheck
-// c: Condition(phi, bound)
-// i: If(c)
-// TODO: Find a less pattern matching approach?
-bool HLoopOptimization::TrySetSimpleLoopHeader(HBasicBlock* block) {
+bool HLoopOptimization::TrySetPhiReduction(HPhi* phi) {
DCHECK(iset_->empty());
- HInstruction* phi = block->GetFirstPhi();
- if (phi != nullptr &&
- phi->GetNext() == nullptr &&
- TrySetPhiInduction(phi->AsPhi(), /*restrict_uses*/ false)) {
+ // Only unclassified phi cycles are candidates for reductions.
+ if (induction_range_.IsClassified(phi)) {
+ return false;
+ }
+ // Accept operations like x = x + .., provided that the phi and the reduction are
+ // used exactly once inside the loop, and by each other.
+ HInputsRef inputs = phi->GetInputs();
+ if (inputs.size() == 2) {
+ HInstruction* reduction = inputs[1];
+ if (HasReductionFormat(reduction, phi)) {
+ HLoopInformation* loop_info = phi->GetBlock()->GetLoopInformation();
+ int32_t use_count = 0;
+ bool single_use_inside_loop =
+ // Reduction update only used by phi.
+ reduction->GetUses().HasExactlyOneElement() &&
+ !reduction->HasEnvironmentUses() &&
+ // Reduction update is only use of phi inside the loop.
+ IsOnlyUsedAfterLoop(loop_info, phi, /*collect_loop_uses*/ true, &use_count) &&
+ iset_->size() == 1;
+ iset_->clear(); // leave the way you found it
+ if (single_use_inside_loop) {
+ // Link reduction back, and start recording feed value.
+ reductions_->Put(reduction, phi);
+ reductions_->Put(phi, phi->InputAt(0));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool HLoopOptimization::TrySetSimpleLoopHeader(HBasicBlock* block, /*out*/ HPhi** main_phi) {
+ // Start with empty phi induction and reductions.
+ iset_->clear();
+ reductions_->clear();
+
+ // Scan the phis to find the following (the induction structure has already
+ // been optimized, so we don't need to worry about trivial cases):
+ // (1) optional reductions in loop,
+ // (2) the main induction, used in loop control.
+ HPhi* phi = nullptr;
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ if (TrySetPhiReduction(it.Current()->AsPhi())) {
+ continue;
+ } else if (phi == nullptr) {
+ // Found the first candidate for main induction.
+ phi = it.Current()->AsPhi();
+ } else {
+ return false;
+ }
+ }
+
+ // Then test for a typical loopheader:
+ // s: SuspendCheck
+ // c: Condition(phi, bound)
+ // i: If(c)
+ if (phi != nullptr && TrySetPhiInduction(phi, /*restrict_uses*/ false)) {
HInstruction* s = block->GetFirstInstruction();
if (s != nullptr && s->IsSuspendCheck()) {
HInstruction* c = s->GetNext();
@@ -1649,6 +1755,7 @@
if (i != nullptr && i->IsIf() && i->InputAt(0) == c) {
iset_->insert(c);
iset_->insert(s);
+ *main_phi = phi;
return true;
}
}
@@ -1672,6 +1779,7 @@
bool HLoopOptimization::IsUsedOutsideLoop(HLoopInformation* loop_info,
HInstruction* instruction) {
+ // Deal with regular uses.
for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
if (use.GetUser()->GetBlock()->GetLoopInformation() != loop_info) {
return true;
@@ -1684,6 +1792,7 @@
HInstruction* instruction,
bool collect_loop_uses,
/*out*/ int32_t* use_count) {
+ // Deal with regular uses.
for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
HInstruction* user = use.GetUser();
if (iset_->find(user) == iset_->end()) { // not excluded?
@@ -1709,6 +1818,7 @@
// Try to replace outside uses with the last value.
if (induction_range_.CanGenerateLastValue(instruction)) {
HInstruction* replacement = induction_range_.GenerateLastValue(instruction, graph_, block);
+ // Deal with regular uses.
const HUseList<HInstruction*>& uses = instruction->GetUses();
for (auto it = uses.begin(), end = uses.end(); it != end;) {
HInstruction* user = it->GetUser();
@@ -1724,6 +1834,7 @@
induction_range_.Replace(user, instruction, replacement); // update induction
}
}
+ // Deal with environment uses.
const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
HEnvironment* user = it->GetUser();
@@ -1739,7 +1850,6 @@
}
}
}
- induction_simplication_count_++;
return true;
}
return false;
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index de4bd85..49be8a3 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -104,18 +104,33 @@
bool lhs; // def/use
};
+ //
// Loop setup and traversal.
+ //
+
void LocalRun();
void AddLoop(HLoopInformation* loop_info);
void RemoveLoop(LoopNode* node);
- void TraverseLoopsInnerToOuter(LoopNode* node);
+ // Traverses all loops inner to outer to perform simplifications and optimizations.
+ // Returns true if loops nested inside current loop (node) have changed.
+ bool TraverseLoopsInnerToOuter(LoopNode* node);
+
+ //
// Optimization.
+ //
+
void SimplifyInduction(LoopNode* node);
void SimplifyBlocks(LoopNode* node);
- void OptimizeInnerLoop(LoopNode* node);
+ // Performs optimizations specific to inner loop (empty loop removal,
+ // unrolling, vectorization). Returns true if anything changed.
+ bool OptimizeInnerLoop(LoopNode* node);
+
+ //
// Vectorization analysis and synthesis.
+ //
+
bool ShouldVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count);
void Vectorize(LoopNode* node, HBasicBlock* block, HBasicBlock* exit, int64_t trip_count);
void GenerateNewLoop(LoopNode* node,
@@ -155,12 +170,20 @@
// Vectorization heuristics.
bool IsVectorizationProfitable(int64_t trip_count);
- void SetPeelingCandidate(int64_t trip_count);
+ void SetPeelingCandidate(const ArrayReference* candidate, int64_t trip_count);
uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count);
+ //
// Helpers.
+ //
+
bool TrySetPhiInduction(HPhi* phi, bool restrict_uses);
- bool TrySetSimpleLoopHeader(HBasicBlock* block);
+ bool TrySetPhiReduction(HPhi* phi);
+
+ // Detects loop header with a single induction (returned in main_phi), possibly
+ // other phis for reductions, but no other side effects. Returns true on success.
+ bool TrySetSimpleLoopHeader(HBasicBlock* block, /*out*/ HPhi** main_phi);
+
bool IsEmptyBody(HBasicBlock* block);
bool IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
HInstruction* instruction,
@@ -200,10 +223,12 @@
// Contents reside in phase-local heap memory.
ArenaSet<HInstruction*>* iset_;
- // Counter that tracks how many induction cycles have been simplified. Useful
- // to trigger incremental updates of induction variable analysis of outer loops
- // when the induction of inner loops has changed.
- uint32_t induction_simplication_count_;
+ // Temporary bookkeeping of reduction instructions. Mapping is two-fold:
+ // (1) reductions in the loop-body are mapped back to their phi definition,
+ // (2) phi definitions are mapped to their initial value (updated during
+ // code generation to feed the proper values into the new chain).
+ // Contents reside in phase-local heap memory.
+ ArenaSafeMap<HInstruction*, HInstruction*>* reductions_;
// Flag that tracks if any simplifications have occurred.
bool simplified_;
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index 5b93506..b5b03d8 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -195,4 +195,44 @@
EXPECT_EQ("[[[[[[[[[[][][][][][][][][][]]]]]]]]]]", LoopStructure());
}
+// Check that SimplifyLoop() doesn't invalidate data flow when ordering loop headers'
+// predecessors.
+TEST_F(LoopOptimizationTest, SimplifyLoop) {
+ // Can't use AddLoop as we want special order for blocks predecessors.
+ HBasicBlock* header = new (&allocator_) HBasicBlock(graph_);
+ HBasicBlock* body = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(header);
+ graph_->AddBlock(body);
+
+ // Control flow: make a loop back edge first in the list of predecessors.
+ entry_block_->RemoveSuccessor(return_block_);
+ body->AddSuccessor(header);
+ entry_block_->AddSuccessor(header);
+ header->AddSuccessor(body);
+ header->AddSuccessor(return_block_);
+ DCHECK(header->GetSuccessors()[1] == return_block_);
+
+ // Data flow.
+ header->AddInstruction(new (&allocator_) HIf(parameter_));
+ body->AddInstruction(new (&allocator_) HGoto());
+
+ HPhi* phi = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt);
+ HInstruction* add = new (&allocator_) HAdd(Primitive::kPrimInt, phi, parameter_);
+ header->AddPhi(phi);
+ body->AddInstruction(add);
+
+ phi->AddInput(add);
+ phi->AddInput(parameter_);
+
+ graph_->ClearLoopInformation();
+ graph_->ClearDominanceInformation();
+ graph_->BuildDominatorTree();
+
+ // Check that after optimizations in BuildDominatorTree()/SimplifyCFG() phi inputs
+ // are still mapped correctly to the block predecessors.
+ for (size_t i = 0, e = phi->InputCount(); i < e; i++) {
+ HInstruction* input = phi->InputAt(i);
+ ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i]));
+ }
+}
} // namespace art
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 4ca8337..ddd798b 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -18,16 +18,16 @@
#include <cfloat>
#include "art_method-inl.h"
+#include "base/bit_utils.h"
+#include "base/bit_vector-inl.h"
+#include "base/stl_util.h"
#include "class_linker-inl.h"
#include "code_generator.h"
#include "common_dominator.h"
-#include "ssa_builder.h"
-#include "base/bit_vector-inl.h"
-#include "base/bit_utils.h"
-#include "base/stl_util.h"
#include "intrinsics.h"
#include "mirror/class-inl.h"
#include "scoped_thread_state_change-inl.h"
+#include "ssa_builder.h"
namespace art {
@@ -90,7 +90,8 @@
}
}
-static void RemoveEnvironmentUses(HInstruction* instruction) {
+// Remove the environment use records of the instruction for users.
+void RemoveEnvironmentUses(HInstruction* instruction) {
for (HEnvironment* environment = instruction->GetEnvironment();
environment != nullptr;
environment = environment->GetParent()) {
@@ -102,6 +103,35 @@
}
}
+// Return whether the instruction has an environment and it's used by others.
+bool HasEnvironmentUsedByOthers(HInstruction* instruction) {
+ for (HEnvironment* environment = instruction->GetEnvironment();
+ environment != nullptr;
+ environment = environment->GetParent()) {
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ HInstruction* user = environment->GetInstructionAt(i);
+ if (user != nullptr) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Reset environment records of the instruction itself.
+void ResetEnvironmentInputRecords(HInstruction* instruction) {
+ for (HEnvironment* environment = instruction->GetEnvironment();
+ environment != nullptr;
+ environment = environment->GetParent()) {
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ DCHECK(environment->GetHolder() == instruction);
+ if (environment->GetInstructionAt(i) != nullptr) {
+ environment->SetRawEnvAt(i, nullptr);
+ }
+ }
+ }
+}
+
static void RemoveAsUser(HInstruction* instruction) {
instruction->RemoveAsUserOfAllInputs();
RemoveEnvironmentUses(instruction);
@@ -328,6 +358,35 @@
}
}
+// Reorder phi inputs to match reordering of the block's predecessors.
+static void FixPhisAfterPredecessorsReodering(HBasicBlock* block, size_t first, size_t second) {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ HInstruction* first_instr = phi->InputAt(first);
+ HInstruction* second_instr = phi->InputAt(second);
+ phi->ReplaceInput(first_instr, second);
+ phi->ReplaceInput(second_instr, first);
+ }
+}
+
+// Make sure that the first predecessor of a loop header is the incoming block.
+void HGraph::OrderLoopHeaderPredecessors(HBasicBlock* header) {
+ DCHECK(header->IsLoopHeader());
+ HLoopInformation* info = header->GetLoopInformation();
+ if (info->IsBackEdge(*header->GetPredecessors()[0])) {
+ HBasicBlock* to_swap = header->GetPredecessors()[0];
+ for (size_t pred = 1, e = header->GetPredecessors().size(); pred < e; ++pred) {
+ HBasicBlock* predecessor = header->GetPredecessors()[pred];
+ if (!info->IsBackEdge(*predecessor)) {
+ header->predecessors_[pred] = to_swap;
+ header->predecessors_[0] = predecessor;
+ FixPhisAfterPredecessorsReodering(header, 0, pred);
+ break;
+ }
+ }
+ }
+}
+
void HGraph::SimplifyLoop(HBasicBlock* header) {
HLoopInformation* info = header->GetLoopInformation();
@@ -351,18 +410,7 @@
pre_header->AddSuccessor(header);
}
- // Make sure the first predecessor of a loop header is the incoming block.
- if (info->IsBackEdge(*header->GetPredecessors()[0])) {
- HBasicBlock* to_swap = header->GetPredecessors()[0];
- for (size_t pred = 1, e = header->GetPredecessors().size(); pred < e; ++pred) {
- HBasicBlock* predecessor = header->GetPredecessors()[pred];
- if (!info->IsBackEdge(*predecessor)) {
- header->predecessors_[pred] = to_swap;
- header->predecessors_[0] = predecessor;
- break;
- }
- }
- }
+ OrderLoopHeaderPredecessors(header);
HInstruction* first_instruction = header->GetFirstInstruction();
if (first_instruction != nullptr && first_instruction->IsSuspendCheck()) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 5e072cd..488d472 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -28,14 +28,14 @@
#include "base/iteration_range.h"
#include "base/stl_util.h"
#include "base/transform_array_ref.h"
+#include "deoptimization_kind.h"
#include "dex_file.h"
#include "dex_file_types.h"
-#include "deoptimization_kind.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
-#include "invoke_type.h"
#include "intrinsics_enum.h"
+#include "invoke_type.h"
#include "locations.h"
#include "method_reference.h"
#include "mirror/class.h"
@@ -418,6 +418,7 @@
HBasicBlock* SplitEdge(HBasicBlock* block, HBasicBlock* successor);
void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
+ void OrderLoopHeaderPredecessors(HBasicBlock* header);
void SimplifyLoop(HBasicBlock* header);
int32_t GetNextInstructionId() {
@@ -7059,6 +7060,10 @@
return instruction;
}
+void RemoveEnvironmentUses(HInstruction* instruction);
+bool HasEnvironmentUsedByOthers(HInstruction* instruction);
+void ResetEnvironmentInputRecords(HInstruction* instruction);
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/nodes_shared.cc b/compiler/optimizing/nodes_shared.cc
index f145bf9..f6d33f0 100644
--- a/compiler/optimizing/nodes_shared.cc
+++ b/compiler/optimizing/nodes_shared.cc
@@ -14,9 +14,15 @@
* limitations under the License.
*/
-#include "common_arm64.h"
+// Note: this include order may seem strange and is against the regular style. However it is the
+// required order as nodes_shared does not have the right dependency chain and compilation
+// will fail (as AsType on HInstruction will be defined before the full Instruction).
+#include "nodes.h"
+
#include "nodes_shared.h"
+#include "common_arm64.h"
+
namespace art {
using helpers::CanFitInShifterOperand;
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 7686ba8..f3a78a0 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "base/arena_allocator.h"
#include "nodes.h"
+
+#include "base/arena_allocator.h"
#include "optimizing_unit_test.h"
#include "gtest/gtest.h"
diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc
index 6cb27b3..c24f1de 100644
--- a/compiler/optimizing/optimizing_cfi_test.cc
+++ b/compiler/optimizing/optimizing_cfi_test.cc
@@ -23,8 +23,8 @@
#include "gtest/gtest.h"
#include "optimizing/code_generator.h"
#include "optimizing/optimizing_unit_test.h"
-#include "utils/assembler.h"
#include "utils/arm/assembler_arm_vixl.h"
+#include "utils/assembler.h"
#include "utils/mips/assembler_mips.h"
#include "utils/mips64/assembler_mips64.h"
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b76a0df..a6c33b4 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -22,8 +22,6 @@
#include <stdint.h>
-#include "android-base/strings.h"
-
#ifdef ART_ENABLE_CODEGEN_arm64
#include "instruction_simplifier_arm64.h"
#endif
@@ -87,8 +85,8 @@
#include "prepare_for_register_allocation.h"
#include "reference_type_propagation.h"
#include "register_allocator_linear_scan.h"
-#include "select_generator.h"
#include "scheduler.h"
+#include "select_generator.h"
#include "sharpening.h"
#include "side_effects_analysis.h"
#include "ssa_builder.h"
@@ -1111,12 +1109,7 @@
bool IsCompilingWithCoreImage() {
const std::string& image = Runtime::Current()->GetImageLocation();
- // TODO: This is under-approximating...
- if (android::base::EndsWith(image, "core.art") ||
- android::base::EndsWith(image, "core-optimizing.art")) {
- return true;
- }
- return false;
+ return CompilerDriver::IsCoreImageFilename(image);
}
bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) {
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 1cdcbd2..08493fa 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -17,12 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
#define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
-#include "nodes.h"
#include "builder.h"
#include "common_compiler_test.h"
#include "dex_file.h"
#include "dex_instruction.h"
#include "handle_scope.h"
+#include "nodes.h"
#include "scoped_thread_state_change.h"
#include "ssa_builder.h"
#include "ssa_liveness_analysis.h"
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 5e8fe37..50620f0 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -18,8 +18,8 @@
#include "nodes.h"
#include "parallel_move_resolver.h"
-#include "gtest/gtest.h"
#include "gtest/gtest-typed-test.h"
+#include "gtest/gtest.h"
namespace art {
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index aa42fd6..7c6b69f 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -208,8 +208,8 @@
void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
if (invoke->IsStaticWithExplicitClinitCheck()) {
- HLoadClass* last_input = invoke->GetInputs().back()->AsLoadClass();
- DCHECK(last_input != nullptr)
+ HInstruction* last_input = invoke->GetInputs().back();
+ DCHECK(last_input->IsLoadClass())
<< "Last input is not HLoadClass. It is " << last_input->DebugName();
// Detach the explicit class initialization check from the invoke.
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 1af94f3..14d2360 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -14,13 +14,14 @@
* limitations under the License.
*/
+#include "pretty_printer.h"
+
#include "base/arena_allocator.h"
#include "builder.h"
#include "dex_file.h"
#include "dex_instruction.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-#include "pretty_printer.h"
#include "gtest/gtest.h"
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index ecbf52b..561c9ea 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -525,7 +525,7 @@
// Use a null loader. We should probably use the compiling method's class loader,
// but then we would need to pass it to RTPVisitor just for this debug check. Since
// the method is from the String class, the null loader is good enough.
- Handle<mirror::ClassLoader> loader;
+ Handle<mirror::ClassLoader> loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
ArtMethod* method = cl->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect);
DCHECK(method != nullptr);
@@ -848,7 +848,7 @@
ScopedObjectAccess soa(Thread::Current());
ArtMethod* method = instr->GetResolvedMethod();
- mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false);
+ ObjPtr<mirror::Class> klass = (method == nullptr) ? nullptr : method->LookupResolvedReturnType();
SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
}
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 215e967..b19f473 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -46,7 +46,7 @@
// Returns true if klass is admissible to the propagation: non-null and resolved.
// For an array type, we also check if the component type is admissible.
- static bool IsAdmissible(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ static bool IsAdmissible(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
return klass != nullptr &&
klass->IsResolved() &&
(!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType()));
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index 0b49ce1..d537459 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -14,12 +14,13 @@
* limitations under the License.
*/
+#include "reference_type_propagation.h"
+
#include "base/arena_allocator.h"
#include "builder.h"
#include "nodes.h"
#include "object_lock.h"
#include "optimizing_unit_test.h"
-#include "reference_type_propagation.h"
namespace art {
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 5b768d5..c3b33e2 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -25,7 +25,6 @@
#include "register_allocator_linear_scan.h"
#include "ssa_liveness_analysis.h"
-
namespace art {
RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator,
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 24a2ab2..bcdd7f9 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "register_allocator.h"
+
#include "arch/x86/instruction_set_features_x86.h"
#include "base/arena_allocator.h"
#include "builder.h"
@@ -25,7 +27,6 @@
#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-#include "register_allocator.h"
#include "register_allocator_linear_scan.h"
#include "ssa_liveness_analysis.h"
#include "ssa_phi_elimination.h"
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index 5ad011d..38cd51b 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -554,6 +554,14 @@
}
void HScheduler::Schedule(HGraph* graph) {
+ // We run lsa here instead of in a separate pass to better control whether we
+ // should run the analysis or not.
+ LoadStoreAnalysis lsa(graph);
+ if (!only_optimize_loop_blocks_ || graph->HasLoops()) {
+ lsa.Run();
+ scheduling_graph_.SetHeapLocationCollector(lsa.GetHeapLocationCollector());
+ }
+
for (HBasicBlock* block : graph->GetReversePostOrder()) {
if (IsSchedulable(block)) {
Schedule(block);
@@ -566,14 +574,6 @@
// Build the scheduling graph.
scheduling_graph_.Clear();
-
- // Only perform LSA/HeapLocation analysis on the basic block that
- // is going to get instruction scheduled.
- HeapLocationCollector heap_location_collector(block->GetGraph());
- heap_location_collector.VisitBasicBlock(block);
- heap_location_collector.BuildAliasingMatrix();
- scheduling_graph_.SetHeapLocationCollector(heap_location_collector);
-
for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
CHECK_EQ(instruction->GetBlock(), block)
@@ -724,8 +724,8 @@
instruction->IsClassTableGet() ||
instruction->IsCurrentMethod() ||
instruction->IsDivZeroCheck() ||
- instruction->IsInstanceFieldGet() ||
- instruction->IsInstanceFieldSet() ||
+ (instruction->IsInstanceFieldGet() && !instruction->AsInstanceFieldGet()->IsVolatile()) ||
+ (instruction->IsInstanceFieldSet() && !instruction->AsInstanceFieldSet()->IsVolatile()) ||
instruction->IsInstanceOf() ||
instruction->IsInvokeInterface() ||
instruction->IsInvokeStaticOrDirect() ||
@@ -741,14 +741,10 @@
instruction->IsReturn() ||
instruction->IsReturnVoid() ||
instruction->IsSelect() ||
- instruction->IsStaticFieldGet() ||
- instruction->IsStaticFieldSet() ||
+ (instruction->IsStaticFieldGet() && !instruction->AsStaticFieldGet()->IsVolatile()) ||
+ (instruction->IsStaticFieldSet() && !instruction->AsStaticFieldSet()->IsVolatile()) ||
instruction->IsSuspendCheck() ||
- instruction->IsTypeConversion() ||
- instruction->IsUnresolvedInstanceFieldGet() ||
- instruction->IsUnresolvedInstanceFieldSet() ||
- instruction->IsUnresolvedStaticFieldGet() ||
- instruction->IsUnresolvedStaticFieldSet();
+ instruction->IsTypeConversion();
}
bool HScheduler::IsSchedulable(const HBasicBlock* block) const {
diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h
index 930a2c8..66ffac5 100644
--- a/compiler/optimizing/scheduler.h
+++ b/compiler/optimizing/scheduler.h
@@ -20,11 +20,11 @@
#include <fstream>
#include "base/time_utils.h"
+#include "code_generator.h"
#include "driver/compiler_driver.h"
#include "load_store_analysis.h"
#include "nodes.h"
#include "optimization.h"
-#include "code_generator.h"
namespace art {
diff --git a/compiler/optimizing/scheduler_arm.cc b/compiler/optimizing/scheduler_arm.cc
index 627ab4e..d6eb6e3 100644
--- a/compiler/optimizing/scheduler_arm.cc
+++ b/compiler/optimizing/scheduler_arm.cc
@@ -14,11 +14,13 @@
* limitations under the License.
*/
+#include "scheduler_arm.h"
+
#include "arch/arm/instruction_set_features_arm.h"
#include "code_generator_utils.h"
#include "common_arm.h"
#include "mirror/array-inl.h"
-#include "scheduler_arm.h"
+#include "mirror/string.h"
namespace art {
namespace arm {
@@ -167,22 +169,346 @@
HandleShiftLatencies(instr);
}
-void SchedulingLatencyVisitorARM::VisitCondition(HCondition* instr) {
- switch (instr->GetLeft()->GetType()) {
- case Primitive::kPrimLong:
- last_visited_internal_latency_ = 4 * kArmIntegerOpLatency;
+void SchedulingLatencyVisitorARM::HandleGenerateConditionWithZero(IfCondition condition) {
+ switch (condition) {
+ case kCondEQ:
+ case kCondBE:
+ case kCondNE:
+ case kCondA:
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ last_visited_latency_ = kArmIntegerOpLatency;
break;
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- last_visited_internal_latency_ = 2 * kArmFloatingPointOpLatency;
+ case kCondGE:
+ // Mvn
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ FALLTHROUGH_INTENDED;
+ case kCondLT:
+ // Lsr
+ last_visited_latency_ = kArmIntegerOpLatency;
+ break;
+ case kCondAE:
+ // Trivially true.
+ // Mov
+ last_visited_latency_ = kArmIntegerOpLatency;
+ break;
+ case kCondB:
+ // Trivially false.
+ // Mov
+ last_visited_latency_ = kArmIntegerOpLatency;
break;
default:
- last_visited_internal_latency_ = 2 * kArmIntegerOpLatency;
- break;
+ LOG(FATAL) << "Unexpected condition " << condition;
+ UNREACHABLE();
}
+}
+
+void SchedulingLatencyVisitorARM::HandleGenerateLongTestConstant(HCondition* condition) {
+ DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
+
+ IfCondition cond = condition->GetCondition();
+
+ HInstruction* right = condition->InputAt(1);
+
+ int64_t value = Uint64ConstantFrom(right);
+
+ // Comparisons against 0 are common enough, so codegen has special handling for them.
+ if (value == 0) {
+ switch (cond) {
+ case kCondNE:
+ case kCondA:
+ case kCondEQ:
+ case kCondBE:
+ // Orrs
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ return;
+ case kCondLT:
+ case kCondGE:
+ // Cmp
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ return;
+ case kCondB:
+ case kCondAE:
+ // Cmp
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ return;
+ default:
+ break;
+ }
+ }
+
+ switch (cond) {
+ case kCondEQ:
+ case kCondNE:
+ case kCondB:
+ case kCondBE:
+ case kCondA:
+ case kCondAE: {
+ // Cmp, IT, Cmp
+ last_visited_internal_latency_ += 3 * kArmIntegerOpLatency;
+ break;
+ }
+ case kCondLE:
+ case kCondGT:
+ // Trivially true or false.
+ if (value == std::numeric_limits<int64_t>::max()) {
+ // Cmp
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case kCondGE:
+ case kCondLT: {
+ // Cmp, Sbcs
+ last_visited_internal_latency_ += 2 * kArmIntegerOpLatency;
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+}
+
+void SchedulingLatencyVisitorARM::HandleGenerateLongTest(HCondition* condition) {
+ DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong);
+
+ IfCondition cond = condition->GetCondition();
+
+ switch (cond) {
+ case kCondEQ:
+ case kCondNE:
+ case kCondB:
+ case kCondBE:
+ case kCondA:
+ case kCondAE: {
+ // Cmp, IT, Cmp
+ last_visited_internal_latency_ += 3 * kArmIntegerOpLatency;
+ break;
+ }
+ case kCondLE:
+ case kCondGT:
+ case kCondGE:
+ case kCondLT: {
+ // Cmp, Sbcs
+ last_visited_internal_latency_ += 2 * kArmIntegerOpLatency;
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+}
+
+// The GenerateTest series of function all counted as internal latency.
+void SchedulingLatencyVisitorARM::HandleGenerateTest(HCondition* condition) {
+ const Primitive::Type type = condition->GetLeft()->GetType();
+
+ if (type == Primitive::kPrimLong) {
+ condition->InputAt(1)->IsConstant()
+ ? HandleGenerateLongTestConstant(condition)
+ : HandleGenerateLongTest(condition);
+ } else if (Primitive::IsFloatingPointType(type)) {
+ // GenerateVcmp + Vmrs
+ last_visited_internal_latency_ += 2 * kArmFloatingPointOpLatency;
+ } else {
+ // Cmp
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ }
+}
+
+bool SchedulingLatencyVisitorARM::CanGenerateTest(HCondition* condition) {
+ if (condition->GetLeft()->GetType() == Primitive::kPrimLong) {
+ HInstruction* right = condition->InputAt(1);
+
+ if (right->IsConstant()) {
+ IfCondition c = condition->GetCondition();
+ const uint64_t value = Uint64ConstantFrom(right);
+
+ if (c < kCondLT || c > kCondGE) {
+ if (value != 0) {
+ return false;
+ }
+ } else if (c == kCondLE || c == kCondGT) {
+ if (value < std::numeric_limits<int64_t>::max() &&
+ !codegen_->GetAssembler()->ShifterOperandCanHold(SBC, High32Bits(value + 1), kCcSet)) {
+ return false;
+ }
+ } else if (!codegen_->GetAssembler()->ShifterOperandCanHold(SBC, High32Bits(value), kCcSet)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void SchedulingLatencyVisitorARM::HandleGenerateConditionGeneric(HCondition* cond) {
+ HandleGenerateTest(cond);
+
+ // Unlike codegen pass, we cannot check 'out' register IsLow() here,
+ // because scheduling is before liveness(location builder) and register allocator,
+ // so we can only choose to follow one path of codegen by assuming otu.IsLow() is true.
+ last_visited_internal_latency_ += 2 * kArmIntegerOpLatency;
last_visited_latency_ = kArmIntegerOpLatency;
}
+void SchedulingLatencyVisitorARM::HandleGenerateEqualLong(HCondition* cond) {
+ DCHECK_EQ(cond->GetLeft()->GetType(), Primitive::kPrimLong);
+
+ IfCondition condition = cond->GetCondition();
+
+ last_visited_internal_latency_ += 2 * kArmIntegerOpLatency;
+
+ if (condition == kCondNE) {
+ // Orrs, IT, Mov
+ last_visited_internal_latency_ += 3 * kArmIntegerOpLatency;
+ } else {
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ HandleGenerateConditionWithZero(condition);
+ }
+}
+
+void SchedulingLatencyVisitorARM::HandleGenerateLongComparesAndJumps() {
+ last_visited_internal_latency_ += 4 * kArmIntegerOpLatency;
+ last_visited_internal_latency_ += kArmBranchLatency;
+}
+
+void SchedulingLatencyVisitorARM::HandleGenerateConditionLong(HCondition* cond) {
+ DCHECK_EQ(cond->GetLeft()->GetType(), Primitive::kPrimLong);
+
+ IfCondition condition = cond->GetCondition();
+ HInstruction* right = cond->InputAt(1);
+
+ if (right->IsConstant()) {
+ // Comparisons against 0 are common enough, so codegen has special handling for them.
+ if (Uint64ConstantFrom(right) == 0) {
+ switch (condition) {
+ case kCondNE:
+ case kCondA:
+ case kCondEQ:
+ case kCondBE:
+ // Orr
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ HandleGenerateConditionWithZero(condition);
+ return;
+ case kCondLT:
+ case kCondGE:
+ FALLTHROUGH_INTENDED;
+ case kCondAE:
+ case kCondB:
+ HandleGenerateConditionWithZero(condition);
+ return;
+ case kCondLE:
+ case kCondGT:
+ default:
+ break;
+ }
+ }
+ }
+
+ if ((condition == kCondEQ || condition == kCondNE) &&
+ !CanGenerateTest(cond)) {
+ HandleGenerateEqualLong(cond);
+ return;
+ }
+
+ if (CanGenerateTest(cond)) {
+ HandleGenerateConditionGeneric(cond);
+ return;
+ }
+
+ HandleGenerateLongComparesAndJumps();
+
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ last_visited_latency_ = kArmBranchLatency;;
+}
+
+void SchedulingLatencyVisitorARM::HandleGenerateConditionIntegralOrNonPrimitive(HCondition* cond) {
+ const Primitive::Type type = cond->GetLeft()->GetType();
+
+ DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
+
+ if (type == Primitive::kPrimLong) {
+ HandleGenerateConditionLong(cond);
+ return;
+ }
+
+ IfCondition condition = cond->GetCondition();
+ HInstruction* right = cond->InputAt(1);
+ int64_t value;
+
+ if (right->IsConstant()) {
+ value = Uint64ConstantFrom(right);
+
+ // Comparisons against 0 are common enough, so codegen has special handling for them.
+ if (value == 0) {
+ switch (condition) {
+ case kCondNE:
+ case kCondA:
+ case kCondEQ:
+ case kCondBE:
+ case kCondLT:
+ case kCondGE:
+ case kCondAE:
+ case kCondB:
+ HandleGenerateConditionWithZero(condition);
+ return;
+ case kCondLE:
+ case kCondGT:
+ default:
+ break;
+ }
+ }
+ }
+
+ if (condition == kCondEQ || condition == kCondNE) {
+ if (condition == kCondNE) {
+ // CMP, IT, MOV.ne
+ last_visited_internal_latency_ += 2 * kArmIntegerOpLatency;
+ last_visited_latency_ = kArmIntegerOpLatency;
+ } else {
+ last_visited_internal_latency_ += kArmIntegerOpLatency;
+ HandleGenerateConditionWithZero(condition);
+ }
+ return;
+ }
+
+ HandleGenerateConditionGeneric(cond);
+}
+
+void SchedulingLatencyVisitorARM::HandleCondition(HCondition* cond) {
+ if (cond->IsEmittedAtUseSite()) {
+ last_visited_latency_ = 0;
+ return;
+ }
+
+ const Primitive::Type type = cond->GetLeft()->GetType();
+
+ if (Primitive::IsFloatingPointType(type)) {
+ HandleGenerateConditionGeneric(cond);
+ return;
+ }
+
+ DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type;
+
+ const IfCondition condition = cond->GetCondition();
+
+ if (type == Primitive::kPrimBoolean &&
+ cond->GetRight()->GetType() == Primitive::kPrimBoolean &&
+ (condition == kCondEQ || condition == kCondNE)) {
+ if (condition == kCondEQ) {
+ last_visited_internal_latency_ = kArmIntegerOpLatency;
+ }
+ last_visited_latency_ = kArmIntegerOpLatency;
+ return;
+ }
+
+ HandleGenerateConditionIntegralOrNonPrimitive(cond);
+}
+
+void SchedulingLatencyVisitorARM::VisitCondition(HCondition* instr) {
+ HandleCondition(instr);
+}
+
void SchedulingLatencyVisitorARM::VisitCompare(HCompare* instr) {
Primitive::Type type = instr->InputAt(0)->GetType();
switch (type) {
diff --git a/compiler/optimizing/scheduler_arm.h b/compiler/optimizing/scheduler_arm.h
index a9f2295..fe274d2 100644
--- a/compiler/optimizing/scheduler_arm.h
+++ b/compiler/optimizing/scheduler_arm.h
@@ -109,6 +109,17 @@
#undef DECLARE_VISIT_INSTRUCTION
private:
+ bool CanGenerateTest(HCondition* cond);
+ void HandleGenerateConditionWithZero(IfCondition cond);
+ void HandleGenerateLongTestConstant(HCondition* cond);
+ void HandleGenerateLongTest(HCondition* cond);
+ void HandleGenerateLongComparesAndJumps();
+ void HandleGenerateTest(HCondition* cond);
+ void HandleGenerateConditionGeneric(HCondition* cond);
+ void HandleGenerateEqualLong(HCondition* cond);
+ void HandleGenerateConditionLong(HCondition* cond);
+ void HandleGenerateConditionIntegralOrNonPrimitive(HCondition* cond);
+ void HandleCondition(HCondition* instr);
void HandleBinaryOperationLantencies(HBinaryOperation* instr);
void HandleBitwiseOperationLantencies(HBinaryOperation* instr);
void HandleShiftLatencies(HBinaryOperation* instr);
diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc
index 83b487f..510619f 100644
--- a/compiler/optimizing/scheduler_arm64.cc
+++ b/compiler/optimizing/scheduler_arm64.cc
@@ -15,8 +15,10 @@
*/
#include "scheduler_arm64.h"
+
#include "code_generator_utils.h"
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace arm64 {
diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc
index 10c3cd7..cdb6666 100644
--- a/compiler/optimizing/scheduler_test.cc
+++ b/compiler/optimizing/scheduler_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "scheduler.h"
+
#include "base/arena_allocator.h"
#include "builder.h"
#include "codegen_test_utils.h"
@@ -23,7 +25,6 @@
#include "optimizing_unit_test.h"
#include "pc_relative_fixups_x86.h"
#include "register_allocator.h"
-#include "scheduler.h"
#ifdef ART_ENABLE_CODEGEN_arm64
#include "scheduler_arm64.h"
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 8bd568b..9536d14 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -21,10 +21,9 @@
#include "base/enums.h"
#include "class_linker.h"
#include "code_generator.h"
+#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
-#include "utils/dex_cache_arrays_layout-inl.h"
-#include "driver/compiler_driver.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "handle_scope-inl.h"
@@ -33,6 +32,7 @@
#include "nodes.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
diff --git a/compiler/optimizing/ssa_liveness_analysis_test.cc b/compiler/optimizing/ssa_liveness_analysis_test.cc
index 029eb4b..b46060a 100644
--- a/compiler/optimizing/ssa_liveness_analysis_test.cc
+++ b/compiler/optimizing/ssa_liveness_analysis_test.cc
@@ -14,15 +14,16 @@
* limitations under the License.
*/
+#include "ssa_liveness_analysis.h"
+
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
#include "base/arena_allocator.h"
#include "base/arena_containers.h"
-#include "driver/compiler_options.h"
#include "code_generator.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-#include "ssa_liveness_analysis.h"
namespace art {
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index aec7a3c..b4f8408 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -16,8 +16,8 @@
#include "ssa_phi_elimination.h"
-#include "base/arena_containers.h"
#include "base/arena_bit_vector.h"
+#include "base/arena_containers.h"
#include "base/bit_vector-inl.h"
namespace art {
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index af3b447..9df1b74 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -82,6 +82,22 @@
}
}
+void ArmVIXLAssembler::GenerateMarkingRegisterCheck(vixl32::Register temp, int code) {
+ // The Marking Register is only used in the Baker read barrier configuration.
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ vixl32::Label mr_is_ok;
+
+ // temp = self.tls32_.is.gc_marking
+ ___ Ldr(temp, MemOperand(tr, Thread::IsGcMarkingOffset<kArmPointerSize>().Int32Value()));
+ // Check that mr == self.tls32_.is.gc_marking.
+ ___ Cmp(mr, temp);
+ ___ B(eq, &mr_is_ok, /* far_target */ false);
+ ___ Bkpt(code);
+ ___ Bind(&mr_is_ok);
+}
+
void ArmVIXLAssembler::LoadImmediate(vixl32::Register rd, int32_t value) {
// TODO(VIXL): Implement this optimization in VIXL.
if (!ShifterOperandCanAlwaysHold(value) && ShifterOperandCanAlwaysHold(~value)) {
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index 66b22ea..9c11fd3 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -178,6 +178,7 @@
//
// Heap poisoning.
//
+
// Poison a heap reference contained in `reg`.
void PoisonHeapReference(vixl32::Register reg);
// Unpoison a heap reference contained in `reg`.
@@ -187,6 +188,15 @@
// Unpoison a heap reference contained in `reg` if heap poisoning is enabled.
void MaybeUnpoisonHeapReference(vixl32::Register reg);
+ // Emit code checking the status of the Marking Register, and aborting
+ // the program if MR does not match the value stored in the art::Thread
+ // object.
+ //
+ // Argument `temp` is used as a temporary register to generate code.
+ // Argument `code` is used to identify the different occurrences of
+ // MaybeGenerateMarkingRegisterCheck and is passed to the BKPT instruction.
+ void GenerateMarkingRegisterCheck(vixl32::Register temp, int code = 0);
+
void StoreToOffset(StoreOperandType type,
vixl32::Register reg,
vixl32::Register base,
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index bebe64c..ed57ca6 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include "jni_macro_assembler_arm_vixl.h"
+
#include <iostream>
#include <type_traits>
-#include "jni_macro_assembler_arm_vixl.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "thread.h"
diff --git a/compiler/utils/arm/managed_register_arm_test.cc b/compiler/utils/arm/managed_register_arm_test.cc
index f5d4cc0..43b0b51 100644
--- a/compiler/utils/arm/managed_register_arm_test.cc
+++ b/compiler/utils/arm/managed_register_arm_test.cc
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "globals.h"
#include "managed_register_arm.h"
+#include "globals.h"
#include "gtest/gtest.h"
namespace art {
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 6ed0e9b..d8a48a5 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -158,6 +158,24 @@
}
}
+void Arm64Assembler::GenerateMarkingRegisterCheck(Register temp, int code) {
+ // The Marking Register is only used in the Baker read barrier configuration.
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ vixl::aarch64::Register mr = reg_x(MR); // Marking Register.
+ vixl::aarch64::Register tr = reg_x(TR); // Thread Register.
+ vixl::aarch64::Label mr_is_ok;
+
+ // temp = self.tls32_.is.gc_marking
+ ___ Ldr(temp, MemOperand(tr, Thread::IsGcMarkingOffset<kArm64PointerSize>().Int32Value()));
+ // Check that mr == self.tls32_.is.gc_marking.
+ ___ Cmp(mr.W(), temp);
+ ___ B(eq, &mr_is_ok);
+ ___ Brk(code);
+ ___ Bind(&mr_is_ok);
+}
+
#undef ___
} // namespace arm64
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index 66a7fed..6b28363 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -23,9 +23,9 @@
#include "base/arena_containers.h"
#include "base/logging.h"
+#include "offsets.h"
#include "utils/arm64/managed_register_arm64.h"
#include "utils/assembler.h"
-#include "offsets.h"
// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
@@ -98,6 +98,15 @@
// Unpoison a heap reference contained in `reg` if heap poisoning is enabled.
void MaybeUnpoisonHeapReference(vixl::aarch64::Register reg);
+ // Emit code checking the status of the Marking Register, and aborting
+ // the program if MR does not match the value stored in the art::Thread
+ // object.
+ //
+ // Argument `temp` is used as a temporary register to generate code.
+ // Argument `code` is used to identify the different occurrences of
+ // MaybeGenerateMarkingRegisterCheck and is passed to the BRK instruction.
+ void GenerateMarkingRegisterCheck(vixl::aarch64::Register temp, int code = 0);
+
void Bind(Label* label ATTRIBUTE_UNUSED) OVERRIDE {
UNIMPLEMENTED(FATAL) << "Do not use Bind for ARM64";
}
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index bab84be..9732b76 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -662,7 +662,7 @@
___ Bind(Arm64JNIMacroLabel::Cast(label)->AsArm64());
}
-void Arm64JNIMacroAssembler::EmitExceptionPoll(Arm64Exception *exception) {
+void Arm64JNIMacroAssembler::EmitExceptionPoll(Arm64Exception* exception) {
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(reg_x(exception->scratch_.AsXRegister()));
Register temp = temps.AcquireX();
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h
index 264e99a..baf0434 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.h
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h
@@ -25,9 +25,9 @@
#include "base/arena_containers.h"
#include "base/enums.h"
#include "base/logging.h"
+#include "offsets.h"
#include "utils/assembler.h"
#include "utils/jni_macro_assembler.h"
-#include "offsets.h"
// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc
index 79076b8..2a79313 100644
--- a/compiler/utils/arm64/managed_register_arm64_test.cc
+++ b/compiler/utils/arm64/managed_register_arm64_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "globals.h"
-#include "assembler_arm64.h"
#include "managed_register_arm64.h"
+
+#include "assembler_arm64.h"
+#include "globals.h"
#include "gtest/gtest.h"
namespace art {
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index f655994..ef53d72 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -19,14 +19,15 @@
#include "assembler.h"
-#include "assembler_test_base.h"
-#include "common_runtime_test.h" // For ScratchFile
+#include <sys/stat.h>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iterator>
-#include <sys/stat.h>
+
+#include "assembler_test_base.h"
+#include "common_runtime_test.h" // For ScratchFile
namespace art {
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index d76cb1c..2ef43bd 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -17,11 +17,11 @@
#ifndef ART_COMPILER_UTILS_ASSEMBLER_TEST_BASE_H_
#define ART_COMPILER_UTILS_ASSEMBLER_TEST_BASE_H_
+#include <sys/stat.h>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iterator>
-#include <sys/stat.h>
#include "android-base/strings.h"
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 741beab..e51b622 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -16,10 +16,10 @@
#include <dirent.h>
#include <errno.h>
-#include <fstream>
-#include <map>
#include <string.h>
#include <sys/types.h>
+#include <fstream>
+#include <map>
#include "gtest/gtest.h"
diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h
index c06e9ca..c866504 100644
--- a/compiler/utils/dedupe_set-inl.h
+++ b/compiler/utils/dedupe_set-inl.h
@@ -19,14 +19,15 @@
#include "dedupe_set.h"
-#include <algorithm>
#include <inttypes.h>
+
+#include <algorithm>
#include <unordered_map>
#include "android-base/stringprintf.h"
-#include "base/mutex.h"
#include "base/hash_set.h"
+#include "base/mutex.h"
#include "base/stl_util.h"
#include "base/time_utils.h"
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index b62f216..3baa061 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -17,8 +17,8 @@
#ifndef ART_COMPILER_UTILS_DEDUPE_SET_H_
#define ART_COMPILER_UTILS_DEDUPE_SET_H_
-#include <memory>
#include <stdint.h>
+#include <memory>
#include <string>
#include "base/macros.h"
diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h
index 59a1a48..a8ca111 100644
--- a/compiler/utils/jni_macro_assembler.h
+++ b/compiler/utils/jni_macro_assembler.h
@@ -216,8 +216,15 @@
*/
virtual DebugFrameOpCodeWriterForAssembler& cfi() = 0;
+ void SetEmitRunTimeChecksInDebugMode(bool value) {
+ emit_run_time_checks_in_debug_mode_ = value;
+ }
+
protected:
- explicit JNIMacroAssembler() {}
+ JNIMacroAssembler() {}
+
+ // Should run-time checks be emitted in debug mode?
+ bool emit_run_time_checks_in_debug_mode_ = false;
};
// A "Label" class used with the JNIMacroAssembler
diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h
index 293f4cd..6129680 100644
--- a/compiler/utils/jni_macro_assembler_test.h
+++ b/compiler/utils/jni_macro_assembler_test.h
@@ -22,11 +22,12 @@
#include "assembler_test_base.h"
#include "common_runtime_test.h" // For ScratchFile
+#include <sys/stat.h>
+
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iterator>
-#include <sys/stat.h>
namespace art {
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 24e3450..2cbabcf 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -2920,6 +2920,102 @@
static_cast<FRegister>(wt));
}
+void MipsAssembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
+void MipsAssembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b),
+ static_cast<FRegister>(wd),
+ static_cast<FRegister>(ws),
+ static_cast<FRegister>(wt));
+}
+
void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst,
FRegister src,
bool is_double) {
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index e42bb3f..a7ff931 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -613,6 +613,19 @@
void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
// Helper for replicating floating point value in all destination elements.
void ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double);
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index 6ee2a5c..b72a14e 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -1752,6 +1752,66 @@
DriverStr(RepeatVVV(&mips::MipsAssembler::IlvrD, "ilvr.d ${reg1}, ${reg2}, ${reg3}"), "ilvr.d");
}
+TEST_F(AssemblerMIPS32r6Test, MaddvB) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvB, "maddv.b ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.b");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvH) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvH, "maddv.h ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.h");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvW) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvW, "maddv.w ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaddvD) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvD, "maddv.d ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvB) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvB, "msubv.b ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.b");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvH) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvH, "msubv.h ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.h");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvW) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvW, "msubv.w ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MsubvD) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvD, "msubv.d ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmaddW) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::FmaddW, "fmadd.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmadd.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmaddD) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::FmaddD, "fmadd.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmadd.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmsubW) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::FmsubW, "fmsub.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmsub.w");
+}
+
+TEST_F(AssemblerMIPS32r6Test, FmsubD) {
+ DriverStr(RepeatVVV(&mips::MipsAssembler::FmsubD, "fmsub.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmsub.d");
+}
+
#undef __
} // namespace art
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 9039854..7a1beb6 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -1899,6 +1899,66 @@
EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x14);
}
+void Mips64Assembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12);
+}
+
+void Mips64Assembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b);
+}
+
+void Mips64Assembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) {
+ CHECK(HasMsa());
+ EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b);
+}
+
void Mips64Assembler::ReplicateFPToVectorRegister(VectorRegister dst,
FpuRegister src,
bool is_double) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 5e88033..c39d120 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -796,6 +796,19 @@
void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+ void FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt);
+
// Helper for replicating floating point value in all destination elements.
void ReplicateFPToVectorRegister(VectorRegister dst, FpuRegister src, bool is_double);
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index bdf9598..021e335 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -3340,6 +3340,66 @@
"ilvr.d");
}
+TEST_F(AssemblerMIPS64Test, MaddvB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvB, "maddv.b ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvH, "maddv.h ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvW, "maddv.w ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, MaddvD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvD, "maddv.d ${reg1}, ${reg2}, ${reg3}"),
+ "maddv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvB) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvB, "msubv.b ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.b");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvH) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvH, "msubv.h ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.h");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvW, "msubv.w ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.w");
+}
+
+TEST_F(AssemblerMIPS64Test, MsubvD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvD, "msubv.d ${reg1}, ${reg2}, ${reg3}"),
+ "msubv.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FmaddW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmaddW, "fmadd.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmadd.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FmaddD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmaddD, "fmadd.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmadd.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FmsubW) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmsubW, "fmsub.w ${reg1}, ${reg2}, ${reg3}"),
+ "fmsub.w");
+}
+
+TEST_F(AssemblerMIPS64Test, FmsubD) {
+ DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmsubD, "fmsub.d ${reg1}, ${reg2}, ${reg3}"),
+ "fmsub.d");
+}
+
#undef __
} // namespace art
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
index 621a652..12d113d 100644
--- a/compiler/utils/swap_space.cc
+++ b/compiler/utils/swap_space.cc
@@ -16,9 +16,10 @@
#include "swap_space.h"
+#include <sys/mman.h>
+
#include <algorithm>
#include <numeric>
-#include <sys/mman.h>
#include "base/bit_utils.h"
#include "base/logging.h"
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
index 0ff9fc6..08e243b 100644
--- a/compiler/utils/swap_space.h
+++ b/compiler/utils/swap_space.h
@@ -17,12 +17,12 @@
#ifndef ART_COMPILER_UTILS_SWAP_SPACE_H_
#define ART_COMPILER_UTILS_SWAP_SPACE_H_
+#include <stddef.h>
+#include <stdint.h>
#include <cstdlib>
#include <list>
-#include <vector>
#include <set>
-#include <stdint.h>
-#include <stddef.h>
+#include <vector>
#include "base/logging.h"
#include "base/macros.h"
diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc
index bf50ac3..f4bca59 100644
--- a/compiler/utils/swap_space_test.cc
+++ b/compiler/utils/swap_space_test.cc
@@ -16,10 +16,12 @@
#include "utils/swap_space.h"
-#include <cstdio>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdio>
+
#include "gtest/gtest.h"
#include "base/unix_file/fd_file.h"
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index 6921780..9ba3903 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_
#define ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_
-#include <cstring>
-#include <set>
-#include <map>
-#include <vector>
#include <zlib.h>
+#include <cstring>
+#include <map>
+#include <set>
+#include <vector>
+
#include "base/bit_utils.h"
#include "base/logging.h"
#include "dex_file.h"
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index bef32f8..b50f1af 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -1238,6 +1238,101 @@
EmitXmmRegisterOperand(dst, src);
}
+
+void X86Assembler::psadbw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xF6);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::pmaddwd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xF5);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::phaddw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x01);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::phaddd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x02);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::haddps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF2);
+ EmitUint8(0x0F);
+ EmitUint8(0x7C);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::haddpd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x7C);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::phsubw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x05);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::phsubd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x06);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::hsubps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF2);
+ EmitUint8(0x0F);
+ EmitUint8(0x7D);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::hsubpd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x7D);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
void X86Assembler::pminsb(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index c4bb9ee..8578340 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -497,6 +497,16 @@
void pavgb(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void pavgw(XmmRegister dst, XmmRegister src);
+ void psadbw(XmmRegister dst, XmmRegister src);
+ void pmaddwd(XmmRegister dst, XmmRegister src);
+ void phaddw(XmmRegister dst, XmmRegister src);
+ void phaddd(XmmRegister dst, XmmRegister src);
+ void haddps(XmmRegister dst, XmmRegister src);
+ void haddpd(XmmRegister dst, XmmRegister src);
+ void phsubw(XmmRegister dst, XmmRegister src);
+ void phsubd(XmmRegister dst, XmmRegister src);
+ void hsubps(XmmRegister dst, XmmRegister src);
+ void hsubpd(XmmRegister dst, XmmRegister src);
void pminsb(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void pmaxsb(XmmRegister dst, XmmRegister src);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 34f2a47..3e1244e 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -613,6 +613,46 @@
DriverStr(RepeatFF(&x86::X86Assembler::pavgw, "pavgw %{reg2}, %{reg1}"), "pavgw");
}
+TEST_F(AssemblerX86Test, PSadBW) {
+ DriverStr(RepeatFF(&x86::X86Assembler::psadbw, "psadbw %{reg2}, %{reg1}"), "psadbw");
+}
+
+TEST_F(AssemblerX86Test, PMAddWD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::pmaddwd, "pmaddwd %{reg2}, %{reg1}"), "pmaddwd");
+}
+
+TEST_F(AssemblerX86Test, PHAddW) {
+ DriverStr(RepeatFF(&x86::X86Assembler::phaddw, "phaddw %{reg2}, %{reg1}"), "phaddw");
+}
+
+TEST_F(AssemblerX86Test, PHAddD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::phaddd, "phaddd %{reg2}, %{reg1}"), "phaddd");
+}
+
+TEST_F(AssemblerX86Test, HAddPS) {
+ DriverStr(RepeatFF(&x86::X86Assembler::haddps, "haddps %{reg2}, %{reg1}"), "haddps");
+}
+
+TEST_F(AssemblerX86Test, HAddPD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::haddpd, "haddpd %{reg2}, %{reg1}"), "haddpd");
+}
+
+TEST_F(AssemblerX86Test, PHSubW) {
+ DriverStr(RepeatFF(&x86::X86Assembler::phsubw, "phsubw %{reg2}, %{reg1}"), "phsubw");
+}
+
+TEST_F(AssemblerX86Test, PHSubD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::phsubd, "phsubd %{reg2}, %{reg1}"), "phsubd");
+}
+
+TEST_F(AssemblerX86Test, HSubPS) {
+ DriverStr(RepeatFF(&x86::X86Assembler::hsubps, "hsubps %{reg2}, %{reg1}"), "hsubps");
+}
+
+TEST_F(AssemblerX86Test, HSubPD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::hsubpd, "hsubpd %{reg2}, %{reg1}"), "hsubpd");
+}
+
TEST_F(AssemblerX86Test, PMinSB) {
DriverStr(RepeatFF(&x86::X86Assembler::pminsb, "pminsb %{reg2}, %{reg1}"), "pminsb");
}
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc
index cfdf80b..e074346 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.cc
+++ b/compiler/utils/x86/jni_macro_assembler_x86.cc
@@ -16,10 +16,10 @@
#include "jni_macro_assembler_x86.h"
-#include "utils/assembler.h"
#include "base/casts.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "thread.h"
+#include "utils/assembler.h"
namespace art {
namespace x86 {
diff --git a/compiler/utils/x86/managed_register_x86_test.cc b/compiler/utils/x86/managed_register_x86_test.cc
index 4fbafda..0ed5c36 100644
--- a/compiler/utils/x86/managed_register_x86_test.cc
+++ b/compiler/utils/x86/managed_register_x86_test.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "globals.h"
#include "managed_register_x86.h"
+
+#include "globals.h"
#include "gtest/gtest.h"
namespace art {
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 82d1174..ea69a1c 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -1445,6 +1445,100 @@
EmitXmmRegisterOperand(dst.LowBits(), src);
}
+void X86_64Assembler::psadbw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xF6);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::pmaddwd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xF5);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::phaddw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x01);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::phaddd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x02);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::haddps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF2);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x7C);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::haddpd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x7C);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::phsubw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x05);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::phsubd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x06);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::hsubps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF2);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x7D);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::hsubpd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x7D);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
void X86_64Assembler::pminsb(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 6e584fe..41450bf 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -525,6 +525,16 @@
void pavgb(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void pavgw(XmmRegister dst, XmmRegister src);
+ void psadbw(XmmRegister dst, XmmRegister src);
+ void pmaddwd(XmmRegister dst, XmmRegister src);
+ void phaddw(XmmRegister dst, XmmRegister src);
+ void phaddd(XmmRegister dst, XmmRegister src);
+ void haddps(XmmRegister dst, XmmRegister src);
+ void haddpd(XmmRegister dst, XmmRegister src);
+ void phsubw(XmmRegister dst, XmmRegister src);
+ void phsubd(XmmRegister dst, XmmRegister src);
+ void hsubps(XmmRegister dst, XmmRegister src);
+ void hsubpd(XmmRegister dst, XmmRegister src);
void pminsb(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void pmaxsb(XmmRegister dst, XmmRegister src);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index b574003..9f2c44d 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1301,6 +1301,46 @@
DriverStr(RepeatFF(&x86_64::X86_64Assembler::pavgw, "pavgw %{reg2}, %{reg1}"), "pavgw");
}
+TEST_F(AssemblerX86_64Test, Psadbw) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::psadbw, "psadbw %{reg2}, %{reg1}"), "psadbw");
+}
+
+TEST_F(AssemblerX86_64Test, Pmaddwd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmaddwd, "pmaddwd %{reg2}, %{reg1}"), "pmadwd");
+}
+
+TEST_F(AssemblerX86_64Test, Phaddw) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::phaddw, "phaddw %{reg2}, %{reg1}"), "phaddw");
+}
+
+TEST_F(AssemblerX86_64Test, Phaddd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::phaddd, "phaddd %{reg2}, %{reg1}"), "phaddd");
+}
+
+TEST_F(AssemblerX86_64Test, Haddps) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::haddps, "haddps %{reg2}, %{reg1}"), "haddps");
+}
+
+TEST_F(AssemblerX86_64Test, Haddpd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::haddpd, "haddpd %{reg2}, %{reg1}"), "haddpd");
+}
+
+TEST_F(AssemblerX86_64Test, Phsubw) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::phsubw, "phsubw %{reg2}, %{reg1}"), "phsubw");
+}
+
+TEST_F(AssemblerX86_64Test, Phsubd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::phsubd, "phsubd %{reg2}, %{reg1}"), "phsubd");
+}
+
+TEST_F(AssemblerX86_64Test, Hsubps) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::hsubps, "hsubps %{reg2}, %{reg1}"), "hsubps");
+}
+
+TEST_F(AssemblerX86_64Test, Hsubpd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::hsubpd, "hsubpd %{reg2}, %{reg1}"), "hsubpd");
+}
+
TEST_F(AssemblerX86_64Test, Pminsb) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::pminsb, "pminsb %{reg2}, %{reg1}"), "pminsb");
}
@@ -1972,7 +2012,7 @@
x86_64::X86_64ManagedRegister method_reg = ManagedFromCpu(x86_64::RDI);
size_t frame_size = 10 * kStackAlignment;
- assembler->BuildFrame(10 * kStackAlignment, method_reg, spill_regs, entry_spills);
+ assembler->BuildFrame(frame_size, method_reg, spill_regs, entry_spills);
// Construct assembly text counterpart.
std::ostringstream str;
@@ -2008,7 +2048,7 @@
ArrayRef<const ManagedRegister> spill_regs(raw_spill_regs);
size_t frame_size = 10 * kStackAlignment;
- assembler->RemoveFrame(10 * kStackAlignment, spill_regs);
+ assembler->RemoveFrame(frame_size, spill_regs);
// Construct assembly text counterpart.
std::ostringstream str;
diff --git a/compiler/utils/x86_64/managed_register_x86_64_test.cc b/compiler/utils/x86_64/managed_register_x86_64_test.cc
index 2dc7581..e43d717 100644
--- a/compiler/utils/x86_64/managed_register_x86_64_test.cc
+++ b/compiler/utils/x86_64/managed_register_x86_64_test.cc
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "globals.h"
#include "managed_register_x86_64.h"
+#include "globals.h"
#include "gtest/gtest.h"
namespace art {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 72e2a6c..6538925 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -87,13 +87,13 @@
TimingLogger timings("Verify", false, false);
// The compiler driver handles the verifier deps in the callbacks, so
// remove what this class did for unit testing.
- verifier_deps_.reset(nullptr);
+ if (deps == nullptr) {
+ // Create some verifier deps by default if they are not already specified.
+ deps = new verifier::VerifierDeps(dex_files_);
+ verifier_deps_.reset(deps);
+ }
callbacks_->SetVerifierDeps(deps);
compiler_driver_->Verify(class_loader_, dex_files_, &timings);
- // The compiler driver may have updated the VerifierDeps in the callback object.
- if (callbacks_->GetVerifierDeps() != deps) {
- verifier_deps_.reset(callbacks_->GetVerifierDeps());
- }
callbacks_->SetVerifierDeps(nullptr);
// Clear entries in the verification results to avoid hitting a DCHECK that
// we always succeed inserting a new entry after verifying.
@@ -128,6 +128,7 @@
for (const DexFile* dex_file : dex_files_) {
compiler_driver_->GetVerificationResults()->AddDexFile(dex_file);
}
+ compiler_driver_->SetDexFilesForOatFile(dex_files_);
}
void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -228,8 +229,7 @@
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
for (const DexFile* dex_file : dex_files_) {
- const std::vector<dex::TypeIndex>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
- std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
+ const std::set<dex::TypeIndex>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
@@ -237,7 +237,7 @@
if (cls == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
- } else if (set.find(class_def.class_idx_) == set.end()) {
+ } else if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) {
ASSERT_EQ(cls->GetStatus(), mirror::Class::kStatusVerified);
} else {
ASSERT_LT(cls->GetStatus(), mirror::Class::kStatusVerified);
@@ -1144,6 +1144,39 @@
ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
}
+TEST_F(VerifierDepsTest, UnverifiedOrder) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject loader = LoadDex("VerifierDeps");
+ std::vector<const DexFile*> dex_files = GetDexFiles(loader);
+ ASSERT_GT(dex_files.size(), 0u);
+ const DexFile* dex_file = dex_files[0];
+ VerifierDeps deps1(dex_files);
+ Thread* const self = Thread::Current();
+ ASSERT_TRUE(self->GetVerifierDeps() == nullptr);
+ self->SetVerifierDeps(&deps1);
+ deps1.MaybeRecordVerificationStatus(*dex_file,
+ dex::TypeIndex(0),
+ verifier::FailureKind::kHardFailure);
+ deps1.MaybeRecordVerificationStatus(*dex_file,
+ dex::TypeIndex(1),
+ verifier::FailureKind::kHardFailure);
+ VerifierDeps deps2(dex_files);
+ self->SetVerifierDeps(nullptr);
+ self->SetVerifierDeps(&deps2);
+ deps2.MaybeRecordVerificationStatus(*dex_file,
+ dex::TypeIndex(1),
+ verifier::FailureKind::kHardFailure);
+ deps2.MaybeRecordVerificationStatus(*dex_file,
+ dex::TypeIndex(0),
+ verifier::FailureKind::kHardFailure);
+ self->SetVerifierDeps(nullptr);
+ std::vector<uint8_t> buffer1;
+ deps1.Encode(dex_files, &buffer1);
+ std::vector<uint8_t> buffer2;
+ deps2.Encode(dex_files, &buffer2);
+ EXPECT_EQ(buffer1, buffer2);
+}
+
TEST_F(VerifierDepsTest, VerifyDeps) {
VerifyDexFile();
@@ -1441,7 +1474,6 @@
ASSERT_FALSE(verifier_deps_ == nullptr);
ASSERT_FALSE(verifier_deps_->Equals(decoded_deps));
} else {
- ASSERT_TRUE(verifier_deps_ == nullptr);
VerifyClassStatus(decoded_deps);
}
}
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 346f5a7..0d453ef 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -30,15 +30,6 @@
android: {
// Use the 32-bit version of dex2oat on devices
compile_multilib: "prefer32",
-
- sanitize: {
- // ASan slows down dex2oat by ~3.5x, which translates into
- // extremely slow first boot. Disabled to help speed up
- // SANITIZE_TARGET mode.
- // Bug: 22233158
- address: false,
- coverage: false,
- },
},
},
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ea74f29..e9ec5fa 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -65,8 +65,10 @@
#include "elf_writer_quick.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
+#include "gc/verification.h"
#include "image_writer.h"
#include "interpreter/unstarted_runtime.h"
+#include "java_vm_ext.h"
#include "jit/profile_compilation_info.h"
#include "leb128.h"
#include "linker/buffered_output_stream.h"
@@ -353,6 +355,9 @@
UsageError("");
UsageError(" --debuggable: Produce code debuggable with Java debugger.");
UsageError("");
+ UsageError(" --avoid-storing-invocation: Avoid storing the invocation args in the key value");
+ UsageError(" store. Used to test determinism with different args.");
+ UsageError("");
UsageError(" --runtime-arg <argument>: used to specify various arguments for the runtime,");
UsageError(" such as initial heap size, maximum heap size, and verbose output.");
UsageError(" Use a separate --runtime-arg switch for each argument.");
@@ -406,17 +411,17 @@
UsageError("");
UsageError(" --class-loader-context=<string spec>: a string specifying the intended");
UsageError(" runtime loading context for the compiled dex files.");
- UsageError(" ");
+ UsageError("");
UsageError(" It describes how the class loader chain should be built in order to ensure");
UsageError(" classes are resolved during dex2aot as they would be resolved at runtime.");
UsageError(" This spec will be encoded in the oat file. If at runtime the dex file is");
UsageError(" loaded in a different context, the oat file will be rejected.");
- UsageError(" ");
+ UsageError("");
UsageError(" The chain is interpreted in the natural 'parent order', meaning that class");
UsageError(" loader 'i+1' will be the parent of class loader 'i'.");
UsageError(" The compilation sources will be appended to the classpath of the first class");
UsageError(" loader.");
- UsageError(" ");
+ UsageError("");
UsageError(" E.g. if the context is 'PCL[lib1.dex];DLC[lib2.dex]' and ");
UsageError(" --dex-file=src.dex then dex2oat will setup a PathClassLoader with classpath ");
UsageError(" 'lib1.dex:src.dex' and set its parent to a DelegateLastClassLoader with ");
@@ -426,9 +431,12 @@
UsageError(" with --dex-file are found in the classpath. The source dex files will be");
UsageError(" removed from any class loader's classpath possibly resulting in empty");
UsageError(" class loaders.");
- UsageError(" ");
+ UsageError("");
UsageError(" Example: --class-loader-context=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
UsageError("");
+ UsageError(" --dirty-image-objects=<directory-path>: list of known dirty objects in the image.");
+ UsageError(" The image writer will group them together.");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -591,9 +599,9 @@
compiled_methods_zip_filename_(nullptr),
compiled_methods_filename_(nullptr),
passes_to_run_filename_(nullptr),
+ dirty_image_objects_filename_(nullptr),
multi_image_(false),
is_host_(false),
- class_loader_(nullptr),
elf_writers_(),
oat_writers_(),
rodata_(),
@@ -606,6 +614,7 @@
dump_passes_(false),
dump_timing_(false),
dump_slow_timing_(kIsDebugBuild),
+ avoid_storing_invocation_(false),
swap_fd_(kInvalidFd),
app_image_fd_(kInvalidFd),
profile_file_fd_(kInvalidFd),
@@ -764,6 +773,11 @@
compiler_options_->boot_image_ = !image_filenames_.empty();
compiler_options_->app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
+ if (IsBootImage() && image_filenames_.size() == 1) {
+ const std::string& boot_image_filename = image_filenames_[0];
+ compiler_options_->core_image_ = CompilerDriver::IsCoreImageFilename(boot_image_filename);
+ }
+
if (IsAppImage() && IsBootImage()) {
Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
}
@@ -1128,14 +1142,16 @@
void InsertCompileOptions(int argc, char** argv) {
std::ostringstream oss;
- for (int i = 0; i < argc; ++i) {
- if (i > 0) {
- oss << ' ';
+ if (!avoid_storing_invocation_) {
+ for (int i = 0; i < argc; ++i) {
+ if (i > 0) {
+ oss << ' ';
+ }
+ oss << argv[i];
}
- oss << argv[i];
+ key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+ oss.str(""); // Reset.
}
- key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
- oss.str(""); // Reset.
oss << kRuntimeISA;
key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
key_value_store_->Put(
@@ -1266,6 +1282,8 @@
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=")) {
@@ -1303,9 +1321,11 @@
} 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) {
+ 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());
}
@@ -1484,23 +1504,14 @@
}
}
- void Shutdown() {
- ScopedObjectAccess soa(Thread::Current());
- for (jobject dex_cache : dex_caches_) {
- soa.Env()->DeleteLocalRef(dex_cache);
- }
- dex_caches_.clear();
- }
-
void LoadClassProfileDescriptors() {
if (profile_compilation_info_ != nullptr && IsImage()) {
Runtime* runtime = Runtime::Current();
CHECK(runtime != nullptr);
// Filter out class path classes since we don't want to include these in the image.
- std::set<DexCacheResolvedClasses> resolved_classes(
- profile_compilation_info_->GetResolvedClasses(dex_files_));
- image_classes_.reset(new std::unordered_set<std::string>(
- runtime->GetClassLinker()->GetClassDescriptorsForResolvedClasses(resolved_classes)));
+ image_classes_.reset(
+ new std::unordered_set<std::string>(
+ profile_compilation_info_->GetClassDescriptors(dex_files_)));
VLOG(compiler) << "Loaded " << image_classes_->size()
<< " image class descriptors from profile";
if (VLOG_IS_ON(compiler)) {
@@ -1516,21 +1527,20 @@
dex2oat::ReturnCode Setup() {
TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
- if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
+ if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods() ||
+ !PrepareDirtyObjects()) {
return dex2oat::ReturnCode::kOther;
}
// Verification results are null since we don't know if we will need them yet as the compler
// filter may change.
- // This needs to be done before PrepareRuntimeOptions since the callbacks are passed to the
- // runtime.
callbacks_.reset(new QuickCompilerCallbacks(
IsBootImage() ?
CompilerCallbacks::CallbackMode::kCompileBootImage :
CompilerCallbacks::CallbackMode::kCompileApp));
RuntimeArgumentMap runtime_options;
- if (!PrepareRuntimeOptions(&runtime_options)) {
+ if (!PrepareRuntimeOptions(&runtime_options, callbacks_.get())) {
return dex2oat::ReturnCode::kOther;
}
@@ -1579,20 +1589,12 @@
}
// Open dex files for class path.
+
if (class_loader_context_ == nullptr) {
- // TODO(calin): Temporary workaround while we transition to use
- // --class-loader-context instead of --runtime-arg -cp
- if (runtime_->GetClassPathString().empty()) {
- class_loader_context_ = std::unique_ptr<ClassLoaderContext>(
- new ClassLoaderContext());
- } else {
- std::string spec = runtime_->GetClassPathString() == OatFile::kSpecialSharedLibrary
- ? OatFile::kSpecialSharedLibrary
- : "PCL[" + runtime_->GetClassPathString() + "]";
- class_loader_context_ = ClassLoaderContext::Create(spec);
- }
+ // If no context was specified use the default one (which is an empty PathClassLoader).
+ class_loader_context_ = std::unique_ptr<ClassLoaderContext>(ClassLoaderContext::Default());
}
- CHECK(class_loader_context_ != nullptr);
+
DCHECK_EQ(oat_writers_.size(), 1u);
// Note: Ideally we would reject context where the source dex files are also
@@ -1663,6 +1665,8 @@
// If we need to downgrade the compiler-filter for size reasons.
if (!IsBootImage() && IsVeryLarge(dex_files_)) {
+ // If we need to downgrade the compiler-filter for size reasons, do that early before we read
+ // it below for creating verification callbacks.
if (!CompilerFilter::IsAsGoodAs(kLargeAppFilter, compiler_options_->GetCompilerFilter())) {
LOG(INFO) << "Very large app, downgrading to verify.";
// Note: this change won't be reflected in the key-value store, as that had to be
@@ -1715,13 +1719,11 @@
Thread* self = Thread::Current();
WellKnownClasses::Init(self->GetJniEnv());
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
if (!IsBootImage()) {
constexpr bool kSaveDexInput = false;
if (kSaveDexInput) {
SaveDexInput();
}
- class_loader_ = class_loader_context_->CreateClassLoader(dex_files_);
}
// Ensure opened dex files are writable for dex-to-dex transformations.
@@ -1732,24 +1734,12 @@
}
}
- // Ensure that the dex caches stay live since we don't want class unloading
- // to occur during compilation.
- for (const auto& dex_file : dex_files_) {
- ScopedObjectAccess soa(self);
- dex_caches_.push_back(soa.AddLocalReference<jobject>(
- class_linker->RegisterDexFile(*dex_file,
- soa.Decode<mirror::ClassLoader>(class_loader_).Ptr())));
- if (dex_caches_.back() == nullptr) {
- soa.Self()->AssertPendingException();
- soa.Self()->ClearException();
- PLOG(ERROR) << "Failed to register dex file.";
- return dex2oat::ReturnCode::kOther;
- }
- // Pre-register dex files so that we can access verification results without locks during
- // compilation and verification.
- if (verification_results_ != nullptr) {
- // Verification results are only required for modes that have any compilation. Avoid
- // adding the dex files if possible to prevent allocating large arrays.
+ // Verification results are only required for modes that have any compilation. Avoid
+ // adding the dex files if possible to prevent allocating large arrays.
+ if (verification_results_ != nullptr) {
+ for (const auto& dex_file : dex_files_) {
+ // Pre-register dex files so that we can access verification results without locks during
+ // compilation and verification.
verification_results_->AddDexFile(dex_file);
}
}
@@ -1762,13 +1752,48 @@
return IsImage() && oat_fd_ != kInvalidFd;
}
- // Create and invoke the compiler driver. This will compile all the dex files.
- void Compile() {
+ // Doesn't return the class loader since it's not meant to be used for image compilation.
+ void CompileDexFilesIndividually() {
+ CHECK(!IsImage()) << "Not supported with image";
+ for (const DexFile* dex_file : dex_files_) {
+ std::vector<const DexFile*> dex_files(1u, dex_file);
+ VLOG(compiler) << "Compiling " << dex_file->GetLocation();
+ jobject class_loader = CompileDexFiles(dex_files);
+ CHECK(class_loader != nullptr);
+ ScopedObjectAccess soa(Thread::Current());
+ // Unload class loader to free RAM.
+ jweak weak_class_loader = soa.Env()->vm->AddWeakGlobalRef(
+ soa.Self(),
+ soa.Decode<mirror::ClassLoader>(class_loader));
+ soa.Env()->vm->DeleteGlobalRef(soa.Self(), class_loader);
+ runtime_->GetHeap()->CollectGarbage(/*clear_soft_references*/ true);
+ ObjPtr<mirror::ClassLoader> decoded_weak = soa.Decode<mirror::ClassLoader>(weak_class_loader);
+ if (decoded_weak != nullptr) {
+ LOG(FATAL) << "Failed to unload class loader, path from root set: "
+ << runtime_->GetHeap()->GetVerification()->FirstPathFromRootSet(decoded_weak);
+ }
+ VLOG(compiler) << "Unloaded classloader";
+ }
+ }
+
+ bool ShouldCompileDexFilesIndividually() const {
+ // Compile individually if we are not building an image, not using any compilation, and are
+ // using multidex.
+ // This means extract, verify, and quicken, will use the individual compilation mode (to reduce
+ // RAM used by the compiler).
+ return !IsImage() &&
+ dex_files_.size() > 1 &&
+ !CompilerFilter::IsAotCompilationEnabled(compiler_options_->GetCompilerFilter());
+ }
+
+ // Set up and create the compiler driver and then invoke it to compile all the dex files.
+ jobject Compile() {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+
TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));
// Find the dex files we should not inline from.
-
std::vector<std::string> no_inline_filters;
Split(no_inline_from_string_, ',', &no_inline_filters);
@@ -1779,7 +1804,6 @@
}
if (!no_inline_filters.empty()) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
std::vector<const DexFile*> class_path_files;
if (!IsBootImage()) {
// The class loader context is used only for apps.
@@ -1835,6 +1859,16 @@
profile_compilation_info_.get()));
driver_->SetDexFilesForOatFile(dex_files_);
+ const bool compile_individually = ShouldCompileDexFilesIndividually();
+ if (compile_individually) {
+ // Set the compiler driver in the callbacks so that we can avoid re-verification. This not
+ // only helps performance but also prevents reverifying quickened bytecodes. Attempting
+ // verify quickened bytecode causes verification failures.
+ // Only set the compiler filter if we are doing separate compilation since there is a bit
+ // of overhead when checking if a class was previously verified.
+ callbacks_->SetDoesClassUnloading(true, driver_.get());
+ }
+
// Setup vdex for compilation.
if (!DoEagerUnquickeningOfVdex() && input_vdex_file_ != nullptr) {
callbacks_->SetVerifierDeps(
@@ -1845,8 +1879,46 @@
// experimentation.
TimingLogger::ScopedTiming time_unquicken("Unquicken", timings_);
VdexFile::Unquicken(dex_files_, input_vdex_file_->GetQuickeningInfo());
+ } else {
+ // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
+ // the results for all the dex files, not just the results for the current dex file.
+ callbacks_->SetVerifierDeps(new verifier::VerifierDeps(dex_files_));
}
- driver_->CompileAll(class_loader_, dex_files_, timings_);
+ // Invoke the compilation.
+ if (compile_individually) {
+ CompileDexFilesIndividually();
+ // Return a null classloader since we already freed released it.
+ return nullptr;
+ }
+ return CompileDexFiles(dex_files_);
+ }
+
+ // Create the class loader, use it to compile, and return.
+ jobject CompileDexFiles(const std::vector<const DexFile*>& dex_files) {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+
+ jobject class_loader = nullptr;
+ if (!IsBootImage()) {
+ class_loader = class_loader_context_->CreateClassLoader(dex_files_);
+ }
+
+ // Register dex caches and key them to the class loader so that they only unload when the
+ // class loader unloads.
+ for (const auto& dex_file : dex_files) {
+ ScopedObjectAccess soa(Thread::Current());
+ // Registering the dex cache adds a strong root in the class loader that prevents the dex
+ // cache from being unloaded early.
+ ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile(
+ *dex_file,
+ soa.Decode<mirror::ClassLoader>(class_loader));
+ if (dex_cache == nullptr) {
+ soa.Self()->AssertPendingException();
+ LOG(FATAL) << "Failed to register dex file " << dex_file->GetLocation() << " "
+ << soa.Self()->GetException()->Dump();
+ }
+ }
+ driver_->CompileAll(class_loader, dex_files, timings_);
+ return class_loader;
}
// Notes on the interleaving of creating the images and oat files to
@@ -1950,7 +2022,8 @@
IsAppImage(),
image_storage_mode_,
oat_filenames_,
- dex_file_oat_index_map_));
+ dex_file_oat_index_map_,
+ dirty_image_objects_.get()));
// We need to prepare method offsets in the image address space for direct method patching.
TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
@@ -2376,6 +2449,22 @@
return true;
}
+ bool PrepareDirtyObjects() {
+ if (dirty_image_objects_filename_ != nullptr) {
+ dirty_image_objects_.reset(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
+ dirty_image_objects_filename_,
+ nullptr));
+ if (dirty_image_objects_ == nullptr) {
+ LOG(ERROR) << "Failed to create list of dirty objects from '"
+ << dirty_image_objects_filename_ << "'";
+ return false;
+ }
+ } else {
+ dirty_image_objects_.reset(nullptr);
+ }
+ return true;
+ }
+
void PruneNonExistentDexFiles() {
DCHECK_EQ(dex_filenames_.size(), dex_locations_.size());
size_t kept = 0u;
@@ -2464,7 +2553,8 @@
}
}
- bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options) {
+ bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options,
+ QuickCompilerCallbacks* callbacks) {
RuntimeOptions raw_options;
if (boot_image_filename_.empty()) {
std::string boot_class_path = "-Xbootclasspath:";
@@ -2482,7 +2572,7 @@
raw_options.push_back(std::make_pair(runtime_args_[i], nullptr));
}
- raw_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+ raw_options.push_back(std::make_pair("compilercallbacks", callbacks));
raw_options.push_back(
std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_)));
@@ -2554,7 +2644,6 @@
runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(), type);
}
}
- runtime_->GetClassLinker()->FixupDexCaches(runtime_->GetResolutionMethod());
// Initialize maps for unstarted runtime. This needs to be here, as running clinits needs this
// set up.
@@ -2793,9 +2882,11 @@
const char* compiled_methods_zip_filename_;
const char* compiled_methods_filename_;
const char* passes_to_run_filename_;
+ const char* dirty_image_objects_filename_;
std::unique_ptr<std::unordered_set<std::string>> image_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_methods_;
+ std::unique_ptr<std::unordered_set<std::string>> dirty_image_objects_;
std::unique_ptr<std::vector<std::string>> passes_to_run_;
bool multi_image_;
bool is_host_;
@@ -2803,8 +2894,6 @@
// Dex files we are compiling, does not include the class path dex files.
std::vector<const DexFile*> dex_files_;
std::string no_inline_from_string_;
- std::vector<jobject> dex_caches_;
- jobject class_loader_;
std::vector<std::unique_ptr<ElfWriter>> elf_writers_;
std::vector<std::unique_ptr<OatWriter>> oat_writers_;
@@ -2823,6 +2912,7 @@
bool dump_passes_;
bool dump_timing_;
bool dump_slow_timing_;
+ bool avoid_storing_invocation_;
std::string swap_file_name_;
int swap_fd_;
size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap;
@@ -2873,9 +2963,25 @@
#endif
}
+class ScopedGlobalRef {
+ public:
+ explicit ScopedGlobalRef(jobject obj) : obj_(obj) {}
+ ~ScopedGlobalRef() {
+ if (obj_ != nullptr) {
+ ScopedObjectAccess soa(Thread::Current());
+ soa.Env()->vm->DeleteGlobalRef(soa.Self(), obj_);
+ }
+ }
+
+ private:
+ jobject obj_;
+};
+
static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) {
dex2oat.LoadClassProfileDescriptors();
- dex2oat.Compile();
+ // Keep the class loader that was used for compilation live for the rest of the compilation
+ // process.
+ ScopedGlobalRef class_loader(dex2oat.Compile());
if (!dex2oat.WriteOutputFiles()) {
dex2oat.EraseOutputFiles();
@@ -2923,7 +3029,9 @@
}
static dex2oat::ReturnCode CompileApp(Dex2Oat& dex2oat) {
- dex2oat.Compile();
+ // Keep the class loader that was used for compilation live for the rest of the compilation
+ // process.
+ ScopedGlobalRef class_loader(dex2oat.Compile());
if (!dex2oat.WriteOutputFiles()) {
dex2oat.EraseOutputFiles();
@@ -3017,7 +3125,6 @@
result = CompileApp(*dex2oat);
}
- dex2oat->Shutdown();
return result;
}
} // namespace art
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 95fb16d..46c5f58 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -340,6 +340,15 @@
// EXPECT_GE(profile_sizes.oat_size / kRatio, compiled_methods_sizes.oat_size);
EXPECT_GE(profile_sizes.vdex_size / kRatio, compiled_methods_sizes.vdex_size);
}
+ // Test dirty image objects.
+ {
+ ScratchFile classes;
+ GenerateClasses(classes.GetFile(), /*frequency*/ 1u);
+ image_classes_sizes = CompileImageAndGetSizes(
+ {"--dirty-image-objects=" + classes.GetFilename()});
+ classes.Close();
+ std::cout << "Dirty image object sizes " << image_classes_sizes << std::endl;
+ }
}
} // namespace art
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 68ec0b5..6a9d979 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -30,9 +30,9 @@
#include "base/macros.h"
#include "base/mutex-inl.h"
#include "bytecode_utils.h"
-#include "dex_file-inl.h"
#include "dex2oat_environment_test.h"
#include "dex2oat_return_codes.h"
+#include "dex_file-inl.h"
#include "jit/profile_compilation_info.h"
#include "oat.h"
#include "oat_file.h"
@@ -41,6 +41,7 @@
namespace art {
static constexpr size_t kMaxMethodIds = 65535;
+static constexpr bool kDebugArgs = false;
using android::base::StringPrintf;
@@ -55,7 +56,7 @@
}
protected:
- int GenerateOdexForTestWithStatus(const std::string& dex_location,
+ int GenerateOdexForTestWithStatus(const std::vector<std::string>& dex_locations,
const std::string& odex_location,
CompilerFilter::Filter filter,
std::string* error_msg,
@@ -63,7 +64,10 @@
bool use_fd = false) {
std::unique_ptr<File> oat_file;
std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location);
+ // Add dex file args.
+ for (const std::string& dex_location : dex_locations) {
+ args.push_back("--dex-file=" + dex_location);
+ }
if (use_fd) {
oat_file.reset(OS::CreateEmptyFile(odex_location.c_str()));
CHECK(oat_file != nullptr) << odex_location;
@@ -93,7 +97,7 @@
bool use_fd = false,
std::function<void(const OatFile&)> check_oat = [](const OatFile&) {}) {
std::string error_msg;
- int status = GenerateOdexForTestWithStatus(dex_location,
+ int status = GenerateOdexForTestWithStatus({dex_location},
odex_location,
filter,
&error_msg,
@@ -134,7 +138,7 @@
}
}
- // Check the input compiler filter against the generated oat file's filter. Mayb be overridden
+ // Check the input compiler filter against the generated oat file's filter. May be overridden
// in subclasses when equality is not expected.
virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) {
EXPECT_EQ(expected, actual);
@@ -153,14 +157,7 @@
std::vector<std::string> argv;
argv.push_back(runtime->GetCompilerExecutable());
- argv.push_back("--runtime-arg");
- argv.push_back("-classpath");
- argv.push_back("--runtime-arg");
- std::string class_path = runtime->GetClassPathString();
- if (class_path == "") {
- class_path = OatFile::kSpecialSharedLibrary;
- }
- argv.push_back(class_path);
+
if (runtime->IsJavaDebuggable()) {
argv.push_back("--debuggable");
}
@@ -194,6 +191,14 @@
CHECK(android_root != nullptr);
argv.push_back("--android-root=" + std::string(android_root));
+ if (kDebugArgs) {
+ std::string all_args;
+ for (const std::string& arg : argv) {
+ all_args += arg + " ";
+ }
+ LOG(ERROR) << all_args;
+ }
+
int link[2];
if (pipe(link) == -1) {
@@ -958,7 +963,7 @@
Copy(GetTestDexFileName(), dex_location);
std::string error_msg;
- return GenerateOdexForTestWithStatus(dex_location,
+ return GenerateOdexForTestWithStatus({dex_location},
odex_location,
CompilerFilter::kSpeed,
&error_msg,
@@ -1114,4 +1119,72 @@
RunTest(context.c_str(), expected_classpath_key.c_str(), true);
}
+class Dex2oatDeterminism : public Dex2oatTest {};
+
+TEST_F(Dex2oatDeterminism, UnloadCompile) {
+ if (!kUseReadBarrier &&
+ gc::kCollectorTypeDefault != gc::kCollectorTypeCMS &&
+ gc::kCollectorTypeDefault != gc::kCollectorTypeMS) {
+ LOG(INFO) << "Test requires determinism support.";
+ return;
+ }
+ Runtime* const runtime = Runtime::Current();
+ std::string out_dir = GetScratchDir();
+ const std::string base_oat_name = out_dir + "/base.oat";
+ const std::string base_vdex_name = out_dir + "/base.vdex";
+ const std::string unload_oat_name = out_dir + "/unload.oat";
+ const std::string unload_vdex_name = out_dir + "/unload.vdex";
+ const std::string no_unload_oat_name = out_dir + "/nounload.oat";
+ const std::string no_unload_vdex_name = out_dir + "/nounload.vdex";
+ const std::string app_image_name = out_dir + "/unload.art";
+ std::string error_msg;
+ const std::vector<gc::space::ImageSpace*>& spaces = runtime->GetHeap()->GetBootImageSpaces();
+ ASSERT_GT(spaces.size(), 0u);
+ const std::string image_location = spaces[0]->GetImageLocation();
+ // Without passing in an app image, it will unload in between compilations.
+ const int res = GenerateOdexForTestWithStatus(
+ GetLibCoreDexFileNames(),
+ base_oat_name,
+ CompilerFilter::Filter::kQuicken,
+ &error_msg,
+ {"--force-determinism", "--avoid-storing-invocation"});
+ EXPECT_EQ(res, 0);
+ Copy(base_oat_name, unload_oat_name);
+ Copy(base_vdex_name, unload_vdex_name);
+ std::unique_ptr<File> unload_oat(OS::OpenFileForReading(unload_oat_name.c_str()));
+ std::unique_ptr<File> unload_vdex(OS::OpenFileForReading(unload_vdex_name.c_str()));
+ ASSERT_TRUE(unload_oat != nullptr);
+ ASSERT_TRUE(unload_vdex != nullptr);
+ EXPECT_GT(unload_oat->GetLength(), 0u);
+ EXPECT_GT(unload_vdex->GetLength(), 0u);
+ // Regenerate with an app image to disable the dex2oat unloading and verify that the output is
+ // the same.
+ const int res2 = GenerateOdexForTestWithStatus(
+ GetLibCoreDexFileNames(),
+ base_oat_name,
+ CompilerFilter::Filter::kQuicken,
+ &error_msg,
+ {"--force-determinism", "--avoid-storing-invocation", "--app-image-file=" + app_image_name});
+ EXPECT_EQ(res2, 0);
+ Copy(base_oat_name, no_unload_oat_name);
+ Copy(base_vdex_name, no_unload_vdex_name);
+ std::unique_ptr<File> no_unload_oat(OS::OpenFileForReading(no_unload_oat_name.c_str()));
+ std::unique_ptr<File> no_unload_vdex(OS::OpenFileForReading(no_unload_vdex_name.c_str()));
+ ASSERT_TRUE(no_unload_oat != nullptr);
+ ASSERT_TRUE(no_unload_vdex != nullptr);
+ EXPECT_GT(no_unload_oat->GetLength(), 0u);
+ EXPECT_GT(no_unload_vdex->GetLength(), 0u);
+ // Verify that both of the files are the same (odex and vdex).
+ EXPECT_EQ(unload_oat->GetLength(), no_unload_oat->GetLength());
+ EXPECT_EQ(unload_vdex->GetLength(), no_unload_vdex->GetLength());
+ EXPECT_EQ(unload_oat->Compare(no_unload_oat.get()), 0)
+ << unload_oat_name << " " << no_unload_oat_name;
+ EXPECT_EQ(unload_vdex->Compare(no_unload_vdex.get()), 0)
+ << unload_vdex_name << " " << no_unload_vdex_name;
+ // App image file.
+ std::unique_ptr<File> app_image_file(OS::OpenFileForReading(app_image_name.c_str()));
+ ASSERT_TRUE(app_image_file != nullptr);
+ EXPECT_GT(app_image_file->GetLength(), 0u);
+}
+
} // namespace art
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 8437ea5..0db790b 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -44,10 +44,10 @@
#include "android-base/stringprintf.h"
-#include "dexdump_cfg.h"
#include "dex_file-inl.h"
#include "dex_file_types.h"
#include "dex_instruction-inl.h"
+#include "dexdump_cfg.h"
namespace art {
diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc
index 9c0429f..2831707 100644
--- a/dexdump/dexdump_cfg.cc
+++ b/dexdump/dexdump_cfg.cc
@@ -19,8 +19,9 @@
#include "dexdump_cfg.h"
#include <inttypes.h>
-#include <ostream>
+
#include <map>
+#include <ostream>
#include <set>
#include "dex_file-inl.h"
diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc
index 606d4f8..43c3d12 100644
--- a/dexdump/dexdump_main.cc
+++ b/dexdump/dexdump_main.cc
@@ -29,8 +29,8 @@
#include <unistd.h>
#include "base/logging.h"
-#include "runtime.h"
#include "mem_map.h"
+#include "runtime.h"
namespace art {
diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc
index 640f387..b8b65d6 100644
--- a/dexdump/dexdump_test.cc
+++ b/dexdump/dexdump_test.cc
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include <sstream>
#include <string>
#include <vector>
-#include <sstream>
#include <sys/types.h>
#include <unistd.h>
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index fe74572..9c887b9 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -19,9 +19,10 @@
#ifndef ART_DEXLAYOUT_DEX_IR_H_
#define ART_DEXLAYOUT_DEX_IR_H_
+#include <stdint.h>
+
#include <map>
#include <vector>
-#include <stdint.h>
#include "base/stl_util.h"
#include "dex_file-inl.h"
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index c0478bd..fd92d77 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -33,10 +33,10 @@
#include "android-base/stringprintf.h"
-#include "dex_ir_builder.h"
#include "dex_file-inl.h"
#include "dex_file_verifier.h"
#include "dex_instruction-inl.h"
+#include "dex_ir_builder.h"
#include "dex_verify.h"
#include "dex_visualize.h"
#include "dex_writer.h"
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 33d62de..17097f1 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -22,17 +22,17 @@
#include "dexlayout.h"
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
#include <sys/stat.h>
-#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "base/logging.h"
#include "jit/profile_compilation_info.h"
-#include "runtime.h"
#include "mem_map.h"
+#include "runtime.h"
namespace art {
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 43c531d..f3b4c86 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include <sstream>
#include <string>
#include <vector>
-#include <sstream>
#include <sys/types.h>
#include <unistd.h>
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index fa232a7..6a1e22a 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -23,8 +23,8 @@
* List all methods in all concrete classes in one or more DEX files.
*/
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include "dex_file-inl.h"
#include "mem_map.h"
diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc
index 173a456..f645f87 100644
--- a/dexlist/dexlist_test.cc
+++ b/dexlist/dexlist_test.cc
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include <sstream>
#include <string>
#include <vector>
-#include <sstream>
#include <sys/types.h>
#include <unistd.h>
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index e2c159a..fc72bbd 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -97,6 +97,9 @@
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(" --downgrade: optional, if the purpose of dexopt is to downgrade the dex file");
+ UsageError(" By default, dexopt considers upgrade case.");
+ UsageError("");
UsageError("Return code:");
UsageError(" To make it easier to integrate with the internal tools this command will make");
UsageError(" available its result (dexoptNeeded) as the exit/return code. i.e. it will not");
@@ -121,7 +124,9 @@
class DexoptAnalyzer FINAL {
public:
- DexoptAnalyzer() : assume_profile_changed_(false) {}
+ DexoptAnalyzer() :
+ assume_profile_changed_(false),
+ downgrade_(false) {}
void ParseArgs(int argc, char **argv) {
original_argc = argc;
@@ -160,9 +165,9 @@
// compute dalvik-cache folder). This is mostly used in tests.
std::string new_android_data = option.substr(strlen("--android-data=")).ToString();
setenv("ANDROID_DATA", new_android_data.c_str(), 1);
- } else {
- Usage("Unknown argument '%s'", option.data());
- }
+ } else if (option.starts_with("--downgrade")) {
+ downgrade_ = true;
+ } else { Usage("Unknown argument '%s'", option.data()); }
}
if (image_.empty()) {
@@ -225,7 +230,7 @@
return kNoDexOptNeeded;
}
int dexoptNeeded = oat_file_assistant.GetDexOptNeeded(
- compiler_filter_, assume_profile_changed_);
+ compiler_filter_, assume_profile_changed_, downgrade_);
// Convert OatFileAssitant codes to dexoptanalyzer codes.
switch (dexoptNeeded) {
@@ -249,6 +254,7 @@
InstructionSet isa_;
CompilerFilter::Filter compiler_filter_;
bool assume_profile_changed_;
+ bool downgrade_;
std::string image_;
};
diff --git a/dexoptanalyzer/dexoptanalyzer_test.cc b/dexoptanalyzer/dexoptanalyzer_test.cc
index 1703ff4..1cbf546 100644
--- a/dexoptanalyzer/dexoptanalyzer_test.cc
+++ b/dexoptanalyzer/dexoptanalyzer_test.cc
@@ -71,12 +71,13 @@
// as the output of OatFileAssistant::GetDexOptNeeded.
void Verify(const std::string& dex_file,
CompilerFilter::Filter compiler_filter,
- bool assume_profile_changed = false) {
+ bool assume_profile_changed = false,
+ bool downgrade = false) {
int dexoptanalyzerResult = Analyze(dex_file, compiler_filter, assume_profile_changed);
dexoptanalyzerResult = DexoptanalyzerToOatFileAssistant(dexoptanalyzerResult);
OatFileAssistant oat_file_assistant(dex_file.c_str(), kRuntimeISA, /*load_executable*/ false);
int assistantResult = oat_file_assistant.GetDexOptNeeded(
- compiler_filter, assume_profile_changed);
+ compiler_filter, assume_profile_changed, downgrade);
EXPECT_EQ(assistantResult, dexoptanalyzerResult);
}
};
@@ -118,6 +119,16 @@
Verify(dex_location, CompilerFilter::kQuicken, true);
}
+TEST_F(DexoptAnalyzerTest, Downgrade) {
+ std::string dex_location = GetScratchDir() + "/Downgrade.jar";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kQuicken);
+
+ Verify(dex_location, CompilerFilter::kSpeedProfile, false, true);
+ Verify(dex_location, CompilerFilter::kQuicken, false, true);
+ Verify(dex_location, CompilerFilter::kVerify, false, true);
+}
+
// Case: We have a MultiDEX file and up-to-date OAT file for it.
TEST_F(DexoptAnalyzerTest, MultiDexOatUpToDate) {
std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 3347dac..66419e3 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -26,8 +26,8 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
-#include "aarch32/instructions-aarch32.h"
#include "aarch32/disasm-aarch32.h"
+#include "aarch32/instructions-aarch32.h"
#pragma GCC diagnostic pop
namespace art {
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 7cb216e..1a395a4 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -477,6 +477,10 @@
{ kMsaSpecialMask | (0xf << 2), kMsa | (0x8 << 2), "ld", "kw" },
{ kMsaSpecialMask | (0xf << 2), kMsa | (0x9 << 2), "st", "kw" },
{ kMsaMask | (0x7 << 23), kMsa | (0x5 << 23) | 0x14, "ilvr", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x1 << 23) | 0x12, "maddv", "Vkmn" },
+ { kMsaMask | (0x7 << 23), kMsa | (0x2 << 23) | 0x12, "msubv", "Vkmn" },
+ { kMsaMask | (0xf << 22), kMsa | (0x4 << 22) | 0x1b, "fmadd", "Ukmn" },
+ { kMsaMask | (0xf << 22), kMsa | (0x5 << 22) | 0x1b, "fmsub", "Ukmn" },
};
static uint32_t ReadU32(const uint8_t* ptr) {
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 2763c07..9ffc414 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -20,48 +20,897 @@
#include <fstream>
#include <functional>
#include <iostream>
-#include <string>
-#include <vector>
-#include <set>
#include <map>
+#include <set>
+#include <string>
#include <unordered_set>
+#include <vector>
#include "android-base/stringprintf.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/unix_file/fd_file.h"
-#include "gc/space/image_space.h"
#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "image.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
-#include "image.h"
-#include "scoped_thread_state_change-inl.h"
#include "os.h"
+#include "scoped_thread_state_change-inl.h"
-#include "cmdline.h"
#include "backtrace/BacktraceMap.h"
+#include "cmdline.h"
+#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <signal.h>
namespace art {
using android::base::StringPrintf;
+namespace {
+
+constexpr size_t kMaxAddressPrint = 5;
+
+enum class ProcessType {
+ kZygote,
+ kRemote
+};
+
+enum class RemoteProcesses {
+ kImageOnly,
+ kZygoteOnly,
+ kImageAndZygote
+};
+
+struct MappingData {
+ // The count of pages that are considered dirty by the OS.
+ size_t dirty_pages = 0;
+ // The count of pages that differ by at least one byte.
+ size_t different_pages = 0;
+ // The count of differing bytes.
+ size_t different_bytes = 0;
+ // The count of differing four-byte units.
+ size_t different_int32s = 0;
+ // The count of pages that have mapping count == 1.
+ size_t private_pages = 0;
+ // The count of private pages that are also dirty.
+ size_t private_dirty_pages = 0;
+ // The count of pages that are marked dirty but do not differ.
+ size_t false_dirty_pages = 0;
+ // Set of the local virtual page indices that are dirty.
+ std::set<size_t> dirty_page_set;
+};
+
+static std::string GetClassDescriptor(mirror::Class* klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK(klass != nullptr);
+
+ std::string descriptor;
+ const char* descriptor_str = klass->GetDescriptor(&descriptor /*out*/);
+
+ return std::string(descriptor_str);
+}
+
+static std::string PrettyFieldValue(ArtField* field, mirror::Object* object)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::ostringstream oss;
+ switch (field->GetTypeAsPrimitiveType()) {
+ case Primitive::kPrimNot: {
+ oss << object->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
+ field->GetOffset());
+ break;
+ }
+ case Primitive::kPrimBoolean: {
+ oss << static_cast<bool>(object->GetFieldBoolean<kVerifyNone>(field->GetOffset()));
+ break;
+ }
+ case Primitive::kPrimByte: {
+ oss << static_cast<int32_t>(object->GetFieldByte<kVerifyNone>(field->GetOffset()));
+ break;
+ }
+ case Primitive::kPrimChar: {
+ oss << object->GetFieldChar<kVerifyNone>(field->GetOffset());
+ break;
+ }
+ case Primitive::kPrimShort: {
+ oss << object->GetFieldShort<kVerifyNone>(field->GetOffset());
+ break;
+ }
+ case Primitive::kPrimInt: {
+ oss << object->GetField32<kVerifyNone>(field->GetOffset());
+ break;
+ }
+ case Primitive::kPrimLong: {
+ oss << object->GetField64<kVerifyNone>(field->GetOffset());
+ break;
+ }
+ case Primitive::kPrimFloat: {
+ oss << object->GetField32<kVerifyNone>(field->GetOffset());
+ break;
+ }
+ case Primitive::kPrimDouble: {
+ oss << object->GetField64<kVerifyNone>(field->GetOffset());
+ break;
+ }
+ case Primitive::kPrimVoid: {
+ oss << "void";
+ break;
+ }
+ }
+ return oss.str();
+}
+
+template <typename K, typename V, typename D>
+static std::vector<std::pair<V, K>> SortByValueDesc(
+ const std::map<K, D> map,
+ std::function<V(const D&)> value_mapper = [](const D& d) { return static_cast<V>(d); }) {
+ // Store value->key so that we can use the default sort from pair which
+ // sorts by value first and then key
+ std::vector<std::pair<V, K>> value_key_vector;
+
+ for (const auto& kv_pair : map) {
+ value_key_vector.push_back(std::make_pair(value_mapper(kv_pair.second), kv_pair.first));
+ }
+
+ // Sort in reverse (descending order)
+ std::sort(value_key_vector.rbegin(), value_key_vector.rend());
+ return value_key_vector;
+}
+
+// Fixup a remote pointer that we read from a foreign boot.art to point to our own memory.
+// Returned pointer will point to inside of remote_contents.
+template <typename T>
+static T* FixUpRemotePointer(T* remote_ptr,
+ std::vector<uint8_t>& remote_contents,
+ const backtrace_map_t& boot_map) {
+ if (remote_ptr == nullptr) {
+ return nullptr;
+ }
+
+ uintptr_t remote = reinterpret_cast<uintptr_t>(remote_ptr);
+
+ CHECK_LE(boot_map.start, remote);
+ CHECK_GT(boot_map.end, remote);
+
+ off_t boot_offset = remote - boot_map.start;
+
+ return reinterpret_cast<T*>(&remote_contents[boot_offset]);
+}
+
+template <typename T>
+static T* RemoteContentsPointerToLocal(T* remote_ptr,
+ std::vector<uint8_t>& remote_contents,
+ const ImageHeader& image_header) {
+ if (remote_ptr == nullptr) {
+ return nullptr;
+ }
+
+ uint8_t* remote = reinterpret_cast<uint8_t*>(remote_ptr);
+ ptrdiff_t boot_offset = remote - &remote_contents[0];
+
+ const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + boot_offset;
+
+ return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr));
+}
+
+template <typename T> size_t EntrySize(T* entry);
+template<> size_t EntrySize(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return object->SizeOf();
+}
+template<> size_t EntrySize(ArtMethod* art_method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return sizeof(*art_method);
+}
+
+template <typename T>
+static bool EntriesDiffer(T* entry1, T* entry2) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return memcmp(entry1, entry2, EntrySize(entry1)) != 0;
+}
+
+template <typename T>
+struct RegionCommon {
+ public:
+ RegionCommon(std::ostream* os,
+ std::vector<uint8_t>* remote_contents,
+ std::vector<uint8_t>* zygote_contents,
+ const backtrace_map_t& boot_map,
+ const ImageHeader& image_header) :
+ os_(*os),
+ remote_contents_(remote_contents),
+ zygote_contents_(zygote_contents),
+ boot_map_(boot_map),
+ image_header_(image_header),
+ different_entries_(0),
+ dirty_entry_bytes_(0),
+ false_dirty_entry_bytes_(0) {
+ CHECK(remote_contents != nullptr);
+ CHECK(zygote_contents != nullptr);
+ }
+
+ void DumpSamplesAndOffsetCount() {
+ os_ << " sample object addresses: ";
+ for (size_t i = 0; i < dirty_entries_.size() && i < kMaxAddressPrint; ++i) {
+ T* entry = dirty_entries_[i];
+ os_ << reinterpret_cast<void*>(entry) << ", ";
+ }
+ os_ << "\n";
+ os_ << " dirty byte +offset:count list = ";
+ std::vector<std::pair<size_t, off_t>> field_dirty_count_sorted =
+ SortByValueDesc<off_t, size_t, size_t>(field_dirty_count_);
+ for (const std::pair<size_t, off_t>& pair : field_dirty_count_sorted) {
+ off_t offset = pair.second;
+ size_t count = pair.first;
+ os_ << "+" << offset << ":" << count << ", ";
+ }
+ os_ << "\n";
+ }
+
+ size_t GetDifferentEntryCount() const { return different_entries_; }
+ size_t GetDirtyEntryBytes() const { return dirty_entry_bytes_; }
+ size_t GetFalseDirtyEntryCount() const { return false_dirty_entries_.size(); }
+ size_t GetFalseDirtyEntryBytes() const { return false_dirty_entry_bytes_; }
+ size_t GetZygoteDirtyEntryCount() const { return zygote_dirty_entries_.size(); }
+
+ protected:
+ bool IsEntryOnDirtyPage(T* entry, const std::set<size_t>& dirty_pages) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ size_t size = EntrySize(entry);
+ size_t page_off = 0;
+ size_t current_page_idx;
+ uintptr_t entry_address = reinterpret_cast<uintptr_t>(entry);
+ // Iterate every page this entry belongs to
+ do {
+ current_page_idx = entry_address / kPageSize + page_off;
+ if (dirty_pages.find(current_page_idx) != dirty_pages.end()) {
+ // This entry is on a dirty page
+ return true;
+ }
+ page_off++;
+ } while ((current_page_idx * kPageSize) < RoundUp(entry_address + size, kObjectAlignment));
+ return false;
+ }
+
+ void AddZygoteDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) {
+ zygote_dirty_entries_.insert(entry);
+ }
+
+ void AddImageDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) {
+ image_dirty_entries_.insert(entry);
+ }
+
+ void AddFalseDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) {
+ false_dirty_entries_.push_back(entry);
+ false_dirty_entry_bytes_ += EntrySize(entry);
+ }
+
+ // The output stream to write to.
+ std::ostream& os_;
+ // The byte contents of the remote (image) process' image.
+ std::vector<uint8_t>* remote_contents_;
+ // The byte contents of the zygote process' image.
+ std::vector<uint8_t>* zygote_contents_;
+ const backtrace_map_t& boot_map_;
+ const ImageHeader& image_header_;
+
+ // Count of entries that are different.
+ size_t different_entries_;
+
+ // Local entries that are dirty (differ in at least one byte).
+ size_t dirty_entry_bytes_;
+ std::vector<T*> dirty_entries_;
+
+ // Local entries that are clean, but located on dirty pages.
+ size_t false_dirty_entry_bytes_;
+ std::vector<T*> false_dirty_entries_;
+
+ // Image dirty entries
+ // If zygote_pid_only_ == true, these are shared dirty entries in the zygote.
+ // If zygote_pid_only_ == false, these are private dirty entries in the application.
+ std::set<T*> image_dirty_entries_;
+
+ // Zygote dirty entries (probably private dirty).
+ // We only add entries here if they differed in both the image and the zygote, so
+ // they are probably private dirty.
+ std::set<T*> zygote_dirty_entries_;
+
+ std::map<off_t /* field offset */, size_t /* count */> field_dirty_count_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RegionCommon);
+};
+
+template <typename T>
+class RegionSpecializedBase : public RegionCommon<T> {
+};
+
+// Region analysis for mirror::Objects
+template<>
+class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object> {
+ public:
+ RegionSpecializedBase(std::ostream* os,
+ std::vector<uint8_t>* remote_contents,
+ std::vector<uint8_t>* zygote_contents,
+ const backtrace_map_t& boot_map,
+ const ImageHeader& image_header,
+ bool dump_dirty_objects)
+ : RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header),
+ os_(*os),
+ dump_dirty_objects_(dump_dirty_objects) { }
+
+ void CheckEntrySanity(const uint8_t* current) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK_ALIGNED(current, kObjectAlignment);
+ mirror::Object* entry = reinterpret_cast<mirror::Object*>(const_cast<uint8_t*>(current));
+ // Sanity check that we are reading a real mirror::Object
+ CHECK(entry->GetClass() != nullptr) << "Image object at address "
+ << entry
+ << " has null class";
+ if (kUseBakerReadBarrier) {
+ entry->AssertReadBarrierState();
+ }
+ }
+
+ mirror::Object* GetNextEntry(mirror::Object* entry)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t* next =
+ reinterpret_cast<uint8_t*>(entry) + RoundUp(EntrySize(entry), kObjectAlignment);
+ return reinterpret_cast<mirror::Object*>(next);
+ }
+
+ void VisitEntry(mirror::Object* entry)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Unconditionally store the class descriptor in case we need it later
+ mirror::Class* klass = entry->GetClass();
+ class_data_[klass].descriptor = GetClassDescriptor(klass);
+ }
+
+ void AddCleanEntry(mirror::Object* entry)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ class_data_[entry->GetClass()].AddCleanObject();
+ }
+
+ void AddFalseDirtyEntry(mirror::Object* entry)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ RegionCommon<mirror::Object>::AddFalseDirtyEntry(entry);
+ class_data_[entry->GetClass()].AddFalseDirtyObject(entry);
+ }
+
+ void AddDirtyEntry(mirror::Object* entry, mirror::Object* entry_remote)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ size_t entry_size = EntrySize(entry);
+ ++different_entries_;
+ dirty_entry_bytes_ += entry_size;
+ // Log dirty count and objects for class objects only.
+ mirror::Class* klass = entry->GetClass();
+ if (klass->IsClassClass()) {
+ // Increment counts for the fields that are dirty
+ const uint8_t* current = reinterpret_cast<const uint8_t*>(entry);
+ const uint8_t* current_remote = reinterpret_cast<const uint8_t*>(entry_remote);
+ for (size_t i = 0; i < entry_size; ++i) {
+ if (current[i] != current_remote[i]) {
+ field_dirty_count_[i]++;
+ }
+ }
+ dirty_entries_.push_back(entry);
+ }
+ class_data_[klass].AddDirtyObject(entry, entry_remote);
+ }
+
+ void DiffEntryContents(mirror::Object* entry,
+ uint8_t* remote_bytes,
+ const uint8_t* base_ptr,
+ bool log_dirty_objects)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const char* tabs = " ";
+ // Attempt to find fields for all dirty bytes.
+ mirror::Class* klass = entry->GetClass();
+ if (entry->IsClass()) {
+ os_ << tabs
+ << "Class " << mirror::Class::PrettyClass(entry->AsClass()) << " " << entry << "\n";
+ } else {
+ os_ << tabs
+ << "Instance of " << mirror::Class::PrettyClass(klass) << " " << entry << "\n";
+ }
+
+ std::unordered_set<ArtField*> dirty_instance_fields;
+ std::unordered_set<ArtField*> dirty_static_fields;
+ // Examine the bytes comprising the Object, computing which fields are dirty
+ // and recording them for later display. If the Object is an array object,
+ // compute the dirty entries.
+ mirror::Object* remote_entry = reinterpret_cast<mirror::Object*>(remote_bytes);
+ for (size_t i = 0, count = entry->SizeOf(); i < count; ++i) {
+ if (base_ptr[i] != remote_bytes[i]) {
+ ArtField* field = ArtField::FindInstanceFieldWithOffset</*exact*/false>(klass, i);
+ if (field != nullptr) {
+ dirty_instance_fields.insert(field);
+ } else if (entry->IsClass()) {
+ field = ArtField::FindStaticFieldWithOffset</*exact*/false>(entry->AsClass(), i);
+ if (field != nullptr) {
+ dirty_static_fields.insert(field);
+ }
+ }
+ if (field == nullptr) {
+ if (klass->IsArrayClass()) {
+ mirror::Class* component_type = klass->GetComponentType();
+ Primitive::Type primitive_type = component_type->GetPrimitiveType();
+ size_t component_size = Primitive::ComponentSize(primitive_type);
+ size_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value();
+ if (i >= data_offset) {
+ os_ << tabs << "Dirty array element " << (i - data_offset) / component_size << "\n";
+ // Skip to next element to prevent spam.
+ i += component_size - 1;
+ continue;
+ }
+ }
+ os_ << tabs << "No field for byte offset " << i << "\n";
+ }
+ }
+ }
+ // Dump different fields.
+ if (!dirty_instance_fields.empty()) {
+ os_ << tabs << "Dirty instance fields " << dirty_instance_fields.size() << "\n";
+ for (ArtField* field : dirty_instance_fields) {
+ os_ << tabs << ArtField::PrettyField(field)
+ << " original=" << PrettyFieldValue(field, entry)
+ << " remote=" << PrettyFieldValue(field, remote_entry) << "\n";
+ }
+ }
+ if (!dirty_static_fields.empty()) {
+ if (dump_dirty_objects_ && log_dirty_objects) {
+ dirty_objects_.insert(entry);
+ }
+ os_ << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n";
+ for (ArtField* field : dirty_static_fields) {
+ os_ << tabs << ArtField::PrettyField(field)
+ << " original=" << PrettyFieldValue(field, entry)
+ << " remote=" << PrettyFieldValue(field, remote_entry) << "\n";
+ }
+ }
+ os_ << "\n";
+ }
+
+ void DumpDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (mirror::Object* obj : dirty_objects_) {
+ if (obj->IsClass()) {
+ os_ << "Private dirty object: " << obj->AsClass()->PrettyDescriptor() << "\n";
+ }
+ }
+ }
+
+ void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // vector of pairs (size_t count, Class*)
+ auto dirty_object_class_values =
+ SortByValueDesc<mirror::Class*, size_t, ClassData>(
+ class_data_,
+ [](const ClassData& d) { return d.dirty_object_count; });
+ os_ << "\n" << " Dirty object count by class:\n";
+ for (const auto& vk_pair : dirty_object_class_values) {
+ size_t dirty_object_count = vk_pair.first;
+ mirror::Class* klass = vk_pair.second;
+ ClassData& class_data = class_data_[klass];
+ size_t object_sizes = class_data.dirty_object_size_in_bytes;
+ float avg_dirty_bytes_per_class =
+ class_data.dirty_object_byte_count * 1.0f / object_sizes;
+ float avg_object_size = object_sizes * 1.0f / dirty_object_count;
+ const std::string& descriptor = class_data.descriptor;
+ os_ << " " << mirror::Class::PrettyClass(klass) << " ("
+ << "objects: " << dirty_object_count << ", "
+ << "avg dirty bytes: " << avg_dirty_bytes_per_class << ", "
+ << "avg object size: " << avg_object_size << ", "
+ << "class descriptor: '" << descriptor << "'"
+ << ")\n";
+ if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
+ DumpSamplesAndOffsetCount();
+ os_ << " field contents:\n";
+ for (mirror::Object* object : class_data.dirty_objects) {
+ // remote class object
+ auto remote_klass = reinterpret_cast<mirror::Class*>(object);
+ // local class object
+ auto local_klass =
+ RemoteContentsPointerToLocal(remote_klass,
+ *RegionCommon<mirror::Object>::remote_contents_,
+ RegionCommon<mirror::Object>::image_header_);
+ os_ << " " << reinterpret_cast<const void*>(object) << " ";
+ os_ << " class_status (remote): " << remote_klass->GetStatus() << ", ";
+ os_ << " class_status (local): " << local_klass->GetStatus();
+ os_ << "\n";
+ }
+ }
+ }
+ }
+
+ void DumpFalseDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // vector of pairs (size_t count, Class*)
+ auto false_dirty_object_class_values =
+ SortByValueDesc<mirror::Class*, size_t, ClassData>(
+ class_data_,
+ [](const ClassData& d) { return d.false_dirty_object_count; });
+ os_ << "\n" << " False-dirty object count by class:\n";
+ for (const auto& vk_pair : false_dirty_object_class_values) {
+ size_t object_count = vk_pair.first;
+ mirror::Class* klass = vk_pair.second;
+ ClassData& class_data = class_data_[klass];
+ size_t object_sizes = class_data.false_dirty_byte_count;
+ float avg_object_size = object_sizes * 1.0f / object_count;
+ const std::string& descriptor = class_data.descriptor;
+ os_ << " " << mirror::Class::PrettyClass(klass) << " ("
+ << "objects: " << object_count << ", "
+ << "avg object size: " << avg_object_size << ", "
+ << "total bytes: " << object_sizes << ", "
+ << "class descriptor: '" << descriptor << "'"
+ << ")\n";
+ }
+ }
+
+ void DumpCleanEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // vector of pairs (size_t count, Class*)
+ auto clean_object_class_values =
+ SortByValueDesc<mirror::Class*, size_t, ClassData>(
+ class_data_,
+ [](const ClassData& d) { return d.clean_object_count; });
+ os_ << "\n" << " Clean object count by class:\n";
+ for (const auto& vk_pair : clean_object_class_values) {
+ os_ << " " << mirror::Class::PrettyClass(vk_pair.second) << " (" << vk_pair.first << ")\n";
+ }
+ }
+
+ private:
+ // Aggregate and detail class data from an image diff.
+ struct ClassData {
+ size_t dirty_object_count = 0;
+ // Track only the byte-per-byte dirtiness (in bytes)
+ size_t dirty_object_byte_count = 0;
+ // Track the object-by-object dirtiness (in bytes)
+ size_t dirty_object_size_in_bytes = 0;
+ size_t clean_object_count = 0;
+ std::string descriptor;
+ size_t false_dirty_byte_count = 0;
+ size_t false_dirty_object_count = 0;
+ std::vector<mirror::Object*> false_dirty_objects;
+ // Remote pointers to dirty objects
+ std::vector<mirror::Object*> dirty_objects;
+
+ void AddCleanObject() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ++clean_object_count;
+ }
+
+ void AddDirtyObject(mirror::Object* object, mirror::Object* object_remote)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ++dirty_object_count;
+ dirty_object_byte_count += CountDirtyBytes(object, object_remote);
+ dirty_object_size_in_bytes += EntrySize(object);
+ dirty_objects.push_back(object_remote);
+ }
+
+ void AddFalseDirtyObject(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ++false_dirty_object_count;
+ false_dirty_objects.push_back(object);
+ false_dirty_byte_count += EntrySize(object);
+ }
+
+ private:
+ // Go byte-by-byte and figure out what exactly got dirtied
+ static size_t CountDirtyBytes(mirror::Object* object1, mirror::Object* object2)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint8_t* cur1 = reinterpret_cast<const uint8_t*>(object1);
+ const uint8_t* cur2 = reinterpret_cast<const uint8_t*>(object2);
+ size_t dirty_bytes = 0;
+ size_t object_size = EntrySize(object1);
+ for (size_t i = 0; i < object_size; ++i) {
+ if (cur1[i] != cur2[i]) {
+ dirty_bytes++;
+ }
+ }
+ return dirty_bytes;
+ }
+ };
+
+ std::ostream& os_;
+ bool dump_dirty_objects_;
+ std::unordered_set<mirror::Object*> dirty_objects_;
+ std::map<mirror::Class*, ClassData> class_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase);
+};
+
+// Region analysis for ArtMethods.
+// TODO: most of these need work.
+template<>
+class RegionSpecializedBase<ArtMethod> : RegionCommon<ArtMethod> {
+ public:
+ RegionSpecializedBase(std::ostream* os,
+ std::vector<uint8_t>* remote_contents,
+ std::vector<uint8_t>* zygote_contents,
+ const backtrace_map_t& boot_map,
+ const ImageHeader& image_header) :
+ RegionCommon<ArtMethod>(os, remote_contents, zygote_contents, boot_map, image_header),
+ os_(*os) { }
+
+ void CheckEntrySanity(const uint8_t* current ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ }
+
+ ArtMethod* GetNextEntry(ArtMethod* entry)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t* next = reinterpret_cast<uint8_t*>(entry) + RoundUp(EntrySize(entry), kObjectAlignment);
+ return reinterpret_cast<ArtMethod*>(next);
+ }
+
+ void VisitEntry(ArtMethod* method ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ }
+
+ void AddFalseDirtyEntry(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ RegionCommon<ArtMethod>::AddFalseDirtyEntry(method);
+ }
+
+ void AddCleanEntry(ArtMethod* method ATTRIBUTE_UNUSED) {
+ }
+
+ void AddDirtyEntry(ArtMethod* method, ArtMethod* method_remote)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ size_t entry_size = EntrySize(method);
+ ++different_entries_;
+ dirty_entry_bytes_ += entry_size;
+ // Increment counts for the fields that are dirty
+ const uint8_t* current = reinterpret_cast<const uint8_t*>(method);
+ const uint8_t* current_remote = reinterpret_cast<const uint8_t*>(method_remote);
+ // ArtMethods always log their dirty count and entries.
+ for (size_t i = 0; i < entry_size; ++i) {
+ if (current[i] != current_remote[i]) {
+ field_dirty_count_[i]++;
+ }
+ }
+ dirty_entries_.push_back(method);
+ }
+
+ void DiffEntryContents(ArtMethod* method ATTRIBUTE_UNUSED,
+ uint8_t* remote_bytes ATTRIBUTE_UNUSED,
+ const uint8_t* base_ptr ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ }
+
+ void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DumpSamplesAndOffsetCount();
+ os_ << " field contents:\n";
+ for (ArtMethod* method : dirty_entries_) {
+ // remote method
+ auto art_method = reinterpret_cast<ArtMethod*>(method);
+ // remote class
+ mirror::Class* remote_declaring_class =
+ FixUpRemotePointer(art_method->GetDeclaringClass(),
+ *RegionCommon<ArtMethod>::remote_contents_,
+ RegionCommon<ArtMethod>::boot_map_);
+ // local class
+ mirror::Class* declaring_class =
+ RemoteContentsPointerToLocal(remote_declaring_class,
+ *RegionCommon<ArtMethod>::remote_contents_,
+ RegionCommon<ArtMethod>::image_header_);
+ DumpOneArtMethod(art_method, declaring_class, remote_declaring_class);
+ }
+ }
+
+ void DumpFalseDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
+ os_ << " field contents:\n";
+ for (ArtMethod* method : false_dirty_entries_) {
+ // local class
+ mirror::Class* declaring_class = method->GetDeclaringClass();
+ DumpOneArtMethod(method, declaring_class, nullptr);
+ }
+ }
+
+ void DumpCleanEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
+ }
+
+ private:
+ std::ostream& os_;
+
+ void DumpOneArtMethod(ArtMethod* art_method,
+ mirror::Class* declaring_class,
+ mirror::Class* remote_declaring_class)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ PointerSize pointer_size = InstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
+ os_ << " " << reinterpret_cast<const void*>(art_method) << " ";
+ os_ << " entryPointFromJni: "
+ << reinterpret_cast<const void*>(art_method->GetDataPtrSize(pointer_size)) << ", ";
+ os_ << " entryPointFromQuickCompiledCode: "
+ << reinterpret_cast<const void*>(
+ art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size))
+ << ", ";
+ os_ << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", ";
+ os_ << " class_status (local): " << declaring_class->GetStatus();
+ if (remote_declaring_class != nullptr) {
+ os_ << ", class_status (remote): " << remote_declaring_class->GetStatus();
+ }
+ os_ << "\n";
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase);
+};
+
+template <typename T>
+class RegionData : public RegionSpecializedBase<T> {
+ public:
+ RegionData(std::ostream* os,
+ std::vector<uint8_t>* remote_contents,
+ std::vector<uint8_t>* zygote_contents,
+ const backtrace_map_t& boot_map,
+ const ImageHeader& image_header,
+ bool dump_dirty_objects)
+ : RegionSpecializedBase<T>(os,
+ remote_contents,
+ zygote_contents,
+ boot_map,
+ image_header,
+ dump_dirty_objects),
+ os_(*os) {
+ CHECK(remote_contents != nullptr);
+ CHECK(zygote_contents != nullptr);
+ }
+
+ // Walk over the type T entries in theregion between begin_image_ptr and end_image_ptr,
+ // collecting and reporting data regarding dirty, difference, etc.
+ void ProcessRegion(const MappingData& mapping_data,
+ RemoteProcesses remotes,
+ const uint8_t* begin_image_ptr,
+ const uint8_t* end_image_ptr)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment);
+ T* entry = reinterpret_cast<T*>(const_cast<uint8_t*>(current));
+ while (reinterpret_cast<uintptr_t>(entry) < reinterpret_cast<uintptr_t>(end_image_ptr)) {
+ ComputeEntryDirty(entry, begin_image_ptr, mapping_data.dirty_page_set);
+
+ entry = RegionSpecializedBase<T>::GetNextEntry(entry);
+ }
+
+ // Looking at only dirty pages, figure out how many of those bytes belong to dirty entries.
+ // TODO: fix this now that there are multiple regions in a mapping.
+ float true_dirtied_percent =
+ RegionCommon<T>::GetDirtyEntryBytes() * 1.0f / (mapping_data.dirty_pages * kPageSize);
+
+ // Entry specific statistics.
+ os_ << RegionCommon<T>::GetDifferentEntryCount() << " different entries, \n "
+ << RegionCommon<T>::GetDirtyEntryBytes() << " different entry [bytes], \n "
+ << RegionCommon<T>::GetFalseDirtyEntryCount() << " false dirty entries,\n "
+ << RegionCommon<T>::GetFalseDirtyEntryBytes() << " false dirty entry [bytes], \n "
+ << true_dirtied_percent << " different entries-vs-total in a dirty page;\n "
+ << "\n";
+
+ const uint8_t* base_ptr = begin_image_ptr;
+ switch (remotes) {
+ case RemoteProcesses::kZygoteOnly:
+ os_ << " Zygote shared dirty entries: ";
+ break;
+ case RemoteProcesses::kImageAndZygote:
+ os_ << " Application dirty entries (private dirty): ";
+ // If we are dumping private dirty, diff against the zygote map to make it clearer what
+ // fields caused the page to be private dirty.
+ base_ptr = &RegionCommon<T>::zygote_contents_->operator[](0);
+ break;
+ case RemoteProcesses::kImageOnly:
+ os_ << " Application dirty entries (unknown whether private or shared dirty): ";
+ break;
+ }
+ DiffDirtyEntries(ProcessType::kRemote,
+ begin_image_ptr,
+ RegionCommon<T>::remote_contents_,
+ base_ptr,
+ /*log_dirty_objects*/true);
+ // Print shared dirty after since it's less important.
+ if (RegionCommon<T>::GetZygoteDirtyEntryCount() != 0) {
+ // We only reach this point if both pids were specified. Furthermore,
+ // entries are only displayed here if they differed in both the image
+ // and the zygote, so they are probably private dirty.
+ CHECK(remotes == RemoteProcesses::kImageAndZygote);
+ os_ << "\n" << " Zygote dirty entries (probably shared dirty): ";
+ DiffDirtyEntries(ProcessType::kZygote,
+ begin_image_ptr,
+ RegionCommon<T>::zygote_contents_,
+ begin_image_ptr,
+ /*log_dirty_objects*/false);
+ }
+ RegionSpecializedBase<T>::DumpDirtyObjects();
+ RegionSpecializedBase<T>::DumpDirtyEntries();
+ RegionSpecializedBase<T>::DumpFalseDirtyEntries();
+ RegionSpecializedBase<T>::DumpCleanEntries();
+ }
+
+ private:
+ std::ostream& os_;
+
+ void DiffDirtyEntries(ProcessType process_type,
+ const uint8_t* begin_image_ptr,
+ std::vector<uint8_t>* contents,
+ const uint8_t* base_ptr,
+ bool log_dirty_objects)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ os_ << RegionCommon<T>::dirty_entries_.size() << "\n";
+ const std::set<T*>& entries =
+ (process_type == ProcessType::kZygote) ?
+ RegionCommon<T>::zygote_dirty_entries_:
+ RegionCommon<T>::image_dirty_entries_;
+ for (T* entry : entries) {
+ uint8_t* entry_bytes = reinterpret_cast<uint8_t*>(entry);
+ ptrdiff_t offset = entry_bytes - begin_image_ptr;
+ uint8_t* remote_bytes = &(*contents)[offset];
+ RegionSpecializedBase<T>::DiffEntryContents(entry,
+ remote_bytes,
+ &base_ptr[offset],
+ log_dirty_objects);
+ }
+ }
+
+ void ComputeEntryDirty(T* entry,
+ const uint8_t* begin_image_ptr,
+ const std::set<size_t>& dirty_pages)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Set up pointers in the remote and the zygote for comparison.
+ uint8_t* current = reinterpret_cast<uint8_t*>(entry);
+ ptrdiff_t offset = current - begin_image_ptr;
+ T* entry_remote =
+ reinterpret_cast<T*>(const_cast<uint8_t*>(&(*RegionCommon<T>::remote_contents_)[offset]));
+ const bool have_zygote = !RegionCommon<T>::zygote_contents_->empty();
+ const uint8_t* current_zygote =
+ have_zygote ? &(*RegionCommon<T>::zygote_contents_)[offset] : nullptr;
+ T* entry_zygote = reinterpret_cast<T*>(const_cast<uint8_t*>(current_zygote));
+ // Visit and classify entries at the current location.
+ RegionSpecializedBase<T>::VisitEntry(entry);
+
+ // Test private dirty first.
+ bool is_dirty = false;
+ if (have_zygote) {
+ bool private_dirty = EntriesDiffer(entry_zygote, entry_remote);
+ if (private_dirty) {
+ // Private dirty, app vs zygote.
+ is_dirty = true;
+ RegionCommon<T>::AddImageDirtyEntry(entry);
+ }
+ if (EntriesDiffer(entry_zygote, entry)) {
+ // Shared dirty, zygote vs image.
+ is_dirty = true;
+ RegionCommon<T>::AddZygoteDirtyEntry(entry);
+ }
+ } else if (EntriesDiffer(entry_remote, entry)) {
+ // Shared or private dirty, app vs image.
+ is_dirty = true;
+ RegionCommon<T>::AddImageDirtyEntry(entry);
+ }
+ if (is_dirty) {
+ // TODO: Add support dirty entries in zygote and image.
+ RegionSpecializedBase<T>::AddDirtyEntry(entry, entry_remote);
+ } else {
+ RegionSpecializedBase<T>::AddCleanEntry(entry);
+ if (RegionCommon<T>::IsEntryOnDirtyPage(entry, dirty_pages)) {
+ // This entry was either never mutated or got mutated back to the same value.
+ // TODO: Do I want to distinguish a "different" vs a "dirty" page here?
+ RegionSpecializedBase<T>::AddFalseDirtyEntry(entry);
+ }
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(RegionData);
+};
+
+} // namespace
+
+
class ImgDiagDumper {
public:
explicit ImgDiagDumper(std::ostream* os,
const ImageHeader& image_header,
const std::string& image_location,
pid_t image_diff_pid,
- pid_t zygote_diff_pid)
+ pid_t zygote_diff_pid,
+ bool dump_dirty_objects)
: os_(os),
image_header_(image_header),
image_location_(image_location),
image_diff_pid_(image_diff_pid),
zygote_diff_pid_(zygote_diff_pid),
+ dump_dirty_objects_(dump_dirty_objects),
zygote_pid_only_(false) {}
bool Init() {
@@ -123,8 +972,6 @@
CHECK(boot_map_.end >= boot_map_.start);
boot_map_size_ = boot_map_.end - boot_map_.start;
- pointer_size_ = InstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
-
// Open /proc/<image_diff_pid_>/mem and read as remote_contents_.
std::string image_file_name =
StringPrintf("/proc/%ld/mem", static_cast<long>(image_diff_pid_)); // NOLINT [runtime/int]
@@ -150,7 +997,7 @@
return false;
}
// The boot map should be at the same address.
- tmp_zygote_contents.reserve(boot_map_size_);
+ tmp_zygote_contents.resize(boot_map_size_);
if (!zygote_map_file->PreadFully(&tmp_zygote_contents[0], boot_map_size_, boot_map_.start)) {
LOG(WARNING) << "Could not fully read zygote file " << zygote_file_name;
return false;
@@ -188,7 +1035,7 @@
return false;
}
- // Commit the mappings, etc., to the object state.
+ // Commit the mappings, etc.
proc_maps_ = std::move(tmp_proc_maps);
remote_contents_ = std::move(tmp_remote_contents);
zygote_contents_ = std::move(tmp_zygote_contents);
@@ -228,14 +1075,7 @@
return DumpImageDiffMap();
}
- bool ComputeDirtyBytes(const uint8_t* image_begin,
- size_t* dirty_pages /*out*/,
- size_t* different_pages /*out*/,
- size_t* different_bytes /*out*/,
- size_t* different_int32s /*out*/,
- size_t* private_pages /*out*/,
- size_t* private_dirty_pages /*out*/,
- std::set<size_t>* dirty_page_set_local) {
+ bool ComputeDirtyBytes(const uint8_t* image_begin, MappingData* mapping_data /*out*/) {
std::ostream& os = *os_;
size_t virtual_page_idx = 0; // Virtual page number (for an absolute memory address)
@@ -254,7 +1094,7 @@
uint8_t* remote_ptr = &remote_contents_[offset];
if (memcmp(local_ptr, remote_ptr, kPageSize) != 0) {
- ++*different_pages;
+ mapping_data->different_pages++;
// Count the number of 32-bit integers that are different.
for (size_t i = 0; i < kPageSize / sizeof(uint32_t); ++i) {
@@ -262,12 +1102,14 @@
const uint32_t* local_ptr_int32 = reinterpret_cast<const uint32_t*>(local_ptr);
if (remote_ptr_int32[i] != local_ptr_int32[i]) {
- ++*different_int32s;
+ mapping_data->different_int32s++;
}
}
}
}
+ std::vector<size_t> private_dirty_pages_for_section(ImageHeader::kSectionCount, 0u);
+
// Iterate through one byte at a time.
ptrdiff_t page_off_begin = image_header_.GetImageBegin() - image_begin;
for (uintptr_t begin = boot_map_.start; begin != boot_map_.end; ++begin) {
@@ -286,7 +1128,7 @@
page_idx = (offset + page_off_begin) / kPageSize;
if (*local_ptr != *remote_ptr) {
// Track number of bytes that are different
- ++*different_bytes;
+ mapping_data->different_bytes++;
}
// Independently count the # of dirty pages on the remote side
@@ -307,294 +1149,56 @@
os << error_msg;
return false;
} else if (dirtiness > 0) {
- ++*dirty_pages;
- dirty_page_set_local->insert(dirty_page_set_local->end(), virtual_page_idx);
+ mapping_data->dirty_pages++;
+ mapping_data->dirty_page_set.insert(mapping_data->dirty_page_set.end(), virtual_page_idx);
}
bool is_dirty = dirtiness > 0;
bool is_private = page_count == 1;
if (page_count == 1) {
- ++*private_pages;
+ mapping_data->private_pages++;
}
if (is_dirty && is_private) {
- ++*private_dirty_pages;
- }
- }
- }
- return true;
- }
-
- bool ObjectIsOnDirtyPage(const uint8_t* item,
- size_t size,
- const std::set<size_t>& dirty_page_set_local) {
- size_t page_off = 0;
- size_t current_page_idx;
- uintptr_t object_address = reinterpret_cast<uintptr_t>(item);
- // Iterate every page this object belongs to
- do {
- current_page_idx = object_address / kPageSize + page_off;
-
- if (dirty_page_set_local.find(current_page_idx) != dirty_page_set_local.end()) {
- // This object is on a dirty page
- return true;
- }
-
- page_off++;
- } while ((current_page_idx * kPageSize) < RoundUp(object_address + size, kObjectAlignment));
-
- return false;
- }
-
- static std::string PrettyFieldValue(ArtField* field, mirror::Object* obj)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- std::ostringstream oss;
- switch (field->GetTypeAsPrimitiveType()) {
- case Primitive::kPrimNot: {
- oss << obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
- field->GetOffset());
- break;
- }
- case Primitive::kPrimBoolean: {
- oss << static_cast<bool>(obj->GetFieldBoolean<kVerifyNone>(field->GetOffset()));
- break;
- }
- case Primitive::kPrimByte: {
- oss << static_cast<int32_t>(obj->GetFieldByte<kVerifyNone>(field->GetOffset()));
- break;
- }
- case Primitive::kPrimChar: {
- oss << obj->GetFieldChar<kVerifyNone>(field->GetOffset());
- break;
- }
- case Primitive::kPrimShort: {
- oss << obj->GetFieldShort<kVerifyNone>(field->GetOffset());
- break;
- }
- case Primitive::kPrimInt: {
- oss << obj->GetField32<kVerifyNone>(field->GetOffset());
- break;
- }
- case Primitive::kPrimLong: {
- oss << obj->GetField64<kVerifyNone>(field->GetOffset());
- break;
- }
- case Primitive::kPrimFloat: {
- oss << obj->GetField32<kVerifyNone>(field->GetOffset());
- break;
- }
- case Primitive::kPrimDouble: {
- oss << obj->GetField64<kVerifyNone>(field->GetOffset());
- break;
- }
- case Primitive::kPrimVoid: {
- oss << "void";
- break;
- }
- }
- return oss.str();
- }
-
- // Aggregate and detail class data from an image diff.
- struct ClassData {
- size_t dirty_object_count = 0;
-
- // Track only the byte-per-byte dirtiness (in bytes)
- size_t dirty_object_byte_count = 0;
-
- // Track the object-by-object dirtiness (in bytes)
- size_t dirty_object_size_in_bytes = 0;
-
- size_t clean_object_count = 0;
-
- std::string descriptor;
-
- size_t false_dirty_byte_count = 0;
- size_t false_dirty_object_count = 0;
- std::vector<const uint8_t*> false_dirty_objects;
-
- // Remote pointers to dirty objects
- std::vector<const uint8_t*> dirty_objects;
- };
-
- void DiffObjectContents(mirror::Object* obj,
- uint8_t* remote_bytes,
- std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_) {
- const char* tabs = " ";
- // Attempt to find fields for all dirty bytes.
- mirror::Class* klass = obj->GetClass();
- if (obj->IsClass()) {
- os << tabs << "Class " << mirror::Class::PrettyClass(obj->AsClass()) << " " << obj << "\n";
- } else {
- os << tabs << "Instance of " << mirror::Class::PrettyClass(klass) << " " << obj << "\n";
- }
-
- std::unordered_set<ArtField*> dirty_instance_fields;
- std::unordered_set<ArtField*> dirty_static_fields;
- const uint8_t* obj_bytes = reinterpret_cast<const uint8_t*>(obj);
- mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>(remote_bytes);
- for (size_t i = 0, count = obj->SizeOf(); i < count; ++i) {
- if (obj_bytes[i] != remote_bytes[i]) {
- ArtField* field = ArtField::FindInstanceFieldWithOffset</*exact*/false>(klass, i);
- if (field != nullptr) {
- dirty_instance_fields.insert(field);
- } else if (obj->IsClass()) {
- field = ArtField::FindStaticFieldWithOffset</*exact*/false>(obj->AsClass(), i);
- if (field != nullptr) {
- dirty_static_fields.insert(field);
- }
- }
- if (field == nullptr) {
- if (klass->IsArrayClass()) {
- mirror::Class* component_type = klass->GetComponentType();
- Primitive::Type primitive_type = component_type->GetPrimitiveType();
- size_t component_size = Primitive::ComponentSize(primitive_type);
- size_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value();
- if (i >= data_offset) {
- os << tabs << "Dirty array element " << (i - data_offset) / component_size << "\n";
- // Skip to next element to prevent spam.
- i += component_size - 1;
- continue;
+ mapping_data->private_dirty_pages++;
+ for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+ const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
+ if (image_header_.GetImageSection(section).Contains(offset)) {
+ ++private_dirty_pages_for_section[i];
}
}
- os << tabs << "No field for byte offset " << i << "\n";
}
}
}
- // Dump different fields. TODO: Dump field contents.
- if (!dirty_instance_fields.empty()) {
- os << tabs << "Dirty instance fields " << dirty_instance_fields.size() << "\n";
- for (ArtField* field : dirty_instance_fields) {
- os << tabs << ArtField::PrettyField(field)
- << " original=" << PrettyFieldValue(field, obj)
- << " remote=" << PrettyFieldValue(field, remote_obj) << "\n";
- }
- }
- if (!dirty_static_fields.empty()) {
- os << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n";
- for (ArtField* field : dirty_static_fields) {
- os << tabs << ArtField::PrettyField(field)
- << " original=" << PrettyFieldValue(field, obj)
- << " remote=" << PrettyFieldValue(field, remote_obj) << "\n";
- }
+ mapping_data->false_dirty_pages = mapping_data->dirty_pages - mapping_data->different_pages;
+ // Print low-level (bytes, int32s, pages) statistics.
+ os << mapping_data->different_bytes << " differing bytes,\n "
+ << mapping_data->different_int32s << " differing int32s,\n "
+ << mapping_data->different_pages << " differing pages,\n "
+ << mapping_data->dirty_pages << " pages are dirty;\n "
+ << mapping_data->false_dirty_pages << " pages are false dirty;\n "
+ << mapping_data->private_pages << " pages are private;\n "
+ << mapping_data->private_dirty_pages << " pages are Private_Dirty\n "
+ << "\n";
+
+ size_t total_private_dirty_pages = std::accumulate(private_dirty_pages_for_section.begin(),
+ private_dirty_pages_for_section.end(),
+ 0u);
+ os << "Image sections (total private dirty pages " << total_private_dirty_pages << ")\n";
+ for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+ const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
+ os << section << " " << image_header_.GetImageSection(section)
+ << " private dirty pages=" << private_dirty_pages_for_section[i] << "\n";
}
os << "\n";
- }
- struct ObjectRegionData {
- // Count of objects that are different.
- size_t different_objects = 0;
-
- // Local objects that are dirty (differ in at least one byte).
- size_t dirty_object_bytes = 0;
- std::vector<const uint8_t*>* dirty_objects;
-
- // Local objects that are clean, but located on dirty pages.
- size_t false_dirty_object_bytes = 0;
- std::vector<const uint8_t*> false_dirty_objects;
-
- // Image dirty objects
- // If zygote_pid_only_ == true, these are shared dirty objects in the zygote.
- // If zygote_pid_only_ == false, these are private dirty objects in the application.
- std::set<const uint8_t*> image_dirty_objects;
-
- // Zygote dirty objects (probably private dirty).
- // We only add objects here if they differed in both the image and the zygote, so
- // they are probably private dirty.
- std::set<const uint8_t*> zygote_dirty_objects;
-
- std::map<off_t /* field offset */, size_t /* count */>* field_dirty_count;
- };
-
- void ComputeObjectDirty(const uint8_t* current,
- const uint8_t* current_remote,
- const uint8_t* current_zygote,
- ClassData* obj_class_data,
- size_t obj_size,
- const std::set<size_t>& dirty_page_set_local,
- ObjectRegionData* region_data /*out*/) {
- bool different_image_object = memcmp(current, current_remote, obj_size) != 0;
- if (different_image_object) {
- bool different_zygote_object = false;
- if (!zygote_contents_.empty()) {
- different_zygote_object = memcmp(current, current_zygote, obj_size) != 0;
- }
- if (different_zygote_object) {
- // Different from zygote.
- region_data->zygote_dirty_objects.insert(current);
- } else {
- // Just different from image.
- region_data->image_dirty_objects.insert(current);
- }
-
- ++region_data->different_objects;
- region_data->dirty_object_bytes += obj_size;
-
- ++obj_class_data->dirty_object_count;
-
- // Go byte-by-byte and figure out what exactly got dirtied
- size_t dirty_byte_count_per_object = 0;
- for (size_t i = 0; i < obj_size; ++i) {
- if (current[i] != current_remote[i]) {
- dirty_byte_count_per_object++;
- }
- }
- obj_class_data->dirty_object_byte_count += dirty_byte_count_per_object;
- obj_class_data->dirty_object_size_in_bytes += obj_size;
- obj_class_data->dirty_objects.push_back(current_remote);
- } else {
- ++obj_class_data->clean_object_count;
- }
-
- if (different_image_object) {
- if (region_data->dirty_objects != nullptr) {
- // print the fields that are dirty
- for (size_t i = 0; i < obj_size; ++i) {
- if (current[i] != current_remote[i]) {
- size_t dirty_count = 0;
- if (region_data->field_dirty_count->find(i) != region_data->field_dirty_count->end()) {
- dirty_count = (*region_data->field_dirty_count)[i];
- }
- (*region_data->field_dirty_count)[i] = dirty_count + 1;
- }
- }
-
- region_data->dirty_objects->push_back(current);
- }
- /*
- * TODO: Resurrect this stuff in the client when we add ArtMethod iterator.
- } else {
- std::string descriptor = GetClassDescriptor(klass);
- if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
- // this is an ArtMethod
- ArtMethod* art_method = reinterpret_cast<ArtMethod*>(remote_obj);
-
- // print the fields that are dirty
- for (size_t i = 0; i < obj_size; ++i) {
- if (current[i] != current_remote[i]) {
- art_method_field_dirty_count[i]++;
- }
- }
-
- art_method_dirty_objects.push_back(art_method);
- }
- }
- */
- } else if (ObjectIsOnDirtyPage(current, obj_size, dirty_page_set_local)) {
- // This object was either never mutated or got mutated back to the same value.
- // TODO: Do I want to distinguish a "different" vs a "dirty" page here?
- region_data->false_dirty_objects.push_back(current);
- obj_class_data->false_dirty_objects.push_back(current);
- region_data->false_dirty_object_bytes += obj_size;
- obj_class_data->false_dirty_byte_count += obj_size;
- obj_class_data->false_dirty_object_count += 1;
- }
+ return true;
}
// Look at /proc/$pid/mem and only diff the things from there
bool DumpImageDiffMap()
- REQUIRES_SHARED(Locks::mutator_lock_) {
+ REQUIRES_SHARED(Locks::mutator_lock_) {
std::ostream& os = *os_;
std::string error_msg;
@@ -624,384 +1228,38 @@
// If we wanted even more validation we could map the ImageHeader from the file
}
- size_t dirty_pages = 0;
- size_t different_pages = 0;
- size_t different_bytes = 0;
- size_t different_int32s = 0;
- size_t private_pages = 0;
- size_t private_dirty_pages = 0;
+ MappingData mapping_data;
- // Set of the local virtual page indices that are dirty
- std::set<size_t> dirty_page_set_local;
-
- if (!ComputeDirtyBytes(image_begin,
- &dirty_pages,
- &different_pages,
- &different_bytes,
- &different_int32s,
- &private_pages,
- &private_dirty_pages,
- &dirty_page_set_local)) {
+ os << "Mapping at [" << reinterpret_cast<void*>(boot_map_.start) << ", "
+ << reinterpret_cast<void*>(boot_map_.end) << ") had:\n ";
+ if (!ComputeDirtyBytes(image_begin, &mapping_data)) {
return false;
}
- std::map<mirror::Class*, ClassData> class_data;
+ RegionData<mirror::Object> object_region_data(os_,
+ &remote_contents_,
+ &zygote_contents_,
+ boot_map_,
+ image_header_,
+ dump_dirty_objects_);
- // Walk each object in the remote image space and compare it against ours
- std::map<off_t /* field offset */, int /* count */> art_method_field_dirty_count;
- std::vector<ArtMethod*> art_method_dirty_objects;
-
- std::map<off_t /* field offset */, size_t /* count */> class_field_dirty_count;
- std::vector<const uint8_t*> class_dirty_objects;
-
-
- // Look up remote classes by their descriptor
- std::map<std::string, mirror::Class*> remote_class_map;
- // Look up local classes by their descriptor
- std::map<std::string, mirror::Class*> local_class_map;
-
- const uint8_t* begin_image_ptr = image_begin_unaligned;
- const uint8_t* end_image_ptr = image_mirror_end_unaligned;
-
- ObjectRegionData region_data;
-
- const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment);
- while (reinterpret_cast<uintptr_t>(current) < reinterpret_cast<uintptr_t>(end_image_ptr)) {
- CHECK_ALIGNED(current, kObjectAlignment);
- mirror::Object* obj = reinterpret_cast<mirror::Object*>(const_cast<uint8_t*>(current));
-
- // Sanity check that we are reading a real object
- CHECK(obj->GetClass() != nullptr) << "Image object at address " << obj << " has null class";
- if (kUseBakerReadBarrier) {
- obj->AssertReadBarrierState();
- }
-
- mirror::Class* klass = obj->GetClass();
- size_t obj_size = obj->SizeOf();
- ClassData& obj_class_data = class_data[klass];
-
- // Check against the other object and see if they are different
- ptrdiff_t offset = current - begin_image_ptr;
- const uint8_t* current_remote = &remote_contents_[offset];
- const uint8_t* current_zygote =
- zygote_contents_.empty() ? nullptr : &zygote_contents_[offset];
-
- if (klass->IsClassClass()) {
- region_data.field_dirty_count = &class_field_dirty_count;
- region_data.dirty_objects = &class_dirty_objects;
- } else {
- region_data.field_dirty_count = nullptr;
- region_data.dirty_objects = nullptr;
- }
-
-
- ComputeObjectDirty(current,
- current_remote,
- current_zygote,
- &obj_class_data,
- obj_size,
- dirty_page_set_local,
- ®ion_data);
-
- // Object specific stuff.
- std::string descriptor = GetClassDescriptor(klass);
- if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
- local_class_map[descriptor] = reinterpret_cast<mirror::Class*>(obj);
- mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>(
- const_cast<uint8_t*>(current_remote));
- remote_class_map[descriptor] = reinterpret_cast<mirror::Class*>(remote_obj);
- }
-
- // Unconditionally store the class descriptor in case we need it later
- obj_class_data.descriptor = descriptor;
-
- current += RoundUp(obj_size, kObjectAlignment);
- }
-
- // Looking at only dirty pages, figure out how many of those bytes belong to dirty objects.
- float true_dirtied_percent = region_data.dirty_object_bytes * 1.0f / (dirty_pages * kPageSize);
- size_t false_dirty_pages = dirty_pages - different_pages;
-
- os << "Mapping at [" << reinterpret_cast<void*>(boot_map_.start) << ", "
- << reinterpret_cast<void*>(boot_map_.end) << ") had: \n "
- << different_bytes << " differing bytes, \n "
- << different_int32s << " differing int32s, \n "
- << region_data.different_objects << " different objects, \n "
- << region_data.dirty_object_bytes << " different object [bytes], \n "
- << region_data.false_dirty_objects.size() << " false dirty objects,\n "
- << region_data.false_dirty_object_bytes << " false dirty object [bytes], \n "
- << true_dirtied_percent << " different objects-vs-total in a dirty page;\n "
- << different_pages << " different pages; \n "
- << dirty_pages << " pages are dirty; \n "
- << false_dirty_pages << " pages are false dirty; \n "
- << private_pages << " pages are private; \n "
- << private_dirty_pages << " pages are Private_Dirty\n "
- << "";
-
- // vector of pairs (int count, Class*)
- auto dirty_object_class_values = SortByValueDesc<mirror::Class*, int, ClassData>(
- class_data, [](const ClassData& d) { return d.dirty_object_count; });
- auto clean_object_class_values = SortByValueDesc<mirror::Class*, int, ClassData>(
- class_data, [](const ClassData& d) { return d.clean_object_count; });
-
- if (!region_data.zygote_dirty_objects.empty()) {
- // We only reach this point if both pids were specified. Furthermore,
- // objects are only displayed here if they differed in both the image
- // and the zygote, so they are probably private dirty.
- CHECK(image_diff_pid_ > 0 && zygote_diff_pid_ > 0);
- os << "\n" << " Zygote dirty objects (probably shared dirty): "
- << region_data.zygote_dirty_objects.size() << "\n";
- for (const uint8_t* obj_bytes : region_data.zygote_dirty_objects) {
- auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(obj_bytes));
- ptrdiff_t offset = obj_bytes - begin_image_ptr;
- uint8_t* remote_bytes = &zygote_contents_[offset];
- DiffObjectContents(obj, remote_bytes, os);
- }
- }
- os << "\n";
+ RemoteProcesses remotes;
if (zygote_pid_only_) {
- // image_diff_pid_ is the zygote process.
- os << " Zygote shared dirty objects: ";
+ remotes = RemoteProcesses::kZygoteOnly;
+ } else if (zygote_diff_pid_ > 0) {
+ remotes = RemoteProcesses::kImageAndZygote;
} else {
- // image_diff_pid_ is actually the image (application) process.
- if (zygote_diff_pid_ > 0) {
- os << " Application dirty objects (private dirty): ";
- } else {
- os << " Application dirty objects (unknown whether private or shared dirty): ";
- }
- }
- os << region_data.image_dirty_objects.size() << "\n";
- for (const uint8_t* obj_bytes : region_data.image_dirty_objects) {
- auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(obj_bytes));
- ptrdiff_t offset = obj_bytes - begin_image_ptr;
- uint8_t* remote_bytes = &remote_contents_[offset];
- DiffObjectContents(obj, remote_bytes, os);
+ remotes = RemoteProcesses::kImageOnly;
}
- os << "\n" << " Dirty object count by class:\n";
- for (const auto& vk_pair : dirty_object_class_values) {
- int dirty_object_count = vk_pair.first;
- mirror::Class* klass = vk_pair.second;
- int object_sizes = class_data[klass].dirty_object_size_in_bytes;
- float avg_dirty_bytes_per_class =
- class_data[klass].dirty_object_byte_count * 1.0f / object_sizes;
- float avg_object_size = object_sizes * 1.0f / dirty_object_count;
- const std::string& descriptor = class_data[klass].descriptor;
- os << " " << mirror::Class::PrettyClass(klass) << " ("
- << "objects: " << dirty_object_count << ", "
- << "avg dirty bytes: " << avg_dirty_bytes_per_class << ", "
- << "avg object size: " << avg_object_size << ", "
- << "class descriptor: '" << descriptor << "'"
- << ")\n";
-
- constexpr size_t kMaxAddressPrint = 5;
- if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
- os << " sample object addresses: ";
- for (size_t i = 0; i < art_method_dirty_objects.size() && i < kMaxAddressPrint; ++i) {
- auto art_method = art_method_dirty_objects[i];
-
- os << reinterpret_cast<void*>(art_method) << ", ";
- }
- os << "\n";
-
- os << " dirty byte +offset:count list = ";
- auto art_method_field_dirty_count_sorted =
- SortByValueDesc<off_t, int, int>(art_method_field_dirty_count);
- for (auto pair : art_method_field_dirty_count_sorted) {
- off_t offset = pair.second;
- int count = pair.first;
-
- os << "+" << offset << ":" << count << ", ";
- }
-
- os << "\n";
-
- os << " field contents:\n";
- const auto& dirty_objects_list = class_data[klass].dirty_objects;
- for (const uint8_t* uobj : dirty_objects_list) {
- auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(uobj));
- // remote method
- auto art_method = reinterpret_cast<ArtMethod*>(obj);
-
- // remote class
- mirror::Class* remote_declaring_class =
- FixUpRemotePointer(art_method->GetDeclaringClass(), remote_contents_, boot_map_);
-
- // local class
- mirror::Class* declaring_class =
- RemoteContentsPointerToLocal(remote_declaring_class, remote_contents_, image_header_);
-
- os << " " << reinterpret_cast<void*>(obj) << " ";
- os << " entryPointFromJni: "
- << reinterpret_cast<const void*>(
- art_method->GetDataPtrSize(pointer_size_)) << ", ";
- os << " entryPointFromQuickCompiledCode: "
- << reinterpret_cast<const void*>(
- art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_))
- << ", ";
- os << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", ";
- os << " class_status (local): " << declaring_class->GetStatus();
- os << " class_status (remote): " << remote_declaring_class->GetStatus();
- os << "\n";
- }
- }
- if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
- os << " sample object addresses: ";
- for (size_t i = 0; i < class_dirty_objects.size() && i < kMaxAddressPrint; ++i) {
- auto class_ptr = class_dirty_objects[i];
-
- os << reinterpret_cast<const void*>(class_ptr) << ", ";
- }
- os << "\n";
-
- os << " dirty byte +offset:count list = ";
- auto class_field_dirty_count_sorted =
- SortByValueDesc<off_t, int, size_t>(class_field_dirty_count);
- for (auto pair : class_field_dirty_count_sorted) {
- off_t offset = pair.second;
- int count = pair.first;
-
- os << "+" << offset << ":" << count << ", ";
- }
- os << "\n";
-
- os << " field contents:\n";
- // TODO: templatize this to avoid the awful casts down to uint8_t* and back.
- const auto& dirty_objects_list = class_data[klass].dirty_objects;
- for (const uint8_t* uobj : dirty_objects_list) {
- auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(uobj));
- // remote class object
- auto remote_klass = reinterpret_cast<mirror::Class*>(obj);
-
- // local class object
- auto local_klass = RemoteContentsPointerToLocal(remote_klass,
- remote_contents_,
- image_header_);
-
- os << " " << reinterpret_cast<const void*>(obj) << " ";
- os << " class_status (remote): " << remote_klass->GetStatus() << ", ";
- os << " class_status (local): " << local_klass->GetStatus();
- os << "\n";
- }
- }
- }
-
- auto false_dirty_object_class_values = SortByValueDesc<mirror::Class*, int, ClassData>(
- class_data, [](const ClassData& d) { return d.false_dirty_object_count; });
-
- os << "\n" << " False-dirty object count by class:\n";
- for (const auto& vk_pair : false_dirty_object_class_values) {
- int object_count = vk_pair.first;
- mirror::Class* klass = vk_pair.second;
- int object_sizes = class_data[klass].false_dirty_byte_count;
- float avg_object_size = object_sizes * 1.0f / object_count;
- const std::string& descriptor = class_data[klass].descriptor;
- os << " " << mirror::Class::PrettyClass(klass) << " ("
- << "objects: " << object_count << ", "
- << "avg object size: " << avg_object_size << ", "
- << "total bytes: " << object_sizes << ", "
- << "class descriptor: '" << descriptor << "'"
- << ")\n";
-
- if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
- // TODO: templatize this to avoid the awful casts down to uint8_t* and back.
- auto& art_method_false_dirty_objects = class_data[klass].false_dirty_objects;
-
- os << " field contents:\n";
- for (const uint8_t* uobj : art_method_false_dirty_objects) {
- auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(uobj));
- // local method
- auto art_method = reinterpret_cast<ArtMethod*>(obj);
-
- // local class
- mirror::Class* declaring_class = art_method->GetDeclaringClass();
-
- os << " " << reinterpret_cast<const void*>(obj) << " ";
- os << " entryPointFromJni: "
- << reinterpret_cast<const void*>(
- art_method->GetDataPtrSize(pointer_size_)) << ", ";
- os << " entryPointFromQuickCompiledCode: "
- << reinterpret_cast<const void*>(
- art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_))
- << ", ";
- os << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", ";
- os << " class_status (local): " << declaring_class->GetStatus();
- os << "\n";
- }
- }
- }
-
- os << "\n" << " Clean object count by class:\n";
- for (const auto& vk_pair : clean_object_class_values) {
- os << " " << mirror::Class::PrettyClass(vk_pair.second) << " (" << vk_pair.first << ")\n";
- }
+ object_region_data.ProcessRegion(mapping_data,
+ remotes,
+ image_begin_unaligned,
+ image_mirror_end_unaligned);
return true;
}
- // Fixup a remote pointer that we read from a foreign boot.art to point to our own memory.
- // Returned pointer will point to inside of remote_contents.
- template <typename T>
- static T* FixUpRemotePointer(T* remote_ptr,
- std::vector<uint8_t>& remote_contents,
- const backtrace_map_t& boot_map) {
- if (remote_ptr == nullptr) {
- return nullptr;
- }
-
- uintptr_t remote = reinterpret_cast<uintptr_t>(remote_ptr);
-
- CHECK_LE(boot_map.start, remote);
- CHECK_GT(boot_map.end, remote);
-
- off_t boot_offset = remote - boot_map.start;
-
- return reinterpret_cast<T*>(&remote_contents[boot_offset]);
- }
-
- template <typename T>
- static T* RemoteContentsPointerToLocal(T* remote_ptr,
- std::vector<uint8_t>& remote_contents,
- const ImageHeader& image_header) {
- if (remote_ptr == nullptr) {
- return nullptr;
- }
-
- uint8_t* remote = reinterpret_cast<uint8_t*>(remote_ptr);
- ptrdiff_t boot_offset = remote - &remote_contents[0];
-
- const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + boot_offset;
-
- return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr));
- }
-
- static std::string GetClassDescriptor(mirror::Class* klass)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK(klass != nullptr);
-
- std::string descriptor;
- const char* descriptor_str = klass->GetDescriptor(&descriptor);
-
- return std::string(descriptor_str);
- }
-
- template <typename K, typename V, typename D>
- static std::vector<std::pair<V, K>> SortByValueDesc(
- const std::map<K, D> map,
- std::function<V(const D&)> value_mapper = [](const D& d) { return static_cast<V>(d); }) {
- // Store value->key so that we can use the default sort from pair which
- // sorts by value first and then key
- std::vector<std::pair<V, K>> value_key_vector;
-
- for (const auto& kv_pair : map) {
- value_key_vector.push_back(std::make_pair(value_mapper(kv_pair.second), kv_pair.first));
- }
-
- // Sort in reverse (descending order)
- std::sort(value_key_vector.rbegin(), value_key_vector.rend());
- return value_key_vector;
- }
-
static bool GetPageFrameNumber(File* page_map_file,
size_t virtual_page_index,
uint64_t* page_frame_number,
@@ -1140,10 +1398,9 @@
const std::string image_location_;
pid_t image_diff_pid_; // Dump image diff against boot.art if pid is non-negative
pid_t zygote_diff_pid_; // Dump image diff against zygote boot.art if pid is non-negative
+ bool dump_dirty_objects_; // Adds dumping of objects that are dirty.
bool zygote_pid_only_; // The user only specified a pid for the zygote.
- // Pointer size constant for object fields, etc.
- PointerSize pointer_size_;
// BacktraceMap used for finding the memory mapping of the image file.
std::unique_ptr<BacktraceMap> proc_maps_;
// Boot image mapping.
@@ -1169,7 +1426,8 @@
static int DumpImage(Runtime* runtime,
std::ostream* os,
pid_t image_diff_pid,
- pid_t zygote_diff_pid) {
+ pid_t zygote_diff_pid,
+ bool dump_dirty_objects) {
ScopedObjectAccess soa(Thread::Current());
gc::Heap* heap = runtime->GetHeap();
std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
@@ -1185,7 +1443,8 @@
image_header,
image_space->GetImageLocation(),
image_diff_pid,
- zygote_diff_pid);
+ zygote_diff_pid,
+ dump_dirty_objects);
if (!img_diag_dumper.Init()) {
return EXIT_FAILURE;
}
@@ -1223,6 +1482,8 @@
*error_msg = "Zygote diff pid out of range";
return kParseError;
}
+ } else if (option == "--dump-dirty-objects") {
+ dump_dirty_objects_ = true;
} else {
return kParseUnknownArgument;
}
@@ -1275,6 +1536,7 @@
" --zygote-diff-pid=<pid>: provide the PID of the zygote whose boot.art you want to diff "
"against.\n"
" Example: --zygote-diff-pid=$(pid zygote)\n"
+ " --dump-dirty-objects: additionally output dirty objects of interest.\n"
"\n";
return usage;
@@ -1283,6 +1545,7 @@
public:
pid_t image_diff_pid_ = -1;
pid_t zygote_diff_pid_ = -1;
+ bool dump_dirty_objects_ = false;
};
struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
@@ -1292,7 +1555,8 @@
return DumpImage(runtime,
args_->os_,
args_->image_diff_pid_,
- args_->zygote_diff_pid_) == EXIT_SUCCESS;
+ args_->zygote_diff_pid_,
+ args_->dump_dirty_objects_) == EXIT_SUCCESS;
}
};
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index c948d3c..3245795 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -14,21 +14,21 @@
* limitations under the License.
*/
+#include <sstream>
#include <string>
#include <vector>
-#include <sstream>
#include "common_runtime_test.h"
#include "android-base/stringprintf.h"
-#include "runtime/os.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/exec_utils.h"
-#include "runtime/utils.h"
-#include "runtime/gc/space/image_space.h"
#include "runtime/gc/heap.h"
+#include "runtime/gc/space/image_space.h"
+#include "runtime/os.h"
#include "runtime/runtime.h"
+#include "runtime/utils.h"
#include <sys/types.h>
#include <unistd.h>
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 0a95d49..f8b1f53 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -89,6 +89,8 @@
"kSaveRefsOnlyMethod",
"kSaveRefsAndArgsMethod",
"kSaveEverythingMethod",
+ "kSaveEverythingMethodForClinit",
+ "kSaveEverythingMethodForSuspendCheck",
};
const char* image_roots_descriptions_[] = {
@@ -2233,16 +2235,15 @@
if (num_methods != 0u) {
os << "Methods (size=" << num_methods << "):\n";
ScopedIndentation indent2(&vios_);
- auto* resolved_methods = dex_cache->GetResolvedMethods();
+ mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) {
- auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods,
- i,
- image_pointer_size);
+ ArtMethod* elem = mirror::DexCache::GetNativePairPtrSize(
+ resolved_methods, i, image_pointer_size).object;
size_t run = 0;
for (size_t j = i + 1;
- j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_methods,
- j,
- image_pointer_size);
+ j != length &&
+ elem == mirror::DexCache::GetNativePairPtrSize(
+ resolved_methods, j, image_pointer_size).object;
++j) {
++run;
}
@@ -2270,7 +2271,7 @@
ScopedIndentation indent2(&vios_);
auto* resolved_fields = dex_cache->GetResolvedFields();
for (size_t i = 0, length = dex_cache->NumResolvedFields(); i < length; ++i) {
- auto* elem = mirror::DexCache::GetNativePairPtrSize(
+ ArtField* elem = mirror::DexCache::GetNativePairPtrSize(
resolved_fields, i, image_pointer_size).object;
size_t run = 0;
for (size_t j = i + 1;
diff --git a/runtime/openjdkjvm/Android.bp b/openjdkjvm/Android.bp
similarity index 96%
rename from runtime/openjdkjvm/Android.bp
rename to openjdkjvm/Android.bp
index 37112b6..071b434 100644
--- a/runtime/openjdkjvm/Android.bp
+++ b/openjdkjvm/Android.bp
@@ -18,7 +18,6 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: ["OpenjdkJvm.cc"],
- include_dirs: ["art/runtime"],
shared_libs: [
"libbase",
"libnativehelper"
diff --git a/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
similarity index 100%
rename from runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
rename to openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
diff --git a/runtime/openjdkjvm/NOTICE b/openjdkjvm/NOTICE
similarity index 100%
rename from runtime/openjdkjvm/NOTICE
rename to openjdkjvm/NOTICE
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc
similarity index 99%
rename from runtime/openjdkjvm/OpenjdkJvm.cc
rename to openjdkjvm/OpenjdkJvm.cc
index c1b2636..b212ea1 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/openjdkjvm/OpenjdkJvm.cc
@@ -32,12 +32,12 @@
/*
* Services that OpenJDK expects the VM to provide.
*/
-#include<stdio.h>
#include <dlfcn.h>
#include <limits.h>
-#include <sys/time.h>
-#include <sys/socket.h>
+#include <stdio.h>
#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
#include <unistd.h>
#include "../../libcore/ojluni/src/main/native/jvm.h" // TODO(narayan): fix it
diff --git a/runtime/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
similarity index 97%
rename from runtime/openjdkjvmti/Android.bp
rename to openjdkjvmti/Android.bp
index aec1bd0..b6b1b56 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -48,7 +48,6 @@
"ti_threadgroup.cc",
"ti_timers.cc",
"transform.cc"],
- include_dirs: ["art/runtime"],
header_libs: ["libopenjdkjvmti_headers"],
shared_libs: [
"libbase",
diff --git a/runtime/openjdkjvmti/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/openjdkjvmti/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
similarity index 100%
rename from runtime/openjdkjvmti/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
rename to openjdkjvmti/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
diff --git a/runtime/openjdkjvmti/NOTICE b/openjdkjvmti/NOTICE
similarity index 100%
rename from runtime/openjdkjvmti/NOTICE
rename to openjdkjvmti/NOTICE
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
similarity index 95%
rename from runtime/openjdkjvmti/OpenjdkJvmTi.cc
rename to openjdkjvmti/OpenjdkJvmTi.cc
index 3c1311b..af77072 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -503,112 +503,112 @@
}
static jvmtiError GetLocalObject(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jobject* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalInstance(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jobject* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jobject* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalInstance(env, thread, depth, value_ptr);
}
static jvmtiError GetLocalInt(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jint* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalLong(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jlong* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalFloat(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jfloat* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalDouble(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jdouble* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError SetLocalObject(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jobject value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalInt(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jint value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalLong(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jlong value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalFloat(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jfloat value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalDouble(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jdouble value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
@@ -904,12 +904,12 @@
}
static jvmtiError GetLocalVariableTable(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jint* entry_count_ptr ATTRIBUTE_UNUSED,
- jvmtiLocalVariableEntry** table_ptr ATTRIBUTE_UNUSED) {
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariableTable(env, method, entry_count_ptr, table_ptr);
}
static jvmtiError GetBytecodes(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/README.md b/openjdkjvmti/README.md
similarity index 100%
rename from runtime/openjdkjvmti/README.md
rename to openjdkjvmti/README.md
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
similarity index 96%
rename from runtime/openjdkjvmti/art_jvmti.h
rename to openjdkjvmti/art_jvmti.h
index 4d5bb95..12f4cab 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_ART_JVMTI_H_
-#define ART_RUNTIME_OPENJDKJVMTI_ART_JVMTI_H_
+#ifndef ART_OPENJDKJVMTI_ART_JVMTI_H_
+#define ART_OPENJDKJVMTI_ART_JVMTI_H_
#include <memory>
#include <type_traits>
@@ -204,6 +204,10 @@
ALWAYS_INLINE
static inline JvmtiUniquePtr<char[]> CopyString(jvmtiEnv* env, const char* src, jvmtiError* error) {
+ if (src == nullptr) {
+ JvmtiUniquePtr<char[]> ret = AllocJvmtiUniquePtr<char[]>(env, 0, error);
+ return ret;
+ }
size_t len = strlen(src) + 1;
JvmtiUniquePtr<char[]> ret = AllocJvmtiUniquePtr<char[]>(env, len, error);
if (ret != nullptr) {
@@ -227,8 +231,8 @@
.can_get_source_file_name = 1,
.can_get_line_numbers = 1,
.can_get_source_debug_extension = 1,
- .can_access_local_variables = 0,
- .can_maintain_original_method_order = 0,
+ .can_access_local_variables = 1,
+ .can_maintain_original_method_order = 1,
.can_generate_single_step_events = 1,
.can_generate_exception_events = 0,
.can_generate_frame_pop_events = 0,
@@ -258,4 +262,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_ART_JVMTI_H_
+#endif // ART_OPENJDKJVMTI_ART_JVMTI_H_
diff --git a/runtime/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
similarity index 97%
rename from runtime/openjdkjvmti/events-inl.h
rename to openjdkjvmti/events-inl.h
index 43177ab..32dba3e 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
-#define ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
+#ifndef ART_OPENJDKJVMTI_EVENTS_INL_H_
+#define ART_OPENJDKJVMTI_EVENTS_INL_H_
#include <array>
@@ -414,9 +414,10 @@
bool added) {
ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
: ArtJvmtiEvent::kClassFileLoadHookRetransformable;
- return caps.can_retransform_classes == 1 &&
- IsEventEnabledAnywhere(event) &&
- env->event_masks.IsEnabledAnywhere(event);
+ return (added && caps.can_access_local_variables == 1) ||
+ (caps.can_retransform_classes == 1 &&
+ IsEventEnabledAnywhere(event) &&
+ env->event_masks.IsEnabledAnywhere(event));
}
inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
@@ -428,9 +429,12 @@
RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
}
+ if (added && caps.can_access_local_variables == 1) {
+ HandleLocalAccessCapabilityAdded();
+ }
}
}
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
+#endif // ART_OPENJDKJVMTI_EVENTS_INL_H_
diff --git a/runtime/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
similarity index 96%
rename from runtime/openjdkjvmti/events.cc
rename to openjdkjvmti/events.cc
index 7a930d4..2944a45 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -610,6 +610,21 @@
}
}
+void EventHandler::HandleLocalAccessCapabilityAdded() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative);
+ art::instrumentation::Instrumentation* instr = art::Runtime::Current()->GetInstrumentation();
+ art::gc::ScopedGCCriticalSection gcs(art::Thread::Current(),
+ art::gc::kGcCauseInstrumentation,
+ art::gc::kCollectorTypeInstrumentation);
+ art::ScopedSuspendAll ssa("Deoptimize everything for local variable access", true);
+ // TODO This should be disabled when there are no environments using it.
+ if (!instr->CanDeoptimize()) {
+ instr->EnableDeoptimization();
+ }
+ // TODO We should be able to support can_access_local_variables without this.
+ instr->DeoptimizeEverything("jvmti-local-variable-access");
+}
+
// Handle special work for the given event type, if necessary.
void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
switch (event) {
diff --git a/runtime/openjdkjvmti/events.h b/openjdkjvmti/events.h
similarity index 98%
rename from runtime/openjdkjvmti/events.h
rename to openjdkjvmti/events.h
index 5f37dcf..3d05fa1 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_H_
-#define ART_RUNTIME_OPENJDKJVMTI_EVENTS_H_
+#ifndef ART_OPENJDKJVMTI_EVENTS_H_
+#define ART_OPENJDKJVMTI_EVENTS_H_
#include <bitset>
#include <vector>
@@ -210,6 +210,7 @@
unsigned char** new_class_data) const;
void HandleEventType(ArtJvmtiEvent event, bool enable);
+ void HandleLocalAccessCapabilityAdded();
// List of all JvmTiEnv objects that have been created, in their creation order.
// NB Some elements might be null representing envs that have been deleted. They should be skipped
@@ -226,4 +227,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_H_
+#endif // ART_OPENJDKJVMTI_EVENTS_H_
diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
similarity index 100%
rename from runtime/openjdkjvmti/fixed_up_dex_file.cc
rename to openjdkjvmti/fixed_up_dex_file.cc
diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.h b/openjdkjvmti/fixed_up_dex_file.h
similarity index 94%
rename from runtime/openjdkjvmti/fixed_up_dex_file.h
rename to openjdkjvmti/fixed_up_dex_file.h
index db12f48..4cb39cf 100644
--- a/runtime/openjdkjvmti/fixed_up_dex_file.h
+++ b/openjdkjvmti/fixed_up_dex_file.h
@@ -29,14 +29,15 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
-#define ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
+#ifndef ART_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
+#define ART_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
#include <memory>
#include <vector>
#include "jni.h"
#include "jvmti.h"
+
#include "base/mutex.h"
#include "dex_file.h"
@@ -79,4 +80,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
+#endif // ART_OPENJDKJVMTI_FIXED_UP_DEX_FILE_H_
diff --git a/runtime/openjdkjvmti/include/jvmti.h b/openjdkjvmti/include/jvmti.h
similarity index 100%
rename from runtime/openjdkjvmti/include/jvmti.h
rename to openjdkjvmti/include/jvmti.h
diff --git a/runtime/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h
similarity index 96%
rename from runtime/openjdkjvmti/jvmti_allocator.h
rename to openjdkjvmti/jvmti_allocator.h
index 44b1cb1..e29e034 100644
--- a/runtime/openjdkjvmti/jvmti_allocator.h
+++ b/openjdkjvmti/jvmti_allocator.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
-#define ART_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
+#ifndef ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
+#define ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
#include "base/logging.h"
#include "base/macros.h"
@@ -171,4 +171,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
+#endif // ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
diff --git a/runtime/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h
similarity index 98%
rename from runtime/openjdkjvmti/jvmti_weak_table-inl.h
rename to openjdkjvmti/jvmti_weak_table-inl.h
index a640acb..1c82255 100644
--- a/runtime/openjdkjvmti/jvmti_weak_table-inl.h
+++ b/openjdkjvmti/jvmti_weak_table-inl.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_
-#define ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_
+#ifndef ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_
+#define ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_
#include "jvmti_weak_table.h"
@@ -403,4 +403,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_
+#endif // ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_
diff --git a/runtime/openjdkjvmti/jvmti_weak_table.h b/openjdkjvmti/jvmti_weak_table.h
similarity index 97%
rename from runtime/openjdkjvmti/jvmti_weak_table.h
rename to openjdkjvmti/jvmti_weak_table.h
index a5175a4..5a821c9 100644
--- a/runtime/openjdkjvmti/jvmti_weak_table.h
+++ b/openjdkjvmti/jvmti_weak_table.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_
-#define ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_
+#ifndef ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_
+#define ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_
#include <unordered_map>
@@ -224,4 +224,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_
+#endif // ART_OPENJDKJVMTI_JVMTI_WEAK_TABLE_H_
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc
similarity index 100%
rename from runtime/openjdkjvmti/object_tagging.cc
rename to openjdkjvmti/object_tagging.cc
diff --git a/runtime/openjdkjvmti/object_tagging.h b/openjdkjvmti/object_tagging.h
similarity index 94%
rename from runtime/openjdkjvmti/object_tagging.h
rename to openjdkjvmti/object_tagging.h
index ca84e44..b474845 100644
--- a/runtime/openjdkjvmti/object_tagging.h
+++ b/openjdkjvmti/object_tagging.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_
-#define ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_
+#ifndef ART_OPENJDKJVMTI_OBJECT_TAGGING_H_
+#define ART_OPENJDKJVMTI_OBJECT_TAGGING_H_
#include <unordered_map>
@@ -83,4 +83,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_
+#endif // ART_OPENJDKJVMTI_OBJECT_TAGGING_H_
diff --git a/runtime/openjdkjvmti/ti_allocator.cc b/openjdkjvmti/ti_allocator.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_allocator.cc
rename to openjdkjvmti/ti_allocator.cc
diff --git a/runtime/openjdkjvmti/ti_allocator.h b/openjdkjvmti/ti_allocator.h
similarity index 93%
rename from runtime/openjdkjvmti/ti_allocator.h
rename to openjdkjvmti/ti_allocator.h
index 35575c3..776cc5e 100644
--- a/runtime/openjdkjvmti/ti_allocator.h
+++ b/openjdkjvmti/ti_allocator.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_ALLOCATOR_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_ALLOCATOR_H_
+#ifndef ART_OPENJDKJVMTI_TI_ALLOCATOR_H_
+#define ART_OPENJDKJVMTI_TI_ALLOCATOR_H_
#include "jni.h"
#include "jvmti.h"
@@ -61,5 +61,5 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_ALLOCATOR_H_
+#endif // ART_OPENJDKJVMTI_TI_ALLOCATOR_H_
diff --git a/runtime/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_breakpoint.cc
rename to openjdkjvmti/ti_breakpoint.cc
diff --git a/runtime/openjdkjvmti/ti_breakpoint.h b/openjdkjvmti/ti_breakpoint.h
similarity index 94%
rename from runtime/openjdkjvmti/ti_breakpoint.h
rename to openjdkjvmti/ti_breakpoint.h
index c3dbef7..9b08b42 100644
--- a/runtime/openjdkjvmti/ti_breakpoint.h
+++ b/openjdkjvmti/ti_breakpoint.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+#ifndef ART_OPENJDKJVMTI_TI_BREAKPOINT_H_
+#define ART_OPENJDKJVMTI_TI_BREAKPOINT_H_
#include "jni.h"
#include "jvmti.h"
@@ -91,4 +91,4 @@
};
} // namespace std
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+#endif // ART_OPENJDKJVMTI_TI_BREAKPOINT_H_
diff --git a/runtime/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_class.cc
rename to openjdkjvmti/ti_class.cc
diff --git a/runtime/openjdkjvmti/ti_class.h b/openjdkjvmti/ti_class.h
similarity index 96%
rename from runtime/openjdkjvmti/ti_class.h
rename to openjdkjvmti/ti_class.h
index 7bb6b3e..dd99e36 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/openjdkjvmti/ti_class.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
+#ifndef ART_OPENJDKJVMTI_TI_CLASS_H_
+#define ART_OPENJDKJVMTI_TI_CLASS_H_
#include "jni.h"
#include "jvmti.h"
@@ -92,4 +92,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
+#endif // ART_OPENJDKJVMTI_TI_CLASS_H_
diff --git a/runtime/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
similarity index 99%
rename from runtime/openjdkjvmti/ti_class_definition.cc
rename to openjdkjvmti/ti_class_definition.cc
index 8e8ab19..c73ef0d 100644
--- a/runtime/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -35,10 +35,10 @@
#include "class_linker-inl.h"
#include "dex_file.h"
#include "fixed_up_dex_file.h"
-#include "handle_scope-inl.h"
#include "handle.h"
-#include "mirror/class_ext.h"
+#include "handle_scope-inl.h"
#include "mirror/class-inl.h"
+#include "mirror/class_ext.h"
#include "mirror/object-inl.h"
#include "reflection.h"
#include "thread.h"
diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h
similarity index 95%
rename from runtime/openjdkjvmti/ti_class_definition.h
rename to openjdkjvmti/ti_class_definition.h
index 2c268dd..accc456 100644
--- a/runtime/openjdkjvmti/ti_class_definition.h
+++ b/openjdkjvmti/ti_class_definition.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
+#ifndef ART_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
+#define ART_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
#include "art_jvmti.h"
@@ -128,4 +128,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
+#endif // ART_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_class_loader.cc
rename to openjdkjvmti/ti_class_loader.cc
diff --git a/runtime/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h
similarity index 95%
rename from runtime/openjdkjvmti/ti_class_loader.h
rename to openjdkjvmti/ti_class_loader.h
index 1ac4988..767e258 100644
--- a/runtime/openjdkjvmti/ti_class_loader.h
+++ b/openjdkjvmti/ti_class_loader.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_
+#ifndef ART_OPENJDKJVMTI_TI_CLASS_LOADER_H_
+#define ART_OPENJDKJVMTI_TI_CLASS_LOADER_H_
#include <string>
@@ -57,8 +57,8 @@
#include "obj_ptr.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
-#include "ti_class_definition.h"
#include "thread_list.h"
+#include "ti_class_definition.h"
#include "transform.h"
#include "utf.h"
#include "utils/dex_cache_arrays_layout-inl.h"
@@ -96,4 +96,4 @@
};
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_
+#endif // ART_OPENJDKJVMTI_TI_CLASS_LOADER_H_
diff --git a/runtime/openjdkjvmti/ti_dump.cc b/openjdkjvmti/ti_dump.cc
similarity index 99%
rename from runtime/openjdkjvmti/ti_dump.cc
rename to openjdkjvmti/ti_dump.cc
index 7a1e53f..809a5e4 100644
--- a/runtime/openjdkjvmti/ti_dump.cc
+++ b/openjdkjvmti/ti_dump.cc
@@ -33,7 +33,6 @@
#include <limits>
-
#include "art_jvmti.h"
#include "base/mutex.h"
#include "events-inl.h"
diff --git a/runtime/openjdkjvmti/ti_dump.h b/openjdkjvmti/ti_dump.h
similarity index 92%
rename from runtime/openjdkjvmti/ti_dump.h
rename to openjdkjvmti/ti_dump.h
index 67cb239..323bf56 100644
--- a/runtime/openjdkjvmti/ti_dump.h
+++ b/openjdkjvmti/ti_dump.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_
+#ifndef ART_OPENJDKJVMTI_TI_DUMP_H_
+#define ART_OPENJDKJVMTI_TI_DUMP_H_
#include "jni.h"
#include "jvmti.h"
@@ -47,4 +47,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_
+#endif // ART_OPENJDKJVMTI_TI_DUMP_H_
diff --git a/runtime/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc
similarity index 99%
rename from runtime/openjdkjvmti/ti_field.cc
rename to openjdkjvmti/ti_field.cc
index 32c064e..c45b926 100644
--- a/runtime/openjdkjvmti/ti_field.cc
+++ b/openjdkjvmti/ti_field.cc
@@ -31,8 +31,8 @@
#include "ti_field.h"
-#include "art_jvmti.h"
#include "art_field-inl.h"
+#include "art_jvmti.h"
#include "base/enums.h"
#include "dex_file_annotations.h"
#include "jni_internal.h"
diff --git a/runtime/openjdkjvmti/ti_field.h b/openjdkjvmti/ti_field.h
similarity index 95%
rename from runtime/openjdkjvmti/ti_field.h
rename to openjdkjvmti/ti_field.h
index 880949e..8a229ed 100644
--- a/runtime/openjdkjvmti/ti_field.h
+++ b/openjdkjvmti/ti_field.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
+#ifndef ART_OPENJDKJVMTI_TI_FIELD_H_
+#define ART_OPENJDKJVMTI_TI_FIELD_H_
#include "jni.h"
#include "jvmti.h"
@@ -69,4 +69,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
+#endif // ART_OPENJDKJVMTI_TI_FIELD_H_
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
similarity index 99%
rename from runtime/openjdkjvmti/ti_heap.cc
rename to openjdkjvmti/ti_heap.cc
index 91fdaca..3397210 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -21,8 +21,8 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "class_linker.h"
-#include "gc/heap.h"
#include "gc/heap-visit-objects-inl.h"
+#include "gc/heap.h"
#include "gc_root-inl.h"
#include "java_frame_root_info.h"
#include "jni_env_ext.h"
@@ -31,8 +31,8 @@
#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
-#include "object_tagging.h"
#include "obj_ptr-inl.h"
+#include "object_tagging.h"
#include "primitive.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/runtime/openjdkjvmti/ti_heap.h b/openjdkjvmti/ti_heap.h
similarity index 94%
rename from runtime/openjdkjvmti/ti_heap.h
rename to openjdkjvmti/ti_heap.h
index 0c973db..62761b5 100644
--- a/runtime/openjdkjvmti/ti_heap.h
+++ b/openjdkjvmti/ti_heap.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
+#ifndef ART_OPENJDKJVMTI_TI_HEAP_H_
+#define ART_OPENJDKJVMTI_TI_HEAP_H_
#include "jvmti.h"
@@ -70,4 +70,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
+#endif // ART_OPENJDKJVMTI_TI_HEAP_H_
diff --git a/runtime/openjdkjvmti/ti_jni.cc b/openjdkjvmti/ti_jni.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_jni.cc
rename to openjdkjvmti/ti_jni.cc
diff --git a/runtime/openjdkjvmti/ti_jni.h b/openjdkjvmti/ti_jni.h
similarity index 94%
rename from runtime/openjdkjvmti/ti_jni.h
rename to openjdkjvmti/ti_jni.h
index 906aab0..590fd54 100644
--- a/runtime/openjdkjvmti/ti_jni.h
+++ b/openjdkjvmti/ti_jni.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+#ifndef ART_OPENJDKJVMTI_TI_JNI_H_
+#define ART_OPENJDKJVMTI_TI_JNI_H_
#include "jni.h"
#include "jvmti.h"
@@ -55,4 +55,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+#endif // ART_OPENJDKJVMTI_TI_JNI_H_
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
new file mode 100644
index 0000000..8f72714
--- /dev/null
+++ b/openjdkjvmti/ti_method.cc
@@ -0,0 +1,1071 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_method.h"
+
+#include "art_jvmti.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "base/mutex-inl.h"
+#include "dex_file_annotations.h"
+#include "events-inl.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "modifiers.h"
+#include "nativehelper/ScopedLocalRef.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
+#include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_thread.h"
+#include "ti_phase.h"
+
+namespace openjdkjvmti {
+
+struct TiMethodCallback : public art::MethodCallback {
+ void RegisterNativeMethod(art::ArtMethod* method,
+ const void* cur_method,
+ /*out*/void** new_method)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) {
+ art::Thread* thread = art::Thread::Current();
+ art::JNIEnvExt* jnienv = thread->GetJniEnv();
+ ScopedLocalRef<jthread> thread_jni(
+ jnienv, PhaseUtil::IsLivePhase() ? jnienv->AddLocalReference<jthread>(thread->GetPeer())
+ : nullptr);
+ art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(
+ thread,
+ static_cast<JNIEnv*>(jnienv),
+ thread_jni.get(),
+ art::jni::EncodeArtMethod(method),
+ const_cast<void*>(cur_method),
+ new_method);
+ }
+ }
+
+ EventHandler* event_handler = nullptr;
+};
+
+TiMethodCallback gMethodCallback;
+
+void MethodUtil::Register(EventHandler* handler) {
+ gMethodCallback.event_handler = handler;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add method callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback);
+}
+
+void MethodUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove method callback");
+ art::Runtime* runtime = art::Runtime::Current();
+ runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
+}
+
+jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
+ jmethodID method,
+ jint* size_ptr,
+ unsigned char** bytecode_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (size_ptr == nullptr || bytecode_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+ if (code_item == nullptr) {
+ *size_ptr = 0;
+ *bytecode_ptr = nullptr;
+ return OK;
+ }
+ // 2 bytes per instruction for dex code.
+ *size_ptr = code_item->insns_size_in_code_units_ * 2;
+ jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
+ if (err != OK) {
+ return err;
+ }
+ memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
+ return OK;
+}
+
+jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jint* size_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (size_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+ // Use the shorty.
+ art::ArtMethod* base_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
+ size_t arg_count = art::ArtMethod::NumArgRegisters(base_method->GetShorty());
+ if (!base_method->IsStatic()) {
+ arg_count++;
+ }
+ *size_ptr = static_cast<jint>(arg_count);
+ return ERR(NONE);
+ }
+
+ DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+ *size_ptr = art_method->GetCodeItem()->ins_size_;
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (entry_count_ptr == nullptr || table_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ const art::DexFile* dex_file = art_method->GetDexFile();
+ const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+ // TODO code_item == nullptr means that the method is abstract (or native, but we check that
+ // earlier). We should check what is returned by the RI in this situation since it's not clear
+ // what the appropriate return value is from the spec.
+ if (dex_file == nullptr || code_item == nullptr) {
+ return ERR(ABSENT_INFORMATION);
+ }
+
+ struct LocalVariableContext {
+ explicit LocalVariableContext(jvmtiEnv* jenv) : env_(jenv), variables_(), err_(OK) {}
+
+ static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
+ reinterpret_cast<LocalVariableContext*>(raw_ctx)->Insert(entry);
+ }
+
+ void Insert(const art::DexFile::LocalInfo& entry) {
+ if (err_ != OK) {
+ return;
+ }
+ JvmtiUniquePtr<char[]> name_str = CopyString(env_, entry.name_, &err_);
+ if (err_ != OK) {
+ return;
+ }
+ JvmtiUniquePtr<char[]> sig_str = CopyString(env_, entry.descriptor_, &err_);
+ if (err_ != OK) {
+ return;
+ }
+ JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env_, entry.signature_, &err_);
+ if (err_ != OK) {
+ return;
+ }
+ variables_.push_back({
+ .start_location = static_cast<jlocation>(entry.start_address_),
+ .length = static_cast<jint>(entry.end_address_ - entry.start_address_),
+ .name = name_str.release(),
+ .signature = sig_str.release(),
+ .generic_signature = generic_sig_str.release(),
+ .slot = entry.reg_,
+ });
+ }
+
+ jvmtiError Release(jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) {
+ jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables_.size();
+ if (err_ != OK ||
+ (err_ = env_->Allocate(table_size,
+ reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) {
+ Cleanup();
+ return err_;
+ } else {
+ *out_entry_count_ptr = variables_.size();
+ memcpy(*out_table_ptr, variables_.data(), table_size);
+ return OK;
+ }
+ }
+
+ void Cleanup() {
+ for (jvmtiLocalVariableEntry& e : variables_) {
+ env_->Deallocate(reinterpret_cast<unsigned char*>(e.name));
+ env_->Deallocate(reinterpret_cast<unsigned char*>(e.signature));
+ env_->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature));
+ }
+ }
+
+ jvmtiEnv* env_;
+ std::vector<jvmtiLocalVariableEntry> variables_;
+ jvmtiError err_;
+ };
+
+ LocalVariableContext context(env);
+ if (!dex_file->DecodeDebugLocalInfo(code_item,
+ art_method->IsStatic(),
+ art_method->GetDexMethodIndex(),
+ LocalVariableContext::Callback,
+ &context)) {
+ // Something went wrong with decoding the debug information. It might as well not be there.
+ return ERR(ABSENT_INFORMATION);
+ } else {
+ return context.Release(entry_count_ptr, table_ptr);
+ }
+}
+
+jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jint* max_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (max_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+ // This isn't specified as an error case, so return 0.
+ *max_ptr = 0;
+ return ERR(NONE);
+ }
+
+ DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+ *max_ptr = art_method->GetCodeItem()->registers_size_;
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
+ jmethodID method,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+ art_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
+
+ JvmtiUniquePtr<char[]> name_copy;
+ if (name_ptr != nullptr) {
+ const char* method_name = art_method->GetName();
+ if (method_name == nullptr) {
+ method_name = "<error>";
+ }
+ jvmtiError ret;
+ name_copy = CopyString(env, method_name, &ret);
+ if (name_copy == nullptr) {
+ return ret;
+ }
+ *name_ptr = name_copy.get();
+ }
+
+ JvmtiUniquePtr<char[]> signature_copy;
+ if (signature_ptr != nullptr) {
+ const art::Signature sig = art_method->GetSignature();
+ std::string str = sig.ToString();
+ jvmtiError ret;
+ signature_copy = CopyString(env, str.c_str(), &ret);
+ if (signature_copy == nullptr) {
+ return ret;
+ }
+ *signature_ptr = signature_copy.get();
+ }
+
+ if (generic_ptr != nullptr) {
+ *generic_ptr = nullptr;
+ if (!art_method->GetDeclaringClass()->IsProxyClass()) {
+ art::mirror::ObjectArray<art::mirror::String>* str_array =
+ art::annotations::GetSignatureAnnotationForMethod(art_method);
+ if (str_array != nullptr) {
+ std::ostringstream oss;
+ for (int32_t i = 0; i != str_array->GetLength(); ++i) {
+ oss << str_array->Get(i)->ToModifiedUtf8();
+ }
+ std::string output_string = oss.str();
+ jvmtiError ret;
+ JvmtiUniquePtr<char[]> generic_copy = CopyString(env, output_string.c_str(), &ret);
+ if (generic_copy == nullptr) {
+ return ret;
+ }
+ *generic_ptr = generic_copy.release();
+ } else if (soa.Self()->IsExceptionPending()) {
+ // TODO: Should we report an error here?
+ soa.Self()->ClearException();
+ }
+ }
+ }
+
+ // Everything is fine, release the buffers.
+ name_copy.release();
+ signature_copy.release();
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMethodDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jclass* declaring_class_ptr) {
+ if (declaring_class_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+ // Note: No GetInterfaceMethodIfProxy, we want to actual class.
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::mirror::Class* klass = art_method->GetDeclaringClass();
+ *declaring_class_ptr = soa.AddLocalReference<jclass>(klass);
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jlocation* start_location_ptr,
+ jlocation* end_location_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (start_location_ptr == nullptr || end_location_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
+ // This isn't specified as an error case, so return -1/-1 as the RI does.
+ *start_location_ptr = -1;
+ *end_location_ptr = -1;
+ return ERR(NONE);
+ }
+
+ DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
+ *start_location_ptr = 0;
+ *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1;
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jint* modifiers_ptr) {
+ if (modifiers_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+ uint32_t modifiers = art_method->GetAccessFlags();
+
+ // Note: Keep this code in sync with Executable.fixMethodFlags.
+ if ((modifiers & art::kAccAbstract) != 0) {
+ modifiers &= ~art::kAccNative;
+ }
+ modifiers &= ~art::kAccSynchronized;
+ if ((modifiers & art::kAccDeclaredSynchronized) != 0) {
+ modifiers |= art::kAccSynchronized;
+ }
+ modifiers &= art::kAccJavaFlagsMask;
+
+ *modifiers_ptr = modifiers;
+ return ERR(NONE);
+}
+
+using LineNumberContext = std::vector<jvmtiLineNumberEntry>;
+
+static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) {
+ LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context);
+ jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_),
+ static_cast<jint>(entry.line_) };
+ context->push_back(jvmti_entry);
+ return false; // Collect all, no early exit.
+}
+
+jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr) {
+ if (method == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+ DCHECK(!art_method->IsRuntimeMethod());
+
+ const art::DexFile::CodeItem* code_item;
+ const art::DexFile* dex_file;
+ {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+
+ if (art_method->IsProxyMethod()) {
+ return ERR(ABSENT_INFORMATION);
+ }
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+ if (entry_count_ptr == nullptr || table_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ code_item = art_method->GetCodeItem();
+ dex_file = art_method->GetDexFile();
+ DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
+ }
+
+ LineNumberContext context;
+ bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
+ if (!success) {
+ return ERR(ABSENT_INFORMATION);
+ }
+
+ unsigned char* data;
+ jlong mem_size = context.size() * sizeof(jvmtiLineNumberEntry);
+ jvmtiError alloc_error = env->Allocate(mem_size, &data);
+ if (alloc_error != ERR(NONE)) {
+ return alloc_error;
+ }
+ *table_ptr = reinterpret_cast<jvmtiLineNumberEntry*>(data);
+ memcpy(*table_ptr, context.data(), mem_size);
+ *entry_count_ptr = static_cast<jint>(context.size());
+
+ return ERR(NONE);
+}
+
+template <typename T>
+static jvmtiError IsMethodT(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ T test,
+ jboolean* is_t_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ if (is_t_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+ *is_t_ptr = test(art_method) ? JNI_TRUE : JNI_FALSE;
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::IsMethodNative(jvmtiEnv* env, jmethodID m, jboolean* is_native_ptr) {
+ auto test = [](art::ArtMethod* method) {
+ return method->IsNative();
+ };
+ return IsMethodT(env, m, test, is_native_ptr);
+}
+
+jvmtiError MethodUtil::IsMethodObsolete(jvmtiEnv* env, jmethodID m, jboolean* is_obsolete_ptr) {
+ auto test = [](art::ArtMethod* method) {
+ return method->IsObsolete();
+ };
+ return IsMethodT(env, m, test, is_obsolete_ptr);
+}
+
+jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* is_synthetic_ptr) {
+ auto test = [](art::ArtMethod* method) {
+ return method->IsSynthetic();
+ };
+ return IsMethodT(env, m, test, is_synthetic_ptr);
+}
+
+struct FindFrameAtDepthVisitor : art::StackVisitor {
+ public:
+ FindFrameAtDepthVisitor(art::Thread* target, art::Context* ctx, jint depth)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ : art::StackVisitor(target, ctx, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ found_frame_(false),
+ cnt_(0),
+ depth_(static_cast<size_t>(depth)) { }
+
+ bool FoundFrame() {
+ return found_frame_;
+ }
+
+ bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
+ if (GetMethod()->IsRuntimeMethod()) {
+ return true;
+ }
+ if (cnt_ == depth_) {
+ // We found our frame, exit.
+ found_frame_ = true;
+ return false;
+ } else {
+ cnt_++;
+ return true;
+ }
+ }
+
+ private:
+ bool found_frame_;
+ size_t cnt_;
+ size_t depth_;
+};
+
+class CommonLocalVariableClosure : public art::Closure {
+ public:
+ CommonLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot)
+ : result_(ERR(INTERNAL)), caller_(caller), depth_(depth), slot_(slot) {}
+
+ void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor visitor(self, context.get(), depth_);
+ visitor.WalkStack();
+ if (!visitor.FoundFrame()) {
+ // Must have been a bad depth.
+ result_ = ERR(NO_MORE_FRAMES);
+ return;
+ }
+ art::ArtMethod* method = visitor.GetMethod();
+ if (method->IsNative() || !visitor.IsShadowFrame()) {
+ // TODO We really should support get/set for non-shadow frames.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ } else if (method->GetCodeItem()->registers_size_ <= slot_) {
+ result_ = ERR(INVALID_SLOT);
+ return;
+ }
+ uint32_t pc = visitor.GetDexPc(/*abort_on_failure*/ false);
+ if (pc == art::DexFile::kDexNoIndex) {
+ // Cannot figure out current PC.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ }
+ std::string descriptor;
+ art::Primitive::Type slot_type = art::Primitive::kPrimVoid;
+ jvmtiError err = GetSlotType(method, pc, &descriptor, &slot_type);
+ if (err != OK) {
+ result_ = err;
+ return;
+ }
+
+ err = GetTypeError(method, slot_type, descriptor);
+ if (err != OK) {
+ result_ = err;
+ return;
+ }
+ result_ = Execute(method, visitor);
+ }
+
+ jvmtiError GetResult() const {
+ return result_;
+ }
+
+ protected:
+ virtual jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ REQUIRES(art::Locks::mutator_lock_) = 0;
+ virtual jvmtiError GetTypeError(art::ArtMethod* method,
+ art::Primitive::Type type,
+ const std::string& descriptor)
+ REQUIRES(art::Locks::mutator_lock_) = 0;
+
+ jvmtiError GetSlotType(art::ArtMethod* method,
+ uint32_t dex_pc,
+ /*out*/std::string* descriptor,
+ /*out*/art::Primitive::Type* type)
+ REQUIRES(art::Locks::mutator_lock_) {
+ const art::DexFile* dex_file = method->GetDexFile();
+ const art::DexFile::CodeItem* code_item = method->GetCodeItem();
+ if (dex_file == nullptr || code_item == nullptr) {
+ return ERR(OPAQUE_FRAME);
+ }
+
+ struct GetLocalVariableInfoContext {
+ explicit GetLocalVariableInfoContext(jint slot,
+ uint32_t pc,
+ std::string* out_descriptor,
+ art::Primitive::Type* out_type)
+ : found_(false), jslot_(slot), pc_(pc), descriptor_(out_descriptor), type_(out_type) {
+ *descriptor_ = "";
+ *type_ = art::Primitive::kPrimVoid;
+ }
+
+ static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
+ reinterpret_cast<GetLocalVariableInfoContext*>(raw_ctx)->Handle(entry);
+ }
+
+ void Handle(const art::DexFile::LocalInfo& entry) {
+ if (found_) {
+ return;
+ } else if (entry.start_address_ <= pc_ &&
+ entry.end_address_ > pc_ &&
+ entry.reg_ == jslot_) {
+ found_ = true;
+ *type_ = art::Primitive::GetType(entry.descriptor_[0]);
+ *descriptor_ = entry.descriptor_;
+ }
+ return;
+ }
+
+ bool found_;
+ jint jslot_;
+ uint32_t pc_;
+ std::string* descriptor_;
+ art::Primitive::Type* type_;
+ };
+
+ GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type);
+ if (!dex_file->DecodeDebugLocalInfo(code_item,
+ method->IsStatic(),
+ method->GetDexMethodIndex(),
+ GetLocalVariableInfoContext::Callback,
+ &context) || !context.found_) {
+ // Something went wrong with decoding the debug information. It might as well not be there.
+ return ERR(INVALID_SLOT);
+ } else {
+ return OK;
+ }
+ }
+
+ jvmtiError result_;
+ art::Thread* caller_;
+ jint depth_;
+ jint slot_;
+};
+
+class GetLocalVariableClosure : public CommonLocalVariableClosure {
+ public:
+ GetLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* val)
+ : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {}
+
+ protected:
+ jvmtiError GetTypeError(art::ArtMethod* method ATTRIBUTE_UNUSED,
+ art::Primitive::Type slot_type,
+ const std::string& descriptor ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (slot_type) {
+ case art::Primitive::kPrimByte:
+ case art::Primitive::kPrimChar:
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimShort:
+ case art::Primitive::kPrimBoolean:
+ return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimLong:
+ case art::Primitive::kPrimFloat:
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimNot:
+ return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected primitive type " << slot_type;
+ UNREACHABLE();
+ }
+ }
+
+ jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (type_) {
+ case art::Primitive::kPrimNot: {
+ uint32_t ptr_val;
+ if (!visitor.GetVReg(method,
+ static_cast<uint16_t>(slot_),
+ art::kReferenceVReg,
+ &ptr_val)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ art::ObjPtr<art::mirror::Object> obj(reinterpret_cast<art::mirror::Object*>(ptr_val));
+ val_->l = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference<jobject>(obj);
+ break;
+ }
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimFloat: {
+ if (!visitor.GetVReg(method,
+ static_cast<uint16_t>(slot_),
+ type_ == art::Primitive::kPrimFloat ? art::kFloatVReg : art::kIntVReg,
+ reinterpret_cast<uint32_t*>(&val_->i))) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimLong: {
+ auto lo_type = type_ == art::Primitive::kPrimLong ? art::kLongLoVReg : art::kDoubleLoVReg;
+ auto high_type = type_ == art::Primitive::kPrimLong ? art::kLongHiVReg : art::kDoubleHiVReg;
+ if (!visitor.GetVRegPair(method,
+ static_cast<uint16_t>(slot_),
+ lo_type,
+ high_type,
+ reinterpret_cast<uint64_t*>(&val_->j))) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ default: {
+ LOG(FATAL) << "unexpected register type " << type_;
+ UNREACHABLE();
+ }
+ }
+ return OK;
+ }
+
+ private:
+ art::Primitive::Type type_;
+ jvalue* val_;
+};
+
+jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* val) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ GetLocalVariableClosure c(self, depth, slot, type, val);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+class SetLocalVariableClosure : public CommonLocalVariableClosure {
+ public:
+ SetLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue val)
+ : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {}
+
+ protected:
+ jvmtiError GetTypeError(art::ArtMethod* method,
+ art::Primitive::Type slot_type,
+ const std::string& descriptor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (slot_type) {
+ case art::Primitive::kPrimNot: {
+ if (type_ != art::Primitive::kPrimNot) {
+ return ERR(TYPE_MISMATCH);
+ } else if (val_.l == nullptr) {
+ return OK;
+ } else {
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ art::ObjPtr<art::mirror::Class> set_class =
+ caller_->DecodeJObject(val_.l)->GetClass();
+ art::ObjPtr<art::mirror::ClassLoader> loader =
+ method->GetDeclaringClass()->GetClassLoader();
+ art::ObjPtr<art::mirror::Class> slot_class =
+ cl->LookupClass(caller_, descriptor.c_str(), loader);
+ DCHECK(!slot_class.IsNull());
+ return slot_class->IsAssignableFrom(set_class) ? OK : ERR(TYPE_MISMATCH);
+ }
+ }
+ case art::Primitive::kPrimByte:
+ case art::Primitive::kPrimChar:
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimShort:
+ case art::Primitive::kPrimBoolean:
+ return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimLong:
+ case art::Primitive::kPrimFloat:
+ case art::Primitive::kPrimDouble:
+ return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected primitive type " << slot_type;
+ UNREACHABLE();
+ }
+ }
+
+ jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (type_) {
+ case art::Primitive::kPrimNot: {
+ uint32_t ptr_val;
+ art::ObjPtr<art::mirror::Object> obj(caller_->DecodeJObject(val_.l));
+ ptr_val = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(obj.Ptr()));
+ if (!visitor.SetVReg(method,
+ static_cast<uint16_t>(slot_),
+ ptr_val,
+ art::kReferenceVReg)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimFloat: {
+ if (!visitor.SetVReg(method,
+ static_cast<uint16_t>(slot_),
+ static_cast<uint32_t>(val_.i),
+ type_ == art::Primitive::kPrimFloat ? art::kFloatVReg
+ : art::kIntVReg)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimLong: {
+ auto lo_type = type_ == art::Primitive::kPrimLong ? art::kLongLoVReg : art::kDoubleLoVReg;
+ auto high_type = type_ == art::Primitive::kPrimLong ? art::kLongHiVReg : art::kDoubleHiVReg;
+ if (!visitor.SetVRegPair(method,
+ static_cast<uint16_t>(slot_),
+ static_cast<uint64_t>(val_.j),
+ lo_type,
+ high_type)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ default: {
+ LOG(FATAL) << "unexpected register type " << type_;
+ UNREACHABLE();
+ }
+ }
+ return OK;
+ }
+
+ private:
+ art::Primitive::Type type_;
+ jvalue val_;
+};
+
+jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue val) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ SetLocalVariableClosure c(self, depth, slot, type, val);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+class GetLocalInstanceClosure : public art::Closure {
+ public:
+ GetLocalInstanceClosure(art::Thread* caller, jint depth, jobject* val)
+ : result_(ERR(INTERNAL)),
+ caller_(caller),
+ depth_(depth),
+ val_(val) {}
+
+ void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor visitor(self, context.get(), depth_);
+ visitor.WalkStack();
+ if (!visitor.FoundFrame()) {
+ // Must have been a bad depth.
+ result_ = ERR(NO_MORE_FRAMES);
+ return;
+ }
+ art::ArtMethod* method = visitor.GetMethod();
+ if (!visitor.IsShadowFrame() && !method->IsNative() && !method->IsProxyMethod()) {
+ // TODO We really should support get/set for non-shadow frames.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ }
+ result_ = OK;
+ art::ObjPtr<art::mirror::Object> obj = visitor.GetThisObject();
+ *val_ = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference<jobject>(obj);
+ }
+
+ jvmtiError GetResult() const {
+ return result_;
+ }
+
+ private:
+ jvmtiError result_;
+ art::Thread* caller_;
+ jint depth_;
+ jobject* val_;
+};
+
+jvmtiError MethodUtil::GetLocalInstance(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jobject* data) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ GetLocalInstanceClosure c(self, depth, data);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+#define FOR_JVMTI_JVALUE_TYPES(fn) \
+ fn(jint, art::Primitive::kPrimInt, i) \
+ fn(jlong, art::Primitive::kPrimLong, j) \
+ fn(jfloat, art::Primitive::kPrimFloat, f) \
+ fn(jdouble, art::Primitive::kPrimDouble, d) \
+ fn(jobject, art::Primitive::kPrimNot, l)
+
+namespace impl {
+
+template<typename T> void WriteJvalue(T, jvalue*);
+template<typename T> void ReadJvalue(jvalue, T*);
+template<typename T> art::Primitive::Type GetJNIType();
+
+#define JNI_TYPE_CHAR(type, prim, id) \
+template<> art::Primitive::Type GetJNIType<type>() { \
+ return prim; \
+}
+
+FOR_JVMTI_JVALUE_TYPES(JNI_TYPE_CHAR);
+
+#undef JNI_TYPE_CHAR
+
+#define RW_JVALUE(type, prim, id) \
+ template<> void ReadJvalue<type>(jvalue in, type* out) { \
+ *out = in.id; \
+ } \
+ template<> void WriteJvalue<type>(type in, jvalue* out) { \
+ out->id = in; \
+ }
+
+FOR_JVMTI_JVALUE_TYPES(RW_JVALUE);
+
+#undef RW_JVALUE
+
+} // namespace impl
+
+template<typename T>
+jvmtiError MethodUtil::SetLocalVariable(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ T data) {
+ jvalue v = {.j = 0};
+ art::Primitive::Type type = impl::GetJNIType<T>();
+ impl::WriteJvalue(data, &v);
+ return SetLocalVariableGeneric(env, thread, depth, slot, type, v);
+}
+
+template<typename T>
+jvmtiError MethodUtil::GetLocalVariable(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ T* data) {
+ if (data == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ jvalue v = {.j = 0};
+ art::Primitive::Type type = impl::GetJNIType<T>();
+ jvmtiError err = GetLocalVariableGeneric(env, thread, depth, slot, type, &v);
+ if (err != OK) {
+ return err;
+ } else {
+ impl::ReadJvalue(v, data);
+ return OK;
+ }
+}
+
+#define GET_SET_LV(type, prim, id) \
+ template jvmtiError MethodUtil::GetLocalVariable<type>(jvmtiEnv*, jthread, jint, jint, type*); \
+ template jvmtiError MethodUtil::SetLocalVariable<type>(jvmtiEnv*, jthread, jint, jint, type);
+
+FOR_JVMTI_JVALUE_TYPES(GET_SET_LV);
+
+#undef GET_SET_LV
+
+#undef FOR_JVMTI_JVALUE_TYPES
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/openjdkjvmti/ti_method.h
similarity index 70%
rename from runtime/openjdkjvmti/ti_method.h
rename to openjdkjvmti/ti_method.h
index d95a81b..e3578a4 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/openjdkjvmti/ti_method.h
@@ -29,11 +29,12 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_METHOD_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_METHOD_H_
+#ifndef ART_OPENJDKJVMTI_TI_METHOD_H_
+#define ART_OPENJDKJVMTI_TI_METHOD_H_
#include "jni.h"
#include "jvmti.h"
+#include "primitive.h"
namespace openjdkjvmti {
@@ -80,8 +81,34 @@
static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr);
static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr);
static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr);
+ static jvmtiError GetLocalVariableTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr);
+
+ template<typename T>
+ static jvmtiError SetLocalVariable(jvmtiEnv* env, jthread thread, jint depth, jint slot, T data);
+
+ template<typename T>
+ static jvmtiError GetLocalVariable(jvmtiEnv* env, jthread thread, jint depth, jint slot, T* data);
+
+ static jvmtiError GetLocalInstance(jvmtiEnv* env, jthread thread, jint depth, jobject* data);
+
+ private:
+ static jvmtiError SetLocalVariableGeneric(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue value);
+ static jvmtiError GetLocalVariableGeneric(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* value);
};
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_METHOD_H_
+#endif // ART_OPENJDKJVMTI_TI_METHOD_H_
diff --git a/runtime/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_monitor.cc
rename to openjdkjvmti/ti_monitor.cc
diff --git a/runtime/openjdkjvmti/ti_monitor.h b/openjdkjvmti/ti_monitor.h
similarity index 93%
rename from runtime/openjdkjvmti/ti_monitor.h
rename to openjdkjvmti/ti_monitor.h
index 96ccb0d..add089c 100644
--- a/runtime/openjdkjvmti/ti_monitor.h
+++ b/openjdkjvmti/ti_monitor.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_MONITOR_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_MONITOR_H_
+#ifndef ART_OPENJDKJVMTI_TI_MONITOR_H_
+#define ART_OPENJDKJVMTI_TI_MONITOR_H_
#include "jni.h"
#include "jvmti.h"
@@ -56,4 +56,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_MONITOR_H_
+#endif // ART_OPENJDKJVMTI_TI_MONITOR_H_
diff --git a/runtime/openjdkjvmti/ti_object.cc b/openjdkjvmti/ti_object.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_object.cc
rename to openjdkjvmti/ti_object.cc
diff --git a/runtime/openjdkjvmti/ti_object.h b/openjdkjvmti/ti_object.h
similarity index 92%
rename from runtime/openjdkjvmti/ti_object.h
rename to openjdkjvmti/ti_object.h
index 09eee61..fa3bd0f 100644
--- a/runtime/openjdkjvmti/ti_object.h
+++ b/openjdkjvmti/ti_object.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_OBJECT_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_OBJECT_H_
+#ifndef ART_OPENJDKJVMTI_TI_OBJECT_H_
+#define ART_OPENJDKJVMTI_TI_OBJECT_H_
#include "jni.h"
#include "jvmti.h"
@@ -46,4 +46,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_OBJECT_H_
+#endif // ART_OPENJDKJVMTI_TI_OBJECT_H_
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/openjdkjvmti/ti_phase.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_phase.cc
rename to openjdkjvmti/ti_phase.cc
diff --git a/runtime/openjdkjvmti/ti_phase.h b/openjdkjvmti/ti_phase.h
similarity index 93%
rename from runtime/openjdkjvmti/ti_phase.h
rename to openjdkjvmti/ti_phase.h
index a2c0d11..d4ed86b 100644
--- a/runtime/openjdkjvmti/ti_phase.h
+++ b/openjdkjvmti/ti_phase.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
+#ifndef ART_OPENJDKJVMTI_TI_PHASE_H_
+#define ART_OPENJDKJVMTI_TI_PHASE_H_
#include "jni.h"
#include "jvmti.h"
@@ -66,4 +66,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
+#endif // ART_OPENJDKJVMTI_TI_PHASE_H_
diff --git a/runtime/openjdkjvmti/ti_properties.cc b/openjdkjvmti/ti_properties.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_properties.cc
rename to openjdkjvmti/ti_properties.cc
diff --git a/runtime/openjdkjvmti/ti_properties.h b/openjdkjvmti/ti_properties.h
similarity index 92%
rename from runtime/openjdkjvmti/ti_properties.h
rename to openjdkjvmti/ti_properties.h
index 7073481..187b85d 100644
--- a/runtime/openjdkjvmti/ti_properties.h
+++ b/openjdkjvmti/ti_properties.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PROPERTIES_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_PROPERTIES_H_
+#ifndef ART_OPENJDKJVMTI_TI_PROPERTIES_H_
+#define ART_OPENJDKJVMTI_TI_PROPERTIES_H_
#include "jni.h"
#include "jvmti.h"
@@ -48,4 +48,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_PROPERTIES_H_
+#endif // ART_OPENJDKJVMTI_TI_PROPERTIES_H_
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_redefine.cc
rename to openjdkjvmti/ti_redefine.cc
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
similarity index 98%
rename from runtime/openjdkjvmti/ti_redefine.h
rename to openjdkjvmti/ti_redefine.h
index 27d7c3d..984f922 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_REDEFINE_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_REDEFINE_H_
+#ifndef ART_OPENJDKJVMTI_TI_REDEFINE_H_
+#define ART_OPENJDKJVMTI_TI_REDEFINE_H_
#include <string>
@@ -57,8 +57,8 @@
#include "obj_ptr.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
-#include "ti_class_definition.h"
#include "thread_list.h"
+#include "ti_class_definition.h"
#include "transform.h"
#include "utf.h"
#include "utils/dex_cache_arrays_layout-inl.h"
@@ -265,4 +265,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_REDEFINE_H_
+#endif // ART_OPENJDKJVMTI_TI_REDEFINE_H_
diff --git a/runtime/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_search.cc
rename to openjdkjvmti/ti_search.cc
diff --git a/runtime/openjdkjvmti/ti_search.h b/openjdkjvmti/ti_search.h
similarity index 92%
rename from runtime/openjdkjvmti/ti_search.h
rename to openjdkjvmti/ti_search.h
index cd7b4be..81a28cc 100644
--- a/runtime/openjdkjvmti/ti_search.h
+++ b/openjdkjvmti/ti_search.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+#ifndef ART_OPENJDKJVMTI_TI_SEARCH_H_
+#define ART_OPENJDKJVMTI_TI_SEARCH_H_
#include <vector>
@@ -50,4 +50,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+#endif // ART_OPENJDKJVMTI_TI_SEARCH_H_
diff --git a/runtime/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_stack.cc
rename to openjdkjvmti/ti_stack.cc
diff --git a/runtime/openjdkjvmti/ti_stack.h b/openjdkjvmti/ti_stack.h
similarity index 95%
rename from runtime/openjdkjvmti/ti_stack.h
rename to openjdkjvmti/ti_stack.h
index 6a593cf..2e96b82 100644
--- a/runtime/openjdkjvmti/ti_stack.h
+++ b/openjdkjvmti/ti_stack.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_
+#ifndef ART_OPENJDKJVMTI_TI_STACK_H_
+#define ART_OPENJDKJVMTI_TI_STACK_H_
#include "jni.h"
#include "jvmti.h"
@@ -71,4 +71,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_
+#endif // ART_OPENJDKJVMTI_TI_STACK_H_
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
similarity index 81%
rename from runtime/openjdkjvmti/ti_thread.cc
rename to openjdkjvmti/ti_thread.cc
index f16b419..6fa73f8 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -159,26 +159,13 @@
return ERR(NONE);
}
-static art::Thread* GetNativeThreadLocked(jthread thread,
- const art::ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(art::Locks::mutator_lock_)
- REQUIRES(art::Locks::thread_list_lock_) {
- if (thread == nullptr) {
- return art::Thread::Current();
- }
-
- return art::Thread::FromManagedThread(soa, thread);
-}
-
// Get the native thread. The spec says a null object denotes the current thread.
-static art::Thread* GetNativeThread(jthread thread,
- const art::ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
+art::Thread* ThreadUtil::GetNativeThread(jthread thread,
+ const art::ScopedObjectAccessAlreadyRunnable& soa) {
if (thread == nullptr) {
return art::Thread::Current();
}
- art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
return art::Thread::FromManagedThread(soa, thread);
}
@@ -190,18 +177,20 @@
return JVMTI_ERROR_WRONG_PHASE;
}
- art::ScopedObjectAccess soa(art::Thread::Current());
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
- art::Thread* self = GetNativeThread(thread, soa);
- if (self == nullptr && thread == nullptr) {
+ art::Thread* target = GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
return ERR(INVALID_THREAD);
}
JvmtiUniquePtr<char[]> name_uptr;
- if (self != nullptr) {
+ if (target != nullptr) {
// Have a native thread object, this thread is alive.
std::string name;
- self->GetThreadName(name);
+ target->GetThreadName(name);
jvmtiError name_result;
name_uptr = CopyString(env, name.c_str(), &name_result);
if (name_uptr == nullptr) {
@@ -209,11 +198,11 @@
}
info_ptr->name = name_uptr.get();
- info_ptr->priority = self->GetNativePriority();
+ info_ptr->priority = target->GetNativePriority();
- info_ptr->is_daemon = self->IsDaemon();
+ info_ptr->is_daemon = target->IsDaemon();
- art::ObjPtr<art::mirror::Object> peer = self->GetPeerFromOtherThread();
+ art::ObjPtr<art::mirror::Object> peer = target->GetPeerFromOtherThread();
// ThreadGroup.
if (peer != nullptr) {
@@ -310,9 +299,8 @@
static InternalThreadState GetNativeThreadState(jthread thread,
const art::ScopedObjectAccessAlreadyRunnable& soa)
REQUIRES_SHARED(art::Locks::mutator_lock_)
- REQUIRES(art::Locks::user_code_suspension_lock_) {
+ REQUIRES(art::Locks::thread_list_lock_, art::Locks::user_code_suspension_lock_) {
art::Thread* self = nullptr;
- art::MutexLock tll_mu(soa.Self(), *art::Locks::thread_list_lock_);
if (thread == nullptr) {
self = art::Thread::Current();
} else {
@@ -456,43 +444,46 @@
}
}
art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
state = GetNativeThreadState(thread, soa);
- break;
+ if (state.art_state == art::ThreadState::kStarting) {
+ break;
+ }
+ DCHECK(state.native_thread != nullptr);
+
+ // Translate internal thread state to JVMTI and Java state.
+ jint jvmti_state = GetJvmtiThreadStateFromInternal(state);
+
+ // Java state is derived from nativeGetState.
+ // TODO: Our implementation assigns "runnable" to suspended. As such, we will have slightly
+ // different mask if a thread got suspended due to user-code. However, this is for
+ // consistency with the Java view.
+ jint java_state = GetJavaStateFromInternal(state);
+
+ *thread_state_ptr = jvmti_state | java_state;
+
+ return ERR(NONE);
} while (true);
- if (state.art_state == art::ThreadState::kStarting) {
- if (thread == nullptr) {
- // No native thread, and no Java thread? We must be starting up. Report as wrong phase.
- return ERR(WRONG_PHASE);
- }
+ DCHECK_EQ(state.art_state, art::ThreadState::kStarting);
- art::ScopedObjectAccess soa(self);
-
- // Need to read the Java "started" field to know whether this is starting or terminated.
- art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread);
- art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
- art::ArtField* started_field = klass->FindDeclaredInstanceField("started", "Z");
- CHECK(started_field != nullptr);
- bool started = started_field->GetBoolean(peer) != 0;
- constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW;
- constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED |
- JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
- *thread_state_ptr = started ? kTerminatedState : kStartedState;
- return ERR(NONE);
+ if (thread == nullptr) {
+ // No native thread, and no Java thread? We must be starting up. Report as wrong phase.
+ return ERR(WRONG_PHASE);
}
- DCHECK(state.native_thread != nullptr);
- // Translate internal thread state to JVMTI and Java state.
- jint jvmti_state = GetJvmtiThreadStateFromInternal(state);
+ art::ScopedObjectAccess soa(self);
- // Java state is derived from nativeGetState.
- // TODO: Our implementation assigns "runnable" to suspended. As such, we will have slightly
- // different mask if a thread got suspended due to user-code. However, this is for
- // consistency with the Java view.
- jint java_state = GetJavaStateFromInternal(state);
-
- *thread_state_ptr = jvmti_state | java_state;
-
+ // Need to read the Java "started" field to know whether this is starting or terminated.
+ art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread);
+ art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
+ art::ArtField* started_field = klass->FindDeclaredInstanceField("started", "Z");
+ CHECK(started_field != nullptr);
+ bool started = started_field->GetBoolean(peer) != 0;
+ constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW;
+ constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED |
+ JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
+ *thread_state_ptr = started ? kTerminatedState : kStartedState;
return ERR(NONE);
}
@@ -571,7 +562,7 @@
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
art::MutexLock mu(self, *art::Locks::thread_list_lock_);
- art::Thread* target = GetNativeThreadLocked(thread, soa);
+ art::Thread* target = GetNativeThread(thread, soa);
if (target == nullptr && thread == nullptr) {
return ERR(INVALID_THREAD);
}
@@ -600,7 +591,7 @@
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
art::MutexLock mu(self, *art::Locks::thread_list_lock_);
- art::Thread* target = GetNativeThreadLocked(thread, soa);
+ art::Thread* target = GetNativeThread(thread, soa);
if (target == nullptr && thread == nullptr) {
return ERR(INVALID_THREAD);
}
@@ -700,8 +691,7 @@
}
jvmtiError ThreadUtil::SuspendOther(art::Thread* self,
- jthread target_jthread,
- art::Thread* target) {
+ jthread target_jthread) {
// Loop since we need to bail out and try again if we would end up getting suspended while holding
// the user_code_suspension_lock_ due to a SuspendReason::kForUserCode. In this situation we
// release the lock, wait to get resumed and try again.
@@ -714,33 +704,43 @@
SuspendCheck(self);
art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_);
{
- art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_);
+ art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_);
// Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by
// a user-code suspension. We retry and do another SuspendCheck to clear this.
if (self->GetUserCodeSuspendCount() != 0) {
continue;
- } else if (target->GetUserCodeSuspendCount() != 0) {
- return ERR(THREAD_SUSPENDED);
}
+ // We are not going to be suspended by user code from now on.
}
- bool timeout = true;
- while (timeout) {
+ {
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock thread_list_mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = GetNativeThread(target_jthread, soa);
art::ThreadState state = target->GetState();
if (state == art::ThreadState::kTerminated || state == art::ThreadState::kStarting) {
return ERR(THREAD_NOT_ALIVE);
- }
- target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer(
- target_jthread,
- /* request_suspension */ true,
- art::SuspendReason::kForUserCode,
- &timeout);
- if (target == nullptr && !timeout) {
- // TODO It would be good to get more information about why exactly the thread failed to
- // suspend.
- return ERR(INTERNAL);
+ } else {
+ art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_);
+ if (target->GetUserCodeSuspendCount() != 0) {
+ return ERR(THREAD_SUSPENDED);
+ }
}
}
- return OK;
+ bool timeout = true;
+ art::Thread* ret_target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer(
+ target_jthread,
+ /* request_suspension */ true,
+ art::SuspendReason::kForUserCode,
+ &timeout);
+ if (ret_target == nullptr && !timeout) {
+ // TODO It would be good to get more information about why exactly the thread failed to
+ // suspend.
+ return ERR(INTERNAL);
+ } else if (!timeout) {
+ // we didn't time out and got a result.
+ return OK;
+ }
+ // We timed out. Just go around and try again.
} while (true);
UNREACHABLE();
}
@@ -769,18 +769,21 @@
jvmtiError ThreadUtil::SuspendThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread thread) {
art::Thread* self = art::Thread::Current();
- art::Thread* target;
+ bool target_is_self = false;
{
art::ScopedObjectAccess soa(self);
- target = GetNativeThread(thread, soa);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = GetNativeThread(thread, soa);
+ if (target == nullptr) {
+ return ERR(INVALID_THREAD);
+ } else if (target == self) {
+ target_is_self = true;
+ }
}
- if (target == nullptr) {
- return ERR(INVALID_THREAD);
- }
- if (target == self) {
+ if (target_is_self) {
return SuspendSelf(self);
} else {
- return SuspendOther(self, thread, target);
+ return SuspendOther(self, thread);
}
}
@@ -791,41 +794,56 @@
}
art::Thread* self = art::Thread::Current();
art::Thread* target;
- {
- // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
- // have the 'suspend_lock' locked here.
- art::ScopedObjectAccess soa(self);
- target = GetNativeThread(thread, soa);
- }
- if (target == nullptr) {
- return ERR(INVALID_THREAD);
- } else if (target == self) {
- // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so we
- // can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs about
- // current state since it's all concurrent.
- return ERR(THREAD_NOT_SUSPENDED);
- }
- // Now that we know we aren't getting suspended ourself (since we have a mutator lock) we lock the
- // suspend_lock to start suspending.
- art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_);
- {
- // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really cannot
- // tell why resume failed.
- art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_);
- if (target->GetUserCodeSuspendCount() == 0) {
- return ERR(THREAD_NOT_SUSPENDED);
+ // Retry until we know we won't get suspended by user code while resuming something.
+ do {
+ SuspendCheck(self);
+ art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+ {
+ art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_);
+ // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by
+ // a user-code suspension. We retry and do another SuspendCheck to clear this.
+ if (self->GetUserCodeSuspendCount() != 0) {
+ continue;
+ }
}
- }
- if (target->GetState() == art::ThreadState::kTerminated) {
- return ERR(THREAD_NOT_ALIVE);
- }
- DCHECK(target != self);
- if (!art::Runtime::Current()->GetThreadList()->Resume(target, art::SuspendReason::kForUserCode)) {
- // TODO Give a better error.
- // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure.
- return ERR(INTERNAL);
- }
- return OK;
+ // From now on we know we cannot get suspended by user-code.
+ {
+ // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
+ // have the 'suspend_lock' locked here.
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+ target = GetNativeThread(thread, soa);
+ if (target == nullptr) {
+ return ERR(INVALID_THREAD);
+ } else if (target == self) {
+ // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so
+ // we can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs
+ // about current state since it's all concurrent.
+ return ERR(THREAD_NOT_SUSPENDED);
+ } else if (target->GetState() == art::ThreadState::kTerminated) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really
+ // cannot tell why resume failed.
+ {
+ art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_);
+ if (target->GetUserCodeSuspendCount() == 0) {
+ return ERR(THREAD_NOT_SUSPENDED);
+ }
+ }
+ }
+ // It is okay that we don't have a thread_list_lock here since we know that the thread cannot
+ // die since it is currently held suspended by a SuspendReason::kForUserCode suspend.
+ DCHECK(target != self);
+ if (!art::Runtime::Current()->GetThreadList()->Resume(target,
+ art::SuspendReason::kForUserCode)) {
+ // TODO Give a better error.
+ // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure.
+ return ERR(INTERNAL);
+ } else {
+ return OK;
+ }
+ } while (true);
}
// Suspends all the threads in the list at the same time. Getting this behavior is a little tricky
@@ -851,6 +869,7 @@
for (jint i = 0; i < request_count; i++) {
{
art::ScopedObjectAccess soa(self);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
if (threads[i] == nullptr || GetNativeThread(threads[i], soa) == self) {
current_thread_indexes.push_back(i);
continue;
diff --git a/runtime/openjdkjvmti/ti_thread.h b/openjdkjvmti/ti_thread.h
similarity index 91%
rename from runtime/openjdkjvmti/ti_thread.h
rename to openjdkjvmti/ti_thread.h
index d07dc06..03c49d7 100644
--- a/runtime/openjdkjvmti/ti_thread.h
+++ b/openjdkjvmti/ti_thread.h
@@ -29,16 +29,18 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_THREAD_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_THREAD_H_
+#ifndef ART_OPENJDKJVMTI_TI_THREAD_H_
+#define ART_OPENJDKJVMTI_TI_THREAD_H_
#include "jni.h"
#include "jvmti.h"
+#include "base/macros.h"
#include "base/mutex.h"
namespace art {
class ArtField;
+class ScopedObjectAccessAlreadyRunnable;
class Thread;
} // namespace art
@@ -86,6 +88,11 @@
const jthread* threads,
jvmtiError* results);
+ static art::Thread* GetNativeThread(jthread thread,
+ const art::ScopedObjectAccessAlreadyRunnable& soa)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ REQUIRES(art::Locks::thread_list_lock_);
+
private:
// We need to make sure only one thread tries to suspend threads at a time so we can get the
// 'suspend-only-once' behavior the spec requires. Internally, ART considers suspension to be a
@@ -98,7 +105,7 @@
// cause the thread to wake up if the thread is suspended for the debugger or gc or something.
static jvmtiError SuspendSelf(art::Thread* self)
REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_);
- static jvmtiError SuspendOther(art::Thread* self, jthread target_jthread, art::Thread* target)
+ static jvmtiError SuspendOther(art::Thread* self, jthread target_jthread)
REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_);
static art::ArtField* context_class_loader_;
@@ -106,4 +113,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_THREAD_H_
+#endif // ART_OPENJDKJVMTI_TI_THREAD_H_
diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_threadgroup.cc
rename to openjdkjvmti/ti_threadgroup.cc
diff --git a/runtime/openjdkjvmti/ti_threadgroup.h b/openjdkjvmti/ti_threadgroup.h
similarity index 93%
rename from runtime/openjdkjvmti/ti_threadgroup.h
rename to openjdkjvmti/ti_threadgroup.h
index c3a0ff5..4911566 100644
--- a/runtime/openjdkjvmti/ti_threadgroup.h
+++ b/openjdkjvmti/ti_threadgroup.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
+#ifndef ART_OPENJDKJVMTI_TI_THREADGROUP_H_
+#define ART_OPENJDKJVMTI_TI_THREADGROUP_H_
#include "jni.h"
#include "jvmti.h"
@@ -57,4 +57,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
+#endif // ART_OPENJDKJVMTI_TI_THREADGROUP_H_
diff --git a/runtime/openjdkjvmti/ti_timers.cc b/openjdkjvmti/ti_timers.cc
similarity index 100%
rename from runtime/openjdkjvmti/ti_timers.cc
rename to openjdkjvmti/ti_timers.cc
diff --git a/runtime/openjdkjvmti/ti_timers.h b/openjdkjvmti/ti_timers.h
similarity index 92%
rename from runtime/openjdkjvmti/ti_timers.h
rename to openjdkjvmti/ti_timers.h
index 6300678..892205a 100644
--- a/runtime/openjdkjvmti/ti_timers.h
+++ b/openjdkjvmti/ti_timers.h
@@ -29,8 +29,8 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_
+#ifndef ART_OPENJDKJVMTI_TI_TIMERS_H_
+#define ART_OPENJDKJVMTI_TI_TIMERS_H_
#include "jni.h"
#include "jvmti.h"
@@ -48,4 +48,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_
+#endif // ART_OPENJDKJVMTI_TI_TIMERS_H_
diff --git a/runtime/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc
similarity index 100%
rename from runtime/openjdkjvmti/transform.cc
rename to openjdkjvmti/transform.cc
diff --git a/runtime/openjdkjvmti/transform.h b/openjdkjvmti/transform.h
similarity index 94%
rename from runtime/openjdkjvmti/transform.h
rename to openjdkjvmti/transform.h
index ba40e04..6bbe60a 100644
--- a/runtime/openjdkjvmti/transform.h
+++ b/openjdkjvmti/transform.h
@@ -29,16 +29,16 @@
* questions.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_TRANSFORM_H_
-#define ART_RUNTIME_OPENJDKJVMTI_TRANSFORM_H_
+#ifndef ART_OPENJDKJVMTI_TRANSFORM_H_
+#define ART_OPENJDKJVMTI_TRANSFORM_H_
#include <string>
#include <jni.h>
+#include "jvmti.h"
#include "art_jvmti.h"
#include "ti_class_definition.h"
-#include "jvmti.h"
namespace openjdkjvmti {
@@ -65,5 +65,5 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_TRANSFORM_H_
+#endif // ART_OPENJDKJVMTI_TRANSFORM_H_
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index a93969f..ed7623a 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -35,17 +35,17 @@
#include "base/stringpiece.h"
#include "base/unix_file/fd_file.h"
#include "base/unix_file/random_access_file_utils.h"
-#include "elf_utils.h"
#include "elf_file.h"
#include "elf_file_impl.h"
+#include "elf_utils.h"
#include "gc/space/image_space.h"
#include "image-inl.h"
#include "intern_table.h"
#include "mirror/dex_cache.h"
#include "mirror/executable.h"
+#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "mirror/object-refvisitor-inl.h"
-#include "mirror/method.h"
#include "mirror/reference.h"
#include "noop_compiler_callbacks.h"
#include "offsets.h"
@@ -535,17 +535,18 @@
orig_dex_cache->FixupResolvedTypes(RelocatedCopyOf(orig_types),
RelocatedPointerVisitor(this));
}
- ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
- ArtMethod** relocated_methods = RelocatedAddressOfPointer(orig_methods);
+ mirror::MethodDexCacheType* orig_methods = orig_dex_cache->GetResolvedMethods();
+ mirror::MethodDexCacheType* relocated_methods = RelocatedAddressOfPointer(orig_methods);
copy_dex_cache->SetField64<false>(
mirror::DexCache::ResolvedMethodsOffset(),
static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_methods)));
if (orig_methods != nullptr) {
- ArtMethod** copy_methods = RelocatedCopyOf(orig_methods);
+ mirror::MethodDexCacheType* copy_methods = RelocatedCopyOf(orig_methods);
for (size_t j = 0, num = orig_dex_cache->NumResolvedMethods(); j != num; ++j) {
- ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, j, pointer_size);
- ArtMethod* copy = RelocatedAddressOfPointer(orig);
- mirror::DexCache::SetElementPtrSize(copy_methods, j, copy, pointer_size);
+ mirror::MethodDexCachePair orig =
+ mirror::DexCache::GetNativePairPtrSize(orig_methods, j, pointer_size);
+ mirror::MethodDexCachePair copy(RelocatedAddressOfPointer(orig.object), orig.index);
+ mirror::DexCache::SetNativePairPtrSize(copy_methods, j, copy, pointer_size);
}
}
mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields();
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 182ce94..d4c5a10 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -24,8 +24,8 @@
#include "elf_file.h"
#include "elf_utils.h"
#include "gc/accounting/space_bitmap.h"
-#include "gc/space/image_space.h"
#include "gc/heap.h"
+#include "gc/space/image_space.h"
#include "os.h"
#include "runtime.h"
diff --git a/profman/profman.cc b/profman/profman.cc
index 6c8ca56..fd3bd11 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "errno.h"
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
@@ -44,7 +44,6 @@
#include "runtime.h"
#include "type_reference.h"
#include "utils.h"
-#include "type_reference.h"
#include "zip_archive.h"
namespace art {
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 8d15c34..d534542 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -25,6 +25,7 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: [
+ "aot_class_linker.cc",
"art_field.cc",
"art_method.cc",
"atomic.cc",
@@ -50,7 +51,6 @@
"class_linker.cc",
"class_loader_context.cc",
"class_table.cc",
- "code_simulator_container.cc",
"common_throws.cc",
"compiler_filter.cc",
"debugger.cc",
@@ -625,9 +625,3 @@
"libvixld-arm64",
],
}
-
-subdirs = [
- "openjdkjvm",
- "openjdkjvmti",
- "simulator",
-]
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
new file mode 100644
index 0000000..9396565
--- /dev/null
+++ b/runtime/aot_class_linker.cc
@@ -0,0 +1,85 @@
+/*
+ * 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 "aot_class_linker.h"
+
+#include "class_reference.h"
+#include "compiler_callbacks.h"
+#include "handle_scope-inl.h"
+#include "mirror/class-inl.h"
+#include "runtime.h"
+#include "verifier/verifier_enums.h"
+
+namespace art {
+
+AotClassLinker::AotClassLinker(InternTable *intern_table) : ClassLinker(intern_table) {}
+
+AotClassLinker::~AotClassLinker() {}
+
+// Wrap the original InitializeClass with creation of transaction when in strict mode.
+bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
+ bool can_init_statics, bool can_init_parents) {
+ Runtime* const runtime = Runtime::Current();
+ bool strict_mode_ = runtime->IsActiveStrictTransactionMode();
+
+ DCHECK(klass != nullptr);
+ if (klass->IsInitialized() || klass->IsInitializing()) {
+ return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
+ }
+
+ // Don't initialize klass if it's superclass is not initialized, because superclass might abort
+ // the transaction and rolled back after klass's change is commited.
+ if (strict_mode_ && !klass->IsInterface() && klass->HasSuperClass()) {
+ if (klass->GetSuperClass()->GetStatus() == mirror::Class::kStatusInitializing) {
+ runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve "
+ + klass->PrettyTypeOf() + " because it's superclass is not initialized.");
+ return false;
+ }
+ }
+
+ if (strict_mode_) {
+ runtime->EnterTransactionMode(true, klass.Get()->AsClass());
+ }
+ bool success = ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
+
+ if (strict_mode_) {
+ if (success) {
+ // Exit Transaction if success.
+ runtime->ExitTransactionMode();
+ } else {
+ // If not successfully initialized, the last transaction must abort. Don't rollback
+ // immediately, leave the cleanup to compiler driver which needs abort message and exception.
+ DCHECK(runtime->IsTransactionAborted());
+ DCHECK(self->IsExceptionPending());
+ }
+ }
+ return success;
+}
+
+verifier::FailureKind AotClassLinker::PerformClassVerification(Thread* self,
+ Handle<mirror::Class> klass,
+ verifier::HardFailLogMode log_level,
+ std::string* error_msg) {
+ Runtime* const runtime = Runtime::Current();
+ CompilerCallbacks* callbacks = runtime->GetCompilerCallbacks();
+ if (callbacks->CanAssumeVerified(ClassReference(&klass->GetDexFile(),
+ klass->GetDexClassDefIndex()))) {
+ return verifier::FailureKind::kNoFailure;
+ }
+ return ClassLinker::PerformClassVerification(self, klass, log_level, error_msg);
+}
+
+} // namespace art
diff --git a/runtime/aot_class_linker.h b/runtime/aot_class_linker.h
new file mode 100644
index 0000000..927b533
--- /dev/null
+++ b/runtime/aot_class_linker.h
@@ -0,0 +1,50 @@
+/*
+ * 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_AOT_CLASS_LINKER_H_
+#define ART_RUNTIME_AOT_CLASS_LINKER_H_
+
+#include "class_linker.h"
+
+namespace art {
+// AotClassLinker is only used for AOT compiler, which includes some logic for class initialization
+// which will only be used in pre-compilation.
+class AotClassLinker : public ClassLinker {
+ public:
+ explicit AotClassLinker(InternTable *intern_table);
+ ~AotClassLinker();
+
+ protected:
+ // Overridden version of PerformClassVerification allows skipping verification if the class was
+ // previously verified but unloaded.
+ verifier::FailureKind PerformClassVerification(Thread* self,
+ Handle<mirror::Class> klass,
+ verifier::HardFailLogMode log_level,
+ std::string* error_msg)
+ OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ bool InitializeClass(Thread *self,
+ Handle<mirror::Class> klass,
+ bool can_run_clinit,
+ bool can_init_parents)
+ OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+};
+} // namespace art
+
+#endif // ART_RUNTIME_AOT_CLASS_LINKER_H_
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index dd98f51..1ba4070 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -21,7 +21,6 @@
#include "common_runtime_test.h"
#include "quick/quick_method_frame_info.h"
-
// asm_support.h declares tests next to the #defines. We use asm_support_check.h to (safely)
// generate CheckAsmSupportOffsetsAndSizes using gtest's EXPECT for the tests. We also use the
// RETURN_TYPE, HEADER and FOOTER defines from asm_support_check.h to try to ensure that any
@@ -89,6 +88,11 @@
#undef FRAME_SIZE_SAVE_REFS_ONLY
static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS;
#undef FRAME_SIZE_SAVE_REFS_AND_ARGS
+static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT
+static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck =
+ FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK
static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING;
#undef FRAME_SIZE_SAVE_EVERYTHING
#undef BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET
@@ -110,6 +114,11 @@
#undef FRAME_SIZE_SAVE_REFS_ONLY
static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS;
#undef FRAME_SIZE_SAVE_REFS_AND_ARGS
+static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT
+static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck =
+ FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK
static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING;
#undef FRAME_SIZE_SAVE_EVERYTHING
#undef BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET
@@ -127,6 +136,11 @@
#undef FRAME_SIZE_SAVE_REFS_ONLY
static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS;
#undef FRAME_SIZE_SAVE_REFS_AND_ARGS
+static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT
+static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck =
+ FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK
static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING;
#undef FRAME_SIZE_SAVE_EVERYTHING
#undef BAKER_MARK_INTROSPECTION_REGISTER_COUNT
@@ -143,6 +157,11 @@
#undef FRAME_SIZE_SAVE_REFS_ONLY
static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS;
#undef FRAME_SIZE_SAVE_REFS_AND_ARGS
+static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT
+static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck =
+ FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK
static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING;
#undef FRAME_SIZE_SAVE_EVERYTHING
#undef BAKER_MARK_INTROSPECTION_REGISTER_COUNT
@@ -159,6 +178,11 @@
#undef FRAME_SIZE_SAVE_REFS_ONLY
static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS;
#undef FRAME_SIZE_SAVE_REFS_AND_ARGS
+static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT
+static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck =
+ FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK
static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING;
#undef FRAME_SIZE_SAVE_EVERYTHING
} // namespace x86
@@ -171,25 +195,36 @@
#undef FRAME_SIZE_SAVE_REFS_ONLY
static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS;
#undef FRAME_SIZE_SAVE_REFS_AND_ARGS
+static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT
+static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck =
+ FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK;
+#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK
static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING;
#undef FRAME_SIZE_SAVE_EVERYTHING
} // namespace x86_64
// Check architecture specific constants are sound.
-#define TEST_ARCH(Arch, arch) \
- TEST_F(ArchTest, Arch) { \
- CheckFrameSize(InstructionSet::k##Arch, \
- CalleeSaveType::kSaveAllCalleeSaves, \
- arch::kFrameSizeSaveAllCalleeSaves); \
- CheckFrameSize(InstructionSet::k##Arch, \
- CalleeSaveType::kSaveRefsOnly, \
- arch::kFrameSizeSaveRefsOnly); \
- CheckFrameSize(InstructionSet::k##Arch, \
- CalleeSaveType::kSaveRefsAndArgs, \
- arch::kFrameSizeSaveRefsAndArgs); \
- CheckFrameSize(InstructionSet::k##Arch, \
- CalleeSaveType::kSaveEverything, \
- arch::kFrameSizeSaveEverything); \
+#define TEST_ARCH(Arch, arch) \
+ TEST_F(ArchTest, Arch) { \
+ CheckFrameSize(InstructionSet::k##Arch, \
+ CalleeSaveType::kSaveAllCalleeSaves, \
+ arch::kFrameSizeSaveAllCalleeSaves); \
+ CheckFrameSize(InstructionSet::k##Arch, \
+ CalleeSaveType::kSaveRefsOnly, \
+ arch::kFrameSizeSaveRefsOnly); \
+ CheckFrameSize(InstructionSet::k##Arch, \
+ CalleeSaveType::kSaveRefsAndArgs, \
+ arch::kFrameSizeSaveRefsAndArgs); \
+ CheckFrameSize(InstructionSet::k##Arch, \
+ CalleeSaveType::kSaveEverything, \
+ arch::kFrameSizeSaveEverything); \
+ CheckFrameSize(InstructionSet::k##Arch, \
+ CalleeSaveType::kSaveEverythingForClinit, \
+ arch::kFrameSizeSaveEverythingForClinit); \
+ CheckFrameSize(InstructionSet::k##Arch, \
+ CalleeSaveType::kSaveEverythingForSuspendCheck, \
+ arch::kFrameSizeSaveEverythingForSuspendCheck); \
}
TEST_ARCH(Arm, arm)
TEST_ARCH(Arm64, arm64)
diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h
index 8f2fd6e..3d85872 100644
--- a/runtime/arch/arm/asm_support_arm.h
+++ b/runtime/arch/arm/asm_support_arm.h
@@ -23,6 +23,8 @@
#define FRAME_SIZE_SAVE_REFS_ONLY 32
#define FRAME_SIZE_SAVE_REFS_AND_ARGS 112
#define FRAME_SIZE_SAVE_EVERYTHING 192
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
// The offset from the art_quick_read_barrier_mark_introspection (used for field
// loads with 32-bit LDR) to the entrypoint for field loads with 16-bit LDR,
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 8a8d264..090bab7 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -19,13 +19,13 @@
#include "arch/arm/asm_support_arm.h"
#include "base/bit_utils.h"
+#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/math_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_default_init_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
-#include "entrypoints/entrypoint_utils.h"
-#include "entrypoints/math_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "interpreter/interpreter.h"
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index b4bca01..5c31378 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "fault_handler.h"
#include <sys/ucontext.h>
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index 0942356..2496968 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -17,11 +17,12 @@
#include "instruction_set_features_arm.h"
#if defined(ART_TARGET_ANDROID) && defined(__arm__)
-#include <sys/auxv.h>
#include <asm/hwcap.h>
+#include <sys/auxv.h>
#endif
#include "signal.h"
+
#include <fstream>
#include "android-base/stringprintf.h"
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 0de5905..cf2bfee 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -182,7 +182,7 @@
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
* when core registers are already saved.
*/
-.macro SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED rTemp
+.macro SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED rTemp, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
@ 14 words of callee saves and args already saved.
vpush {d0-d15} @ 32 words, 2 for each of the 16 saved doubles.
.cfi_adjust_cfa_offset 128
@@ -190,7 +190,7 @@
.cfi_adjust_cfa_offset 8
RUNTIME_CURRENT1 \rTemp @ Load Runtime::Current into rTemp.
@ Load kSaveEverything Method* into rTemp.
- ldr \rTemp, [\rTemp, #RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET]
+ ldr \rTemp, [\rTemp, #\runtime_method_offset]
str \rTemp, [sp, #0] @ Place Method* at bottom of stack.
str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame.
@@ -204,7 +204,7 @@
* Macro that sets up the callee save frame to conform with
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
*/
-.macro SETUP_SAVE_EVERYTHING_FRAME rTemp
+.macro SETUP_SAVE_EVERYTHING_FRAME rTemp, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
push {r0-r12, lr} @ 14 words of callee saves and args.
.cfi_adjust_cfa_offset 56
.cfi_rel_offset r0, 0
@@ -221,7 +221,7 @@
.cfi_rel_offset r11, 44
.cfi_rel_offset ip, 48
.cfi_rel_offset lr, 52
- SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED \rTemp
+ SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED \rTemp, \runtime_method_offset
.endm
.macro RESTORE_SAVE_EVERYTHING_FRAME
@@ -1004,10 +1004,10 @@
.endm
// Macro for string and type resolution and initialization.
-.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
.extern \entrypoint
ENTRY \name
- SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC
+ SETUP_SAVE_EVERYTHING_FRAME r1, \runtime_method_offset @ save everything in case of GC
mov r1, r9 @ pass Thread::Current
bl \entrypoint @ (uint32_t index, Thread*)
cbz r0, 1f @ If result is null, deliver the OOME.
@@ -1021,8 +1021,12 @@
END \name
.endm
-ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
-ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT name, entrypoint
+ ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
+.endm
+
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
@@ -1537,7 +1541,7 @@
1:
mov rSUSPEND, #SUSPEND_CHECK_INTERVAL @ reset rSUSPEND to SUSPEND_CHECK_INTERVAL
#endif
- SETUP_SAVE_EVERYTHING_FRAME r0 @ save everything for GC stack crawl
+ SETUP_SAVE_EVERYTHING_FRAME r0, RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET @ save everything for GC stack crawl
mov r0, rSELF
bl artTestSuspendFromCode @ (Thread*)
RESTORE_SAVE_EVERYTHING_FRAME
@@ -1585,31 +1589,98 @@
*
* Note that this stub writes to r0, r4, and r12.
*/
+ .extern artLookupResolvedMethod
ENTRY art_quick_imt_conflict_trampoline
- ldr r4, [sp, #0] // Load referrer
- ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32] // Load dex cache methods array
- ldr r12, [r4, r12, lsl #POINTER_SIZE_SHIFT] // Load interface method
- ldr r0, [r0, #ART_METHOD_JNI_OFFSET_32] // Load ImtConflictTable
- ldr r4, [r0] // Load first entry in ImtConflictTable.
+ push {r1-r2}
+ .cfi_adjust_cfa_offset (2 * 4)
+ .cfi_rel_offset r1, 0
+ .cfi_rel_offset r2, 4
+ ldr r4, [sp, #(2 * 4)] // Load referrer.
+ ubfx r1, r12, #0, #METHOD_DEX_CACHE_HASH_BITS // Calculate DexCache method slot index.
+ ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32] // Load dex cache methods array
+ add r4, r4, r1, lsl #(POINTER_SIZE_SHIFT + 1) // Load DexCache method slot address.
+ ldr r2, [r0, #ART_METHOD_JNI_OFFSET_32] // Load ImtConflictTable
+
+// FIXME: Configure the build to use the faster code when appropriate.
+// Currently we fall back to the slower version.
+#if HAS_ATOMIC_LDRD
+ ldrd r0, r1, [r4]
+#else
+ push {r3}
+ .cfi_adjust_cfa_offset 4
+ .cfi_rel_offset r3, 0
+.Limt_conflict_trampoline_retry_load:
+ ldrexd r0, r1, [r4]
+ strexd r3, r0, r1, [r4]
+ cmp r3, #0
+ bne .Limt_conflict_trampoline_retry_load
+ pop {r3}
+ .cfi_adjust_cfa_offset -4
+ .cfi_restore r3
+#endif
+
+ ldr r4, [r2] // Load first entry in ImtConflictTable.
+ cmp r1, r12 // Compare method index to see if we had a DexCache method hit.
+ bne .Limt_conflict_trampoline_dex_cache_miss
.Limt_table_iterate:
- cmp r4, r12
+ cmp r4, r0
// Branch if found. Benchmarks have shown doing a branch here is better.
- beq .Limt_table_found
+ beq .Limt_table_found
// If the entry is null, the interface method is not in the ImtConflictTable.
- cbz r4, .Lconflict_trampoline
+ cbz r4, .Lconflict_trampoline
// Iterate over the entries of the ImtConflictTable.
- ldr r4, [r0, #(2 * __SIZEOF_POINTER__)]!
+ ldr r4, [r2, #(2 * __SIZEOF_POINTER__)]!
b .Limt_table_iterate
.Limt_table_found:
// We successfully hit an entry in the table. Load the target method
// and jump to it.
- ldr r0, [r0, #__SIZEOF_POINTER__]
- ldr pc, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
+ ldr r0, [r2, #__SIZEOF_POINTER__]
+ .cfi_remember_state
+ pop {r1-r2}
+ .cfi_adjust_cfa_offset -(2 * 4)
+ .cfi_restore r1
+ .cfi_restore r2
+ ldr pc, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
+ .cfi_restore_state
.Lconflict_trampoline:
// Call the runtime stub to populate the ImtConflictTable and jump to the
// resolved method.
- mov r0, r12 // Load interface method
+ .cfi_remember_state
+ pop {r1-r2}
+ .cfi_adjust_cfa_offset -(2 * 4)
+ .cfi_restore r1
+ .cfi_restore r2
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
+ .cfi_restore_state
+.Limt_conflict_trampoline_dex_cache_miss:
+ // We're not creating a proper runtime method frame here,
+ // artLookupResolvedMethod() is not allowed to walk the stack.
+
+ // Save ImtConflictTable (r2), remaining arg (r3), first entry (r4), return address (lr).
+ push {r2-r4, lr}
+ .cfi_adjust_cfa_offset (4 * 4)
+ .cfi_rel_offset r3, 4
+ .cfi_rel_offset lr, 12
+ // Save FPR args.
+ vpush {d0-d7}
+ .cfi_adjust_cfa_offset (8 * 8)
+
+ mov r0, ip // Pass method index.
+ ldr r1, [sp, #(8 * 8 + 6 * 4)] // Pass referrer.
+ bl artLookupResolvedMethod // (uint32_t method_index, ArtMethod* referrer)
+
+ // Restore FPR args.
+ vpop {d0-d7}
+ .cfi_adjust_cfa_offset -(8 * 8)
+ // Restore ImtConflictTable (r2), remaining arg (r3), first entry (r4), return address (lr).
+ pop {r2-r4, lr}
+ .cfi_adjust_cfa_offset -(4 * 4)
+ .cfi_restore r3
+ .cfi_restore lr
+
+ cmp r0, #0 // If the method wasn't resolved,
+ beq .Lconflict_trampoline // skip the lookup and go to artInvokeInterfaceTrampoline().
+ b .Limt_table_iterate
END art_quick_imt_conflict_trampoline
.extern artQuickResolutionTrampoline
diff --git a/runtime/arch/arm/quick_method_frame_info_arm.h b/runtime/arch/arm/quick_method_frame_info_arm.h
index 39061f0..5c5b81b 100644
--- a/runtime/arch/arm/quick_method_frame_info_arm.h
+++ b/runtime/arch/arm/quick_method_frame_info_arm.h
@@ -56,6 +56,7 @@
kArmCalleeSaveFpArgSpills | kArmCalleeSaveFpAllSpills;
constexpr uint32_t ArmCalleeSaveCoreSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveArgSpills : 0) |
(type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveAllSpills : 0) |
@@ -63,6 +64,7 @@
}
constexpr uint32_t ArmCalleeSaveFpSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) |
(type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) |
@@ -70,29 +72,34 @@
}
constexpr uint32_t ArmCalleeSaveFrameSize(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return RoundUp((POPCOUNT(ArmCalleeSaveCoreSpills(type)) /* gprs */ +
POPCOUNT(ArmCalleeSaveFpSpills(type)) /* fprs */ +
1 /* Method* */) * static_cast<size_t>(kArmPointerSize), kStackAlignment);
}
constexpr QuickMethodFrameInfo ArmCalleeSaveMethodFrameInfo(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return QuickMethodFrameInfo(ArmCalleeSaveFrameSize(type),
ArmCalleeSaveCoreSpills(type),
ArmCalleeSaveFpSpills(type));
}
constexpr size_t ArmCalleeSaveFpr1Offset(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return ArmCalleeSaveFrameSize(type) -
(POPCOUNT(ArmCalleeSaveCoreSpills(type)) +
POPCOUNT(ArmCalleeSaveFpSpills(type))) * static_cast<size_t>(kArmPointerSize);
}
constexpr size_t ArmCalleeSaveGpr1Offset(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return ArmCalleeSaveFrameSize(type) -
POPCOUNT(ArmCalleeSaveCoreSpills(type)) * static_cast<size_t>(kArmPointerSize);
}
constexpr size_t ArmCalleeSaveLrOffset(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return ArmCalleeSaveFrameSize(type) -
POPCOUNT(ArmCalleeSaveCoreSpills(type) & (-(1 << LR))) * static_cast<size_t>(kArmPointerSize);
}
diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h
index 6b77200..57376d0 100644
--- a/runtime/arch/arm64/asm_support_arm64.h
+++ b/runtime/arch/arm64/asm_support_arm64.h
@@ -23,6 +23,8 @@
#define FRAME_SIZE_SAVE_REFS_ONLY 96
#define FRAME_SIZE_SAVE_REFS_AND_ARGS 224
#define FRAME_SIZE_SAVE_EVERYTHING 512
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
// The offset from art_quick_read_barrier_mark_introspection to the array switch cases,
// i.e. art_quick_read_barrier_mark_introspection_arrays.
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 9bbcef3..d270bdb 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -19,13 +19,13 @@
#include "arch/arm64/asm_support_arm64.h"
#include "base/bit_utils.h"
+#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/math_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_default_init_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
-#include "entrypoints/entrypoint_utils.h"
-#include "entrypoints/math_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "interpreter/interpreter.h"
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index 0ead732..b9f9d55 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "fault_handler.h"
#include <sys/ucontext.h>
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index e097a33..3d8ca40 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -287,7 +287,7 @@
* when the SP has already been decremented by FRAME_SIZE_SAVE_EVERYTHING
* and saving registers x29 and LR is handled elsewhere.
*/
-.macro SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR
+.macro SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
// Ugly compile-time check, but we only have the preprocessor.
#if (FRAME_SIZE_SAVE_EVERYTHING != 512)
#error "FRAME_SIZE_SAVE_EVERYTHING(ARM64) size not as expected."
@@ -337,7 +337,7 @@
ldr xIP0, [xIP0] // art::Runtime* xIP0 = art::Runtime::instance_;
// ArtMethod* xIP0 = Runtime::instance_->callee_save_methods_[kSaveEverything];
- ldr xIP0, [xIP0, RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET]
+ ldr xIP0, [xIP0, \runtime_method_offset]
// Store ArtMethod* Runtime::callee_save_methods_[kSaveEverything].
str xIP0, [sp]
@@ -350,10 +350,10 @@
* Macro that sets up the callee save frame to conform with
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
*/
-.macro SETUP_SAVE_EVERYTHING_FRAME
+.macro SETUP_SAVE_EVERYTHING_FRAME runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
INCREASE_FRAME 512
SAVE_TWO_REGS x29, xLR, 496
- SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR
+ SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR \runtime_method_offset
.endm
.macro RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
@@ -398,7 +398,7 @@
.endm
.macro RESTORE_SAVE_EVERYTHING_FRAME
- RESTORE_REG x0, 264
+ RESTORE_REG x0, 264
RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
.endm
@@ -1593,10 +1593,10 @@
.endm
// Macro for string and type resolution and initialization.
-.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
.extern \entrypoint
ENTRY \name
- SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl
+ SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset // save everything for stack crawl
mov x1, xSELF // pass Thread::Current
bl \entrypoint // (int32_t index, Thread* self)
cbz w0, 1f // If result is null, deliver the OOME.
@@ -1611,6 +1611,10 @@
END \name
.endm
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT name, entrypoint
+ ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
+.endm
+
.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
cbz w0, 1f // result zero branch over
ret // return
@@ -1629,9 +1633,8 @@
* initializer and deliver the exception on error. On success the static storage base is
* returned.
*/
-ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
-
-ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
@@ -2008,7 +2011,7 @@
*/
.extern artTestSuspendFromCode
ENTRY art_quick_test_suspend
- SETUP_SAVE_EVERYTHING_FRAME // save callee saves for stack crawl
+ SETUP_SAVE_EVERYTHING_FRAME RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET // save callee saves for stack crawl
mov x0, xSELF
bl artTestSuspendFromCode // (Thread*)
RESTORE_SAVE_EVERYTHING_FRAME
@@ -2052,17 +2055,28 @@
* x0 is the conflict ArtMethod.
* xIP1 is a hidden argument that holds the target interface method's dex method index.
*
- * Note that this stub writes to xIP0, xIP1, and x0.
+ * Note that this stub writes to xIP0, xIP1, x13-x15, and x0.
*/
- .extern artInvokeInterfaceTrampoline
+ .extern artLookupResolvedMethod
ENTRY art_quick_imt_conflict_trampoline
ldr xIP0, [sp, #0] // Load referrer
+ ubfx x15, xIP1, #0, #METHOD_DEX_CACHE_HASH_BITS // Calculate DexCache method slot index.
ldr xIP0, [xIP0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_64] // Load dex cache methods array
- ldr xIP0, [xIP0, xIP1, lsl #POINTER_SIZE_SHIFT] // Load interface method
+ add xIP0, xIP0, x15, lsl #(POINTER_SIZE_SHIFT + 1) // Load DexCache method slot address.
+
+ // Relaxed atomic load x14:x15 from the dex cache slot.
+.Limt_conflict_trampoline_retry_load:
+ ldxp x14, x15, [xIP0]
+ stxp w13, x14, x15, [xIP0]
+ cbnz w13, .Limt_conflict_trampoline_retry_load
+
+ cmp x15, xIP1 // Compare method index to see if we had a DexCache method hit.
+ bne .Limt_conflict_trampoline_dex_cache_miss
+.Limt_conflict_trampoline_have_interface_method:
ldr xIP1, [x0, #ART_METHOD_JNI_OFFSET_64] // Load ImtConflictTable
ldr x0, [xIP1] // Load first entry in ImtConflictTable.
.Limt_table_iterate:
- cmp x0, xIP0
+ cmp x0, x14
// Branch if found. Benchmarks have shown doing a branch here is better.
beq .Limt_table_found
// If the entry is null, the interface method is not in the ImtConflictTable.
@@ -2079,8 +2093,46 @@
.Lconflict_trampoline:
// Call the runtime stub to populate the ImtConflictTable and jump to the
// resolved method.
- mov x0, xIP0 // Load interface method
+ mov x0, x14 // Load interface method
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
+.Limt_conflict_trampoline_dex_cache_miss:
+ // We're not creating a proper runtime method frame here,
+ // artLookupResolvedMethod() is not allowed to walk the stack.
+
+ // Save GPR args and return address, allocate space for FPR args, align stack.
+ SAVE_TWO_REGS_INCREASE_FRAME x0, x1, (8 * 8 + 8 * 8 + 8 + 8)
+ SAVE_TWO_REGS x2, x3, 16
+ SAVE_TWO_REGS x4, x5, 32
+ SAVE_TWO_REGS x6, x7, 48
+ SAVE_REG xLR, (8 * 8 + 8 * 8 + 8)
+
+ // Save FPR args.
+ stp d0, d1, [sp, #64]
+ stp d2, d3, [sp, #80]
+ stp d4, d5, [sp, #96]
+ stp d6, d7, [sp, #112]
+
+ mov x0, xIP1 // Pass method index.
+ ldr x1, [sp, #(8 * 8 + 8 * 8 + 8 + 8)] // Pass referrer.
+ bl artLookupResolvedMethod // (uint32_t method_index, ArtMethod* referrer)
+ mov x14, x0 // Move the interface method to x14 where the loop above expects it.
+
+ // Restore FPR args.
+ ldp d0, d1, [sp, #64]
+ ldp d2, d3, [sp, #80]
+ ldp d4, d5, [sp, #96]
+ ldp d6, d7, [sp, #112]
+
+ // Restore GPR args and return address.
+ RESTORE_REG xLR, (8 * 8 + 8 * 8 + 8)
+ RESTORE_TWO_REGS x2, x3, 16
+ RESTORE_TWO_REGS x4, x5, 32
+ RESTORE_TWO_REGS x6, x7, 48
+ RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, (8 * 8 + 8 * 8 + 8 + 8)
+
+ // If the method wasn't resolved, skip the lookup and go to artInvokeInterfaceTrampoline().
+ cbz x14, .Lconflict_trampoline
+ b .Limt_conflict_trampoline_have_interface_method
END art_quick_imt_conflict_trampoline
ENTRY art_quick_resolution_trampoline
diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/quick_method_frame_info_arm64.h
index c231d4d..3e6f6c6 100644
--- a/runtime/arch/arm64/quick_method_frame_info_arm64.h
+++ b/runtime/arch/arm64/quick_method_frame_info_arm64.h
@@ -80,6 +80,7 @@
(1 << art::arm64::D30) | (1 << art::arm64::D31);
constexpr uint32_t Arm64CalleeSaveCoreSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kArm64CalleeSaveAlwaysSpills | kArm64CalleeSaveRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveArgSpills : 0) |
(type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveAllSpills : 0) |
@@ -87,6 +88,7 @@
}
constexpr uint32_t Arm64CalleeSaveFpSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) |
(type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) |
@@ -94,29 +96,34 @@
}
constexpr uint32_t Arm64CalleeSaveFrameSize(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return RoundUp((POPCOUNT(Arm64CalleeSaveCoreSpills(type)) /* gprs */ +
POPCOUNT(Arm64CalleeSaveFpSpills(type)) /* fprs */ +
1 /* Method* */) * static_cast<size_t>(kArm64PointerSize), kStackAlignment);
}
constexpr QuickMethodFrameInfo Arm64CalleeSaveMethodFrameInfo(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return QuickMethodFrameInfo(Arm64CalleeSaveFrameSize(type),
Arm64CalleeSaveCoreSpills(type),
Arm64CalleeSaveFpSpills(type));
}
constexpr size_t Arm64CalleeSaveFpr1Offset(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return Arm64CalleeSaveFrameSize(type) -
(POPCOUNT(Arm64CalleeSaveCoreSpills(type)) +
POPCOUNT(Arm64CalleeSaveFpSpills(type))) * static_cast<size_t>(kArm64PointerSize);
}
constexpr size_t Arm64CalleeSaveGpr1Offset(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return Arm64CalleeSaveFrameSize(type) -
POPCOUNT(Arm64CalleeSaveCoreSpills(type)) * static_cast<size_t>(kArm64PointerSize);
}
constexpr size_t Arm64CalleeSaveLrOffset(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return Arm64CalleeSaveFrameSize(type) -
POPCOUNT(Arm64CalleeSaveCoreSpills(type) & (-(1 << LR))) *
static_cast<size_t>(kArm64PointerSize);
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index 43c1711..ed8ff60 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -21,7 +21,6 @@
#include "base/casts.h"
#include "utils.h"
-
#include "arm/instruction_set_features_arm.h"
#include "arm64/instruction_set_features_arm64.h"
#include "mips/instruction_set_features_mips.h"
diff --git a/runtime/arch/memcmp16_test.cc b/runtime/arch/memcmp16_test.cc
index 9ba7de1..2f3639c 100644
--- a/runtime/arch/memcmp16_test.cc
+++ b/runtime/arch/memcmp16_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "gtest/gtest.h"
#include "memcmp16.h"
+#include "gtest/gtest.h"
+
class RandGen {
public:
explicit RandGen(uint32_t seed) : val_(seed) {}
diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h
index 9d8572f..2edd63f 100644
--- a/runtime/arch/mips/asm_support_mips.h
+++ b/runtime/arch/mips/asm_support_mips.h
@@ -23,6 +23,8 @@
#define FRAME_SIZE_SAVE_REFS_ONLY 48
#define FRAME_SIZE_SAVE_REFS_AND_ARGS 112
#define FRAME_SIZE_SAVE_EVERYTHING 256
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
// &art_quick_read_barrier_mark_introspection is the first of many entry points:
// 21 entry points for long field offsets, large array indices and variable array indices
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 3010246..75cfc41 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -18,12 +18,12 @@
#include "arch/mips/asm_support_mips.h"
#include "atomic.h"
+#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/math_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_entrypoints.h"
-#include "entrypoints/entrypoint_utils.h"
-#include "entrypoints/math_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "entrypoints_direct_mips.h"
#include "interpreter/interpreter.h"
diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc
index 52a3df5..bf3e96a 100644
--- a/runtime/arch/mips/fault_handler_mips.cc
+++ b/runtime/arch/mips/fault_handler_mips.cc
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "fault_handler.h"
#include <sys/ucontext.h>
+#include "fault_handler.h"
#include "art_method.h"
#include "base/callee_save_type.h"
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index d9abaa0..59a2c10 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -165,13 +165,29 @@
.endm
/*
+ * Individually usable part of macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY.
+ */
+.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8
+ sw $s8, 104($sp)
+ .cfi_rel_offset 30, 104
+ sw $s7, 96($sp)
+ .cfi_rel_offset 23, 96
+ sw $s6, 92($sp)
+ .cfi_rel_offset 22, 92
+ sw $s5, 88($sp)
+ .cfi_rel_offset 21, 88
+ sw $s4, 84($sp)
+ .cfi_rel_offset 20, 84
+.endm
+
+ /*
* Macro that sets up the callee save frame to conform with
* Runtime::CreateCalleeSaveMethod(kSaveRefsAndArgs).
* callee-save: $a1-$a3, $t0-$t1, $s2-$s8, $gp, $ra, $f8-$f19
* (26 total + 1 word padding + method*)
*/
-.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY
- addiu $sp, $sp, -112
+.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY save_s4_thru_s8=1
+ addiu $sp, $sp, -112
.cfi_adjust_cfa_offset 112
// Ugly compile-time check, but we only have the preprocessor.
@@ -179,40 +195,33 @@
#error "FRAME_SIZE_SAVE_REFS_AND_ARGS(MIPS) size not as expected."
#endif
- sw $ra, 108($sp)
+ sw $ra, 108($sp)
.cfi_rel_offset 31, 108
- sw $s8, 104($sp)
- .cfi_rel_offset 30, 104
- sw $gp, 100($sp)
+ sw $gp, 100($sp)
.cfi_rel_offset 28, 100
- sw $s7, 96($sp)
- .cfi_rel_offset 23, 96
- sw $s6, 92($sp)
- .cfi_rel_offset 22, 92
- sw $s5, 88($sp)
- .cfi_rel_offset 21, 88
- sw $s4, 84($sp)
- .cfi_rel_offset 20, 84
- sw $s3, 80($sp)
+ .if \save_s4_thru_s8
+ SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8
+ .endif
+ sw $s3, 80($sp)
.cfi_rel_offset 19, 80
- sw $s2, 76($sp)
+ sw $s2, 76($sp)
.cfi_rel_offset 18, 76
- sw $t1, 72($sp)
+ sw $t1, 72($sp)
.cfi_rel_offset 9, 72
- sw $t0, 68($sp)
+ sw $t0, 68($sp)
.cfi_rel_offset 8, 68
- sw $a3, 64($sp)
+ sw $a3, 64($sp)
.cfi_rel_offset 7, 64
- sw $a2, 60($sp)
+ sw $a2, 60($sp)
.cfi_rel_offset 6, 60
- sw $a1, 56($sp)
+ sw $a1, 56($sp)
.cfi_rel_offset 5, 56
- SDu $f18, $f19, 48, $sp, $t8
- SDu $f16, $f17, 40, $sp, $t8
- SDu $f14, $f15, 32, $sp, $t8
- SDu $f12, $f13, 24, $sp, $t8
- SDu $f10, $f11, 16, $sp, $t8
- SDu $f8, $f9, 8, $sp, $t8
+ SDu $f18, $f19, 48, $sp, $t8
+ SDu $f16, $f17, 40, $sp, $t8
+ SDu $f14, $f15, 32, $sp, $t8
+ SDu $f12, $f13, 24, $sp, $t8
+ SDu $f10, $f11, 16, $sp, $t8
+ SDu $f8, $f9, 8, $sp, $t8
# bottom will hold Method*
.endm
@@ -225,8 +234,14 @@
* Allocates ARG_SLOT_SIZE bytes at the bottom of the stack for arg slots.
* Reserves FRAME_SIZE_SAVE_REFS_AND_ARGS + ARG_SLOT_SIZE bytes on the stack
*/
-.macro SETUP_SAVE_REFS_AND_ARGS_FRAME
- SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY
+.macro SETUP_SAVE_REFS_AND_ARGS_FRAME save_s4_thru_s8_only=0
+ .if \save_s4_thru_s8_only
+ // It is expected that `SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY /* save_s4_thru_s8 */ 0`
+ // has been done prior to `SETUP_SAVE_REFS_AND_ARGS_FRAME /* save_s4_thru_s8_only */ 1`.
+ SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8
+ .else
+ SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY
+ .endif
lw $t0, %got(_ZN3art7Runtime9instance_E)($gp)
lw $t0, 0($t0)
lw $t0, RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET($t0)
@@ -254,44 +269,64 @@
.cfi_adjust_cfa_offset ARG_SLOT_SIZE
.endm
-.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME
- addiu $sp, $sp, ARG_SLOT_SIZE # remove argument slots on the stack
- .cfi_adjust_cfa_offset -ARG_SLOT_SIZE
- lw $ra, 108($sp)
- .cfi_restore 31
- lw $s8, 104($sp)
- .cfi_restore 30
- lw $gp, 100($sp)
+ /*
+ * Individually usable part of macro RESTORE_SAVE_REFS_AND_ARGS_FRAME.
+ */
+.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME_GP
+ lw $gp, 100($sp)
.cfi_restore 28
- lw $s7, 96($sp)
- .cfi_restore 23
- lw $s6, 92($sp)
- .cfi_restore 22
- lw $s5, 88($sp)
- .cfi_restore 21
- lw $s4, 84($sp)
- .cfi_restore 20
- lw $s3, 80($sp)
- .cfi_restore 19
- lw $s2, 76($sp)
- .cfi_restore 18
- lw $t1, 72($sp)
- .cfi_restore 9
- lw $t0, 68($sp)
- .cfi_restore 8
- lw $a3, 64($sp)
- .cfi_restore 7
- lw $a2, 60($sp)
- .cfi_restore 6
- lw $a1, 56($sp)
+.endm
+
+ /*
+ * Individually usable part of macro RESTORE_SAVE_REFS_AND_ARGS_FRAME.
+ */
+.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1
+ lw $a1, 56($sp)
.cfi_restore 5
- LDu $f18, $f19, 48, $sp, $t8
- LDu $f16, $f17, 40, $sp, $t8
- LDu $f14, $f15, 32, $sp, $t8
- LDu $f12, $f13, 24, $sp, $t8
- LDu $f10, $f11, 16, $sp, $t8
- LDu $f8, $f9, 8, $sp, $t8
- addiu $sp, $sp, 112 # pop frame
+.endm
+
+.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME restore_s4_thru_s8=1, remove_arg_slots=1
+ .if \remove_arg_slots
+ addiu $sp, $sp, ARG_SLOT_SIZE # Remove argument slots from the stack.
+ .cfi_adjust_cfa_offset -ARG_SLOT_SIZE
+ .endif
+ lw $ra, 108($sp)
+ .cfi_restore 31
+ .if \restore_s4_thru_s8
+ lw $s8, 104($sp)
+ .cfi_restore 30
+ .endif
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME_GP
+ .if \restore_s4_thru_s8
+ lw $s7, 96($sp)
+ .cfi_restore 23
+ lw $s6, 92($sp)
+ .cfi_restore 22
+ lw $s5, 88($sp)
+ .cfi_restore 21
+ lw $s4, 84($sp)
+ .cfi_restore 20
+ .endif
+ lw $s3, 80($sp)
+ .cfi_restore 19
+ lw $s2, 76($sp)
+ .cfi_restore 18
+ lw $t1, 72($sp)
+ .cfi_restore 9
+ lw $t0, 68($sp)
+ .cfi_restore 8
+ lw $a3, 64($sp)
+ .cfi_restore 7
+ lw $a2, 60($sp)
+ .cfi_restore 6
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1
+ LDu $f18, $f19, 48, $sp, $t8
+ LDu $f16, $f17, 40, $sp, $t8
+ LDu $f14, $f15, 32, $sp, $t8
+ LDu $f12, $f13, 24, $sp, $t8
+ LDu $f10, $f11, 16, $sp, $t8
+ LDu $f8, $f9, 8, $sp, $t8
+ addiu $sp, $sp, 112 # Pop frame.
.cfi_adjust_cfa_offset -112
.endm
@@ -826,9 +861,10 @@
* On success this wrapper will restore arguments and *jump* to the target, leaving the lr
* pointing back to the original caller.
*/
-.macro INVOKE_TRAMPOLINE_BODY cxx_name
+.macro INVOKE_TRAMPOLINE_BODY cxx_name, save_s4_thru_s8_only=0
.extern \cxx_name
- SETUP_SAVE_REFS_AND_ARGS_FRAME # save callee saves in case allocation triggers GC
+ SETUP_SAVE_REFS_AND_ARGS_FRAME \save_s4_thru_s8_only # save callee saves in case
+ # allocation triggers GC
move $a2, rSELF # pass Thread::Current
la $t9, \cxx_name
jalr $t9 # (method_idx, this, Thread*, $sp)
@@ -2063,39 +2099,83 @@
* a0 is the conflict ArtMethod.
* t7 is a hidden argument that holds the target interface method's dex method index.
*
- * Note that this stub writes to a0, t7 and t8.
+ * Note that this stub writes to v0-v1, a0, t2-t9, f0-f7.
*/
+ .extern artLookupResolvedMethod
+ .extern __atomic_load_8 # For int64_t std::atomic::load(std::memory_order).
ENTRY art_quick_imt_conflict_trampoline
- lw $t8, 0($sp) # Load referrer.
- lw $t8, ART_METHOD_DEX_CACHE_METHODS_OFFSET_32($t8) # Load dex cache methods array.
- sll $t7, $t7, POINTER_SIZE_SHIFT # Calculate offset.
- addu $t7, $t8, $t7 # Add offset to base.
- lw $t7, 0($t7) # Load interface method.
- lw $a0, ART_METHOD_JNI_OFFSET_32($a0) # Load ImtConflictTable.
+ SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY /* save_s4_thru_s8 */ 0
+
+ lw $t8, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $t8 = referrer.
+ la $t9, __atomic_load_8
+ addiu $sp, $sp, -ARG_SLOT_SIZE # Reserve argument slots on the stack.
+ .cfi_adjust_cfa_offset ARG_SLOT_SIZE
+ lw $t8, ART_METHOD_DEX_CACHE_METHODS_OFFSET_32($t8) # $t8 = dex cache methods array.
+
+ move $s2, $t7 # $s2 = method index (callee-saved).
+ lw $s3, ART_METHOD_JNI_OFFSET_32($a0) # $s3 = ImtConflictTable (callee-saved).
+
+ sll $t7, $t7, 32 - METHOD_DEX_CACHE_HASH_BITS # $t7 = slot index in top bits, zeroes below.
+ srl $t7, $t7, 32 - METHOD_DEX_CACHE_HASH_BITS - (POINTER_SIZE_SHIFT + 1)
+ # $t7 = slot offset.
+
+ li $a1, STD_MEMORY_ORDER_RELAXED # $a1 = std::memory_order_relaxed.
+ jalr $t9 # [$v0, $v1] = __atomic_load_8($a0, $a1).
+ addu $a0, $t8, $t7 # $a0 = DexCache method slot address.
+
+ bne $v1, $s2, .Limt_conflict_trampoline_dex_cache_miss # Branch if method index miss.
+ addiu $sp, $sp, ARG_SLOT_SIZE # Remove argument slots from the stack.
+ .cfi_adjust_cfa_offset -ARG_SLOT_SIZE
.Limt_table_iterate:
- lw $t8, 0($a0) # Load next entry in ImtConflictTable.
+ lw $t8, 0($s3) # Load next entry in ImtConflictTable.
# Branch if found.
- beq $t8, $t7, .Limt_table_found
+ beq $t8, $v0, .Limt_table_found
nop
# If the entry is null, the interface method is not in the ImtConflictTable.
beqz $t8, .Lconflict_trampoline
nop
# Iterate over the entries of the ImtConflictTable.
b .Limt_table_iterate
- addiu $a0, $a0, 2 * __SIZEOF_POINTER__ # Iterate to the next entry.
+ addiu $s3, $s3, 2 * __SIZEOF_POINTER__ # Iterate to the next entry.
.Limt_table_found:
# We successfully hit an entry in the table. Load the target method and jump to it.
- lw $a0, __SIZEOF_POINTER__($a0)
+ .cfi_remember_state
+ lw $a0, __SIZEOF_POINTER__($s3)
lw $t9, ART_METHOD_QUICK_CODE_OFFSET_32($a0)
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME /* restore_s4_thru_s8 */ 0, /* remove_arg_slots */ 0
jalr $zero, $t9
nop
+ .cfi_restore_state
.Lconflict_trampoline:
# Call the runtime stub to populate the ImtConflictTable and jump to the resolved method.
- move $a0, $t7 # Load interface method.
- INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
+ .cfi_remember_state
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME_GP # Restore clobbered $gp.
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1 # Restore this.
+ move $a0, $v0 # Load interface method.
+ INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline, /* save_s4_thru_s8_only */ 1
+ .cfi_restore_state
+
+.Limt_conflict_trampoline_dex_cache_miss:
+ # We're not creating a proper runtime method frame here,
+ # artLookupResolvedMethod() is not allowed to walk the stack.
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME_GP # Restore clobbered $gp.
+ lw $a1, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $a1 = referrer.
+ la $t9, artLookupResolvedMethod
+ addiu $sp, $sp, -ARG_SLOT_SIZE # Reserve argument slots on the stack.
+ .cfi_adjust_cfa_offset ARG_SLOT_SIZE
+ jalr $t9 # (uint32_t method_index, ArtMethod* referrer).
+ move $a0, $s2 # $a0 = method index.
+
+ # If the method wasn't resolved, skip the lookup and go to artInvokeInterfaceTrampoline().
+ beqz $v0, .Lconflict_trampoline
+ addiu $sp, $sp, ARG_SLOT_SIZE # Remove argument slots from the stack.
+ .cfi_adjust_cfa_offset -ARG_SLOT_SIZE
+
+ b .Limt_table_iterate
+ nop
END art_quick_imt_conflict_trampoline
.extern artQuickResolutionTrampoline
diff --git a/runtime/arch/mips/quick_method_frame_info_mips.h b/runtime/arch/mips/quick_method_frame_info_mips.h
index 01879a5..45a21ab 100644
--- a/runtime/arch/mips/quick_method_frame_info_mips.h
+++ b/runtime/arch/mips/quick_method_frame_info_mips.h
@@ -65,6 +65,7 @@
(1 << art::mips::F28) | (1 << art::mips::F29) | (1 << art::mips::F30) | (1u << art::mips::F31);
constexpr uint32_t MipsCalleeSaveCoreSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kMipsCalleeSaveAlwaysSpills | kMipsCalleeSaveRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveArgSpills : 0) |
(type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllSpills : 0) |
@@ -72,6 +73,7 @@
}
constexpr uint32_t MipsCalleeSaveFPSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kMipsCalleeSaveFpAlwaysSpills | kMipsCalleeSaveFpRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveFpArgSpills : 0) |
(type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllFPSpills : 0) |
@@ -79,12 +81,14 @@
}
constexpr uint32_t MipsCalleeSaveFrameSize(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return RoundUp((POPCOUNT(MipsCalleeSaveCoreSpills(type)) /* gprs */ +
POPCOUNT(MipsCalleeSaveFPSpills(type)) /* fprs */ +
1 /* Method* */) * static_cast<size_t>(kMipsPointerSize), kStackAlignment);
}
constexpr QuickMethodFrameInfo MipsCalleeSaveMethodFrameInfo(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return QuickMethodFrameInfo(MipsCalleeSaveFrameSize(type),
MipsCalleeSaveCoreSpills(type),
MipsCalleeSaveFPSpills(type));
diff --git a/runtime/arch/mips64/asm_support_mips64.h b/runtime/arch/mips64/asm_support_mips64.h
index 7185da5..a8e907e 100644
--- a/runtime/arch/mips64/asm_support_mips64.h
+++ b/runtime/arch/mips64/asm_support_mips64.h
@@ -27,6 +27,8 @@
#define FRAME_SIZE_SAVE_REFS_AND_ARGS 208
// $f0-$f31, $at, $v0-$v1, $a0-$a7, $t0-$t3, $s0-$s7, $t8-$t9, $gp, $s8, $ra + padding + method*
#define FRAME_SIZE_SAVE_EVERYTHING 496
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
// &art_quick_read_barrier_mark_introspection is the first of many entry points:
// 20 entry points for long field offsets, large array indices and variable array indices
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
index 5e58827..15b3e38 100644
--- a/runtime/arch/mips64/entrypoints_init_mips64.cc
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -19,13 +19,13 @@
#include "arch/mips64/asm_support_mips64.h"
#include "atomic.h"
+#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/math_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_default_init_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
-#include "entrypoints/entrypoint_utils.h"
-#include "entrypoints/math_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "interpreter/interpreter.h"
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index fcbed0e..3b92daa 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -188,7 +188,23 @@
// This assumes the top part of these stack frame types are identical.
#define REFS_AND_ARGS_MINUS_REFS_SIZE (FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY)
-.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL
+ /*
+ * Individually usable part of macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL.
+ */
+.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8
+ sd $s8, 192($sp)
+ .cfi_rel_offset 30, 192
+ sd $s7, 176($sp)
+ .cfi_rel_offset 23, 176
+ sd $s6, 168($sp)
+ .cfi_rel_offset 22, 168
+ sd $s5, 160($sp)
+ .cfi_rel_offset 21, 160
+ sd $s4, 152($sp)
+ .cfi_rel_offset 20, 152
+.endm
+
+.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL save_s4_thru_s8=1
daddiu $sp, $sp, -208
.cfi_adjust_cfa_offset 208
@@ -197,48 +213,40 @@
#error "FRAME_SIZE_SAVE_REFS_AND_ARGS(MIPS64) size not as expected."
#endif
- sd $ra, 200($sp) # = kQuickCalleeSaveFrame_RefAndArgs_LrOffset
+ sd $ra, 200($sp) # = kQuickCalleeSaveFrame_RefAndArgs_LrOffset
.cfi_rel_offset 31, 200
- sd $s8, 192($sp)
- .cfi_rel_offset 30, 192
- sd $t8, 184($sp) # t8 holds caller's gp, now save it to the stack.
- .cfi_rel_offset 28, 184 # Value from gp is pushed, so set the cfi offset accordingly.
- sd $s7, 176($sp)
- .cfi_rel_offset 23, 176
- sd $s6, 168($sp)
- .cfi_rel_offset 22, 168
- sd $s5, 160($sp)
- .cfi_rel_offset 21, 160
- sd $s4, 152($sp)
- .cfi_rel_offset 20, 152
- sd $s3, 144($sp)
+ sd $t8, 184($sp) # t8 holds caller's gp, now save it to the stack.
+ .cfi_rel_offset 28, 184 # Value from gp is pushed, so set the cfi offset accordingly.
+ .if \save_s4_thru_s8
+ SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8
+ .endif
+ sd $s3, 144($sp)
.cfi_rel_offset 19, 144
- sd $s2, 136($sp)
+ sd $s2, 136($sp)
.cfi_rel_offset 18, 136
-
- sd $a7, 128($sp)
+ sd $a7, 128($sp)
.cfi_rel_offset 11, 128
- sd $a6, 120($sp)
+ sd $a6, 120($sp)
.cfi_rel_offset 10, 120
- sd $a5, 112($sp)
+ sd $a5, 112($sp)
.cfi_rel_offset 9, 112
- sd $a4, 104($sp)
+ sd $a4, 104($sp)
.cfi_rel_offset 8, 104
- sd $a3, 96($sp)
+ sd $a3, 96($sp)
.cfi_rel_offset 7, 96
- sd $a2, 88($sp)
+ sd $a2, 88($sp)
.cfi_rel_offset 6, 88
- sd $a1, 80($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset
+ sd $a1, 80($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset
.cfi_rel_offset 5, 80
- s.d $f19, 72($sp)
- s.d $f18, 64($sp)
- s.d $f17, 56($sp)
- s.d $f16, 48($sp)
- s.d $f15, 40($sp)
- s.d $f14, 32($sp)
- s.d $f13, 24($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset
- s.d $f12, 16($sp) # This isn't necessary to store.
+ s.d $f19, 72($sp)
+ s.d $f18, 64($sp)
+ s.d $f17, 56($sp)
+ s.d $f16, 48($sp)
+ s.d $f15, 40($sp)
+ s.d $f14, 32($sp)
+ s.d $f13, 24($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset
+ s.d $f12, 16($sp) # This isn't necessary to store.
# 1x8 bytes padding + Method*
.endm
@@ -248,8 +256,14 @@
* non-moving GC.
* callee-save: padding + $f12-$f19 + $a1-$a7 + $s2-$s7 + $gp + $ra + $s8 = 24 total + 1 words padding + Method*
*/
-.macro SETUP_SAVE_REFS_AND_ARGS_FRAME
- SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL
+.macro SETUP_SAVE_REFS_AND_ARGS_FRAME save_s4_thru_s8_only=0
+ .if \save_s4_thru_s8_only
+ // It is expected that `SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL /* save_s4_thru_s8 */ 0`
+ // has been done prior to `SETUP_SAVE_REFS_AND_ARGS_FRAME /* save_s4_thru_s8_only */ 1`.
+ SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8
+ .else
+ SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL
+ .endif
# load appropriate callee-save-method
ld $t1, %got(_ZN3art7Runtime9instance_E)($gp)
ld $t1, 0($t1)
@@ -264,52 +278,62 @@
sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
.endm
-.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME
- ld $ra, 200($sp)
- .cfi_restore 31
- ld $s8, 192($sp)
- .cfi_restore 30
- ld $t8, 184($sp) # Restore gp back to it's temp storage.
- .cfi_restore 28
- ld $s7, 176($sp)
- .cfi_restore 23
- ld $s6, 168($sp)
- .cfi_restore 22
- ld $s5, 160($sp)
- .cfi_restore 21
- ld $s4, 152($sp)
- .cfi_restore 20
- ld $s3, 144($sp)
- .cfi_restore 19
- ld $s2, 136($sp)
- .cfi_restore 18
-
- ld $a7, 128($sp)
- .cfi_restore 11
- ld $a6, 120($sp)
- .cfi_restore 10
- ld $a5, 112($sp)
- .cfi_restore 9
- ld $a4, 104($sp)
- .cfi_restore 8
- ld $a3, 96($sp)
- .cfi_restore 7
- ld $a2, 88($sp)
- .cfi_restore 6
- ld $a1, 80($sp)
+ /*
+ * Individually usable part of macro RESTORE_SAVE_REFS_AND_ARGS_FRAME.
+ */
+.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1
+ ld $a1, 80($sp)
.cfi_restore 5
+.endm
- l.d $f19, 72($sp)
- l.d $f18, 64($sp)
- l.d $f17, 56($sp)
- l.d $f16, 48($sp)
- l.d $f15, 40($sp)
- l.d $f14, 32($sp)
- l.d $f13, 24($sp)
- l.d $f12, 16($sp)
+.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME restore_s4_thru_s8=1
+ ld $ra, 200($sp)
+ .cfi_restore 31
+ .if \restore_s4_thru_s8
+ ld $s8, 192($sp)
+ .cfi_restore 30
+ .endif
+ ld $t8, 184($sp) # Restore gp back to it's temp storage.
+ .cfi_restore 28
+ .if \restore_s4_thru_s8
+ ld $s7, 176($sp)
+ .cfi_restore 23
+ ld $s6, 168($sp)
+ .cfi_restore 22
+ ld $s5, 160($sp)
+ .cfi_restore 21
+ ld $s4, 152($sp)
+ .cfi_restore 20
+ .endif
+ ld $s3, 144($sp)
+ .cfi_restore 19
+ ld $s2, 136($sp)
+ .cfi_restore 18
+ ld $a7, 128($sp)
+ .cfi_restore 11
+ ld $a6, 120($sp)
+ .cfi_restore 10
+ ld $a5, 112($sp)
+ .cfi_restore 9
+ ld $a4, 104($sp)
+ .cfi_restore 8
+ ld $a3, 96($sp)
+ .cfi_restore 7
+ ld $a2, 88($sp)
+ .cfi_restore 6
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1
+
+ l.d $f19, 72($sp)
+ l.d $f18, 64($sp)
+ l.d $f17, 56($sp)
+ l.d $f16, 48($sp)
+ l.d $f15, 40($sp)
+ l.d $f14, 32($sp)
+ l.d $f13, 24($sp)
+ l.d $f12, 16($sp)
.cpreturn
- daddiu $sp, $sp, 208
+ daddiu $sp, $sp, 208
.cfi_adjust_cfa_offset -208
.endm
@@ -913,9 +937,10 @@
* On success this wrapper will restore arguments and *jump* to the target, leaving the ra
* pointing back to the original caller.
*/
-.macro INVOKE_TRAMPOLINE_BODY cxx_name
+.macro INVOKE_TRAMPOLINE_BODY cxx_name, save_s4_thru_s8_only=0
.extern \cxx_name
- SETUP_SAVE_REFS_AND_ARGS_FRAME # save callee saves in case allocation triggers GC
+ SETUP_SAVE_REFS_AND_ARGS_FRAME \save_s4_thru_s8_only # save callee saves in case
+ # allocation triggers GC
move $a2, rSELF # pass Thread::Current
jal \cxx_name # (method_idx, this, Thread*, $sp)
move $a3, $sp # pass $sp
@@ -1986,38 +2011,69 @@
* a0 is the conflict ArtMethod.
* t0 is a hidden argument that holds the target interface method's dex method index.
*
- * Mote that this stub writes to a0, t0 and t1.
+ * Mote that this stub writes to v0-v1, a0, t0-t3, t8-t9, f0-f11, f20-f23.
*/
+ .extern artLookupResolvedMethod
+ .extern __atomic_load_16 # For __int128_t std::atomic::load(std::memory_order).
ENTRY art_quick_imt_conflict_trampoline
- ld $t1, 0($sp) # Load referrer.
- ld $t1, ART_METHOD_DEX_CACHE_METHODS_OFFSET_64($t1) # Load dex cache methods array.
- dsll $t0, $t0, POINTER_SIZE_SHIFT # Calculate offset.
- daddu $t0, $t1, $t0 # Add offset to base.
- ld $t0, 0($t0) # Load interface method.
- ld $a0, ART_METHOD_JNI_OFFSET_64($a0) # Load ImtConflictTable.
+ SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL /* save_s4_thru_s8 */ 0
+
+ ld $t1, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $t1 = referrer.
+ dla $t9, __atomic_load_16
+ ld $t1, ART_METHOD_DEX_CACHE_METHODS_OFFSET_64($t1) # $t1 = dex cache methods array.
+
+ dext $s2, $t0, 0, 32 # $s2 = zero-extended method index
+ # (callee-saved).
+ ld $s3, ART_METHOD_JNI_OFFSET_64($a0) # $s3 = ImtConflictTable (callee-saved).
+
+ dext $t0, $t0, 0, METHOD_DEX_CACHE_HASH_BITS # $t0 = slot index.
+
+ li $a1, STD_MEMORY_ORDER_RELAXED # $a1 = std::memory_order_relaxed.
+ jalr $t9 # [$v0, $v1] = __atomic_load_16($a0, $a1).
+ dlsa $a0, $t0, $t1, POINTER_SIZE_SHIFT + 1 # $a0 = DexCache method slot address.
+
+ bnec $v1, $s2, .Limt_conflict_trampoline_dex_cache_miss # Branch if method index miss.
.Limt_table_iterate:
- ld $t1, 0($a0) # Load next entry in ImtConflictTable.
+ ld $t1, 0($s3) # Load next entry in ImtConflictTable.
# Branch if found.
- beq $t1, $t0, .Limt_table_found
+ beq $t1, $v0, .Limt_table_found
nop
# If the entry is null, the interface method is not in the ImtConflictTable.
beqzc $t1, .Lconflict_trampoline
# Iterate over the entries of the ImtConflictTable.
- daddiu $a0, $a0, 2 * __SIZEOF_POINTER__ # Iterate to the next entry.
- bc .Limt_table_iterate
+ daddiu $s3, $s3, 2 * __SIZEOF_POINTER__ # Iterate to the next entry.
+ bc .Limt_table_iterate
.Limt_table_found:
# We successfully hit an entry in the table. Load the target method and jump to it.
- ld $a0, __SIZEOF_POINTER__($a0)
+ .cfi_remember_state
+ ld $a0, __SIZEOF_POINTER__($s3)
ld $t9, ART_METHOD_QUICK_CODE_OFFSET_64($a0)
- jr $t9
- .cpreturn # Restore gp from t8 in branch delay slot.
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME /* restore_s4_thru_s8 */ 0
+ jic $t9, 0
+ .cfi_restore_state
.Lconflict_trampoline:
# Call the runtime stub to populate the ImtConflictTable and jump to the resolved method.
- move $a0, $t0 # Load interface method.
- INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
+ .cfi_remember_state
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1 # Restore this.
+ move $a0, $v0 # Load interface method.
+ INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline, /* save_s4_thru_s8_only */ 1
+ .cfi_restore_state
+
+.Limt_conflict_trampoline_dex_cache_miss:
+ # We're not creating a proper runtime method frame here,
+ # artLookupResolvedMethod() is not allowed to walk the stack.
+ dla $t9, artLookupResolvedMethod
+ ld $a1, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $a1 = referrer.
+ jalr $t9 # (uint32_t method_index, ArtMethod* referrer).
+ sll $a0, $s2, 0 # $a0 = sign-extended method index.
+
+ # If the method wasn't resolved, skip the lookup and go to artInvokeInterfaceTrampoline().
+ beqzc $v0, .Lconflict_trampoline
+ nop
+ bc .Limt_table_iterate
END art_quick_imt_conflict_trampoline
.extern artQuickResolutionTrampoline
diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/quick_method_frame_info_mips64.h
index a55ab0e..520f631 100644
--- a/runtime/arch/mips64/quick_method_frame_info_mips64.h
+++ b/runtime/arch/mips64/quick_method_frame_info_mips64.h
@@ -72,6 +72,7 @@
(1 << art::mips64::F30) | (1 << art::mips64::F31);
constexpr uint32_t Mips64CalleeSaveCoreSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kMips64CalleeSaveAlwaysSpills | kMips64CalleeSaveRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) |
(type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveAllSpills : 0) |
@@ -79,6 +80,7 @@
}
constexpr uint32_t Mips64CalleeSaveFpSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kMips64CalleeSaveFpRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) |
(type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) |
@@ -86,12 +88,14 @@
}
constexpr uint32_t Mips64CalleeSaveFrameSize(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return RoundUp((POPCOUNT(Mips64CalleeSaveCoreSpills(type)) /* gprs */ +
POPCOUNT(Mips64CalleeSaveFpSpills(type)) /* fprs */ +
+ 1 /* Method* */) * static_cast<size_t>(kMips64PointerSize), kStackAlignment);
}
constexpr QuickMethodFrameInfo Mips64CalleeSaveMethodFrameInfo(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return QuickMethodFrameInfo(Mips64CalleeSaveFrameSize(type),
Mips64CalleeSaveCoreSpills(type),
Mips64CalleeSaveFpSpills(type));
diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index 2bba08d..737d736 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -23,5 +23,7 @@
#define FRAME_SIZE_SAVE_REFS_ONLY 32
#define FRAME_SIZE_SAVE_REFS_AND_ARGS (32 + 32)
#define FRAME_SIZE_SAVE_EVERYTHING (48 + 64)
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index 798c500..349ce3b 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "fault_handler.h"
#include <sys/ucontext.h>
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 031b36b..4e5e93a 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -226,7 +226,7 @@
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
* when EDI and ESI are already saved.
*/
-MACRO2(SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED, got_reg, temp_reg)
+MACRO3(SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED, got_reg, temp_reg, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
// Save core registers from highest to lowest to agree with core spills bitmap.
// EDI and ESI, or at least placeholders for them, are already on the stack.
PUSH ebp
@@ -252,7 +252,7 @@
movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg)
movl (REG_VAR(temp_reg)), REG_VAR(temp_reg)
// Push save everything callee-save method.
- pushl RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET(REG_VAR(temp_reg))
+ pushl \runtime_method_offset(REG_VAR(temp_reg))
CFI_ADJUST_CFA_OFFSET(4)
// Store esp as the stop quick frame.
movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
@@ -269,20 +269,20 @@
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
* when EDI is already saved.
*/
-MACRO2(SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED, got_reg, temp_reg)
+MACRO3(SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED, got_reg, temp_reg, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
// Save core registers from highest to lowest to agree with core spills bitmap.
// EDI, or at least a placeholder for it, is already on the stack.
PUSH esi
- SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED RAW_VAR(got_reg), RAW_VAR(temp_reg)
+ SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED RAW_VAR(got_reg), RAW_VAR(temp_reg), \runtime_method_offset
END_MACRO
/*
* Macro that sets up the callee save frame to conform with
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
*/
-MACRO2(SETUP_SAVE_EVERYTHING_FRAME, got_reg, temp_reg)
+MACRO3(SETUP_SAVE_EVERYTHING_FRAME, got_reg, temp_reg, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
PUSH edi
- SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED RAW_VAR(got_reg), RAW_VAR(temp_reg)
+ SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED RAW_VAR(got_reg), RAW_VAR(temp_reg), \runtime_method_offset
END_MACRO
MACRO0(RESTORE_SAVE_EVERYTHING_FRAME_FRPS)
@@ -923,9 +923,9 @@
END_MACRO
// Macro for string and type resolution and initialization.
-MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name)
+MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
DEFINE_FUNCTION VAR(c_name)
- SETUP_SAVE_EVERYTHING_FRAME ebx, ebx // save ref containing registers for GC
+ SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, \runtime_method_offset // save ref containing registers for GC
// Outgoing argument set up
subl MACRO_LITERAL(8), %esp // push padding
CFI_ADJUST_CFA_OFFSET(8)
@@ -947,6 +947,10 @@
END_FUNCTION VAR(c_name)
END_MACRO
+MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT, c_name, cxx_name)
+ ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
+END_MACRO
+
MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
testl %eax, %eax // eax == 0 ?
jz 1f // if eax == 0 goto 1
@@ -1270,8 +1274,8 @@
GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
-ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
-ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
@@ -1598,7 +1602,7 @@
END_FUNCTION art_quick_memcpy
DEFINE_FUNCTION art_quick_test_suspend
- SETUP_SAVE_EVERYTHING_FRAME ebx, ebx // save everything for GC
+ SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET // save everything for GC
// Outgoing argument set up
subl MACRO_LITERAL(12), %esp // push padding
CFI_ADJUST_CFA_OFFSET(12)
@@ -1780,35 +1784,90 @@
*/
DEFINE_FUNCTION art_quick_imt_conflict_trampoline
PUSH EDI
- movl 8(%esp), %edi // Load referrer
- movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi // Load dex cache methods array
+ PUSH ESI
+ PUSH EDX
+ movl 16(%esp), %edi // Load referrer.
+ movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi // Load dex cache methods array.
pushl ART_METHOD_JNI_OFFSET_32(%eax) // Push ImtConflictTable.
CFI_ADJUST_CFA_OFFSET(4)
- movd %xmm7, %eax // get target method index stored in xmm7
- movl 0(%edi, %eax, __SIZEOF_POINTER__), %edi // Load interface method
- popl %eax // Pop ImtConflictTable.
+ movd %xmm7, %eax // Get target method index stored in xmm7.
+ movl %eax, %esi // Remember method index in ESI.
+ andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax // Calculate DexCache method slot index.
+ leal 0(%edi, %eax, 2 * __SIZEOF_POINTER__), %edi // Load DexCache method slot address.
+ mov %ecx, %edx // Make EDX:EAX == ECX:EBX so that LOCK CMPXCHG8B makes no changes.
+ mov %ebx, %eax // (The actual value does not matter.)
+ lock cmpxchg8b (%edi) // Relaxed atomic load EDX:EAX from the dex cache slot.
+ popl %edi // Pop ImtConflictTable.
CFI_ADJUST_CFA_OFFSET(-4)
+ cmp %edx, %esi // Compare method index to see if we had a DexCache method hit.
+ jne .Limt_conflict_trampoline_dex_cache_miss
.Limt_table_iterate:
- cmpl %edi, 0(%eax)
+ cmpl %eax, 0(%edi)
jne .Limt_table_next_entry
// We successfully hit an entry in the table. Load the target method
// and jump to it.
+ movl __SIZEOF_POINTER__(%edi), %eax
+ CFI_REMEMBER_STATE
+ POP EDX
+ POP ESI
POP EDI
- movl __SIZEOF_POINTER__(%eax), %eax
jmp *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)
+ CFI_RESTORE_STATE
.Limt_table_next_entry:
// If the entry is null, the interface method is not in the ImtConflictTable.
- cmpl LITERAL(0), 0(%eax)
+ cmpl LITERAL(0), 0(%edi)
jz .Lconflict_trampoline
// Iterate over the entries of the ImtConflictTable.
- addl LITERAL(2 * __SIZEOF_POINTER__), %eax
+ addl LITERAL(2 * __SIZEOF_POINTER__), %edi
jmp .Limt_table_iterate
.Lconflict_trampoline:
// Call the runtime stub to populate the ImtConflictTable and jump to the
// resolved method.
- movl %edi, %eax // Load interface method
+ CFI_REMEMBER_STATE
+ POP EDX
+ POP ESI
POP EDI
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
+ CFI_RESTORE_STATE
+.Limt_conflict_trampoline_dex_cache_miss:
+ // We're not creating a proper runtime method frame here,
+ // artLookupResolvedMethod() is not allowed to walk the stack.
+
+ // Save core register args; EDX is already saved.
+ PUSH ebx
+ PUSH ecx
+
+ // Save FPR args.
+ subl MACRO_LITERAL(32), %esp
+ CFI_ADJUST_CFA_OFFSET(32)
+ movsd %xmm0, 0(%esp)
+ movsd %xmm1, 8(%esp)
+ movsd %xmm2, 16(%esp)
+ movsd %xmm3, 24(%esp)
+
+ pushl 32+8+16(%esp) // Pass referrer.
+ CFI_ADJUST_CFA_OFFSET(4)
+ pushl %esi // Pass method index.
+ CFI_ADJUST_CFA_OFFSET(4)
+ call SYMBOL(artLookupResolvedMethod) // (uint32_t method_index, ArtMethod* referrer)
+ addl LITERAL(8), %esp // Pop arguments.
+ CFI_ADJUST_CFA_OFFSET(-8)
+
+ // Restore FPR args.
+ movsd 0(%esp), %xmm0
+ movsd 8(%esp), %xmm1
+ movsd 16(%esp), %xmm2
+ movsd 24(%esp), %xmm3
+ addl MACRO_LITERAL(32), %esp
+ CFI_ADJUST_CFA_OFFSET(-32)
+
+ // Restore core register args.
+ POP ecx
+ POP ebx
+
+ cmp LITERAL(0), %eax // If the method wasn't resolved,
+ je .Lconflict_trampoline // skip the lookup and go to artInvokeInterfaceTrampoline().
+ jmp .Limt_table_iterate
END_FUNCTION art_quick_imt_conflict_trampoline
DEFINE_FUNCTION art_quick_resolution_trampoline
diff --git a/runtime/arch/x86/quick_method_frame_info_x86.h b/runtime/arch/x86/quick_method_frame_info_x86.h
index 8342c9f..9a66333 100644
--- a/runtime/arch/x86/quick_method_frame_info_x86.h
+++ b/runtime/arch/x86/quick_method_frame_info_x86.h
@@ -57,23 +57,27 @@
(1 << art::x86::XMM6) | (1 << art::x86::XMM7);
constexpr uint32_t X86CalleeSaveCoreSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kX86CalleeSaveAlwaysSpills | kX86CalleeSaveRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveArgSpills : 0) |
(type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveEverythingSpills : 0);
}
constexpr uint32_t X86CalleeSaveFpSpills(CalleeSaveType type) {
- return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) |
- (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0);
+ type = GetCanonicalCalleeSaveType(type);
+ return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) |
+ (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0);
}
constexpr uint32_t X86CalleeSaveFrameSize(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return RoundUp((POPCOUNT(X86CalleeSaveCoreSpills(type)) /* gprs */ +
2 * POPCOUNT(X86CalleeSaveFpSpills(type)) /* fprs */ +
1 /* Method* */) * static_cast<size_t>(kX86PointerSize), kStackAlignment);
}
constexpr QuickMethodFrameInfo X86CalleeSaveMethodFrameInfo(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return QuickMethodFrameInfo(X86CalleeSaveFrameSize(type),
X86CalleeSaveCoreSpills(type),
X86CalleeSaveFpSpills(type));
diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h
index a4446d3..51befbe 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.h
+++ b/runtime/arch/x86_64/asm_support_x86_64.h
@@ -23,5 +23,7 @@
#define FRAME_SIZE_SAVE_REFS_ONLY (64 + 4*8)
#define FRAME_SIZE_SAVE_REFS_AND_ARGS (112 + 12*8)
#define FRAME_SIZE_SAVE_EVERYTHING (144 + 16*8)
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
+#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
#endif // ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 1e56e8a..5f7380f 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -17,13 +17,13 @@
#include <math.h>
#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/math_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#if !defined(__APPLE__)
#include "entrypoints/quick/quick_default_init_entrypoints.h"
#endif
#include "entrypoints/quick/quick_entrypoints.h"
-#include "entrypoints/math_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "interpreter/interpreter.h"
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index ad06873..73e8610 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -272,7 +272,7 @@
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
* when R14 and R15 are already saved.
*/
-MACRO0(SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED)
+MACRO1(SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
#if defined(__APPLE__)
int3
int3
@@ -316,7 +316,7 @@
movq %xmm14, 120(%rsp)
movq %xmm15, 128(%rsp)
// Push ArtMethod* for save everything frame method.
- pushq RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET(%r10)
+ pushq \runtime_method_offset(%r10)
CFI_ADJUST_CFA_OFFSET(8)
// Store rsp as the top quick frame.
movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET
@@ -334,18 +334,18 @@
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
* when R15 is already saved.
*/
-MACRO0(SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED)
+MACRO1(SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
PUSH r14
- SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED
+ SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED \runtime_method_offset
END_MACRO
/*
* Macro that sets up the callee save frame to conform with
* Runtime::CreateCalleeSaveMethod(kSaveEverything)
*/
-MACRO0(SETUP_SAVE_EVERYTHING_FRAME)
+MACRO1(SETUP_SAVE_EVERYTHING_FRAME, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
PUSH r15
- SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED
+ SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED \runtime_method_offset
END_MACRO
MACRO0(RESTORE_SAVE_EVERYTHING_FRAME_FRPS)
@@ -951,9 +951,9 @@
END_MACRO
// Macro for string and type resolution and initialization.
-MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name)
+MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
DEFINE_FUNCTION VAR(c_name)
- SETUP_SAVE_EVERYTHING_FRAME // save everything for GC
+ SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset // save everything for GC
// Outgoing argument set up
movl %eax, %edi // pass string index
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
@@ -970,6 +970,10 @@
END_FUNCTION VAR(c_name)
END_MACRO
+MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT, c_name, cxx_name)
+ ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
+END_MACRO
+
MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
testq %rax, %rax // rax == 0 ?
jz 1f // if rax == 0 goto 1
@@ -1290,8 +1294,8 @@
ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedRegionTLAB
END_FUNCTION art_quick_alloc_object_initialized_region_tlab
-ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
-ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
@@ -1575,7 +1579,7 @@
END_FUNCTION art_quick_memcpy
DEFINE_FUNCTION art_quick_test_suspend
- SETUP_SAVE_EVERYTHING_FRAME // save everything for GC
+ SETUP_SAVE_EVERYTHING_FRAME RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET // save everything for GC
// Outgoing argument set up
movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current()
call SYMBOL(artTestSuspendFromCode) // (Thread*)
@@ -1641,17 +1645,29 @@
int3
int3
#else
- movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer
- movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10 // Load dex cache methods array
- movq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load interface method
+ movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer.
+ movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10 // Load dex cache methods array.
+ mov %eax, %r11d // Remember method index in R11.
+ andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax // Calculate DexCache method slot index.
+ shll LITERAL(1), %eax // Multiply by 2 as entries have size 2 * __SIZEOF_POINTER__.
+ leaq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load DexCache method slot address.
+ PUSH rdx // Preserve RDX as we need to clobber it by LOCK CMPXCHG16B.
+ mov %rcx, %rdx // Make RDX:RAX == RCX:RBX so that LOCK CMPXCHG16B makes no changes.
+ mov %rbx, %rax // (The actual value does not matter.)
+ lock cmpxchg16b (%r10) // Relaxed atomic load RDX:RAX from the dex cache slot.
movq ART_METHOD_JNI_OFFSET_64(%rdi), %rdi // Load ImtConflictTable
+ cmp %rdx, %r11 // Compare method index to see if we had a DexCache method hit.
+ jne .Limt_conflict_trampoline_dex_cache_miss
.Limt_table_iterate:
- cmpq %r10, 0(%rdi)
+ cmpq %rax, 0(%rdi)
jne .Limt_table_next_entry
// We successfully hit an entry in the table. Load the target method
// and jump to it.
movq __SIZEOF_POINTER__(%rdi), %rdi
+ CFI_REMEMBER_STATE
+ POP rdx
jmp *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi)
+ CFI_RESTORE_STATE
.Limt_table_next_entry:
// If the entry is null, the interface method is not in the ImtConflictTable.
cmpq LITERAL(0), 0(%rdi)
@@ -1662,8 +1678,66 @@
.Lconflict_trampoline:
// Call the runtime stub to populate the ImtConflictTable and jump to the
// resolved method.
- movq %r10, %rdi // Load interface method
+ CFI_REMEMBER_STATE
+ POP rdx
+ movq %rax, %rdi // Load interface method
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
+ CFI_RESTORE_STATE
+.Limt_conflict_trampoline_dex_cache_miss:
+ // We're not creating a proper runtime method frame here,
+ // artLookupResolvedMethod() is not allowed to walk the stack.
+
+ // Save GPR args and ImtConflictTable; RDX is already saved.
+ PUSH r9 // Quick arg 5.
+ PUSH r8 // Quick arg 4.
+ PUSH rsi // Quick arg 1.
+ PUSH rcx // Quick arg 3.
+ PUSH rdi // ImtConflictTable
+ // Save FPR args and callee-saves, align stack to 16B.
+ subq MACRO_LITERAL(12 * 8 + 8), %rsp
+ CFI_ADJUST_CFA_OFFSET(12 * 8 + 8)
+ movq %xmm0, 0(%rsp)
+ movq %xmm1, 8(%rsp)
+ movq %xmm2, 16(%rsp)
+ movq %xmm3, 24(%rsp)
+ movq %xmm4, 32(%rsp)
+ movq %xmm5, 40(%rsp)
+ movq %xmm6, 48(%rsp)
+ movq %xmm7, 56(%rsp)
+ movq %xmm12, 64(%rsp) // XMM12-15 are callee-save in ART compiled code ABI
+ movq %xmm13, 72(%rsp) // but caller-save in native ABI.
+ movq %xmm14, 80(%rsp)
+ movq %xmm15, 88(%rsp)
+
+ movq %r11, %rdi // Pass method index.
+ movq 12 * 8 + 8 + 6 * 8 + 8(%rsp), %rsi // Pass referrer.
+ call SYMBOL(artLookupResolvedMethod) // (uint32_t method_index, ArtMethod* referrer)
+
+ // Restore FPRs.
+ movq 0(%rsp), %xmm0
+ movq 8(%rsp), %xmm1
+ movq 16(%rsp), %xmm2
+ movq 24(%rsp), %xmm3
+ movq 32(%rsp), %xmm4
+ movq 40(%rsp), %xmm5
+ movq 48(%rsp), %xmm6
+ movq 56(%rsp), %xmm7
+ movq 64(%rsp), %xmm12
+ movq 72(%rsp), %xmm13
+ movq 80(%rsp), %xmm14
+ movq 88(%rsp), %xmm15
+ addq MACRO_LITERAL(12 * 8 + 8), %rsp
+ CFI_ADJUST_CFA_OFFSET(-(12 * 8 + 8))
+ // Restore ImtConflictTable and GPR args.
+ POP rdi
+ POP rcx
+ POP rsi
+ POP r8
+ POP r9
+
+ cmp LITERAL(0), %rax // If the method wasn't resolved,
+ je .Lconflict_trampoline // skip the lookup and go to artInvokeInterfaceTrampoline().
+ jmp .Limt_table_iterate
#endif // __APPLE__
END_FUNCTION art_quick_imt_conflict_trampoline
diff --git a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h b/runtime/arch/x86_64/quick_method_frame_info_x86_64.h
index 425d616..ebf976e 100644
--- a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h
+++ b/runtime/arch/x86_64/quick_method_frame_info_x86_64.h
@@ -56,24 +56,28 @@
(1 << art::x86_64::XMM10) | (1 << art::x86_64::XMM11);
constexpr uint32_t X86_64CalleeSaveCoreSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kX86_64CalleeSaveAlwaysSpills | kX86_64CalleeSaveRefSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveArgSpills : 0) |
(type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveEverythingSpills : 0);
}
constexpr uint32_t X86_64CalleeSaveFpSpills(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return kX86_64CalleeSaveFpSpills |
(type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0) |
(type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveFpEverythingSpills : 0);
}
constexpr uint32_t X86_64CalleeSaveFrameSize(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return RoundUp((POPCOUNT(X86_64CalleeSaveCoreSpills(type)) /* gprs */ +
POPCOUNT(X86_64CalleeSaveFpSpills(type)) /* fprs */ +
1 /* Method* */) * static_cast<size_t>(kX86_64PointerSize), kStackAlignment);
}
constexpr QuickMethodFrameInfo X86_64CalleeSaveMethodFrameInfo(CalleeSaveType type) {
+ type = GetCanonicalCalleeSaveType(type);
return QuickMethodFrameInfo(X86_64CalleeSaveFrameSize(type),
X86_64CalleeSaveCoreSpills(type),
X86_64CalleeSaveFpSpills(type));
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index a8a58e1..057f58c 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -22,14 +22,14 @@
#include "base/logging.h"
#include "class_linker.h"
#include "dex_file-inl.h"
-#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
+#include "gc_root-inl.h"
#include "jvalue.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
#include "primitive.h"
-#include "thread-current-inl.h"
#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
#include "well_known_classes.h"
namespace art {
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 40d7e5c..50e9144 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -24,9 +24,9 @@
#include "base/logging.h"
#include "class_linker-inl.h"
#include "common_throws.h"
+#include "dex_file-inl.h"
#include "dex_file.h"
#include "dex_file_annotations.h"
-#include "dex_file-inl.h"
#include "gc_root-inl.h"
#include "invoke_type.h"
#include "jit/profiling_info.h"
@@ -102,20 +102,21 @@
return GetDexMethodIndexUnchecked();
}
-inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) {
- return GetNativePointer<ArtMethod**>(DexCacheResolvedMethodsOffset(pointer_size),
- pointer_size);
+inline mirror::MethodDexCacheType* ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) {
+ return GetNativePointer<mirror::MethodDexCacheType*>(DexCacheResolvedMethodsOffset(pointer_size),
+ pointer_size);
}
inline ArtMethod* ArtMethod::GetDexCacheResolvedMethod(uint16_t method_index,
PointerSize pointer_size) {
// NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
// without accessing the DexCache and we don't want to do that in release build.
- DCHECK_LT(method_index,
- GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
- ArtMethod* method = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
- method_index,
- pointer_size);
+ DCHECK_LT(method_index, GetInterfaceMethodIfProxy(pointer_size)->GetDexFile()->NumMethodIds());
+ uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize;
+ DCHECK_LT(slot_idx, GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
+ mirror::MethodDexCachePair pair = mirror::DexCache::GetNativePairPtrSize(
+ GetDexCacheResolvedMethods(pointer_size), slot_idx, pointer_size);
+ ArtMethod* method = pair.GetObjectForIndex(method_index);
if (LIKELY(method != nullptr)) {
auto* declaring_class = method->GetDeclaringClass();
if (LIKELY(declaring_class == nullptr || !declaring_class->IsErroneous())) {
@@ -130,43 +131,45 @@
PointerSize pointer_size) {
// NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
// without accessing the DexCache and we don't want to do that in release build.
- DCHECK_LT(method_index,
- GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
+ DCHECK_LT(method_index, GetInterfaceMethodIfProxy(pointer_size)->GetDexFile()->NumMethodIds());
DCHECK(new_method == nullptr || new_method->GetDeclaringClass() != nullptr);
- mirror::DexCache::SetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
- method_index,
- new_method,
- pointer_size);
+ uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize;
+ DCHECK_LT(slot_idx, GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
+ mirror::MethodDexCachePair pair(new_method, method_index);
+ mirror::DexCache::SetNativePairPtrSize(
+ GetDexCacheResolvedMethods(pointer_size), slot_idx, pair, pointer_size);
}
inline bool ArtMethod::HasDexCacheResolvedMethods(PointerSize pointer_size) {
return GetDexCacheResolvedMethods(pointer_size) != nullptr;
}
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod** other_cache,
- PointerSize pointer_size) {
- return GetDexCacheResolvedMethods(pointer_size) == other_cache;
-}
-
inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, PointerSize pointer_size) {
return GetDexCacheResolvedMethods(pointer_size) ==
other->GetDexCacheResolvedMethods(pointer_size);
}
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
- // TODO: Refactor this function into two functions, Resolve...() and Lookup...()
- // so that we can properly annotate it with no-suspension possible / suspension possible.
+inline bool ArtMethod::HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType* other_cache,
+ PointerSize pointer_size) {
+ return GetDexCacheResolvedMethods(pointer_size) == other_cache;
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx) {
ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
if (UNLIKELY(type == nullptr)) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (resolve) {
- type = class_linker->ResolveType(type_idx, this);
- CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
- } else {
- type = class_linker->LookupResolvedType(
- *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader());
- }
+ type = Runtime::Current()->GetClassLinker()->LookupResolvedType(
+ *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader());
+ }
+ return type.Ptr();
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::ResolveClassFromTypeIndex(dex::TypeIndex type_idx) {
+ ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
+ ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
+ if (UNLIKELY(type == nullptr)) {
+ type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
+ CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
}
return type.Ptr();
}
@@ -282,6 +285,10 @@
return "<runtime internal callee-save reference and argument registers method>";
} else if (this == runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything)) {
return "<runtime internal save-every-register method>";
+ } else if (this == runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit)) {
+ return "<runtime internal save-every-register method for clinit>";
+ } else if (this == runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck)) {
+ return "<runtime internal save-every-register method for suspend check>";
} else {
return "<unknown runtime internal method>";
}
@@ -293,7 +300,7 @@
inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) {
DCHECK(!IsProxyMethod());
- return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr;
+ return LookupResolvedClassFromTypeIndex(type_idx) != nullptr;
}
inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) {
@@ -381,30 +388,41 @@
if (LIKELY(!IsProxyMethod())) {
return this;
}
- ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize(
- GetDexCacheResolvedMethods(pointer_size),
- GetDexMethodIndex(),
- pointer_size);
- DCHECK(interface_method != nullptr);
- DCHECK_EQ(interface_method,
- Runtime::Current()->GetClassLinker()->FindMethodForProxy(GetDeclaringClass(), this));
+ uint32_t method_index = GetDexMethodIndex();
+ uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize;
+ mirror::MethodDexCachePair pair = mirror::DexCache::GetNativePairPtrSize(
+ GetDexCacheResolvedMethods(pointer_size), slot_idx, pointer_size);
+ ArtMethod* interface_method = pair.GetObjectForIndex(method_index);
+ if (LIKELY(interface_method != nullptr)) {
+ DCHECK_EQ(interface_method, Runtime::Current()->GetClassLinker()->FindMethodForProxy(this));
+ } else {
+ interface_method = Runtime::Current()->GetClassLinker()->FindMethodForProxy(this);
+ DCHECK(interface_method != nullptr);
+ }
return interface_method;
}
-inline void ArtMethod::SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_methods,
+inline void ArtMethod::SetDexCacheResolvedMethods(mirror::MethodDexCacheType* new_dex_cache_methods,
PointerSize pointer_size) {
SetNativePointer(DexCacheResolvedMethodsOffset(pointer_size),
new_dex_cache_methods,
pointer_size);
}
-inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
+inline dex::TypeIndex ArtMethod::GetReturnTypeIndex() {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
- dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
- return GetClassFromTypeIndex(return_type_idx, resolve);
+ return proto_id.return_type_idx_;
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::LookupResolvedReturnType() {
+ return LookupResolvedClassFromTypeIndex(GetReturnTypeIndex());
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::ResolveReturnType() {
+ return ResolveClassFromTypeIndex(GetReturnTypeIndex());
}
inline bool ArtMethod::HasSingleImplementation() {
@@ -462,14 +480,8 @@
if (UNLIKELY(klass->IsProxyClass())) {
// For normal methods, dex cache shortcuts will be visited through the declaring class.
// However, for proxies we need to keep the interface method alive, so we visit its roots.
- ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize(
- GetDexCacheResolvedMethods(pointer_size),
- GetDexMethodIndex(),
- pointer_size);
+ ArtMethod* interface_method = GetInterfaceMethodIfProxy(pointer_size);
DCHECK(interface_method != nullptr);
- DCHECK_EQ(interface_method,
- Runtime::Current()->GetClassLinker()->FindMethodForProxy<kReadBarrierOption>(
- klass, this));
interface_method->VisitRoots(visitor, pointer_size);
}
}
@@ -483,8 +495,8 @@
if (old_class != new_class) {
SetDeclaringClass(new_class);
}
- ArtMethod** old_methods = GetDexCacheResolvedMethods(pointer_size);
- ArtMethod** new_methods = visitor(old_methods);
+ mirror::MethodDexCacheType* old_methods = GetDexCacheResolvedMethods(pointer_size);
+ mirror::MethodDexCacheType* new_methods = visitor(old_methods);
if (old_methods != new_methods) {
SetDexCacheResolvedMethods(new_methods, pointer_size);
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index ef9c457..7d8deda 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -38,8 +38,8 @@
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
#include "mirror/executable.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
#include "mirror/string.h"
#include "oat_file-inl.h"
#include "runtime_callbacks.h"
@@ -216,11 +216,8 @@
} else {
// Method didn't override superclass method so search interfaces
if (IsProxyMethod()) {
- result = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
- GetDexMethodIndex(),
- pointer_size);
- CHECK_EQ(result,
- Runtime::Current()->GetClassLinker()->FindMethodForProxy(GetDeclaringClass(), this));
+ result = GetInterfaceMethodIfProxy(pointer_size);
+ DCHECK(result != nullptr);
} else {
mirror::IfTable* iftable = GetDeclaringClass()->GetIfTable();
for (size_t i = 0; i < iftable->Count() && result == nullptr; i++) {
@@ -283,7 +280,7 @@
break;
}
// Does this catch exception type apply?
- mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */);
+ ObjPtr<mirror::Class> iter_exception_type = ResolveClassFromTypeIndex(iter_type_idx);
if (UNLIKELY(iter_exception_type == nullptr)) {
// Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
// removed by a pro-guard like tool.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 4b3e8ef..cac40e0 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -53,6 +53,10 @@
template <typename MirrorType> class ObjectArray;
class PointerArray;
class String;
+
+template <typename T> struct NativeDexCachePair;
+using MethodDexCachePair = NativeDexCachePair<ArtMethod>;
+using MethodDexCacheType = std::atomic<MethodDexCachePair>;
} // namespace mirror
class ArtMethod FINAL {
@@ -352,7 +356,7 @@
dex_method_index_ = new_idx;
}
- ALWAYS_INLINE ArtMethod** GetDexCacheResolvedMethods(PointerSize pointer_size)
+ ALWAYS_INLINE mirror::MethodDexCacheType* GetDexCacheResolvedMethods(PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_index,
PointerSize pointer_size)
@@ -362,17 +366,21 @@
ArtMethod* new_method,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ALWAYS_INLINE void SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_methods,
+ ALWAYS_INLINE void SetDexCacheResolvedMethods(mirror::MethodDexCacheType* new_dex_cache_methods,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
bool HasDexCacheResolvedMethods(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_);
bool HasSameDexCacheResolvedMethods(ArtMethod* other, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size)
+ bool HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType* other_cache,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Get the Class* from the type index into this method's dex cache.
- mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve)
+ // Lookup the Class* from the type index into this method's dex cache.
+ ObjPtr<mirror::Class> LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Resolve the Class* from the type index into this method's dex cache.
+ ObjPtr<mirror::Class> ResolveClassFromTypeIndex(dex::TypeIndex type_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if this method has the same name and signature of the other method.
@@ -587,9 +595,11 @@
const char* GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
- // number of bugs at call sites.
- mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Lookup return type.
+ ObjPtr<mirror::Class> LookupResolvedReturnType() REQUIRES_SHARED(Locks::mutator_lock_);
+ // Resolve return type. May cause thread suspension due to GetClassFromTypeIdx
+ // calling ResolveType this caused a large number of bugs at call sites.
+ ObjPtr<mirror::Class> ResolveReturnType() REQUIRES_SHARED(Locks::mutator_lock_);
mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -714,7 +724,7 @@
// Must be the last fields in the method.
struct PtrSizedFields {
// Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
- ArtMethod** dex_cache_resolved_methods_;
+ mirror::MethodDexCacheType* dex_cache_resolved_methods_;
// Pointer to JNI function registered to this method, or a function to resolve the JNI function,
// or the profiling data for non-native methods, or an ImtConflictTable, or the
@@ -743,6 +753,8 @@
// Compare given pointer size to the image pointer size.
static bool IsImagePointerSize(PointerSize pointer_size);
+ dex::TypeIndex GetReturnTypeIndex() REQUIRES_SHARED(Locks::mutator_lock_);
+
template<typename T>
ALWAYS_INLINE T GetNativePointer(MemberOffset offset, PointerSize pointer_size) const {
static_assert(std::is_pointer<T>::value, "T must be a pointer type");
diff --git a/runtime/asm_support_check.h b/runtime/asm_support_check.h
index cc6a578..3163506 100644
--- a/runtime/asm_support_check.h
+++ b/runtime/asm_support_check.h
@@ -28,10 +28,10 @@
#include "mirror/class.h"
#include "mirror/dex_cache.h"
#include "mirror/string.h"
-#include "utils/dex_cache_arrays_layout.h"
#include "runtime.h"
#include "stack.h"
#include "thread.h"
+#include "utils/dex_cache_arrays_layout.h"
#ifndef ADD_TEST_EQ
#define ADD_TEST_EQ(x, y) CHECK_EQ(x, y);
diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc
index 25b6925..ecdabba 100644
--- a/runtime/barrier_test.cc
+++ b/runtime/barrier_test.cc
@@ -21,8 +21,8 @@
#include "atomic.h"
#include "common_runtime_test.h"
#include "mirror/object_array-inl.h"
-#include "thread_pool.h"
#include "thread-current-inl.h"
+#include "thread_pool.h"
namespace art {
class CheckWaitTask : public Task {
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 54b40f2..148ef86 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -26,8 +26,8 @@
#include "logging.h"
#include "mem_map.h"
#include "mutex.h"
-#include "thread-current-inl.h"
#include "systrace.h"
+#include "thread-current-inl.h"
namespace art {
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index a484c5c..0b1a3ba 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_
#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_
-#include <stdint.h>
#include <stddef.h>
+#include <stdint.h>
#include "base/bit_utils.h"
#include "base/dchecked_vector.h"
diff --git a/runtime/base/array_slice.h b/runtime/base/array_slice.h
index 0da977d..a7bce7d 100644
--- a/runtime/base/array_slice.h
+++ b/runtime/base/array_slice.h
@@ -17,10 +17,10 @@
#ifndef ART_RUNTIME_BASE_ARRAY_SLICE_H_
#define ART_RUNTIME_BASE_ARRAY_SLICE_H_
-#include "stride_iterator.h"
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/iteration_range.h"
+#include "stride_iterator.h"
namespace art {
diff --git a/runtime/base/callee_save_type.h b/runtime/base/callee_save_type.h
index 501b296..e9cd63c 100644
--- a/runtime/base/callee_save_type.h
+++ b/runtime/base/callee_save_type.h
@@ -28,10 +28,20 @@
kSaveRefsOnly, // Only those callee-save registers that can hold references.
kSaveRefsAndArgs, // References (see above) and arguments (usually caller-save registers).
kSaveEverything, // All registers, including both callee-save and caller-save.
+ kSaveEverythingForClinit, // Special kSaveEverything for clinit.
+ kSaveEverythingForSuspendCheck, // Special kSaveEverything for suspend check.
kLastCalleeSaveType // Value used for iteration.
};
std::ostream& operator<<(std::ostream& os, const CalleeSaveType& rhs);
+static inline constexpr CalleeSaveType GetCanonicalCalleeSaveType(CalleeSaveType type) {
+ if (type == CalleeSaveType::kSaveEverythingForClinit ||
+ type == CalleeSaveType::kSaveEverythingForSuspendCheck) {
+ return CalleeSaveType::kSaveEverything;
+ }
+ return type;
+}
+
} // namespace art
#endif // ART_RUNTIME_BASE_CALLEE_SAVE_TYPE_H_
diff --git a/runtime/base/casts.h b/runtime/base/casts.h
index c5b0af6..0cbabba 100644
--- a/runtime/base/casts.h
+++ b/runtime/base/casts.h
@@ -18,9 +18,10 @@
#define ART_RUNTIME_BASE_CASTS_H_
#include <assert.h>
-#include <limits>
#include <stdint.h>
#include <string.h>
+
+#include <limits>
#include <type_traits>
#include "base/logging.h"
diff --git a/runtime/base/hash_set.h b/runtime/base/hash_set.h
index a22efcf..c472a9e 100644
--- a/runtime/base/hash_set.h
+++ b/runtime/base/hash_set.h
@@ -17,10 +17,11 @@
#ifndef ART_RUNTIME_BASE_HASH_SET_H_
#define ART_RUNTIME_BASE_HASH_SET_H_
+#include <stdint.h>
+
#include <functional>
#include <iterator>
#include <memory>
-#include <stdint.h>
#include <utility>
#include "bit_utils.h"
diff --git a/runtime/base/hash_set_test.cc b/runtime/base/hash_set_test.cc
index 8254063..31b28eb 100644
--- a/runtime/base/hash_set_test.cc
+++ b/runtime/base/hash_set_test.cc
@@ -16,8 +16,8 @@
#include "hash_set.h"
-#include <map>
#include <forward_list>
+#include <map>
#include <sstream>
#include <string>
#include <unordered_set>
diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h
index 0e3bc8e..e0c921e 100644
--- a/runtime/base/histogram.h
+++ b/runtime/base/histogram.h
@@ -16,8 +16,8 @@
#ifndef ART_RUNTIME_BASE_HISTOGRAM_H_
#define ART_RUNTIME_BASE_HISTOGRAM_H_
-#include <vector>
#include <string>
+#include <vector>
#include "base/logging.h"
diff --git a/runtime/base/length_prefixed_array.h b/runtime/base/length_prefixed_array.h
index a570b81..2df5a99 100644
--- a/runtime/base/length_prefixed_array.h
+++ b/runtime/base/length_prefixed_array.h
@@ -20,10 +20,10 @@
#include <stddef.h> // for offsetof()
#include <string.h> // for memset()
-#include "stride_iterator.h"
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/iteration_range.h"
+#include "stride_iterator.h"
namespace art {
diff --git a/runtime/base/memory_tool.h b/runtime/base/memory_tool.h
index 42cbaa0..223c1de 100644
--- a/runtime/base/memory_tool.h
+++ b/runtime/base/memory_tool.h
@@ -52,8 +52,8 @@
#else
-#include <valgrind.h>
#include <memcheck/memcheck.h>
+#include <valgrind.h>
#define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s)
#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s)
#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s)
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 03dda12..6392198 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -23,8 +23,8 @@
#include "atomic.h"
#include "base/logging.h"
-#include "base/time_utils.h"
#include "base/systrace.h"
+#include "base/time_utils.h"
#include "base/value_object.h"
#include "mutex-inl.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/runtime/base/safe_copy.cc b/runtime/base/safe_copy.cc
index 06249ac..c76ea11 100644
--- a/runtime/base/safe_copy.cc
+++ b/runtime/base/safe_copy.cc
@@ -16,9 +16,9 @@
#include "safe_copy.h"
-#include <unistd.h>
#include <sys/uio.h>
#include <sys/user.h>
+#include <unistd.h>
#include <algorithm>
diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h
index 7964705..4a6c907 100644
--- a/runtime/base/scoped_arena_containers.h
+++ b/runtime/base/scoped_arena_containers.h
@@ -26,8 +26,8 @@
#include "arena_containers.h" // For ArenaAllocatorAdapterKind.
#include "base/dchecked_vector.h"
-#include "scoped_arena_allocator.h"
#include "safe_map.h"
+#include "scoped_arena_allocator.h"
namespace art {
diff --git a/runtime/base/systrace.h b/runtime/base/systrace.h
index 3901f96..06db48a 100644
--- a/runtime/base/systrace.h
+++ b/runtime/base/systrace.h
@@ -19,9 +19,10 @@
#define ATRACE_TAG ATRACE_TAG_DALVIK
#include <cutils/trace.h>
-#include <string>
#include <utils/Trace.h>
+#include <string>
+
namespace art {
class ScopedTrace {
diff --git a/runtime/base/time_utils.h b/runtime/base/time_utils.h
index dbb8bcd..919937f 100644
--- a/runtime/base/time_utils.h
+++ b/runtime/base/time_utils.h
@@ -18,9 +18,10 @@
#define ART_RUNTIME_BASE_TIME_UTILS_H_
#include <stdint.h>
-#include <string>
#include <time.h>
+#include <string>
+
#include "base/macros.h"
namespace art {
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index aaa2431..b2e5251 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-
#include <stdio.h>
#include "timing_logger.h"
+#include "base/histogram-inl.h"
#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/histogram-inl.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "gc/heap.h"
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 00b5567..eb8ced0 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -17,11 +17,12 @@
#include "base/unix_file/fd_file.h"
#include <errno.h>
-#include <limits>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <limits>
+
#include "base/logging.h"
// Includes needed for FdFile::Copy().
@@ -437,4 +438,30 @@
return true;
}
+int FdFile::Compare(FdFile* other) {
+ int64_t length = GetLength();
+ int64_t length2 = other->GetLength();
+ if (length != length2) {
+ return length < length2 ? -1 : 1;
+ }
+ static const size_t kBufferSize = 4096;
+ std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]);
+ std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]);
+ while (length > 0) {
+ size_t len = std::min(kBufferSize, static_cast<size_t>(length));
+ if (!ReadFully(&buffer1[0], len)) {
+ return -1;
+ }
+ if (!other->ReadFully(&buffer2[0], len)) {
+ return 1;
+ }
+ int result = memcmp(&buffer1[0], &buffer2[0], len);
+ if (result != 0) {
+ return result;
+ }
+ length -= len;
+ }
+ return 0;
+}
+
} // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index eb85c4f..91b08bc 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -21,8 +21,8 @@
#include <string>
-#include "base/unix_file/random_access_file.h"
#include "base/macros.h"
+#include "base/unix_file/random_access_file.h"
namespace unix_file {
@@ -145,6 +145,11 @@
// WARNING: Only use this when you know what you're doing!
void MarkUnchecked();
+ // Compare against another file. Returns 0 if the files are equivalent, otherwise returns -1 or 1
+ // depending on if the lenghts are different. If the lengths are the same, the function returns
+ // the difference of the first byte that differs.
+ int Compare(FdFile* other);
+
protected:
// If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the
// given warning if the current state is or exceeds warn_threshold.
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index 6aef348..8b1a115 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -220,4 +220,58 @@
EXPECT_FALSE(art::OS::FileExists(filename.c_str())) << filename;
}
+TEST_F(FdFileTest, Compare) {
+ std::vector<uint8_t> buffer;
+ constexpr int64_t length = 17 * art::KB;
+ for (size_t i = 0; i < length; ++i) {
+ buffer.push_back(static_cast<uint8_t>(i));
+ }
+
+ auto reset_compare = [&](art::ScratchFile& a, art::ScratchFile& b) {
+ a.GetFile()->ResetOffset();
+ b.GetFile()->ResetOffset();
+ return a.GetFile()->Compare(b.GetFile());
+ };
+
+ art::ScratchFile tmp;
+ EXPECT_TRUE(tmp.GetFile()->WriteFully(&buffer[0], length));
+ EXPECT_EQ(tmp.GetFile()->GetLength(), length);
+
+ art::ScratchFile tmp2;
+ EXPECT_TRUE(tmp2.GetFile()->WriteFully(&buffer[0], length));
+ EXPECT_EQ(tmp2.GetFile()->GetLength(), length);
+
+ // Basic equality check.
+ tmp.GetFile()->ResetOffset();
+ tmp2.GetFile()->ResetOffset();
+ // Files should be the same.
+ EXPECT_EQ(reset_compare(tmp, tmp2), 0);
+
+ // Change a byte near the start.
+ ++buffer[2];
+ art::ScratchFile tmp3;
+ EXPECT_TRUE(tmp3.GetFile()->WriteFully(&buffer[0], length));
+ --buffer[2];
+ EXPECT_NE(reset_compare(tmp, tmp3), 0);
+
+ // Change a byte near the middle.
+ ++buffer[length / 2];
+ art::ScratchFile tmp4;
+ EXPECT_TRUE(tmp4.GetFile()->WriteFully(&buffer[0], length));
+ --buffer[length / 2];
+ EXPECT_NE(reset_compare(tmp, tmp4), 0);
+
+ // Change a byte near the end.
+ ++buffer[length - 5];
+ art::ScratchFile tmp5;
+ EXPECT_TRUE(tmp5.GetFile()->WriteFully(&buffer[0], length));
+ --buffer[length - 5];
+ EXPECT_NE(reset_compare(tmp, tmp5), 0);
+
+ // Reference check
+ art::ScratchFile tmp6;
+ EXPECT_TRUE(tmp6.GetFile()->WriteFully(&buffer[0], length));
+ EXPECT_EQ(reset_compare(tmp, tmp6), 0);
+}
+
} // namespace unix_file
diff --git a/runtime/base/unix_file/random_access_file_utils.cc b/runtime/base/unix_file/random_access_file_utils.cc
index df3b308..aae65c1 100644
--- a/runtime/base/unix_file/random_access_file_utils.cc
+++ b/runtime/base/unix_file/random_access_file_utils.cc
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-#include <vector>
#include "base/unix_file/random_access_file_utils.h"
+
+#include <vector>
+
#include "base/unix_file/random_access_file.h"
namespace unix_file {
diff --git a/runtime/bytecode_utils.h b/runtime/bytecode_utils.h
index fa87b1d..b6a3c03 100644
--- a/runtime/bytecode_utils.h
+++ b/runtime/bytecode_utils.h
@@ -18,8 +18,8 @@
#define ART_RUNTIME_BYTECODE_UTILS_H_
#include "base/arena_object.h"
-#include "dex_file.h"
#include "dex_file-inl.h"
+#include "dex_file.h"
#include "dex_instruction-inl.h"
namespace art {
diff --git a/runtime/cha.cc b/runtime/cha.cc
index e6bdb84..6c011e8 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -19,6 +19,7 @@
#include "art_method-inl.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
+#include "linear_alloc.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
@@ -174,23 +175,43 @@
DISALLOW_COPY_AND_ASSIGN(CHACheckpoint);
};
-void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify_class,
- uint16_t verify_index,
- ArtMethod* excluded_method) {
+
+static void VerifyNonSingleImplementation(mirror::Class* verify_class,
+ uint16_t verify_index,
+ ArtMethod* excluded_method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!kIsDebugBuild) {
+ return;
+ }
+
// Grab cha_lock_ to make sure all single-implementation updates are seen.
+ MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_);
+
PointerSize image_pointer_size =
Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_);
+
+ mirror::Class* input_verify_class = verify_class;
+
while (verify_class != nullptr) {
if (verify_index >= verify_class->GetVTableLength()) {
return;
}
ArtMethod* verify_method = verify_class->GetVTableEntry(verify_index, image_pointer_size);
if (verify_method != excluded_method) {
+ auto construct_parent_chain = [](mirror::Class* failed, mirror::Class* in)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string tmp = in->PrettyClass();
+ while (in != failed) {
+ in = in->GetSuperClass();
+ tmp = tmp + "->" + in->PrettyClass();
+ }
+ return tmp;
+ };
DCHECK(!verify_method->HasSingleImplementation())
<< "class: " << verify_class->PrettyClass()
<< " verify_method: " << verify_method->PrettyMethod(true)
- << " excluded_method: " << excluded_method->PrettyMethod(true);
+ << " (" << construct_parent_chain(verify_class, input_verify_class) << ")"
+ << " excluded_method: " << ArtMethod::PrettyMethod(excluded_method);
if (verify_method->IsAbstract()) {
DCHECK(verify_method->GetSingleImplementation(image_pointer_size) == nullptr);
}
@@ -239,24 +260,19 @@
// method_in_super already has multiple implementations. All methods in the
// same vtable slots in its super classes should have
// non-single-implementation already.
- if (kIsDebugBuild) {
- VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(),
- method_in_super->GetMethodIndex(),
- nullptr /* excluded_method */);
- }
+ VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(),
+ method_in_super->GetMethodIndex(),
+ nullptr /* excluded_method */);
return;
}
uint16_t method_index = method_in_super->GetMethodIndex();
if (method_in_super->IsAbstract()) {
- if (kIsDebugBuild) {
- // An abstract method should have made all methods in the same vtable
- // slot above it in the class hierarchy having non-single-implementation.
- mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass();
- VerifyNonSingleImplementation(super_super,
- method_index,
- method_in_super);
- }
+ // An abstract method should have made all methods in the same vtable
+ // slot above it in the class hierarchy having non-single-implementation.
+ VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(),
+ method_index,
+ method_in_super);
if (virtual_method->IsAbstract()) {
// SUPER: abstract, VIRTUAL: abstract.
@@ -338,11 +354,9 @@
// other methods (abstract or not) in the vtable slot to be non-single-implementation.
}
- if (kIsDebugBuild) {
- VerifyNonSingleImplementation(super_super->GetSuperClass(),
- method_index,
- method_in_super_super);
- }
+ VerifyNonSingleImplementation(super_super->GetSuperClass(),
+ method_index,
+ method_in_super_super);
// No need to go any further.
return;
} else {
@@ -568,4 +582,17 @@
}
}
+void ClassHierarchyAnalysis::RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc) {
+ MutexLock mu(Thread::Current(), *Locks::cha_lock_);
+ for (auto it = cha_dependency_map_.begin(); it != cha_dependency_map_.end(); ) {
+ // Use unsafe to avoid locking since the allocator is going to be deleted.
+ if (linear_alloc->ContainsUnsafe(it->first)) {
+ // About to delete the ArtMethod, erase the entry from the map.
+ it = cha_dependency_map_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/cha.h b/runtime/cha.h
index 81458db..40999dd 100644
--- a/runtime/cha.h
+++ b/runtime/cha.h
@@ -17,17 +17,19 @@
#ifndef ART_RUNTIME_CHA_H_
#define ART_RUNTIME_CHA_H_
+#include <unordered_map>
+#include <unordered_set>
+
#include "base/enums.h"
#include "base/mutex.h"
#include "handle.h"
#include "mirror/class.h"
#include "oat_quick_method_header.h"
-#include <unordered_map>
-#include <unordered_set>
namespace art {
class ArtMethod;
+class LinearAlloc;
/**
* Class Hierarchy Analysis (CHA) tries to devirtualize virtual calls into
@@ -111,6 +113,11 @@
// Update CHA info for methods that `klass` overrides, after loading `klass`.
void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Remove all of the dependencies for a linear allocator. This is called when dex cache unloading
+ // occurs.
+ void RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc)
+ REQUIRES(!Locks::cha_lock_);
+
private:
void InitSingleImplementationFlag(Handle<mirror::Class> klass,
ArtMethod* method,
@@ -148,14 +155,6 @@
std::unordered_set<ArtMethod*>& invalidated_single_impl_methods)
REQUIRES_SHARED(Locks::mutator_lock_);
- // For all methods in vtable slot at `verify_index` of `verify_class` and its
- // superclasses, single-implementation status should be false, except if the
- // method is `excluded_method`.
- void VerifyNonSingleImplementation(mirror::Class* verify_class,
- uint16_t verify_index,
- ArtMethod* excluded_method)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// A map that maps a method to a set of compiled code that assumes that method has a
// single implementation, which is used to do CHA-based devirtualization.
std::unordered_map<ArtMethod*, ListOfDependentPairs> cha_dependency_map_
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 1c3328e..c3dd702 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -16,18 +16,19 @@
#include "check_jni.h"
-#include <iomanip>
#include <sys/mman.h>
#include <zlib.h>
+#include <iomanip>
+
#include "android-base/stringprintf.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/logging.h"
#include "base/to_str.h"
-#include "class_linker.h"
#include "class_linker-inl.h"
+#include "class_linker.h"
#include "dex_file-inl.h"
#include "gc/space/space.h"
#include "java_vm_ext.h"
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index d29db15..439ecaf 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -19,14 +19,14 @@
#include "art_field.h"
#include "class_linker.h"
-#include "gc_root-inl.h"
#include "gc/heap-inl.h"
-#include "obj_ptr-inl.h"
+#include "gc_root-inl.h"
+#include "handle_scope-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/iftable.h"
#include "mirror/object_array-inl.h"
-#include "handle_scope-inl.h"
+#include "obj_ptr-inl.h"
#include "scoped_thread_state_change-inl.h"
#include <atomic>
@@ -156,6 +156,29 @@
});
}
+inline ArtMethod* ClassLinker::LookupResolvedMethod(uint32_t method_idx,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader) {
+ PointerSize pointer_size = image_pointer_size_;
+ ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, pointer_size);
+ if (resolved == nullptr) {
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
+ ObjPtr<mirror::Class> klass = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader);
+ if (klass != nullptr) {
+ if (klass->IsInterface()) {
+ resolved = klass->FindInterfaceMethod(dex_cache, method_idx, pointer_size);
+ } else {
+ resolved = klass->FindClassMethod(dex_cache, method_idx, pointer_size);
+ }
+ if (resolved != nullptr) {
+ dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size);
+ }
+ }
+ }
+ return resolved;
+}
+
template <InvokeType type, ClassLinker::ResolveMode kResolveMode>
inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) {
DCHECK(referrer != nullptr);
@@ -164,9 +187,10 @@
// However, we delay the GetInterfaceMethodIfProxy() until needed.
DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
- if (resolved_method == nullptr || resolved_method->IsRuntimeMethod()) {
+ if (resolved_method == nullptr) {
return nullptr;
}
+ DCHECK(!resolved_method->IsRuntimeMethod());
if (kResolveMode == ResolveMode::kCheckICCEAndIAE) {
referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
// Check if the invoke type matches the class type.
@@ -203,7 +227,8 @@
DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
Thread::PoisonObjectPointersIfDebug();
ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
- if (UNLIKELY(resolved_method == nullptr || resolved_method->IsRuntimeMethod())) {
+ DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod());
+ if (UNLIKELY(resolved_method == nullptr)) {
referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
StackHandleScope<2> hs(self);
@@ -287,33 +312,15 @@
return klass.Ptr();
}
-template<ReadBarrierOption kReadBarrierOption>
-ArtMethod* ClassLinker::FindMethodForProxy(ObjPtr<mirror::Class> proxy_class,
- ArtMethod* proxy_method) {
- DCHECK(proxy_class->IsProxyClass());
- DCHECK(proxy_method->IsProxyMethod());
- {
- Thread* const self = Thread::Current();
- ReaderMutexLock mu(self, *Locks::dex_lock_);
- // Locate the dex cache of the original interface/Object
- for (const DexCacheData& data : dex_caches_) {
- if (!self->IsJWeakCleared(data.weak_root) &&
- proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods,
- image_pointer_size_)) {
- ObjPtr<mirror::DexCache> dex_cache =
- ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
- if (dex_cache != nullptr) {
- ArtMethod* resolved_method = dex_cache->GetResolvedMethod(
- proxy_method->GetDexMethodIndex(), image_pointer_size_);
- CHECK(resolved_method != nullptr);
- return resolved_method;
- }
- }
+template <class Visitor>
+inline void ClassLinker::VisitClassTables(const Visitor& visitor) {
+ Thread* const self = Thread::Current();
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ for (const ClassLoaderData& data : class_loaders_) {
+ if (data.class_table != nullptr) {
+ visitor(data.class_table);
}
}
- LOG(FATAL) << "Didn't find dex cache for " << proxy_class->PrettyClass() << " "
- << proxy_method->PrettyMethod();
- UNREACHABLE();
}
} // namespace art
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index a227d18..9756f57 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -16,6 +16,8 @@
#include "class_linker.h"
+#include <unistd.h>
+
#include <algorithm>
#include <deque>
#include <iostream>
@@ -24,7 +26,6 @@
#include <queue>
#include <string>
#include <tuple>
-#include <unistd.h>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -369,7 +370,10 @@
quick_imt_conflict_trampoline_(nullptr),
quick_generic_jni_trampoline_(nullptr),
quick_to_interpreter_bridge_trampoline_(nullptr),
- image_pointer_size_(kRuntimePointerSize) {
+ image_pointer_size_(kRuntimePointerSize),
+ cha_(Runtime::Current()->IsAotCompiler() ? nullptr : new ClassHierarchyAnalysis()) {
+ // For CHA disabled during Aot, see b/34193647.
+
CHECK(intern_table_ != nullptr);
static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_),
"Array cache size wrong.");
@@ -1114,7 +1118,8 @@
virtual void Visit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
const bool is_copied = method->IsCopied();
- ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(kRuntimePointerSize);
+ mirror::MethodDexCacheType* resolved_methods =
+ method->GetDexCacheResolvedMethods(kRuntimePointerSize);
if (resolved_methods != nullptr) {
bool in_image_space = false;
if (kIsDebugBuild || is_copied) {
@@ -1136,49 +1141,6 @@
const ImageHeader& header_;
};
-class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor {
- public:
- explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {}
-
- virtual void Visit(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) {
- ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
- if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
- CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass);
- }
- }
-
- private:
- ClassTable* const table_;
-};
-
-class VerifyDirectInterfacesInTableClassVisitor {
- public:
- explicit VerifyDirectInterfacesInTableClassVisitor(ObjPtr<mirror::ClassLoader> class_loader)
- : class_loader_(class_loader), self_(Thread::Current()) { }
-
- bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader_) {
- classes_.push_back(klass);
- }
- return true;
- }
-
- void Check() const REQUIRES_SHARED(Locks::mutator_lock_) {
- for (ObjPtr<mirror::Class> klass : classes_) {
- for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) {
- CHECK(klass->GetDirectInterface(self_, klass, i) != nullptr)
- << klass->PrettyDescriptor() << " iface #" << i;
- }
- }
- }
-
- private:
- ObjPtr<mirror::ClassLoader> class_loader_;
- Thread* self_;
- std::vector<ObjPtr<mirror::Class>> classes_;
-};
-
class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
public:
VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
@@ -1284,6 +1246,25 @@
}
}
+template <typename T>
+static void CopyNativeDexCachePairs(std::atomic<mirror::NativeDexCachePair<T>>* src,
+ size_t count,
+ std::atomic<mirror::NativeDexCachePair<T>>* dst,
+ PointerSize pointer_size) {
+ DCHECK_NE(count, 0u);
+ DCHECK(mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).object != nullptr ||
+ mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).index != 0u);
+ for (size_t i = 0; i < count; ++i) {
+ DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).index, 0u);
+ DCHECK(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).object == nullptr);
+ mirror::NativeDexCachePair<T> source =
+ mirror::DexCache::GetNativePairPtrSize(src, i, pointer_size);
+ if (source.index != 0u || source.object != nullptr) {
+ mirror::DexCache::SetNativePairPtrSize(dst, i, source, pointer_size);
+ }
+ }
+}
+
// new_class_set is the set of classes that were read from the class table section in the image.
// If there was no class table section, it is null.
// Note: using a class here to avoid having to make ClassLinker internals public.
@@ -1363,7 +1344,10 @@
if (dex_file->NumTypeIds() < num_types) {
num_types = dex_file->NumTypeIds();
}
- const size_t num_methods = dex_file->NumMethodIds();
+ size_t num_methods = mirror::DexCache::kDexCacheMethodCacheSize;
+ if (dex_file->NumMethodIds() < num_methods) {
+ num_methods = dex_file->NumMethodIds();
+ }
size_t num_fields = mirror::DexCache::kDexCacheFieldCacheSize;
if (dex_file->NumFieldIds() < num_fields) {
num_fields = dex_file->NumFieldIds();
@@ -1396,37 +1380,18 @@
dex_cache->SetResolvedTypes(types);
}
if (num_methods != 0u) {
- ArtMethod** const methods = reinterpret_cast<ArtMethod**>(
- raw_arrays + layout.MethodsOffset());
- ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods();
- for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) {
- DCHECK(methods[j] == nullptr);
- }
- CopyNonNull(image_resolved_methods,
- num_methods,
- methods,
- [] (const ArtMethod* method) {
- return method == nullptr;
- });
+ mirror::MethodDexCacheType* const image_resolved_methods =
+ dex_cache->GetResolvedMethods();
+ mirror::MethodDexCacheType* const methods =
+ reinterpret_cast<mirror::MethodDexCacheType*>(raw_arrays + layout.MethodsOffset());
+ CopyNativeDexCachePairs(image_resolved_methods, num_methods, methods, image_pointer_size);
dex_cache->SetResolvedMethods(methods);
}
if (num_fields != 0u) {
mirror::FieldDexCacheType* const image_resolved_fields = dex_cache->GetResolvedFields();
mirror::FieldDexCacheType* const fields =
reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset());
- for (size_t j = 0; j < num_fields; ++j) {
- DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(fields, j, image_pointer_size).index,
- 0u);
- DCHECK(mirror::DexCache::GetNativePairPtrSize(fields, j, image_pointer_size).object ==
- nullptr);
- mirror::DexCache::SetNativePairPtrSize(
- fields,
- j,
- mirror::DexCache::GetNativePairPtrSize(image_resolved_fields,
- j,
- image_pointer_size),
- image_pointer_size);
- }
+ CopyNativeDexCachePairs(image_resolved_fields, num_fields, fields, image_pointer_size);
dex_cache->SetResolvedFields(fields);
}
if (num_method_types != 0u) {
@@ -1663,13 +1628,13 @@
heap->VisitObjects(visitor);
}
- static void CheckPointerArray(gc::Heap* heap,
- ClassLinker* class_linker,
- ArtMethod** arr,
- size_t size)
+ static void CheckArtMethodDexCacheArray(gc::Heap* heap,
+ ClassLinker* class_linker,
+ mirror::MethodDexCacheType* arr,
+ size_t size)
REQUIRES_SHARED(Locks::mutator_lock_) {
ImageSanityChecks isc(heap, class_linker);
- isc.SanityCheckArtMethodPointerArray(arr, size);
+ isc.SanityCheckArtMethodDexCacheArray(arr, size);
}
private:
@@ -1724,7 +1689,7 @@
}
}
- void SanityCheckArtMethodPointerArray(ArtMethod** arr, size_t size)
+ void SanityCheckArtMethodDexCacheArray(mirror::MethodDexCacheType* arr, size_t size)
REQUIRES_SHARED(Locks::mutator_lock_) {
CHECK_EQ(arr != nullptr, size != 0u);
if (arr != nullptr) {
@@ -1740,7 +1705,8 @@
CHECK(contains);
}
for (size_t j = 0; j < size; ++j) {
- ArtMethod* method = mirror::DexCache::GetElementPtrSize(arr, j, pointer_size_);
+ auto pair = mirror::DexCache::GetNativePairPtrSize(arr, j, pointer_size_);
+ ArtMethod* method = pair.object;
// expected_class == null means we are a dex cache.
if (method != nullptr) {
SanityCheckArtMethod(method, nullptr);
@@ -1757,6 +1723,63 @@
std::vector<const ImageSection*> runtime_method_sections_;
};
+static void VerifyAppImage(const ImageHeader& header,
+ const Handle<mirror::ClassLoader>& class_loader,
+ const Handle<mirror::ObjectArray<mirror::DexCache> >& dex_caches,
+ ClassTable* class_table, gc::space::ImageSpace* space)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ {
+ class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor {
+ public:
+ explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {}
+
+ virtual void Visit(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) {
+ ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
+ if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass);
+ }
+ }
+
+ private:
+ ClassTable* const table_;
+ };
+ VerifyClassInTableArtMethodVisitor visitor(class_table);
+ header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize);
+ }
+ {
+ // Verify that all direct interfaces of classes in the class table are also resolved.
+ std::vector<ObjPtr<mirror::Class>> classes;
+ auto verify_direct_interfaces_in_table = [&](ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader.Get()) {
+ classes.push_back(klass);
+ }
+ return true;
+ };
+ class_table->Visit(verify_direct_interfaces_in_table);
+ Thread* self = Thread::Current();
+ for (ObjPtr<mirror::Class> klass : classes) {
+ for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) {
+ CHECK(klass->GetDirectInterface(self, klass, i) != nullptr)
+ << klass->PrettyDescriptor() << " iface #" << i;
+ }
+ }
+ }
+ // Check that all non-primitive classes in dex caches are also in the class table.
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
+ mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
+ for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
+ ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
+ if (klass != nullptr && !klass->IsPrimitive()) {
+ CHECK(class_table->Contains(klass))
+ << klass->PrettyDescriptor() << " " << dex_cache->GetDexFile()->GetLocation();
+ }
+ }
+ }
+}
+
bool ClassLinker::AddImageSpace(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
@@ -1851,10 +1874,10 @@
}
} else {
if (kSanityCheckObjects) {
- ImageSanityChecks::CheckPointerArray(heap,
- this,
- dex_cache->GetResolvedMethods(),
- dex_cache->NumResolvedMethods());
+ ImageSanityChecks::CheckArtMethodDexCacheArray(heap,
+ this,
+ dex_cache->GetResolvedMethods(),
+ dex_cache->NumResolvedMethods());
}
// Register dex files, keep track of existing ones that are conflicts.
AppendToBootClassPath(*dex_file.get(), dex_cache);
@@ -2010,28 +2033,13 @@
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
class_table->AddClassSet(std::move(temp_set));
}
+
if (kIsDebugBuild && app_image) {
// This verification needs to happen after the classes have been added to the class loader.
// Since it ensures classes are in the class table.
- VerifyClassInTableArtMethodVisitor visitor2(class_table);
- header.VisitPackedArtMethods(&visitor2, space->Begin(), kRuntimePointerSize);
- // Verify that all direct interfaces of classes in the class table are also resolved.
- VerifyDirectInterfacesInTableClassVisitor visitor(class_loader.Get());
- class_table->Visit(visitor);
- visitor.Check();
- // Check that all non-primitive classes in dex caches are also in the class table.
- for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
- ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
- mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
- for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
- ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
- if (klass != nullptr && !klass->IsPrimitive()) {
- CHECK(class_table->Contains(klass)) << klass->PrettyDescriptor()
- << " " << dex_cache->GetDexFile()->GetLocation();
- }
- }
- }
+ VerifyAppImage(header, class_loader, dex_caches, class_table, space);
}
+
VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
return true;
}
@@ -2312,8 +2320,12 @@
if (runtime->GetJit() != nullptr) {
jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
if (code_cache != nullptr) {
+ // For the JIT case, RemoveMethodsIn removes the CHA dependencies.
code_cache->RemoveMethodsIn(self, *data.allocator);
}
+ } else if (cha_ != nullptr) {
+ // If we don't have a JIT, we need to manually remove the CHA dependencies manually.
+ cha_->RemoveDependenciesForLinearAlloc(data.allocator);
}
delete data.allocator;
delete data.class_table;
@@ -3483,7 +3495,8 @@
ObjPtr<mirror::DexCache> dex_cache) {
CHECK(dex_cache != nullptr) << dex_file.GetLocation();
boot_class_path_.push_back(&dex_file);
- RegisterBootClassPathDexFile(dex_file, dex_cache);
+ WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_);
+ RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr);
}
void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file,
@@ -3542,6 +3555,14 @@
data.resolved_methods = dex_cache->GetResolvedMethods();
data.class_table = ClassTableForClassLoader(class_loader);
DCHECK(data.class_table != nullptr);
+ // Make sure to hold the dex cache live in the class table. This case happens for the boot class
+ // path dex caches without an image.
+ data.class_table->InsertStrongRoot(dex_cache);
+ if (class_loader != nullptr) {
+ // Since we added a strong root to the class table, do the write barrier as required for
+ // remembered sets and generational GCs.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
+ }
dex_caches_.push_back(data);
}
@@ -3666,12 +3687,6 @@
return h_dex_cache.Get();
}
-void ClassLinker::RegisterBootClassPathDexFile(const DexFile& dex_file,
- ObjPtr<mirror::DexCache> dex_cache) {
- WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_);
- RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr);
-}
-
bool ClassLinker::IsDexFileRegistered(Thread* self, const DexFile& dex_file) {
ReaderMutexLock mu(self, *Locks::dex_lock_);
return DecodeDexCache(self, FindDexCacheDataLocked(dex_file)) != nullptr;
@@ -3679,7 +3694,8 @@
ObjPtr<mirror::DexCache> ClassLinker::FindDexCache(Thread* self, const DexFile& dex_file) {
ReaderMutexLock mu(self, *Locks::dex_lock_);
- ObjPtr<mirror::DexCache> dex_cache = DecodeDexCache(self, FindDexCacheDataLocked(dex_file));
+ DexCacheData dex_cache_data = FindDexCacheDataLocked(dex_file);
+ ObjPtr<mirror::DexCache> dex_cache = DecodeDexCache(self, dex_cache_data);
if (dex_cache != nullptr) {
return dex_cache;
}
@@ -3689,7 +3705,8 @@
LOG(FATAL_WITHOUT_ABORT) << "Registered dex file " << data.dex_file->GetLocation();
}
}
- LOG(FATAL) << "Failed to find DexCache for DexFile " << dex_file.GetLocation();
+ LOG(FATAL) << "Failed to find DexCache for DexFile " << dex_file.GetLocation()
+ << " " << &dex_file << " " << dex_cache_data.dex_file;
UNREACHABLE();
}
@@ -3722,20 +3739,6 @@
return DexCacheData();
}
-void ClassLinker::FixupDexCaches(ArtMethod* resolution_method) {
- Thread* const self = Thread::Current();
- ReaderMutexLock mu(self, *Locks::dex_lock_);
- for (const DexCacheData& data : dex_caches_) {
- if (!self->IsJWeakCleared(data.weak_root)) {
- ObjPtr<mirror::DexCache> dex_cache = ObjPtr<mirror::DexCache>::DownCast(
- self->DecodeJObject(data.weak_root));
- if (dex_cache != nullptr) {
- dex_cache->Fixup(resolution_method, image_pointer_size_);
- }
- }
- }
-}
-
mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) {
ObjPtr<mirror::Class> klass =
AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_));
@@ -4279,13 +4282,7 @@
std::string error_msg;
verifier::FailureKind verifier_failure = verifier::FailureKind::kNoFailure;
if (!preverified) {
- Runtime* runtime = Runtime::Current();
- verifier_failure = verifier::MethodVerifier::VerifyClass(self,
- klass.Get(),
- runtime->GetCompilerCallbacks(),
- runtime->IsAotCompiler(),
- log_level,
- &error_msg);
+ verifier_failure = PerformClassVerification(self, klass, log_level, &error_msg);
}
// Verification is done, grab the lock again.
@@ -4353,6 +4350,19 @@
return verifier_failure;
}
+verifier::FailureKind ClassLinker::PerformClassVerification(Thread* self,
+ Handle<mirror::Class> klass,
+ verifier::HardFailLogMode log_level,
+ std::string* error_msg) {
+ Runtime* const runtime = Runtime::Current();
+ return verifier::MethodVerifier::VerifyClass(self,
+ klass.Get(),
+ runtime->GetCompilerCallbacks(),
+ runtime->IsAotCompiler(),
+ log_level,
+ error_msg);
+}
+
bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file,
ObjPtr<mirror::Class> klass,
mirror::Class::Status& oat_file_class_status) {
@@ -4719,7 +4729,7 @@
CHECK_STREQ(np->GetName(), prototype->GetName());
CHECK_STREQ(np->GetShorty(), prototype->GetShorty());
// More complex sanity - via dex cache
- CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */));
+ CHECK_EQ(np->ResolveReturnType(), prototype->ResolveReturnType());
}
bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics,
@@ -4859,11 +4869,16 @@
return WaitForInitializeClass(klass, self, lock);
}
+ // Try to get the oat class's status for this class if the oat file is present. The compiler
+ // tries to validate superclass descriptors, and writes the result into the oat file.
+ // Runtime correctness is guaranteed by classpath checks done on loading. If the classpath
+ // is different at runtime than it was at compile time, the oat file is rejected. So if the
+ // oat file is present, the classpaths must match, and the runtime time check can be skipped.
bool has_oat_class = false;
- const OatFile::OatClass oat_class =
- (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler())
- ? OatFile::FindOatClass(klass->GetDexFile(), klass->GetDexClassDefIndex(), &has_oat_class)
- : OatFile::OatClass::Invalid();
+ const Runtime* runtime = Runtime::Current();
+ const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler())
+ ? OatFile::FindOatClass(klass->GetDexFile(), klass->GetDexClassDefIndex(), &has_oat_class)
+ : OatFile::OatClass::Invalid();
if (oat_class.GetStatus() < mirror::Class::kStatusSuperclassValidated &&
!ValidateSuperClassDescriptors(klass)) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
@@ -4894,7 +4909,9 @@
if (!super_initialized) {
// The super class was verified ahead of entering initializing, we should only be here if
// the super class became erroneous due to initialization.
- CHECK(handle_scope_super->IsErroneous() && self->IsExceptionPending())
+ // For the case of aot compiler, the super class might also be initializing but we don't
+ // want to process circular dependencies in pre-compile.
+ CHECK(self->IsExceptionPending())
<< "Super class initialization failed for "
<< handle_scope_super->PrettyDescriptor()
<< " that has unexpected status " << handle_scope_super->GetStatus()
@@ -5195,12 +5212,12 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
{
StackHandleScope<1> hs(self);
- Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */)));
+ Handle<mirror::Class> return_type(hs.NewHandle(method1->ResolveReturnType()));
if (UNLIKELY(return_type == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
return false;
}
- ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */);
+ ObjPtr<mirror::Class> other_return_type = method2->ResolveReturnType();
if (UNLIKELY(other_return_type == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2);
return false;
@@ -5245,7 +5262,7 @@
StackHandleScope<1> hs(self);
dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
Handle<mirror::Class> param_type(hs.NewHandle(
- method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */)));
+ method1->ResolveClassFromTypeIndex(param_type_idx)));
if (UNLIKELY(param_type == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method1, i, param_type_idx);
@@ -5253,7 +5270,7 @@
}
dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_;
ObjPtr<mirror::Class> other_param_type =
- method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */);
+ method2->ResolveClassFromTypeIndex(other_param_type_idx);
if (UNLIKELY(other_param_type == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method2, i, other_param_type_idx);
@@ -5490,7 +5507,9 @@
// Update CHA info based on whether we override methods.
// Have to do this before setting the class as resolved which allows
// instantiation of klass.
- Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(klass);
+ if (cha_ != nullptr) {
+ cha_->UpdateAfterLoadingOf(klass);
+ }
// This will notify waiters on klass that saw the not yet resolved
// class in the class_table_ during EnsureResolved.
@@ -5538,7 +5557,9 @@
// Update CHA info based on whether we override methods.
// Have to do this before setting the class as resolved which allows
// instantiation of klass.
- Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(h_new_class);
+ if (cha_ != nullptr) {
+ cha_->UpdateAfterLoadingOf(h_new_class);
+ }
// This will notify waiters on temp class that saw the not yet resolved class in the
// class_table_ during EnsureResolved.
@@ -5628,12 +5649,18 @@
return false;
}
// Verify
- if (super->IsFinal() || super->IsInterface()) {
+ if (super->IsFinal()) {
+ ThrowVerifyError(klass.Get(),
+ "Superclass %s of %s is declared final",
+ super->PrettyDescriptor().c_str(),
+ klass->PrettyDescriptor().c_str());
+ return false;
+ }
+ if (super->IsInterface()) {
ThrowIncompatibleClassChangeError(klass.Get(),
- "Superclass %s of %s is %s",
+ "Superclass %s of %s is an interface",
super->PrettyDescriptor().c_str(),
- klass->PrettyDescriptor().c_str(),
- super->IsFinal() ? "declared final" : "an interface");
+ klass->PrettyDescriptor().c_str());
return false;
}
if (!klass->CanAccess(super)) {
@@ -6886,7 +6913,8 @@
// Check that there are no stale methods are in the dex cache array.
auto* resolved_methods = klass_->GetDexCache()->GetResolvedMethods();
for (size_t i = 0, count = klass_->GetDexCache()->NumResolvedMethods(); i < count; ++i) {
- auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, pointer_size);
+ auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_methods, i, pointer_size);
+ ArtMethod* m = pair.object;
CHECK(move_table_.find(m) == move_table_.end() ||
// The original versions of copied methods will still be present so allow those too.
// Note that if the first check passes this might fail to GetDeclaringClass().
@@ -7100,7 +7128,7 @@
method_alignment_);
const size_t old_methods_ptr_size = (old_methods != nullptr) ? old_size : 0;
auto* methods = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
- Runtime::Current()->GetLinearAlloc()->Realloc(
+ class_linker_->GetAllocatorForClassLoader(klass_->GetClassLoader())->Realloc(
self_, old_methods, old_methods_ptr_size, new_size));
CHECK(methods != nullptr); // Native allocation failure aborts.
@@ -7949,7 +7977,8 @@
PointerSize pointer_size = image_pointer_size_;
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, pointer_size);
Thread::PoisonObjectPointersIfDebug();
- bool valid_dex_cache_method = resolved != nullptr && !resolved->IsRuntimeMethod();
+ DCHECK(resolved == nullptr || !resolved->IsRuntimeMethod());
+ bool valid_dex_cache_method = resolved != nullptr;
if (kResolveMode == ResolveMode::kNoChecks && valid_dex_cache_method) {
// We have a valid method from the DexCache and no checks to perform.
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
@@ -8045,7 +8074,8 @@
Handle<mirror::ClassLoader> class_loader) {
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
Thread::PoisonObjectPointersIfDebug();
- if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
+ if (resolved != nullptr) {
+ DCHECK(!resolved->IsRuntimeMethod());
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
return resolved;
}
@@ -8504,7 +8534,7 @@
it.Next();
}
- Handle<mirror::Class> return_type = hs.NewHandle(target_method->GetReturnType(true));
+ Handle<mirror::Class> return_type = hs.NewHandle(target_method->ResolveReturnType());
if (UNLIKELY(return_type.IsNull())) {
DCHECK(self->IsExceptionPending());
return nullptr;
@@ -8581,7 +8611,7 @@
if (!method->IsNative()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
} else {
- SetEntryPointsToCompiledCode(method, GetQuickGenericJniStub());
+ method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
}
}
@@ -8833,16 +8863,6 @@
find_array_class_cache_next_victim_ = 0;
}
-void ClassLinker::ClearClassTableStrongRoots() const {
- Thread* const self = Thread::Current();
- WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- for (const ClassLoaderData& data : class_loaders_) {
- if (data.class_table != nullptr) {
- data.class_table->ClearStrongRoots();
- }
- }
-}
-
void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const {
Thread* const self = Thread::Current();
for (const ClassLoaderData& data : class_loaders_) {
@@ -8986,51 +9006,6 @@
return ret;
}
-std::unordered_set<std::string> ClassLinker::GetClassDescriptorsForResolvedClasses(
- const std::set<DexCacheResolvedClasses>& classes) {
- ScopedTrace trace(__PRETTY_FUNCTION__);
- std::unordered_set<std::string> ret;
- Thread* const self = Thread::Current();
- std::unordered_map<std::string, const DexFile*> location_to_dex_file;
- ScopedObjectAccess soa(self);
- ScopedAssertNoThreadSuspension ants(__FUNCTION__);
- ReaderMutexLock mu(self, *Locks::dex_lock_);
- for (const ClassLinker::DexCacheData& data : GetDexCachesData()) {
- if (!self->IsJWeakCleared(data.weak_root)) {
- ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(data.weak_root);
- if (dex_cache != nullptr) {
- const DexFile* dex_file = dex_cache->GetDexFile();
- // There could be duplicates if two dex files with the same location are mapped.
- location_to_dex_file.emplace(dex_file->GetLocation(), dex_file);
- }
- }
- }
- for (const DexCacheResolvedClasses& info : classes) {
- const std::string& location = info.GetDexLocation();
- auto found = location_to_dex_file.find(location);
- if (found != location_to_dex_file.end()) {
- const DexFile* dex_file = found->second;
- VLOG(profiler) << "Found opened dex file for " << dex_file->GetLocation() << " with "
- << info.GetClasses().size() << " classes";
- DCHECK_EQ(dex_file->GetLocationChecksum(), info.GetLocationChecksum());
- for (dex::TypeIndex type_idx : info.GetClasses()) {
- if (!dex_file->IsTypeIndexValid(type_idx)) {
- // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
- LOG(WARNING) << "Corrupted profile: invalid type index "
- << type_idx.index_ << " in dex " << location;
- return std::unordered_set<std::string>();
- }
- const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
- const char* descriptor = dex_file->GetTypeDescriptor(type_id);
- ret.insert(descriptor);
- }
- } else {
- VLOG(class_linker) << "Failed to find opened dex file for location " << location;
- }
- }
- return ret;
-}
-
class ClassLinker::FindVirtualMethodHolderVisitor : public ClassVisitor {
public:
FindVirtualMethodHolderVisitor(const ArtMethod* method, PointerSize pointer_size)
@@ -9065,6 +9040,53 @@
ifcount * mirror::IfTable::kMax));
}
+ArtMethod* ClassLinker::FindMethodForProxy(ArtMethod* proxy_method) {
+ DCHECK(proxy_method->IsProxyMethod());
+ {
+ uint32_t method_index = proxy_method->GetDexMethodIndex();
+ PointerSize pointer_size = image_pointer_size_;
+ Thread* const self = Thread::Current();
+ ReaderMutexLock mu(self, *Locks::dex_lock_);
+ // Locate the dex cache of the original interface/Object
+ for (const DexCacheData& data : dex_caches_) {
+ if (!self->IsJWeakCleared(data.weak_root) &&
+ proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods, pointer_size)) {
+ ObjPtr<mirror::DexCache> dex_cache =
+ ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
+ if (dex_cache != nullptr) {
+ // Lookup up the method. Instead of going through LookupResolvedMethod()
+ // and thus LookupResolvedType(), use the ClassTable from the DexCacheData.
+ ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_index, pointer_size);
+ if (resolved_method == nullptr) {
+ const DexFile::MethodId& method_id = data.dex_file->GetMethodId(method_index);
+ ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(method_id.class_idx_);
+ if (klass == nullptr) {
+ const char* descriptor = data.dex_file->StringByTypeIdx(method_id.class_idx_);
+ klass = data.class_table->Lookup(descriptor, ComputeModifiedUtf8Hash(descriptor));
+ DCHECK(klass != nullptr);
+ dex_cache->SetResolvedType(method_id.class_idx_, klass);
+ }
+ if (klass->IsInterface()) {
+ resolved_method = klass->FindInterfaceMethod(dex_cache, method_index, pointer_size);
+ } else {
+ DCHECK(
+ klass == WellKnownClasses::ToClass(WellKnownClasses::java_lang_reflect_Proxy) ||
+ klass == WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object));
+ resolved_method = klass->FindClassMethod(dex_cache, method_index, pointer_size);
+ }
+ CHECK(resolved_method != nullptr);
+ dex_cache->SetResolvedMethod(method_index, resolved_method, pointer_size);
+ }
+ return resolved_method;
+ }
+ }
+ }
+ }
+ // Note: Do not use proxy_method->PrettyMethod() as it can call back here.
+ LOG(FATAL) << "Didn't find dex cache for " << proxy_method->GetDeclaringClass()->PrettyClass();
+ UNREACHABLE();
+}
+
// Instantiate ResolveMethod.
template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
const DexFile& dex_file,
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 3cf59f0..2fbbe79 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -55,8 +55,12 @@
class MethodType;
template<class T> class ObjectArray;
class StackTraceElement;
+ template <typename T> struct NativeDexCachePair;
+ using MethodDexCachePair = NativeDexCachePair<ArtMethod>;
+ using MethodDexCacheType = std::atomic<MethodDexCachePair>;
} // namespace mirror
+class ClassHierarchyAnalysis;
class ClassTable;
template<class T> class Handle;
class ImtConflictTable;
@@ -137,7 +141,7 @@
};
explicit ClassLinker(InternTable* intern_table);
- ~ClassLinker();
+ virtual ~ClassLinker();
// Initialize class linker by bootstraping from dex files.
bool InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path,
@@ -287,6 +291,12 @@
kCheckICCEAndIAE
};
+ // Look up a previously resolved method with the given index.
+ ArtMethod* LookupResolvedMethod(uint32_t method_idx,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Resolve a method with a given ID from the DexFile, storing the
// result in DexCache. The ClassLinker and ClassLoader are used as
// in ResolveType. What is unique is the method type argument which
@@ -387,9 +397,6 @@
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- void RegisterBootClassPathDexFile(const DexFile& dex_file, ObjPtr<mirror::DexCache> dex_cache)
- REQUIRES(!Locks::dex_lock_)
- REQUIRES_SHARED(Locks::mutator_lock_);
const std::vector<const DexFile*>& GetBootClassPath() {
return boot_class_path_;
@@ -423,9 +430,6 @@
ClassTable* FindClassTable(Thread* self, ObjPtr<mirror::DexCache> dex_cache)
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- void FixupDexCaches(ArtMethod* resolution_method)
- REQUIRES(!Locks::dex_lock_)
- REQUIRES_SHARED(Locks::mutator_lock_);
LengthPrefixedArray<ArtField>* AllocArtFieldArray(Thread* self,
LinearAlloc* allocator,
@@ -475,8 +479,7 @@
REQUIRES_SHARED(Locks::mutator_lock_);
std::string GetDescriptorForProxy(ObjPtr<mirror::Class> proxy_class)
REQUIRES_SHARED(Locks::mutator_lock_);
- template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
- ArtMethod* FindMethodForProxy(ObjPtr<mirror::Class> proxy_class, ArtMethod* proxy_method)
+ ArtMethod* FindMethodForProxy(ArtMethod* proxy_method)
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -609,11 +612,6 @@
std::set<DexCacheResolvedClasses> GetResolvedClasses(bool ignore_boot_classes)
REQUIRES(!Locks::dex_lock_);
- // Returns the class descriptors for loaded dex files.
- std::unordered_set<std::string> GetClassDescriptorsForResolvedClasses(
- const std::set<DexCacheResolvedClasses>& classes)
- REQUIRES(!Locks::dex_lock_);
-
static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -637,9 +635,9 @@
// Create the IMT and conflict tables for a class.
void FillIMTAndConflictTables(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
- // Clear class table strong roots (other than classes themselves). This is done by dex2oat to
- // allow pruning dex caches.
- void ClearClassTableStrongRoots() const
+ // Visit all of the class tables. This is used by dex2oat to allow pruning dex caches.
+ template <class Visitor>
+ void VisitClassTables(const Visitor& visitor)
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -672,6 +670,10 @@
bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
+ ClassHierarchyAnalysis* GetClassHierarchyAnalysis() {
+ return cha_.get();
+ }
+
struct DexCacheData {
// Construct an invalid data object.
DexCacheData()
@@ -692,7 +694,7 @@
// jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with
// class unloading.)
const DexFile* dex_file;
- ArtMethod** resolved_methods;
+ mirror::MethodDexCacheType* resolved_methods;
// Identify the associated class loader's class table. This is used to make sure that
// the Java call to native DexCache.setResolvedType() inserts the resolved type in that
// class table. It is also used to make sure we don't register the same dex cache with
@@ -700,6 +702,20 @@
ClassTable* class_table;
};
+ protected:
+ virtual bool InitializeClass(Thread* self,
+ Handle<mirror::Class> klass,
+ bool can_run_clinit,
+ bool can_init_parents)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
+ virtual verifier::FailureKind PerformClassVerification(Thread* self,
+ Handle<mirror::Class> klass,
+ verifier::HardFailLogMode log_level,
+ std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
class LinkInterfaceMethodsHelper;
@@ -718,7 +734,7 @@
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- static void DeleteClassLoader(Thread* self, const ClassLoaderData& data)
+ void DeleteClassLoader(Thread* self, const ClassLoaderData& data)
REQUIRES_SHARED(Locks::mutator_lock_);
void VisitClassesInternal(ClassVisitor* visitor)
@@ -889,12 +905,6 @@
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool InitializeClass(Thread* self,
- Handle<mirror::Class> klass,
- bool can_run_clinit,
- bool can_init_parents)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
bool InitializeDefaultInterfaceRecursive(Thread* self,
Handle<mirror::Class> klass,
bool can_run_clinit,
@@ -1268,6 +1278,8 @@
// Image pointer size.
PointerSize image_pointer_size_;
+ std::unique_ptr<ClassHierarchyAnalysis> cha_;
+
class FindVirtualMethodHolderVisitor;
friend class AppImageClassLoadersAndDexCachesHelper;
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 98d7c7c..5e9707c 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -28,9 +28,10 @@
#include "common_runtime_test.h"
#include "dex_file.h"
#include "dex_file_types.h"
-#include "experimental_flags.h"
#include "entrypoints/entrypoint_utils-inl.h"
+#include "experimental_flags.h"
#include "gc/heap.h"
+#include "handle_scope-inl.h"
#include "mirror/accessible_object.h"
#include "mirror/call_site.h"
#include "mirror/class-inl.h"
@@ -39,16 +40,15 @@
#include "mirror/emulated_stack_frame.h"
#include "mirror/executable.h"
#include "mirror/field.h"
-#include "mirror/method_type.h"
#include "mirror/method_handle_impl.h"
#include "mirror/method_handles_lookup.h"
+#include "mirror/method_type.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
-#include "handle_scope-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
@@ -440,14 +440,6 @@
}
TestRootVisitor visitor;
class_linker_->VisitRoots(&visitor, kVisitRootFlagAllRoots);
- // Verify the dex cache has resolution methods in all resolved method slots
- ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), dex);
- auto* resolved_methods = dex_cache->GetResolvedMethods();
- for (size_t i = 0, num_methods = dex_cache->NumResolvedMethods(); i != num_methods; ++i) {
- EXPECT_TRUE(
- mirror::DexCache::GetElementPtrSize(resolved_methods, i, kRuntimePointerSize) != nullptr)
- << dex.GetLocation() << " i=" << i;
- }
}
class TestRootVisitor : public SingleRootVisitor {
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index eab3b86..e7051b3 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -68,6 +68,10 @@
}
}
+std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Default() {
+ return Create("");
+}
+
std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) {
std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext());
if (result->Parse(spec)) {
@@ -145,6 +149,10 @@
// ClasspathElem is the path of dex/jar/apk file.
bool ClassLoaderContext::Parse(const std::string& spec, bool parse_checksums) {
if (spec.empty()) {
+ // By default we load the dex files in a PathClassLoader.
+ // So an empty spec is equivalent to an empty PathClassLoader (this happens when running
+ // tests)
+ class_loader_chain_.push_back(ClassLoaderInfo(kPathClassLoader));
return true;
}
@@ -259,17 +267,30 @@
return removed_locations;
}
+std::string ClassLoaderContext::EncodeContextForDex2oat(const std::string& base_dir) const {
+ return EncodeContext(base_dir, /*for_dex2oat*/ true);
+}
+
std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir) const {
+ return EncodeContext(base_dir, /*for_dex2oat*/ false);
+}
+
+std::string ClassLoaderContext::EncodeContext(const std::string& base_dir,
+ bool for_dex2oat) const {
CheckDexFilesOpened("EncodeContextForOatFile");
if (special_shared_library_) {
return OatFile::kSpecialSharedLibrary;
}
- if (class_loader_chain_.empty()) {
- return "";
- }
-
std::ostringstream out;
+ if (class_loader_chain_.empty()) {
+ // We can get in this situation if the context was created with a class path containing the
+ // source dex files which were later removed (happens during run-tests).
+ out << GetClassLoaderTypeName(kPathClassLoader)
+ << kClassLoaderOpeningMark
+ << kClassLoaderClosingMark;
+ return out.str();
+ }
for (size_t i = 0; i < class_loader_chain_.size(); i++) {
const ClassLoaderInfo& info = class_loader_chain_[i];
@@ -278,8 +299,17 @@
}
out << GetClassLoaderTypeName(info.type);
out << kClassLoaderOpeningMark;
+ std::set<std::string> seen_locations;
for (size_t k = 0; k < info.opened_dex_files.size(); k++) {
const std::unique_ptr<const DexFile>& dex_file = info.opened_dex_files[k];
+ 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;
+ if (!new_insert) {
+ continue;
+ }
+ }
const std::string& location = dex_file->GetLocation();
if (k > 0) {
out << kClasspathSeparator;
@@ -290,8 +320,11 @@
} else {
out << dex_file->GetLocation().c_str();
}
- out << kDexFileChecksumSeparator;
- out << dex_file->GetLocationChecksum();
+ // dex2oat does not need the checksums.
+ if (!for_dex2oat) {
+ out << kDexFileChecksumSeparator;
+ out << dex_file->GetLocationChecksum();
+ }
}
out << kClassLoaderClosingMark;
}
@@ -585,7 +618,7 @@
}
}
-bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) {
+bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
ClassLoaderContext expected_context;
if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
LOG(WARNING) << "Invalid class loader context: " << context_spec;
@@ -599,7 +632,8 @@
if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) {
LOG(WARNING) << "ClassLoaderContext size mismatch. expected="
<< expected_context.class_loader_chain_.size()
- << ", actual=" << class_loader_chain_.size();
+ << ", actual=" << class_loader_chain_.size()
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
@@ -609,13 +643,15 @@
if (info.type != expected_info.type) {
LOG(WARNING) << "ClassLoaderContext type mismatch for position " << i
<< ". expected=" << GetClassLoaderTypeName(expected_info.type)
- << ", found=" << GetClassLoaderTypeName(info.type);
+ << ", found=" << GetClassLoaderTypeName(info.type)
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
if (info.classpath.size() != expected_info.classpath.size()) {
LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i
<< ". expected=" << expected_info.classpath.size()
- << ", found=" << info.classpath.size();
+ << ", found=" << info.classpath.size()
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
@@ -626,13 +662,15 @@
if (info.classpath[k] != expected_info.classpath[k]) {
LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
<< ". expected=" << expected_info.classpath[k]
- << ", found=" << info.classpath[k];
+ << ", found=" << info.classpath[k]
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
if (info.checksums[k] != expected_info.checksums[k]) {
LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
<< ". expected=" << expected_info.checksums[k]
- << ", found=" << info.checksums[k];
+ << ", found=" << info.checksums[k]
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
}
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 37dd02b..9afa880 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -34,9 +34,6 @@
// Utility class which holds the class loader context used during compilation/verification.
class ClassLoaderContext {
public:
- // Creates an empty context (with no class loaders).
- ClassLoaderContext();
-
~ClassLoaderContext();
// Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
@@ -82,9 +79,16 @@
// (so that it can be read and verified at runtime against the actual class
// loader hierarchy).
// Should only be called if OpenDexFiles() returned true.
- // E.g. if the context is PCL[a.dex:b.dex] this will return "a.dex*a_checksum*b.dex*a_checksum".
+ // E.g. if the context is PCL[a.dex:b.dex] this will return
+ // "PCL[a.dex*a_checksum*b.dex*a_checksum]".
std::string EncodeContextForOatFile(const std::string& base_dir) const;
+ // Encodes the context as a string suitable to be passed to dex2oat.
+ // This is the same as EncodeContextForOatFile but without adding the checksums
+ // and only adding each dex files once (no multidex).
+ // Should only be called if OpenDexFiles() returned true.
+ std::string EncodeContextForDex2oat(const std::string& base_dir) const;
+
// Flattens the opened dex files into the given vector.
// Should only be called if OpenDexFiles() returned true.
std::vector<const DexFile*> FlattenOpenedDexFiles() const;
@@ -94,7 +98,7 @@
// - the number and type of the class loaders from the chain matches
// - the class loader from the same position have the same classpath
// (the order and checksum of the dex files matches)
- bool VerifyClassLoaderContextMatch(const std::string& context_spec);
+ bool VerifyClassLoaderContextMatch(const std::string& context_spec) const;
// Creates the class loader context from the given string.
// The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
@@ -119,6 +123,10 @@
static std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader,
jobjectArray dex_elements);
+ // Returns the default class loader context to be used when none is specified.
+ // This will return a context with a single and empty PathClassLoader.
+ static std::unique_ptr<ClassLoaderContext> Default();
+
private:
enum ClassLoaderType {
kInvalidClassLoader = 0,
@@ -144,6 +152,9 @@
explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {}
};
+ // Creates an empty context (with no class loaders).
+ ClassLoaderContext();
+
// Constructs an empty context.
// `owns_the_dex_files` specifies whether or not the context will own the opened dex files
// present in the class loader chain. If `owns_the_dex_files` is true then OpenDexFiles cannot
@@ -173,7 +184,15 @@
bool AddInfoToContextFromClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ClassLoader> class_loader,
Handle<mirror::ObjectArray<mirror::Object>> dex_elements)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Encodes the context as a string suitable to be passed to dex2oat or to be added to the
+ // oat file as the class path key.
+ // If for_dex2oat is true, the encoding adds each file once (i.e. it does not add multidex
+ // location). Otherwise, for oat files, the encoding adds all the dex files (including multidex)
+ // together with their checksums.
+ // Should only be called if OpenDexFiles() returned true.
+ std::string EncodeContext(const std::string& base_dir, bool for_dex2oat) const;
// Extracts the class loader type from the given spec.
// Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index a87552d..d4688c1 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -16,7 +16,6 @@
#include <gtest/gtest.h>
-
#include "class_loader_context.h"
#include "common_runtime_test.h"
@@ -162,6 +161,19 @@
}
};
+TEST_F(ClassLoaderContextTest, ParseValidEmptyContext) {
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("");
+ // An empty context should create a single empty PathClassLoader.
+ VerifyContextSize(context.get(), 1);
+ VerifyClassLoaderPCL(context.get(), 0, "");
+}
+
+TEST_F(ClassLoaderContextTest, ParseValidSharedLibraryContext) {
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("&");
+ // An shared library context should have no class loader in the chain.
+ VerifyContextSize(context.get(), 0);
+}
+
TEST_F(ClassLoaderContextTest, ParseValidContextPCL) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[a.dex]");
@@ -313,6 +325,34 @@
soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
}
+TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraryContext) {
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("&");
+
+ ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+ std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
+
+ std::vector<const DexFile*> compilation_sources_raw =
+ MakeNonOwningPointerVector(compilation_sources);
+ jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
+ ASSERT_TRUE(jclass_loader != nullptr);
+
+ ScopedObjectAccess soa(Thread::Current());
+
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
+ soa.Decode<mirror::ClassLoader>(jclass_loader));
+
+ // A shared library context should create a single PathClassLoader with only the compilation
+ // sources.
+ VerifyClassLoaderDexFiles(soa,
+ class_loader,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ compilation_sources_raw);
+ ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
+ soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+}
+
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) {
// Setup the context.
std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA");
@@ -415,6 +455,20 @@
ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
}
+TEST_F(ClassLoaderContextTest, EncodeForDex2oat) {
+ std::string dex1_name = GetTestDexFileName("Main");
+ std::string dex2_name = GetTestDexFileName("MultiDex");
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]");
+ ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+ std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main");
+ std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MultiDex");
+ std::string encoding = context->EncodeContextForDex2oat("");
+ std::string expected_encoding = "PCL[" + dex1_name + ":" + dex2_name + "]";
+ ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat(""));
+}
+
// TODO(calin) add a test which creates the context for a class loader together with dex_elements.
TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) {
// The chain is
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index b15d82f..1280466 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -132,6 +132,13 @@
}
}
+template <typename Filter>
+inline void ClassTable::RemoveStrongRoots(const Filter& filter) {
+ WriterMutexLock mu(Thread::Current(), lock_);
+ strong_roots_.erase(std::remove_if(strong_roots_.begin(), strong_roots_.end(), filter),
+ strong_roots_.end());
+}
+
} // namespace art
#endif // ART_RUNTIME_CLASS_TABLE_INL_H_
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 8616dfb..a259725 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -250,6 +250,12 @@
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Filter strong roots (other than classes themselves).
+ template <typename Filter>
+ void RemoveStrongRoots(const Filter& filter)
+ REQUIRES(!lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ReaderWriterMutex& GetLock() {
return lock_;
}
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 528db96..fcc5393 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -200,6 +200,11 @@
break;
}
}
+ if (transaction_active) {
+ if (UNLIKELY(self->IsExceptionPending())) {
+ return false;
+ }
+ }
return true;
}
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index a425224..29b376a 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -16,12 +16,12 @@
#include "common_runtime_test.h"
-#include <cstdio>
#include <dirent.h>
#include <dlfcn.h>
#include <fcntl.h>
-#include "nativehelper/ScopedLocalRef.h"
#include <stdlib.h>
+#include <cstdio>
+#include "nativehelper/ScopedLocalRef.h"
#include "../../external/icu/icu4c/source/common/unicode/uvernum.h"
#include "android-base/stringprintf.h"
@@ -425,7 +425,6 @@
PostRuntimeCreate();
runtime_.reset(Runtime::Current());
class_linker_ = runtime_->GetClassLinker();
- class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
// Runtime::Create acquired the mutator_lock_ that is normally given away when we
// Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess.
@@ -786,6 +785,60 @@
return classpath;
}
+void CommonRuntimeTestImpl::FillHeap(Thread* self,
+ ClassLinker* class_linker,
+ VariableSizedHandleScope* handle_scope) {
+ DCHECK(handle_scope != nullptr);
+
+ Runtime::Current()->GetHeap()->SetIdealFootprint(1 * GB);
+
+ // Class java.lang.Object.
+ Handle<mirror::Class> c(handle_scope->NewHandle(
+ class_linker->FindSystemClass(self, "Ljava/lang/Object;")));
+ // Array helps to fill memory faster.
+ Handle<mirror::Class> ca(handle_scope->NewHandle(
+ class_linker->FindSystemClass(self, "[Ljava/lang/Object;")));
+
+ // Start allocating with ~128K
+ size_t length = 128 * KB;
+ while (length > 40) {
+ const int32_t array_length = length / 4; // Object[] has elements of size 4.
+ MutableHandle<mirror::Object> h(handle_scope->NewHandle<mirror::Object>(
+ mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), array_length)));
+ if (self->IsExceptionPending() || h == nullptr) {
+ self->ClearException();
+
+ // Try a smaller length
+ length = length / 2;
+ // Use at most a quarter the reported free space.
+ size_t mem = Runtime::Current()->GetHeap()->GetFreeMemory();
+ if (length * 4 > mem) {
+ length = mem / 4;
+ }
+ }
+ }
+
+ // Allocate simple objects till it fails.
+ while (!self->IsExceptionPending()) {
+ handle_scope->NewHandle<mirror::Object>(c->AllocObject(self));
+ }
+ self->ClearException();
+}
+
+void CommonRuntimeTestImpl::SetUpRuntimeOptionsForFillHeap(RuntimeOptions *options) {
+ // Use a smaller heap
+ bool found = false;
+ for (std::pair<std::string, const void*>& pair : *options) {
+ if (pair.first.find("-Xmx") == 0) {
+ pair.first = "-Xmx4M"; // Smallest we can go.
+ found = true;
+ }
+ }
+ if (!found) {
+ options->emplace_back("-Xmx4M", nullptr);
+ }
+}
+
CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) {
vm_->SetCheckJniAbortHook(Hook, &actual_);
}
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index daf9ac3..74bc0b2 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -44,6 +44,8 @@
class JavaVMExt;
class Runtime;
typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;
+class Thread;
+class VariableSizedHandleScope;
uint8_t* DecodeBase64(const char* src, size_t* dst_size);
@@ -105,6 +107,14 @@
// Retuerns the filename for a test dex (i.e. XandY or ManyMethods).
std::string GetTestDexFileName(const char* name) const;
+ // A helper function to fill the heap.
+ static void FillHeap(Thread* self,
+ ClassLinker* class_linker,
+ VariableSizedHandleScope* handle_scope)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // A helper to set up a small heap (4M) to make FillHeap faster.
+ static void SetUpRuntimeOptionsForFillHeap(RuntimeOptions *options);
+
protected:
// Allow subclases such as CommonCompilerTest to add extra options.
virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {}
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
index 806653a..c51bb5e 100644
--- a/runtime/compiler_callbacks.h
+++ b/runtime/compiler_callbacks.h
@@ -22,6 +22,8 @@
namespace art {
+class CompilerDriver;
+
namespace verifier {
class MethodVerifier;
@@ -49,6 +51,13 @@
virtual verifier::VerifierDeps* GetVerifierDeps() const = 0;
virtual void SetVerifierDeps(verifier::VerifierDeps* deps ATTRIBUTE_UNUSED) {}
+ virtual bool CanAssumeVerified(ClassReference ref ATTRIBUTE_UNUSED) {
+ return false;
+ }
+
+ virtual void SetDoesClassUnloading(bool does_class_unloading ATTRIBUTE_UNUSED,
+ CompilerDriver* compiler_driver ATTRIBUTE_UNUSED) {}
+
bool IsBootImage() {
return mode_ == CallbackMode::kCompileBootImage;
}
diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc
index 4847f38..7b2dd05 100644
--- a/runtime/compiler_filter.cc
+++ b/runtime/compiler_filter.cc
@@ -165,6 +165,10 @@
return current >= target;
}
+bool CompilerFilter::IsBetter(Filter current, Filter target) {
+ return current > target;
+}
+
std::string CompilerFilter::NameOfFilter(Filter filter) {
switch (filter) {
case CompilerFilter::kAssumeVerified: return "assume-verified";
diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h
index f802439..60975b0 100644
--- a/runtime/compiler_filter.h
+++ b/runtime/compiler_filter.h
@@ -84,6 +84,11 @@
// not as good as kSpeed.
static bool IsAsGoodAs(Filter current, Filter target);
+ // Returns true if 'current' compiler filter is better than 'target' compiler
+ // filter. Compared to IsAsGoodAs, this returns false if the compiler filters are
+ // equal.
+ static bool IsBetter(Filter current, Filter target);
+
// Return the flag name of the given filter.
// For example: given kVerifyAtRuntime, returns "verify-at-runtime".
// The name returned corresponds to the name accepted by
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 5a87ae8..0b7af4e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4008,8 +4008,8 @@
if (shorty[i + 1] == 'L') {
// Did we really get an argument of an appropriate reference type?
- mirror::Class* parameter_type =
- m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */);
+ ObjPtr<mirror::Class> parameter_type =
+ m->ResolveClassFromTypeIndex(types->GetTypeItem(i).type_idx_);
mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 4f3ff40..0be46d6 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -27,8 +27,8 @@
#include <string>
#include <vector>
-#include "gc_root.h"
#include "class_linker.h"
+#include "gc_root.h"
#include "handle.h"
#include "jdwp/jdwp.h"
#include "jni.h"
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 2b81f0a..4225ab9 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -695,8 +695,7 @@
if (annotation_method == nullptr) {
return nullptr;
}
- Handle<mirror::Class> method_return(hs.NewHandle(
- annotation_method->GetReturnType(true /* resolve */)));
+ Handle<mirror::Class> method_return(hs.NewHandle(annotation_method->ResolveReturnType()));
DexFile::AnnotationValue annotation_value;
if (!ProcessAnnotationValue<false>(klass,
@@ -762,15 +761,16 @@
}
const uint8_t* annotation = annotation_item->annotation_;
uint32_t type_index = DecodeUnsignedLeb128(&annotation);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::Current();
mirror::Class* resolved_class;
if (lookup_in_resolved_boot_classes) {
+ // Note: We cannot use ClassLinker::LookupResolvedType() because the current DexCache
+ // may not be registered with the boot class path ClassLoader and we must not pollute
+ // the DexCache with classes that are not in the associated ClassLoader's ClassTable.
+ const char* descriptor = dex_file.StringByTypeIdx(dex::TypeIndex(type_index));
ObjPtr<mirror::Class> looked_up_class =
- Runtime::Current()->GetClassLinker()->LookupResolvedType(
- klass.GetDexFile(),
- dex::TypeIndex(type_index),
- klass.GetDexCache(),
- // Force the use of the bootstrap class loader.
- static_cast<mirror::ClassLoader*>(nullptr));
+ class_linker->LookupClass(self, descriptor, /* class_loader */ nullptr);
resolved_class = looked_up_class.Ptr();
if (resolved_class == nullptr) {
// If `resolved_class` is null, this is fine: just ignore that
@@ -779,8 +779,8 @@
continue;
}
} else {
- StackHandleScope<2> hs(Thread::Current());
- resolved_class = Runtime::Current()->GetClassLinker()->ResolveType(
+ StackHandleScope<2> hs(self);
+ resolved_class = class_linker->ResolveType(
klass.GetDexFile(),
dex::TypeIndex(type_index),
hs.NewHandle(klass.GetDexCache()),
@@ -789,8 +789,8 @@
std::string temp;
LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d",
klass.GetRealClass()->GetDescriptor(&temp), type_index);
- CHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
continue;
}
}
@@ -1073,7 +1073,7 @@
}
DexFile::AnnotationValue annotation_value;
StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */)));
+ Handle<mirror::Class> return_type(hs.NewHandle(method->ResolveReturnType()));
if (!ProcessAnnotationValue<false>(klass,
&annotation,
&annotation_value,
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index c5c4eda..8fdd470 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -363,7 +363,7 @@
// Check file size from the header.
uint32_t expected_size = header_->file_size_;
if (size_ != expected_size) {
- ErrorStringPrintf("Bad file size (%zd, expected %ud)", size_, expected_size);
+ ErrorStringPrintf("Bad file size (%zd, expected %u)", size_, expected_size);
return false;
}
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 0e58e6d..e2770d1 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -16,14 +16,15 @@
#include "dex_file_verifier.h"
-#include "sys/mman.h"
-#include "zlib.h"
+#include <sys/mman.h>
+#include <zlib.h>
+
#include <functional>
#include <memory>
-#include "base/unix_file/fd_file.h"
#include "base/bit_utils.h"
#include "base/macros.h"
+#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "dex_file-inl.h"
#include "dex_file_types.h"
diff --git a/runtime/dex_reference_collection.h b/runtime/dex_reference_collection.h
index 01b9b97..047771f 100644
--- a/runtime/dex_reference_collection.h
+++ b/runtime/dex_reference_collection.h
@@ -17,10 +17,10 @@
#ifndef ART_RUNTIME_DEX_REFERENCE_COLLECTION_H_
#define ART_RUNTIME_DEX_REFERENCE_COLLECTION_H_
-#include "base/macros.h"
-
-#include <vector>
#include <map>
+#include <vector>
+
+#include "base/macros.h"
namespace art {
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 828148a..be3e4f8 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -84,7 +84,8 @@
const DexFile* dex_file = dex_cache->GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
ArtMethod* inlined_method = caller->GetDexCacheResolvedMethod(method_index, kRuntimePointerSize);
- if (inlined_method != nullptr && !inlined_method->IsRuntimeMethod()) {
+ if (inlined_method != nullptr) {
+ DCHECK(!inlined_method->IsRuntimeMethod());
return inlined_method;
}
const char* descriptor = dex_file->StringByTypeIdx(method_id.class_idx_);
@@ -475,7 +476,7 @@
case kDirect:
return resolved_method;
case kVirtual: {
- mirror::Class* klass = (*this_object)->GetClass();
+ ObjPtr<mirror::Class> klass = (*this_object)->GetClass();
uint16_t vtable_index = resolved_method->GetMethodIndex();
if (access_check &&
(!klass->HasVTable() ||
@@ -508,7 +509,7 @@
// It is not an interface. If the referring class is in the class hierarchy of the
// referenced class in the bytecode, we use its super class. Otherwise, we throw
// a NoSuchMethodError.
- mirror::Class* super_class = nullptr;
+ ObjPtr<mirror::Class> super_class = nullptr;
if (method_reference_class->IsAssignableFrom(h_referring_class.Get())) {
super_class = h_referring_class->GetSuperClass();
}
@@ -553,11 +554,10 @@
case kInterface: {
uint32_t imt_index = ImTable::GetImtIndex(resolved_method);
PointerSize pointer_size = class_linker->GetImagePointerSize();
- ArtMethod* imt_method = (*this_object)->GetClass()->GetImt(pointer_size)->
- Get(imt_index, pointer_size);
+ ObjPtr<mirror::Class> klass = (*this_object)->GetClass();
+ ArtMethod* imt_method = klass->GetImt(pointer_size)->Get(imt_index, pointer_size);
if (!imt_method->IsRuntimeMethod()) {
if (kIsDebugBuild) {
- mirror::Class* klass = (*this_object)->GetClass();
ArtMethod* method = klass->FindVirtualMethodForInterface(
resolved_method, class_linker->GetImagePointerSize());
CHECK_EQ(imt_method, method) << ArtMethod::PrettyMethod(resolved_method) << " / "
@@ -567,7 +567,7 @@
}
return imt_method;
} else {
- ArtMethod* interface_method = (*this_object)->GetClass()->FindVirtualMethodForInterface(
+ ArtMethod* interface_method = klass->FindVirtualMethodForInterface(
resolved_method, class_linker->GetImagePointerSize());
if (UNLIKELY(interface_method == nullptr)) {
ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method,
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 01fc9ce..2bf4372 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -45,7 +45,7 @@
}
// Make sure that the result is an instance of the type this method was expected to return.
ArtMethod* method = self->GetCurrentMethod(nullptr);
- mirror::Class* return_type = method->GetReturnType(true /* resolve */);
+ ObjPtr<mirror::Class> return_type = method->ResolveReturnType();
if (!o->InstanceOf(return_type)) {
Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
@@ -108,7 +108,7 @@
ArtMethod* interface_method =
soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
// This can cause thread suspension.
- mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */);
+ ObjPtr<mirror::Class> result_type = interface_method->ResolveReturnType();
ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
JValue result_unboxed;
if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) {
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index fe85887..4f90908 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -23,8 +23,8 @@
#include "base/callee_save_type.h"
#include "base/macros.h"
#include "base/mutex.h"
-#include "dex_instruction.h"
#include "dex_file_types.h"
+#include "dex_instruction.h"
#include "gc/allocator_type.h"
#include "handle.h"
#include "invoke_type.h"
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 582f0cf..b8d96af 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -22,8 +22,8 @@
#include "dex_file_types.h"
#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/class-inl.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
namespace art {
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index fe56543..5355267 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -17,16 +17,16 @@
#include "art_method-inl.h"
#include "base/callee_save_type.h"
#include "callee_save_frame.h"
-#include "entrypoints/entrypoint_utils-inl.h"
#include "class_linker-inl.h"
#include "class_table-inl.h"
#include "dex_file-inl.h"
#include "dex_file_types.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/heap.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
#include "oat_file.h"
#include "runtime.h"
@@ -65,8 +65,8 @@
// A class may be accessing another class' fields when it doesn't have access, as access has been
// given by inheritance.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self,
- CalleeSaveType::kSaveEverything);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(
+ self, CalleeSaveType::kSaveEverythingForClinit);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false);
@@ -80,8 +80,8 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
// Called when method->dex_cache_resolved_types_[] misses.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self,
- CalleeSaveType::kSaveEverything);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(
+ self, CalleeSaveType::kSaveEverythingForClinit);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false);
diff --git a/runtime/entrypoints/quick/quick_entrypoints_enum.h b/runtime/entrypoints/quick/quick_entrypoints_enum.h
index abf2c34..1cf7f8d 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_enum.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_enum.h
@@ -24,8 +24,7 @@
namespace art {
// Define an enum for the entrypoints. Names are prepended a 'kQuick'.
-enum QuickEntrypointEnum
-{ // NOLINT(whitespace/braces)
+enum QuickEntrypointEnum { // NOLINT(whitespace/braces)
#define ENTRYPOINT_ENUM(name, rettype, ...) kQuick ## name,
#include "quick_entrypoints_list.h"
QUICK_ENTRYPOINT_LIST(ENTRYPOINT_ENUM)
@@ -58,7 +57,7 @@
#define ENTRYPOINT_ENUM(name, ...) \
template <> inline void CheckEntrypointTypes<kQuick ## name, __VA_ARGS__>() {}; // NOLINT [readability/braces] [4]
#include "quick_entrypoints_list.h"
- QUICK_ENTRYPOINT_LIST(ENTRYPOINT_ENUM)
+QUICK_ENTRYPOINT_LIST(ENTRYPOINT_ENUM)
#undef QUICK_ENTRYPOINT_LIST
#undef ENTRYPOINT_ENUM
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 726bddd..b298db6 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -301,6 +301,7 @@
extern "C" mirror::Object* artReadBarrierSlow(mirror::Object* ref ATTRIBUTE_UNUSED,
mirror::Object* obj,
uint32_t offset) {
+ // Used only in connection with non-volatile loads.
DCHECK(kEmitCompilerReadBarrier);
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(obj) + offset;
mirror::HeapReference<mirror::Object>* ref_addr =
@@ -308,9 +309,10 @@
constexpr ReadBarrierOption kReadBarrierOption =
kUseReadBarrier ? kWithReadBarrier : kWithoutReadBarrier;
mirror::Object* result =
- ReadBarrier::Barrier<mirror::Object, kReadBarrierOption>(obj,
- MemberOffset(offset),
- ref_addr);
+ ReadBarrier::Barrier<mirror::Object, /* kIsVolatile */ false, kReadBarrierOption>(
+ obj,
+ MemberOffset(offset),
+ ref_addr);
return result;
}
diff --git a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
index f63c9c2..d4bc1c7 100644
--- a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
@@ -16,8 +16,8 @@
#include "art_method-inl.h"
#include "callee_save_frame.h"
-#include "mirror/array.h"
#include "entrypoints/entrypoint_utils.h"
+#include "mirror/array.h"
namespace art {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 6abf7c5..e08319d 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -27,8 +27,8 @@
#include "gc/accounting/card_table-inl.h"
#include "imt_conflict_table.h"
#include "imtable-inl.h"
-#include "interpreter/interpreter.h"
#include "instrumentation.h"
+#include "interpreter/interpreter.h"
#include "linear_alloc.h"
#include "method_bss_mapping.h"
#include "method_handles.h"
@@ -2461,6 +2461,21 @@
return artInvokeCommon<kVirtual, true>(method_idx, this_object, self, sp);
}
+// Helper function for art_quick_imt_conflict_trampoline to look up the interface method.
+extern "C" ArtMethod* artLookupResolvedMethod(uint32_t method_index, ArtMethod* referrer)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ DCHECK(!referrer->IsProxyMethod());
+ ArtMethod* result = Runtime::Current()->GetClassLinker()->LookupResolvedMethod(
+ method_index, referrer->GetDexCache(), referrer->GetClassLoader());
+ DCHECK(result == nullptr ||
+ result->GetDeclaringClass()->IsInterface() ||
+ result->GetDeclaringClass() ==
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object))
+ << result->PrettyMethod();
+ return result;
+}
+
// Determine target of interface dispatch. The interface method and this object are known non-null.
// The interface method is the method returned by the dex cache in the conflict trampoline.
extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_method,
@@ -2468,46 +2483,17 @@
Thread* self,
ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK(interface_method != nullptr);
- ObjPtr<mirror::Object> this_object(raw_this_object);
ScopedQuickEntrypointChecks sqec(self);
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> cls(hs.NewHandle(this_object->GetClass()));
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Object> this_object = hs.NewHandle(raw_this_object);
+ Handle<mirror::Class> cls = hs.NewHandle(this_object->GetClass());
ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
ArtMethod* method = nullptr;
ImTable* imt = cls->GetImt(kRuntimePointerSize);
- if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
- // If the interface method is already resolved, look whether we have a match in the
- // ImtConflictTable.
- ArtMethod* conflict_method = imt->Get(ImTable::GetImtIndex(interface_method),
- kRuntimePointerSize);
- if (LIKELY(conflict_method->IsRuntimeMethod())) {
- ImtConflictTable* current_table = conflict_method->GetImtConflictTable(kRuntimePointerSize);
- DCHECK(current_table != nullptr);
- method = current_table->Lookup(interface_method, kRuntimePointerSize);
- } else {
- // It seems we aren't really a conflict method!
- method = cls->FindVirtualMethodForInterface(interface_method, kRuntimePointerSize);
- }
- if (method != nullptr) {
- return GetTwoWordSuccessValue(
- reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode()),
- reinterpret_cast<uintptr_t>(method));
- }
-
- // No match, use the IfTable.
- method = cls->FindVirtualMethodForInterface(interface_method, kRuntimePointerSize);
- if (UNLIKELY(method == nullptr)) {
- ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(
- interface_method, this_object, caller_method);
- return GetTwoWordFailureValue(); // Failure.
- }
- } else {
- // The interface method is unresolved, so look it up in the dex file of the caller.
- DCHECK_EQ(interface_method, Runtime::Current()->GetResolutionMethod());
-
+ if (UNLIKELY(interface_method == nullptr)) {
+ // The interface method is unresolved, so resolve it in the dex file of the caller.
// Fetch the dex_method_idx of the target interface method from the caller.
uint32_t dex_method_idx;
uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
@@ -2525,50 +2511,74 @@
dex_method_idx = instr->VRegB_3rc();
}
- const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()
- ->GetDexFile();
+ const DexFile& dex_file = caller_method->GetDeclaringClass()->GetDexFile();
uint32_t shorty_len;
- const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx),
- &shorty_len);
+ const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(dex_method_idx),
+ &shorty_len);
{
- // Remember the args in case a GC happens in FindMethodFromCode.
+ // Remember the args in case a GC happens in ClassLinker::ResolveMethod().
ScopedObjectAccessUnchecked soa(self->GetJniEnv());
RememberForGcArgumentVisitor visitor(sp, false, shorty, shorty_len, &soa);
visitor.VisitArguments();
- method = FindMethodFromCode<kInterface, false>(dex_method_idx,
- &this_object,
- caller_method,
- self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ interface_method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
+ self, dex_method_idx, caller_method, kInterface);
visitor.FixupReferences();
}
- if (UNLIKELY(method == nullptr)) {
+ if (UNLIKELY(interface_method == nullptr)) {
CHECK(self->IsExceptionPending());
return GetTwoWordFailureValue(); // Failure.
}
- interface_method =
- caller_method->GetDexCacheResolvedMethod(dex_method_idx, kRuntimePointerSize);
- DCHECK(!interface_method->IsRuntimeMethod());
+ }
+
+ DCHECK(!interface_method->IsRuntimeMethod());
+ // Look whether we have a match in the ImtConflictTable.
+ uint32_t imt_index = ImTable::GetImtIndex(interface_method);
+ ArtMethod* conflict_method = imt->Get(imt_index, kRuntimePointerSize);
+ if (LIKELY(conflict_method->IsRuntimeMethod())) {
+ ImtConflictTable* current_table = conflict_method->GetImtConflictTable(kRuntimePointerSize);
+ DCHECK(current_table != nullptr);
+ method = current_table->Lookup(interface_method, kRuntimePointerSize);
+ } else {
+ // It seems we aren't really a conflict method!
+ if (kIsDebugBuild) {
+ ArtMethod* m = cls->FindVirtualMethodForInterface(interface_method, kRuntimePointerSize);
+ CHECK_EQ(conflict_method, m)
+ << interface_method->PrettyMethod() << " / " << conflict_method->PrettyMethod() << " / "
+ << " / " << ArtMethod::PrettyMethod(m) << " / " << cls->PrettyClass();
+ }
+ method = conflict_method;
+ }
+ if (method != nullptr) {
+ return GetTwoWordSuccessValue(
+ reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode()),
+ reinterpret_cast<uintptr_t>(method));
+ }
+
+ // No match, use the IfTable.
+ method = cls->FindVirtualMethodForInterface(interface_method, kRuntimePointerSize);
+ if (UNLIKELY(method == nullptr)) {
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(
+ interface_method, this_object.Get(), caller_method);
+ return GetTwoWordFailureValue(); // Failure.
}
// We arrive here if we have found an implementation, and it is not in the ImtConflictTable.
// We create a new table with the new pair { interface_method, method }.
- uint32_t imt_index = ImTable::GetImtIndex(interface_method);
- ArtMethod* conflict_method = imt->Get(imt_index, kRuntimePointerSize);
- if (conflict_method->IsRuntimeMethod()) {
- ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable(
- cls.Get(),
- conflict_method,
- interface_method,
- method,
- /*force_new_conflict_method*/false);
- if (new_conflict_method != conflict_method) {
- // Update the IMT if we create a new conflict method. No fence needed here, as the
- // data is consistent.
- imt->Set(imt_index,
- new_conflict_method,
- kRuntimePointerSize);
- }
+ DCHECK(conflict_method->IsRuntimeMethod());
+ ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable(
+ cls.Get(),
+ conflict_method,
+ interface_method,
+ method,
+ /*force_new_conflict_method*/false);
+ if (new_conflict_method != conflict_method) {
+ // Update the IMT if we create a new conflict method. No fence needed here, as the
+ // data is consistent.
+ imt->Set(imt_index,
+ new_conflict_method,
+ kRuntimePointerSize);
}
const void* code = method->GetEntryPointFromQuickCompiledCode();
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
index 7e08b7a..b692618 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc
@@ -90,7 +90,18 @@
GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveRefsOnly)); \
CheckFrameSize(isa, \
CalleeSaveType::kSaveAllCalleeSaves, \
- GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveAllCalleeSaves))
+ GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveAllCalleeSaves)); \
+ CheckFrameSize(isa, \
+ CalleeSaveType::kSaveEverything, \
+ GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveEverything)); \
+ CheckFrameSize(isa, \
+ CalleeSaveType::kSaveEverythingForClinit, \
+ GetCalleeSaveFrameSize(isa, \
+ CalleeSaveType::kSaveEverythingForClinit)); \
+ CheckFrameSize(isa, \
+ CalleeSaveType::kSaveEverythingForSuspendCheck, \
+ GetCalleeSaveFrameSize( \
+ isa, CalleeSaveType::kSaveEverythingForSuspendCheck))
CHECK_FRAME_SIZE(kArm);
CHECK_FRAME_SIZE(kArm64);
@@ -123,6 +134,13 @@
GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly));
CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves,
GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves));
+ CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverything,
+ GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverything));
+ CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit,
+ GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit));
+ CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForSuspendCheck,
+ GetCalleeSaveReturnPcOffset(kRuntimeISA,
+ CalleeSaveType::kSaveEverythingForSuspendCheck));
}
} // namespace art
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index a3c3981..281dfd9 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include <memory>
#include <setjmp.h>
+#include <memory>
+
#include "base/macros.h"
#include "common_runtime_test.h"
#include "thread.h"
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index fd0cd5f..7d01af0 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -79,8 +79,7 @@
static mirror::Class* SafeGetClass(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
char* obj_cls = reinterpret_cast<char*>(obj) + mirror::Object::ClassOffset().SizeValue();
- mirror::HeapReference<mirror::Class> cls =
- mirror::HeapReference<mirror::Class>::FromMirrorPtr(nullptr);
+ mirror::HeapReference<mirror::Class> cls;
ssize_t rc = SafeCopy(&cls, obj_cls, sizeof(cls));
CHECK_NE(-1, rc);
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index d56cf17..3e2664c 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -18,12 +18,13 @@
#ifndef ART_RUNTIME_FAULT_HANDLER_H_
#define ART_RUNTIME_FAULT_HANDLER_H_
-#include <signal.h>
-#include <vector>
#include <setjmp.h>
+#include <signal.h>
#include <stdint.h>
-#include "base/mutex.h" // For annotalysis.
+#include <vector>
+
+#include "base/mutex.h" // For annotalysis.
namespace art {
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 2901995..1b3d0da 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -115,8 +115,8 @@
}
private:
- template<bool kPoisonReferences>
- void MarkReference(mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr) const
+ template<typename CompressedReferenceType>
+ void MarkReference(CompressedReferenceType* obj_ptr) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Only add the reference if it is non null and fits our criteria.
mirror::Object* ref = obj_ptr->AsMirrorPtr();
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index 9e261fd..ee25eae 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_GC_ACCOUNTING_MOD_UNION_TABLE_H_
#define ART_RUNTIME_GC_ACCOUNTING_MOD_UNION_TABLE_H_
-#include "bitmap.h"
#include "base/allocator.h"
+#include "bitmap.h"
#include "card_table.h"
#include "globals.h"
#include "mirror/object_reference.h"
diff --git a/runtime/gc/accounting/remembered_set.cc b/runtime/gc/accounting/remembered_set.cc
index f2fe58a..9dea2f8 100644
--- a/runtime/gc/accounting/remembered_set.cc
+++ b/runtime/gc/accounting/remembered_set.cc
@@ -20,12 +20,12 @@
#include "base/stl_util.h"
#include "card_table-inl.h"
-#include "heap_bitmap.h"
-#include "gc/collector/mark_sweep.h"
#include "gc/collector/mark_sweep-inl.h"
+#include "gc/collector/mark_sweep.h"
#include "gc/collector/semi_space.h"
#include "gc/heap.h"
#include "gc/space/space.h"
+#include "heap_bitmap.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object-refvisitor-inl.h"
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index 317e2fc..280c0b1 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -21,8 +21,8 @@
#include "art_field-inl.h"
#include "dex_file-inl.h"
#include "mem_map.h"
-#include "mirror/object-inl.h"
#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
#include "mirror/object_array.h"
namespace art {
@@ -48,16 +48,21 @@
CHECK(mem_map != nullptr);
uintptr_t* bitmap_begin = reinterpret_cast<uintptr_t*>(mem_map->Begin());
const size_t bitmap_size = ComputeBitmapSize(heap_capacity);
- return new SpaceBitmap(name, mem_map, bitmap_begin, bitmap_size, heap_begin);
+ return new SpaceBitmap(name, mem_map, bitmap_begin, bitmap_size, heap_begin, heap_capacity);
}
template<size_t kAlignment>
-SpaceBitmap<kAlignment>::SpaceBitmap(const std::string& name, MemMap* mem_map, uintptr_t* bitmap_begin,
- size_t bitmap_size, const void* heap_begin)
+SpaceBitmap<kAlignment>::SpaceBitmap(const std::string& name,
+ MemMap* mem_map,
+ uintptr_t* bitmap_begin,
+ size_t bitmap_size,
+ const void* heap_begin,
+ size_t heap_capacity)
: mem_map_(mem_map),
bitmap_begin_(reinterpret_cast<Atomic<uintptr_t>*>(bitmap_begin)),
bitmap_size_(bitmap_size),
heap_begin_(reinterpret_cast<uintptr_t>(heap_begin)),
+ heap_limit_(reinterpret_cast<uintptr_t>(heap_begin) + heap_capacity),
name_(name) {
CHECK(bitmap_begin_ != nullptr);
CHECK_NE(bitmap_size, 0U);
@@ -89,6 +94,7 @@
if (new_size < bitmap_size_) {
bitmap_size_ = new_size;
}
+ heap_limit_ = new_end;
// Not sure if doing this trim is necessary, since nothing past the end of the heap capacity
// should be marked.
}
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index 2fe6394..b49e0b7 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -163,6 +163,7 @@
void SetHeapSize(size_t bytes) {
// TODO: Un-map the end of the mem map.
+ heap_limit_ = heap_begin_ + bytes;
bitmap_size_ = OffsetToIndex(bytes) * sizeof(intptr_t);
CHECK_EQ(HeapSize(), bytes);
}
@@ -173,7 +174,7 @@
// The maximum address which the bitmap can span. (HeapBegin() <= object < HeapLimit()).
uint64_t HeapLimit() const {
- return static_cast<uint64_t>(HeapBegin()) + HeapSize();
+ return heap_limit_;
}
// Set the max address which can covered by the bitmap.
@@ -196,8 +197,12 @@
private:
// TODO: heap_end_ is initialized so that the heap bitmap is empty, this doesn't require the -1,
// however, we document that this is expected on heap_end_
- SpaceBitmap(const std::string& name, MemMap* mem_map, uintptr_t* bitmap_begin, size_t bitmap_size,
- const void* heap_begin);
+ SpaceBitmap(const std::string& name,
+ MemMap* mem_map,
+ uintptr_t* bitmap_begin,
+ size_t bitmap_size,
+ const void* heap_begin,
+ size_t heap_capacity);
template<bool kSetBit>
bool Modify(const mirror::Object* obj);
@@ -211,10 +216,13 @@
// Size of this bitmap.
size_t bitmap_size_;
- // The base address of the heap, which corresponds to the word containing the first bit in the
- // bitmap.
+ // The start address of the memory covered by the bitmap, which corresponds to the word
+ // containing the first bit in the bitmap.
const uintptr_t heap_begin_;
+ // The end address of the memory covered by the bitmap. This may not be on a word boundary.
+ uintptr_t heap_limit_;
+
// Name of this bitmap.
std::string name_;
};
diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc
index 8c06cfd..bd5f77e 100644
--- a/runtime/gc/accounting/space_bitmap_test.cc
+++ b/runtime/gc/accounting/space_bitmap_test.cc
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <memory>
+#include "base/mutex.h"
#include "common_runtime_test.h"
#include "globals.h"
#include "space_bitmap-inl.h"
@@ -145,22 +146,21 @@
explicit RandGen(uint32_t seed) : val_(seed) {}
uint32_t next() {
- val_ = val_ * 48271 % 2147483647;
+ val_ = val_ * 48271 % 2147483647 + 13;
return val_;
}
uint32_t val_;
};
-template <size_t kAlignment>
-void RunTest() NO_THREAD_SAFETY_ANALYSIS {
+template <size_t kAlignment, typename TestFn>
+static void RunTest(TestFn&& fn) NO_THREAD_SAFETY_ANALYSIS {
uint8_t* heap_begin = reinterpret_cast<uint8_t*>(0x10000000);
size_t heap_capacity = 16 * MB;
// Seed with 0x1234 for reproducability.
RandGen r(0x1234);
-
for (int i = 0; i < 5 ; ++i) {
std::unique_ptr<ContinuousSpaceBitmap> space_bitmap(
ContinuousSpaceBitmap::Create("test bitmap", heap_begin, heap_capacity));
@@ -177,15 +177,9 @@
}
for (int j = 0; j < 50; ++j) {
- size_t count = 0;
- SimpleCounter c(&count);
-
- size_t offset = RoundDown(r.next() % heap_capacity, kAlignment);
- size_t remain = heap_capacity - offset;
- size_t end = offset + RoundDown(r.next() % (remain + 1), kAlignment);
-
- space_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(heap_begin) + offset,
- reinterpret_cast<uintptr_t>(heap_begin) + end, c);
+ const size_t offset = RoundDown(r.next() % heap_capacity, kAlignment);
+ const size_t remain = heap_capacity - offset;
+ const size_t end = offset + RoundDown(r.next() % (remain + 1), kAlignment);
size_t manual = 0;
for (uintptr_t k = offset; k < end; k += kAlignment) {
@@ -194,17 +188,73 @@
}
}
- EXPECT_EQ(count, manual);
+ uintptr_t range_begin = reinterpret_cast<uintptr_t>(heap_begin) + offset;
+ uintptr_t range_end = reinterpret_cast<uintptr_t>(heap_begin) + end;
+
+ fn(space_bitmap.get(), range_begin, range_end, manual);
}
}
}
+template <size_t kAlignment>
+static void RunTestCount() {
+ auto count_test_fn = [](ContinuousSpaceBitmap* space_bitmap,
+ uintptr_t range_begin,
+ uintptr_t range_end,
+ size_t manual_count) {
+ size_t count = 0;
+ auto count_fn = [&count](mirror::Object* obj ATTRIBUTE_UNUSED) {
+ count++;
+ };
+ space_bitmap->VisitMarkedRange(range_begin, range_end, count_fn);
+ EXPECT_EQ(count, manual_count);
+ };
+ RunTest<kAlignment>(count_test_fn);
+}
+
TEST_F(SpaceBitmapTest, VisitorObjectAlignment) {
- RunTest<kObjectAlignment>();
+ RunTestCount<kObjectAlignment>();
}
TEST_F(SpaceBitmapTest, VisitorPageAlignment) {
- RunTest<kPageSize>();
+ RunTestCount<kPageSize>();
+}
+
+template <size_t kAlignment>
+void RunTestOrder() {
+ auto order_test_fn = [](ContinuousSpaceBitmap* space_bitmap,
+ uintptr_t range_begin,
+ uintptr_t range_end,
+ size_t manual_count)
+ REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
+ mirror::Object* last_ptr = nullptr;
+ auto order_check = [&last_ptr](mirror::Object* obj) {
+ EXPECT_LT(last_ptr, obj);
+ last_ptr = obj;
+ };
+
+ // Test complete walk.
+ space_bitmap->Walk(order_check);
+ if (manual_count > 0) {
+ EXPECT_NE(nullptr, last_ptr);
+ }
+
+ // Test range.
+ last_ptr = nullptr;
+ space_bitmap->VisitMarkedRange(range_begin, range_end, order_check);
+ if (manual_count > 0) {
+ EXPECT_NE(nullptr, last_ptr);
+ }
+ };
+ RunTest<kAlignment>(order_test_fn);
+}
+
+TEST_F(SpaceBitmapTest, OrderObjectAlignment) {
+ RunTestOrder<kObjectAlignment>();
+}
+
+TEST_F(SpaceBitmapTest, OrderPageAlignment) {
+ RunTestOrder<kPageSize>();
}
} // namespace accounting
diff --git a/runtime/gc/allocation_listener.h b/runtime/gc/allocation_listener.h
index 21fa214..0be9aec 100644
--- a/runtime/gc/allocation_listener.h
+++ b/runtime/gc/allocation_listener.h
@@ -22,8 +22,8 @@
#include "base/macros.h"
#include "base/mutex.h"
-#include "obj_ptr.h"
#include "gc_root.h"
+#include "obj_ptr.h"
namespace art {
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
index d31e442..fcd08c1 100644
--- a/runtime/gc/allocation_record.h
+++ b/runtime/gc/allocation_record.h
@@ -21,8 +21,8 @@
#include <memory>
#include "base/mutex.h"
-#include "obj_ptr.h"
#include "gc_root.h"
+#include "obj_ptr.h"
namespace art {
diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc
index 0c84224..ef916f8 100644
--- a/runtime/gc/allocator/dlmalloc.cc
+++ b/runtime/gc/allocator/dlmalloc.cc
@@ -55,9 +55,10 @@
<< " not expected";
}
+#include <sys/mman.h>
+
#include "globals.h"
#include "utils.h"
-#include <sys/mman.h>
extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) {
// Is this chunk in use?
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index d5d3540..b742ac4 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -16,8 +16,8 @@
#include "rosalloc.h"
-#include <map>
#include <list>
+#include <map>
#include <sstream>
#include <vector>
@@ -28,8 +28,8 @@
#include "gc/space/memory_tool_settings.h"
#include "mem_map.h"
#include "mirror/class-inl.h"
-#include "mirror/object.h"
#include "mirror/object-inl.h"
+#include "mirror/object.h"
#include "thread-current-inl.h"
#include "thread_list.h"
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index b85d7df..2c90773 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -28,8 +28,8 @@
#include "base/allocator.h"
#include "base/bit_utils.h"
-#include "base/mutex.h"
#include "base/logging.h"
+#include "base/mutex.h"
#include "globals.h"
#include "thread.h"
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 9d672b1..52b355d 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2431,24 +2431,31 @@
// Non-immune non-moving space. Use the mark bitmap.
accounting::ContinuousSpaceBitmap* mark_bitmap =
heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
bool is_los = mark_bitmap == nullptr;
if (!is_los && mark_bitmap->Test(from_ref)) {
// Already marked.
to_ref = from_ref;
- } else if (is_los && los_bitmap->Test(from_ref)) {
- // Already marked in LOS.
- to_ref = from_ref;
} else {
- // Not marked.
- if (IsOnAllocStack(from_ref)) {
- // If on the allocation stack, it's considered marked.
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
+ // We may not have a large object space for dex2oat, don't assume it exists.
+ if (los_bitmap == nullptr) {
+ CHECK(heap_->GetLargeObjectsSpace() == nullptr)
+ << "LOS bitmap covers the entire address range " << from_ref
+ << " " << heap_->DumpSpaces();
+ }
+ if (los_bitmap != nullptr && is_los && los_bitmap->Test(from_ref)) {
+ // Already marked in LOS.
to_ref = from_ref;
} else {
// Not marked.
- to_ref = nullptr;
+ if (IsOnAllocStack(from_ref)) {
+ // If on the allocation stack, it's considered marked.
+ to_ref = from_ref;
+ } else {
+ // Not marked.
+ to_ref = nullptr;
+ }
}
}
}
@@ -2457,6 +2464,7 @@
}
bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) {
+ // TODO: Explain why this is here. What release operation does it pair with?
QuasiAtomic::ThreadFenceAcquire();
accounting::ObjectStack* alloc_stack = GetAllocationStack();
return alloc_stack->Contains(ref);
@@ -2617,9 +2625,8 @@
}
} while (!field->CasWeakRelaxed(from_ref, to_ref));
} else {
- QuasiAtomic::ThreadFenceRelease();
- field->Assign(to_ref);
- QuasiAtomic::ThreadFenceSequentiallyConsistent();
+ // TODO: Why is this seq_cst when the above is relaxed? Document memory ordering.
+ field->Assign</* kIsVolatile */ true>(to_ref);
}
}
return true;
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index ab60990..cc7072d 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -21,8 +21,8 @@
#include "garbage_collector.h"
#include "immune_spaces.h"
#include "jni.h"
-#include "offsets.h"
#include "mirror/object_reference.h"
+#include "offsets.h"
#include "safe_map.h"
#include <unordered_map>
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index dec206b..f722e8d 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -113,7 +113,7 @@
virtual mirror::Object* IsMarked(mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
// Returns true if the given heap reference is null or is already marked. If it's already marked,
- // update the reference (uses a CAS if do_atomic_update is true. Otherwise, returns false.
+ // update the reference (uses a CAS if do_atomic_update is true). Otherwise, returns false.
virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj,
bool do_atomic_update)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc
index 1e5f283..1024050 100644
--- a/runtime/gc/collector/immune_spaces.cc
+++ b/runtime/gc/collector/immune_spaces.cc
@@ -16,8 +16,8 @@
#include "immune_spaces.h"
-#include <vector>
#include <tuple>
+#include <vector>
#include "gc/space/space-inl.h"
#include "mirror/object.h"
diff --git a/runtime/gc/collector/iteration.h b/runtime/gc/collector/iteration.h
index fbe4166..363459a 100644
--- a/runtime/gc/collector/iteration.h
+++ b/runtime/gc/collector/iteration.h
@@ -22,6 +22,7 @@
#include "android-base/macros.h"
#include "base/timing_logger.h"
+#include "gc/gc_cause.h"
#include "object_byte_pair.h"
namespace art {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 0bf4095..7d64a0c 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -24,8 +24,8 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "garbage_collector.h"
-#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
+#include "gc_root.h"
#include "immune_spaces.h"
#include "lock_word.h"
#include "offsets.h"
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index fb82b4d..34de83a 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -17,9 +17,9 @@
#include "mark_sweep.h"
#include <atomic>
+#include <climits>
#include <functional>
#include <numeric>
-#include <climits>
#include <vector>
#include "base/bounded_fifo.h"
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index b9e06f9..53b899e 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -24,8 +24,8 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "garbage_collector.h"
-#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
+#include "gc_root.h"
#include "immune_spaces.h"
#include "offsets.h"
diff --git a/runtime/gc/collector/semi_space-inl.h b/runtime/gc/collector/semi_space-inl.h
index 78fb2d2..7db5d2c 100644
--- a/runtime/gc/collector/semi_space-inl.h
+++ b/runtime/gc/collector/semi_space-inl.h
@@ -38,9 +38,8 @@
// Used to mark and copy objects. Any newly-marked objects who are in the from space Get moved to
// the to-space and have their forward address updated. Objects which have been newly marked are
// pushed on the mark stack.
-template<bool kPoisonReferences>
-inline void SemiSpace::MarkObject(
- mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr) {
+template<typename CompressedReferenceType>
+inline void SemiSpace::MarkObject(CompressedReferenceType* obj_ptr) {
mirror::Object* obj = obj_ptr->AsMirrorPtr();
if (obj == nullptr) {
return;
@@ -73,9 +72,8 @@
}
}
-template<bool kPoisonReferences>
-inline void SemiSpace::MarkObjectIfNotInToSpace(
- mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr) {
+template<typename CompressedReferenceType>
+inline void SemiSpace::MarkObjectIfNotInToSpace(CompressedReferenceType* obj_ptr) {
if (!to_space_->HasAddress(obj_ptr->AsMirrorPtr())) {
MarkObject(obj_ptr);
}
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index d379892..9fb37b6 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -32,8 +32,8 @@
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/reference_processor.h"
-#include "gc/space/bump_pointer_space.h"
#include "gc/space/bump_pointer_space-inl.h"
+#include "gc/space/bump_pointer_space.h"
#include "gc/space/image_space.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
@@ -41,10 +41,10 @@
#include "intern_table.h"
#include "jni_internal.h"
#include "mark_sweep-inl.h"
-#include "monitor.h"
-#include "mirror/reference-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object-refvisitor-inl.h"
+#include "mirror/reference-inl.h"
+#include "monitor.h"
#include "runtime.h"
#include "thread-inl.h"
#include "thread_list.h"
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index d3858ba..6d4d789 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -23,8 +23,8 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "garbage_collector.h"
-#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
+#include "gc_root.h"
#include "immune_spaces.h"
#include "mirror/object_reference.h"
#include "offsets.h"
@@ -97,13 +97,13 @@
// Find the default mark bitmap.
void FindDefaultMarkBitmap();
- // Updates obj_ptr if the object has moved.
- template<bool kPoisonReferences>
- void MarkObject(mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr)
+ // Updates obj_ptr if the object has moved. Takes either an ObjectReference or a HeapReference.
+ template<typename CompressedReferenceType>
+ void MarkObject(CompressedReferenceType* obj_ptr)
REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
- template<bool kPoisonReferences>
- void MarkObjectIfNotInToSpace(mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr)
+ template<typename CompressedReferenceType>
+ void MarkObjectIfNotInToSpace(CompressedReferenceType* obj_ptr)
REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
virtual mirror::Object* MarkObject(mirror::Object* root) OVERRIDE
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index a3a2051..8712080 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -15,8 +15,8 @@
*/
#include "gc_cause.h"
-#include "globals.h"
#include "base/logging.h"
+#include "globals.h"
#include <ostream>
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index bf5cf29..2047646 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -30,9 +30,9 @@
#include "gc/space/large_object_space.h"
#include "gc/space/region_space-inl.h"
#include "gc/space/rosalloc_space-inl.h"
+#include "handle_scope-inl.h"
#include "obj_ptr-inl.h"
#include "runtime.h"
-#include "handle_scope-inl.h"
#include "thread-inl.h"
#include "utils.h"
#include "verify_object.h"
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index e172d2d..1534fd6 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -27,10 +27,10 @@
#include "atomic.h"
#include "base/mutex.h"
#include "base/time_utils.h"
-#include "gc/gc_cause.h"
#include "gc/collector/gc_type.h"
#include "gc/collector/iteration.h"
#include "gc/collector_type.h"
+#include "gc/gc_cause.h"
#include "gc/space/large_object_space.h"
#include "globals.h"
#include "handle.h"
diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc
index 613b034..ce0807c 100644
--- a/runtime/gc/reference_queue_test.cc
+++ b/runtime/gc/reference_queue_test.cc
@@ -17,9 +17,9 @@
#include <sstream>
#include "common_runtime_test.h"
-#include "reference_queue.h"
#include "handle_scope-inl.h"
#include "mirror/class-inl.h"
+#include "reference_queue.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index 5d91f4b..ce0e0f3 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -16,8 +16,8 @@
#include "bump_pointer_space.h"
#include "bump_pointer_space-inl.h"
-#include "mirror/object-inl.h"
#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
#include "thread_list.h"
namespace art {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 3ae382e..14e017a 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -17,11 +17,12 @@
#include "image_space.h"
#include <lz4.h>
-#include <random>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <unistd.h>
+#include <random>
+
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -30,8 +31,8 @@
#include "base/callee_save_type.h"
#include "base/enums.h"
#include "base/macros.h"
-#include "base/stl_util.h"
#include "base/scoped_flock.h"
+#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "exec_utils.h"
@@ -724,6 +725,10 @@
image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod));
CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything),
image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod));
+ CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit),
+ image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit));
+ CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck),
+ image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck));
} else if (!runtime->HasResolutionMethod()) {
runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet());
runtime->SetResolutionMethod(image_header->GetImageMethod(ImageHeader::kResolutionMethod));
@@ -742,6 +747,12 @@
runtime->SetCalleeSaveMethod(
image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod),
CalleeSaveType::kSaveEverything);
+ runtime->SetCalleeSaveMethod(
+ image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit),
+ CalleeSaveType::kSaveEverythingForClinit);
+ runtime->SetCalleeSaveMethod(
+ image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck),
+ CalleeSaveType::kSaveEverythingForSuspendCheck);
}
VLOG(image) << "ImageSpace::Init exiting " << *space.get();
@@ -1268,17 +1279,19 @@
}
dex_cache->FixupResolvedTypes<kWithoutReadBarrier>(new_types, fixup_adapter);
}
- ArtMethod** methods = dex_cache->GetResolvedMethods();
+ mirror::MethodDexCacheType* methods = dex_cache->GetResolvedMethods();
if (methods != nullptr) {
- ArtMethod** new_methods = fixup_adapter.ForwardObject(methods);
+ mirror::MethodDexCacheType* new_methods = fixup_adapter.ForwardObject(methods);
if (methods != new_methods) {
dex_cache->SetResolvedMethods(new_methods);
}
for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) {
- ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, pointer_size);
+ auto pair = mirror::DexCache::GetNativePairPtrSize(new_methods, j, pointer_size);
+ ArtMethod* orig = pair.object;
ArtMethod* copy = fixup_adapter.ForwardObject(orig);
if (orig != copy) {
- mirror::DexCache::SetElementPtrSize(new_methods, j, copy, pointer_size);
+ pair.object = copy;
+ mirror::DexCache::SetNativePairPtrSize(new_methods, j, pair, pointer_size);
}
}
}
diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc
index 2544914..9baa016 100644
--- a/runtime/gc/space/large_object_space_test.cc
+++ b/runtime/gc/space/large_object_space_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "large_object_space.h"
+
#include "base/time_utils.h"
#include "space_test.h"
-#include "large_object_space.h"
namespace art {
namespace gc {
@@ -37,12 +38,19 @@
Thread* const self = Thread::Current();
for (size_t i = 0; i < 2; ++i) {
LargeObjectSpace* los = nullptr;
+ const size_t capacity = 128 * MB;
if (i == 0) {
los = space::LargeObjectMapSpace::Create("large object space");
} else {
- los = space::FreeListSpace::Create("large object space", nullptr, 128 * MB);
+ los = space::FreeListSpace::Create("large object space", nullptr, capacity);
}
+ // Make sure the bitmap is not empty and actually covers at least how much we expect.
+ CHECK_LT(static_cast<uintptr_t>(los->GetLiveBitmap()->HeapBegin()),
+ static_cast<uintptr_t>(los->GetLiveBitmap()->HeapLimit()));
+ CHECK_LE(static_cast<uintptr_t>(los->GetLiveBitmap()->HeapBegin() + capacity),
+ static_cast<uintptr_t>(los->GetLiveBitmap()->HeapLimit()));
+
static const size_t num_allocations = 64;
static const size_t max_allocation_size = 0x100000;
std::vector<std::pair<mirror::Object*, size_t>> requests;
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index 1154620..c994127 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -23,10 +23,10 @@
#include "gc/heap.h"
#include "gc/space/space-inl.h"
#include "gc/space/zygote_space.h"
+#include "handle_scope-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "runtime.h"
-#include "handle_scope-inl.h"
#include "thread.h"
#include "thread_list.h"
#include "utils.h"
@@ -191,14 +191,10 @@
VLOG(heap) << "Size " << GetMemMap()->Size();
VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit);
VLOG(heap) << "Capacity " << PrettySize(capacity);
- // Remap the tail. Pass MAP_PRIVATE since we don't want to share the same ashmem as the zygote
- // space.
+ // Remap the tail.
std::string error_msg;
- std::unique_ptr<MemMap> mem_map(GetMemMap()->RemapAtEnd(End(),
- alloc_space_name,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
- &error_msg));
+ std::unique_ptr<MemMap> mem_map(GetMemMap()->RemapAtEnd(End(), alloc_space_name,
+ PROT_READ | PROT_WRITE, &error_msg));
CHECK(mem_map.get() != nullptr) << error_msg;
void* allocator = CreateAllocator(End(), starting_size_, initial_size_, capacity,
low_memory_mode);
diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h
index 6cb2465..8282f3d 100644
--- a/runtime/gc/space/memory_tool_malloc_space-inl.h
+++ b/runtime/gc/space/memory_tool_malloc_space-inl.h
@@ -17,9 +17,11 @@
#ifndef ART_RUNTIME_GC_SPACE_MEMORY_TOOL_MALLOC_SPACE_INL_H_
#define ART_RUNTIME_GC_SPACE_MEMORY_TOOL_MALLOC_SPACE_INL_H_
-#include "base/memory_tool.h"
#include "memory_tool_malloc_space.h"
+
+#include "base/memory_tool.h"
#include "memory_tool_settings.h"
+#include "mirror/object-inl.h"
namespace art {
namespace gc {
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index fe3c1c0..b2e1fa5 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#include "bump_pointer_space.h"
#include "bump_pointer_space-inl.h"
+#include "bump_pointer_space.h"
#include "gc/accounting/read_barrier_table.h"
-#include "mirror/object-inl.h"
#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
#include "thread_list.h"
namespace art {
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 9e900e4..eca0e43 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -21,6 +21,7 @@
#include "gc/accounting/card_table.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
+#include "memory_tool_malloc_space-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "runtime.h"
@@ -28,7 +29,6 @@
#include "thread.h"
#include "thread_list.h"
#include "utils.h"
-#include "memory_tool_malloc_space-inl.h"
namespace art {
namespace gc {
diff --git a/runtime/gc/task_processor_test.cc b/runtime/gc/task_processor_test.cc
index 5a75b37..77b40e4 100644
--- a/runtime/gc/task_processor_test.cc
+++ b/runtime/gc/task_processor_test.cc
@@ -14,11 +14,11 @@
* limitations under the License.
*/
+#include "task_processor.h"
#include "base/time_utils.h"
#include "common_runtime_test.h"
-#include "task_processor.h"
-#include "thread_pool.h"
#include "thread-current-inl.h"
+#include "thread_pool.h"
namespace art {
namespace gc {
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 06e4704..314c45e 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -34,6 +34,10 @@
DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveRefsAndArgs))))
#define RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET 0x18
DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverything))))
+#define RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET 0x20
+DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverythingForClinit))))
+#define RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET 0x28
+DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverythingForSuspendCheck))))
#define THREAD_FLAGS_OFFSET 0
DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_FLAGS_OFFSET), (static_cast<int32_t>(art::Thread:: ThreadFlagsOffset<art::kRuntimePointerSize>().Int32Value())))
#define THREAD_ID_OFFSET 12
@@ -78,6 +82,10 @@
DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_HASH_BITS), (static_cast<int32_t>(art::LeastSignificantBit(art::mirror::DexCache::kDexCacheStringCacheSize))))
#define STRING_DEX_CACHE_ELEMENT_SIZE 8
DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE), (static_cast<int32_t>(sizeof(art::mirror::StringDexCachePair))))
+#define METHOD_DEX_CACHE_SIZE_MINUS_ONE 1023
+DEFINE_CHECK_EQ(static_cast<int32_t>(METHOD_DEX_CACHE_SIZE_MINUS_ONE), (static_cast<int32_t>(art::mirror::DexCache::kDexCacheMethodCacheSize - 1)))
+#define METHOD_DEX_CACHE_HASH_BITS 10
+DEFINE_CHECK_EQ(static_cast<int32_t>(METHOD_DEX_CACHE_HASH_BITS), (static_cast<int32_t>(art::LeastSignificantBit(art::mirror::DexCache::kDexCacheMethodCacheSize))))
#define CARD_TABLE_CARD_SHIFT 0xa
DEFINE_CHECK_EQ(static_cast<size_t>(CARD_TABLE_CARD_SHIFT), (static_cast<size_t>(art::gc::accounting::CardTable::kCardShift)))
#define MIN_LARGE_OBJECT_THRESHOLD 0x3000
@@ -110,6 +118,8 @@
DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_MARK_BIT_SHIFT), (static_cast<int32_t>(art::LockWord::kMarkBitStateShift)))
#define LOCK_WORD_MARK_BIT_MASK_SHIFTED 0x20000000
DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_MARK_BIT_MASK_SHIFTED), (static_cast<uint32_t>(art::LockWord::kMarkBitStateMaskShifted)))
+#define STD_MEMORY_ORDER_RELAXED 0
+DEFINE_CHECK_EQ(static_cast<int32_t>(STD_MEMORY_ORDER_RELAXED), (static_cast<int32_t>(std::memory_order_relaxed)))
#define OBJECT_ALIGNMENT_MASK 0x7
DEFINE_CHECK_EQ(static_cast<size_t>(OBJECT_ALIGNMENT_MASK), (static_cast<size_t>(art::kObjectAlignment - 1)))
#define OBJECT_ALIGNMENT_MASK_TOGGLED 0xfffffff8
diff --git a/runtime/globals.h b/runtime/globals.h
index 6164225..256306d 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -51,11 +51,17 @@
static constexpr size_t kObjectAlignment = 1u << kObjectAlignmentShift;
static constexpr size_t kLargeObjectAlignment = kPageSize;
+// Clion, clang analyzer, etc can falsely believe that "if (kIsDebugBuild)" always
+// returns the same value. By wrapping into a call to another constexpr function, we force it
+// to realize that is not actually always evaluating to the same value.
+static constexpr bool GlobalsReturnSelf(bool self) { return self; }
+
// Whether or not this is a debug build. Useful in conditionals where NDEBUG isn't.
-#if defined(NDEBUG)
-static constexpr bool kIsDebugBuild = false;
+// TODO: Use only __clang_analyzer__ here. b/64455231
+#if defined(NDEBUG) && !defined(__CLION_IDE__)
+static constexpr bool kIsDebugBuild = GlobalsReturnSelf(false);
#else
-static constexpr bool kIsDebugBuild = true;
+static constexpr bool kIsDebugBuild = GlobalsReturnSelf(true);
#endif
// ART_TARGET - Defined for target builds of ART.
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index f428bc2..7976a1a 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -32,8 +32,8 @@
#include <sys/time.h>
#include <sys/uio.h>
#include <time.h>
-#include <time.h>
#include <unistd.h>
+
#include <set>
#include "android-base/stringprintf.h"
@@ -47,18 +47,18 @@
#include "common_throws.h"
#include "debugger.h"
#include "dex_file-inl.h"
-#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc/allocation_record.h"
-#include "gc/scoped_gc_critical_section.h"
-#include "gc/heap.h"
#include "gc/heap-visit-objects-inl.h"
+#include "gc/heap.h"
+#include "gc/scoped_gc_critical_section.h"
#include "gc/space/space.h"
+#include "gc_root.h"
#include "globals.h"
#include "jdwp/jdwp.h"
#include "jdwp/jdwp_priv.h"
-#include "mirror/class.h"
#include "mirror/class-inl.h"
+#include "mirror/class.h"
#include "mirror/object-refvisitor-inl.h"
#include "os.h"
#include "safe_map.h"
diff --git a/runtime/image.cc b/runtime/image.cc
index ac36d7c..950ac5d 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -18,15 +18,15 @@
#include "base/bit_utils.h"
#include "base/length_prefixed_array.h"
-#include "mirror/object_array.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object_array.h"
#include "utils.h"
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '5', '\0' }; // Fix DexCache fields.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '6', '\0' }; // Hash-based methods array.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index 6c76f49..7bb796c 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -183,6 +183,8 @@
kSaveRefsOnlyMethod,
kSaveRefsAndArgsMethod,
kSaveEverythingMethod,
+ kSaveEverythingMethodForClinit,
+ kSaveEverythingMethodForSuspendCheck,
kImageMethodsCount, // Number of elements in enum.
};
diff --git a/runtime/imtable_test.cc b/runtime/imtable_test.cc
index d482183..d662114 100644
--- a/runtime/imtable_test.cc
+++ b/runtime/imtable_test.cc
@@ -24,10 +24,10 @@
#include "base/mutex.h"
#include "class_linker.h"
#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
#include "mirror/accessible_object.h"
#include "mirror/class.h"
#include "mirror/class_loader.h"
-#include "handle_scope-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
diff --git a/runtime/indenter.h b/runtime/indenter.h
index 78b18f6..cc6d4c4 100644
--- a/runtime/indenter.h
+++ b/runtime/indenter.h
@@ -17,11 +17,12 @@
#ifndef ART_RUNTIME_INDENTER_H_
#define ART_RUNTIME_INDENTER_H_
-#include "base/logging.h"
-#include "base/macros.h"
#include <ostream>
#include <streambuf>
+#include "base/logging.h"
+#include "base/macros.h"
+
namespace art {
constexpr char kIndentChar =' ';
diff --git a/runtime/indenter_test.cc b/runtime/indenter_test.cc
index 1a26d7b..09c0c54 100644
--- a/runtime/indenter_test.cc
+++ b/runtime/indenter_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "gtest/gtest.h"
#include "indenter.h"
+#include "gtest/gtest.h"
+
namespace art {
TEST(IndenterTest, MultiLineTest) {
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 8120cc4..a8cf59b 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -19,15 +19,15 @@
#include <sstream>
#include "arch/context.h"
-#include "art_method-inl.h"
#include "art_field-inl.h"
+#include "art_method-inl.h"
#include "atomic.h"
#include "base/callee_save_type.h"
#include "class_linker.h"
#include "debugger.h"
#include "dex_file-inl.h"
-#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc_root-inl.h"
#include "interpreter/interpreter.h"
@@ -36,8 +36,8 @@
#include "jvalue-inl.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
#include "nth_caller_visitor.h"
#include "oat_quick_method_header.h"
#include "thread.h"
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 90b5def..9969489 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -196,6 +196,10 @@
}
bool ShouldNotifyMethodEnterExitEvents() const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool CanDeoptimize() {
+ return deoptimization_enabled_;
+ }
+
// Executes everything with interpreter.
void DeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
@@ -457,8 +461,7 @@
// This is used by the debugger to cause a deoptimization of the thread's stack after updating
// local variable(s).
void InstrumentThreadStack(Thread* thread)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::thread_list_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_);
static size_t ComputeFrameId(Thread* self,
size_t frame_depth,
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 9e9fa71..d25655f 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -18,9 +18,9 @@
#include "art_method-inl.h"
#include "base/enums.h"
+#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "common_throws.h"
-#include "class_linker-inl.h"
#include "dex_file.h"
#include "gc/scoped_gc_critical_section.h"
#include "handle_scope-inl.h"
@@ -28,8 +28,8 @@
#include "jvalue.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
-#include "thread_list.h"
#include "thread-inl.h"
+#include "thread_list.h"
#include "well_known_classes.h"
namespace art {
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 2bac231..f4da5a4 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -18,14 +18,14 @@
#include <memory>
-#include "gc_root-inl.h"
#include "gc/collector/garbage_collector.h"
#include "gc/space/image_space.h"
#include "gc/weak_root_state.h"
+#include "gc_root-inl.h"
#include "image-inl.h"
#include "mirror/dex_cache-inl.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
#include "object_callbacks.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 2ec03be..8714840 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -23,8 +23,8 @@
#include "base/allocator.h"
#include "base/hash_set.h"
#include "base/mutex.h"
-#include "gc_root.h"
#include "gc/weak_root_state.h"
+#include "gc_root.h"
namespace art {
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index bb27b34..9c3ea8d 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -19,8 +19,8 @@
#include "base/hash_set.h"
#include "common_runtime_test.h"
#include "gc_root-inl.h"
-#include "mirror/object.h"
#include "handle_scope-inl.h"
+#include "mirror/object.h"
#include "mirror/string.h"
#include "scoped_thread_state_change-inl.h"
#include "utf.h"
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index be2d34d..f8cb243 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -23,16 +23,17 @@
#include "entrypoints/runtime_asm_entrypoints.h"
#include "jit/jit.h"
#include "jvalue.h"
-#include "method_handles.h"
#include "method_handles-inl.h"
+#include "method_handles.h"
#include "mirror/array-inl.h"
#include "mirror/class.h"
#include "mirror/emulated_stack_frame.h"
#include "mirror/method_handle_impl-inl.h"
-#include "reflection.h"
#include "reflection-inl.h"
+#include "reflection.h"
#include "stack.h"
#include "thread-inl.h"
+#include "transaction.h"
#include "well_known_classes.h"
namespace art {
@@ -42,7 +43,8 @@
ThrowNullPointerExceptionFromDexPC();
}
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
+ bool transaction_active>
bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
uint16_t inst_data) {
const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
@@ -57,6 +59,13 @@
ObjPtr<mirror::Object> obj;
if (is_static) {
obj = f->GetDeclaringClass();
+ if (transaction_active) {
+ if (Runtime::Current()->GetTransaction()->ReadConstraint(obj.Ptr(), f)) {
+ Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Can't read static fields of "
+ + obj->PrettyTypeOf() + " since it does not belong to clinit's class.");
+ return false;
+ }
+ }
} else {
obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
if (UNLIKELY(obj == nullptr)) {
@@ -102,15 +111,17 @@
}
// Explicitly instantiate all DoFieldGet functions.
-#define EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, _do_check) \
- template bool DoFieldGet<_find_type, _field_type, _do_check>(Thread* self, \
+#define EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, _do_check, _transaction_active) \
+ template bool DoFieldGet<_find_type, _field_type, _do_check, _transaction_active>(Thread* self, \
ShadowFrame& shadow_frame, \
const Instruction* inst, \
uint16_t inst_data)
#define EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(_find_type, _field_type) \
- EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, false); \
- EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, true);
+ EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, false, true); \
+ EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, false, false); \
+ EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, true, true); \
+ EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, true, false);
// iget-XXX
EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimBoolean)
@@ -261,6 +272,14 @@
ObjPtr<mirror::Object> obj;
if (is_static) {
obj = f->GetDeclaringClass();
+ if (transaction_active) {
+ if (Runtime::Current()->GetTransaction()->WriteConstraint(obj.Ptr(), f)) {
+ Runtime::Current()->AbortTransactionAndThrowAbortError(
+ self, "Can't set fields of " + obj->PrettyTypeOf());
+ return false;
+ }
+ }
+
} else {
obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
if (UNLIKELY(obj == nullptr)) {
@@ -1070,7 +1089,7 @@
// Preserve o since it is used below and GetClassFromTypeIndex may cause thread
// suspension.
HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o);
- arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */);
+ arg_type = method->ResolveClassFromTypeIndex(type_idx);
if (arg_type == nullptr) {
CHECK(self->IsExceptionPending());
return false;
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 74fec48..5b942f2 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -22,9 +22,9 @@
#include <math.h>
+#include <atomic>
#include <iostream>
#include <sstream>
-#include <atomic>
#include "android-base/stringprintf.h"
@@ -141,11 +141,8 @@
return false;
} else {
jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr) {
- if (type == kVirtual) {
- jit->InvokeVirtualOrInterface(receiver, sf_method, shadow_frame.GetDexPC(), called_method);
- }
- jit->AddSamples(self, sf_method, 1, /*with_backedges*/false);
+ if (jit != nullptr && type == kVirtual) {
+ jit->InvokeVirtualOrInterface(receiver, sf_method, shadow_frame.GetDexPC(), called_method);
}
if (called_method->IsIntrinsic()) {
if (MterpHandleIntrinsic(&shadow_frame, called_method, inst, inst_data,
@@ -182,11 +179,8 @@
return false;
} else {
jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr) {
- if (type == kVirtual || type == kInterface) {
- jit->InvokeVirtualOrInterface(receiver, sf_method, shadow_frame.GetDexPC(), called_method);
- }
- jit->AddSamples(self, sf_method, 1, /*with_backedges*/false);
+ if (jit != nullptr && (type == kVirtual || type == kInterface)) {
+ jit->InvokeVirtualOrInterface(receiver, sf_method, shadow_frame.GetDexPC(), called_method);
}
// TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
if (type == kVirtual || type == kInterface) {
@@ -270,7 +264,8 @@
// Handles iget-XXX and sget-XXX instructions.
// Returns true on success, otherwise throws an exception and returns false.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
+ bool transaction_active = false>
bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
uint16_t inst_data) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 0a2705d..0c5a45f 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -349,7 +349,7 @@
const size_t ref_idx = inst->VRegA_11x(inst_data);
ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
if (do_assignability_check && obj_result != nullptr) {
- ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */);
+ ObjPtr<mirror::Class> return_type = method->ResolveReturnType();
// Re-load since it might have moved.
obj_result = shadow_frame.GetVRegReference(ref_idx);
if (return_type == nullptr) {
@@ -1313,50 +1313,50 @@
}
case Instruction::SGET_BOOLEAN: {
PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(
- self, shadow_frame, inst, inst_data);
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SGET_BYTE: {
PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(
- self, shadow_frame, inst, inst_data);
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SGET_CHAR: {
PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(
- self, shadow_frame, inst, inst_data);
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SGET_SHORT: {
PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(
- self, shadow_frame, inst, inst_data);
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SGET: {
PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(
- self, shadow_frame, inst, inst_data);
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SGET_WIDE: {
PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(
- self, shadow_frame, inst, inst_data);
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
case Instruction::SGET_OBJECT: {
PREAMBLE();
- bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(
- self, shadow_frame, inst, inst_data);
+ bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 5f94d04..88254a8 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -17,11 +17,11 @@
/*
* Mterp entry point and support functions.
*/
-#include "interpreter/interpreter_common.h"
-#include "interpreter/interpreter_intrinsics.h"
-#include "entrypoints/entrypoint_utils-inl.h"
#include "mterp.h"
#include "debugger.h"
+#include "entrypoints/entrypoint_utils-inl.h"
+#include "interpreter/interpreter_common.h"
+#include "interpreter/interpreter_intrinsics.h"
namespace art {
namespace interpreter {
@@ -280,7 +280,6 @@
if (jit != nullptr) {
jit->InvokeVirtualOrInterface(
receiver, shadow_frame->GetMethod(), shadow_frame->GetDexPC(), called_method);
- jit->AddSamples(self, shadow_frame->GetMethod(), 1, /*with_backedges*/false);
}
return !self->IsExceptionPending();
}
diff --git a/runtime/interpreter/mterp/mterp.h b/runtime/interpreter/mterp/mterp.h
index 45ab98b..1a56d26 100644
--- a/runtime/interpreter/mterp/mterp.h
+++ b/runtime/interpreter/mterp/mterp.h
@@ -17,6 +17,9 @@
#ifndef ART_RUNTIME_INTERPRETER_MTERP_MTERP_H_
#define ART_RUNTIME_INTERPRETER_MTERP_MTERP_H_
+#include <cstddef>
+#include <cstdint>
+
/*
* Mterp assembly handler bases
*/
@@ -26,6 +29,9 @@
extern "C" void* artMterpAsmAltInstructionEnd[];
namespace art {
+
+class Thread;
+
namespace interpreter {
void InitMterpTls(Thread* self);
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 69b2382..05768cd 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_INTERPRETER_SHADOW_FRAME_H_
#define ART_RUNTIME_INTERPRETER_SHADOW_FRAME_H_
+#include <cstdint>
#include <cstring>
-#include <stdint.h>
#include <string>
#include "base/macros.h"
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 2c72821..ce06a03 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1438,7 +1438,11 @@
mirror::HeapReference<mirror::Object>* field_addr =
reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
reinterpret_cast<uint8_t*>(obj) + static_cast<size_t>(offset));
- ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, /* kAlwaysUpdateField */ true>(
+ ReadBarrier::Barrier<
+ mirror::Object,
+ /* kIsVolatile */ false,
+ kWithReadBarrier,
+ /* kAlwaysUpdateField */ true>(
obj,
MemberOffset(offset),
field_addr);
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 3461a65..71ab01e 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -29,8 +29,8 @@
#include "handle_scope-inl.h"
#include "interpreter/interpreter_common.h"
#include "mirror/class_loader.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
@@ -780,44 +780,40 @@
{
JValue result;
tmp->SetVReg(0, static_cast<int32_t>(i));
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+ ASSERT_TRUE(Runtime::Current()->IsTransactionAborted());
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(self->IsExceptionPending());
- ASSERT_TRUE(transaction.IsAborted());
}
{
JValue result;
tmp->SetVReg(0, static_cast<int32_t>(i));
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
UnstartedCharacterToUpperCase(self, tmp, &result, 0);
+ ASSERT_TRUE(Runtime::Current()->IsTransactionAborted());
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(self->IsExceptionPending());
- ASSERT_TRUE(transaction.IsAborted());
}
}
for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) {
{
JValue result;
tmp->SetVReg(0, static_cast<int32_t>(i));
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+ ASSERT_TRUE(Runtime::Current()->IsTransactionAborted());
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(self->IsExceptionPending());
- ASSERT_TRUE(transaction.IsAborted());
}
{
JValue result;
tmp->SetVReg(0, static_cast<int32_t>(i));
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
UnstartedCharacterToUpperCase(self, tmp, &result, 0);
+ ASSERT_TRUE(Runtime::Current()->IsTransactionAborted());
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(self->IsExceptionPending());
- ASSERT_TRUE(transaction.IsAborted());
}
}
@@ -996,12 +992,11 @@
ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0);
shadow_frame->SetLink(caller_frame);
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
UnstartedThreadLocalGet(self, shadow_frame, &result, 0);
+ ASSERT_TRUE(Runtime::Current()->IsTransactionAborted());
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(self->IsExceptionPending());
- ASSERT_TRUE(transaction.IsAborted());
self->ClearException();
ShadowFrame::DeleteDeoptimizedFrame(caller_frame);
@@ -1066,12 +1061,11 @@
PrepareForAborts();
{
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
UnstartedThreadCurrentThread(self, shadow_frame, &result, 0);
+ ASSERT_TRUE(Runtime::Current()->IsTransactionAborted());
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(self->IsExceptionPending());
- ASSERT_TRUE(transaction.IsAborted());
self->ClearException();
}
@@ -1138,28 +1132,27 @@
mirror::String* name_string = mirror::String::AllocFromModifiedUtf8(self, name);
CHECK(name_string != nullptr);
- Transaction transaction;
if (in_transaction) {
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
}
CHECK(!self->IsExceptionPending());
runner(self, shadow_frame, name_string, &result);
- if (in_transaction) {
- Runtime::Current()->ExitTransactionMode();
- }
-
if (should_succeed) {
CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump();
CHECK(result.GetL() != nullptr) << name;
} else {
CHECK(self->IsExceptionPending()) << name;
if (in_transaction) {
- ASSERT_TRUE(transaction.IsAborted());
+ ASSERT_TRUE(Runtime::Current()->IsTransactionAborted());
}
self->ClearException();
}
+
+ if (in_transaction) {
+ Runtime::Current()->ExitTransactionMode();
+ }
}
ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 27501b9..47ace7f 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -47,13 +47,9 @@
static constexpr int kProtAll = PROT_READ | PROT_WRITE | PROT_EXEC;
static constexpr int kProtData = PROT_READ | PROT_WRITE;
static constexpr int kProtCode = PROT_READ | PROT_EXEC;
-static constexpr int kProtReadOnly = PROT_READ;
-static constexpr int kProtNone = PROT_NONE;
static constexpr size_t kCodeSizeLogThreshold = 50 * KB;
static constexpr size_t kStackMapSizeLogThreshold = 50 * KB;
-static constexpr size_t kMinMapSpacingPages = 1;
-static constexpr size_t kMaxMapSpacingPages = 128;
#define CHECKED_MPROTECT(memory, size, prot) \
do { \
@@ -64,39 +60,12 @@
} \
} while (false) \
-static MemMap* SplitMemMap(MemMap* existing_map,
- const char* name,
- size_t split_offset,
- int split_prot,
- std::string* error_msg,
- bool use_ashmem,
- unique_fd* shmem_fd = nullptr) {
- std::string error_str;
- uint8_t* divider = existing_map->Begin() + split_offset;
- MemMap* new_map = existing_map->RemapAtEnd(divider,
- name,
- split_prot,
- MAP_SHARED,
- &error_str,
- use_ashmem,
- shmem_fd);
- if (new_map == nullptr) {
- std::ostringstream oss;
- oss << "Failed to create spacing for " << name << ": "
- << error_str << " offset=" << split_offset;
- *error_msg = oss.str();
- return nullptr;
- }
- return new_map;
-}
-
JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
size_t max_capacity,
bool generate_debug_info,
std::string* error_msg) {
ScopedTrace trace(__PRETTY_FUNCTION__);
- CHECK_GT(max_capacity, initial_capacity);
- CHECK_GE(max_capacity - kMaxMapSpacingPages * kPageSize, initial_capacity);
+ CHECK_GE(max_capacity, initial_capacity);
// Generating debug information is for using the Linux perf tool on
// host which does not work with ashmem.
@@ -106,10 +75,6 @@
// With 'perf', we want a 1-1 mapping between an address and a method.
bool garbage_collect_code = !generate_debug_info;
- // We only use two mappings (separating rw from rx) if we are able to use ashmem.
- // See the above comment for debug information and not using ashmem.
- bool use_two_mappings = use_ashmem;
-
// We need to have 32 bit offsets from method headers in code cache which point to things
// in the data cache. If the maps are more than 4G apart, having multiple maps wouldn't work.
// Ensure we're below 1 GB to be safe.
@@ -146,114 +111,30 @@
initial_capacity = RoundDown(initial_capacity, 2 * kPageSize);
max_capacity = RoundDown(max_capacity, 2 * kPageSize);
- // Create a region for JIT data and executable code. This will be
- // laid out as:
- //
- // +----------------+ --------------------
- // : : ^ ^
- // : post_code_map : | post_code_size |
- // : [padding] : v |
- // +----------------+ - |
- // | | ^ |
- // | code_map | | code_size |
- // | [JIT Code] | v |
- // +----------------+ - | total_mapping_size
- // : : ^ |
- // : pre_code_map : | pre_code_size |
- // : [padding] : v |
- // +----------------+ - |
- // | | ^ |
- // | data_map | | data_size |
- // | [Jit Data] | v v
- // +----------------+ --------------------
- //
- // The padding regions - pre_code_map and post_code_map - exist to
- // put some random distance between the writable JIT code mapping
- // and the executable mapping. The padding is discarded at the end
- // of this function.
- size_t total_mapping_size = kMaxMapSpacingPages * kPageSize;
- size_t data_size = RoundUp((max_capacity - total_mapping_size) / 2, kPageSize);
- size_t pre_code_size =
- GetRandomNumber(kMinMapSpacingPages, kMaxMapSpacingPages) * kPageSize;
- size_t code_size = max_capacity - total_mapping_size - data_size;
- size_t post_code_size = total_mapping_size - pre_code_size;
- DCHECK_EQ(code_size + data_size + total_mapping_size, max_capacity);
+ // Data cache is 1 / 2 of the map.
+ // TODO: Make this variable?
+ size_t data_size = max_capacity / 2;
+ size_t code_size = max_capacity - data_size;
+ DCHECK_EQ(code_size + data_size, max_capacity);
+ uint8_t* divider = data_map->Begin() + data_size;
- // Create pre-code padding region after data region, discarded after
- // code and data regions are set-up.
- std::unique_ptr<MemMap> pre_code_map(SplitMemMap(data_map.get(),
- "jit-code-cache-padding",
- data_size,
- kProtNone,
- error_msg,
- use_ashmem));
- if (pre_code_map == nullptr) {
- return nullptr;
- }
- DCHECK_EQ(data_map->Size(), data_size);
- DCHECK_EQ(pre_code_map->Size(), pre_code_size + code_size + post_code_size);
-
- // Create code region.
- unique_fd writable_code_fd;
- std::unique_ptr<MemMap> code_map(SplitMemMap(pre_code_map.get(),
- "jit-code-cache",
- pre_code_size,
- use_two_mappings ? kProtCode : kProtAll,
- error_msg,
- use_ashmem,
- &writable_code_fd));
+ MemMap* code_map =
+ data_map->RemapAtEnd(divider, "jit-code-cache", kProtAll, &error_str, use_ashmem);
if (code_map == nullptr) {
+ std::ostringstream oss;
+ oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
+ *error_msg = oss.str();
return nullptr;
}
- DCHECK_EQ(pre_code_map->Size(), pre_code_size);
- DCHECK_EQ(code_map->Size(), code_size + post_code_size);
-
- // Padding after code region, discarded after code and data regions
- // are set-up.
- std::unique_ptr<MemMap> post_code_map(SplitMemMap(code_map.get(),
- "jit-code-cache-padding",
- code_size,
- kProtNone,
- error_msg,
- use_ashmem));
- if (post_code_map == nullptr) {
- return nullptr;
- }
- DCHECK_EQ(code_map->Size(), code_size);
- DCHECK_EQ(post_code_map->Size(), post_code_size);
-
- std::unique_ptr<MemMap> writable_code_map;
- if (use_two_mappings) {
- // Allocate the R/W view.
- writable_code_map.reset(MemMap::MapFile(code_size,
- kProtData,
- MAP_SHARED,
- writable_code_fd.get(),
- /* start */ 0,
- /* low_4gb */ true,
- "jit-writable-code",
- &error_str));
- if (writable_code_map == nullptr) {
- std::ostringstream oss;
- oss << "Failed to create writable code cache: " << error_str << " size=" << code_size;
- *error_msg = oss.str();
- return nullptr;
- }
- }
+ DCHECK_EQ(code_map->Begin(), divider);
data_size = initial_capacity / 2;
code_size = initial_capacity - data_size;
DCHECK_EQ(code_size + data_size, initial_capacity);
- return new JitCodeCache(writable_code_map.release(),
- code_map.release(),
- data_map.release(),
- code_size,
- data_size,
- max_capacity,
- garbage_collect_code);
+ return new JitCodeCache(
+ code_map, data_map.release(), code_size, data_size, max_capacity, garbage_collect_code);
}
-JitCodeCache::JitCodeCache(MemMap* writable_code_map,
- MemMap* executable_code_map,
+JitCodeCache::JitCodeCache(MemMap* code_map,
MemMap* data_map,
size_t initial_code_capacity,
size_t initial_data_capacity,
@@ -262,9 +143,8 @@
: lock_("Jit code cache", kJitCodeCacheLock),
lock_cond_("Jit code cache condition variable", lock_),
collection_in_progress_(false),
+ code_map_(code_map),
data_map_(data_map),
- executable_code_map_(executable_code_map),
- writable_code_map_(writable_code_map),
max_capacity_(max_capacity),
current_capacity_(initial_code_capacity + initial_data_capacity),
code_end_(initial_code_capacity),
@@ -284,8 +164,7 @@
inline_cache_cond_("Jit inline cache condition variable", lock_) {
DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
- MemMap* writable_map = GetWritableMemMap();
- code_mspace_ = create_mspace_with_base(writable_map->Begin(), code_end_, false /*locked*/);
+ code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
data_mspace_ = create_mspace_with_base(data_map_->Begin(), data_end_, false /*locked*/);
if (code_mspace_ == nullptr || data_mspace_ == nullptr) {
@@ -294,10 +173,7 @@
SetFootprintLimit(current_capacity_);
- if (writable_code_map_ != nullptr) {
- CHECKED_MPROTECT(writable_code_map_->Begin(), writable_code_map_->Size(), kProtReadOnly);
- }
- CHECKED_MPROTECT(executable_code_map_->Begin(), executable_code_map_->Size(), kProtCode);
+ CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtCode);
CHECKED_MPROTECT(data_map_->Begin(), data_map_->Size(), kProtData);
VLOG(jit) << "Created jit code cache: initial data size="
@@ -307,7 +183,7 @@
}
bool JitCodeCache::ContainsPc(const void* ptr) const {
- return executable_code_map_->Begin() <= ptr && ptr < executable_code_map_->End();
+ return code_map_->Begin() <= ptr && ptr < code_map_->End();
}
bool JitCodeCache::ContainsMethod(ArtMethod* method) {
@@ -320,96 +196,27 @@
return false;
}
-/* This method is only for CHECK/DCHECK that pointers are within to a region. */
-static bool IsAddressInMap(const void* addr,
- const MemMap* mem_map,
- const char* check_name) {
- if (addr == nullptr || mem_map->HasAddress(addr)) {
- return true;
- }
- LOG(ERROR) << "Is" << check_name << "Address " << addr
- << " not in [" << reinterpret_cast<void*>(mem_map->Begin())
- << ", " << reinterpret_cast<void*>(mem_map->Begin() + mem_map->Size()) << ")";
- return false;
-}
-
-bool JitCodeCache::IsDataAddress(const void* raw_addr) const {
- return IsAddressInMap(raw_addr, data_map_.get(), "Data");
-}
-
-bool JitCodeCache::IsExecutableAddress(const void* raw_addr) const {
- return IsAddressInMap(raw_addr, executable_code_map_.get(), "Executable");
-}
-
-bool JitCodeCache::IsWritableAddress(const void* raw_addr) const {
- return IsAddressInMap(raw_addr, GetWritableMemMap(), "Writable");
-}
-
-// Convert one address within the source map to the same offset within the destination map.
-static void* ConvertAddress(const void* source_address,
- const MemMap* source_map,
- const MemMap* destination_map) {
- DCHECK(source_map->HasAddress(source_address)) << source_address;
- ptrdiff_t offset = reinterpret_cast<const uint8_t*>(source_address) - source_map->Begin();
- uintptr_t address = reinterpret_cast<uintptr_t>(destination_map->Begin()) + offset;
- return reinterpret_cast<void*>(address);
-}
-
-template <typename T>
-T* JitCodeCache::ToExecutableAddress(T* writable_address) const {
- CHECK(IsWritableAddress(writable_address));
- if (writable_address == nullptr) {
- return nullptr;
- }
- void* executable_address = ConvertAddress(writable_address,
- GetWritableMemMap(),
- executable_code_map_.get());
- CHECK(IsExecutableAddress(executable_address));
- return reinterpret_cast<T*>(executable_address);
-}
-
-void* JitCodeCache::ToWritableAddress(const void* executable_address) const {
- CHECK(IsExecutableAddress(executable_address));
- if (executable_address == nullptr) {
- return nullptr;
- }
- void* writable_address = ConvertAddress(executable_address,
- executable_code_map_.get(),
- GetWritableMemMap());
- CHECK(IsWritableAddress(writable_address));
- return writable_address;
-}
-
class ScopedCodeCacheWrite : ScopedTrace {
public:
- explicit ScopedCodeCacheWrite(JitCodeCache* code_cache, bool only_for_tlb_shootdown = false)
- : ScopedTrace("ScopedCodeCacheWrite") {
+ explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false)
+ : ScopedTrace("ScopedCodeCacheWrite"),
+ code_map_(code_map),
+ only_for_tlb_shootdown_(only_for_tlb_shootdown) {
ScopedTrace trace("mprotect all");
- int prot_to_start_writing = kProtAll;
- if (code_cache->writable_code_map_ == nullptr) {
- // If there is only one mapping, use the executable mapping and toggle between rwx and rx.
- prot_to_start_writing = kProtAll;
- prot_to_stop_writing_ = kProtCode;
- } else {
- // If there are two mappings, use the writable mapping and toggle between rw and r.
- prot_to_start_writing = kProtData;
- prot_to_stop_writing_ = kProtReadOnly;
- }
- writable_map_ = code_cache->GetWritableMemMap();
- // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to
- // one page.
- size_ = only_for_tlb_shootdown ? kPageSize : writable_map_->Size();
- CHECKED_MPROTECT(writable_map_->Begin(), size_, prot_to_start_writing);
+ CHECKED_MPROTECT(
+ code_map_->Begin(), only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(), kProtAll);
}
~ScopedCodeCacheWrite() {
ScopedTrace trace("mprotect code");
- CHECKED_MPROTECT(writable_map_->Begin(), size_, prot_to_stop_writing_);
+ CHECKED_MPROTECT(
+ code_map_->Begin(), only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(), kProtCode);
}
-
private:
- int prot_to_stop_writing_;
- MemMap* writable_map_;
- size_t size_;
+ MemMap* const code_map_;
+
+ // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to
+ // one page.
+ const bool only_for_tlb_shootdown_;
DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite);
};
@@ -519,10 +326,8 @@
}
}
-uint8_t* JitCodeCache::GetRootTable(const void* code_ptr, uint32_t* number_of_roots) {
- CHECK(IsExecutableAddress(code_ptr));
+static uint8_t* GetRootTable(const void* code_ptr, uint32_t* number_of_roots = nullptr) {
OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
- // GetOptimizedCodeInfoPtr uses offsets relative to the EXECUTABLE address.
uint8_t* data = method_header->GetOptimizedCodeInfoPtr();
uint32_t roots = GetNumberOfRoots(data);
if (number_of_roots != nullptr) {
@@ -567,8 +372,6 @@
void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) {
MutexLock mu(Thread::Current(), lock_);
for (const auto& entry : method_code_map_) {
- // GetRootTable takes an EXECUTABLE address.
- CHECK(IsExecutableAddress(entry.first));
uint32_t number_of_roots = 0;
uint8_t* roots_data = GetRootTable(entry.first, &number_of_roots);
GcRoot<mirror::Object>* roots = reinterpret_cast<GcRoot<mirror::Object>*>(roots_data);
@@ -606,22 +409,20 @@
}
}
-void JitCodeCache::FreeCodeAndData(const void* code_ptr) {
- CHECK(IsExecutableAddress(code_ptr));
+void JitCodeCache::FreeCode(const void* code_ptr) {
+ uintptr_t allocation = FromCodeToAllocation(code_ptr);
// Notify native debugger that we are about to remove the code.
// It does nothing if we are not using native debugger.
DeleteJITCodeEntryForAddress(reinterpret_cast<uintptr_t>(code_ptr));
- // GetRootTable takes an EXECUTABLE address.
FreeData(GetRootTable(code_ptr));
- FreeRawCode(reinterpret_cast<uint8_t*>(FromCodeToAllocation(code_ptr)));
+ FreeCode(reinterpret_cast<uint8_t*>(allocation));
}
void JitCodeCache::FreeAllMethodHeaders(
const std::unordered_set<OatQuickMethodHeader*>& method_headers) {
- // method_headers are expected to be in the executable region.
{
MutexLock mu(Thread::Current(), *Locks::cha_lock_);
- Runtime::Current()->GetClassHierarchyAnalysis()
+ Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()
->RemoveDependentsWithMethodHeaders(method_headers);
}
@@ -630,9 +431,9 @@
// so it's possible for the same method_header to start representing
// different compile code.
MutexLock mu(Thread::Current(), lock_);
- ScopedCodeCacheWrite scc(this);
+ ScopedCodeCacheWrite scc(code_map_.get());
for (const OatQuickMethodHeader* method_header : method_headers) {
- FreeCodeAndData(method_header->GetCode());
+ FreeCode(method_header->GetCode());
}
}
@@ -649,10 +450,9 @@
// with the classlinker_classes_lock_ held, and suspending ourselves could
// lead to a deadlock.
{
- ScopedCodeCacheWrite scc(this);
+ ScopedCodeCacheWrite scc(code_map_.get());
for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
if (alloc.ContainsUnsafe(it->second)) {
- CHECK(IsExecutableAddress(OatQuickMethodHeader::FromCodePointer(it->first)));
method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first));
it = method_code_map_.erase(it);
} else {
@@ -744,87 +544,6 @@
method->SetCounter(std::min(jit_warmup_threshold - 1, 1));
}
-#ifdef __aarch64__
-
-static void FlushJitCodeCacheRange(uint8_t* code_ptr,
- uint8_t* writable_ptr ATTRIBUTE_UNUSED,
- size_t code_size) {
- // Cache maintenance instructions can cause permission faults when a
- // page is not present (e.g. swapped out or not backed). These
- // faults should be handled by the kernel, but a bug in some Linux
- // kernels may surface these permission faults to user-land which
- // does not currently deal with them (b/63885946). To work around
- // this, we read a value from each page to fault it in before
- // attempting to perform cache maintenance operations.
- //
- // For reference, this behavior is caused by this commit:
- // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c
-
- // The cache-line size could be probed for from the CPU, but
- // assuming a safe lower bound is safe for CPUs that have different
- // cache-line sizes for big and little cores.
- static const uintptr_t kSafeCacheLineSize = 32;
-
- // Ensure stores are present in data cache.
- __asm __volatile("dsb sy");
-
- uintptr_t addr = RoundDown(reinterpret_cast<uintptr_t>(code_ptr), kSafeCacheLineSize);
- const uintptr_t limit_addr = RoundUp(reinterpret_cast<uintptr_t>(code_ptr) + code_size,
- kSafeCacheLineSize);
- volatile uint8_t mutant;
- while (addr < limit_addr) {
- // Read from the cache-line to minimize the chance that a cache
- // maintenance instruction causes a fault (see kernel bug comment
- // above).
- mutant = *reinterpret_cast<const uint8_t*>(addr);
-
- // Invalidating the data cache line is only strictly necessary
- // when the JIT code cache has two mappings (the default). We know
- // this cache line is clean so this is just invalidating it (using
- // "dc ivac" would be preferable, but is privileged).
- __asm volatile("dc cvau, %0" :: "r"(addr));
-
- // Invalidate the instruction cache line to force instructions in
- // range to be re-fetched following update.
- __asm volatile("ic ivau, %0" :: "r"(addr));
-
- addr += kSafeCacheLineSize;
- }
-
- // Drain data and instruction buffers.
- __asm __volatile("dsb sy");
- __asm __volatile("isb sy");
-}
-
-#else // __aarch64
-
-static void FlushJitCodeCacheRange(uint8_t* code_ptr,
- uint8_t* writable_ptr,
- size_t code_size) {
- if (writable_ptr != code_ptr) {
- // When there are two mappings of the JIT code cache, RX and
- // RW, flush the RW version first as we've just dirtied the
- // cache lines with new code. Flushing the RX version first
- // can cause a permission fault as the those addresses are not
- // writable, but can appear dirty in the cache. There is a lot
- // of potential subtlety here depending on how the cache is
- // indexed and tagged.
- //
- // Flushing the RX version after the RW version is just
- // invalidating cachelines in the instruction cache. This is
- // necessary as the instruction cache will often have a
- // different set of cache lines present and because the JIT
- // code cache can start a new function at any boundary within
- // a cache-line.
- FlushDataCache(reinterpret_cast<char*>(writable_ptr),
- reinterpret_cast<char*>(writable_ptr + code_size));
- }
- FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
- reinterpret_cast<char*>(code_ptr + code_size));
-}
-
-#endif // __aarch64
-
uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
ArtMethod* method,
uint8_t* stack_map,
@@ -855,36 +574,35 @@
MutexLock mu(self, lock_);
WaitForPotentialCollectionToComplete(self);
{
- ScopedCodeCacheWrite scc(this);
+ ScopedCodeCacheWrite scc(code_map_.get());
memory = AllocateCode(total_size);
if (memory == nullptr) {
return nullptr;
}
- uint8_t* writable_ptr = memory + header_size;
- code_ptr = ToExecutableAddress(writable_ptr);
+ code_ptr = memory + header_size;
- std::copy(code, code + code_size, writable_ptr);
- OatQuickMethodHeader* writable_method_header =
- OatQuickMethodHeader::FromCodePointer(writable_ptr);
- // We need to be able to write the OatQuickMethodHeader, so we use writable_method_header.
- // Otherwise, the offsets encoded in OatQuickMethodHeader are used relative to an executable
- // address, so we use code_ptr.
- new (writable_method_header) OatQuickMethodHeader(
+ std::copy(code, code + code_size, code_ptr);
+ method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+ new (method_header) OatQuickMethodHeader(
code_ptr - stack_map,
code_ptr - method_info,
frame_size_in_bytes,
core_spill_mask,
fp_spill_mask,
code_size);
-
- FlushJitCodeCacheRange(code_ptr, writable_ptr, code_size);
-
+ // Flush caches before we remove write permission because some ARMv8 Qualcomm kernels may
+ // trigger a segfault if a page fault occurs when requesting a cache maintenance operation.
+ // This is a kernel bug that we need to work around until affected devices (e.g. Nexus 5X and
+ // 6P) stop being supported or their kernels are fixed.
+ //
+ // For reference, this behavior is caused by this commit:
+ // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c
+ FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
+ reinterpret_cast<char*>(code_ptr + code_size));
DCHECK(!Runtime::Current()->IsAotCompiler());
if (has_should_deoptimize_flag) {
- writable_method_header->SetHasShouldDeoptimizeFlag();
+ method_header->SetHasShouldDeoptimizeFlag();
}
- // All the pointers exported from the cache are executable addresses.
- method_header = ToExecutableAddress(writable_method_header);
}
number_of_compilations_++;
@@ -914,7 +632,7 @@
<< "Should not be using cha on debuggable apps/runs!";
for (ArtMethod* single_impl : cha_single_implementation_list) {
- Runtime::Current()->GetClassHierarchyAnalysis()->AddDependency(
+ Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()->AddDependency(
single_impl, method, method_header);
}
@@ -923,14 +641,13 @@
// but below we still make the compiled code valid for the method.
MutexLock mu(self, lock_);
// Fill the root table before updating the entry point.
- CHECK(IsDataAddress(roots_data));
DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data);
DCHECK_LE(roots_data, stack_map);
FillRootTable(roots_data, roots);
{
// Flush data cache, as compiled code references literals in it.
// We also need a TLB shootdown to act as memory barrier across cores.
- ScopedCodeCacheWrite ccw(this, /* only_for_tlb_shootdown */ true);
+ ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true);
FlushDataCache(reinterpret_cast<char*>(roots_data),
reinterpret_cast<char*>(roots_data + data_size));
}
@@ -981,11 +698,11 @@
bool in_cache = false;
{
- ScopedCodeCacheWrite ccw(this);
+ ScopedCodeCacheWrite ccw(code_map_.get());
for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) {
if (code_iter->second == method) {
if (release_memory) {
- FreeCodeAndData(code_iter->first);
+ FreeCode(code_iter->first);
}
code_iter = method_code_map_.erase(code_iter);
in_cache = true;
@@ -1039,10 +756,10 @@
profiling_infos_.erase(profile);
}
method->SetProfilingInfo(nullptr);
- ScopedCodeCacheWrite ccw(this);
+ ScopedCodeCacheWrite ccw(code_map_.get());
for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) {
if (code_iter->second == method) {
- FreeCodeAndData(code_iter->first);
+ FreeCode(code_iter->first);
code_iter = method_code_map_.erase(code_iter);
continue;
}
@@ -1108,7 +825,6 @@
uint8_t* stack_map_data,
uint8_t* roots_data) {
DCHECK_EQ(FromStackMapToRoots(stack_map_data), roots_data);
- CHECK(IsDataAddress(roots_data));
MutexLock mu(self, lock_);
FreeData(reinterpret_cast<uint8_t*>(roots_data));
}
@@ -1230,11 +946,11 @@
void JitCodeCache::SetFootprintLimit(size_t new_footprint) {
size_t per_space_footprint = new_footprint / 2;
- CHECK(IsAlignedParam(per_space_footprint, kPageSize));
+ DCHECK(IsAlignedParam(per_space_footprint, kPageSize));
DCHECK_EQ(per_space_footprint * 2, new_footprint);
mspace_set_footprint_limit(data_mspace_, per_space_footprint);
{
- ScopedCodeCacheWrite scc(this);
+ ScopedCodeCacheWrite scc(code_map_.get());
mspace_set_footprint_limit(code_mspace_, per_space_footprint);
}
}
@@ -1255,9 +971,7 @@
current_capacity_ = max_capacity_;
}
- if (!kIsDebugBuild || VLOG_IS_ON(jit)) {
- LOG(INFO) << "Increasing code cache capacity to " << PrettySize(current_capacity_);
- }
+ VLOG(jit) << "Increasing code cache capacity to " << PrettySize(current_capacity_);
SetFootprintLimit(current_capacity_);
@@ -1312,8 +1026,8 @@
number_of_collections_++;
live_bitmap_.reset(CodeCacheBitmap::Create(
"code-cache-bitmap",
- reinterpret_cast<uintptr_t>(executable_code_map_->Begin()),
- reinterpret_cast<uintptr_t>(executable_code_map_->Begin() + current_capacity_ / 2)));
+ reinterpret_cast<uintptr_t>(code_map_->Begin()),
+ reinterpret_cast<uintptr_t>(code_map_->Begin() + current_capacity_ / 2)));
collection_in_progress_ = true;
}
}
@@ -1328,21 +1042,17 @@
do_full_collection = ShouldDoFullCollection();
}
- if (!kIsDebugBuild || VLOG_IS_ON(jit)) {
- LOG(INFO) << "Do "
- << (do_full_collection ? "full" : "partial")
- << " code cache collection, code="
- << PrettySize(CodeCacheSize())
- << ", data=" << PrettySize(DataCacheSize());
- }
+ VLOG(jit) << "Do "
+ << (do_full_collection ? "full" : "partial")
+ << " code cache collection, code="
+ << PrettySize(CodeCacheSize())
+ << ", data=" << PrettySize(DataCacheSize());
DoCollection(self, /* collect_profiling_info */ do_full_collection);
- if (!kIsDebugBuild || VLOG_IS_ON(jit)) {
- LOG(INFO) << "After code cache collection, code="
- << PrettySize(CodeCacheSize())
- << ", data=" << PrettySize(DataCacheSize());
- }
+ VLOG(jit) << "After code cache collection, code="
+ << PrettySize(CodeCacheSize())
+ << ", data=" << PrettySize(DataCacheSize());
{
MutexLock mu(self, lock_);
@@ -1389,16 +1099,14 @@
std::unordered_set<OatQuickMethodHeader*> method_headers;
{
MutexLock mu(self, lock_);
- ScopedCodeCacheWrite scc(this);
+ ScopedCodeCacheWrite scc(code_map_.get());
// Iterate over all compiled code and remove entries that are not marked.
for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
const void* code_ptr = it->first;
- CHECK(IsExecutableAddress(code_ptr));
uintptr_t allocation = FromCodeToAllocation(code_ptr);
if (GetLiveBitmap()->Test(allocation)) {
++it;
} else {
- CHECK(IsExecutableAddress(it->first));
method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first));
it = method_code_map_.erase(it);
}
@@ -1441,7 +1149,6 @@
for (const auto& it : method_code_map_) {
ArtMethod* method = it.second;
const void* code_ptr = it.first;
- CHECK(IsExecutableAddress(code_ptr));
const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) {
GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));
@@ -1467,7 +1174,6 @@
// Free all profiling infos of methods not compiled nor being compiled.
auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(),
[this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS {
- CHECK(IsDataAddress(info));
const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
// We have previously cleared the ProfilingInfo pointer in the ArtMethod in the hope
// that the compiled code would not get revived. As mutator threads run concurrently,
@@ -1528,7 +1234,6 @@
--it;
const void* code_ptr = it->first;
- CHECK(IsExecutableAddress(code_ptr));
OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
if (!method_header->Contains(pc)) {
return nullptr;
@@ -1540,7 +1245,7 @@
// is the one we expect. We change to the non-obsolete versions in the error message since the
// obsolete version of the method might not be fully initialized yet. This situation can only
// occur when we are in the process of allocating and setting up obsolete methods. Otherwise
- // method and it->second should be identical. (See runtime/openjdkjvmti/ti_redefine.cc for more
+ // method and it->second should be identical. (See openjdkjvmti/ti_redefine.cc for more
// information.)
DCHECK_EQ(it->second->GetNonObsoleteMethod(), method->GetNonObsoleteMethod())
<< ArtMethod::PrettyMethod(method->GetNonObsoleteMethod()) << " "
@@ -1611,7 +1316,6 @@
// store in the ArtMethod's ProfilingInfo pointer.
QuasiAtomic::ThreadFenceRelease();
- CHECK(IsDataAddress(info));
method->SetProfilingInfo(info);
profiling_infos_.push_back(info);
histogram_profiling_info_memory_use_.AddValue(profile_info_size);
@@ -1624,8 +1328,7 @@
if (code_mspace_ == mspace) {
size_t result = code_end_;
code_end_ += increment;
- MemMap* writable_map = GetWritableMemMap();
- return reinterpret_cast<void*>(result + writable_map->Begin());
+ return reinterpret_cast<void*>(result + code_map_->Begin());
} else {
DCHECK_EQ(data_mspace_, mspace);
size_t result = data_end_;
@@ -1777,7 +1480,6 @@
size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) {
MutexLock mu(Thread::Current(), lock_);
- CHECK(IsExecutableAddress(ptr));
return mspace_usable_size(reinterpret_cast<const void*>(FromCodeToAllocation(ptr)));
}
@@ -1813,27 +1515,22 @@
size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
// Ensure the header ends up at expected instruction alignment.
DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(result + header_size), alignment);
- CHECK(IsWritableAddress(result));
used_memory_for_code_ += mspace_usable_size(result);
return result;
}
-void JitCodeCache::FreeRawCode(void* code) {
- CHECK(IsExecutableAddress(code));
- void* writable_code = ToWritableAddress(code);
- used_memory_for_code_ -= mspace_usable_size(writable_code);
- mspace_free(code_mspace_, writable_code);
+void JitCodeCache::FreeCode(uint8_t* code) {
+ used_memory_for_code_ -= mspace_usable_size(code);
+ mspace_free(code_mspace_, code);
}
uint8_t* JitCodeCache::AllocateData(size_t data_size) {
void* result = mspace_malloc(data_mspace_, data_size);
- CHECK(IsDataAddress(reinterpret_cast<uint8_t*>(result)));
used_memory_for_data_ += mspace_usable_size(result);
return reinterpret_cast<uint8_t*>(result);
}
void JitCodeCache::FreeData(uint8_t* data) {
- CHECK(IsDataAddress(data));
used_memory_for_data_ -= mspace_usable_size(data);
mspace_free(data_mspace_, data);
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index a062ce4..daa1d61 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -229,8 +229,6 @@
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- uint8_t* GetRootTable(const void* code_ptr, uint32_t* number_of_roots = nullptr);
-
// The GC needs to disallow the reading of inline caches when it processes them,
// to avoid having a class being used while it is being deleted.
void AllowInlineCacheAccess() REQUIRES(!lock_);
@@ -249,12 +247,9 @@
}
private:
- friend class ScopedCodeCacheWrite;
-
// Take ownership of maps.
JitCodeCache(MemMap* code_map,
MemMap* data_map,
- MemMap* writable_code_map,
size_t initial_code_capacity,
size_t initial_data_capacity,
size_t max_capacity,
@@ -297,7 +292,7 @@
REQUIRES(!Locks::cha_lock_);
// Free in the mspace allocations for `code_ptr`.
- void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_);
+ void FreeCode(const void* code_ptr) REQUIRES(lock_);
// Number of bytes allocated in the code cache.
size_t CodeCacheSizeLocked() REQUIRES(lock_);
@@ -330,7 +325,7 @@
bool CheckLiveCompiledCodeHasProfilingInfo()
REQUIRES(lock_);
- void FreeRawCode(void* code) REQUIRES(lock_);
+ void FreeCode(uint8_t* code) REQUIRES(lock_);
uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_);
void FreeData(uint8_t* data) REQUIRES(lock_);
uint8_t* AllocateData(size_t data_size) REQUIRES(lock_);
@@ -340,58 +335,25 @@
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- MemMap* GetWritableMemMap() const {
- if (writable_code_map_ == nullptr) {
- // The system required us to map the JIT Code Cache RWX (see
- // JitCodeCache::Create()).
- return executable_code_map_.get();
- } else {
- // Executable code is mapped RX, and writable code is mapped RW
- // to the underlying same memory, but at a different address.
- return writable_code_map_.get();
- }
- }
-
- bool IsDataAddress(const void* raw_addr) const;
-
- bool IsExecutableAddress(const void* raw_addr) const;
-
- bool IsWritableAddress(const void* raw_addr) const;
-
- template <typename T>
- T* ToExecutableAddress(T* writable_address) const;
-
- void* ToWritableAddress(const void* executable_address) const;
-
// Lock for guarding allocations, collections, and the method_code_map_.
Mutex lock_;
// Condition to wait on during collection.
ConditionVariable lock_cond_ GUARDED_BY(lock_);
// Whether there is a code cache collection in progress.
bool collection_in_progress_ GUARDED_BY(lock_);
- // JITting methods obviously requires both write and execute permissions on a region of memory.
- // In tye typical (non-debugging) case, we separate the memory mapped view that can write the code
- // from a view that the runtime uses to execute the code. Having these two views eliminates any
- // single address region having rwx permissions. An attacker could still write the writable
- // address and then execute the executable address. We allocate the mappings with a random
- // address relationship to each other which makes the attacker need two addresses rather than
- // just one. In the debugging case there is no file descriptor to back the
- // shared memory, and hence we have to use a single mapping.
+ // Mem map which holds code.
+ std::unique_ptr<MemMap> code_map_;
// Mem map which holds data (stack maps and profiling info).
std::unique_ptr<MemMap> data_map_;
- // Mem map which holds a non-writable view of code for JIT.
- std::unique_ptr<MemMap> executable_code_map_;
- // Mem map which holds a non-executable view of code for JIT.
- std::unique_ptr<MemMap> writable_code_map_;
// The opaque mspace for allocating code.
void* code_mspace_ GUARDED_BY(lock_);
// The opaque mspace for allocating data.
void* data_mspace_ GUARDED_BY(lock_);
// Bitmap for collecting code and data.
std::unique_ptr<CodeCacheBitmap> live_bitmap_;
- // Holds non-writable compiled code associated to the ArtMethod.
+ // Holds compiled code associated to the ArtMethod.
SafeMap<const void*, ArtMethod*> method_code_map_ GUARDED_BY(lock_);
- // Holds non-writable osr compiled code associated to the ArtMethod.
+ // Holds osr compiled code associated to the ArtMethod.
SafeMap<ArtMethod*, const void*> osr_code_map_ GUARDED_BY(lock_);
// ProfilingInfo objects we have allocated.
std::vector<ProfilingInfo*> profiling_infos_ GUARDED_BY(lock_);
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 45c3792..c9bfc9c 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -16,20 +16,20 @@
#include "profile_compilation_info.h"
-#include "errno.h"
-#include <limits.h>
-#include <string>
-#include <vector>
-#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <sys/uio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/types.h>
#include <unistd.h>
#include <zlib.h>
-#include <base/time_utils.h>
+
+#include <cerrno>
+#include <climits>
+#include <cstdlib>
+#include <string>
+#include <vector>
+
+#include "android-base/file.h"
#include "base/arena_allocator.h"
#include "base/dumpable.h"
@@ -37,12 +37,12 @@
#include "base/scoped_flock.h"
#include "base/stl_util.h"
#include "base/systrace.h"
+#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "jit/profiling_info.h"
#include "os.h"
#include "safe_map.h"
#include "utils.h"
-#include "android-base/file.h"
namespace art {
@@ -1652,4 +1652,27 @@
return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&arena_))->second);
}
+std::unordered_set<std::string> ProfileCompilationInfo::GetClassDescriptors(
+ const std::vector<const DexFile*>& dex_files) {
+ std::unordered_set<std::string> ret;
+ for (const DexFile* dex_file : dex_files) {
+ const DexFileData* data = FindDexData(dex_file);
+ if (data != nullptr) {
+ for (dex::TypeIndex type_idx : data->class_set) {
+ if (!dex_file->IsTypeIndexValid(type_idx)) {
+ // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
+ LOG(WARNING) << "Corrupted profile: invalid type index "
+ << type_idx.index_ << " in dex " << dex_file->GetLocation();
+ return std::unordered_set<std::string>();
+ }
+ const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
+ ret.insert(dex_file->GetTypeDescriptor(type_id));
+ }
+ } else {
+ VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation();
+ }
+ }
+ return ret;
+}
+
} // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 079ce8d..ffb67ae 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -21,8 +21,8 @@
#include <vector>
#include "atomic.h"
-#include "base/arena_object.h"
#include "base/arena_containers.h"
+#include "base/arena_object.h"
#include "bit_memory_region.h"
#include "dex_cache_resolved_classes.h"
#include "dex_file.h"
@@ -380,6 +380,9 @@
ArenaAllocator* GetArena() { return &arena_; }
+ // Return all of the class descriptors in the profile for a set of dex files.
+ std::unordered_set<std::string> GetClassDescriptors(const std::vector<const DexFile*>& dex_files);
+
private:
enum ProfileLoadSatus {
kProfileLoadWouldOverwiteData,
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 1ba98ac..6010bce 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -16,17 +16,17 @@
#include <gtest/gtest.h>
-#include "base/unix_file/fd_file.h"
#include "art_method-inl.h"
+#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "dex_file.h"
-#include "method_reference.h"
-#include "mirror/class-inl.h"
-#include "mirror/class_loader.h"
#include "handle_scope-inl.h"
#include "jit/profile_compilation_info.h"
#include "linear_alloc.h"
+#include "method_reference.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
#include "scoped_thread_state_change-inl.h"
#include "type_reference.h"
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 61e5be3..381e95f 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -16,10 +16,10 @@
#include "profile_saver.h"
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include "android-base/strings.h"
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index 251227e..d1e14e2 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -13,8 +13,8 @@
#ifndef ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
#define ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
-#include <string>
#include <ostream>
+#include <string>
namespace art {
diff --git a/runtime/leb128_test.cc b/runtime/leb128_test.cc
index 122f55e..747fc19 100644
--- a/runtime/leb128_test.cc
+++ b/runtime/leb128_test.cc
@@ -17,6 +17,7 @@
#include "leb128.h"
#include "gtest/gtest.h"
+
#include "base/histogram-inl.h"
#include "base/time_utils.h"
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
index edc64f3..14f638e 100644
--- a/runtime/lock_word.h
+++ b/runtime/lock_word.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_LOCK_WORD_H_
#define ART_RUNTIME_LOCK_WORD_H_
+#include <cstdint>
#include <iosfwd>
-#include <stdint.h>
#include "base/bit_utils.h"
#include "base/logging.h"
diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h
index bdf8100..689dd80 100644
--- a/runtime/managed_stack-inl.h
+++ b/runtime/managed_stack-inl.h
@@ -19,10 +19,6 @@
#include "managed_stack.h"
-#include <cstring>
-#include <stdint.h>
-#include <string>
-
#include "interpreter/shadow_frame.h"
namespace art {
diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h
index 8337f96..4f1984d 100644
--- a/runtime/managed_stack.h
+++ b/runtime/managed_stack.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_MANAGED_STACK_H_
#define ART_RUNTIME_MANAGED_STACK_H_
+#include <cstdint>
#include <cstring>
-#include <stdint.h>
#include <string>
#include "base/logging.h"
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 17035dd..743604c 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -38,7 +38,6 @@
#include "globals.h"
#include "utils.h"
-
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
@@ -536,13 +535,8 @@
}
}
-MemMap* MemMap::RemapAtEnd(uint8_t* new_end,
- const char* tail_name,
- int tail_prot,
- int sharing_flags,
- std::string* error_msg,
- bool use_ashmem,
- unique_fd* shmem_fd) {
+MemMap* MemMap::RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_prot,
+ std::string* error_msg, bool use_ashmem) {
use_ashmem = use_ashmem && !kIsTargetLinux;
DCHECK_GE(new_end, Begin());
DCHECK_LE(new_end, End());
@@ -568,14 +562,14 @@
DCHECK_ALIGNED(tail_base_size, kPageSize);
unique_fd fd;
- int flags = MAP_ANONYMOUS | sharing_flags;
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
if (use_ashmem) {
// android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
// prefixed "dalvik-".
std::string debug_friendly_name("dalvik-");
debug_friendly_name += tail_name;
fd.reset(ashmem_create_region(debug_friendly_name.c_str(), tail_base_size));
- flags = MAP_FIXED | sharing_flags;
+ flags = MAP_PRIVATE | MAP_FIXED;
if (fd.get() == -1) {
*error_msg = StringPrintf("ashmem_create_region failed for '%s': %s",
tail_name, strerror(errno));
@@ -609,9 +603,6 @@
fd.get());
return nullptr;
}
- if (shmem_fd != nullptr) {
- shmem_fd->reset(fd.release());
- }
return new MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot, false);
}
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index d8908ad..5603963 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -25,7 +25,6 @@
#include <string>
#include "android-base/thread_annotations.h"
-#include "android-base/unique_fd.h"
namespace art {
@@ -38,8 +37,6 @@
#define USE_ART_LOW_4G_ALLOCATOR 0
#endif
-using android::base::unique_fd;
-
#ifdef __linux__
static constexpr bool kMadviseZeroes = true;
#else
@@ -171,14 +168,11 @@
}
// Unmap the pages at end and remap them to create another memory map.
- // sharing_flags should be either MAP_PRIVATE or MAP_SHARED.
MemMap* RemapAtEnd(uint8_t* new_end,
const char* tail_name,
int tail_prot,
- int sharing_flags,
std::string* error_msg,
- bool use_ashmem = true,
- unique_fd* shmem_fd = nullptr);
+ bool use_ashmem = true);
static bool CheckNoGaps(MemMap* begin_map, MemMap* end_map)
REQUIRES(!MemMap::mem_maps_lock_);
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index 8d6bb38..a4ebb16 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -20,9 +20,9 @@
#include <memory>
-#include "common_runtime_test.h"
#include "base/memory_tool.h"
#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
namespace art {
@@ -74,7 +74,6 @@
MemMap* m1 = m0->RemapAtEnd(base0 + page_size,
"MemMapTest_RemapAtEndTest_map1",
PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
&error_msg);
// Check the states of the two maps.
EXPECT_EQ(m0->Begin(), base0) << error_msg;
@@ -457,7 +456,6 @@
std::unique_ptr<MemMap> m1(m0->RemapAtEnd(base0 + 3 * page_size,
"MemMapTest_AlignByTest_map1",
PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
&error_msg));
uint8_t* base1 = m1->Begin();
ASSERT_TRUE(base1 != nullptr) << error_msg;
@@ -467,7 +465,6 @@
std::unique_ptr<MemMap> m2(m1->RemapAtEnd(base1 + 4 * page_size,
"MemMapTest_AlignByTest_map2",
PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
&error_msg));
uint8_t* base2 = m2->Begin();
ASSERT_TRUE(base2 != nullptr) << error_msg;
@@ -477,7 +474,6 @@
std::unique_ptr<MemMap> m3(m2->RemapAtEnd(base2 + 3 * page_size,
"MemMapTest_AlignByTest_map1",
PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
&error_msg));
uint8_t* base3 = m3->Begin();
ASSERT_TRUE(base3 != nullptr) << error_msg;
diff --git a/runtime/memory_region_test.cc b/runtime/memory_region_test.cc
index 6634c60..e3aead4 100644
--- a/runtime/memory_region_test.cc
+++ b/runtime/memory_region_test.cc
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "bit_memory_region.h"
#include "memory_region.h"
#include "gtest/gtest.h"
+#include "bit_memory_region.h"
+
namespace art {
TEST(MemoryRegion, LoadUnaligned) {
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index f0d3cae..65f39e4 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -19,13 +19,13 @@
#include "android-base/stringprintf.h"
#include "common_dex_operations.h"
-#include "jvalue.h"
#include "jvalue-inl.h"
+#include "jvalue.h"
#include "mirror/emulated_stack_frame.h"
#include "mirror/method_handle_impl-inl.h"
#include "mirror/method_type.h"
-#include "reflection.h"
#include "reflection-inl.h"
+#include "reflection.h"
#include "well_known_classes.h"
namespace art {
diff --git a/runtime/mirror/accessible_object.h b/runtime/mirror/accessible_object.h
index a217193..d489f14 100644
--- a/runtime/mirror/accessible_object.h
+++ b/runtime/mirror/accessible_object.h
@@ -17,11 +17,8 @@
#ifndef ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_
#define ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_
-#include "class.h"
-#include "gc_root.h"
#include "object.h"
#include "read_barrier_option.h"
-#include "thread.h"
namespace art {
@@ -34,12 +31,6 @@
return OFFSET_OF_OBJECT_MEMBER(AccessibleObject, flag_);
}
- template<bool kTransactionActive>
- void SetAccessible(bool value) REQUIRES_SHARED(Locks::mutator_lock_) {
- UNUSED(padding_);
- return SetFieldBoolean<kTransactionActive>(FlagOffset(), value ? 1u : 0u);
- }
-
bool IsAccessible() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldBoolean(FlagOffset());
}
@@ -47,7 +38,7 @@
private:
uint8_t flag_;
// Padding required for correct alignment of subclasses like Executable, Field, etc.
- uint8_t padding_[1];
+ uint8_t padding_[1] ATTRIBUTE_UNUSED;
DISALLOW_IMPLICIT_CONSTRUCTORS(AccessibleObject);
};
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index bfbd4df..2281245 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -26,9 +26,8 @@
#include "base/logging.h"
#include "class.h"
#include "gc/heap-inl.h"
-#include "object-inl.h"
#include "obj_ptr-inl.h"
-#include "thread.h"
+#include "thread-current-inl.h"
namespace art {
namespace mirror {
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index f283ec3..6218dd9 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -16,15 +16,15 @@
#include "array-inl.h"
-#include "class.h"
#include "class-inl.h"
+#include "class.h"
#include "class_linker-inl.h"
#include "common_throws.h"
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
+#include "handle_scope-inl.h"
#include "object-inl.h"
#include "object_array-inl.h"
-#include "handle_scope-inl.h"
#include "thread.h"
#include "utils.h"
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 99565c6..11128bb 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -18,8 +18,8 @@
#define ART_RUNTIME_MIRROR_ARRAY_H_
#include "base/enums.h"
-#include "gc_root.h"
#include "gc/allocator_type.h"
+#include "gc_root.h"
#include "obj_ptr.h"
#include "object.h"
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 121c259..67aeeff 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -30,8 +30,8 @@
#include "dex_file-inl.h"
#include "gc/heap-inl.h"
#include "iftable.h"
-#include "object_array.h"
#include "object-inl.h"
+#include "object_array.h"
#include "read_barrier-inl.h"
#include "reference-inl.h"
#include "runtime.h"
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 6f70b19..c775cf4 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -20,19 +20,19 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
+#include "class-inl.h"
#include "class_ext.h"
#include "class_linker-inl.h"
#include "class_loader.h"
-#include "class-inl.h"
#include "dex_cache.h"
#include "dex_file-inl.h"
#include "dex_file_annotations.h"
#include "gc/accounting/card_table-inl.h"
#include "handle_scope-inl.h"
#include "method.h"
-#include "object_array-inl.h"
#include "object-inl.h"
#include "object-refvisitor-inl.h"
+#include "object_array-inl.h"
#include "object_lock.h"
#include "runtime.h"
#include "thread.h"
@@ -464,14 +464,25 @@
return FindInterfaceMethod(name, signature, pointer_size);
}
+static inline bool IsValidInheritanceCheck(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::Class> declaring_class)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (klass->IsArrayClass()) {
+ return declaring_class->IsObjectClass();
+ } else if (klass->IsInterface()) {
+ return declaring_class->IsObjectClass() || declaring_class == klass;
+ } else {
+ return klass->IsSubClass(declaring_class);
+ }
+}
+
static inline bool IsInheritedMethod(ObjPtr<mirror::Class> klass,
ObjPtr<mirror::Class> declaring_class,
ArtMethod& method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(declaring_class, method.GetDeclaringClass());
DCHECK_NE(klass, declaring_class);
- DCHECK(klass->IsArrayClass() ? declaring_class->IsObjectClass()
- : klass->IsSubClass(declaring_class));
+ DCHECK(IsValidInheritanceCheck(klass, declaring_class));
uint32_t access_flags = method.GetAccessFlags();
if ((access_flags & (kAccPublic | kAccProtected)) != 0) {
return true;
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index c626897..250604b 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -20,11 +20,11 @@
#include "base/bit_utils.h"
#include "base/enums.h"
#include "base/iteration_range.h"
+#include "class_flags.h"
#include "dex_file.h"
#include "dex_file_types.h"
-#include "class_flags.h"
-#include "gc_root.h"
#include "gc/allocator_type.h"
+#include "gc_root.h"
#include "imtable.h"
#include "invoke_type.h"
#include "modifiers.h"
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index 381d96b..f25f18f 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -18,9 +18,9 @@
#define ART_RUNTIME_MIRROR_CLASS_LOADER_H_
#include "base/mutex.h"
+#include "obj_ptr.h"
#include "object.h"
#include "object_reference.h"
-#include "obj_ptr.h"
namespace art {
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 18e22ef..8b11c12 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -26,13 +26,13 @@
#include "base/logging.h"
#include "class_linker.h"
#include "dex_file.h"
-#include "gc_root.h"
#include "gc/heap-inl.h"
-#include "mirror/class.h"
+#include "gc_root.h"
#include "mirror/call_site.h"
+#include "mirror/class.h"
#include "mirror/method_type.h"
-#include "runtime.h"
#include "obj_ptr.h"
+#include "runtime.h"
#include <atomic>
@@ -208,24 +208,38 @@
}
}
+inline uint32_t DexCache::MethodSlotIndex(uint32_t method_idx) {
+ DCHECK_LT(method_idx, GetDexFile()->NumMethodIds());
+ const uint32_t slot_idx = method_idx % kDexCacheMethodCacheSize;
+ DCHECK_LT(slot_idx, NumResolvedMethods());
+ return slot_idx;
+}
+
inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
- DCHECK_LT(method_idx, NumResolvedMethods()); // NOTE: Unchecked, i.e. not throwing AIOOB.
- ArtMethod* method = GetElementPtrSize<ArtMethod*>(GetResolvedMethods(), method_idx, ptr_size);
- // Hide resolution trampoline methods from the caller
- if (method != nullptr && method->IsRuntimeMethod()) {
- DCHECK_EQ(method, Runtime::Current()->GetResolutionMethod());
- return nullptr;
- }
- return method;
+ auto pair = GetNativePairPtrSize(GetResolvedMethods(), MethodSlotIndex(method_idx), ptr_size);
+ return pair.GetObjectForIndex(method_idx);
}
inline void DexCache::SetResolvedMethod(uint32_t method_idx,
ArtMethod* method,
PointerSize ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
- DCHECK_LT(method_idx, NumResolvedMethods()); // NOTE: Unchecked, i.e. not throwing AIOOB.
- SetElementPtrSize(GetResolvedMethods(), method_idx, method, ptr_size);
+ DCHECK(method != nullptr);
+ MethodDexCachePair pair(method, method_idx);
+ SetNativePairPtrSize(GetResolvedMethods(), MethodSlotIndex(method_idx), pair, ptr_size);
+}
+
+inline void DexCache::ClearResolvedMethod(uint32_t method_idx, PointerSize ptr_size) {
+ DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
+ uint32_t slot_idx = MethodSlotIndex(method_idx);
+ auto* resolved_methods = GetResolvedMethods();
+ // This is racy but should only be called from the single-threaded ImageWriter.
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ if (GetNativePairPtrSize(resolved_methods, slot_idx, ptr_size).index == method_idx) {
+ MethodDexCachePair cleared(nullptr, MethodDexCachePair::InvalidIndexForSlot(slot_idx));
+ SetNativePairPtrSize(resolved_methods, slot_idx, cleared, ptr_size);
+ }
}
template <typename PtrType>
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 96e3475..f6f20ba 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -24,8 +24,8 @@
#include "globals.h"
#include "linear_alloc.h"
#include "oat_file.h"
-#include "object.h"
#include "object-inl.h"
+#include "object.h"
#include "object_array-inl.h"
#include "runtime.h"
#include "string.h"
@@ -61,14 +61,14 @@
: reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size()));
}
- mirror::StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr :
- reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
- mirror::TypeDexCacheType* types = (dex_file->NumTypeIds() == 0u) ? nullptr :
- reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
- ArtMethod** methods = (dex_file->NumMethodIds() == 0u) ? nullptr :
- reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
- mirror::FieldDexCacheType* fields = (dex_file->NumFieldIds() == 0u) ? nullptr :
- reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset());
+ StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr :
+ reinterpret_cast<StringDexCacheType*>(raw_arrays + layout.StringsOffset());
+ TypeDexCacheType* types = (dex_file->NumTypeIds() == 0u) ? nullptr :
+ reinterpret_cast<TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
+ MethodDexCacheType* methods = (dex_file->NumMethodIds() == 0u) ? nullptr :
+ reinterpret_cast<MethodDexCacheType*>(raw_arrays + layout.MethodsOffset());
+ FieldDexCacheType* fields = (dex_file->NumFieldIds() == 0u) ? nullptr :
+ reinterpret_cast<FieldDexCacheType*>(raw_arrays + layout.FieldsOffset());
size_t num_strings = kDexCacheStringCacheSize;
if (dex_file->NumStringIds() < num_strings) {
@@ -82,6 +82,10 @@
if (dex_file->NumFieldIds() < num_fields) {
num_fields = dex_file->NumFieldIds();
}
+ size_t num_methods = kDexCacheMethodCacheSize;
+ if (dex_file->NumMethodIds() < num_methods) {
+ num_methods = dex_file->NumMethodIds();
+ }
// Note that we allocate the method type dex caches regardless of this flag,
// and we make sure here that they're not used by the runtime. This is in the
@@ -105,7 +109,7 @@
GcRoot<mirror::CallSite>* call_sites = (dex_file->NumCallSiteIds() == 0)
? nullptr
- : reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+ : reinterpret_cast<GcRoot<CallSite>*>(raw_arrays + layout.CallSitesOffset());
DCHECK_ALIGNED(raw_arrays, alignof(StringDexCacheType)) <<
"Expected raw_arrays to align to StringDexCacheType.";
@@ -125,8 +129,9 @@
CHECK_EQ(types[i].load(std::memory_order_relaxed).index, 0u);
CHECK(types[i].load(std::memory_order_relaxed).object.IsNull());
}
- for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
- CHECK(GetElementPtrSize(methods, i, image_pointer_size) == nullptr);
+ for (size_t i = 0; i < num_methods; ++i) {
+ CHECK_EQ(GetNativePairPtrSize(methods, i, image_pointer_size).index, 0u);
+ CHECK(GetNativePairPtrSize(methods, i, image_pointer_size).object == nullptr);
}
for (size_t i = 0; i < num_fields; ++i) {
CHECK_EQ(GetNativePairPtrSize(fields, i, image_pointer_size).index, 0u);
@@ -149,6 +154,9 @@
if (fields != nullptr) {
mirror::FieldDexCachePair::Initialize(fields, image_pointer_size);
}
+ if (methods != nullptr) {
+ mirror::MethodDexCachePair::Initialize(methods, image_pointer_size);
+ }
if (method_types != nullptr) {
mirror::MethodTypeDexCachePair::Initialize(method_types);
}
@@ -159,14 +167,13 @@
types,
num_types,
methods,
- dex_file->NumMethodIds(),
+ num_methods,
fields,
num_fields,
method_types,
num_method_types,
call_sites,
- dex_file->NumCallSiteIds(),
- image_pointer_size);
+ dex_file->NumCallSiteIds());
}
void DexCache::Init(const DexFile* dex_file,
@@ -175,15 +182,14 @@
uint32_t num_strings,
TypeDexCacheType* resolved_types,
uint32_t num_resolved_types,
- ArtMethod** resolved_methods,
+ MethodDexCacheType* resolved_methods,
uint32_t num_resolved_methods,
FieldDexCacheType* resolved_fields,
uint32_t num_resolved_fields,
MethodTypeDexCacheType* resolved_method_types,
uint32_t num_resolved_method_types,
GcRoot<CallSite>* resolved_call_sites,
- uint32_t num_resolved_call_sites,
- PointerSize pointer_size) {
+ uint32_t num_resolved_call_sites) {
CHECK(dex_file != nullptr);
CHECK(location != nullptr);
CHECK_EQ(num_strings != 0u, strings != nullptr);
@@ -207,31 +213,13 @@
SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types);
SetField32<false>(NumResolvedCallSitesOffset(), num_resolved_call_sites);
-
- Runtime* const runtime = Runtime::Current();
- if (runtime->HasResolutionMethod()) {
- // Initialize the resolve methods array to contain trampolines for resolution.
- Fixup(runtime->GetResolutionMethod(), pointer_size);
- }
-}
-
-void DexCache::Fixup(ArtMethod* trampoline, PointerSize pointer_size) {
- // Fixup the resolve methods array to contain trampoline for resolution.
- CHECK(trampoline != nullptr);
- CHECK(trampoline->IsRuntimeMethod());
- auto* resolved_methods = GetResolvedMethods();
- for (size_t i = 0, length = NumResolvedMethods(); i < length; i++) {
- if (GetElementPtrSize<ArtMethod*>(resolved_methods, i, pointer_size) == nullptr) {
- SetElementPtrSize(resolved_methods, i, trampoline, pointer_size);
- }
- }
}
void DexCache::SetLocation(ObjPtr<mirror::String> location) {
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);
}
-#if !defined(__aarch64__) && !defined(__x86_64__)
+#if !defined(__aarch64__) && !defined(__x86_64__) && !defined(__mips__)
static pthread_mutex_t dex_cache_slow_atomic_mutex = PTHREAD_MUTEX_INITIALIZER;
DexCache::ConversionPair64 DexCache::AtomicLoadRelaxed16B(std::atomic<ConversionPair64>* target) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index cf570b8..f75786b 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -129,6 +129,9 @@
using FieldDexCachePair = NativeDexCachePair<ArtField>;
using FieldDexCacheType = std::atomic<FieldDexCachePair>;
+using MethodDexCachePair = NativeDexCachePair<ArtMethod>;
+using MethodDexCacheType = std::atomic<MethodDexCachePair>;
+
using MethodTypeDexCachePair = DexCachePair<MethodType>;
using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
@@ -153,6 +156,11 @@
static_assert(IsPowerOfTwo(kDexCacheFieldCacheSize),
"Field dex cache size is not a power of 2.");
+ // Size of method dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
+ static constexpr size_t kDexCacheMethodCacheSize = 1024;
+ static_assert(IsPowerOfTwo(kDexCacheMethodCacheSize),
+ "Method dex cache size is not a power of 2.");
+
// Size of method type dex cache. Needs to be a power of 2 for entrypoint assumptions
// to hold.
static constexpr size_t kDexCacheMethodTypeCacheSize = 1024;
@@ -171,6 +179,10 @@
return kDexCacheFieldCacheSize;
}
+ static constexpr size_t StaticMethodSize() {
+ return kDexCacheMethodCacheSize;
+ }
+
static constexpr size_t StaticMethodTypeSize() {
return kDexCacheMethodTypeCacheSize;
}
@@ -189,9 +201,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::dex_lock_);
- void Fixup(ArtMethod* trampoline, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
void FixupStrings(StringDexCacheType* dest, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -284,6 +293,8 @@
ArtMethod* resolved,
PointerSize ptr_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ ALWAYS_INLINE void ClearResolvedMethod(uint32_t method_idx, PointerSize ptr_size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Pointer sized variant, used for patching.
ALWAYS_INLINE ArtField* GetResolvedField(uint32_t idx, PointerSize ptr_size)
@@ -328,11 +339,11 @@
SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types);
}
- ArtMethod** GetResolvedMethods() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<ArtMethod**>(ResolvedMethodsOffset());
+ MethodDexCacheType* GetResolvedMethods() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldPtr<MethodDexCacheType*>(ResolvedMethodsOffset());
}
- void SetResolvedMethods(ArtMethod** resolved_methods)
+ void SetResolvedMethods(MethodDexCacheType* resolved_methods)
ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_) {
SetFieldPtr<false>(ResolvedMethodsOffset(), resolved_methods);
@@ -429,6 +440,7 @@
uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
private:
@@ -438,15 +450,14 @@
uint32_t num_strings,
TypeDexCacheType* resolved_types,
uint32_t num_resolved_types,
- ArtMethod** resolved_methods,
+ MethodDexCacheType* resolved_methods,
uint32_t num_resolved_methods,
FieldDexCacheType* resolved_fields,
uint32_t num_resolved_fields,
MethodTypeDexCacheType* resolved_method_types,
uint32_t num_resolved_method_types,
GcRoot<CallSite>* resolved_call_sites,
- uint32_t num_resolved_call_sites,
- PointerSize pointer_size)
+ uint32_t num_resolved_call_sites)
REQUIRES_SHARED(Locks::mutator_lock_);
// std::pair<> is not trivially copyable and as such it is unsuitable for atomic operations,
@@ -471,8 +482,8 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
// Due to lack of 16-byte atomics support, we use hand-crafted routines.
-#if defined(__aarch64__)
- // 16-byte atomics are supported on aarch64.
+#if defined(__aarch64__) || defined(__mips__)
+ // 16-byte atomics are supported on aarch64, mips and mips64.
ALWAYS_INLINE static ConversionPair64 AtomicLoadRelaxed16B(
std::atomic<ConversionPair64>* target) {
return target->load(std::memory_order_relaxed);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 194d9bc..a3d9035 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -21,10 +21,10 @@
#include "art_method-inl.h"
#include "class_linker.h"
#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
#include "linear_alloc.h"
#include "mirror/class_loader-inl.h"
#include "mirror/dex_cache-inl.h"
-#include "handle_scope-inl.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
@@ -54,7 +54,8 @@
|| java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings());
EXPECT_TRUE(dex_cache->StaticTypeSize() == dex_cache->NumResolvedTypes()
|| java_lang_dex_file_->NumTypeIds() == dex_cache->NumResolvedTypes());
- EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
+ EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods()
+ || java_lang_dex_file_->NumMethodIds() == dex_cache->NumResolvedMethods());
EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields()
|| java_lang_dex_file_->NumFieldIds() == dex_cache->NumResolvedFields());
EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index be0eac0..a6129cc 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -19,8 +19,8 @@
#include "class-inl.h"
#include "gc_root-inl.h"
#include "jvalue-inl.h"
-#include "method_handles.h"
#include "method_handles-inl.h"
+#include "method_handles.h"
#include "reflection-inl.h"
namespace art {
diff --git a/runtime/mirror/executable.cc b/runtime/mirror/executable.cc
index 17c16a2..fac3319 100644
--- a/runtime/mirror/executable.cc
+++ b/runtime/mirror/executable.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "art_method-inl.h"
#include "executable.h"
+#include "art_method-inl.h"
+
namespace art {
namespace mirror {
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index d33df5c..ad48202 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -20,7 +20,8 @@
#include "field.h"
#include "art_field-inl.h"
-#include "mirror/dex_cache-inl.h"
+#include "class-inl.h"
+#include "dex_cache-inl.h"
namespace art {
@@ -87,6 +88,10 @@
SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, type_), type);
}
+inline Primitive::Type Field::GetTypeAsPrimitiveType() {
+ return GetType()->GetPrimitiveType();
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index 54034c2..b4d93b6 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -18,8 +18,8 @@
#include "class-inl.h"
#include "dex_cache-inl.h"
-#include "object_array-inl.h"
#include "object-inl.h"
+#include "object_array-inl.h"
namespace art {
namespace mirror {
diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h
index 40186a6..6845575 100644
--- a/runtime/mirror/field.h
+++ b/runtime/mirror/field.h
@@ -20,8 +20,10 @@
#include "accessible_object.h"
#include "base/enums.h"
#include "gc_root.h"
+#include "modifiers.h"
#include "obj_ptr.h"
#include "object.h"
+#include "primitive.h"
#include "read_barrier_option.h"
namespace art {
@@ -69,10 +71,7 @@
return (GetAccessFlags() & kAccVolatile) != 0;
}
- ALWAYS_INLINE Primitive::Type GetTypeAsPrimitiveType()
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetType()->GetPrimitiveType();
- }
+ ALWAYS_INLINE Primitive::Type GetTypeAsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_);
mirror::Class* GetType() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldObject<mirror::Class>(OFFSET_OF_OBJECT_MEMBER(Field, type_));
diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h
index 205ea7a..61332e3 100644
--- a/runtime/mirror/method.h
+++ b/runtime/mirror/method.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_MIRROR_METHOD_H_
#define ART_RUNTIME_MIRROR_METHOD_H_
-#include "gc_root.h"
#include "executable.h"
+#include "gc_root.h"
namespace art {
namespace mirror {
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index c598fa3..f362d43 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -21,8 +21,8 @@
#include "art_method.h"
#include "class.h"
#include "gc_root.h"
-#include "object.h"
#include "method_type.h"
+#include "object.h"
namespace art {
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
index 9eada6d..a390a2e 100644
--- a/runtime/mirror/method_handles_lookup.cc
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -18,11 +18,11 @@
#include "class-inl.h"
#include "gc_root-inl.h"
-#include "object-inl.h"
#include "handle_scope.h"
#include "jni_internal.h"
#include "mirror/method_handle_impl.h"
#include "modifiers.h"
+#include "object-inl.h"
#include "well_known_classes.h"
namespace art {
diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h
index 2109f60..dd8d45e 100644
--- a/runtime/mirror/method_handles_lookup.h
+++ b/runtime/mirror/method_handles_lookup.h
@@ -17,10 +17,10 @@
#ifndef ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
#define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
-#include "obj_ptr.h"
#include "gc_root.h"
-#include "object.h"
#include "handle.h"
+#include "obj_ptr.h"
+#include "object.h"
#include "utils.h"
namespace art {
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 374bbe5..a9f3c9c 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -17,9 +17,9 @@
#ifndef ART_RUNTIME_MIRROR_METHOD_TYPE_H_
#define ART_RUNTIME_MIRROR_METHOD_TYPE_H_
+#include "object_array.h"
#include "object.h"
#include "string.h"
-#include "mirror/object_array.h"
#include "utils.h"
namespace art {
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 43d70b7..6eb200d 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -19,20 +19,20 @@
#include "object.h"
+#include "array-inl.h"
#include "art_field.h"
#include "art_method.h"
#include "atomic.h"
-#include "array-inl.h"
#include "class-inl.h"
#include "class_flags.h"
#include "class_linker.h"
#include "dex_cache.h"
#include "lock_word-inl.h"
#include "monitor.h"
+#include "obj_ptr-inl.h"
+#include "object-readbarrier-inl.h"
#include "object_array-inl.h"
#include "object_reference-inl.h"
-#include "object-readbarrier-inl.h"
-#include "obj_ptr-inl.h"
#include "read_barrier-inl.h"
#include "reference.h"
#include "runtime.h"
@@ -393,14 +393,6 @@
}
template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
-inline uint8_t Object::GetFieldBoolean(MemberOffset field_offset) {
- if (kVerifyFlags & kVerifyThis) {
- VerifyObject(this);
- }
- return GetField<uint8_t, kIsVolatile>(field_offset);
-}
-
-template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
inline int8_t Object::GetFieldByte(MemberOffset field_offset) {
if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
@@ -724,11 +716,10 @@
}
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
HeapReference<T>* objref_addr = reinterpret_cast<HeapReference<T>*>(raw_addr);
- T* result = ReadBarrier::Barrier<T, kReadBarrierOption>(this, field_offset, objref_addr);
- if (kIsVolatile) {
- // TODO: Refactor to use a SequentiallyConsistent load instead.
- QuasiAtomic::ThreadFenceAcquire(); // Ensure visibility of operations preceding store.
- }
+ T* result = ReadBarrier::Barrier<T, kIsVolatile, kReadBarrierOption>(
+ this,
+ field_offset,
+ objref_addr);
if (kVerifyFlags & kVerifyReads) {
VerifyObject(result);
}
@@ -764,15 +755,7 @@
}
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
HeapReference<Object>* objref_addr = reinterpret_cast<HeapReference<Object>*>(raw_addr);
- if (kIsVolatile) {
- // TODO: Refactor to use a SequentiallyConsistent store instead.
- QuasiAtomic::ThreadFenceRelease(); // Ensure that prior accesses are visible before store.
- objref_addr->Assign(new_value.Ptr());
- QuasiAtomic::ThreadFenceSequentiallyConsistent();
- // Ensure this store occurs before any volatile loads.
- } else {
- objref_addr->Assign(new_value.Ptr());
- }
+ objref_addr->Assign<kIsVolatile>(new_value.Ptr());
}
template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
@@ -843,13 +826,12 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
}
- HeapReference<Object> old_ref(HeapReference<Object>::FromObjPtr(old_value));
- HeapReference<Object> new_ref(HeapReference<Object>::FromObjPtr(new_value));
+ uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value));
+ uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value));
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
- bool success = atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_ref.reference_,
- new_ref.reference_);
+ bool success = atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_ref, new_ref);
return success;
}
@@ -885,13 +867,12 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
}
- HeapReference<Object> old_ref(HeapReference<Object>::FromObjPtr(old_value));
- HeapReference<Object> new_ref(HeapReference<Object>::FromObjPtr(new_value));
+ uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value));
+ uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value));
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
- bool success = atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_ref.reference_,
- new_ref.reference_);
+ bool success = atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref);
return success;
}
@@ -915,13 +896,12 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
}
- HeapReference<Object> old_ref(HeapReference<Object>::FromObjPtr(old_value));
- HeapReference<Object> new_ref(HeapReference<Object>::FromObjPtr(new_value));
+ uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value));
+ uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value));
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
- bool success = atomic_addr->CompareExchangeWeakRelaxed(old_ref.reference_,
- new_ref.reference_);
+ bool success = atomic_addr->CompareExchangeWeakRelaxed(old_ref, new_ref);
return success;
}
@@ -945,13 +925,12 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
}
- HeapReference<Object> old_ref(HeapReference<Object>::FromObjPtr(old_value));
- HeapReference<Object> new_ref(HeapReference<Object>::FromObjPtr(new_value));
+ uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value));
+ uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value));
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
- bool success = atomic_addr->CompareExchangeWeakRelease(old_ref.reference_,
- new_ref.reference_);
+ bool success = atomic_addr->CompareExchangeWeakRelease(old_ref, new_ref);
return success;
}
diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h
index 69365af..f076940 100644
--- a/runtime/mirror/object-readbarrier-inl.h
+++ b/runtime/mirror/object-readbarrier-inl.h
@@ -211,13 +211,12 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
}
- HeapReference<Object> old_ref(HeapReference<Object>::FromObjPtr(old_value));
- HeapReference<Object> new_ref(HeapReference<Object>::FromObjPtr(new_value));
+ uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value));
+ uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value));
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
- bool success = atomic_addr->CompareExchangeStrongRelaxed(old_ref.reference_,
- new_ref.reference_);
+ bool success = atomic_addr->CompareExchangeStrongRelaxed(old_ref, new_ref);
return success;
}
@@ -241,13 +240,12 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
}
- HeapReference<Object> old_ref(HeapReference<Object>::FromObjPtr(old_value));
- HeapReference<Object> new_ref(HeapReference<Object>::FromObjPtr(new_value));
+ uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value));
+ uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value));
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
- bool success = atomic_addr->CompareExchangeStrongRelease(old_ref.reference_,
- new_ref.reference_);
+ bool success = atomic_addr->CompareExchangeStrongRelease(old_ref, new_ref);
return success;
}
diff --git a/runtime/mirror/object-refvisitor-inl.h b/runtime/mirror/object-refvisitor-inl.h
index f5ab4dd..39e32bf 100644
--- a/runtime/mirror/object-refvisitor-inl.h
+++ b/runtime/mirror/object-refvisitor-inl.h
@@ -19,8 +19,8 @@
#include "object-inl.h"
-#include "class_loader-inl.h"
#include "class-refvisitor-inl.h"
+#include "class_loader-inl.h"
#include "dex_cache-inl.h"
namespace art {
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 6e5fdb7..78ef339 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -18,22 +18,22 @@
#include "object.h"
-#include "art_field.h"
-#include "art_field-inl.h"
#include "array-inl.h"
-#include "class.h"
+#include "art_field-inl.h"
+#include "art_field.h"
#include "class-inl.h"
+#include "class.h"
#include "class_linker-inl.h"
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/heap.h"
+#include "handle_scope-inl.h"
#include "iftable-inl.h"
#include "monitor.h"
#include "object-inl.h"
#include "object-refvisitor-inl.h"
#include "object_array-inl.h"
#include "runtime.h"
-#include "handle_scope-inl.h"
#include "throwable.h"
#include "well_known_classes.h"
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 886780f..aedcd66 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -380,7 +380,12 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
ALWAYS_INLINE uint8_t GetFieldBoolean(MemberOffset field_offset)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ return GetField<uint8_t, kIsVolatile>(field_offset);
+ }
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
ALWAYS_INLINE int8_t GetFieldByte(MemberOffset field_offset)
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index dbec40c..5cfc987 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -26,10 +26,10 @@
#include "array-inl.h"
#include "class.h"
#include "gc/heap.h"
-#include "object-inl.h"
-#include "obj_ptr-inl.h"
-#include "runtime.h"
#include "handle_scope-inl.h"
+#include "obj_ptr-inl.h"
+#include "object-inl.h"
+#include "runtime.h"
#include "thread.h"
#include "utils.h"
diff --git a/runtime/mirror/object_reference-inl.h b/runtime/mirror/object_reference-inl.h
index 22fb83c..60f3ce1 100644
--- a/runtime/mirror/object_reference-inl.h
+++ b/runtime/mirror/object_reference-inl.h
@@ -30,17 +30,10 @@
}
template<class MirrorType>
-HeapReference<MirrorType> HeapReference<MirrorType>::FromObjPtr(ObjPtr<MirrorType> ptr) {
- return HeapReference<MirrorType>(ptr.Ptr());
-}
-
-template<class MirrorType>
bool HeapReference<MirrorType>::CasWeakRelaxed(MirrorType* expected_ptr, MirrorType* new_ptr) {
- HeapReference<Object> expected_ref(HeapReference<Object>::FromMirrorPtr(expected_ptr));
- HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_ptr));
- Atomic<uint32_t>* atomic_reference = reinterpret_cast<Atomic<uint32_t>*>(&this->reference_);
- return atomic_reference->CompareExchangeWeakRelaxed(expected_ref.reference_,
- new_ref.reference_);
+ return reference_.CompareExchangeWeakRelaxed(
+ Compression::Compress(expected_ptr),
+ Compression::Compress(new_ptr));
}
} // namespace mirror
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index a96a120..c62ee6c 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_
#define ART_RUNTIME_MIRROR_OBJECT_REFERENCE_H_
+#include "atomic.h"
#include "base/mutex.h" // For Locks::mutator_lock_.
#include "globals.h"
#include "obj_ptr.h"
@@ -30,20 +31,43 @@
// extra platform specific padding.
#define MANAGED PACKED(4)
+template<bool kPoisonReferences, class MirrorType>
+class PtrCompression {
+ public:
+ // Compress reference to its bit representation.
+ static uint32_t Compress(MirrorType* mirror_ptr) {
+ uintptr_t as_bits = reinterpret_cast<uintptr_t>(mirror_ptr);
+ return static_cast<uint32_t>(kPoisonReferences ? -as_bits : as_bits);
+ }
+
+ // Uncompress an encoded reference from its bit representation.
+ static MirrorType* Decompress(uint32_t ref) {
+ uintptr_t as_bits = kPoisonReferences ? -ref : ref;
+ return reinterpret_cast<MirrorType*>(as_bits);
+ }
+
+ // Convert an ObjPtr to a compressed reference.
+ static uint32_t Compress(ObjPtr<MirrorType> ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return Compress(ptr.Ptr());
+ }
+};
+
// Value type representing a reference to a mirror::Object of type MirrorType.
template<bool kPoisonReferences, class MirrorType>
class MANAGED ObjectReference {
+ private:
+ using Compression = PtrCompression<kPoisonReferences, MirrorType>;
+
public:
- MirrorType* AsMirrorPtr() const REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnCompress();
+ MirrorType* AsMirrorPtr() const {
+ return Compression::Decompress(reference_);
}
- void Assign(MirrorType* other) REQUIRES_SHARED(Locks::mutator_lock_) {
- reference_ = Compress(other);
+ void Assign(MirrorType* other) {
+ reference_ = Compression::Compress(other);
}
- void Assign(ObjPtr<MirrorType> ptr)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ void Assign(ObjPtr<MirrorType> ptr) REQUIRES_SHARED(Locks::mutator_lock_);
void Clear() {
reference_ = 0;
@@ -58,48 +82,71 @@
return reference_;
}
+ static ObjectReference<kPoisonReferences, MirrorType> FromMirrorPtr(MirrorType* mirror_ptr)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ObjectReference<kPoisonReferences, MirrorType>(mirror_ptr);
+ }
+
protected:
- explicit ObjectReference(MirrorType* mirror_ptr)
- REQUIRES_SHARED(Locks::mutator_lock_)
- : reference_(Compress(mirror_ptr)) {
+ explicit ObjectReference(MirrorType* mirror_ptr) REQUIRES_SHARED(Locks::mutator_lock_)
+ : reference_(Compression::Compress(mirror_ptr)) {
}
- // Compress reference to its bit representation.
- static uint32_t Compress(MirrorType* mirror_ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
- uintptr_t as_bits = reinterpret_cast<uintptr_t>(mirror_ptr);
- return static_cast<uint32_t>(kPoisonReferences ? -as_bits : as_bits);
- }
-
- // Uncompress an encoded reference from its bit representation.
- MirrorType* UnCompress() const REQUIRES_SHARED(Locks::mutator_lock_) {
- uintptr_t as_bits = kPoisonReferences ? -reference_ : reference_;
- return reinterpret_cast<MirrorType*>(as_bits);
- }
-
- friend class Object;
-
// The encoded reference to a mirror::Object.
uint32_t reference_;
};
// References between objects within the managed heap.
+// Similar API to ObjectReference, but not a value type. Supports atomic access.
template<class MirrorType>
-class MANAGED HeapReference : public ObjectReference<kPoisonHeapReferences, MirrorType> {
+class MANAGED HeapReference {
+ private:
+ using Compression = PtrCompression<kPoisonHeapReferences, MirrorType>;
+
public:
+ HeapReference() REQUIRES_SHARED(Locks::mutator_lock_) : HeapReference(nullptr) {}
+
+ template <bool kIsVolatile = false>
+ MirrorType* AsMirrorPtr() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return Compression::Decompress(
+ kIsVolatile ? reference_.LoadSequentiallyConsistent() : reference_.LoadJavaData());
+ }
+
+ template <bool kIsVolatile = false>
+ void Assign(MirrorType* other) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kIsVolatile) {
+ reference_.StoreSequentiallyConsistent(Compression::Compress(other));
+ } else {
+ reference_.StoreJavaData(Compression::Compress(other));
+ }
+ }
+
+ template <bool kIsVolatile = false>
+ void Assign(ObjPtr<MirrorType> ptr) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void Clear() {
+ reference_.StoreJavaData(0);
+ DCHECK(IsNull());
+ }
+
+ bool IsNull() const {
+ return reference_.LoadJavaData() == 0;
+ }
+
static HeapReference<MirrorType> FromMirrorPtr(MirrorType* mirror_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
return HeapReference<MirrorType>(mirror_ptr);
}
- static HeapReference<MirrorType> FromObjPtr(ObjPtr<MirrorType> ptr)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
bool CasWeakRelaxed(MirrorType* old_ptr, MirrorType* new_ptr)
REQUIRES_SHARED(Locks::mutator_lock_);
private:
explicit HeapReference(MirrorType* mirror_ptr) REQUIRES_SHARED(Locks::mutator_lock_)
- : ObjectReference<kPoisonHeapReferences, MirrorType>(mirror_ptr) {}
+ : reference_(Compression::Compress(mirror_ptr)) {}
+
+ // The encoded reference to a mirror::Object. Atomically updateable.
+ Atomic<uint32_t> reference_;
};
static_assert(sizeof(mirror::HeapReference<mirror::Object>) == kHeapReferenceSize,
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 6230ae9..1a0fc76 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -26,8 +26,8 @@
#include "asm_support.h"
#include "base/enums.h"
#include "class-inl.h"
-#include "class_linker.h"
#include "class_linker-inl.h"
+#include "class_linker.h"
#include "common_runtime_test.h"
#include "dex_file.h"
#include "entrypoints/entrypoint_utils-inl.h"
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index 53de821..bb3242e 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -16,12 +16,12 @@
#include "stack_trace_element.h"
-#include "class.h"
#include "class-inl.h"
+#include "class.h"
#include "gc/accounting/card_table-inl.h"
#include "gc_root-inl.h"
-#include "object-inl.h"
#include "handle_scope-inl.h"
+#include "object-inl.h"
#include "string.h"
namespace art {
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 7fbe8bd..545fe93 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -17,8 +17,8 @@
#ifndef ART_RUNTIME_MIRROR_STRING_H_
#define ART_RUNTIME_MIRROR_STRING_H_
-#include "gc_root.h"
#include "gc/allocator_type.h"
+#include "gc_root.h"
#include "object.h"
namespace art {
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index aee4b19..077ad50 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -24,8 +24,8 @@
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "object-inl.h"
-#include "object_array.h"
#include "object_array-inl.h"
+#include "object_array.h"
#include "stack_trace_element.h"
#include "string.h"
#include "utils.h"
diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc
index 48e9a6b..9d221cc 100644
--- a/runtime/monitor_pool.cc
+++ b/runtime/monitor_pool.cc
@@ -18,8 +18,8 @@
#include "base/logging.h"
#include "base/mutex-inl.h"
-#include "thread-current-inl.h"
#include "monitor.h"
+#include "thread-current-inl.h"
namespace art {
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index 27ce149..7d89652 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "barrier.h"
#include "monitor.h"
#include <string>
#include "atomic.h"
+#include "barrier.h"
#include "base/time_utils.h"
#include "class_linker-inl.h"
#include "common_runtime_test.h"
@@ -36,11 +36,8 @@
protected:
void SetUpRuntimeOptions(RuntimeOptions *options) OVERRIDE {
// Use a smaller heap
- for (std::pair<std::string, const void*>& pair : *options) {
- if (pair.first.find("-Xmx") == 0) {
- pair.first = "-Xmx4M"; // Smallest we can go.
- }
- }
+ SetUpRuntimeOptionsForFillHeap(options);
+
options->push_back(std::make_pair("-Xint", nullptr));
}
public:
@@ -56,52 +53,6 @@
bool completed_;
};
-// Fill the heap.
-static const size_t kMaxHandles = 1000000; // Use arbitrary large amount for now.
-static void FillHeap(Thread* self, ClassLinker* class_linker,
- std::unique_ptr<StackHandleScope<kMaxHandles>>* hsp,
- std::vector<MutableHandle<mirror::Object>>* handles)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- Runtime::Current()->GetHeap()->SetIdealFootprint(1 * GB);
-
- hsp->reset(new StackHandleScope<kMaxHandles>(self));
- // Class java.lang.Object.
- Handle<mirror::Class> c((*hsp)->NewHandle(class_linker->FindSystemClass(self,
- "Ljava/lang/Object;")));
- // Array helps to fill memory faster.
- Handle<mirror::Class> ca((*hsp)->NewHandle(class_linker->FindSystemClass(self,
- "[Ljava/lang/Object;")));
-
- // Start allocating with 128K
- size_t length = 128 * KB / 4;
- while (length > 10) {
- MutableHandle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>(
- mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), length / 4)));
- if (self->IsExceptionPending() || h == nullptr) {
- self->ClearException();
-
- // Try a smaller length
- length = length / 8;
- // Use at most half the reported free space.
- size_t mem = Runtime::Current()->GetHeap()->GetFreeMemory();
- if (length * 8 > mem) {
- length = mem / 8;
- }
- } else {
- handles->push_back(h);
- }
- }
-
- // Allocate simple objects till it fails.
- while (!self->IsExceptionPending()) {
- MutableHandle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self));
- if (!self->IsExceptionPending() && h != nullptr) {
- handles->push_back(h);
- }
- }
- self->ClearException();
-}
-
// Check that an exception can be thrown correctly.
// This test is potentially racy, but the timeout is long enough that it should work.
@@ -304,16 +255,12 @@
test->complete_barrier_ = std::unique_ptr<Barrier>(new Barrier(3));
test->completed_ = false;
- // Fill the heap.
- std::unique_ptr<StackHandleScope<kMaxHandles>> hsp;
- std::vector<MutableHandle<mirror::Object>> handles;
-
// Our job: Fill the heap, then try Wait.
- FillHeap(soa.Self(), class_linker, &hsp, &handles);
+ {
+ VariableSizedHandleScope vhs(soa.Self());
+ test->FillHeap(soa.Self(), class_linker, &vhs);
- // Now release everything.
- for (MutableHandle<mirror::Object>& h : handles) {
- h.Assign(nullptr);
+ // Now release everything.
}
// Need to drop the mutator lock to allow barriers.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index f6a8360..07dfb65 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -458,7 +458,8 @@
const char* filename,
const char* instruction_set,
const char* compiler_filter_name,
- bool profile_changed) {
+ bool profile_changed,
+ bool downgrade) {
if ((filename == nullptr) || !OS::FileExists(filename)) {
LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
@@ -492,7 +493,7 @@
if (oat_file_assistant.IsInBootClassPath()) {
return OatFileAssistant::kNoDexOptNeeded;
}
- return oat_file_assistant.GetDexOptNeeded(filter, profile_changed);
+ return oat_file_assistant.GetDexOptNeeded(filter, profile_changed, downgrade);
}
static jstring DexFile_getDexFileStatus(JNIEnv* env,
@@ -528,7 +529,8 @@
jstring javaFilename,
jstring javaInstructionSet,
jstring javaTargetCompilerFilter,
- jboolean newProfile) {
+ jboolean newProfile,
+ jboolean downgrade) {
ScopedUtfChars filename(env, javaFilename);
if (env->ExceptionCheck()) {
return -1;
@@ -548,7 +550,8 @@
filename.c_str(),
instruction_set.c_str(),
target_compiler_filter.c_str(),
- newProfile == JNI_TRUE);
+ newProfile == JNI_TRUE,
+ downgrade == JNI_TRUE);
}
// public API
@@ -725,7 +728,7 @@
NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile, getDexOptNeeded,
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"),
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
NATIVE_METHOD(DexFile, openDexFileNative,
"(Ljava/lang/String;"
"Ljava/lang/String;"
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index e6e55a2..4db9feb 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -17,8 +17,8 @@
#include "dalvik_system_VMRuntime.h"
#ifdef ART_TARGET_ANDROID
-#include <sys/time.h>
#include <sys/resource.h>
+#include <sys/time.h>
extern "C" void android_set_application_target_sdk_version(uint32_t version);
#endif
#include <limits.h>
@@ -31,8 +31,8 @@
#include "android-base/stringprintf.h"
-#include "art_method-inl.h"
#include "arch/instruction_set.h"
+#include "art_method-inl.h"
#include "base/enums.h"
#include "class_linker-inl.h"
#include "common_throws.h"
@@ -298,15 +298,16 @@
// Based on ClassLinker::ResolveString.
static void PreloadDexCachesResolveString(
- Handle<mirror::DexCache> dex_cache, dex::StringIndex string_idx, StringTable& strings)
+ ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx, StringTable& strings)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::String> string = dex_cache->GetResolvedString(string_idx);
- if (string != nullptr) {
- return;
+ uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx);
+ auto pair = dex_cache->GetStrings()[slot_idx].load(std::memory_order_relaxed);
+ if (!pair.object.IsNull()) {
+ return; // The entry already contains some String.
}
const DexFile* dex_file = dex_cache->GetDexFile();
const char* utf8 = dex_file->StringDataByIdx(string_idx);
- string = strings[utf8];
+ ObjPtr<mirror::String> string = strings[utf8];
if (string == nullptr) {
return;
}
@@ -319,18 +320,17 @@
ObjPtr<mirror::DexCache> dex_cache,
dex::TypeIndex type_idx)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(type_idx);
- if (klass != nullptr) {
- return;
+ uint32_t slot_idx = dex_cache->TypeSlotIndex(type_idx);
+ auto pair = dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed);
+ if (!pair.object.IsNull()) {
+ return; // The entry already contains some Class.
}
const DexFile* dex_file = dex_cache->GetDexFile();
const char* class_name = dex_file->StringByTypeIdx(type_idx);
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- if (class_name[1] == '\0') {
- klass = linker->FindPrimitiveClass(class_name[0]);
- } else {
- klass = linker->LookupClass(self, class_name, nullptr);
- }
+ ObjPtr<mirror::Class> klass = (class_name[1] == '\0')
+ ? linker->FindPrimitiveClass(class_name[0])
+ : linker->LookupClass(self, class_name, nullptr);
if (klass == nullptr) {
return;
}
@@ -345,26 +345,27 @@
}
// Based on ClassLinker::ResolveField.
-static void PreloadDexCachesResolveField(Handle<mirror::DexCache> dex_cache, uint32_t field_idx,
+static void PreloadDexCachesResolveField(ObjPtr<mirror::DexCache> dex_cache,
+ uint32_t field_idx,
bool is_static)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* field = dex_cache->GetResolvedField(field_idx, kRuntimePointerSize);
- if (field != nullptr) {
- return;
+ uint32_t slot_idx = dex_cache->FieldSlotIndex(field_idx);
+ auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedFields(),
+ slot_idx,
+ kRuntimePointerSize);
+ if (pair.object != nullptr) {
+ return; // The entry already contains some ArtField.
}
const DexFile* dex_file = dex_cache->GetDexFile();
const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx);
- Thread* const self = Thread::Current();
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> klass(hs.NewHandle(dex_cache->GetResolvedType(field_id.class_idx_)));
+ ObjPtr<mirror::Class> klass =
+ ClassLinker::LookupResolvedType(field_id.class_idx_, dex_cache, nullptr);
if (klass == nullptr) {
return;
}
- if (is_static) {
- field = mirror::Class::FindStaticField(self, klass.Get(), dex_cache.Get(), field_idx);
- } else {
- field = klass->FindInstanceField(dex_cache.Get(), field_idx);
- }
+ ArtField* field = is_static
+ ? mirror::Class::FindStaticField(Thread::Current(), klass, dex_cache, field_idx)
+ : klass->FindInstanceField(dex_cache, field_idx);
if (field == nullptr) {
return;
}
@@ -372,24 +373,25 @@
}
// Based on ClassLinker::ResolveMethod.
-static void PreloadDexCachesResolveMethod(Handle<mirror::DexCache> dex_cache, uint32_t method_idx)
+static void PreloadDexCachesResolveMethod(ObjPtr<mirror::DexCache> dex_cache, uint32_t method_idx)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtMethod* method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize);
- if (method != nullptr) {
- return;
+ uint32_t slot_idx = dex_cache->MethodSlotIndex(method_idx);
+ auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedMethods(),
+ slot_idx,
+ kRuntimePointerSize);
+ if (pair.object != nullptr) {
+ return; // The entry already contains some ArtMethod.
}
const DexFile* dex_file = dex_cache->GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx);
ObjPtr<mirror::Class> klass =
- ClassLinker::LookupResolvedType(method_id.class_idx_, dex_cache.Get(), nullptr);
+ ClassLinker::LookupResolvedType(method_id.class_idx_, dex_cache, nullptr);
if (klass == nullptr) {
return;
}
- if (klass->IsInterface()) {
- method = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, kRuntimePointerSize);
- } else {
- method = klass->FindClassMethod(dex_cache.Get(), method_idx, kRuntimePointerSize);
- }
+ ArtMethod* method = klass->IsInterface()
+ ? klass->FindInterfaceMethod(dex_cache, method_idx, kRuntimePointerSize)
+ : klass->FindClassMethod(dex_cache, method_idx, kRuntimePointerSize);
if (method == nullptr) {
return;
}
@@ -451,27 +453,31 @@
}
ObjPtr<mirror::DexCache> const dex_cache = class_linker->FindDexCache(self, *dex_file);
DCHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded.
- for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
- ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(j));
- if (string != nullptr) {
+ for (size_t j = 0, num_strings = dex_cache->NumStrings(); j < num_strings; ++j) {
+ auto pair = dex_cache->GetStrings()[j].load(std::memory_order_relaxed);
+ if (!pair.object.IsNull()) {
filled->num_strings++;
}
}
- for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) {
- ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(j));
- if (klass != nullptr) {
+ for (size_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; ++j) {
+ auto pair = dex_cache->GetResolvedTypes()[j].load(std::memory_order_relaxed);
+ if (!pair.object.IsNull()) {
filled->num_types++;
}
}
- for (size_t j = 0; j < dex_cache->NumResolvedFields(); j++) {
- ArtField* field = dex_cache->GetResolvedField(j, class_linker->GetImagePointerSize());
- if (field != nullptr) {
+ for (size_t j = 0, num_fields = dex_cache->NumResolvedFields(); j < num_fields; ++j) {
+ auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedFields(),
+ j,
+ kRuntimePointerSize);
+ if (pair.object != nullptr) {
filled->num_fields++;
}
}
- for (size_t j = 0; j < dex_cache->NumResolvedMethods(); j++) {
- ArtMethod* method = dex_cache->GetResolvedMethod(j, kRuntimePointerSize);
- if (method != nullptr) {
+ for (size_t j = 0, num_methods = dex_cache->NumResolvedMethods(); j < num_methods; ++j) {
+ auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedMethods(),
+ j,
+ kRuntimePointerSize);
+ if (pair.object != nullptr) {
filled->num_methods++;
}
}
@@ -511,8 +517,7 @@
for (size_t i = 0; i < boot_class_path.size(); i++) {
const DexFile* dex_file = boot_class_path[i];
CHECK(dex_file != nullptr);
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->RegisterDexFile(*dex_file, nullptr)));
+ ObjPtr<mirror::DexCache> dex_cache = linker->RegisterDexFile(*dex_file, nullptr);
CHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded.
if (kPreloadDexCachesStrings) {
for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
@@ -522,7 +527,7 @@
if (kPreloadDexCachesTypes) {
for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) {
- PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), dex::TypeIndex(j));
+ PreloadDexCachesResolveType(soa.Self(), dex_cache, dex::TypeIndex(j));
}
}
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 2aeef60..a88563d 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -21,11 +21,11 @@
#include "art_method-inl.h"
#include "gc/task_processor.h"
#include "jni_internal.h"
-#include "nth_caller_visitor.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
#include "native_util.h"
+#include "nth_caller_visitor.h"
#include "scoped_fast_native_object_access-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread_list.h"
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 0e5d740..553cbea 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -22,8 +22,8 @@
#include "gc/accounting/card_table-inl.h"
#include "jni_internal.h"
#include "mirror/array.h"
-#include "mirror/class.h"
#include "mirror/class-inl.h"
+#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "native_util.h"
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 242e87a..abbb347 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -20,8 +20,8 @@
#include "art_method-inl.h"
#include "base/enums.h"
-#include "class_linker.h"
#include "class_linker-inl.h"
+#include "class_linker.h"
#include "dex_file_annotations.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index 2aad12d..f209f1d 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -260,7 +260,7 @@
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
- ObjPtr<mirror::Class> return_type(method->GetReturnType(true /* resolve */));
+ ObjPtr<mirror::Class> return_type(method->ResolveReturnType());
if (return_type.IsNull()) {
CHECK(soa.Self()->IsExceptionPending());
return nullptr;
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index f19004d..2e4dd8a 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -20,14 +20,14 @@
#include "nativehelper/jni_macros.h"
#include "art_field-inl.h"
-#include "class_linker.h"
#include "class_linker-inl.h"
+#include "class_linker.h"
#include "common_throws.h"
#include "dex_file-inl.h"
#include "dex_file_annotations.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
-#include "mirror/field.h"
+#include "mirror/field-inl.h"
#include "native_util.h"
#include "reflection-inl.h"
#include "scoped_fast_native_object_access-inl.h"
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index cbbb6a8..18ff9c3 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -20,8 +20,8 @@
#include "art_method-inl.h"
#include "base/enums.h"
-#include "class_linker.h"
#include "class_linker-inl.h"
+#include "class_linker.h"
#include "dex_file_annotations.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc
index c698548..9743c94 100644
--- a/runtime/native/libcore_util_CharsetUtils.cc
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -27,7 +27,6 @@
#include "scoped_fast_native_object_access-inl.h"
#include "unicode/utf16.h"
-
namespace art {
/**
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index e78c9da..b2bdeed 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -16,11 +16,12 @@
#include "sun_misc_Unsafe.h"
-#include <atomic>
-#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
+#include <cstdlib>
+#include <cstring>
+#include <atomic>
+
#include "nativehelper/jni_macros.h"
#include "common_throws.h"
@@ -66,10 +67,12 @@
if (kUseReadBarrier) {
// Need to make sure the reference stored in the field is a to-space one before attempting the
// CAS or the CAS could fail incorrectly.
+ // Note that the read barrier load does NOT need to be volatile.
mirror::HeapReference<mirror::Object>* field_addr =
reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
reinterpret_cast<uint8_t*>(obj.Ptr()) + static_cast<size_t>(offset));
- ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, /* kAlwaysUpdateField */ true>(
+ ReadBarrier::Barrier<mirror::Object, /* kIsVolatile */ false, kWithReadBarrier,
+ /* kAlwaysUpdateField */ true>(
obj.Ptr(),
MemberOffset(offset),
field_addr);
@@ -111,6 +114,7 @@
jint newValue) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+ // TODO: A release store is likely to be faster on future processors.
QuasiAtomic::ThreadFenceRelease();
// JNI must use non transactional mode.
obj->SetField32<false>(MemberOffset(offset), newValue);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 1c1189d..4033f8c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -17,15 +17,15 @@
#include "oat_file.h"
#include <dlfcn.h>
-#include <string.h>
-#include <type_traits>
-#include <unistd.h>
-
-#include <cstdlib>
#ifndef __APPLE__
#include <link.h> // for dl_iterate_phdr.
#endif
+#include <unistd.h>
+
+#include <cstdlib>
+#include <cstring>
#include <sstream>
+#include <type_traits>
// dlopen_ext support from bionic.
#ifdef ART_TARGET_ANDROID
@@ -44,10 +44,10 @@
#include "elf_file.h"
#include "elf_utils.h"
#include "gc_root.h"
-#include "oat.h"
#include "mem_map.h"
#include "mirror/class.h"
#include "mirror/object-inl.h"
+#include "oat.h"
#include "oat_file-inl.h"
#include "oat_file_manager.h"
#include "os.h"
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index c876657..3794f51 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -25,8 +25,8 @@
#include "base/logging.h"
#include "base/stl_util.h"
-#include "compiler_filter.h"
#include "class_linker.h"
+#include "compiler_filter.h"
#include "exec_utils.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
@@ -37,6 +37,7 @@
#include "scoped_thread_state_change-inl.h"
#include "utils.h"
#include "vdex_file.h"
+#include "class_loader_context.h"
namespace art {
@@ -187,9 +188,11 @@
return true;
}
-int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
+int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
+ bool profile_changed,
+ bool downgrade) {
OatFileInfo& info = GetBestInfo();
- DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed);
+ DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed, downgrade);
if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) {
return dexopt_needed;
}
@@ -223,14 +226,26 @@
}
OatFileAssistant::ResultOfAttemptToUpdate
-OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) {
+OatFileAssistant::MakeUpToDate(bool profile_changed,
+ const std::string& class_loader_context,
+ std::string* error_msg) {
CompilerFilter::Filter target;
if (!GetRuntimeCompilerFilterOption(&target, error_msg)) {
return kUpdateNotAttempted;
}
OatFileInfo& info = GetBestInfo();
- switch (info.GetDexOptNeeded(target, profile_changed)) {
+ // TODO(calin, jeffhao): the context should really be passed to GetDexOptNeeded: b/62269291.
+ // This is actually not trivial in the current logic as it will interact with the collision
+ // check:
+ // - currently, if the context does not match but we have no collisions we still accept the
+ // oat file.
+ // - if GetDexOptNeeded would return kDex2OatFromScratch for a context mismatch and we make
+ // the oat code up to date the collision check becomes useless.
+ // - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files
+ // loaded in other processes). So it boils down to how far do we want to complicate
+ // the logic in order to enable the use of oat files. Maybe its time to try simplify it.
+ switch (info.GetDexOptNeeded(target, profile_changed, /*downgrade*/ false)) {
case kNoDexOptNeeded:
return kUpdateSucceeded;
@@ -241,7 +256,7 @@
case kDex2OatForBootImage:
case kDex2OatForRelocation:
case kDex2OatForFilter:
- return GenerateOatFileNoChecks(info, target, error_msg);
+ return GenerateOatFileNoChecks(info, target, class_loader_context, error_msg);
}
UNREACHABLE();
}
@@ -626,7 +641,10 @@
}
OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks(
- OatFileAssistant::OatFileInfo& info, CompilerFilter::Filter filter, std::string* error_msg) {
+ OatFileAssistant::OatFileInfo& info,
+ CompilerFilter::Filter filter,
+ const std::string& class_loader_context,
+ std::string* error_msg) {
CHECK(error_msg != nullptr);
Runtime* runtime = Runtime::Current();
@@ -702,6 +720,7 @@
args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
args.push_back("--oat-location=" + oat_file_name);
args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+ args.push_back("--class-loader-context=" + class_loader_context);
if (!Dex2Oat(args, error_msg)) {
// Manually delete the oat and vdex files. This ensures there is no garbage
@@ -741,14 +760,6 @@
std::vector<std::string> argv;
argv.push_back(runtime->GetCompilerExecutable());
- argv.push_back("--runtime-arg");
- argv.push_back("-classpath");
- argv.push_back("--runtime-arg");
- std::string class_path = runtime->GetClassPathString();
- if (class_path == "") {
- class_path = OatFile::kSpecialSharedLibrary;
- }
- argv.push_back(class_path);
if (runtime->IsJavaDebuggable()) {
argv.push_back("--debuggable");
}
@@ -1005,9 +1016,9 @@
}
OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
- CompilerFilter::Filter target, bool profile_changed) {
+ CompilerFilter::Filter target, bool profile_changed, bool downgrade) {
bool compilation_desired = CompilerFilter::IsAotCompilationEnabled(target);
- bool filter_okay = CompilerFilterIsOkay(target, profile_changed);
+ bool filter_okay = CompilerFilterIsOkay(target, profile_changed, downgrade);
if (filter_okay && Status() == kOatUpToDate) {
// The oat file is in good shape as is.
@@ -1064,7 +1075,7 @@
}
bool OatFileAssistant::OatFileInfo::CompilerFilterIsOkay(
- CompilerFilter::Filter target, bool profile_changed) {
+ CompilerFilter::Filter target, bool profile_changed, bool downgrade) {
const OatFile* file = GetFile();
if (file == nullptr) {
return false;
@@ -1075,7 +1086,8 @@
VLOG(oat) << "Compiler filter not okay because Profile changed";
return false;
}
- return CompilerFilter::IsAsGoodAs(current, target);
+ return downgrade ? !CompilerFilter::IsBetter(current, target) :
+ CompilerFilter::IsAsGoodAs(current, target);
}
bool OatFileAssistant::OatFileInfo::IsExecutable() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 92d87ea..5eec943 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -147,13 +147,24 @@
bool Lock(std::string* error_msg);
// Return what action needs to be taken to produce up-to-date code for this
- // dex location that is at least as good as an oat file generated with the
- // given compiler filter. profile_changed should be true to indicate the
- // profile has recently changed for this dex location.
+ // dex location. If "downgrade" is set to false, it verifies if the current
+ // compiler filter is at least as good as an oat file generated with the
+ // given compiler filter otherwise, if its set to true, it checks whether
+ // the oat file generated with the target filter will be downgraded as
+ // compared to the current state. For example, if the current compiler filter is
+ // quicken, and target filter is verify, it will recommend to dexopt, while
+ // if the target filter is speed profile, it will recommend to keep it in its
+ // current state.
+ // profile_changed should be true to indicate the profile has recently changed
+ // for this dex location.
+ // If the purpose of the dexopt is to downgrade the compiler filter,
+ // set downgrade to true.
// Returns a positive status code if the status refers to the oat file in
// the oat location. Returns a negative status code if the status refers to
// the oat file in the odex location.
- int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, bool profile_changed = false);
+ int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+ bool profile_changed = false,
+ bool downgrade = false);
// Returns true if there is up-to-date code for this dex location,
// irrespective of the compiler filter of the up-to-date code.
@@ -174,12 +185,17 @@
// profile_changed should be true to indicate the profile has recently
// changed for this dex location.
//
+ // If the dex files need to be made up to date, class_loader_context will be
+ // passed to dex2oat.
+ //
// Returns the result of attempting to update the code.
//
// If the result is not kUpdateSucceeded, the value of error_msg will be set
// to a string describing why there was a failure or the update was not
// attempted. error_msg must not be null.
- ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed, std::string* error_msg);
+ ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed,
+ const std::string& class_loader_context,
+ std::string* error_msg);
// Returns an oat file that can be used for loading dex files.
// Returns null if no suitable oat file was found.
@@ -310,8 +326,11 @@
// given target_compilation_filter.
// profile_changed should be true to indicate the profile has recently
// changed for this dex location.
+ // downgrade should be true if the purpose of dexopt is to downgrade the
+ // compiler filter.
DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
- bool profile_changed);
+ bool profile_changed,
+ bool downgrade);
// Returns the loaded file.
// Loads the file if needed. Returns null if the file failed to load.
@@ -344,7 +363,9 @@
// least as good as the given target filter. profile_changed should be
// true to indicate the profile has recently changed for this dex
// location.
- bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
+ // downgrade should be true if the purpose of dexopt is to downgrade the
+ // compiler filter.
+ bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed, bool downgrade);
// Release the loaded oat file.
// Returns null if the oat file hasn't been loaded.
@@ -373,7 +394,8 @@
};
// Generate the oat file for the given info from the dex file using the
- // current runtime compiler options and the specified filter.
+ // current runtime compiler options, the specified filter and class loader
+ // context.
// This does not check the current status before attempting to generate the
// oat file.
//
@@ -382,6 +404,7 @@
// attempted. error_msg must not be null.
ResultOfAttemptToUpdate GenerateOatFileNoChecks(OatFileInfo& info,
CompilerFilter::Filter target,
+ const std::string& class_loader_context,
std::string* error_msg);
// Return info for the best oat file.
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 1ecdd0d..e048177 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -14,18 +14,23 @@
* limitations under the License.
*/
-#include <string>
-#include <vector>
+#include "oat_file_assistant.h"
+
#include <sys/param.h>
-#include "android-base/strings.h"
+#include <string>
+#include <vector>
+
#include <gtest/gtest.h>
+#include "android-base/strings.h"
+
#include "art_field-inl.h"
#include "class_linker-inl.h"
+#include "class_loader_context.h"
#include "common_runtime_test.h"
#include "dexopt_test.h"
-#include "oat_file_assistant.h"
+#include "oat_file.h"
#include "oat_file_manager.h"
#include "os.h"
#include "scoped_thread_state_change-inl.h"
@@ -34,6 +39,8 @@
namespace art {
+static const std::string kSpecialSharedLibrary = "&";
+
class OatFileAssistantTest : public DexoptTest {};
class OatFileAssistantNoDex2OatTest : public DexoptTest {
@@ -113,7 +120,8 @@
// Trying to make the oat file up to date should not fail or crash.
std::string error_msg;
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, oat_file_assistant.MakeUpToDate(false, &error_msg));
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -766,7 +774,8 @@
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -946,7 +955,7 @@
// We should get kUpdateSucceeded from MakeUpToDate since there's nothing
// that can be done in this situation.
ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
// Verify it didn't create an oat in the default location (dalvik-cache).
OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
@@ -1025,7 +1034,7 @@
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
@@ -1172,7 +1181,8 @@
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=quicken");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ error_msg;
EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
@@ -1180,7 +1190,8 @@
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg))
+ << error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
@@ -1188,7 +1199,7 @@
Runtime::Current()->AddCompilerOption("--compiler-filter=bogus");
EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted,
- oat_file_assistant.MakeUpToDate(false, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
}
TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) {
@@ -1248,7 +1259,8 @@
OatFileAssistant::kDefaultCompilerFilterForDexLoading;
std::string error_msg;
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ error_msg;
EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(default_filter));
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -1256,6 +1268,50 @@
EXPECT_EQ(default_filter, oat_file->GetCompilerFilter());
}
+TEST_F(OatFileAssistantTest, MakeUpToDateWithSpecialSharedLibrary) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ const CompilerFilter::Filter default_filter =
+ OatFileAssistant::kDefaultCompilerFilterForDexLoading;
+ std::string error_msg;
+ int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(default_filter));
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ EXPECT_NE(nullptr, oat_file.get());
+ EXPECT_EQ(kSpecialSharedLibrary,
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+}
+
+TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ const CompilerFilter::Filter default_filter =
+ OatFileAssistant::kDefaultCompilerFilterForDexLoading;
+ std::string error_msg;
+ std::string context_str = "PCL[" + context_location + "]";
+ int status = oat_file_assistant.MakeUpToDate(false, context_str, &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(default_filter));
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ EXPECT_NE(nullptr, oat_file.get());
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create(context_str);
+ context->OpenDexFiles(kRuntimeISA, "");
+ EXPECT_EQ(context->EncodeContextForOatFile(""),
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+}
+
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index e950fca..5baf59c 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -329,12 +329,14 @@
// Check for class-def collisions in dex files.
//
-// This first walks the class loader chain, getting all the dex files from the class loader. If
-// the class loader is null or one of the class loaders in the chain is unsupported, we collect
-// dex files from all open non-boot oat files to be safe.
+// This first walks the class loader chain present in the given context, getting all the dex files
+// from the class loader.
//
-// This first checks whether the shared libraries are in the expected order and the oat files
-// have the expected checksums. If so, we exit early. Otherwise, we do the collision check.
+// If the context is null (which means the initial class loader was null or unsupported)
+// this returns false. b/37777332.
+//
+// This first checks whether all class loaders in the context have the same type and
+// classpath. If so, we exit early. Otherwise, we do the collision check.
//
// The collision check works by maintaining a heap with one class from each dex file, sorted by the
// class descriptor. Then a dex-file/class pair is continually removed from the heap and compared
@@ -342,23 +344,11 @@
// the two elements agree on whether their dex file was from an already-loaded oat-file or the
// new oat file. Any disagreement indicates a collision.
bool OatFileManager::HasCollisions(const OatFile* oat_file,
- jobject class_loader,
- jobjectArray dex_elements,
+ const ClassLoaderContext* context,
std::string* error_msg /*out*/) const {
DCHECK(oat_file != nullptr);
DCHECK(error_msg != nullptr);
- // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
- // directly with DexFile APIs instead of using class loaders.
- if (class_loader == nullptr) {
- LOG(WARNING) << "Opening an oat file without a class loader. "
- << "Are you using the deprecated DexFile APIs?";
- return false;
- }
-
- std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
-
// The context might be null if there are unrecognized class loaders in the chain or they
// don't meet sensible sanity conditions. In this case we assume that the app knows what it's
// doing and accept the oat file.
@@ -406,6 +396,17 @@
Locks::mutator_lock_->AssertNotHeld(self);
Runtime* const runtime = Runtime::Current();
+ std::unique_ptr<ClassLoaderContext> context;
+ // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
+ // directly with DexFile APIs instead of using class loaders.
+ if (class_loader == nullptr) {
+ LOG(WARNING) << "Opening an oat file without a class loader. "
+ << "Are you using the deprecated DexFile APIs?";
+ context = nullptr;
+ } else {
+ context = ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
+ }
+
OatFileAssistant oat_file_assistant(dex_location,
kRuntimeISA,
!runtime->IsAotCompiler());
@@ -425,7 +426,12 @@
// 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, /*out*/ &error_msg)) {
+
+ const std::string& dex2oat_context = context == nullptr
+ ? OatFile::kSpecialSharedLibrary
+ : context->EncodeContextForDex2oat(/*base_dir*/ "");
+ switch (oat_file_assistant.MakeUpToDate(
+ /*profile_changed*/false, dex2oat_context, /*out*/ &error_msg)) {
case OatFileAssistant::kUpdateFailed:
LOG(WARNING) << error_msg;
break;
@@ -451,7 +457,7 @@
if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
// Take the file only if it has no collisions, or we must take it because of preopting.
bool accept_oat_file =
- !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
+ !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg);
if (!accept_oat_file) {
// Failed the collision check. Print warning.
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 05a5f5b..4523494 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -35,6 +35,7 @@
} // namespace space
} // namespace gc
+class ClassLoaderContext;
class DexFile;
class OatFile;
@@ -105,15 +106,17 @@
void DumpForSigQuit(std::ostream& os);
private:
- // Check that the shared libraries in the given oat file match those in the given class loader and
- // dex elements. If the class loader is null or we do not support one of the class loaders in the
- // chain, compare against all non-boot oat files instead. If the shared libraries are not ok,
- // check for duplicate class definitions of the given oat file against the oat files (either from
- // the class loader and dex elements if possible or all non-boot oat files otherwise).
+ // Check that the class loader context of the given oat file matches the given context.
+ // This will perform a check that all class loaders in the chain have the same type and
+ // classpath.
+ // If the context is null (which means the initial class loader was null or unsupported)
+ // this returns false.
+ // If the context does not validate the method will check for duplicate class definitions of
+ // the given oat file against the oat files (either from the class loaders if possible or all
+ // non-boot oat files otherwise).
// Return true if there are any class definition collisions in the oat_file.
bool HasCollisions(const OatFile* oat_file,
- jobject class_loader,
- jobjectArray dex_elements,
+ const ClassLoaderContext* context,
/*out*/ std::string* error_msg) const
REQUIRES(!Locks::oat_file_manager_lock_);
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 152b0ba..3625b9e 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -19,8 +19,8 @@
#include "arch/instruction_set.h"
#include "base/macros.h"
-#include "quick/quick_method_frame_info.h"
#include "method_info.h"
+#include "quick/quick_method_frame_info.h"
#include "stack_map.h"
#include "utils.h"
diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc
index f6db544..744bc42 100644
--- a/runtime/object_lock.cc
+++ b/runtime/object_lock.cc
@@ -16,8 +16,8 @@
#include "object_lock.h"
-#include "mirror/object-inl.h"
#include "mirror/class_ext.h"
+#include "mirror/object-inl.h"
#include "monitor.h"
namespace art {
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
deleted file mode 100644
index ab434d7..0000000
--- a/runtime/openjdkjvmti/ti_method.cc
+++ /dev/null
@@ -1,428 +0,0 @@
-/* Copyright (C) 2016 The Android Open Source Project
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This file implements interfaces from the file jvmti.h. This implementation
- * is licensed under the same terms as the file jvmti.h. The
- * copyright and license information for the file jvmti.h follows.
- *
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-#include "ti_method.h"
-
-#include "art_jvmti.h"
-#include "art_method-inl.h"
-#include "base/enums.h"
-#include "dex_file_annotations.h"
-#include "events-inl.h"
-#include "jni_internal.h"
-#include "mirror/object_array-inl.h"
-#include "modifiers.h"
-#include "nativehelper/ScopedLocalRef.h"
-#include "runtime_callbacks.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-current-inl.h"
-#include "thread_list.h"
-#include "ti_phase.h"
-
-namespace openjdkjvmti {
-
-struct TiMethodCallback : public art::MethodCallback {
- void RegisterNativeMethod(art::ArtMethod* method,
- const void* cur_method,
- /*out*/void** new_method)
- OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
- if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) {
- art::Thread* thread = art::Thread::Current();
- art::JNIEnvExt* jnienv = thread->GetJniEnv();
- ScopedLocalRef<jthread> thread_jni(
- jnienv, PhaseUtil::IsLivePhase() ? jnienv->AddLocalReference<jthread>(thread->GetPeer())
- : nullptr);
- art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(
- thread,
- static_cast<JNIEnv*>(jnienv),
- thread_jni.get(),
- art::jni::EncodeArtMethod(method),
- const_cast<void*>(cur_method),
- new_method);
- }
- }
-
- EventHandler* event_handler = nullptr;
-};
-
-TiMethodCallback gMethodCallback;
-
-void MethodUtil::Register(EventHandler* handler) {
- gMethodCallback.event_handler = handler;
- art::ScopedThreadStateChange stsc(art::Thread::Current(),
- art::ThreadState::kWaitingForDebuggerToAttach);
- art::ScopedSuspendAll ssa("Add method callback");
- art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback);
-}
-
-void MethodUtil::Unregister() {
- art::ScopedThreadStateChange stsc(art::Thread::Current(),
- art::ThreadState::kWaitingForDebuggerToAttach);
- art::ScopedSuspendAll ssa("Remove method callback");
- art::Runtime* runtime = art::Runtime::Current();
- runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
-}
-
-jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
- jmethodID method,
- jint* size_ptr,
- unsigned char** bytecode_ptr) {
- if (method == nullptr) {
- return ERR(INVALID_METHODID);
- }
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
-
- if (art_method->IsNative()) {
- return ERR(NATIVE_METHOD);
- }
-
- if (size_ptr == nullptr || bytecode_ptr == nullptr) {
- return ERR(NULL_POINTER);
- }
-
- art::ScopedObjectAccess soa(art::Thread::Current());
- const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
- if (code_item == nullptr) {
- *size_ptr = 0;
- *bytecode_ptr = nullptr;
- return OK;
- }
- // 2 bytes per instruction for dex code.
- *size_ptr = code_item->insns_size_in_code_units_ * 2;
- jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
- if (err != OK) {
- return err;
- }
- memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
- return OK;
-}
-
-jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
- jmethodID method,
- jint* size_ptr) {
- if (method == nullptr) {
- return ERR(INVALID_METHODID);
- }
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
-
- if (art_method->IsNative()) {
- return ERR(NATIVE_METHOD);
- }
-
- if (size_ptr == nullptr) {
- return ERR(NULL_POINTER);
- }
-
- art::ScopedObjectAccess soa(art::Thread::Current());
- if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
- // Use the shorty.
- art::ArtMethod* base_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
- size_t arg_count = art::ArtMethod::NumArgRegisters(base_method->GetShorty());
- if (!base_method->IsStatic()) {
- arg_count++;
- }
- *size_ptr = static_cast<jint>(arg_count);
- return ERR(NONE);
- }
-
- DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
- *size_ptr = art_method->GetCodeItem()->ins_size_;
-
- return ERR(NONE);
-}
-
-jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
- jmethodID method,
- jint* max_ptr) {
- if (method == nullptr) {
- return ERR(INVALID_METHODID);
- }
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
-
- if (art_method->IsNative()) {
- return ERR(NATIVE_METHOD);
- }
-
- if (max_ptr == nullptr) {
- return ERR(NULL_POINTER);
- }
-
- art::ScopedObjectAccess soa(art::Thread::Current());
- if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
- // This isn't specified as an error case, so return 0.
- *max_ptr = 0;
- return ERR(NONE);
- }
-
- DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
- *max_ptr = art_method->GetCodeItem()->registers_size_;
-
- return ERR(NONE);
-}
-
-jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
- jmethodID method,
- char** name_ptr,
- char** signature_ptr,
- char** generic_ptr) {
- art::ScopedObjectAccess soa(art::Thread::Current());
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
- art_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
-
- JvmtiUniquePtr<char[]> name_copy;
- if (name_ptr != nullptr) {
- const char* method_name = art_method->GetName();
- if (method_name == nullptr) {
- method_name = "<error>";
- }
- jvmtiError ret;
- name_copy = CopyString(env, method_name, &ret);
- if (name_copy == nullptr) {
- return ret;
- }
- *name_ptr = name_copy.get();
- }
-
- JvmtiUniquePtr<char[]> signature_copy;
- if (signature_ptr != nullptr) {
- const art::Signature sig = art_method->GetSignature();
- std::string str = sig.ToString();
- jvmtiError ret;
- signature_copy = CopyString(env, str.c_str(), &ret);
- if (signature_copy == nullptr) {
- return ret;
- }
- *signature_ptr = signature_copy.get();
- }
-
- if (generic_ptr != nullptr) {
- *generic_ptr = nullptr;
- if (!art_method->GetDeclaringClass()->IsProxyClass()) {
- art::mirror::ObjectArray<art::mirror::String>* str_array =
- art::annotations::GetSignatureAnnotationForMethod(art_method);
- if (str_array != nullptr) {
- std::ostringstream oss;
- for (int32_t i = 0; i != str_array->GetLength(); ++i) {
- oss << str_array->Get(i)->ToModifiedUtf8();
- }
- std::string output_string = oss.str();
- jvmtiError ret;
- JvmtiUniquePtr<char[]> generic_copy = CopyString(env, output_string.c_str(), &ret);
- if (generic_copy == nullptr) {
- return ret;
- }
- *generic_ptr = generic_copy.release();
- } else if (soa.Self()->IsExceptionPending()) {
- // TODO: Should we report an error here?
- soa.Self()->ClearException();
- }
- }
- }
-
- // Everything is fine, release the buffers.
- name_copy.release();
- signature_copy.release();
-
- return ERR(NONE);
-}
-
-jvmtiError MethodUtil::GetMethodDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
- jmethodID method,
- jclass* declaring_class_ptr) {
- if (declaring_class_ptr == nullptr) {
- return ERR(NULL_POINTER);
- }
-
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
- // Note: No GetInterfaceMethodIfProxy, we want to actual class.
-
- art::ScopedObjectAccess soa(art::Thread::Current());
- art::mirror::Class* klass = art_method->GetDeclaringClass();
- *declaring_class_ptr = soa.AddLocalReference<jclass>(klass);
-
- return ERR(NONE);
-}
-
-jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED,
- jmethodID method,
- jlocation* start_location_ptr,
- jlocation* end_location_ptr) {
- if (method == nullptr) {
- return ERR(INVALID_METHODID);
- }
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
-
- if (art_method->IsNative()) {
- return ERR(NATIVE_METHOD);
- }
-
- if (start_location_ptr == nullptr || end_location_ptr == nullptr) {
- return ERR(NULL_POINTER);
- }
-
- art::ScopedObjectAccess soa(art::Thread::Current());
- if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
- // This isn't specified as an error case, so return -1/-1 as the RI does.
- *start_location_ptr = -1;
- *end_location_ptr = -1;
- return ERR(NONE);
- }
-
- DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
- *start_location_ptr = 0;
- *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1;
-
- return ERR(NONE);
-}
-
-jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
- jmethodID method,
- jint* modifiers_ptr) {
- if (modifiers_ptr == nullptr) {
- return ERR(NULL_POINTER);
- }
-
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
- uint32_t modifiers = art_method->GetAccessFlags();
-
- // Note: Keep this code in sync with Executable.fixMethodFlags.
- if ((modifiers & art::kAccAbstract) != 0) {
- modifiers &= ~art::kAccNative;
- }
- modifiers &= ~art::kAccSynchronized;
- if ((modifiers & art::kAccDeclaredSynchronized) != 0) {
- modifiers |= art::kAccSynchronized;
- }
- modifiers &= art::kAccJavaFlagsMask;
-
- *modifiers_ptr = modifiers;
- return ERR(NONE);
-}
-
-using LineNumberContext = std::vector<jvmtiLineNumberEntry>;
-
-static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) {
- LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context);
- jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_),
- static_cast<jint>(entry.line_) };
- context->push_back(jvmti_entry);
- return false; // Collect all, no early exit.
-}
-
-jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
- jmethodID method,
- jint* entry_count_ptr,
- jvmtiLineNumberEntry** table_ptr) {
- if (method == nullptr) {
- return ERR(NULL_POINTER);
- }
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
- DCHECK(!art_method->IsRuntimeMethod());
-
- const art::DexFile::CodeItem* code_item;
- const art::DexFile* dex_file;
- {
- art::ScopedObjectAccess soa(art::Thread::Current());
-
- if (art_method->IsProxyMethod()) {
- return ERR(ABSENT_INFORMATION);
- }
- if (art_method->IsNative()) {
- return ERR(NATIVE_METHOD);
- }
- if (entry_count_ptr == nullptr || table_ptr == nullptr) {
- return ERR(NULL_POINTER);
- }
-
- code_item = art_method->GetCodeItem();
- dex_file = art_method->GetDexFile();
- DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
- }
-
- LineNumberContext context;
- bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
- if (!success) {
- return ERR(ABSENT_INFORMATION);
- }
-
- unsigned char* data;
- jlong mem_size = context.size() * sizeof(jvmtiLineNumberEntry);
- jvmtiError alloc_error = env->Allocate(mem_size, &data);
- if (alloc_error != ERR(NONE)) {
- return alloc_error;
- }
- *table_ptr = reinterpret_cast<jvmtiLineNumberEntry*>(data);
- memcpy(*table_ptr, context.data(), mem_size);
- *entry_count_ptr = static_cast<jint>(context.size());
-
- return ERR(NONE);
-}
-
-template <typename T>
-static jvmtiError IsMethodT(jvmtiEnv* env ATTRIBUTE_UNUSED,
- jmethodID method,
- T test,
- jboolean* is_t_ptr) {
- if (method == nullptr) {
- return ERR(INVALID_METHODID);
- }
- if (is_t_ptr == nullptr) {
- return ERR(NULL_POINTER);
- }
-
- art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
- *is_t_ptr = test(art_method) ? JNI_TRUE : JNI_FALSE;
-
- return ERR(NONE);
-}
-
-jvmtiError MethodUtil::IsMethodNative(jvmtiEnv* env, jmethodID m, jboolean* is_native_ptr) {
- auto test = [](art::ArtMethod* method) {
- return method->IsNative();
- };
- return IsMethodT(env, m, test, is_native_ptr);
-}
-
-jvmtiError MethodUtil::IsMethodObsolete(jvmtiEnv* env, jmethodID m, jboolean* is_obsolete_ptr) {
- auto test = [](art::ArtMethod* method) {
- return method->IsObsolete();
- };
- return IsMethodT(env, m, test, is_obsolete_ptr);
-}
-
-jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* is_synthetic_ptr) {
- auto test = [](art::ArtMethod* method) {
- return method->IsSynthetic();
- };
- return IsMethodT(env, m, test, is_synthetic_ptr);
-}
-
-} // namespace openjdkjvmti
diff --git a/runtime/os_linux.cc b/runtime/os_linux.cc
index 0add496..a463f70 100644
--- a/runtime/os_linux.cc
+++ b/runtime/os_linux.cc
@@ -16,9 +16,10 @@
#include "os.h"
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
#include <cstddef>
#include <memory>
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 1f5beb9..0f8555a 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -22,11 +22,11 @@
#include <jni.h>
-#include "globals.h"
+#include "arch/instruction_set.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
-#include "arch/instruction_set.h"
-#include "jit/profile_saver_options.h"
+#include "globals.h"
+// #include "jit/profile_saver_options.h"
#include "runtime_options.h"
namespace art {
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index db10103..b592247 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -533,21 +533,19 @@
void QuickExceptionHandler::DeoptimizeSingleFrame(DeoptimizationKind kind) {
DCHECK(is_deoptimization_);
- if (VLOG_IS_ON(deopt) || kDebugExceptionDelivery) {
- LOG(INFO) << "Single-frame deopting:";
- DumpFramesWithType(self_, true);
- }
-
DeoptimizeStackVisitor visitor(self_, context_, this, true);
visitor.WalkStack(true);
// Compiled code made an explicit deoptimization.
ArtMethod* deopt_method = visitor.GetSingleFrameDeoptMethod();
DCHECK(deopt_method != nullptr);
- LOG(INFO) << "Deoptimizing "
- << deopt_method->PrettyMethod()
- << " due to "
- << GetDeoptimizationKindName(kind);
+ if (VLOG_IS_ON(deopt) || kDebugExceptionDelivery) {
+ LOG(INFO) << "Single-frame deopting: "
+ << deopt_method->PrettyMethod()
+ << " due to "
+ << GetDeoptimizationKindName(kind);
+ DumpFramesWithType(self_, /* details */ true);
+ }
if (Runtime::Current()->UseJitCompilation()) {
Runtime::Current()->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor(
deopt_method, visitor.GetSingleFrameDeoptQuickMethodHeader());
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 2d06e54..6424599 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -22,8 +22,8 @@
#include "gc/accounting/read_barrier_table.h"
#include "gc/collector/concurrent_copying-inl.h"
#include "gc/heap.h"
-#include "mirror/object_reference.h"
#include "mirror/object-readbarrier-inl.h"
+#include "mirror/object_reference.h"
#include "mirror/reference.h"
#include "runtime.h"
#include "utils.h"
@@ -33,7 +33,8 @@
// Disabled for performance reasons.
static constexpr bool kCheckDebugDisallowReadBarrierCount = false;
-template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kAlwaysUpdateField>
+template <typename MirrorType, bool kIsVolatile, ReadBarrierOption kReadBarrierOption,
+ bool kAlwaysUpdateField>
inline MirrorType* ReadBarrier::Barrier(
mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
constexpr bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
@@ -55,7 +56,7 @@
}
ref_addr = reinterpret_cast<mirror::HeapReference<MirrorType>*>(
fake_address_dependency | reinterpret_cast<uintptr_t>(ref_addr));
- MirrorType* ref = ref_addr->AsMirrorPtr();
+ MirrorType* ref = ref_addr->template AsMirrorPtr<kIsVolatile>();
MirrorType* old_ref = ref;
if (is_gray) {
// Slow-path.
@@ -71,9 +72,9 @@
return ref;
} else if (kUseBrooksReadBarrier) {
// To be implemented.
- return ref_addr->AsMirrorPtr();
+ return ref_addr->template AsMirrorPtr<kIsVolatile>();
} else if (kUseTableLookupReadBarrier) {
- MirrorType* ref = ref_addr->AsMirrorPtr();
+ MirrorType* ref = ref_addr->template AsMirrorPtr<kIsVolatile>();
MirrorType* old_ref = ref;
// The heap or the collector can be null at startup. TODO: avoid the need for this null check.
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -93,7 +94,7 @@
}
} else {
// No read barrier.
- return ref_addr->AsMirrorPtr();
+ return ref_addr->template AsMirrorPtr<kIsVolatile>();
}
}
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index ca77685..8a106aa 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -18,8 +18,8 @@
#define ART_RUNTIME_READ_BARRIER_H_
#include "base/logging.h"
-#include "base/mutex.h"
#include "base/macros.h"
+#include "base/mutex.h"
#include "gc_root.h"
#include "jni.h"
#include "mirror/object_reference.h"
@@ -46,9 +46,13 @@
// fast-debug environment.
DECLARE_RUNTIME_DEBUG_FLAG(kEnableReadBarrierInvariantChecks);
+ // Return the reference at ref_addr, invoking read barrier as appropriate.
+ // Ref_addr is an address within obj.
// It's up to the implementation whether the given field gets updated whereas the return value
// must be an updated reference unless kAlwaysUpdateField is true.
- template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+ template <typename MirrorType,
+ bool kIsVolatile,
+ ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
bool kAlwaysUpdateField = false>
ALWAYS_INLINE static MirrorType* Barrier(
mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr)
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index d8b9dcc..e6e588e 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -20,10 +20,10 @@
#include "base/mutex.h"
#include "indirect_reference_table.h"
-#include "mirror/array.h"
#include "mirror/array-inl.h"
-#include "mirror/class.h"
+#include "mirror/array.h"
#include "mirror/class-inl.h"
+#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "mirror/string-inl.h"
#include "runtime-inl.h"
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 6f1d15c..f28f0ca 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -238,8 +238,7 @@
// TODO: The method's parameter's type must have been previously resolved, yet
// we've seen cases where it's not b/34440020.
ObjPtr<mirror::Class> dst_class(
- m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
- true /* resolve */));
+ m->ResolveClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_));
if (dst_class.Ptr() == nullptr) {
CHECK(self->IsExceptionPending());
return false;
@@ -378,7 +377,7 @@
Thread* const self = Thread::Current();
for (uint32_t i = 0; i < num_params; i++) {
dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_;
- ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */));
+ ObjPtr<mirror::Class> param_type(m->ResolveClassFromTypeIndex(type_idx));
if (param_type == nullptr) {
CHECK(self->IsExceptionPending());
LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index 609f0d6..4584351 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -49,7 +49,9 @@
} else if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveRefsOnly)) {
return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsOnly);
} else {
- DCHECK_EQ(method, GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverything));
+ DCHECK(method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverything) ||
+ method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForClinit) ||
+ method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForSuspendCheck));
return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveEverything);
}
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index ebee5ea..a8ccf89 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -23,9 +23,10 @@
#include <sys/prctl.h>
#endif
+#include <fcntl.h>
#include <signal.h>
#include <sys/syscall.h>
-#include "base/memory_tool.h"
+
#if defined(__APPLE__)
#include <crt_externs.h> // for _NSGetEnviron
#endif
@@ -33,12 +34,11 @@
#include <cstdio>
#include <cstdlib>
#include <limits>
-#include <memory_representation.h>
#include <vector>
-#include <fcntl.h>
#include "android-base/strings.h"
+#include "aot_class_linker.h"
#include "arch/arm/quick_method_frame_info_arm.h"
#include "arch/arm/registers_arm.h"
#include "arch/arm64/quick_method_frame_info_arm64.h"
@@ -60,10 +60,10 @@
#include "base/arena_allocator.h"
#include "base/dumpable.h"
#include "base/enums.h"
+#include "base/memory_tool.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
-#include "cha.h"
#include "class_linker-inl.h"
#include "compiler_callbacks.h"
#include "debugger.h"
@@ -88,6 +88,7 @@
#include "jit/profile_saver.h"
#include "jni_internal.h"
#include "linear_alloc.h"
+#include "memory_representation.h"
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
@@ -237,7 +238,7 @@
system_thread_group_(nullptr),
system_class_loader_(nullptr),
dump_gc_performance_on_shutdown_(false),
- preinitialization_transaction_(nullptr),
+ preinitialization_transactions_(),
verify_(verifier::VerifyMode::kNone),
allow_dex_file_fallback_(true),
target_sdk_version_(0),
@@ -258,8 +259,7 @@
pruned_dalvik_cache_(false),
// Initially assume we perceive jank in case the process state is never updated.
process_state_(kProcessStateJankPerceptible),
- zygote_no_threads_(false),
- cha_(nullptr) {
+ zygote_no_threads_(false) {
static_assert(Runtime::kCalleeSaveSize ==
static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
@@ -381,7 +381,6 @@
delete monitor_list_;
delete monitor_pool_;
delete class_linker_;
- delete cha_;
delete heap_;
delete intern_table_;
delete oat_file_manager_;
@@ -1285,8 +1284,11 @@
GetHeap()->EnableObjectValidation();
CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U);
- class_linker_ = new ClassLinker(intern_table_);
- cha_ = new ClassHierarchyAnalysis;
+ if (UNLIKELY(IsAotCompiler())) {
+ class_linker_ = new AotClassLinker(intern_table_);
+ } else {
+ class_linker_ = new ClassLinker(intern_table_);
+ }
if (GetHeap()->HasBootImageSpace()) {
bool result = class_linker_->InitFromBootImage(&error_msg);
if (!result) {
@@ -1832,8 +1834,8 @@
}
void Runtime::VisitTransactionRoots(RootVisitor* visitor) {
- if (preinitialization_transaction_ != nullptr) {
- preinitialization_transaction_->VisitRoots(visitor);
+ for (auto& transaction : preinitialization_transactions_) {
+ transaction->VisitRoots(visitor);
}
}
@@ -2065,17 +2067,32 @@
}
// Transaction support.
-void Runtime::EnterTransactionMode(Transaction* transaction) {
+bool Runtime::IsActiveTransaction() const {
+ return !preinitialization_transactions_.empty() && !GetTransaction()->IsRollingBack();
+}
+
+void Runtime::EnterTransactionMode() {
DCHECK(IsAotCompiler());
- DCHECK(transaction != nullptr);
DCHECK(!IsActiveTransaction());
- preinitialization_transaction_ = transaction;
+ preinitialization_transactions_.push_back(std::make_unique<Transaction>());
+}
+
+void Runtime::EnterTransactionMode(bool strict, mirror::Class* root) {
+ DCHECK(IsAotCompiler());
+ preinitialization_transactions_.push_back(std::make_unique<Transaction>(strict, root));
}
void Runtime::ExitTransactionMode() {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_ = nullptr;
+ preinitialization_transactions_.pop_back();
+}
+
+void Runtime::RollbackAndExitTransactionMode() {
+ DCHECK(IsAotCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transactions_.back()->Rollback();
+ preinitialization_transactions_.pop_back();
}
bool Runtime::IsTransactionAborted() const {
@@ -2083,67 +2100,86 @@
return false;
} else {
DCHECK(IsAotCompiler());
- return preinitialization_transaction_->IsAborted();
+ return GetTransaction()->IsAborted();
}
}
+void Runtime::RollbackAllTransactions() {
+ // If transaction is aborted, all transactions will be kept in the list.
+ // Rollback and exit all of them.
+ while (IsActiveTransaction()) {
+ RollbackAndExitTransactionMode();
+ }
+}
+
+bool Runtime::IsActiveStrictTransactionMode() const {
+ return IsActiveTransaction() && GetTransaction()->IsStrict();
+}
+
+const std::unique_ptr<Transaction>& Runtime::GetTransaction() const {
+ DCHECK(!preinitialization_transactions_.empty());
+ return preinitialization_transactions_.back();
+}
+
void Runtime::AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
// Throwing an exception may cause its class initialization. If we mark the transaction
// aborted before that, we may warn with a false alarm. Throwing the exception before
// marking the transaction aborted avoids that.
- preinitialization_transaction_->ThrowAbortError(self, &abort_message);
- preinitialization_transaction_->Abort(abort_message);
+ // But now the transaction can be nested, and abort the transaction will relax the constraints
+ // for constructing stack trace.
+ GetTransaction()->Abort(abort_message);
+ GetTransaction()->ThrowAbortError(self, &abort_message);
}
void Runtime::ThrowTransactionAbortError(Thread* self) {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
// Passing nullptr means we rethrow an exception with the earlier transaction abort message.
- preinitialization_transaction_->ThrowAbortError(self, nullptr);
+ GetTransaction()->ThrowAbortError(self, nullptr);
}
void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
uint8_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset,
int8_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldByte(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteFieldByte(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset,
uint16_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldChar(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteFieldChar(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset,
int16_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldShort(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteFieldShort(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset,
uint32_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteField32(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteField32(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset,
uint64_t value, bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteField64(obj, field_offset, value, is_volatile);
+ GetTransaction()->RecordWriteField64(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldReference(mirror::Object* obj,
@@ -2152,7 +2188,7 @@
bool is_volatile) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteFieldReference(obj,
+ GetTransaction()->RecordWriteFieldReference(obj,
field_offset,
value.Ptr(),
is_volatile);
@@ -2161,38 +2197,38 @@
void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWriteArray(array, index, value);
+ GetTransaction()->RecordWriteArray(array, index, value);
}
void Runtime::RecordStrongStringInsertion(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordStrongStringInsertion(s);
+ GetTransaction()->RecordStrongStringInsertion(s);
}
void Runtime::RecordWeakStringInsertion(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWeakStringInsertion(s);
+ GetTransaction()->RecordWeakStringInsertion(s);
}
void Runtime::RecordStrongStringRemoval(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordStrongStringRemoval(s);
+ GetTransaction()->RecordStrongStringRemoval(s);
}
void Runtime::RecordWeakStringRemoval(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordWeakStringRemoval(s);
+ GetTransaction()->RecordWeakStringRemoval(s);
}
void Runtime::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache,
dex::StringIndex string_idx) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->RecordResolveString(dex_cache, string_idx);
+ GetTransaction()->RecordResolveString(dex_cache, string_idx);
}
void Runtime::SetFaultMessage(const std::string& message) {
@@ -2410,5 +2446,4 @@
GetClassLinker()->VisitClasses(&visitor);
}
}
-
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index af9d215..4e84468 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -24,6 +24,7 @@
#include <set>
#include <string>
#include <utility>
+#include <memory>
#include <vector>
#include "arch/instruction_set.h"
@@ -72,7 +73,6 @@
class ArenaPool;
class ArtMethod;
enum class CalleeSaveType: uint32_t;
-class ClassHierarchyAnalysis;
class ClassLinker;
class CompilerCallbacks;
class DexFile;
@@ -454,12 +454,17 @@
const std::string& profile_output_filename);
// Transaction support.
- bool IsActiveTransaction() const {
- return preinitialization_transaction_ != nullptr;
- }
- void EnterTransactionMode(Transaction* transaction);
+ bool IsActiveTransaction() const;
+ void EnterTransactionMode();
+ void EnterTransactionMode(bool strict, mirror::Class* root);
void ExitTransactionMode();
+ void RollbackAllTransactions() REQUIRES_SHARED(Locks::mutator_lock_);
+ // Transaction rollback and exit transaction are always done together, it's convenience to
+ // do them in one function.
+ void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_);
bool IsTransactionAborted() const;
+ const std::unique_ptr<Transaction>& GetTransaction() const;
+ bool IsActiveStrictTransactionMode() const;
void AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -645,10 +650,6 @@
void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder);
void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder);
- ClassHierarchyAnalysis* GetClassHierarchyAnalysis() {
- return cha_;
- }
-
void AttachAgent(const std::string& agent_arg);
const std::list<ti::Agent>& GetAgents() const {
@@ -713,7 +714,7 @@
static constexpr int kProfileForground = 0;
static constexpr int kProfileBackground = 1;
- static constexpr uint32_t kCalleeSaveSize = 4u;
+ static constexpr uint32_t kCalleeSaveSize = 6u;
// 64 bit so that we can share the same asm offsets for both 32 and 64 bits.
uint64_t callee_save_methods_[kCalleeSaveSize];
@@ -841,8 +842,11 @@
// If true, then we dump the GC cumulative timings on shutdown.
bool dump_gc_performance_on_shutdown_;
- // Transaction used for pre-initializing classes at compilation time.
- Transaction* preinitialization_transaction_;
+ // Transactions used for pre-initializing classes at compilation time.
+ // Support nested transactions, maintain a list containing all transactions. Transactions are
+ // handled under a stack discipline. Because GC needs to go over all transactions, we choose list
+ // as substantial data structure instead of stack.
+ std::list<std::unique_ptr<Transaction>> preinitialization_transactions_;
// If kNone, verification is disabled. kEnable by default.
verifier::VerifyMode verify_;
@@ -939,8 +943,6 @@
// Generic system-weak holders.
std::vector<gc::AbstractSystemWeakHolder*> system_weak_holders_;
- ClassHierarchyAnalysis* cha_;
-
std::unique_ptr<RuntimeCallbacks> callbacks_;
std::atomic<uint32_t> deoptimization_counts_[
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 0ea3180..983278d 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -16,7 +16,6 @@
#include "runtime_callbacks.h"
-#include "jni.h"
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
@@ -25,6 +24,8 @@
#include <memory>
#include <string>
+#include "jni.h"
+
#include "art_method-inl.h"
#include "base/mutex.h"
#include "class_linker.h"
diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc
index aa14719..b072bb0 100644
--- a/runtime/runtime_options.cc
+++ b/runtime/runtime_options.cc
@@ -18,13 +18,13 @@
#include <memory>
+#include "debugger.h"
#include "gc/heap.h"
#include "monitor.h"
#include "runtime.h"
#include "thread_list.h"
#include "trace.h"
#include "utils.h"
-#include "debugger.h"
namespace art {
diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h
index c509992..89a1dcb 100644
--- a/runtime/runtime_options.h
+++ b/runtime/runtime_options.h
@@ -17,21 +17,20 @@
#ifndef ART_RUNTIME_RUNTIME_OPTIONS_H_
#define ART_RUNTIME_RUNTIME_OPTIONS_H_
-#include <vector>
+#include <cstdarg>
+#include <cstdio>
#include <string>
+#include <vector>
-#include <stdio.h>
-#include <stdarg.h>
-
+#include "arch/instruction_set.h"
#include "base/logging.h"
#include "base/variant_map.h"
#include "cmdline_types.h" // TODO: don't need to include this file here
+#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
#include "jdwp/jdwp.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
-#include "gc/collector_type.h"
-#include "gc/space/large_object_space.h"
-#include "arch/instruction_set.h"
#include "jit/profile_saver_options.h"
#include "verifier/verifier_enums.h"
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 19df0d2..2e06536 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -634,7 +634,7 @@
void StackVisitor::SanityCheckFrame() const {
if (kIsDebugBuild) {
ArtMethod* method = GetMethod();
- auto* declaring_class = method->GetDeclaringClass();
+ mirror::Class* declaring_class = method->GetDeclaringClass();
// Runtime methods have null declaring class.
if (!method->IsRuntimeMethod()) {
CHECK(declaring_class != nullptr);
@@ -647,11 +647,14 @@
LinearAlloc* const linear_alloc = runtime->GetLinearAlloc();
if (!linear_alloc->Contains(method)) {
// Check class linker linear allocs.
- mirror::Class* klass = method->GetDeclaringClass();
+ // We get the canonical method as copied methods may have their declaring
+ // class from another class loader.
+ ArtMethod* canonical = method->GetCanonicalMethod();
+ mirror::Class* klass = canonical->GetDeclaringClass();
LinearAlloc* const class_linear_alloc = (klass != nullptr)
? runtime->GetClassLinker()->GetAllocatorForClassLoader(klass->GetClassLoader())
: linear_alloc;
- if (!class_linear_alloc->Contains(method)) {
+ if (!class_linear_alloc->Contains(canonical)) {
// Check image space.
bool in_image = false;
for (auto& space : runtime->GetHeap()->GetContinuousSpaces()) {
@@ -660,14 +663,14 @@
const auto& header = image_space->GetImageHeader();
const ImageSection& methods = header.GetMethodsSection();
const ImageSection& runtime_methods = header.GetRuntimeMethodsSection();
- const size_t offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin();
+ const size_t offset = reinterpret_cast<const uint8_t*>(canonical) - image_space->Begin();
if (methods.Contains(offset) || runtime_methods.Contains(offset)) {
in_image = true;
break;
}
}
}
- CHECK(in_image) << method->PrettyMethod() << " not in linear alloc or image";
+ CHECK(in_image) << canonical->PrettyMethod() << " not in linear alloc or image";
}
}
if (cur_quick_frame_ != nullptr) {
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 21780a1..3931b62 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -20,13 +20,13 @@
#include <limits>
#include "arch/code_offset.h"
-#include "base/bit_vector.h"
#include "base/bit_utils.h"
+#include "base/bit_vector.h"
#include "bit_memory_region.h"
#include "dex_file.h"
+#include "leb128.h"
#include "memory_region.h"
#include "method_info.h"
-#include "leb128.h"
namespace art {
diff --git a/runtime/thread.h b/runtime/thread.h
index e1102ed..7540fd2 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -17,12 +17,13 @@
#ifndef ART_RUNTIME_THREAD_H_
#define ART_RUNTIME_THREAD_H_
+#include <setjmp.h>
+
#include <bitset>
#include <deque>
#include <iosfwd>
#include <list>
#include <memory>
-#include <setjmp.h>
#include <string>
#include "arch/context.h"
@@ -1704,8 +1705,13 @@
class SCOPED_CAPABILITY ScopedAssertNoThreadSuspension {
public:
- ALWAYS_INLINE explicit ScopedAssertNoThreadSuspension(const char* cause)
- ACQUIRE(Roles::uninterruptible_) {
+ ALWAYS_INLINE ScopedAssertNoThreadSuspension(const char* cause,
+ bool enabled = true)
+ ACQUIRE(Roles::uninterruptible_)
+ : enabled_(enabled) {
+ if (!enabled_) {
+ return;
+ }
if (kIsDebugBuild) {
self_ = Thread::Current();
old_cause_ = self_->StartAssertNoThreadSuspension(cause);
@@ -1714,6 +1720,9 @@
}
}
ALWAYS_INLINE ~ScopedAssertNoThreadSuspension() RELEASE(Roles::uninterruptible_) {
+ if (!enabled_) {
+ return;
+ }
if (kIsDebugBuild) {
self_->EndAssertNoThreadSuspension(old_cause_);
} else {
@@ -1723,6 +1732,7 @@
private:
Thread* self_;
+ const bool enabled_;
const char* old_cause_;
};
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index d5db983..8ff6c52 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -16,10 +16,10 @@
#include "thread.h"
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <limits.h>
#include <errno.h>
+#include <limits.h>
+#include <sys/resource.h>
+#include <sys/time.h>
#include <cutils/sched_policy.h>
#include <utils/threads.h>
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index f1a7b65..488e4a6 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -16,16 +16,16 @@
#include "thread_list.h"
-#include <backtrace/BacktraceMap.h>
#include <dirent.h>
-#include "nativehelper/ScopedLocalRef.h"
-#include "nativehelper/ScopedUtfChars.h"
#include <sys/types.h>
#include <unistd.h>
#include <sstream>
#include "android-base/stringprintf.h"
+#include "backtrace/BacktraceMap.h"
+#include "nativehelper/ScopedLocalRef.h"
+#include "nativehelper/ScopedUtfChars.h"
#include "base/histogram-inl.h"
#include "base/mutex-inl.h"
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 8349f33..fb77b84 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -16,11 +16,11 @@
#include "thread_pool.h"
-#include <pthread.h>
-
#include <sys/mman.h>
-#include <sys/time.h>
#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <pthread.h>
#include "android-base/stringprintf.h"
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 907d37e..e923aff 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -16,8 +16,8 @@
#include "transaction.h"
-#include "base/stl_util.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "gc/accounting/card_table-inl.h"
#include "gc_root-inl.h"
#include "intern_table.h"
@@ -34,10 +34,18 @@
static constexpr bool kEnableTransactionStats = false;
Transaction::Transaction()
- : log_lock_("transaction log lock", kTransactionLogLock), aborted_(false) {
+ : log_lock_("transaction log lock", kTransactionLogLock),
+ aborted_(false),
+ rolling_back_(false),
+ strict_(false) {
CHECK(Runtime::Current()->IsAotCompiler());
}
+Transaction::Transaction(bool strict, mirror::Class* root) : Transaction() {
+ strict_ = strict;
+ root_ = root;
+}
+
Transaction::~Transaction() {
if (kEnableTransactionStats) {
MutexLock mu(Thread::Current(), log_lock_);
@@ -97,11 +105,41 @@
return aborted_;
}
+bool Transaction::IsRollingBack() {
+ return rolling_back_;
+}
+
+bool Transaction::IsStrict() {
+ MutexLock mu(Thread::Current(), log_lock_);
+ return strict_;
+}
+
const std::string& Transaction::GetAbortMessage() {
MutexLock mu(Thread::Current(), log_lock_);
return abort_message_;
}
+bool Transaction::WriteConstraint(mirror::Object* obj, ArtField* field) {
+ MutexLock mu(Thread::Current(), log_lock_);
+ if (strict_ // no constraint for boot image
+ && field->IsStatic() // no constraint instance updating
+ && obj != root_) { // modifying other classes' static field, fail
+ return true;
+ }
+ return false;
+}
+
+bool Transaction::ReadConstraint(mirror::Object* obj, ArtField* field) {
+ DCHECK(field->IsStatic());
+ DCHECK(obj->IsClass());
+ MutexLock mu(Thread::Current(), log_lock_);
+ if (!strict_ || // no constraint for boot image
+ obj == root_) { // self-updating, pass
+ return false;
+ }
+ return true;
+}
+
void Transaction::RecordWriteFieldBoolean(mirror::Object* obj,
MemberOffset field_offset,
uint8_t value,
@@ -222,15 +260,17 @@
}
void Transaction::Rollback() {
- CHECK(!Runtime::Current()->IsActiveTransaction());
Thread* self = Thread::Current();
self->AssertNoPendingException();
MutexLock mu1(self, *Locks::intern_table_lock_);
MutexLock mu2(self, log_lock_);
+ rolling_back_ = true;
+ CHECK(!Runtime::Current()->IsActiveTransaction());
UndoObjectModifications();
UndoArrayModifications();
UndoInternStringTableModifications();
UndoResolveStringModifications();
+ rolling_back_ = false;
}
void Transaction::UndoObjectModifications() {
@@ -270,6 +310,7 @@
void Transaction::VisitRoots(RootVisitor* visitor) {
MutexLock mu(Thread::Current(), log_lock_);
+ visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&root_), RootInfo(kRootUnknown));
VisitObjectLogs(visitor);
VisitArrayLogs(visitor);
VisitInternStringLogs(visitor);
@@ -411,7 +452,7 @@
const FieldValue& field_value) const {
// TODO We may want to abort a transaction while still being in transaction mode. In this case,
// we'd need to disable the check.
- constexpr bool kCheckTransaction = true;
+ constexpr bool kCheckTransaction = false;
switch (field_value.kind) {
case kBoolean:
if (UNLIKELY(field_value.is_volatile)) {
@@ -590,30 +631,39 @@
uint64_t value) const {
// TODO We may want to abort a transaction while still being in transaction mode. In this case,
// we'd need to disable the check.
+ constexpr bool kCheckTransaction = false;
switch (array_type) {
case Primitive::kPrimBoolean:
- array->AsBooleanArray()->SetWithoutChecks<false>(index, static_cast<uint8_t>(value));
+ array->AsBooleanArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<uint8_t>(value));
break;
case Primitive::kPrimByte:
- array->AsByteArray()->SetWithoutChecks<false>(index, static_cast<int8_t>(value));
+ array->AsByteArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<int8_t>(value));
break;
case Primitive::kPrimChar:
- array->AsCharArray()->SetWithoutChecks<false>(index, static_cast<uint16_t>(value));
+ array->AsCharArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<uint16_t>(value));
break;
case Primitive::kPrimShort:
- array->AsShortArray()->SetWithoutChecks<false>(index, static_cast<int16_t>(value));
+ array->AsShortArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<int16_t>(value));
break;
case Primitive::kPrimInt:
- array->AsIntArray()->SetWithoutChecks<false>(index, static_cast<int32_t>(value));
+ array->AsIntArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<int32_t>(value));
break;
case Primitive::kPrimFloat:
- array->AsFloatArray()->SetWithoutChecks<false>(index, static_cast<float>(value));
+ array->AsFloatArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<float>(value));
break;
case Primitive::kPrimLong:
- array->AsLongArray()->SetWithoutChecks<false>(index, static_cast<int64_t>(value));
+ array->AsLongArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<int64_t>(value));
break;
case Primitive::kPrimDouble:
- array->AsDoubleArray()->SetWithoutChecks<false>(index, static_cast<double>(value));
+ array->AsDoubleArray()->SetWithoutChecks<false, kCheckTransaction>(
+ index, static_cast<double>(value));
break;
case Primitive::kPrimNot:
LOG(FATAL) << "ObjectArray should be treated as Object";
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 747c2d0..4e9cde5 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -32,6 +32,7 @@
namespace art {
namespace mirror {
class Array;
+class Class;
class DexCache;
class Object;
class String;
@@ -44,6 +45,7 @@
static constexpr const char* kAbortExceptionSignature = "Ldalvik/system/TransactionAbortError;";
Transaction();
+ explicit Transaction(bool strict, mirror::Class* root);
~Transaction();
void Abort(const std::string& abort_message)
@@ -54,6 +56,15 @@
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsAborted() REQUIRES(!log_lock_);
+ // If the transaction is rollbacking. Transactions will set this flag when they start rollbacking,
+ // because the nested transaction should be disabled when rollbacking to restore the memory.
+ bool IsRollingBack();
+
+ // If the transaction is in strict mode, then all access of static fields will be constrained,
+ // one class's clinit will not be allowed to read or modify another class's static fields, unless
+ // the transaction is aborted.
+ bool IsStrict() REQUIRES(!log_lock_);
+
// Record object field changes.
void RecordWriteFieldBoolean(mirror::Object* obj,
MemberOffset field_offset,
@@ -124,6 +135,14 @@
REQUIRES(!log_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ bool ReadConstraint(mirror::Object* obj, ArtField* field)
+ REQUIRES(!log_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ bool WriteConstraint(mirror::Object* obj, ArtField* field)
+ REQUIRES(!log_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
class ObjectLog : public ValueObject {
public:
@@ -287,7 +306,10 @@
std::list<InternStringLog> intern_string_logs_ GUARDED_BY(log_lock_);
std::list<ResolveStringLog> resolve_string_logs_ GUARDED_BY(log_lock_);
bool aborted_ GUARDED_BY(log_lock_);
+ bool rolling_back_; // Single thread, no race.
+ bool strict_ GUARDED_BY(log_lock_);
std::string abort_message_ GUARDED_BY(log_lock_);
+ mirror::Class* root_ GUARDED_BY(log_lock_);
DISALLOW_COPY_AND_ASSIGN(Transaction);
};
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 9206292..e52dd08 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -69,14 +69,12 @@
mirror::Class::Status old_status = h_klass->GetStatus();
LockWord old_lock_word = h_klass->GetLockWord(false);
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
- Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(Runtime::Current()->IsTransactionAborted());
ASSERT_FALSE(success);
ASSERT_TRUE(h_klass->IsErroneous());
ASSERT_TRUE(soa.Self()->IsExceptionPending());
- ASSERT_TRUE(transaction.IsAborted());
// Check class's monitor get back to its original state without rolling back changes.
LockWord new_lock_word = h_klass->GetLockWord(false);
@@ -84,7 +82,7 @@
// Check class status is rolled back properly.
soa.Self()->ClearException();
- transaction.Rollback();
+ Runtime::Current()->RollbackAndExitTransactionMode();
ASSERT_EQ(old_status, h_klass->GetStatus());
}
};
@@ -97,15 +95,12 @@
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
ASSERT_TRUE(h_klass != nullptr);
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
ASSERT_TRUE(h_obj != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
- Runtime::Current()->ExitTransactionMode();
-
// Rolling back transaction's changes must not clear the Object::class field.
- transaction.Rollback();
+ Runtime::Current()->RollbackAndExitTransactionMode();
EXPECT_EQ(h_obj->GetClass(), h_klass.Get());
}
@@ -124,15 +119,13 @@
h_obj->MonitorEnter(soa.Self());
LockWord old_lock_word = h_obj->GetLockWord(false);
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
// Unlock object's monitor inside the transaction.
h_obj->MonitorExit(soa.Self());
LockWord new_lock_word = h_obj->GetLockWord(false);
- Runtime::Current()->ExitTransactionMode();
-
// Rolling back transaction's changes must not change monitor's state.
- transaction.Rollback();
+ Runtime::Current()->RollbackAndExitTransactionMode();
+
LockWord aborted_lock_word = h_obj->GetLockWord(false);
EXPECT_FALSE(LockWord::Equal<false>(old_lock_word, new_lock_word));
EXPECT_TRUE(LockWord::Equal<false>(aborted_lock_word, new_lock_word));
@@ -148,8 +141,7 @@
constexpr int32_t kArraySize = 2;
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
// Allocate an array during transaction.
Handle<mirror::Array> h_obj(
@@ -159,10 +151,9 @@
Runtime::Current()->GetHeap()->GetCurrentAllocator())));
ASSERT_TRUE(h_obj != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
- Runtime::Current()->ExitTransactionMode();
+ Runtime::Current()->RollbackAndExitTransactionMode();
// Rolling back transaction's changes must not reset array's length.
- transaction.Rollback();
EXPECT_EQ(h_obj->GetLength(), kArraySize);
}
@@ -238,8 +229,7 @@
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
// Modify fields inside transaction then rollback changes.
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
booleanField->SetBoolean<true>(h_klass.Get(), true);
byteField->SetByte<true>(h_klass.Get(), 1);
charField->SetChar<true>(h_klass.Get(), 1u);
@@ -249,8 +239,7 @@
floatField->SetFloat<true>(h_klass.Get(), 1.0);
doubleField->SetDouble<true>(h_klass.Get(), 1.0);
objectField->SetObject<true>(h_klass.Get(), h_obj.Get());
- Runtime::Current()->ExitTransactionMode();
- transaction.Rollback();
+ Runtime::Current()->RollbackAndExitTransactionMode();
// Check values have properly been restored to their original (default) value.
EXPECT_EQ(booleanField->GetBoolean(h_klass.Get()), false);
@@ -340,8 +329,7 @@
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
// Modify fields inside transaction then rollback changes.
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
booleanField->SetBoolean<true>(h_instance.Get(), true);
byteField->SetByte<true>(h_instance.Get(), 1);
charField->SetChar<true>(h_instance.Get(), 1u);
@@ -351,8 +339,7 @@
floatField->SetFloat<true>(h_instance.Get(), 1.0);
doubleField->SetDouble<true>(h_instance.Get(), 1.0);
objectField->SetObject<true>(h_instance.Get(), h_obj.Get());
- Runtime::Current()->ExitTransactionMode();
- transaction.Rollback();
+ Runtime::Current()->RollbackAndExitTransactionMode();
// Check values have properly been restored to their original (default) value.
EXPECT_EQ(booleanField->GetBoolean(h_instance.Get()), false);
@@ -457,8 +444,7 @@
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
// Modify fields inside transaction then rollback changes.
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
booleanArray->SetWithoutChecks<true>(0, true);
byteArray->SetWithoutChecks<true>(0, 1);
charArray->SetWithoutChecks<true>(0, 1u);
@@ -468,8 +454,7 @@
floatArray->SetWithoutChecks<true>(0, 1.0);
doubleArray->SetWithoutChecks<true>(0, 1.0);
objectArray->SetWithoutChecks<true>(0, h_obj.Get());
- Runtime::Current()->ExitTransactionMode();
- transaction.Rollback();
+ Runtime::Current()->RollbackAndExitTransactionMode();
// Check values have properly been restored to their original (default) value.
EXPECT_EQ(booleanArray->GetWithoutChecks(0), false);
@@ -511,8 +496,7 @@
EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr);
EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr);
// Do the transaction, then roll back.
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(success);
ASSERT_TRUE(h_klass->IsInitialized());
@@ -523,8 +507,7 @@
EXPECT_STREQ(s->ToModifiedUtf8().c_str(), kResolvedString);
EXPECT_EQ(s, h_dex_cache->GetResolvedString(string_idx));
}
- Runtime::Current()->ExitTransactionMode();
- transaction.Rollback();
+ Runtime::Current()->RollbackAndExitTransactionMode();
// Check that the string did not stay resolved.
EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr);
EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr);
@@ -547,8 +530,7 @@
class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(success);
@@ -571,8 +553,7 @@
class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
+ Runtime::Current()->EnterTransactionMode();
bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(success);
diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc
index 16cd722..4fab39c 100644
--- a/runtime/type_lookup_table.cc
+++ b/runtime/type_lookup_table.cc
@@ -16,14 +16,14 @@
#include "type_lookup_table.h"
+#include <cstring>
+#include <memory>
+
#include "base/bit_utils.h"
#include "dex_file-inl.h"
#include "utf-inl.h"
#include "utils.h"
-#include <memory>
-#include <cstring>
-
namespace art {
static uint16_t MakeData(uint16_t class_def_idx, uint32_t hash, uint32_t mask) {
diff --git a/runtime/type_lookup_table_test.cc b/runtime/type_lookup_table_test.cc
index ec38b41..ac11871 100644
--- a/runtime/type_lookup_table_test.cc
+++ b/runtime/type_lookup_table_test.cc
@@ -14,13 +14,13 @@
* limitations under the License.
*/
+#include "type_lookup_table.h"
#include <memory>
#include "common_runtime_test.h"
#include "dex_file-inl.h"
#include "scoped_thread_state_change-inl.h"
-#include "type_lookup_table.h"
#include "utf-inl.h"
namespace art {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index c4b0441..ffa9d45 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
+
#include <memory>
#include "android-base/stringprintf.h"
@@ -38,9 +39,9 @@
#include "utf-inl.h"
#if defined(__APPLE__)
-#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
-#include <sys/syscall.h>
#include <crt_externs.h>
+#include <sys/syscall.h>
+#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
#endif
#if defined(__linux__)
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index 72f63c6..9d4e9fb 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -64,7 +64,7 @@
"Expecting alignof(StringDexCacheType) == 8");
static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
"Expecting alignof(MethodTypeDexCacheType) == 8");
- // This is the same as alignof(FieldDexCacheType) for the given pointer size.
+ // This is the same as alignof({Field,Method}DexCacheType) for the given pointer size.
return 2u * static_cast<size_t>(pointer_size);
}
@@ -84,7 +84,7 @@
if (num_elements < cache_size) {
cache_size = num_elements;
}
- return ArraySize(PointerSize::k64, cache_size);
+ return PairArraySize(GcRootAsPointerSize<mirror::Class>(), cache_size);
}
inline size_t DexCacheArraysLayout::TypesAlignment() const {
@@ -96,11 +96,15 @@
}
inline size_t DexCacheArraysLayout::MethodsSize(size_t num_elements) const {
- return ArraySize(pointer_size_, num_elements);
+ size_t cache_size = mirror::DexCache::kDexCacheMethodCacheSize;
+ if (num_elements < cache_size) {
+ cache_size = num_elements;
+ }
+ return PairArraySize(pointer_size_, cache_size);
}
inline size_t DexCacheArraysLayout::MethodsAlignment() const {
- return static_cast<size_t>(pointer_size_);
+ return 2u * static_cast<size_t>(pointer_size_);
}
inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const {
@@ -113,7 +117,7 @@
if (num_elements < cache_size) {
cache_size = num_elements;
}
- return ArraySize(PointerSize::k64, cache_size);
+ return PairArraySize(GcRootAsPointerSize<mirror::String>(), cache_size);
}
inline size_t DexCacheArraysLayout::StringsAlignment() const {
@@ -132,7 +136,7 @@
if (num_elements < cache_size) {
cache_size = num_elements;
}
- return 2u * static_cast<size_t>(pointer_size_) * cache_size;
+ return PairArraySize(pointer_size_, cache_size);
}
inline size_t DexCacheArraysLayout::FieldsAlignment() const {
@@ -170,6 +174,10 @@
return static_cast<size_t>(element_size) * num_elements;
}
+inline size_t DexCacheArraysLayout::PairArraySize(PointerSize element_size, uint32_t num_elements) {
+ return 2u * static_cast<size_t>(element_size) * num_elements;
+}
+
} // namespace art
#endif // ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index 377a374..fc04159 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -130,6 +130,7 @@
static size_t ElementOffset(PointerSize element_size, uint32_t idx);
static size_t ArraySize(PointerSize element_size, uint32_t num_elements);
+ static size_t PairArraySize(PointerSize element_size, uint32_t num_elements);
};
} // namespace art
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 48b703a..ee8eb36 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -22,13 +22,13 @@
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "exec_utils.h"
-#include "mirror/array.h"
+#include "handle_scope-inl.h"
#include "mirror/array-inl.h"
+#include "mirror/array.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string.h"
#include "scoped_thread_state_change-inl.h"
-#include "handle_scope-inl.h"
#include "base/memory_tool.h"
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index e8f947c..b955220 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -20,6 +20,7 @@
#include <memory>
+#include "base/bit_utils.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
@@ -134,6 +135,9 @@
} else {
// Fetch the next dex file. Return null if there is none.
const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_;
+ // Dex files are required to be 4 byte aligned. the OatWriter makes sure they are, see
+ // OatWriter::SeekToDexFiles.
+ data = AlignUp(data, 4);
return (data == DexEnd()) ? nullptr : data;
}
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 0351fd3..63058cf 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -72,8 +72,8 @@
private:
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
- // Last update: Change method lookup.
- static constexpr uint8_t kVdexVersion[] = { '0', '0', '9', '\0' };
+ // Last update: Use set for unverified_classes_.
+ static constexpr uint8_t kVdexVersion[] = { '0', '1', '0', '\0' };
uint8_t magic_[4];
uint8_t version_[4];
diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h
index 363bd8f..6c832e3 100644
--- a/runtime/verifier/method_verifier-inl.h
+++ b/runtime/verifier/method_verifier-inl.h
@@ -17,11 +17,12 @@
#ifndef ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_
#define ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_
-#include "base/logging.h"
#include "method_verifier.h"
+
+#include "base/logging.h"
+#include "handle_scope-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
-#include "handle_scope-inl.h"
namespace art {
namespace verifier {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 6dc7953..312d8df 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -39,8 +39,8 @@
#include "indenter.h"
#include "intern_table.h"
#include "leb128.h"
-#include "mirror/class.h"
#include "mirror/class-inl.h"
+#include "mirror/class.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/method_handle_impl.h"
#include "mirror/object-inl.h"
@@ -51,8 +51,8 @@
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
#include "utils.h"
-#include "verifier_deps.h"
#include "verifier_compiler_binding.h"
+#include "verifier_deps.h"
namespace art {
namespace verifier {
@@ -2899,10 +2899,12 @@
ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range);
const RegType* return_type = nullptr;
if (called_method != nullptr) {
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
+ ObjPtr<mirror::Class> return_type_class = can_load_classes_
+ ? called_method->ResolveReturnType()
+ : called_method->LookupResolvedReturnType();
if (return_type_class != nullptr) {
return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
- return_type_class,
+ return_type_class.Ptr(),
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
@@ -2942,10 +2944,12 @@
} else {
is_constructor = called_method->IsConstructor();
return_type_descriptor = called_method->GetReturnTypeDescriptor();
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
+ ObjPtr<mirror::Class> return_type_class = can_load_classes_
+ ? called_method->ResolveReturnType()
+ : called_method->LookupResolvedReturnType();
if (return_type_class != nullptr) {
return_type = &FromClass(return_type_descriptor,
- return_type_class,
+ return_type_class.Ptr(),
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
@@ -3857,10 +3861,20 @@
// TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_method);
+ bool must_fail = false;
+ // This is traditional and helps with screwy bytecode. It will tell you that, yes, a method
+ // exists, but that it's called incorrectly. This significantly helps debugging, as locally it's
+ // hard to see the differences.
+ // If we don't have res_method here we must fail. Just use this bool to make sure of that with a
+ // DCHECK.
if (res_method == nullptr) {
+ must_fail = true;
// Try to find the method also with the other type for better error reporting below
// but do not store such bogus lookup result in the DexCache or VerifierDeps.
if (klass->IsInterface()) {
+ // NB This is normally not really allowed but we want to get any static or private object
+ // methods for error message purposes. This will never be returned.
+ // TODO We might want to change the verifier to not require this.
res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
} else {
// If there was an interface method with the same signature,
@@ -3918,6 +3932,20 @@
}
}
+ // Check specifically for non-public object methods being provided for interface dispatch. This
+ // can occur if we failed to find a method with FindInterfaceMethod but later find one with
+ // FindClassMethod for error message use.
+ if (method_type == METHOD_INTERFACE &&
+ res_method->GetDeclaringClass()->IsObjectClass() &&
+ !res_method->IsPublic()) {
+ Fail(VERIFY_ERROR_NO_METHOD) << "invoke-interface " << klass->PrettyDescriptor() << "."
+ << dex_file_->GetMethodName(method_id) << " "
+ << dex_file_->GetMethodSignature(method_id) << " resolved to "
+ << "non-public object method " << res_method->PrettyMethod() << " "
+ << "but non-public Object methods are excluded from interface "
+ << "method resolution.";
+ return nullptr;
+ }
// Check if access is allowed.
if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call "
@@ -3946,6 +3974,13 @@
"type of " << res_method->PrettyMethod();
return nullptr;
}
+ // Make sure we weren't expecting to fail.
+ DCHECK(!must_fail) << "invoke type (" << method_type << ")"
+ << klass->PrettyDescriptor() << "."
+ << dex_file_->GetMethodName(method_id) << " "
+ << dex_file_->GetMethodSignature(method_id) << " unexpectedly resolved to "
+ << res_method->PrettyMethod() << " without error. Initially this method was "
+ << "not found so we were expecting to fail for some reason.";
return res_method;
}
@@ -5261,10 +5296,12 @@
const RegType& MethodVerifier::GetMethodReturnType() {
if (return_type_ == nullptr) {
if (mirror_method_ != nullptr) {
- mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
+ ObjPtr<mirror::Class> return_type_class = can_load_classes_
+ ? mirror_method_->ResolveReturnType()
+ : mirror_method_->LookupResolvedReturnType();
if (return_type_class != nullptr) {
return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
- return_type_class,
+ return_type_class.Ptr(),
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 46fdc54..ea8729c 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -30,8 +30,8 @@
#include "handle.h"
#include "instruction_flags.h"
#include "method_reference.h"
-#include "register_line.h"
#include "reg_type_cache.h"
+#include "register_line.h"
#include "verifier_enums.h"
namespace art {
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
index 9245828..704d2a8 100644
--- a/runtime/verifier/reg_type-inl.h
+++ b/runtime/verifier/reg_type-inl.h
@@ -21,8 +21,8 @@
#include "base/casts.h"
#include "base/scoped_arena_allocator.h"
-#include "mirror/class.h"
#include "method_verifier.h"
+#include "mirror/class.h"
#include "verifier_deps.h"
namespace art {
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 740b7dd..8df2e0f 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -24,8 +24,8 @@
#include "class_linker-inl.h"
#include "dex_file-inl.h"
#include "method_verifier.h"
-#include "mirror/class.h"
#include "mirror/class-inl.h"
+#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "reg_type_cache-inl.h"
@@ -711,6 +711,29 @@
DCHECK(c1 != nullptr && !c1->IsPrimitive());
DCHECK(c2 != nullptr && !c2->IsPrimitive());
mirror::Class* join_class = ClassJoin(c1, c2);
+ if (UNLIKELY(join_class == nullptr)) {
+ // Internal error joining the classes (e.g., OOME). Report an unresolved reference type.
+ // We cannot report an unresolved merge type, as that will attempt to merge the resolved
+ // components, leaving us in an infinite loop.
+ // We do not want to report the originating exception, as that would require a fast path
+ // out all the way to VerifyClass. Instead attempt to continue on without a detailed type.
+ Thread* self = Thread::Current();
+ self->AssertPendingException();
+ self->ClearException();
+
+ // When compiling on the host, we rather want to abort to ensure determinism for preopting.
+ // (In that case, it is likely a misconfiguration of dex2oat.)
+ if (!kIsTargetBuild && Runtime::Current()->IsAotCompiler()) {
+ LOG(FATAL) << "Could not create class join of "
+ << c1->PrettyClass()
+ << " & "
+ << c2->PrettyClass();
+ UNREACHABLE();
+ }
+
+ return reg_types->MakeUnresolvedReference();
+ }
+
// Record the dependency that both `c1` and `c2` are assignable to `join_class`.
// The `verifier` is null during unit tests.
if (verifier != nullptr) {
@@ -753,10 +776,18 @@
DCHECK(result->IsObjectClass());
return result;
}
+ Thread* self = Thread::Current();
ObjPtr<mirror::Class> common_elem = ClassJoin(s_ct, t_ct);
+ if (UNLIKELY(common_elem == nullptr)) {
+ self->AssertPendingException();
+ return nullptr;
+ }
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::Class* array_class = class_linker->FindArrayClass(Thread::Current(), &common_elem);
- DCHECK(array_class != nullptr);
+ mirror::Class* array_class = class_linker->FindArrayClass(self, &common_elem);
+ if (UNLIKELY(array_class == nullptr)) {
+ self->AssertPendingException();
+ return nullptr;
+ }
return array_class;
} else {
size_t s_depth = s->Depth();
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 6c01a79..c5d8ff5 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -355,6 +355,10 @@
* the perversion of Object being assignable to an interface type (note, however, that we don't
* allow assignment of Object or Interface to any concrete class and are therefore type safe).
*
+ * Note: This may return null in case of internal errors, e.g., OOME when a new class would have
+ * to be created but there is no heap space. The exception will stay pending, and it is
+ * the job of the caller to handle it.
+ *
* [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
*/
static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t)
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 93286ea..0c00868 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -222,6 +222,11 @@
}
}
+const RegType& RegTypeCache::MakeUnresolvedReference() {
+ // The descriptor is intentionally invalid so nothing else will match this type.
+ return AddEntry(new (&arena_) UnresolvedReferenceType(AddString("a"), entries_.size()));
+}
+
const RegType* RegTypeCache::FindClass(mirror::Class* klass, bool precise) const {
DCHECK(klass != nullptr);
if (klass->IsPrimitive()) {
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index 37f8a1f..7077c55 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -48,6 +48,7 @@
class IntegerType;
class LongHiType;
class LongLoType;
+class MethodVerifier;
class PreciseConstType;
class PreciseReferenceType;
class RegType;
@@ -97,6 +98,10 @@
REQUIRES_SHARED(Locks::mutator_lock_);
const RegType& FromUnresolvedSuperClass(const RegType& child)
REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Note: this should not be used outside of RegType::ClassJoin!
+ const RegType& MakeUnresolvedReference() REQUIRES_SHARED(Locks::mutator_lock_);
+
const ConstantType& Zero() REQUIRES_SHARED(Locks::mutator_lock_) {
return FromCat1Const(0, true);
}
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index b0ea6c8..1bc48ed 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -22,8 +22,9 @@
#include "base/casts.h"
#include "base/scoped_arena_allocator.h"
#include "common_runtime_test.h"
-#include "reg_type_cache-inl.h"
+#include "compiler_callbacks.h"
#include "reg_type-inl.h"
+#include "reg_type_cache-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
@@ -677,5 +678,59 @@
EXPECT_FALSE(imprecise_const.Equals(precise_const));
}
+class RegTypeOOMTest : public RegTypeTest {
+ protected:
+ void SetUpRuntimeOptions(RuntimeOptions *options) OVERRIDE {
+ SetUpRuntimeOptionsForFillHeap(options);
+
+ // We must not appear to be a compiler, or we'll abort on the host.
+ callbacks_.reset();
+ }
+};
+
+TEST_F(RegTypeOOMTest, ClassJoinOOM) {
+ // TODO: Figure out why FillHeap isn't good enough under CMS.
+ TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS();
+
+ // Tests that we don't abort with OOMs.
+
+ ArenaStack stack(Runtime::Current()->GetArenaPool());
+ ScopedArenaAllocator allocator(&stack);
+ ScopedObjectAccess soa(Thread::Current());
+
+ // We cannot allow moving GC. Otherwise we'd have to ensure the reg types are updated (reference
+ // reg types store a class pointer in a GCRoot, which is normally updated through active verifiers
+ // being registered with their thread), which is unnecessarily complex.
+ Runtime::Current()->GetHeap()->IncrementDisableMovingGC(soa.Self());
+
+ // We merge nested array of primitive wrappers. These have a join type of an array of Number of
+ // the same depth. We start with depth five, as we want at least two newly created classes to
+ // test recursion (it's just more likely that nobody uses such deep arrays in runtime bringup).
+ constexpr const char* kIntArrayFive = "[[[[[Ljava/lang/Integer;";
+ constexpr const char* kFloatArrayFive = "[[[[[Ljava/lang/Float;";
+ constexpr const char* kNumberArrayFour = "[[[[Ljava/lang/Number;";
+ constexpr const char* kNumberArrayFive = "[[[[[Ljava/lang/Number;";
+
+ RegTypeCache cache(true, allocator);
+ const RegType& int_array_array = cache.From(nullptr, kIntArrayFive, false);
+ ASSERT_TRUE(int_array_array.HasClass());
+ const RegType& float_array_array = cache.From(nullptr, kFloatArrayFive, false);
+ ASSERT_TRUE(float_array_array.HasClass());
+
+ // Check assumptions: the joined classes don't exist, yet.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ASSERT_TRUE(class_linker->LookupClass(soa.Self(), kNumberArrayFour, nullptr) == nullptr);
+ ASSERT_TRUE(class_linker->LookupClass(soa.Self(), kNumberArrayFive, nullptr) == nullptr);
+
+ // Fill the heap.
+ VariableSizedHandleScope hs(soa.Self());
+ FillHeap(soa.Self(), class_linker, &hs);
+
+ const RegType& join_type = int_array_array.Merge(float_array_array, &cache, nullptr);
+ ASSERT_TRUE(join_type.IsUnresolvedReference());
+
+ Runtime::Current()->GetHeap()->DecrementDisableMovingGC(soa.Self());
+}
+
} // namespace verifier
} // namespace art
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 383d890..34c406e 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -20,8 +20,8 @@
#include "dex_instruction-inl.h"
#include "method_verifier-inl.h"
-#include "register_line-inl.h"
#include "reg_type-inl.h"
+#include "register_line-inl.h"
namespace art {
namespace verifier {
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 112eec8..0481f24 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -33,7 +33,8 @@
namespace art {
namespace verifier {
-VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files) {
+VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, bool output_only)
+ : output_only_(output_only) {
for (const DexFile* dex_file : dex_files) {
DCHECK(GetDexFileDeps(*dex_file) == nullptr);
std::unique_ptr<DexFileDeps> deps(new DexFileDeps());
@@ -41,6 +42,9 @@
}
}
+VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files)
+ : VerifierDeps(dex_files, /*output_only*/ true) {}
+
void VerifierDeps::MergeWith(const VerifierDeps& other,
const std::vector<const DexFile*>& dex_files) {
DCHECK(dex_deps_.size() == other.dex_deps_.size());
@@ -55,9 +59,7 @@
MergeSets(my_deps->classes_, other_deps.classes_);
MergeSets(my_deps->fields_, other_deps.fields_);
MergeSets(my_deps->methods_, other_deps.methods_);
- for (dex::TypeIndex entry : other_deps.unverified_classes_) {
- my_deps->unverified_classes_.push_back(entry);
- }
+ MergeSets(my_deps->unverified_classes_, other_deps.unverified_classes_);
}
}
@@ -503,7 +505,7 @@
VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
if (thread_deps != nullptr) {
DexFileDeps* dex_deps = thread_deps->GetDexFileDeps(dex_file);
- dex_deps->unverified_classes_.push_back(type_idx);
+ dex_deps->unverified_classes_.insert(type_idx);
}
}
@@ -582,6 +584,16 @@
return dex::StringIndex(in);
}
+// TODO: Clean this up, if we use a template arg here it confuses the compiler.
+static inline void EncodeTuple(std::vector<uint8_t>* out, const dex::TypeIndex& t) {
+ EncodeUnsignedLeb128(out, Encode(t));
+}
+
+// TODO: Clean this up, if we use a template arg here it confuses the compiler.
+static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, dex::TypeIndex* t) {
+ *t = Decode<dex::TypeIndex>(DecodeUint32WithOverflowCheck(in, end));
+}
+
template<typename T1, typename T2>
static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2>& t) {
EncodeUnsignedLeb128(out, Encode(std::get<0>(t)));
@@ -688,13 +700,13 @@
EncodeSet(buffer, deps.classes_);
EncodeSet(buffer, deps.fields_);
EncodeSet(buffer, deps.methods_);
- EncodeUint16Vector(buffer, deps.unverified_classes_);
+ EncodeSet(buffer, deps.unverified_classes_);
}
}
VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files,
ArrayRef<const uint8_t> data)
- : VerifierDeps(dex_files) {
+ : VerifierDeps(dex_files, /*output_only*/ false) {
if (data.empty()) {
// Return eagerly, as the first thing we expect from VerifierDeps data is
// the number of created strings, even if there is no dependency.
@@ -711,7 +723,7 @@
DecodeSet(&data_start, data_end, &deps->classes_);
DecodeSet(&data_start, data_end, &deps->fields_);
DecodeSet(&data_start, data_end, &deps->methods_);
- DecodeUint16Vector(&data_start, data_end, &deps->unverified_classes_);
+ DecodeSet(&data_start, data_end, &deps->unverified_classes_);
}
CHECK_LE(data_start, data_end);
}
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index b883a9e..4069a11 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -117,10 +117,14 @@
bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader, Thread* self) const
REQUIRES_SHARED(Locks::mutator_lock_);
- const std::vector<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
+ const std::set<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
return GetDexFileDeps(dex_file)->unverified_classes_;
}
+ bool OutputOnly() const {
+ return output_only_;
+ }
+
private:
static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
@@ -193,11 +197,13 @@
std::set<MethodResolution> methods_;
// List of classes that were not fully verified in that dex file.
- std::vector<dex::TypeIndex> unverified_classes_;
+ std::set<dex::TypeIndex> unverified_classes_;
bool Equals(const DexFileDeps& rhs) const;
};
+ VerifierDeps(const std::vector<const DexFile*>& dex_files, bool output_only);
+
// Finds the DexFileDep instance associated with `dex_file`, or nullptr if
// `dex_file` is not reported as being compiled.
DexFileDeps* GetDexFileDeps(const DexFile& dex_file);
@@ -321,6 +327,9 @@
// Map from DexFiles into dependencies collected from verification of their methods.
std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_;
+ // Output only signifies if we are using the verifier deps to verify or just to generate them.
+ const bool output_only_;
+
friend class VerifierDepsTest;
ART_FRIEND_TEST(VerifierDepsTest, StringToId);
ART_FRIEND_TEST(VerifierDepsTest, EncodeDecode);
diff --git a/runtime/simulator/Android.bp b/simulator/Android.bp
similarity index 60%
rename from runtime/simulator/Android.bp
rename to simulator/Android.bp
index 03e3f15..a399289 100644
--- a/runtime/simulator/Android.bp
+++ b/simulator/Android.bp
@@ -14,6 +14,12 @@
// limitations under the License.
//
+cc_library_headers {
+ name: "libart_simulator_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
cc_defaults {
name: "libart_simulator_defaults",
host_supported: true,
@@ -29,8 +35,8 @@
"liblog",
],
cflags: ["-DVIXL_INCLUDE_SIMULATOR_AARCH64"],
- export_include_dirs: ["."],
- include_dirs: ["art/runtime"],
+
+ header_libs: ["libart_simulator_headers"],
}
art_cc_library {
@@ -53,3 +59,38 @@
"libvixld-arm64",
],
}
+
+cc_defaults {
+ name: "libart_simulator_container_defaults",
+ host_supported: true,
+
+ defaults: ["art_defaults"],
+ srcs: [
+ "code_simulator_container.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+
+ header_libs: ["libart_simulator_headers"],
+ export_include_dirs: ["."], // TODO: Consider a proper separation.
+}
+
+art_cc_library {
+ name: "libart-simulator-container",
+ defaults: ["libart_simulator_container_defaults"],
+ shared_libs: [
+ "libart",
+ ],
+}
+
+art_cc_library {
+ name: "libartd-simulator-container",
+ defaults: [
+ "art_debug_defaults",
+ "libart_simulator_container_defaults",
+ ],
+ shared_libs: [
+ "libartd",
+ ],
+}
diff --git a/runtime/simulator/code_simulator.cc b/simulator/code_simulator.cc
similarity index 92%
rename from runtime/simulator/code_simulator.cc
rename to simulator/code_simulator.cc
index 1a11160..e653dfc 100644
--- a/runtime/simulator/code_simulator.cc
+++ b/simulator/code_simulator.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include "simulator/code_simulator.h"
-#include "simulator/code_simulator_arm64.h"
+#include "code_simulator.h"
+
+#include "code_simulator_arm64.h"
namespace art {
diff --git a/runtime/simulator/code_simulator_arm64.cc b/simulator/code_simulator_arm64.cc
similarity index 97%
rename from runtime/simulator/code_simulator_arm64.cc
rename to simulator/code_simulator_arm64.cc
index c7ad1fd..939d2e2 100644
--- a/runtime/simulator/code_simulator_arm64.cc
+++ b/simulator/code_simulator_arm64.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "simulator/code_simulator_arm64.h"
+#include "code_simulator_arm64.h"
#include "base/logging.h"
diff --git a/runtime/simulator/code_simulator_arm64.h b/simulator/code_simulator_arm64.h
similarity index 88%
rename from runtime/simulator/code_simulator_arm64.h
rename to simulator/code_simulator_arm64.h
index 59ea34f..0542593 100644
--- a/runtime/simulator/code_simulator_arm64.h
+++ b/simulator/code_simulator_arm64.h
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
-#define ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
+#ifndef ART_SIMULATOR_CODE_SIMULATOR_ARM64_H_
+#define ART_SIMULATOR_CODE_SIMULATOR_ARM64_H_
#include "memory"
-#include "simulator/code_simulator.h"
// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
@@ -26,6 +25,8 @@
#include "aarch64/simulator-aarch64.h"
#pragma GCC diagnostic pop
+#include "code_simulator.h"
+
namespace art {
namespace arm64 {
@@ -55,4 +56,4 @@
} // namespace arm64
} // namespace art
-#endif // ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
+#endif // ART_SIMULATOR_CODE_SIMULATOR_ARM64_H_
diff --git a/runtime/code_simulator_container.cc b/simulator/code_simulator_container.cc
similarity index 98%
rename from runtime/code_simulator_container.cc
rename to simulator/code_simulator_container.cc
index d884c58..a5f05dc 100644
--- a/runtime/code_simulator_container.cc
+++ b/simulator/code_simulator_container.cc
@@ -17,6 +17,8 @@
#include <dlfcn.h>
#include "code_simulator_container.h"
+
+#include "code_simulator.h"
#include "globals.h"
namespace art {
diff --git a/runtime/code_simulator_container.h b/simulator/code_simulator_container.h
similarity index 87%
rename from runtime/code_simulator_container.h
rename to simulator/code_simulator_container.h
index 10178ba..31a915e 100644
--- a/runtime/code_simulator_container.h
+++ b/simulator/code_simulator_container.h
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
-#define ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
+#ifndef ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_
+#define ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_
#include "arch/instruction_set.h"
#include "base/logging.h"
-#include "simulator/code_simulator.h"
namespace art {
+class CodeSimulator;
+
// This container dynamically opens and closes libart-simulator.
class CodeSimulatorContainer {
public:
@@ -52,4 +53,4 @@
} // namespace art
-#endif // ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
+#endif // ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_
diff --git a/runtime/simulator/code_simulator.h b/simulator/include/code_simulator.h
similarity index 89%
rename from runtime/simulator/code_simulator.h
rename to simulator/include/code_simulator.h
index bd48909..256ab23 100644
--- a/runtime/simulator/code_simulator.h
+++ b/simulator/include/code_simulator.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
-#define ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
+#ifndef ART_SIMULATOR_INCLUDE_CODE_SIMULATOR_H_
+#define ART_SIMULATOR_INCLUDE_CODE_SIMULATOR_H_
#include "arch/instruction_set.h"
@@ -43,4 +43,4 @@
} // namespace art
-#endif // ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
+#endif // ART_SIMULATOR_INCLUDE_CODE_SIMULATOR_H_
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index f2edd0f..a34d633 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include <iostream>
#include <pthread.h>
-#include <stdio.h>
+
+#include <cstdio>
+#include <iostream>
#include <vector>
#include "art_method-inl.h"
diff --git a/test/004-UnsafeTest/unsafe_test.cc b/test/004-UnsafeTest/unsafe_test.cc
index 4f6ae5a..18d9ea8 100644
--- a/test/004-UnsafeTest/unsafe_test.cc
+++ b/test/004-UnsafeTest/unsafe_test.cc
@@ -17,8 +17,8 @@
#include "art_method-inl.h"
#include "jni.h"
#include "mirror/array.h"
-#include "mirror/class.h"
#include "mirror/class-inl.h"
+#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/test/066-mismatched-super/expected.txt b/test/066-mismatched-super/expected.txt
index 09c0596..f5b15ca 100644
--- a/test/066-mismatched-super/expected.txt
+++ b/test/066-mismatched-super/expected.txt
@@ -1 +1,2 @@
Got expected ICCE
+Got expected VerifyError
diff --git a/test/066-mismatched-super/info.txt b/test/066-mismatched-super/info.txt
index 7865ffc..2b70e06 100644
--- a/test/066-mismatched-super/info.txt
+++ b/test/066-mismatched-super/info.txt
@@ -1,2 +1,5 @@
-This tests what happens when class A extends abstract class B, but somebody
-turns B into an interface without rebuilding A.
+This tests two cases:
+1. What happens when class A extends abstract class B, but somebody
+ turns B into an interface without rebuilding A.
+2. What happens when class A extends a class B, but somebody
+ turns B into a final class without rebuilding A.
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/066-mismatched-super/src/ExtendsFinal.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/066-mismatched-super/src/ExtendsFinal.java
index 023e409..2f53b3b 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/066-mismatched-super/src/ExtendsFinal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,5 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class ExtendsFinal extends Final {
}
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/066-mismatched-super/src/Final.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/066-mismatched-super/src/Final.java
index 023e409..a44d096 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/066-mismatched-super/src/Final.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,5 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public /* final */ class Final {
}
diff --git a/test/066-mismatched-super/src/Main.java b/test/066-mismatched-super/src/Main.java
index 55d0bab..6ae1198 100644
--- a/test/066-mismatched-super/src/Main.java
+++ b/test/066-mismatched-super/src/Main.java
@@ -20,10 +20,16 @@
public class Main {
public static void main(String[] args) {
try {
- Indirect.main();
+ Base base = new Base();
System.out.println("Succeeded unexpectedly");
} catch (IncompatibleClassChangeError icce) {
System.out.println("Got expected ICCE");
}
+ try {
+ ExtendsFinal ef = new ExtendsFinal();
+ System.out.println("Succeeded unexpectedly");
+ } catch (VerifyError ve) {
+ System.out.println("Got expected VerifyError");
+ }
}
}
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/066-mismatched-super/src2/Final.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/066-mismatched-super/src2/Final.java
index 023e409..766da9b 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/066-mismatched-super/src2/Final.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,5 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public final class Final {
}
diff --git a/test/075-verification-error/expected.txt b/test/075-verification-error/expected.txt
index 6e4f584..7ccc32c 100644
--- a/test/075-verification-error/expected.txt
+++ b/test/075-verification-error/expected.txt
@@ -10,3 +10,4 @@
Got expected IllegalAccessError (meth-class)
Got expected IllegalAccessError (field-class)
Got expected IllegalAccessError (meth-meth)
+Got expected IncompatibleClassChangeError (interface)
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/075-verification-error/src/BadIfaceImpl.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/075-verification-error/src/BadIfaceImpl.java
index 023e409..fa2a970 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/075-verification-error/src/BadIfaceImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,4 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
-}
+public class BadIfaceImpl implements BadInterface { }
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/075-verification-error/src/BadInterface.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/075-verification-error/src/BadInterface.java
index 023e409..439aba4 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/075-verification-error/src/BadInterface.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public interface BadInterface {
+ public default Object internalClone() {
+ throw new Error("Should not be called");
+ }
}
diff --git a/test/075-verification-error/src/Main.java b/test/075-verification-error/src/Main.java
index 3f2881e..13aeaee 100644
--- a/test/075-verification-error/src/Main.java
+++ b/test/075-verification-error/src/Main.java
@@ -28,6 +28,20 @@
testClassNewInstance();
testMissingStuff();
testBadAccess();
+ testBadInterfaceMethod();
+ }
+ /**
+ * Try to create and invoke a non-existant interface method.
+ */
+ static void testBadInterfaceMethod() {
+ BadInterface badiface = new BadIfaceImpl();
+ try {
+ badiface.internalClone();
+ } catch (IncompatibleClassChangeError icce) {
+ // TODO b/64274113 This should really be an NSME
+ System.out.println("Got expected IncompatibleClassChangeError (interface)");
+ if (VERBOSE) System.out.println("--- " + icce);
+ }
}
/**
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/075-verification-error/src2/BadInterface.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/075-verification-error/src2/BadInterface.java
index 023e409..5d939cb 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/075-verification-error/src2/BadInterface.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -13,15 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+public interface BadInterface { }
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
-}
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 307fd9b..a049b97 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -16,20 +16,21 @@
// A simple implementation of the native-bridge interface.
-#include <algorithm>
#include <dlfcn.h>
-#include <jni.h>
-#include <stdlib.h>
+#include <setjmp.h>
#include <signal.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
#include <vector>
-#include "stdio.h"
-#include "unistd.h"
-#include "sys/stat.h"
-#include "setjmp.h"
+#include <jni.h>
+#include <nativebridge/native_bridge.h>
#include "base/macros.h"
-#include "nativebridge/native_bridge.h"
struct NativeBridgeMethod {
const char* name;
diff --git a/test/1338-gc-no-los/expected.txt b/test/1338-gc-no-los/expected.txt
new file mode 100644
index 0000000..36bec43
--- /dev/null
+++ b/test/1338-gc-no-los/expected.txt
@@ -0,0 +1 @@
+131072 200 true
diff --git a/test/1338-gc-no-los/info.txt b/test/1338-gc-no-los/info.txt
new file mode 100644
index 0000000..2e27357
--- /dev/null
+++ b/test/1338-gc-no-los/info.txt
@@ -0,0 +1 @@
+Test that the GC works with no large object space. Regression test for b/64393515.
\ No newline at end of file
diff --git a/test/1338-gc-no-los/run b/test/1338-gc-no-los/run
new file mode 100755
index 0000000..f3c43fb
--- /dev/null
+++ b/test/1338-gc-no-los/run
@@ -0,0 +1,16 @@
+#!/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.
+./default-run "$@" --runtime-option -XX:LargeObjectSpace=disabled
diff --git a/test/1338-gc-no-los/src-art/Main.java b/test/1338-gc-no-los/src-art/Main.java
new file mode 100644
index 0000000..662fa7e
--- /dev/null
+++ b/test/1338-gc-no-los/src-art/Main.java
@@ -0,0 +1,39 @@
+/*
+ * 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 dalvik.system.VMRuntime;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+
+public class Main {
+ public static void main(String[] args) {
+ try {
+ // Allocate a large object.
+ byte[] arr = new byte[128 * 1024];
+ // Allocate a non movable object.
+ byte[] arr2 = (byte[])VMRuntime.getRuntime().newNonMovableArray(Byte.TYPE, 200);
+ // Put the array in a weak reference so that IsMarked is called by the GC.
+ WeakReference weakRef = new WeakReference(arr2);
+ // Do a GC.
+ Runtime.getRuntime().gc();
+ arr[0] = 1;
+ arr2[0] = 1;
+ System.out.println(arr.length + " " + arr2.length + " " + (weakRef.get() != null));
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 1ed1f5a..ad705c5 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -18,15 +18,15 @@
#include <errno.h>
#include <signal.h>
#include <string.h>
-#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
+#include <unistd.h>
#endif
#include "jni.h"
-#include "android-base/stringprintf.h"
#include <backtrace/Backtrace.h>
+#include "android-base/stringprintf.h"
#include "base/logging.h"
#include "base/macros.h"
diff --git a/test/162-method-resolution/expected.txt b/test/162-method-resolution/expected.txt
index 1bf39c9..9b48a4c 100644
--- a/test/162-method-resolution/expected.txt
+++ b/test/162-method-resolution/expected.txt
@@ -41,3 +41,6 @@
Calling Test9User2.test():
Caught java.lang.reflect.InvocationTargetException
caused by java.lang.IncompatibleClassChangeError
+Calling Test10User.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IncompatibleClassChangeError
diff --git a/test/162-method-resolution/jasmin/Test10Base.j b/test/162-method-resolution/jasmin/Test10Base.j
new file mode 100644
index 0000000..628f38d
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test10Base.j
@@ -0,0 +1,25 @@
+; 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.
+
+.class public Test10Base
+.super java/lang/Object
+.implements Test10Interface
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial java/lang/Object/<init>()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test10User.j b/test/162-method-resolution/jasmin/Test10User.j
new file mode 100644
index 0000000..6beadab
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test10User.j
@@ -0,0 +1,36 @@
+; 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.
+
+.class public Test10User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 3
+ .limit locals 3
+ new Test10Base
+ dup
+ invokespecial Test10Base.<init>()V
+ invokestatic Test10User.doInvoke(LTest10Interface;)V
+ return
+.end method
+
+.method public static doInvoke(LTest10Interface;)V
+ .limit stack 3
+ .limit locals 3
+ aload_0
+ invokeinterface Test10Interface.clone()Ljava.lang.Object; 1
+ pop
+ return
+.end method
+
diff --git a/test/162-method-resolution/multidex.jpp b/test/162-method-resolution/multidex.jpp
index 22e3aee..5722f7f 100644
--- a/test/162-method-resolution/multidex.jpp
+++ b/test/162-method-resolution/multidex.jpp
@@ -112,6 +112,16 @@
@@com.android.jack.annotations.ForceInMainDex
class Test9User2
+Test10Base:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test10Base
+Test10Interface:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test10Interface
+Test10User:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test10User
+
Main:
@@com.android.jack.annotations.ForceInMainDex
class Main
diff --git a/test/162-method-resolution/src/Main.java b/test/162-method-resolution/src/Main.java
index fa95aa7..864c878 100644
--- a/test/162-method-resolution/src/Main.java
+++ b/test/162-method-resolution/src/Main.java
@@ -36,6 +36,7 @@
test7();
test8();
test9();
+ test10();
// TODO: How to test that interface method resolution returns the unique
// maximally-specific non-abstract superinterface method if there is one?
@@ -376,6 +377,31 @@
invokeUserTest("Test9User2");
}
+ /*
+ * Test10
+ * -----
+ * Tested function:
+ * public class Test10Base implements Test10Interface { }
+ * public interface Test10Interface { }
+ * Tested invokes:
+ * invoke-interface Test10Interface.clone()Ljava/lang/Object; from Test10Caller in first dex
+ * TODO b/64274113 This should throw a NSME (JLS 13.4.12) but actually throws an ICCE.
+ * expected: Throws NoSuchMethodError (JLS 13.4.12)
+ * actual: Throws IncompatibleClassChangeError
+ *
+ * This test is simulating compiling Test10Interface with "public Object clone()" method, along
+ * with every other class. Then we delete "clone" from Test10Interface only, which under JLS
+ * 13.4.12 is expected to be binary incompatible and throw a NoSuchMethodError.
+ *
+ * Files:
+ * jasmin/Test10Base.j - implements Test10Interface
+ * jasmin/Test10Interface.java - defines empty interface
+ * jasmin/Test10User.j - invokeinterface Test10Interface.clone()Ljava/lang/Object;
+ */
+ private static void test10() throws Exception {
+ invokeUserTest("Test10User");
+ }
+
private static void invokeUserTest(String userName) throws Exception {
System.out.println("Calling " + userName + ".test():");
try {
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/162-method-resolution/src/Test10Interface.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/162-method-resolution/src/Test10Interface.java
index 023e409..3c75ea5 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/162-method-resolution/src/Test10Interface.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,5 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
-}
+public interface Test10Interface { }
+
diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc
index edcb734..2b3f8c8 100644
--- a/test/1901-get-bytecodes/bytecodes.cc
+++ b/test/1901-get-bytecodes/bytecodes.cc
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-#include <iostream>
#include <pthread.h>
-#include <stdio.h>
+
+#include <cstdio>
+#include <iostream>
#include <vector>
#include "android-base/logging.h"
#include "jni.h"
+#include "jvmti.h"
+
#include "scoped_local_ref.h"
#include "scoped_primitive_array.h"
-#include "jvmti.h"
-
// Test infrastructure
#include "jvmti_helper.h"
#include "test_env.h"
diff --git a/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/test/1911-get-local-var-table/expected.txt
similarity index 100%
copy from runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
copy to test/1911-get-local-var-table/expected.txt
diff --git a/test/1911-get-local-var-table/info.txt b/test/1911-get-local-var-table/info.txt
new file mode 100644
index 0000000..43955e5
--- /dev/null
+++ b/test/1911-get-local-var-table/info.txt
@@ -0,0 +1 @@
+Tests getting local variable table from JVMTI
diff --git a/test/1911-get-local-var-table/run b/test/1911-get-local-var-table/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1911-get-local-var-table/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/066-mismatched-super/src/Indirect.java b/test/1911-get-local-var-table/src/Main.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/1911-get-local-var-table/src/Main.java
index 023e409..4e0c9e4 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/1911-get-local-var-table/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1911.run();
+ }
}
diff --git a/test/1911-get-local-var-table/src/art/Breakpoint.java b/test/1911-get-local-var-table/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1911-get-local-var-table/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/1911-get-local-var-table/src/art/Locals.java b/test/1911-get-local-var-table/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1911-get-local-var-table/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/1911-get-local-var-table/src/art/Suspension.java b/test/1911-get-local-var-table/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1911-get-local-var-table/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/1911-get-local-var-table/src/art/Test1911.java b/test/1911-get-local-var-table/src/art/Test1911.java
new file mode 100644
index 0000000..4dd9054
--- /dev/null
+++ b/test/1911-get-local-var-table/src/art/Test1911.java
@@ -0,0 +1,218 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Test1911 {
+ // Class/dex file containing the following class.
+ //
+ // CLASS_BYTES generated with java version 1.8.0_45: javac -g art/Target.java
+ // DEX_BYTES generated with dx version 1.14: dx --dex --output=./classes.dex art/Target.class
+ //
+ // package art;
+ // import java.util.ArrayList;
+ // public class Target {
+ // public int zzz;
+ // public Target(int xxx) {
+ // int q = xxx * 4;
+ // zzz = q;
+ // }
+ // public static void doNothing(Object... objs) { doNothing(objs); }
+ // public void doSomething(int x) {
+ // doNothing(this);
+ // int y = x + 3;
+ // for (int z = 0; z < y * x; z++) {
+ // float q = y - z;
+ // double i = 0.3d * q;
+ // doNothing(q, i);
+ // }
+ // Object o = new Object();
+ // ArrayList<Integer> i = new ArrayList<>();
+ // int p = 4 | x;
+ // long q = 3 * p;
+ // doNothing(p, q, o, i);
+ // }
+ // }
+ public static byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQARgoABAAuCQANAC8KAA0AMAcAMQY/0zMzMzMzMwoAMgAzCgA0ADUHADYKAAkALgoA" +
+ "NwA4CgA5ADoHADsBAAN6enoBAAFJAQAGPGluaXQ+AQAEKEkpVgEABENvZGUBAA9MaW5lTnVtYmVy" +
+ "VGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAMTGFydC9UYXJnZXQ7AQADeHh4AQAB" +
+ "cQEACWRvTm90aGluZwEAFihbTGphdmEvbGFuZy9PYmplY3Q7KVYBAARvYmpzAQATW0xqYXZhL2xh" +
+ "bmcvT2JqZWN0OwEAC2RvU29tZXRoaW5nAQABRgEAAWkBAAFEAQABegEAAXgBAAF5AQABbwEAEkxq" +
+ "YXZhL2xhbmcvT2JqZWN0OwEAFUxqYXZhL3V0aWwvQXJyYXlMaXN0OwEAAXABAAFKAQAWTG9jYWxW" +
+ "YXJpYWJsZVR5cGVUYWJsZQEAKkxqYXZhL3V0aWwvQXJyYXlMaXN0PExqYXZhL2xhbmcvSW50ZWdl" +
+ "cjs+OwEADVN0YWNrTWFwVGFibGUBAApTb3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMABAAPAwADgAP" +
+ "DAAZABoBABBqYXZhL2xhbmcvT2JqZWN0BwA9DAA+AD8HAEAMAD4AQQEAE2phdmEvdXRpbC9BcnJh" +
+ "eUxpc3QHAEIMAD4AQwcARAwAPgBFAQAKYXJ0L1RhcmdldAEAAygpVgEAD2phdmEvbGFuZy9GbG9h" +
+ "dAEAB3ZhbHVlT2YBABQoRilMamF2YS9sYW5nL0Zsb2F0OwEAEGphdmEvbGFuZy9Eb3VibGUBABUo" +
+ "RClMamF2YS9sYW5nL0RvdWJsZTsBABFqYXZhL2xhbmcvSW50ZWdlcgEAFihJKUxqYXZhL2xhbmcv" +
+ "SW50ZWdlcjsBAA5qYXZhL2xhbmcvTG9uZwEAEyhKKUxqYXZhL2xhbmcvTG9uZzsAIQANAAQAAAAB" +
+ "AAEADgAPAAAAAwABABAAEQABABIAAABYAAIAAwAAAA4qtwABGwdoPSoctQACsQAAAAIAEwAAABIA" +
+ "BAAAAAUABAAGAAgABwANAAgAFAAAACAAAwAAAA4AFQAWAAAAAAAOABcADwABAAgABgAYAA8AAgCJ" +
+ "ABkAGgABABIAAAAvAAEAAQAAAAUquAADsQAAAAIAEwAAAAYAAQAAAAkAFAAAAAwAAQAAAAUAGwAc" +
+ "AAAAAQAdABEAAQASAAABWAAFAAgAAACCBL0ABFkDKlO4AAMbBmA9Az4dHBtoogAvHB1khjgEFAAF" +
+ "FwSNazkFBb0ABFkDFwS4AAdTWQQYBbgACFO4AAOEAwGn/9C7AARZtwABTrsACVm3AAo6BAcbgDYF" +
+ "BhUFaIU3Bge9AARZAxUFuAALU1kEFga4AAxTWQUtU1kGGQRTuAADsQAAAAQAEwAAADYADQAAAAsA" +
+ "CwAMAA8ADQAYAA4AHgAPACcAEAA+AA0ARAASAEwAEwBVABQAWgAVAGEAFgCBABcAFAAAAGYACgAe" +
+ "ACAAGAAeAAQAJwAXAB8AIAAFABEAMwAhAA8AAwAAAIIAFQAWAAAAAACCACIADwABAA8AcwAjAA8A" +
+ "AgBMADYAJAAlAAMAVQAtAB8AJgAEAFoAKAAnAA8ABQBhACEAGAAoAAYAKQAAAAwAAQBVAC0AHwAq" +
+ "AAQAKwAAAAoAAv0AEQEB+gAyAAEALAAAAAIALQ==");
+ public static byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCQtgjEV631Ma/btYyIy2IzqHWNN+nZiwl0BQAAcAAAAHhWNBIAAAAAAAAAANQEAAAk" +
+ "AAAAcAAAAA0AAAAAAQAABwAAADQBAAABAAAAiAEAAAkAAACQAQAAAQAAANgBAAB8AwAA+AEAAB4D" +
+ "AAAmAwAAKQMAACwDAAAvAwAAMgMAADYDAAA6AwAAPgMAAEIDAABQAwAAZAMAAHcDAACMAwAAngMA" +
+ "ALIDAADJAwAA9QMAAAIEAAAFBAAACQQAAA0EAAAiBAAALQQAADoEAAA9BAAAQAQAAEYEAABJBAAA" +
+ "TAQAAFIEAABbBAAAXgQAAGMEAABmBAAAaQQAAAEAAAACAAAAAwAAAAQAAAAJAAAACgAAAAsAAAAM" +
+ "AAAADQAAAA4AAAAPAAAAEgAAABUAAAAFAAAABQAAAPgCAAAGAAAABgAAAAADAAAHAAAABwAAAAgD" +
+ "AAAIAAAACAAAABADAAASAAAACwAAAAAAAAATAAAACwAAAAgDAAAUAAAACwAAABgDAAAEAAIAIwAA" +
+ "AAQABQAAAAAABAAGABYAAAAEAAUAFwAAAAUAAAAeAAAABgABAB4AAAAHAAIAHgAAAAgAAwAeAAAA" +
+ "CQAEAAAAAAAKAAQAAAAAAAQAAAABAAAACQAAAAAAAAARAAAAAAAAAL4EAAAAAAAAAwACAAEAAABu" +
+ "BAAACAAAAHAQBwABANoAAgRZEAAADgABAAEAAQAAAHsEAAAEAAAAcRABAAAADgAQAAIAAgAAAIEE" +
+ "AABcAAAAEhkjmQwAEgpNDgkKcRABAAkA2AUPAxIIkgkFDzWYJACRCQUIgpYYCjMzMzMzM9M/iWyt" +
+ "AAoMEikjmQwAEgpxEAQABgAMC00LCQoSGnEgAwAQAAwLTQsJCnEQAQAJANgICAEo2yIDCQBwEAcA" +
+ "AwAiAgoAcBAIAAIA3gQPBNoJBAOBlhJJI5kMABIKcRAFAAQADAtNCwkKEhpxIAYAdgAMC00LCQoS" +
+ "Kk0DCQoSOk0CCQpxEAEACQAOAAEAAAAAAAAAAQAAAAEAAAABAAAAAgAAAAEAAAADAAAAAQAAAAwA" +
+ "Bjxpbml0PgABRAABRgABSQABSgACTEQAAkxGAAJMSQACTEoADExhcnQvVGFyZ2V0OwASTGphdmEv" +
+ "bGFuZy9Eb3VibGU7ABFMamF2YS9sYW5nL0Zsb2F0OwATTGphdmEvbGFuZy9JbnRlZ2VyOwAQTGph" +
+ "dmEvbGFuZy9Mb25nOwASTGphdmEvbGFuZy9PYmplY3Q7ABVMamF2YS91dGlsL0FycmF5TGlzdDsA" +
+ "KkxqYXZhL3V0aWwvQXJyYXlMaXN0PExqYXZhL2xhbmcvSW50ZWdlcjs+OwALVGFyZ2V0LmphdmEA" +
+ "AVYAAlZJAAJWTAATW0xqYXZhL2xhbmcvT2JqZWN0OwAJZG9Ob3RoaW5nAAtkb1NvbWV0aGluZwAB" +
+ "aQABbwAEb2JqcwABcAABcQAEdGhpcwAHdmFsdWVPZgABeAADeHh4AAF5AAF6AAN6enoABQEhBw48" +
+ "LQMAHQMtAAkBGwcOAAsBIAcOli0DBSIDAQEDCCMDSzwDBh0ChwMAGQEBFAtABQAFBloDAxoKWgQC" +
+ "GQsRLQMEHAM8AwYdBAEaDwAAAQIBAAEAgYAE+AMBiQGYBAIBsAQADQAAAAAAAAABAAAAAAAAAAEA" +
+ "AAAkAAAAcAAAAAIAAAANAAAAAAEAAAMAAAAHAAAANAEAAAQAAAABAAAAiAEAAAUAAAAJAAAAkAEA" +
+ "AAYAAAABAAAA2AEAAAEgAAADAAAA+AEAAAEQAAAFAAAA+AIAAAIgAAAkAAAAHgMAAAMgAAADAAAA" +
+ "bgQAAAAgAAABAAAAvgQAAAAQAAABAAAA1AQAAA==");
+
+
+ // The variables of the functions in the above Target class.
+ public static Set<Locals.VariableDescription>[] CONSTRUCTOR_VARIABLES = new Set[] {
+ // RI Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(8, 6, "q", "I", null, 2),
+ new Locals.VariableDescription(0, 14, "xxx", "I", null, 1),
+ new Locals.VariableDescription(0, 14, "this", "Lart/Target;", null, 0))),
+ // ART Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(0, 8, "this", "Lart/Target;", null, 1),
+ new Locals.VariableDescription(5, 3, "q", "I", null, 0),
+ new Locals.VariableDescription(0, 8, "xxx", "I", null, 2))),
+ };
+
+ public static Set<Locals.VariableDescription>[] DO_NOTHING_VARIABLES = new Set[] {
+ // RI Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(0, 5, "objs", "[Ljava/lang/Object;", null, 0))),
+ // ART Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(0, 4, "objs", "[Ljava/lang/Object;", null, 0))),
+ };
+
+ public static Set<Locals.VariableDescription>[] DO_SOMETHING_VARIABLES = new Set[] {
+ // RI Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(0, 130, "x", "I", null, 1),
+ new Locals.VariableDescription(76, 54, "o", "Ljava/lang/Object;", null, 3),
+ new Locals.VariableDescription(30, 32, "q", "F", null, 4),
+ new Locals.VariableDescription(39, 23, "i", "D", null, 5),
+ new Locals.VariableDescription(17, 51, "z", "I", null, 3),
+ new Locals.VariableDescription(15, 115, "y", "I", null, 2),
+ new Locals.VariableDescription(90, 40, "p", "I", null, 5),
+ new Locals.VariableDescription(97, 33, "q", "J", null, 6),
+ new Locals.VariableDescription(0, 130, "this", "Lart/Target;", null, 0),
+ new Locals.VariableDescription(85,
+ 45,
+ "i",
+ "Ljava/util/ArrayList;",
+ "Ljava/util/ArrayList<Ljava/lang/Integer;>;",
+ 4))),
+ // ART Local variable table
+ new HashSet<>(Arrays.asList(
+ new Locals.VariableDescription(19, 31, "q", "F", null, 6),
+ new Locals.VariableDescription(55, 37, "o", "Ljava/lang/Object;", null, 3),
+ new Locals.VariableDescription(0, 92, "this", "Lart/Target;", null, 14),
+ new Locals.VariableDescription(12, 80, "z", "I", null, 8),
+ new Locals.VariableDescription(11, 81, "y", "I", null, 5),
+ new Locals.VariableDescription(62, 30, "p", "I", null, 4),
+ new Locals.VariableDescription(0, 92, "x", "I", null, 15),
+ new Locals.VariableDescription(27, 23, "i", "D", null, 0),
+ new Locals.VariableDescription(65, 27, "q", "J", null, 6),
+ new Locals.VariableDescription(60,
+ 32,
+ "i",
+ "Ljava/util/ArrayList;",
+ "Ljava/util/ArrayList<Ljava/lang/Integer;>;",
+ 2))),
+ };
+
+ // Get a classloader that can load the Target class.
+ public static ClassLoader getClassLoader() throws Exception {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+ Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+ // We are on art since we got the InMemoryDexClassLoader.
+ return (ClassLoader)ctor.newInstance(
+ ByteBuffer.wrap(DEX_BYTES), Test1911.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Running on RI.
+ return new ClassLoader(Test1911.class.getClassLoader()) {
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("art.Target")) {
+ return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length);
+ } else {
+ return super.findClass(name);
+ }
+ }
+ };
+ }
+ }
+
+ public static void CheckLocalVariableTable(Executable m,
+ Set<Locals.VariableDescription>[] possible_vars) {
+ Set<Locals.VariableDescription> real_vars =
+ new HashSet<>(Arrays.asList(Locals.GetLocalVariableTable(m)));
+ for (Set<Locals.VariableDescription> pos : possible_vars) {
+ if (pos.equals(real_vars)) {
+ return;
+ }
+ }
+ System.out.println("Unexpected variables for " + m);
+ System.out.println("Received: " + real_vars);
+ System.out.println("Expected one of:");
+ for (Object pos : possible_vars) {
+ System.out.println("\t" + pos);
+ }
+ }
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ Class<?> target = getClassLoader().loadClass("art.Target");
+ CheckLocalVariableTable(target.getDeclaredConstructor(Integer.TYPE),
+ CONSTRUCTOR_VARIABLES);
+ CheckLocalVariableTable(target.getDeclaredMethod("doNothing", (new Object[0]).getClass()),
+ DO_NOTHING_VARIABLES);
+ CheckLocalVariableTable(target.getDeclaredMethod("doSomething", Integer.TYPE),
+ DO_SOMETHING_VARIABLES);
+ }
+}
+
diff --git a/test/1912-get-set-local-primitive/expected.txt b/test/1912-get-set-local-primitive/expected.txt
new file mode 100644
index 0000000..f2c5ce8
--- /dev/null
+++ b/test/1912-get-set-local-primitive/expected.txt
@@ -0,0 +1,108 @@
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "GetInt" on remote thread.
+"GetInt" on public static void art.Test1912.IntMethod(java.lang.Runnable) got value: 42
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "GetLong" on remote thread.
+"GetLong" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "GetFloat" on remote thread.
+"GetFloat" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "GetDouble" on remote thread.
+"GetDouble" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "SetInt" on remote thread.
+"SetInt" on public static void art.Test1912.IntMethod(java.lang.Runnable) set value: 2147483647
+ Value is '2147483647' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "SetLong" on remote thread.
+"SetLong" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed to set value 9223372036854775807 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "SetFloat" on remote thread.
+"SetFloat" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed to set value 9.2 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.IntMethod(java.lang.Runnable) with "SetDouble" on remote thread.
+"SetDouble" on public static void art.Test1912.IntMethod(java.lang.Runnable) failed to set value 12.4 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "GetInt" on remote thread.
+"GetInt" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "GetLong" on remote thread.
+"GetLong" on public static void art.Test1912.LongMethod(java.lang.Runnable) got value: 9001
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "GetFloat" on remote thread.
+"GetFloat" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "GetDouble" on remote thread.
+"GetDouble" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "SetInt" on remote thread.
+"SetInt" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed to set value 2147483647 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "SetLong" on remote thread.
+"SetLong" on public static void art.Test1912.LongMethod(java.lang.Runnable) set value: 9223372036854775807
+ Value is '9223372036854775807' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "SetFloat" on remote thread.
+"SetFloat" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed to set value 9.2 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.LongMethod(java.lang.Runnable) with "SetDouble" on remote thread.
+"SetDouble" on public static void art.Test1912.LongMethod(java.lang.Runnable) failed to set value 12.4 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '9001' (class: class java.lang.Long)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "GetInt" on remote thread.
+"GetInt" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "GetLong" on remote thread.
+"GetLong" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "GetFloat" on remote thread.
+"GetFloat" on public static void art.Test1912.FloatMethod(java.lang.Runnable) got value: 1.618
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "GetDouble" on remote thread.
+"GetDouble" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "SetInt" on remote thread.
+"SetInt" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed to set value 2147483647 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "SetLong" on remote thread.
+"SetLong" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed to set value 9223372036854775807 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "SetFloat" on remote thread.
+"SetFloat" on public static void art.Test1912.FloatMethod(java.lang.Runnable) set value: 9.2
+ Value is '9.2' (class: class java.lang.Float)
+Running public static void art.Test1912.FloatMethod(java.lang.Runnable) with "SetDouble" on remote thread.
+"SetDouble" on public static void art.Test1912.FloatMethod(java.lang.Runnable) failed to set value 12.4 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '1.618' (class: class java.lang.Float)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "GetInt" on remote thread.
+"GetInt" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "GetLong" on remote thread.
+"GetLong" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "GetFloat" on remote thread.
+"GetFloat" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "GetDouble" on remote thread.
+"GetDouble" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) got value: 3.1415
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "SetInt" on remote thread.
+"SetInt" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed to set value 2147483647 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "SetLong" on remote thread.
+"SetLong" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed to set value 9223372036854775807 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "SetFloat" on remote thread.
+"SetFloat" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) failed to set value 9.2 due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '3.1415' (class: class java.lang.Double)
+Running public static void art.Test1912.DoubleMethod(java.lang.Runnable) with "SetDouble" on remote thread.
+"SetDouble" on public static void art.Test1912.DoubleMethod(java.lang.Runnable) set value: 12.4
+ Value is '12.4' (class: class java.lang.Double)
+Running public static void art.Test1912.BooleanMethod(java.lang.Runnable) with "SetIntBoolSize" on remote thread.
+"SetIntBoolSize" on public static void art.Test1912.BooleanMethod(java.lang.Runnable) set value: 1
+ Value is 'true' (class: class java.lang.Boolean)
+Running public static void art.Test1912.ByteMethod(java.lang.Runnable) with "SetIntByteSize" on remote thread.
+"SetIntByteSize" on public static void art.Test1912.ByteMethod(java.lang.Runnable) set value: 126
+ Value is '126' (class: class java.lang.Byte)
+Running public static void art.Test1912.CharMethod(java.lang.Runnable) with "SetIntCharSize" on remote thread.
+"SetIntCharSize" on public static void art.Test1912.CharMethod(java.lang.Runnable) set value: 65534
+ Value is '<Char: -1>' (class: class java.lang.String)
+Running public static void art.Test1912.ShortMethod(java.lang.Runnable) with "SetIntShortSize" on remote thread.
+"SetIntShortSize" on public static void art.Test1912.ShortMethod(java.lang.Runnable) set value: 32766
+ Value is '32766' (class: class java.lang.Short)
diff --git a/test/1912-get-set-local-primitive/info.txt b/test/1912-get-set-local-primitive/info.txt
new file mode 100644
index 0000000..87a7b35
--- /dev/null
+++ b/test/1912-get-set-local-primitive/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable primitives.
+
diff --git a/test/1912-get-set-local-primitive/run b/test/1912-get-set-local-primitive/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1912-get-set-local-primitive/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/066-mismatched-super/src/Indirect.java b/test/1912-get-set-local-primitive/src/Main.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/1912-get-set-local-primitive/src/Main.java
index 023e409..9ff69f8 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/1912-get-set-local-primitive/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1912.run();
+ }
}
diff --git a/test/1912-get-set-local-primitive/src/art/Breakpoint.java b/test/1912-get-set-local-primitive/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1912-get-set-local-primitive/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/1912-get-set-local-primitive/src/art/Locals.java b/test/1912-get-set-local-primitive/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1912-get-set-local-primitive/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/1912-get-set-local-primitive/src/art/StackTrace.java b/test/1912-get-set-local-primitive/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1912-get-set-local-primitive/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/1912-get-set-local-primitive/src/art/Suspension.java b/test/1912-get-set-local-primitive/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1912-get-set-local-primitive/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/1912-get-set-local-primitive/src/art/Test1912.java b/test/1912-get-set-local-primitive/src/art/Test1912.java
new file mode 100644
index 0000000..24149f4
--- /dev/null
+++ b/test/1912-get-set-local-primitive/src/art/Test1912.java
@@ -0,0 +1,260 @@
+/*
+ * 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.Method;
+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;
+
+// TODO Rename test to set-get-local-prim
+
+public class Test1912 {
+ public static final String TARGET_VAR = "TARGET";
+
+
+ public static void reportValue(Object val) {
+ if (val instanceof Character) {
+ val = "<Char: " + Character.getNumericValue(((Character)val).charValue()) + ">";
+ }
+ System.out.println("\tValue is '" + val + "' (class: " + val.getClass() + ")");
+ }
+
+ public static void BooleanMethod(Runnable safepoint) {
+ boolean TARGET = false;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void ByteMethod(Runnable safepoint) {
+ byte TARGET = 8;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void CharMethod(Runnable safepoint) {
+ char TARGET = 'q';
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void ShortMethod(Runnable safepoint) {
+ short TARGET = 321;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void IntMethod(Runnable safepoint) {
+ int TARGET = 42;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void LongMethod(Runnable safepoint) {
+ long TARGET = 9001;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void FloatMethod(Runnable safepoint) {
+ float TARGET = 1.618f;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+ public static void DoubleMethod(Runnable safepoint) {
+ double TARGET = 3.1415d;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static interface SafepointFunction {
+ public void invoke(
+ Thread thread,
+ Method target,
+ Locals.VariableDescription TARGET_desc,
+ int depth) throws Exception;
+ }
+
+ public static interface SetterFunction {
+ public void SetVar(Thread t, int depth, int slot, Object v);
+ }
+
+ public static interface GetterFunction {
+ public Object GetVar(Thread t, int depth, int slot);
+ }
+
+ public static SafepointFunction NamedSet(
+ final String type, final SetterFunction get, final Object v) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
+ try {
+ get.SetVar(t, depth, desc.slot, v);
+ System.out.println(this + " on " + method + " set value: " + v);
+ } catch (Exception e) {
+ System.out.println(
+ this + " on " + method + " failed to set value " + v + " due to " + e.getMessage());
+ }
+ }
+ public String toString() {
+ return "\"Set" + type + "\"";
+ }
+ };
+ }
+
+ public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
+ try {
+ Object res = get.GetVar(t, depth, desc.slot);
+ 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 Method target;
+
+ public TestCase(Method target) {
+ 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(null, 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);
+ Locals.VariableDescription desc = findTargetVar(frame.current_location);
+ safepoint.invoke(remote, target, desc, frame.depth);
+ } finally {
+ Suspension.resume(remote);
+ pause.wakeupOtherThread();
+ remote.join();
+ }
+ }
+
+ private Locals.VariableDescription findTargetVar(long loc) {
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc);
+ }
+
+ private StackTrace.StackFrameData findStackFrame(Thread thr) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+ }
+ public static Method getMethod(String name) throws Exception {
+ return Test1912.class.getDeclaredMethod(name, Runnable.class);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final TestCase[] MAIN_TEST_CASES = new TestCase[] {
+ new TestCase(getMethod("IntMethod")),
+ new TestCase(getMethod("LongMethod")),
+ new TestCase(getMethod("FloatMethod")),
+ new TestCase(getMethod("DoubleMethod")),
+ };
+
+ final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] {
+ NamedGet("Int", Locals::GetLocalVariableInt),
+ NamedGet("Long", Locals::GetLocalVariableLong),
+ NamedGet("Float", Locals::GetLocalVariableFloat),
+ NamedGet("Double", Locals::GetLocalVariableDouble),
+ NamedSet("Int", Locals::SetLocalVariableInt, Integer.MAX_VALUE),
+ NamedSet("Long", Locals::SetLocalVariableLong, Long.MAX_VALUE),
+ NamedSet("Float", Locals::SetLocalVariableFloat, 9.2f),
+ NamedSet("Double", Locals::SetLocalVariableDouble, 12.4d),
+ };
+
+ for (TestCase t: MAIN_TEST_CASES) {
+ for (SafepointFunction s : SAFEPOINTS) {
+ t.exec(s);
+ }
+ }
+
+ // Test int for small values.
+ new TestCase(getMethod("BooleanMethod")).exec(
+ NamedSet("IntBoolSize", Locals::SetLocalVariableInt, 1));
+ new TestCase(getMethod("ByteMethod")).exec(
+ NamedSet("IntByteSize", Locals::SetLocalVariableInt, Byte.MAX_VALUE - 1));
+
+ new TestCase(getMethod("CharMethod")).exec(
+ NamedSet("IntCharSize", Locals::SetLocalVariableInt, Character.MAX_VALUE - 1));
+ new TestCase(getMethod("ShortMethod")).exec(
+ NamedSet("IntShortSize", Locals::SetLocalVariableInt, Short.MAX_VALUE - 1));
+ }
+}
+
diff --git a/test/1913-get-set-local-objects/expected.txt b/test/1913-get-set-local-objects/expected.txt
new file mode 100644
index 0000000..23f4992
--- /dev/null
+++ b/test/1913-get-set-local-objects/expected.txt
@@ -0,0 +1,72 @@
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "GetGetObject" on remote thread.
+"GetGetObject" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) got value: TestClass1("ObjectMethod")
+ Value is 'TestClass1("ObjectMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetNull" on remote thread.
+"SetNull" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: null
+ Value is 'null' (class: NULL)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetTestClass1" on remote thread.
+"SetTestClass1" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: TestClass1("Set TestClass1")
+ Value is 'TestClass1("Set TestClass1")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetTestClass1ext" on remote thread.
+"SetTestClass1ext" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: TestClass1ext("TestClass1("Set TestClass1ext")")
+ Value is 'TestClass1ext("TestClass1("Set TestClass1ext")")' (class: class art.Test1913$TestClass1ext)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetTestClass2" on remote thread.
+"SetTestClass2" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: TestClass2("Set TestClass2")
+ Value is 'TestClass2("Set TestClass2")' (class: class art.Test1913$TestClass2)
+Running public static void art.Test1913.ObjectMethod(java.lang.Runnable) with "SetTestClass2impl" on remote thread.
+"SetTestClass2impl" on public static void art.Test1913.ObjectMethod(java.lang.Runnable) set value: TestClass2impl("TestClass2("Set TestClass2impl")")
+ Value is 'TestClass2impl("TestClass2("Set TestClass2impl")")' (class: class art.Test1913$TestClass2impl)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "GetGetObject" on remote thread.
+"GetGetObject" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) got value: TestClass1("InterfaceMethod")
+ Value is 'TestClass1("InterfaceMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetNull" on remote thread.
+"SetNull" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) set value: null
+ Value is 'null' (class: NULL)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetTestClass1" on remote thread.
+"SetTestClass1" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) set value: TestClass1("Set TestClass1")
+ Value is 'TestClass1("Set TestClass1")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetTestClass1ext" on remote thread.
+"SetTestClass1ext" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) set value: TestClass1ext("TestClass1("Set TestClass1ext")")
+ Value is 'TestClass1ext("TestClass1("Set TestClass1ext")")' (class: class art.Test1913$TestClass1ext)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetTestClass2" on remote thread.
+"SetTestClass2" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) failed to set value TestClass2("Set TestClass2") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is 'TestClass1("InterfaceMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.InterfaceMethod(java.lang.Runnable) with "SetTestClass2impl" on remote thread.
+"SetTestClass2impl" on public static void art.Test1913.InterfaceMethod(java.lang.Runnable) set value: TestClass2impl("TestClass2("Set TestClass2impl")")
+ Value is 'TestClass2impl("TestClass2("Set TestClass2impl")")' (class: class art.Test1913$TestClass2impl)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "GetGetObject" on remote thread.
+"GetGetObject" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) got value: TestClass1("SpecificClassMethod")
+ Value is 'TestClass1("SpecificClassMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetNull" on remote thread.
+"SetNull" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) set value: null
+ Value is 'null' (class: NULL)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetTestClass1" on remote thread.
+"SetTestClass1" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) set value: TestClass1("Set TestClass1")
+ Value is 'TestClass1("Set TestClass1")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetTestClass1ext" on remote thread.
+"SetTestClass1ext" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) set value: TestClass1ext("TestClass1("Set TestClass1ext")")
+ Value is 'TestClass1ext("TestClass1("Set TestClass1ext")")' (class: class art.Test1913$TestClass1ext)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetTestClass2" on remote thread.
+"SetTestClass2" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) failed to set value TestClass2("Set TestClass2") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is 'TestClass1("SpecificClassMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) with "SetTestClass2impl" on remote thread.
+"SetTestClass2impl" on public static void art.Test1913.SpecificClassMethod(java.lang.Runnable) failed to set value TestClass2impl("TestClass2("Set TestClass2impl")") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is 'TestClass1("SpecificClassMethod")' (class: class art.Test1913$TestClass1)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "GetGetObject" on remote thread.
+"GetGetObject" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetNull" on remote thread.
+"SetNull" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value null due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetTestClass1" on remote thread.
+"SetTestClass1" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value TestClass1("Set TestClass1") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetTestClass1ext" on remote thread.
+"SetTestClass1ext" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value TestClass1ext("TestClass1("Set TestClass1ext")") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetTestClass2" on remote thread.
+"SetTestClass2" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value TestClass2("Set TestClass2") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
+Running public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) with "SetTestClass2impl" on remote thread.
+"SetTestClass2impl" on public static void art.Test1913.PrimitiveMethod(java.lang.Runnable) failed to set value TestClass2impl("TestClass2("Set TestClass2impl")") due to JVMTI_ERROR_TYPE_MISMATCH
+ Value is '42' (class: class java.lang.Integer)
diff --git a/test/1913-get-set-local-objects/info.txt b/test/1913-get-set-local-objects/info.txt
new file mode 100644
index 0000000..86ac743
--- /dev/null
+++ b/test/1913-get-set-local-objects/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get and set local variable object.
+
diff --git a/test/1913-get-set-local-objects/run b/test/1913-get-set-local-objects/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1913-get-set-local-objects/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/066-mismatched-super/src/Indirect.java b/test/1913-get-set-local-objects/src/Main.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/1913-get-set-local-objects/src/Main.java
index 023e409..45565c2 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/1913-get-set-local-objects/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1913.run();
+ }
}
diff --git a/test/1913-get-set-local-objects/src/art/Breakpoint.java b/test/1913-get-set-local-objects/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1913-get-set-local-objects/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/1913-get-set-local-objects/src/art/Locals.java b/test/1913-get-set-local-objects/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1913-get-set-local-objects/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/1913-get-set-local-objects/src/art/StackTrace.java b/test/1913-get-set-local-objects/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1913-get-set-local-objects/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/1913-get-set-local-objects/src/art/Suspension.java b/test/1913-get-set-local-objects/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1913-get-set-local-objects/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/1913-get-set-local-objects/src/art/Test1913.java b/test/1913-get-set-local-objects/src/art/Test1913.java
new file mode 100644
index 0000000..417138a
--- /dev/null
+++ b/test/1913-get-set-local-objects/src/art/Test1913.java
@@ -0,0 +1,251 @@
+/*
+ * 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.Method;
+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 Test1913 {
+ public static final String TARGET_VAR = "TARGET";
+
+ public static interface TestInterface {
+ public default void doNothing() {}
+ }
+ public static class TestClass1 implements TestInterface {
+ public String id;
+ public TestClass1(String id) { this.id = id; }
+ public String toString() { return String.format("TestClass1(\"%s\")", id); }
+ }
+
+ public static class TestClass1ext extends TestClass1 {
+ public TestClass1ext(String id) { super(id); }
+ public String toString() { return String.format("TestClass1ext(\"%s\")", super.toString()); }
+ }
+ public static class TestClass2 {
+ public String id;
+ public TestClass2(String id) { this.id = id; }
+ public String toString() { return String.format("TestClass2(\"%s\")", id); }
+ }
+ public static class TestClass2impl extends TestClass2 implements TestInterface {
+ public TestClass2impl(String id) { super(id); }
+ public String toString() { return String.format("TestClass2impl(\"%s\")", super.toString()); }
+ }
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "' (class: "
+ + (val != null ? val.getClass() : "NULL") + ")");
+ }
+
+ public static void PrimitiveMethod(Runnable safepoint) {
+ int TARGET = 42;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ // b/64115302: Needed to make sure that DX doesn't change the type of TARGET to TestClass1.
+ private static Object AsObject(Object o) { return o; }
+ public static void ObjectMethod(Runnable safepoint) {
+ Object TARGET = AsObject(new TestClass1("ObjectMethod"));
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static void InterfaceMethod(Runnable safepoint) {
+ TestInterface TARGET = new TestClass1("InterfaceMethod");
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static void SpecificClassMethod(Runnable safepoint) {
+ TestClass1 TARGET = new TestClass1("SpecificClassMethod");
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static interface SafepointFunction {
+ public void invoke(
+ Thread thread,
+ Method target,
+ Locals.VariableDescription TARGET_desc,
+ int depth) throws Exception;
+ }
+
+ public static interface SetterFunction {
+ public void SetVar(Thread t, int depth, int slot, Object v);
+ }
+
+ public static interface GetterFunction {
+ public Object GetVar(Thread t, int depth, int slot);
+ }
+
+ public static SafepointFunction NamedSet(
+ final String type, final SetterFunction get, final Object v) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
+ try {
+ get.SetVar(t, depth, desc.slot, v);
+ System.out.println(this + " on " + method + " set value: " + v);
+ } catch (Exception e) {
+ System.out.println(
+ this + " on " + method + " failed to set value " + v + " due to " + e.getMessage());
+ }
+ }
+ public String toString() {
+ return "\"Set" + type + "\"";
+ }
+ };
+ }
+
+ public static SafepointFunction NamedGet(final String type, final GetterFunction get) {
+ return new SafepointFunction() {
+ public void invoke(Thread t, Method method, Locals.VariableDescription desc, int depth) {
+ try {
+ Object res = get.GetVar(t, depth, desc.slot);
+ 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 Method target;
+
+ public TestCase(Method target) {
+ 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(null, 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);
+ Locals.VariableDescription desc = findTargetVar(frame.current_location);
+ safepoint.invoke(remote, target, desc, frame.depth);
+ } finally {
+ Suspension.resume(remote);
+ pause.wakeupOtherThread();
+ remote.join();
+ }
+ }
+
+ private Locals.VariableDescription findTargetVar(long loc) {
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(target)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + target + " at loc " + loc);
+ }
+
+ private StackTrace.StackFrameData findStackFrame(Thread thr) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+ }
+ public static Method getMethod(String name) throws Exception {
+ return Test1913.class.getDeclaredMethod(name, Runnable.class);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final TestCase[] MAIN_TEST_CASES = new TestCase[] {
+ new TestCase(getMethod("ObjectMethod")),
+ new TestCase(getMethod("InterfaceMethod")),
+ new TestCase(getMethod("SpecificClassMethod")),
+ new TestCase(getMethod("PrimitiveMethod")),
+ };
+
+ final SetterFunction set_obj = Locals::SetLocalVariableObject;
+ final SafepointFunction[] SAFEPOINTS = new SafepointFunction[] {
+ NamedGet("GetObject", Locals::GetLocalVariableObject),
+ NamedSet("Null", set_obj, null),
+ NamedSet("TestClass1", set_obj, new TestClass1("Set TestClass1")),
+ NamedSet("TestClass1ext", set_obj, new TestClass1ext("Set TestClass1ext")),
+ NamedSet("TestClass2", set_obj, new TestClass2("Set TestClass2")),
+ NamedSet("TestClass2impl", set_obj, new TestClass2impl("Set TestClass2impl")),
+ };
+
+ for (TestCase t: MAIN_TEST_CASES) {
+ for (SafepointFunction s : SAFEPOINTS) {
+ t.exec(s);
+ }
+ }
+ }
+}
+
diff --git a/test/1914-get-local-instance/expected.txt b/test/1914-get-local-instance/expected.txt
new file mode 100644
index 0000000..4117942
--- /dev/null
+++ b/test/1914-get-local-instance/expected.txt
@@ -0,0 +1,12 @@
+Running public static void art.Test1914.StaticMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public static void art.Test1914.StaticMethod(java.lang.Runnable) got value: null
+ Value is 'null' (class: NULL)
+Running public static native void art.Test1914.NativeStaticMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public static native void art.Test1914.NativeStaticMethod(java.lang.Runnable) got value: null
+ Value is 'null' (class: NULL)
+Running public void art.Test1914$TargetClass.InstanceMethod(java.lang.Runnable) with "GetThis" on remote thread.
+"GetThis" on public void art.Test1914$TargetClass.InstanceMethod(java.lang.Runnable) got value: TargetClass("InstanceMethodObject")
+ Value is 'TargetClass("InstanceMethodObject")' (class: class art.Test1914$TargetClass)
+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)
diff --git a/test/1914-get-local-instance/info.txt b/test/1914-get-local-instance/info.txt
new file mode 100644
index 0000000..9fc3d62
--- /dev/null
+++ b/test/1914-get-local-instance/info.txt
@@ -0,0 +1,2 @@
+Test for jvmti get local instance
+
diff --git a/test/1914-get-local-instance/local_instance.cc b/test/1914-get-local-instance/local_instance.cc
new file mode 100644
index 0000000..03aa59e
--- /dev/null
+++ b/test/1914-get-local-instance/local_instance.cc
@@ -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.
+ */
+
+#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 Test1914LocalInstance {
+
+extern "C" JNIEXPORT void Java_art_Test1914_00024TargetClass_NativeInstanceMethod(
+ JNIEnv* env, jobject thiz, jobject run) {
+ ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable"));
+ if (env->ExceptionCheck()) { return; }
+ jmethodID method = env->GetMethodID(runnable.get(), "run", "()V");
+ if (env->ExceptionCheck()) { return; }
+ env->CallVoidMethod(run, method);
+ if (env->ExceptionCheck()) { return; }
+ ScopedLocalRef<jclass> Test1914(env, env->FindClass("art/Test1914"));
+ if (env->ExceptionCheck()) { return; }
+ jmethodID report = env->GetStaticMethodID(Test1914.get(), "reportValue", "(Ljava/lang/Object;)V");
+ if (env->ExceptionCheck()) { return; }
+ env->CallStaticVoidMethod(Test1914.get(), report, thiz);
+}
+
+extern "C" JNIEXPORT void Java_art_Test1914_NativeStaticMethod(
+ JNIEnv* env, jclass, jobject run) {
+ ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable"));
+ if (env->ExceptionCheck()) { return; }
+ jmethodID method = env->GetMethodID(runnable.get(), "run", "()V");
+ if (env->ExceptionCheck()) { return; }
+ env->CallVoidMethod(run, method);
+ if (env->ExceptionCheck()) { return; }
+ ScopedLocalRef<jclass> Test1914(env, env->FindClass("art/Test1914"));
+ if (env->ExceptionCheck()) { return; }
+ jmethodID report = env->GetStaticMethodID(Test1914.get(), "reportValue", "(Ljava/lang/Object;)V");
+ if (env->ExceptionCheck()) { return; }
+ env->CallStaticVoidMethod(Test1914.get(), report, nullptr);
+}
+
+} // namespace Test1914LocalInstance
+} // namespace art
+
diff --git a/test/1914-get-local-instance/run b/test/1914-get-local-instance/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1914-get-local-instance/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/066-mismatched-super/src/Indirect.java b/test/1914-get-local-instance/src/Main.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/1914-get-local-instance/src/Main.java
index 023e409..163221e 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/1914-get-local-instance/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1914.run();
+ }
}
diff --git a/test/1914-get-local-instance/src/art/Breakpoint.java b/test/1914-get-local-instance/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1914-get-local-instance/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/1914-get-local-instance/src/art/Locals.java b/test/1914-get-local-instance/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1914-get-local-instance/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/1914-get-local-instance/src/art/StackTrace.java b/test/1914-get-local-instance/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1914-get-local-instance/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/1914-get-local-instance/src/art/Suspension.java b/test/1914-get-local-instance/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1914-get-local-instance/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/1914-get-local-instance/src/art/Test1914.java b/test/1914-get-local-instance/src/art/Test1914.java
new file mode 100644
index 0000000..c09f519
--- /dev/null
+++ b/test/1914-get-local-instance/src/art/Test1914.java
@@ -0,0 +1,182 @@
+/*
+ * 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.Method;
+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 Test1914 {
+ public static final String TARGET_VAR = "TARGET";
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "' (class: "
+ + (val != null ? val.getClass() : "NULL") + ")");
+ }
+
+ public static void StaticMethod(Runnable safepoint) {
+ safepoint.run();
+ reportValue(null);
+ }
+
+ public static native void NativeStaticMethod(Runnable safepoint);
+
+ public static class TargetClass {
+ public String id;
+ public String toString() { return String.format("TargetClass(\"%s\")", id); }
+ public TargetClass(String id) { this.id = id; }
+
+ public void InstanceMethod(Runnable safepoint) {
+ safepoint.run();
+ reportValue(this);
+ }
+
+ public native void NativeInstanceMethod(Runnable safepoint);
+ }
+
+ 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)) {
+ 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 void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final TestCase[] MAIN_TEST_CASES = new TestCase[] {
+ new TestCase(null, getMethod(Test1914.class, "StaticMethod")),
+ new TestCase(null, getMethod(Test1914.class, "NativeStaticMethod")),
+ new TestCase(new TargetClass("InstanceMethodObject"),
+ getMethod(TargetClass.class, "InstanceMethod")),
+ new TestCase(new TargetClass("NativeInstanceMethodObject"),
+ getMethod(TargetClass.class, "NativeInstanceMethod")),
+ };
+
+ for (TestCase t: MAIN_TEST_CASES) {
+ t.exec(NamedGet("This", Locals::GetLocalInstance));
+ }
+ }
+}
+
diff --git a/test/1915-get-set-local-current-thread/expected.txt b/test/1915-get-set-local-current-thread/expected.txt
new file mode 100644
index 0000000..de39ca9
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/expected.txt
@@ -0,0 +1,5 @@
+GetLocalInt on current thread!
+From GetLocalInt(), value is 42
+ Value is '42'
+SetLocalInt on current thread!
+ Value is '1337'
diff --git a/test/1915-get-set-local-current-thread/info.txt b/test/1915-get-set-local-current-thread/info.txt
new file mode 100644
index 0000000..c59f50d
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable on current thread.
+
diff --git a/test/1915-get-set-local-current-thread/run b/test/1915-get-set-local-current-thread/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/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/066-mismatched-super/src/Indirect.java b/test/1915-get-set-local-current-thread/src/Main.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/1915-get-set-local-current-thread/src/Main.java
index 023e409..47e6767 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/1915-get-set-local-current-thread/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1915.run();
+ }
}
diff --git a/test/1915-get-set-local-current-thread/src/art/Breakpoint.java b/test/1915-get-set-local-current-thread/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/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/1915-get-set-local-current-thread/src/art/Locals.java b/test/1915-get-set-local-current-thread/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/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/1915-get-set-local-current-thread/src/art/StackTrace.java b/test/1915-get-set-local-current-thread/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/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/1915-get-set-local-current-thread/src/art/Suspension.java b/test/1915-get-set-local-current-thread/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/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/1915-get-set-local-current-thread/src/art/Test1915.java b/test/1915-get-set-local-current-thread/src/art/Test1915.java
new file mode 100644
index 0000000..a99a487
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/src/art/Test1915.java
@@ -0,0 +1,105 @@
+/*
+ * 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.Method;
+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 Test1915 {
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
+ }
+ public static interface ThrowRunnable {
+ public void run() throws Exception;
+ }
+
+ public static void IntMethod(ThrowRunnable safepoint) throws Exception {
+ int TARGET = 42;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final Method target = Test1915.class.getDeclaredMethod("IntMethod", ThrowRunnable.class);
+ // Get Variable.
+ System.out.println("GetLocalInt on current thread!");
+ IntMethod(() -> {
+ StackTrace.StackFrameData frame = FindStackFrame(target);
+ int depth = FindExpectedFrameDepth(frame);
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(Thread.currentThread(), depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ });
+ // Set Variable.
+ System.out.println("SetLocalInt on current thread!");
+ IntMethod(() -> {
+ StackTrace.StackFrameData frame = FindStackFrame(target);
+ int depth = FindExpectedFrameDepth(frame);
+ int slot = FindSlot(frame);
+ Locals.SetLocalVariableInt(Thread.currentThread(), depth, slot, SET_VALUE);
+ });
+ }
+
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+ }
+
+ public static int FindExpectedFrameDepth(StackTrace.StackFrameData frame) throws Exception {
+ // Adjust the 'frame' depth since it is modified by:
+ // +1 for Get/SetLocalVariableInt in future.
+ // -1 for FindStackFrame
+ // -1 for GetStackTrace
+ // -1 for GetStackTraceNative
+ // ------------------------------
+ // -2
+ return frame.depth - 2;
+ }
+
+ private static StackTrace.StackFrameData FindStackFrame(Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(Thread.currentThread())) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target);
+ }
+}
+
diff --git a/test/1916-get-set-current-frame/expected.txt b/test/1916-get-set-current-frame/expected.txt
new file mode 100644
index 0000000..343d493
--- /dev/null
+++ b/test/1916-get-set-current-frame/expected.txt
@@ -0,0 +1,4 @@
+From GetLocalInt(), value is 42
+ Value is '42'
+Setting TARGET to 1337
+ Value is '1337'
diff --git a/test/1916-get-set-current-frame/info.txt b/test/1916-get-set-current-frame/info.txt
new file mode 100644
index 0000000..7342af7
--- /dev/null
+++ b/test/1916-get-set-current-frame/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable in the currently executing method frame.
+
diff --git a/test/1916-get-set-current-frame/run b/test/1916-get-set-current-frame/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1916-get-set-current-frame/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/066-mismatched-super/src/Indirect.java b/test/1916-get-set-current-frame/src/Main.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/1916-get-set-current-frame/src/Main.java
index 023e409..7d0cd21 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/1916-get-set-current-frame/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1916.run();
+ }
}
diff --git a/test/1916-get-set-current-frame/src/art/Breakpoint.java b/test/1916-get-set-current-frame/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1916-get-set-current-frame/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/1916-get-set-current-frame/src/art/Locals.java b/test/1916-get-set-current-frame/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1916-get-set-current-frame/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/1916-get-set-current-frame/src/art/StackTrace.java b/test/1916-get-set-current-frame/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1916-get-set-current-frame/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/1916-get-set-current-frame/src/art/Suspension.java b/test/1916-get-set-current-frame/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1916-get-set-current-frame/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/1916-get-set-current-frame/src/art/Test1916.java b/test/1916-get-set-current-frame/src/art/Test1916.java
new file mode 100644
index 0000000..3e5bce2
--- /dev/null
+++ b/test/1916-get-set-current-frame/src/art/Test1916.java
@@ -0,0 +1,148 @@
+/*
+ * 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.Method;
+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 Test1916 {
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
+ }
+
+ public static class IntRunner implements Runnable {
+ private volatile boolean continueBusyLoop;
+ private volatile boolean inBusyLoop;
+ public IntRunner() {
+ this.continueBusyLoop = true;
+ this.inBusyLoop = false;
+ }
+ public void run() {
+ int TARGET = 42;
+ // We will suspend the thread during this loop.
+ while (continueBusyLoop) {
+ inBusyLoop = true;
+ }
+ reportValue(TARGET);
+ }
+ public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
+ public void finish() { continueBusyLoop = false; }
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ runGet();
+ runSet();
+ }
+
+ public static void runGet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Get Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
+ target_get.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_get);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_get.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(target_get, depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ } finally {
+ Suspension.resume(target_get);
+ int_runner.finish();
+ target_get.join();
+ }
+ }
+
+ public static void runSet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Set Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
+ target_set.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_set);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_set.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ System.out.println("Setting TARGET to " + SET_VALUE);
+ Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
+ } finally {
+ Suspension.resume(target_set);
+ int_runner.finish();
+ target_set.join();
+ }
+ }
+
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+ }
+
+ private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+}
+
diff --git a/test/1917-get-stack-frame/expected.txt b/test/1917-get-stack-frame/expected.txt
new file mode 100644
index 0000000..4c9efcf
--- /dev/null
+++ b/test/1917-get-stack-frame/expected.txt
@@ -0,0 +1,33 @@
+Recurring 5 times
+'private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread)' line: -1
+'public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread)' line: 60
+'public void art.Test1917$StackTraceGenerator.run()' line: 82
+'public void art.Test1917$RecurCount.doRecur(int)' line: 104
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.run()' line: 97
+'public static void art.Test1917.run() throws java.lang.Exception' line: 133
+Recurring 5 times on another thread
+'private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread)' line: -1
+'public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread)' line: 60
+'public void art.Test1917$StackTraceGenerator.run()' line: 82
+'public void art.Test1917$RecurCount.doRecur(int)' line: 104
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.run()' line: 97
+Recurring 5 times on another thread. Stack trace from main thread!
+'public void java.util.concurrent.Semaphore.acquire() throws java.lang.InterruptedException' line: <NOT-DETERMINISTIC>
+'public void art.Test1917$ThreadPauser.run()' line: 46
+'public void art.Test1917$RecurCount.doRecur(int)' line: 104
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.doRecur(int)' line: 102
+'public void art.Test1917$RecurCount.run()' line: 97
diff --git a/test/1917-get-stack-frame/info.txt b/test/1917-get-stack-frame/info.txt
new file mode 100644
index 0000000..e72034a
--- /dev/null
+++ b/test/1917-get-stack-frame/info.txt
@@ -0,0 +1 @@
+Tests stack frame functions of jvmti
diff --git a/test/1917-get-stack-frame/run b/test/1917-get-stack-frame/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1917-get-stack-frame/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/066-mismatched-super/src/Indirect.java b/test/1917-get-stack-frame/src/Main.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/1917-get-stack-frame/src/Main.java
index 023e409..c055a5c 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/1917-get-stack-frame/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1917.run();
+ }
}
diff --git a/test/1917-get-stack-frame/src/art/Breakpoint.java b/test/1917-get-stack-frame/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1917-get-stack-frame/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/1917-get-stack-frame/src/art/StackTrace.java b/test/1917-get-stack-frame/src/art/StackTrace.java
new file mode 100644
index 0000000..b12c3df
--- /dev/null
+++ b/test/1917-get-stack-frame/src/art/StackTrace.java
@@ -0,0 +1,67 @@
+/*
+ * 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());
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1917-get-stack-frame/src/art/Suspension.java b/test/1917-get-stack-frame/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1917-get-stack-frame/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/1917-get-stack-frame/src/art/Test1917.java b/test/1917-get-stack-frame/src/art/Test1917.java
new file mode 100644
index 0000000..def7530
--- /dev/null
+++ b/test/1917-get-stack-frame/src/art/Test1917.java
@@ -0,0 +1,150 @@
+/*
+ * 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.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.Vector;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Test1917 {
+ public final static boolean TEST_PRINT_ALL = false;
+
+ public static class ThreadPauser implements Runnable {
+ public Semaphore sem_wakeup_main = new Semaphore(0);
+ public Semaphore 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();
+ while (!sem_wait.hasQueuedThreads()) {}
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public static class StackTraceGenerator implements Runnable {
+ private final Thread thr;
+ private final Consumer<StackTrace.StackFrameData> con;
+ public StackTraceGenerator(Thread thr, Consumer<StackTrace.StackFrameData> con) {
+ this.thr = thr;
+ this.con = con;
+ }
+
+ public StackTraceGenerator(Consumer<StackTrace.StackFrameData> con) {
+ this(null, con);
+ }
+
+ public Thread getThread() {
+ if (thr == null) {
+ return Thread.currentThread();
+ } else {
+ return thr;
+ }
+ }
+ public void run() {
+ for (StackTrace.StackFrameData s : StackTrace.GetStackTrace(getThread())) {
+ con.accept(s);
+ }
+ }
+ }
+
+ public static class RecurCount implements Runnable {
+ private final int cnt;
+ private final Runnable then;
+ public RecurCount(int cnt, Runnable then) {
+ this.cnt = cnt;
+ this.then = then;
+ }
+
+ public void run() {
+ doRecur(0);
+ }
+
+ public void doRecur(int n) {
+ if (n < cnt) {
+ doRecur(n + 1);
+ } else {
+ then.run();
+ }
+ }
+ }
+
+ public static Consumer<StackTrace.StackFrameData> makePrintStackFramesConsumer()
+ throws Exception {
+ final Method end_method = Test1917.class.getDeclaredMethod("run");
+ return new Consumer<StackTrace.StackFrameData>() {
+ public void accept(StackTrace.StackFrameData data) {
+ if (TEST_PRINT_ALL) {
+ System.out.println(data);
+ } else {
+ Package p = data.method.getDeclaringClass().getPackage();
+ // Filter out anything to do with the testing harness.
+ if (p != null && p.equals(Test1917.class.getPackage())) {
+ System.out.printf("'%s' line: %d\n",
+ data.method,
+ Breakpoint.locationToLine(data.method, data.current_location));
+ } else if (data.method.getDeclaringClass().equals(Semaphore.class)) {
+ System.out.printf("'%s' line: <NOT-DETERMINISTIC>\n", data.method);
+ }
+ }
+ }
+ };
+ }
+
+ public static void run() throws Exception {
+ System.out.println("Recurring 5 times");
+ new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())).run();
+
+ System.out.println("Recurring 5 times on another thread");
+ Thread thr = new Thread(
+ new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())));
+ thr.start();
+ thr.join();
+
+ System.out.println("Recurring 5 times on another thread. Stack trace from main thread!");
+ ThreadPauser pause = new ThreadPauser();
+ Thread thr2 = new Thread(new RecurCount(5, pause));
+ thr2.start();
+ pause.waitForOtherThreadToPause();
+ new StackTraceGenerator(thr2, makePrintStackFramesConsumer()).run();
+ pause.wakeupOtherThread();
+ thr2.join();
+ }
+}
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 5103540..f466eea 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -876,6 +876,91 @@
return true;
}
+ /// CHECK-START: void Main.modArrayIndex1(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+
+ /// CHECK-START: void Main.modArrayIndex1(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ public static void modArrayIndex1(int[] array) {
+ for(int i = 0; i < 100; i++) {
+ // Cannot statically eliminate, for example, when array.length == 5.
+ // Currently dynamic BCE isn't applied for this case.
+ array[i % 10] = i;
+ // Can be eliminated by BCE.
+ array[i % array.length] = i;
+ }
+ }
+
+ /// CHECK-START: void Main.modArrayIndex2(int[], int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+
+ /// CHECK-START: void Main.modArrayIndex2(int[], int) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ public static void modArrayIndex2(int array[], int index) {
+ for(int i = 0; i < 100; i++) {
+ // Both bounds checks cannot be statically eliminated, because index can be < 0.
+ // Currently dynamic BCE isn't applied for this case.
+ array[(index+i) % 10] = i;
+ array[(index+i) % array.length] = i;
+ }
+ }
+
+ static final int[] staticArray = new int[10];
+
+ /// CHECK-START: void Main.modArrayIndex3() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+
+ /// CHECK-START: void Main.modArrayIndex3() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ public static void modArrayIndex3() {
+ for(int i = 0; i < 100; i++) {
+ // Currently dynamic BCE isn't applied for this case.
+ staticArray[i % 10] = i;
+ // Can be eliminated by BCE.
+ staticArray[i % staticArray.length] = i;
+ }
+ }
+
+ /// CHECK-START: void Main.modArrayIndex4() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArraySet
+
+ /// CHECK-START: void Main.modArrayIndex4() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArraySet
+ public static void modArrayIndex4() {
+ int[] array = new int[20];
+ for(int i = 0; i < 100; i++) {
+ // The local array length is statically know. Both can be eliminated by BCE.
+ array[i % 10] = i;
+ array[i % array.length] = i;
+ }
+ }
/// CHECK-START: void Main.bubbleSort(int[]) GVN (before)
/// CHECK: BoundsCheck
diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc
index 9ba05bc..c113042 100644
--- a/test/497-inlining-and-class-loader/clear_dex_cache.cc
+++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc
@@ -34,22 +34,32 @@
ScopedObjectAccess soa(Thread::Current());
mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
size_t num_methods = dex_cache->NumResolvedMethods();
- ArtMethod** methods = dex_cache->GetResolvedMethods();
+ mirror::MethodDexCacheType* methods = dex_cache->GetResolvedMethods();
CHECK_EQ(num_methods != 0u, methods != nullptr);
if (num_methods == 0u) {
return nullptr;
}
jarray array;
if (sizeof(void*) == 4) {
- array = env->NewIntArray(num_methods);
+ array = env->NewIntArray(2u * num_methods);
} else {
- array = env->NewLongArray(num_methods);
+ array = env->NewLongArray(2u * num_methods);
}
CHECK(array != nullptr);
- mirror::PointerArray* pointer_array = soa.Decode<mirror::PointerArray>(array).Ptr();
+ ObjPtr<mirror::Array> decoded_array = soa.Decode<mirror::Array>(array);
for (size_t i = 0; i != num_methods; ++i) {
- ArtMethod* method = mirror::DexCache::GetElementPtrSize(methods, i, kRuntimePointerSize);
- pointer_array->SetElementPtrSize(i, method, kRuntimePointerSize);
+ auto pair = mirror::DexCache::GetNativePairPtrSize(methods, i, kRuntimePointerSize);
+ uint32_t index = pair.index;
+ ArtMethod* method = pair.object;
+ if (sizeof(void*) == 4) {
+ ObjPtr<mirror::IntArray> int_array = down_cast<mirror::IntArray*>(decoded_array.Ptr());
+ int_array->Set(2u * i, index);
+ int_array->Set(2u * i + 1u, static_cast<jint>(reinterpret_cast<uintptr_t>(method)));
+ } else {
+ ObjPtr<mirror::LongArray> long_array = down_cast<mirror::LongArray*>(decoded_array.Ptr());
+ long_array->Set(2u * i, index);
+ long_array->Set(2u * i + 1u, reinterpret_cast64<jlong>(method));
+ }
}
return array;
}
@@ -59,14 +69,26 @@
ScopedObjectAccess soa(Thread::Current());
mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
size_t num_methods = dex_cache->NumResolvedMethods();
- ArtMethod** methods = soa.Decode<mirror::Class>(cls)->GetDexCache()->GetResolvedMethods();
+ mirror::MethodDexCacheType* methods =
+ soa.Decode<mirror::Class>(cls)->GetDexCache()->GetResolvedMethods();
CHECK_EQ(num_methods != 0u, methods != nullptr);
- ObjPtr<mirror::PointerArray> old = soa.Decode<mirror::PointerArray>(old_cache);
+ ObjPtr<mirror::Array> old = soa.Decode<mirror::Array>(old_cache);
CHECK_EQ(methods != nullptr, old != nullptr);
CHECK_EQ(num_methods, static_cast<size_t>(old->GetLength()));
for (size_t i = 0; i != num_methods; ++i) {
- ArtMethod* method = old->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize);
- mirror::DexCache::SetElementPtrSize(methods, i, method, kRuntimePointerSize);
+ uint32_t index;
+ ArtMethod* method;
+ if (sizeof(void*) == 4) {
+ ObjPtr<mirror::IntArray> int_array = down_cast<mirror::IntArray*>(old.Ptr());
+ index = static_cast<uint32_t>(int_array->Get(2u * i));
+ method = reinterpret_cast<ArtMethod*>(static_cast<uint32_t>(int_array->Get(2u * i + 1u)));
+ } else {
+ ObjPtr<mirror::LongArray> long_array = down_cast<mirror::LongArray*>(old.Ptr());
+ index = dchecked_integral_cast<uint32_t>(long_array->Get(2u * i));
+ method = reinterpret_cast64<ArtMethod*>(long_array->Get(2u * i + 1u));
+ }
+ mirror::MethodDexCachePair pair(method, index);
+ mirror::DexCache::SetNativePairPtrSize(methods, i, pair, kRuntimePointerSize);
}
}
diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java
index 810f0fa..6fd9cdd 100644
--- a/test/550-checker-multiply-accumulate/src/Main.java
+++ b/test/550-checker-multiply-accumulate/src/Main.java
@@ -434,7 +434,7 @@
/// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<<Loop>> outer_loop:none
/// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: VecMull
+ /// CHECK-NOT: VecMul
/// CHECK-NOT: VecAdd
public static void SimdMulAdd(int[] array1, int[] array2) {
for (int j = 0; j < 100; j++) {
@@ -452,7 +452,7 @@
/// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<<Loop>> outer_loop:none
/// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: VecMull
+ /// CHECK-NOT: VecMul
/// CHECK-NOT: VecSub
public static void SimdMulSub(int[] array1, int[] array2) {
for (int j = 0; j < 100; j++) {
diff --git a/test/596-app-images/app_images.cc b/test/596-app-images/app_images.cc
index fa9c902..498ea1d 100644
--- a/test/596-app-images/app_images.cc
+++ b/test/596-app-images/app_images.cc
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-#include <iostream>
#include <pthread.h>
-#include <stdio.h>
+
+#include <cstdio>
+#include <iostream>
#include <vector>
+#include "jni.h"
+
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
#include "image.h"
-#include "jni.h"
#include "mirror/class.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/test/597-deopt-new-string/deopt.cc b/test/597-deopt-new-string/deopt.cc
index 0f02efe..fe229e4 100644
--- a/test/597-deopt-new-string/deopt.cc
+++ b/test/597-deopt-new-string/deopt.cc
@@ -15,13 +15,14 @@
*/
#include "jni.h"
-#include "mirror/class-inl.h"
-#include "runtime.h"
-#include "thread_list.h"
-#include "thread_state.h"
+
#include "gc/gc_cause.h"
#include "gc/scoped_gc_critical_section.h"
+#include "mirror/class-inl.h"
+#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
+#include "thread_list.h"
+#include "thread_state.h"
namespace art {
diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java
index beea90a..27da7cc 100644
--- a/test/616-cha/src/Main.java
+++ b/test/616-cha/src/Main.java
@@ -187,7 +187,12 @@
System.loadLibrary(args[0]);
// CHeck some boot-image methods.
- assertSingleImplementation(java.util.ArrayList.class, "size", true);
+
+ // We would want to have this, but currently setting single-implementation in the boot image
+ // does not work well with app images. b/34193647
+ final boolean ARRAYLIST_SIZE_EXPECTED = false;
+ assertSingleImplementation(java.util.ArrayList.class, "size", ARRAYLIST_SIZE_EXPECTED);
+
// java.util.LinkedHashMap overrides get().
assertSingleImplementation(java.util.HashMap.class, "get", false);
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index aca997e..0e85612 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -426,6 +426,44 @@
}
}
+ // Environment of an instruction, removed during SimplifyInduction, should be adjusted.
+ //
+ /// CHECK-START: void Main.inductionMax(int[]) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.inductionMax(int[]) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ private static void inductionMax(int[] a) {
+ int s = 0;
+ for (int i = 0; i < 10; i++) {
+ s = Math.max(s, 5);
+ }
+ }
+
+ /// CHECK-START: int Main.feedsIntoDeopt(int[]) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop2:B\d+>> outer_loop:none
+ //
+ /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
+ //
+ /// CHECK-START: int Main.feedsIntoDeopt(int[]) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ /// CHECK-NOT: Phi
+ static int feedsIntoDeopt(int[] a) {
+ // Reduction should be removed.
+ int r = 0;
+ for (int i = 0; i < 100; i++) {
+ r += 10;
+ }
+ // Even though uses feed into deopts of BCE.
+ for (int i = 1; i < 100; i++) {
+ a[i] = a[i - 1];
+ }
+ return r;
+ }
+
public static void main(String[] args) {
expectEquals(10, earlyExitFirst(-1));
for (int i = 0; i <= 10; i++) {
@@ -539,6 +577,15 @@
expectEquals((byte)(i + 1), b1[i]);
}
+ inductionMax(yy);
+
+ int[] f = new int[100];
+ f[0] = 11;
+ expectEquals(1000, feedsIntoDeopt(f));
+ for (int i = 0; i < 100; i++) {
+ expectEquals(11, f[i]);
+ }
+
System.out.println("passed");
}
diff --git a/test/660-clinit/expected.txt b/test/660-clinit/expected.txt
new file mode 100644
index 0000000..9eb4941
--- /dev/null
+++ b/test/660-clinit/expected.txt
@@ -0,0 +1,13 @@
+JNI_OnLoad called
+A.a: 5
+A.a: 10
+B.b: 10
+C.c: 10
+X: 4950
+Y: 5730
+str: Hello World!
+ooo: OoooooO
+Z: 11206655
+A: 100
+AA: 100
+a != 101
diff --git a/test/660-clinit/info.txt b/test/660-clinit/info.txt
new file mode 100644
index 0000000..da0193d
--- /dev/null
+++ b/test/660-clinit/info.txt
@@ -0,0 +1 @@
+Tests that class initializers are executed correctly.
diff --git a/test/660-clinit/profile b/test/660-clinit/profile
new file mode 100644
index 0000000..0239f22
--- /dev/null
+++ b/test/660-clinit/profile
@@ -0,0 +1,10 @@
+LMain;
+LClInit;
+LDay;
+LA;
+LB;
+LC;
+LG;
+LGs;
+LObjectRef;
+
diff --git a/test/660-clinit/run b/test/660-clinit/run
new file mode 100644
index 0000000..d24ef42
--- /dev/null
+++ b/test/660-clinit/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+exec ${RUN} $@ --profile
diff --git a/test/660-clinit/src/Main.java b/test/660-clinit/src/Main.java
new file mode 100644
index 0000000..f9b068e
--- /dev/null
+++ b/test/660-clinit/src/Main.java
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+import java.util.*;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ if (!checkAppImageLoaded()) {
+ System.out.println("AppImage not loaded.");
+ }
+
+ expectNotPreInit(Day.class);
+ expectNotPreInit(ClInit.class); // should pass
+ expectNotPreInit(A.class); // should pass
+ expectNotPreInit(B.class); // should fail
+ expectNotPreInit(C.class); // should fail
+ expectNotPreInit(G.class); // should fail
+ expectNotPreInit(Gs.class); // should fail
+ expectNotPreInit(Gss.class); // should fail
+
+ expectNotPreInit(Add.class);
+ expectNotPreInit(Mul.class);
+ expectNotPreInit(ObjectRef.class);
+
+ A x = new A();
+ System.out.println("A.a: " + A.a);
+
+ B y = new B();
+ C z = new C();
+ System.out.println("A.a: " + A.a);
+ System.out.println("B.b: " + B.b);
+ System.out.println("C.c: " + C.c);
+
+ ClInit c = new ClInit();
+ int aa = c.a;
+
+ System.out.println("X: " + c.getX());
+ System.out.println("Y: " + c.getY());
+ System.out.println("str: " + c.str);
+ System.out.println("ooo: " + c.ooo);
+ System.out.println("Z: " + c.getZ());
+ System.out.println("A: " + c.getA());
+ System.out.println("AA: " + aa);
+
+ if (c.a != 101) {
+ System.out.println("a != 101");
+ }
+
+ return;
+ }
+
+ static void expectPreInit(Class<?> klass) {
+ if (checkInitialized(klass) == false) {
+ System.out.println(klass.getName() + " should be initialized!");
+ }
+ }
+
+ static void expectNotPreInit(Class<?> klass) {
+ if (checkInitialized(klass) == true) {
+ System.out.println(klass.getName() + " should not be initialized!");
+ }
+ }
+
+ public static native boolean checkAppImageLoaded();
+ public static native boolean checkAppImageContains(Class<?> klass);
+ public static native boolean checkInitialized(Class<?> klass);
+}
+
+enum Day {
+ SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
+ THURSDAY, FRIDAY, SATURDAY
+}
+
+class ClInit {
+
+ static String ooo = "OoooooO";
+ static String str;
+ static int z;
+ static int x, y;
+ public static volatile int a = 100;
+
+ static {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Hello ");
+ sb.append("World!");
+ str = sb.toString();
+
+ z = 0xFF;
+ z += 0xFF00;
+ z += 0xAA0000;
+
+ for(int i = 0; i < 100; i++) {
+ x += i;
+ }
+
+ y = x;
+ for(int i = 0; i < 40; i++) {
+ y += i;
+ }
+ }
+
+ int getX() {
+ return x;
+ }
+
+ int getZ() {
+ return z;
+ }
+
+ int getY() {
+ return y;
+ }
+
+ int getA() {
+ return a;
+ }
+}
+
+class A {
+ public static int a = 2;
+ static {
+ a = 5; // self-updating, pass
+ }
+}
+
+class B {
+ public static int b;
+ static {
+ A.a = 10; // write other's static field, fail
+ b = A.a; // read other's static field, fail
+ }
+}
+
+class C {
+ public static int c;
+ static {
+ c = A.a; // read other's static field, fail
+ }
+}
+
+class G {
+ static G g;
+ static int i;
+ static {
+ g = new Gss(); // fail because recursive dependency
+ i = A.a; // read other's static field, fail
+ }
+}
+
+// Gs will be successfully initialized as G's status is initializing at that point, which will
+// later aborted but Gs' transaction is already committed.
+// Instantiation of Gs will fail because we try to invoke G's <init>
+// but G's status will be StatusVerified. INVOKE_DIRECT will not initialize class.
+class Gs extends G {} // fail because super class can't be initialized
+class Gss extends Gs {}
+
+// pruned because holding reference to non-image class
+class ObjectRef {
+ static Class<?> klazz[] = new Class<?>[]{Add.class, Mul.class};
+}
+
+// non-image
+class Add {
+ static int exec(int a, int b) {
+ return a + b;
+ }
+}
+
+// non-image
+class Mul {
+ static int exec(int a, int b) {
+ return a * b;
+ }
+}
diff --git a/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/test/660-store-8-16/expected.txt
similarity index 100%
copy from runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
copy to test/660-store-8-16/expected.txt
diff --git a/test/660-store-8-16/info.txt b/test/660-store-8-16/info.txt
new file mode 100644
index 0000000..aad6c56
--- /dev/null
+++ b/test/660-store-8-16/info.txt
@@ -0,0 +1,3 @@
+Regression test for the compiler whose x86 and x64 backends
+used to crash on 8bits / 16bits immediate stores when the Java
+input was a wide immediate.
diff --git a/test/660-store-8-16/smali/TestCase.smali b/test/660-store-8-16/smali/TestCase.smali
new file mode 100644
index 0000000..ec8cbd8
--- /dev/null
+++ b/test/660-store-8-16/smali/TestCase.smali
@@ -0,0 +1,102 @@
+# 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.
+
+.class public LTestCase;
+
+.super Ljava/lang/Object;
+
+.method public static setByteArray([B)V
+ .registers 3
+ const/16 v0, 0x0
+ const/16 v1, 0x0101
+ aput-byte v1, p0, v0
+ return-void
+.end method
+
+.method public static setByteStaticField()V
+ .registers 1
+ const/16 v0, 0x0101
+ sput-byte v0, LTestCase;->staticByteField:B
+ return-void
+.end method
+
+.method public static setByteInstanceField(LTestCase;)V
+ .registers 2
+ const/16 v0, 0x0101
+ iput-byte v0, p0, LTestCase;->instanceByteField:B
+ return-void
+.end method
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static setShortArray([S)V
+ .registers 3
+ const/16 v0, 0x0
+ const v1, 0x10101
+ aput-short v1, p0, v0
+ return-void
+.end method
+
+.method public static setShortStaticField()V
+ .registers 1
+ const v0, 0x10101
+ sput-short v0, LTestCase;->staticShortField:S
+ return-void
+.end method
+
+.method public static setShortInstanceField(LTestCase;)V
+ .registers 2
+ const v0, 0x10101
+ iput-short v0, p0, LTestCase;->instanceShortField:S
+ return-void
+.end method
+
+.method public static setCharArray([C)V
+ .registers 3
+ const/16 v0, 0x0
+ const v1, 0x10101
+ aput-char v1, p0, v0
+ return-void
+.end method
+
+.method public static setCharStaticField()V
+ .registers 1
+ const v0, 0x10101
+ sput-char v0, LTestCase;->staticCharField:C
+ return-void
+.end method
+
+.method public static setCharInstanceField(LTestCase;)V
+ .registers 2
+ const v0, 0x10101
+ iput-char v0, p0, LTestCase;->instanceCharField:C
+ return-void
+.end method
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.field public static staticByteField:B
+.field public instanceByteField:B
+.field public static staticShortField:S
+.field public instanceShortField:S
+.field public static staticCharField:C
+.field public instanceCharField:C
diff --git a/test/660-store-8-16/src/Main.java b/test/660-store-8-16/src/Main.java
new file mode 100644
index 0000000..32b2568
--- /dev/null
+++ b/test/660-store-8-16/src/Main.java
@@ -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.
+ */
+
+public class Main {
+ public static void expectEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void main(String[] unused) throws Exception {
+ Class<?> cls = Class.forName("TestCase");
+
+ cls.getMethod("setByteStaticField").invoke(null);
+ expectEquals(1, cls.getField("staticByteField").getByte(null));
+
+ cls.getMethod("setShortStaticField").invoke(null);
+ expectEquals(0x101, cls.getField("staticShortField").getShort(null));
+
+ cls.getMethod("setCharStaticField").invoke(null);
+ expectEquals(0x101, cls.getField("staticCharField").getChar(null));
+
+ {
+ Object[] args = { new byte[2] };
+ cls.getMethod("setByteArray", byte[].class).invoke(null, args);
+ expectEquals(1, ((byte[])args[0])[0]);
+ }
+ {
+ Object[] args = { new short[2] };
+ cls.getMethod("setShortArray", short[].class).invoke(null, args);
+ expectEquals(0x101, ((short[])args[0])[0]);
+ }
+ {
+ Object[] args = { new char[2] };
+ cls.getMethod("setCharArray", char[].class).invoke(null, args);
+ expectEquals(0x101, ((char[])args[0])[0]);
+ }
+ {
+ Object[] args = { cls.newInstance() };
+
+ cls.getMethod("setByteInstanceField", cls).invoke(null, args);
+ expectEquals(1, cls.getField("staticByteField").getByte(args[0]));
+
+ cls.getMethod("setShortInstanceField", cls).invoke(null, args);
+ expectEquals(0x101, cls.getField("staticShortField").getShort(args[0]));
+
+ cls.getMethod("setCharInstanceField", cls).invoke(null, args);
+ expectEquals(0x101, cls.getField("staticCharField").getChar(args[0]));
+ }
+ }
+}
diff --git a/test/661-checker-simd-reduc/expected.txt b/test/661-checker-simd-reduc/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/661-checker-simd-reduc/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/661-checker-simd-reduc/info.txt b/test/661-checker-simd-reduc/info.txt
new file mode 100644
index 0000000..4d071fb
--- /dev/null
+++ b/test/661-checker-simd-reduc/info.txt
@@ -0,0 +1 @@
+Functional tests on vectorization of the most basic reductions.
diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java
new file mode 100644
index 0000000..741b5fa
--- /dev/null
+++ b/test/661-checker-simd-reduc/src/Main.java
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+
+/**
+ * Tests for simple integral reductions: same type for accumulator and data.
+ */
+public class Main {
+
+ static final int N = 500;
+
+ //
+ // Basic reductions in loops.
+ //
+
+ // TODO: vectorize these (second step of b/64091002 plan)
+
+ private static byte reductionByte(byte[] x) {
+ byte sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ private static short reductionShort(short[] x) {
+ short sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ private static char reductionChar(char[] x) {
+ char sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ private static int reductionInt(int[] x) {
+ int sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ private static long reductionLong(long[] x) {
+ long sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ private static byte reductionMinusByte(byte[] x) {
+ byte sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum -= x[i];
+ }
+ return sum;
+ }
+
+ private static short reductionMinusShort(short[] x) {
+ short sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum -= x[i];
+ }
+ return sum;
+ }
+
+ private static char reductionMinusChar(char[] x) {
+ char sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum -= x[i];
+ }
+ return sum;
+ }
+
+ private static int reductionMinusInt(int[] x) {
+ int sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum -= x[i];
+ }
+ return sum;
+ }
+
+ private static long reductionMinusLong(long[] x) {
+ long sum = 0;
+ for (int i = 0; i < x.length; i++) {
+ sum -= x[i];
+ }
+ return sum;
+ }
+
+ private static byte reductionMinByte(byte[] x) {
+ byte min = Byte.MAX_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ min = (byte) Math.min(min, x[i]);
+ }
+ return min;
+ }
+
+ private static short reductionMinShort(short[] x) {
+ short min = Short.MAX_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ min = (short) Math.min(min, x[i]);
+ }
+ return min;
+ }
+
+ private static char reductionMinChar(char[] x) {
+ char min = Character.MAX_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ min = (char) Math.min(min, x[i]);
+ }
+ return min;
+ }
+
+ private static int reductionMinInt(int[] x) {
+ int min = Integer.MAX_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ min = Math.min(min, x[i]);
+ }
+ return min;
+ }
+
+ private static long reductionMinLong(long[] x) {
+ long min = Long.MAX_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ min = Math.min(min, x[i]);
+ }
+ return min;
+ }
+
+ private static byte reductionMaxByte(byte[] x) {
+ byte max = Byte.MIN_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ max = (byte) Math.max(max, x[i]);
+ }
+ return max;
+ }
+
+ private static short reductionMaxShort(short[] x) {
+ short max = Short.MIN_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ max = (short) Math.max(max, x[i]);
+ }
+ return max;
+ }
+
+ private static char reductionMaxChar(char[] x) {
+ char max = Character.MIN_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ max = (char) Math.max(max, x[i]);
+ }
+ return max;
+ }
+
+ private static int reductionMaxInt(int[] x) {
+ int max = Integer.MIN_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ max = Math.max(max, x[i]);
+ }
+ return max;
+ }
+
+ private static long reductionMaxLong(long[] x) {
+ long max = Long.MIN_VALUE;
+ for (int i = 0; i < x.length; i++) {
+ max = Math.max(max, x[i]);
+ }
+ return max;
+ }
+
+ //
+ // A few special cases.
+ //
+
+ // TODO: consider unrolling
+
+ private static int reductionInt10(int[] x) {
+ int sum = 0;
+ // Amenable to complete unrolling.
+ for (int i = 10; i <= 10; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ private static int reductionMinusInt10(int[] x) {
+ int sum = 0;
+ // Amenable to complete unrolling.
+ for (int i = 10; i <= 10; i++) {
+ sum -= x[i];
+ }
+ return sum;
+ }
+
+ private static int reductionMinInt10(int[] x) {
+ int min = Integer.MAX_VALUE;
+ // Amenable to complete unrolling.
+ for (int i = 10; i <= 10; i++) {
+ min = Math.min(min, x[i]);
+ }
+ return min;
+ }
+
+ private static int reductionMaxInt10(int[] x) {
+ int max = Integer.MIN_VALUE;
+ // Amenable to complete unrolling.
+ for (int i = 10; i <= 10; i++) {
+ max = Math.max(max, x[i]);
+ }
+ return max;
+ }
+
+ //
+ // Main driver.
+ //
+
+ public static void main(String[] args) {
+ byte[] xb = new byte[N];
+ short[] xs = new short[N];
+ char[] xc = new char[N];
+ int[] xi = new int[N];
+ long[] xl = new long[N];
+ for (int i = 0, k = -17; i < N; i++, k += 3) {
+ xb[i] = (byte) k;
+ xs[i] = (short) k;
+ xc[i] = (char) k;
+ xi[i] = k;
+ xl[i] = k;
+ }
+
+ // Test various reductions in loops.
+ expectEquals(-74, reductionByte(xb));
+ expectEquals(-27466, reductionShort(xs));
+ expectEquals(38070, reductionChar(xc));
+ expectEquals(365750, reductionInt(xi));
+ expectEquals(365750L, reductionLong(xl));
+ expectEquals(74, reductionMinusByte(xb));
+ expectEquals(27466, reductionMinusShort(xs));
+ expectEquals(27466, reductionMinusChar(xc));
+ expectEquals(-365750, reductionMinusInt(xi));
+ expectEquals(-365750L, reductionMinusLong(xl));
+ expectEquals(-128, reductionMinByte(xb));
+ expectEquals(-17, reductionMinShort(xs));
+ expectEquals(1, reductionMinChar(xc));
+ expectEquals(-17, reductionMinInt(xi));
+ expectEquals(-17L, reductionMinLong(xl));
+ expectEquals(127, reductionMaxByte(xb));
+ expectEquals(1480, reductionMaxShort(xs));
+ expectEquals(65534, reductionMaxChar(xc));
+ expectEquals(1480, reductionMaxInt(xi));
+ expectEquals(1480L, reductionMaxLong(xl));
+
+ // Test special cases.
+ expectEquals(13, reductionInt10(xi));
+ expectEquals(-13, reductionMinusInt10(xi));
+ expectEquals(13, reductionMinInt10(xi));
+ expectEquals(13, reductionMaxInt10(xi));
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/661-classloader-allocator/expected.txt b/test/661-classloader-allocator/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/661-classloader-allocator/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/661-classloader-allocator/info.txt b/test/661-classloader-allocator/info.txt
new file mode 100644
index 0000000..872b5e0
--- /dev/null
+++ b/test/661-classloader-allocator/info.txt
@@ -0,0 +1,3 @@
+Regression test for copied methods which used to not
+be (re-)allocated with the right class loader allocator,
+which crashed the JIT profiler.
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/661-classloader-allocator/src-ex/OtherClass.java
similarity index 65%
copy from test/066-mismatched-super/src/Indirect.java
copy to test/661-classloader-allocator/src-ex/OtherClass.java
index 023e409..e59cb95 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/661-classloader-allocator/src-ex/OtherClass.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+package p1;
+
+interface Itf {
+ public default int defaultMethod() {
+ return 42;
+ }
+}
+
+public class OtherClass implements Itf {
+ public void foo() {
+ }
}
diff --git a/test/661-classloader-allocator/src/Main.java b/test/661-classloader-allocator/src/Main.java
new file mode 100644
index 0000000..40f8f7a
--- /dev/null
+++ b/test/661-classloader-allocator/src/Main.java
@@ -0,0 +1,62 @@
+/*
+ * 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 java.lang.reflect.Constructor;
+
+public class Main {
+ static final String DEX_FILE =
+ System.getenv("DEX_LOCATION") + "/661-classloader-allocator-ex.jar";
+ static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+ private static void doUnloading() {
+ // Stop the JIT to ensure its threads and work queue are not keeping classes
+ // artifically alive.
+ stopJit();
+ // Do multiple GCs to prevent rare flakiness if some other thread is keeping the
+ // classloader live.
+ for (int i = 0; i < 5; ++i) {
+ Runtime.getRuntime().gc();
+ }
+ startJit();
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ loadClass();
+ doUnloading();
+ // fetchProfiles iterate over the ProfilingInfo, we used to crash in the presence
+ // of unloaded copied methods.
+ fetchProfiles();
+ }
+
+ public static void loadClass() throws Exception {
+ Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new AssertionError("Couldn't find path class loader class");
+ }
+ Constructor<?> constructor =
+ pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
+ ClassLoader loader = (ClassLoader) constructor.newInstance(
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
+ Class<?> otherClass = loader.loadClass("p1.OtherClass");
+ ensureJitCompiled(otherClass, "foo");
+ }
+
+ public static native void ensureJitCompiled(Class<?> cls, String methodName);
+ public static native void fetchProfiles();
+ public static native void stopJit();
+ public static native void startJit();
+}
diff --git a/test/662-regression-alias/expected.txt b/test/662-regression-alias/expected.txt
new file mode 100644
index 0000000..7250211
--- /dev/null
+++ b/test/662-regression-alias/expected.txt
@@ -0,0 +1 @@
+passed 127 4
diff --git a/test/662-regression-alias/info.txt b/test/662-regression-alias/info.txt
new file mode 100644
index 0000000..584d3ee
--- /dev/null
+++ b/test/662-regression-alias/info.txt
@@ -0,0 +1 @@
+Regression test on missed array element aliasing.
diff --git a/test/662-regression-alias/src/Main.java b/test/662-regression-alias/src/Main.java
new file mode 100644
index 0000000..aad61e2
--- /dev/null
+++ b/test/662-regression-alias/src/Main.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+/**
+ * Regression test on ARM-scheduling/array-aliasing bug (b/64018485).
+ */
+public class Main {
+
+ //
+ // Mimic original bug.
+ //
+
+ static void setFields(int[] fields) {
+ if (fields == null || fields.length < 6)
+ fields = new int[6]; // creates phi
+ fields[5] = 127;
+ }
+
+ static void processFieldValues(int field0, int field1, int field2,
+ int field3, int field4, int field5) {
+ if (field5 != 127) {
+ throw new Error("field = " + field5);
+ } else if (field0 != 0) {
+ processFieldValues(0, 0, 0, 0, 0, 0); // disable inlining
+ }
+ }
+
+ static int doit(int pass) {
+ int[] fields = new int[6];
+ for (; ; pass++) {
+ setFields(fields);
+ processFieldValues(fields[0], fields[1], fields[2],
+ fields[3], fields[4], fields[5]);
+ if (pass == 0)
+ break;
+ }
+ return fields[5];
+ }
+
+ //
+ // Similar situation.
+ //
+
+ private static int aliasing(boolean f) {
+ int[] array = new int[6];
+ int[] array2 = null;
+ int s = 0;
+ for (int i = 0; i < 1; i++) {
+ if (f) {
+ array2 = array;
+ }
+ array2[1] = 4;
+ s = array[1];
+ }
+ return s;
+ }
+
+ //
+ // Main driver.
+ //
+
+ static public void main(String[] args) {
+ int r = doit(0);
+ int s = aliasing(true);
+ System.out.println("passed " + r + " " + s);
+ }
+}
diff --git a/test/663-odd-dex-size/classes.dex b/test/663-odd-dex-size/classes.dex
new file mode 100644
index 0000000..633e3a2
--- /dev/null
+++ b/test/663-odd-dex-size/classes.dex
Binary files differ
diff --git a/test/663-odd-dex-size/expected.txt b/test/663-odd-dex-size/expected.txt
new file mode 100644
index 0000000..3da1ec2
--- /dev/null
+++ b/test/663-odd-dex-size/expected.txt
@@ -0,0 +1 @@
+HelloWorld
diff --git a/test/663-odd-dex-size/info.txt b/test/663-odd-dex-size/info.txt
new file mode 100644
index 0000000..11a50e0
--- /dev/null
+++ b/test/663-odd-dex-size/info.txt
@@ -0,0 +1,14 @@
+Test for a dex file with an odd size in a vdex file.
+
+The code in the file is:
+
+class Main {
+ public static void main(String[] args) {
+ System.out.println("HelloWorld");
+ }
+}
+
+The generated dex file was then manually edited to:
+1) Add 1 to the size value in the dex header.
+2) Add 1 byte to the file.
+3) Change the checksum in the dex header.
diff --git a/test/663-odd-dex-size2/663-odd-dex-size2.jar b/test/663-odd-dex-size2/663-odd-dex-size2.jar
new file mode 100644
index 0000000..a29224e
--- /dev/null
+++ b/test/663-odd-dex-size2/663-odd-dex-size2.jar
Binary files differ
diff --git a/test/663-odd-dex-size2/build b/test/663-odd-dex-size2/build
new file mode 100644
index 0000000..5636558
--- /dev/null
+++ b/test/663-odd-dex-size2/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+# Nothing to do
diff --git a/test/663-odd-dex-size2/expected.txt b/test/663-odd-dex-size2/expected.txt
new file mode 100644
index 0000000..3da1ec2
--- /dev/null
+++ b/test/663-odd-dex-size2/expected.txt
@@ -0,0 +1 @@
+HelloWorld
diff --git a/test/663-odd-dex-size2/info.txt b/test/663-odd-dex-size2/info.txt
new file mode 100644
index 0000000..900394d
--- /dev/null
+++ b/test/663-odd-dex-size2/info.txt
@@ -0,0 +1,15 @@
+Test for two files with an odd size in a vdex file.
+
+The code in boths file is:
+
+class Main {
+ public static void main(String[] args) {
+ System.out.println("HelloWorld");
+ }
+}
+
+The generated dex file was then manually edited to:
+1) Add 1 to the size value in the dex header.
+2) Add 1 byte to the file.
+3) Change the checksum in the dex header.
+
diff --git a/test/663-odd-dex-size3/663-odd-dex-size3.jar b/test/663-odd-dex-size3/663-odd-dex-size3.jar
new file mode 100644
index 0000000..d23ed57
--- /dev/null
+++ b/test/663-odd-dex-size3/663-odd-dex-size3.jar
Binary files differ
diff --git a/test/663-odd-dex-size3/build b/test/663-odd-dex-size3/build
new file mode 100644
index 0000000..5636558
--- /dev/null
+++ b/test/663-odd-dex-size3/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+# Nothing to do
diff --git a/test/663-odd-dex-size3/expected.txt b/test/663-odd-dex-size3/expected.txt
new file mode 100644
index 0000000..3da1ec2
--- /dev/null
+++ b/test/663-odd-dex-size3/expected.txt
@@ -0,0 +1 @@
+HelloWorld
diff --git a/test/663-odd-dex-size3/info.txt b/test/663-odd-dex-size3/info.txt
new file mode 100644
index 0000000..256c77d
--- /dev/null
+++ b/test/663-odd-dex-size3/info.txt
@@ -0,0 +1,19 @@
+Test for a dex file with an odd size followed by an aligned dex file.
+
+The code in classes.dex is:
+
+class Main {
+ public static void main(String[] args) {
+ System.out.println("HelloWorld");
+ }
+}
+
+The generated dex file was then manually edited to:
+1) Add 1 to the size value in the dex header.
+2) Add 1 byte to the file.
+3) Change the checksum in the dex header.
+
+The code in classes2.dex is:
+
+class Foo {
+}
diff --git a/test/663-odd-dex-size4/663-odd-dex-size4.jar b/test/663-odd-dex-size4/663-odd-dex-size4.jar
new file mode 100644
index 0000000..d229663
--- /dev/null
+++ b/test/663-odd-dex-size4/663-odd-dex-size4.jar
Binary files differ
diff --git a/test/663-odd-dex-size4/build b/test/663-odd-dex-size4/build
new file mode 100644
index 0000000..5636558
--- /dev/null
+++ b/test/663-odd-dex-size4/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+# Nothing to do
diff --git a/test/663-odd-dex-size4/expected.txt b/test/663-odd-dex-size4/expected.txt
new file mode 100644
index 0000000..3da1ec2
--- /dev/null
+++ b/test/663-odd-dex-size4/expected.txt
@@ -0,0 +1 @@
+HelloWorld
diff --git a/test/663-odd-dex-size4/info.txt b/test/663-odd-dex-size4/info.txt
new file mode 100644
index 0000000..2c34557
--- /dev/null
+++ b/test/663-odd-dex-size4/info.txt
@@ -0,0 +1,19 @@
+Test for an aligned dex file followed by a dex file with an odd size.
+
+The code in classes.dex is:
+
+class Foo {
+}
+
+The code in classes2.dex is:
+
+class Main {
+ public static void main(String[] args) {
+ System.out.println("HelloWorld");
+ }
+}
+
+The generated dex file was then manually edited to:
+1) Add 1 to the size value in the dex header.
+2) Add 1 byte to the file.
+3) Change the checksum in the dex header.
diff --git a/test/706-checker-scheduler/run b/test/706-checker-scheduler/run
new file mode 100644
index 0000000..5ffc303
--- /dev/null
+++ b/test/706-checker-scheduler/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/test/066-mismatched-super/src/Indirect.java b/test/706-checker-scheduler/src-dex2oat-unresolved/UnresolvedClass.java
similarity index 65%
rename from test/066-mismatched-super/src/Indirect.java
rename to test/706-checker-scheduler/src-dex2oat-unresolved/UnresolvedClass.java
index 023e409..4faa12a 100644
--- a/test/066-mismatched-super/src/Indirect.java
+++ b/test/706-checker-scheduler/src-dex2oat-unresolved/UnresolvedClass.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-/**
- * Error indirection class.
- *
- * Some VMs will load this class and fail on the "new" call, others will
- * refuse to load this class at all.
- */
-public class Indirect {
- public static void main() {
- Base base = new Base();
- }
+public class UnresolvedClass {
+ public static int staticInt;
+ public int instanceInt;
}
+
diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java
index a68565b..08a23a7 100644
--- a/test/706-checker-scheduler/src/Main.java
+++ b/test/706-checker-scheduler/src/Main.java
@@ -31,6 +31,7 @@
public ExampleObj my_obj;
public static int number1;
public static int number2;
+ public static volatile int number3;
/// CHECK-START-ARM64: int Main.arrayAccess() scheduler (before)
/// CHECK: <<Const1:i\d+>> IntConstant 1
@@ -340,6 +341,87 @@
}
}
+ /// CHECK-START-ARM: void Main.accessFieldsVolatile() scheduler (before)
+ /// CHECK-START-ARM64: void Main.accessFieldsVolatile() scheduler (before)
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: StaticFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: StaticFieldSet
+
+ /// CHECK-START-ARM: void Main.accessFieldsVolatile() scheduler (after)
+ /// CHECK-START-ARM64: void Main.accessFieldsVolatile() scheduler (after)
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: StaticFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: StaticFieldSet
+
+ public void accessFieldsVolatile() {
+ my_obj = new ExampleObj(1, 2);
+ for (int i = 0; i < 10; i++) {
+ my_obj.n1++;
+ my_obj.n2++;
+ number1++;
+ number3++;
+ }
+ }
+
+ /// CHECK-START-ARM: void Main.accessFieldsUnresolved() scheduler (before)
+ /// CHECK-START-ARM64: void Main.accessFieldsUnresolved() scheduler (before)
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: UnresolvedInstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: UnresolvedInstanceFieldSet
+ /// CHECK: UnresolvedStaticFieldGet
+ /// CHECK: Add
+ /// CHECK: UnresolvedStaticFieldSet
+
+ /// CHECK-START-ARM: void Main.accessFieldsUnresolved() scheduler (after)
+ /// CHECK-START-ARM64: void Main.accessFieldsUnresolved() scheduler (after)
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: UnresolvedInstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: UnresolvedInstanceFieldSet
+ /// CHECK: UnresolvedStaticFieldGet
+ /// CHECK: Add
+ /// CHECK: UnresolvedStaticFieldSet
+
+ public void accessFieldsUnresolved() {
+ my_obj = new ExampleObj(1, 2);
+ UnresolvedClass unresolved_obj = new UnresolvedClass();
+ for (int i = 0; i < 10; i++) {
+ my_obj.n1++;
+ my_obj.n2++;
+ unresolved_obj.instanceInt++;
+ UnresolvedClass.staticInt++;
+ }
+ }
+
/// CHECK-START-ARM64: int Main.intDiv(int) scheduler (before)
/// CHECK: Sub
/// CHECK: DivZeroCheck
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 21dcf98..2edd91e 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -127,7 +127,7 @@
printf("Unable to get jvmti env!\n");
return 1;
}
- SetAllCapabilities(jvmti_env);
+ SetStandardCapabilities(jvmti_env);
jvmtiPhase current_phase;
jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase);
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
index a2694e7..e0a0136 100644
--- a/test/903-hello-tagging/tagging.cc
+++ b/test/903-hello-tagging/tagging.cc
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#include <iostream>
#include <pthread.h>
-#include <stdio.h>
+
+#include <cstdio>
+#include <iostream>
#include <vector>
#include "android-base/logging.h"
#include "jni.h"
+#include "jvmti.h"
#include "scoped_local_ref.h"
#include "scoped_primitive_array.h"
-#include "jvmti.h"
-
// Test infrastructure
#include "jvmti_helper.h"
#include "test_env.h"
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index 20b5328..9d2592a 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include <pthread.h>
+
+#include <cstdio>
#include <iostream>
#include <mutex>
-#include <pthread.h>
-#include <stdio.h>
#include <vector>
#include "android-base/logging.h"
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
index 998194a..bf86c9a 100644
--- a/test/905-object-free/tracking_free.cc
+++ b/test/905-object-free/tracking_free.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include <iostream>
#include <pthread.h>
-#include <stdio.h>
+
+#include <cstdio>
+#include <iostream>
#include <vector>
#include "android-base/logging.h"
@@ -61,7 +62,7 @@
JavaVM* jvm = nullptr;
env->GetJavaVM(&jvm);
CHECK_EQ(jvm->GetEnv(reinterpret_cast<void**>(&jvmti_env2), JVMTI_VERSION_1_2), 0);
- SetAllCapabilities(jvmti_env2);
+ SetStandardCapabilities(jvmti_env2);
setupObjectFreeCallback(env, jvmti_env2, ObjectFree2);
}
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 02ac699..57c0274 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#include "inttypes.h"
+#include <inttypes.h>
+#include <pthread.h>
+#include <cstdio>
#include <iomanip>
#include <iostream>
-#include <pthread.h>
#include <sstream>
-#include <stdio.h>
#include <vector>
#include "android-base/logging.h"
diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc
index 1eadf15..87c98e1 100644
--- a/test/907-get-loaded-classes/get_loaded_classes.cc
+++ b/test/907-get-loaded-classes/get_loaded_classes.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include <iostream>
#include <pthread.h>
-#include <stdio.h>
+
+#include <cstdio>
+#include <iostream>
#include <vector>
#include "android-base/macros.h"
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index 985120c..2b620b1 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -15,8 +15,9 @@
*/
#include <inttypes.h>
+
+#include <cstdio>
#include <memory>
-#include <stdio.h>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index bf3f7b6..f7862c7 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -15,15 +15,15 @@
*/
#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
+#include <cstdio>
+#include <cstring>
#include <iostream>
#include <sstream>
#include <vector>
-#include "android-base/macros.h"
#include "android-base/logging.h"
+#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "jni.h"
diff --git a/test/924-threads/src/art/Test924.java b/test/924-threads/src/art/Test924.java
index 84b7c62..b73eb30 100644
--- a/test/924-threads/src/art/Test924.java
+++ b/test/924-threads/src/art/Test924.java
@@ -164,8 +164,10 @@
do {
Thread.yield();
} while (t.getState() != Thread.State.BLOCKED);
- Thread.sleep(10);
- printThreadState(t);
+ // Since internal thread suspension (For GC or other cases) can happen at any time and changes
+ // the thread state we just have it print the majority thread state across 11 calls over 55
+ // milliseconds.
+ printMajorityThreadState(t, 11, 5);
}
// Sleeping.
@@ -357,10 +359,32 @@
STATE_KEYS.addAll(STATE_NAMES.keySet());
Collections.sort(STATE_KEYS);
}
-
- private static void printThreadState(Thread t) {
- int state = getThreadState(t);
+ // Call getThreadState 'votes' times waiting 'wait' millis between calls and print the most common
+ // result.
+ private static void printMajorityThreadState(Thread t, int votes, int wait) throws Exception {
+ Map<Integer, Integer> states = new HashMap<>();
+ for (int i = 0; i < votes; i++) {
+ int cur_state = getThreadState(t);
+ states.put(cur_state, states.getOrDefault(cur_state, 0) + 1);
+ Thread.sleep(wait); // Wait a little bit.
+ }
+ int best_state = -1;
+ int highest_count = 0;
+ for (Map.Entry<Integer, Integer> e : states.entrySet()) {
+ if (e.getValue() > highest_count) {
+ highest_count = e.getValue();
+ best_state = e.getKey();
+ }
+ }
+ printThreadState(best_state);
+ }
+
+ private static void printThreadState(Thread t) {
+ printThreadState(getThreadState(t));
+ }
+
+ private static void printThreadState(int state) {
StringBuilder sb = new StringBuilder();
for (Integer i : STATE_KEYS) {
diff --git a/test/933-misc-events/misc_events.cc b/test/933-misc-events/misc_events.cc
index 27dab8b..d2ae0f4 100644
--- a/test/933-misc-events/misc_events.cc
+++ b/test/933-misc-events/misc_events.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include <atomic>
#include <signal.h>
#include <sys/types.h>
+#include <atomic>
+
#include "android-base/logging.h"
#include "android-base/macros.h"
#include "jni.h"
diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc
index b2ef056..90d87e0 100644
--- a/test/936-search-onload/search_onload.cc
+++ b/test/936-search-onload/search_onload.cc
@@ -39,7 +39,7 @@
printf("Unable to get jvmti env!\n");
return 1;
}
- SetAllCapabilities(jvmti_env);
+ SetStandardCapabilities(jvmti_env);
char* dex_loc = getenv("DEX_LOCATION");
std::string dex1 = android::base::StringPrintf("%s/936-search-onload.jar", dex_loc);
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
index e3090f5..418ce90 100644
--- a/test/945-obsolete-native/obsolete_native.cc
+++ b/test/945-obsolete-native/obsolete_native.cc
@@ -15,8 +15,9 @@
*/
#include <inttypes.h>
+
+#include <cstdio>
#include <memory>
-#include <stdio.h>
#include "android-base/stringprintf.h"
#include "jni.h"
@@ -24,8 +25,8 @@
// Test infrastructure
#include "jni_binder.h"
-#include "test_env.h"
#include "scoped_local_ref.h"
+#include "test_env.h"
namespace art {
namespace Test945ObsoleteNative {
diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc
index a433dc9..570ade3 100644
--- a/test/983-source-transform-verify/source_transform.cc
+++ b/test/983-source-transform-verify/source_transform.cc
@@ -15,13 +15,15 @@
*/
#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
+#include <cstdio>
+#include <cstring>
#include <iostream>
#include <vector>
#include "android-base/stringprintf.h"
+#include "jni.h"
+#include "jvmti.h"
#include "base/logging.h"
#include "base/macros.h"
@@ -29,9 +31,7 @@
#include "dex_file.h"
#include "dex_instruction.h"
#include "jit/jit.h"
-#include "jni.h"
#include "native_stack_dump.h"
-#include "jvmti.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
@@ -111,7 +111,7 @@
printf("Unable to get jvmti env!\n");
return 1;
}
- SetAllCapabilities(jvmti_env);
+ SetStandardCapabilities(jvmti_env);
jvmtiEventCallbacks cb;
memset(&cb, 0, sizeof(cb));
cb.ClassFileLoadHook = CheckDexFileHook;
diff --git a/test/986-native-method-bind/native_bind.cc b/test/986-native-method-bind/native_bind.cc
index eec635b..1afe4db 100644
--- a/test/986-native-method-bind/native_bind.cc
+++ b/test/986-native-method-bind/native_bind.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include <inttypes.h>
-#include <memory>
-#include <stdio.h>
#include <dlfcn.h>
+#include <inttypes.h>
+
+#include <cstdio>
+#include <memory>
#include "android-base/stringprintf.h"
#include "jni.h"
@@ -26,8 +27,8 @@
// Test infrastructure
#include "jni_binder.h"
#include "jvmti_helper.h"
-#include "test_env.h"
#include "scoped_local_ref.h"
+#include "test_env.h"
namespace art {
namespace Test986NativeBind {
diff --git a/test/987-agent-bind/agent_bind.cc b/test/987-agent-bind/agent_bind.cc
index 44366c1..7dbdd8e 100644
--- a/test/987-agent-bind/agent_bind.cc
+++ b/test/987-agent-bind/agent_bind.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include <inttypes.h>
-#include <memory>
-#include <stdio.h>
#include <dlfcn.h>
+#include <inttypes.h>
+
+#include <cstdio>
+#include <memory>
#include "android-base/stringprintf.h"
#include "jni.h"
@@ -26,8 +27,8 @@
// Test infrastructure
#include "jni_binder.h"
#include "jvmti_helper.h"
-#include "test_env.h"
#include "scoped_local_ref.h"
+#include "test_env.h"
namespace art {
namespace Test987AgentBind {
diff --git a/test/989-method-trace-throw/method_trace.cc b/test/989-method-trace-throw/method_trace.cc
index 554784e..019b6a9 100644
--- a/test/989-method-trace-throw/method_trace.cc
+++ b/test/989-method-trace-throw/method_trace.cc
@@ -15,8 +15,9 @@
*/
#include <inttypes.h>
+
+#include <cstdio>
#include <memory>
-#include <stdio.h>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
diff --git a/test/992-source-data/source_file.cc b/test/992-source-data/source_file.cc
index 3e8989e..46d197d 100644
--- a/test/992-source-data/source_file.cc
+++ b/test/992-source-data/source_file.cc
@@ -15,8 +15,9 @@
*/
#include <inttypes.h>
+
+#include <cstdio>
#include <memory>
-#include <stdio.h>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
diff --git a/test/993-breakpoints/breakpoints.cc b/test/993-breakpoints/breakpoints.cc
index 1292070..3734ce8 100644
--- a/test/993-breakpoints/breakpoints.cc
+++ b/test/993-breakpoints/breakpoints.cc
@@ -15,8 +15,9 @@
*/
#include <inttypes.h>
+
+#include <cstdio>
#include <memory>
-#include <stdio.h>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
diff --git a/test/996-breakpoint-obsolete/obsolete_breakpoints.cc b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
index b6a67e4..820af47 100644
--- a/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
+++ b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
@@ -15,8 +15,9 @@
*/
#include <inttypes.h>
+
+#include <cstdio>
#include <memory>
-#include <stdio.h>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
diff --git a/test/Android.bp b/test/Android.bp
index 44cb4f6..fab664a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -252,8 +252,10 @@
"ti-agent/test_env.cc",
"ti-agent/breakpoint_helper.cc",
"ti-agent/common_helper.cc",
+ "ti-agent/locals_helper.cc",
"ti-agent/redefinition_helper.cc",
"ti-agent/suspension_helper.cc",
+ "ti-agent/stack_trace_helper.cc",
"ti-agent/trace_helper.cc",
// This is the list of non-special OnLoad things and excludes BCI and anything that depends
// on ART internals.
@@ -292,6 +294,7 @@
"1905-suspend-native/native_suspend.cc",
"1908-suspend-native-resume-self/native_suspend_resume.cc",
"1909-per-agent-tls/agent_tls.cc",
+ "1914-get-local-instance/local_instance.cc",
],
shared_libs: [
"libbase",
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 7c0ed69..60c7315 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -254,4 +254,17 @@
return Runtime::Current()->GetNumberOfDeoptimizations();
}
+extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) {
+ jit::Jit* jit = GetJitIfEnabled();
+ if (jit == nullptr) {
+ return;
+ }
+ jit::JitCodeCache* code_cache = jit->GetCodeCache();
+ std::vector<ProfileMethodInfo> unused_vector;
+ std::set<std::string> unused_locations;
+ unused_locations.insert("fake_location");
+ ScopedObjectAccess soa(Thread::Current());
+ code_cache->GetProfiledMethods(unused_locations, unused_vector);
+}
+
} // namespace art
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index ede485a..e989e39 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -453,6 +453,10 @@
if [ "$USE_JVM" = "y" ]; then
export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
+ # Some jvmti tests are flaky without -Xint on the RI.
+ if [ "$IS_JVMTI_TEST" = "y" ]; then
+ FLAGS="${FLAGS} -Xint"
+ fi
# Xmx is necessary since we don't pass down the ART flags to JVM.
# We pass the classes2 path whether it's used (src-multidex) or not.
cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 ${FLAGS} $MAIN $@ ${ARGS}"
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 3edb0a8..a8191bb 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -600,15 +600,6 @@
},
{
"tests": [
- "567-checker-compare",
- "988-method-trace"
- ],
- "description": "Checker tests fail because desugar lowers Long.compare to lcmp",
- "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
- "bug": "b/63078894"
- },
- {
- "tests": [
"536-checker-needs-access-check",
"537-checker-inline-and-unverified",
"569-checker-pattern-replacement",
@@ -716,10 +707,22 @@
"variant": "gcstress & jit & target"
},
{
- "tests": ["004-JniTest"],
- "description": [ "Tests failing with --build-with-javac-dx since the new annotation",
- "lookup changes" ],
- "bug": "b/63089991",
- "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"}
+ "tests": "660-clinit",
+ "variant": "no-image | no-dex2oat | no-prebuild",
+ "description": ["Tests <clinit> for app images, which --no-image, --no-prebuild and",
+ "--no-dex2oat do not create"]
+ },
+ {
+ "tests": ["961-default-iface-resolution-gen",
+ "964-default-iface-init-gen",
+ "968-default-partial-compile-gen"],
+ "env_vars": {"SANITIZE_HOST": "address"},
+ "description": ["Test hits dex2oat watchdog timeout (60sec) on art-asan"]
+ },
+ {
+ "tests": "662-regression-alias",
+ "variant": "target",
+ "description": ["disable until ARM scheduling/aliasing bug is fixed."],
+ "bug": "b/64018485"
}
]
diff --git a/test/run-test b/test/run-test
index 486b465..e6196a0 100755
--- a/test/run-test
+++ b/test/run-test
@@ -45,7 +45,7 @@
export RUN="${progdir}/etc/run-test-jar"
export DEX_LOCATION=/data/run-test/${test_dir}
export NEED_DEX="true"
-export USE_JACK="true"
+export USE_JACK="false"
export USE_DESUGAR="true"
export SMALI_ARGS=""
@@ -926,6 +926,11 @@
tail -n 3000 "$tmp_dir/$strace_output"
echo '####################'
fi
+ if [ "x$target_mode" = "xno" -a "x$SANITIZE_HOST" = "xaddress" ]; then
+ # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The
+ # tools used by the given ABI work for both x86 and x86-64.
+ echo "ABI: 'x86_64'" | cat - "$output" | $ANDROID_BUILD_TOP/development/scripts/stack | tail -n 3000
+ fi
echo ' '
fi
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 6596ff4..d45d009 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -32,7 +32,9 @@
'TARGET_2ND_ARCH',
'TARGET_ARCH',
'HOST_PREFER_32_BIT',
- 'HOST_OUT_EXECUTABLES']
+ 'HOST_OUT_EXECUTABLES',
+ 'ANDROID_JAVA_TOOLCHAIN',
+ 'ANDROID_COMPILE_WITH_JACK']
_DUMP_MANY_VARS = None # To be set to a dictionary with above list being the keys,
# and the build variable being the value.
def _dump_many_vars(var_name):
@@ -77,6 +79,15 @@
def _get_build_var(var_name):
return _dump_many_vars(var_name)
+def _get_build_var_boolean(var, default):
+ val = _get_build_var(var)
+ if val:
+ if val == "True" or val == "true":
+ return True
+ if val == "False" or val == "false":
+ return False
+ return default
+
def get_env(key):
return _env.get(key)
@@ -96,7 +107,7 @@
ANDROID_BUILD_TOP = _get_android_build_top()
# Compiling with jack? Possible values in (True, False, 'default')
-ANDROID_COMPILE_WITH_JACK = _getEnvBoolean('ANDROID_COMPILE_WITH_JACK', 'default')
+ANDROID_COMPILE_WITH_JACK = _get_build_var_boolean('ANDROID_COMPILE_WITH_JACK', 'default')
# Directory used for temporary test files on the host.
ART_HOST_TEST_DIR = tempfile.mkdtemp(prefix = 'test-art-')
@@ -238,3 +249,9 @@
for tool in ['jack', 'dx', 'smali', 'jasmin', 'dxmerger']:
binary = tool if tool != 'dxmerger' else 'dexmerger'
os.environ.setdefault(tool.upper(), HOST_OUT_EXECUTABLES + '/' + binary)
+
+ANDROID_JAVA_TOOLCHAIN = os.path.join(ANDROID_BUILD_TOP,
+ _get_build_var('ANDROID_JAVA_TOOLCHAIN'))
+
+# include platform prebuilt java, javac, etc in $PATH.
+os.environ['PATH'] = ANDROID_JAVA_TOOLCHAIN + ':' + os.environ['PATH']
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 37b3559..b1274c9 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -100,8 +100,6 @@
run_test_command += ['-b']
run_test_command += ['--host']
run_test_command += ['--verbose']
- run_test_command += ['--timeout']
- run_test_command += ['14100'] # 235 minutes (The go/ab timeout is 14500)
sys.stdout.write(str(run_test_command) + '\n')
sys.stdout.flush()
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index baf7600..e8b6f1c 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -303,7 +303,8 @@
}
},
'art-gtest-valgrind32': {
- 'make' : 'valgrind-test-art-host32',
+ # Disabled: x86 valgrind does not understand SSE4.x
+ # 'make' : 'valgrind-test-art-host32',
'env': {
'ART_USE_READ_BARRIER' : 'false'
}
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 017c19b..68e1856 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -50,7 +50,6 @@
import json
import multiprocessing
import os
-import operator
import re
import subprocess
import sys
@@ -76,7 +75,9 @@
OPTIMIZING_COMPILER_TYPES = set()
JVMTI_TYPES = set()
ADDRESS_SIZES_TARGET = {'host': set(), 'target': set()}
-TIME_STATS = {}
+# timeout for individual tests.
+# TODO: make it adjustable per tests and for buildbots
+timeout = 3000 # 50 minutes
# DISABLED_TEST_CONTAINER holds information about the disabled tests. It is a map
# that has key as the test name (like 001-HelloWorld), and value as set of
@@ -127,11 +128,6 @@
gdb_arg = ''
stop_testrunner = False
-# timeout for individual tests.
-# TODO: make it adjustable per tests and for buildbots
-test_timeout = 3000 # 50 minutes
-timeout = sys.maxsize
-
def gather_test_info():
"""The method gathers test information about the test to be run which includes
generating the list of total tests from the art/test directory and the list
@@ -510,13 +506,12 @@
test_skipped = True
else:
test_skipped = False
- start_recording_time(test_name)
if gdb:
proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, universal_newlines=True)
else:
proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, stdout = subprocess.PIPE,
universal_newlines=True)
- script_output = proc.communicate(timeout=test_timeout)[0]
+ script_output = proc.communicate(timeout=timeout)[0]
test_passed = not proc.wait()
if not test_skipped:
@@ -534,16 +529,15 @@
else:
print_test_info(test_name, '')
except subprocess.TimeoutExpired as e:
- failed_tests.append((test_name, 'Timed out in %d seconds' % test_timeout))
+ failed_tests.append((test_name, 'Timed out in %d seconds' % timeout))
print_test_info(test_name, 'TIMEOUT', 'Timed out in %d seconds\n%s' % (
- test_timeout, command))
+ timeout, command))
except Exception as e:
failed_tests.append((test_name, str(e)))
print_test_info(test_name, 'FAIL',
('%s\n%s\n\n') % (command, str(e)))
finally:
semaphore.release()
- stop_recording_time(test_name)
def print_test_info(test_name, result, failed_test_info=""):
@@ -735,7 +729,6 @@
sys.stdout.flush()
def print_analysis():
- print_mutex.acquire()
if not verbose:
# Without --verbose, the testrunner erases passing test info. It
# does that by overriding the printed text with white spaces all across
@@ -769,7 +762,6 @@
print_text(COLOR_ERROR + '----------' + COLOR_NORMAL + '\n')
for failed_test in sorted([test_info[0] for test_info in failed_tests]):
print_text(('%s\n' % (failed_test)))
- print_mutex.release()
def parse_test_name(test_name):
@@ -867,16 +859,12 @@
global build
global gdb
global gdb_arg
- global test_timeout
global timeout
parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
parser.add_argument('-t', '--test', dest='test', help='name of the test')
parser.add_argument('-j', type=int, dest='n_thread')
- parser.add_argument('--timeout', default=timeout, type=int, dest='timeout',
- help='timeout the testrunner')
- parser.add_argument('--test-timeout', default=test_timeout, type=int, dest='test_timeout',
- help='timeout for individual tests')
+ parser.add_argument('--timeout', default=timeout, type=int, dest='timeout')
for variant in TOTAL_VARIANTS_SET:
flag = '--' + variant
flag_dest = variant.replace('-', '_')
@@ -998,58 +986,28 @@
gdb = True
if options['gdb_arg']:
gdb_arg = options['gdb_arg']
-
timeout = options['timeout']
- test_timeout = options['test_timeout']
return test
-def start_recording_time(key):
- """To begin recording time for the event associated with the key.
- """
- TIME_STATS[key] = -(time.time())
-
-def stop_recording_time(key):
- """To stop timer for the event associated with the key.
- """
- TIME_STATS[key] = time.time() + TIME_STATS[key]
-
-def print_time_info():
- """Print time information for different invocation.
- """
- print_mutex.acquire()
- print_text('\nTIME INFO\n')
- for key in TIME_STATS:
- # Handle unfinised jobs.
- if TIME_STATS[key] < 0:
- TIME_STATS[key] = time.time() + TIME_STATS[key]
-
- info_list = sorted(TIME_STATS.items(), key=operator.itemgetter(1), reverse=True)
- for time_info_tuple in info_list:
- print_text('%s : %.2f sec\n' % (time_info_tuple[0], time_info_tuple[1]))
- print_mutex.release()
-
def main():
- start_time = time.time()
gather_test_info()
user_requested_test = parse_option()
setup_test_env()
if build:
build_targets = ''
if 'host' in TARGET_TYPES:
- build_targets += ' test-art-host-run-test-dependencies'
+ build_targets += 'test-art-host-run-test-dependencies'
if 'target' in TARGET_TYPES:
- build_targets += ' test-art-target-run-test-dependencies'
+ build_targets += 'test-art-target-run-test-dependencies'
build_command = 'make'
build_command += ' -j'
build_command += ' -C ' + env.ANDROID_BUILD_TOP
build_command += ' ' + build_targets
# Add 'dist' to avoid Jack issues b/36169180.
build_command += ' dist'
- start_recording_time(build_command)
if subprocess.call(build_command.split()):
sys.exit(1)
- stop_recording_time(build_command)
if user_requested_test:
test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_test,))
else:
@@ -1058,15 +1016,8 @@
try:
test_runner_thread.start()
while threading.active_count() > 1:
- if (time.time() - start_time > timeout):
- # to ensure that the run ends before the go/ab bots
- # time out the invocation.
- print_text("FAILED: timeout reached")
- print_time_info()
- print_analysis()
- sys.exit(1)
- time.sleep(1)
-
+ time.sleep(0.1)
+ print_analysis()
except Exception as e:
print_analysis()
print_text(str(e))
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 0679c1b..1d13c62 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -62,7 +62,7 @@
printf("Unable to get jvmti env!\n");
return 1;
}
- SetAllCapabilities(jvmti_env);
+ SetStandardCapabilities(jvmti_env);
return 0;
}
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
index 598a30f..7280102 100644
--- a/test/ti-agent/jvmti_helper.cc
+++ b/test/ti-agent/jvmti_helper.cc
@@ -15,12 +15,14 @@
*/
#include "jvmti_helper.h"
+#include "test_env.h"
+
+#include <dlfcn.h>
#include <algorithm>
-#include <dlfcn.h>
-#include <stdio.h>
+#include <cstdio>
+#include <cstring>
#include <sstream>
-#include <string.h>
#include "android-base/logging.h"
#include "scoped_local_ref.h"
@@ -38,15 +40,73 @@
}
}
-void SetAllCapabilities(jvmtiEnv* env) {
- jvmtiCapabilities caps;
- jvmtiError error1 = env->GetPotentialCapabilities(&caps);
- CheckJvmtiError(env, error1);
- jvmtiError error2 = env->AddCapabilities(&caps);
- CheckJvmtiError(env, error2);
+// These are a set of capabilities we will enable in all situations. These are chosen since they
+// will not affect the runtime in any significant way if they are enabled.
+static const jvmtiCapabilities standard_caps = {
+ .can_tag_objects = 1,
+ .can_generate_field_modification_events = 1,
+ .can_generate_field_access_events = 1,
+ .can_get_bytecodes = 1,
+ .can_get_synthetic_attribute = 1,
+ .can_get_owned_monitor_info = 0,
+ .can_get_current_contended_monitor = 0,
+ .can_get_monitor_info = 0,
+ .can_pop_frame = 0,
+ .can_redefine_classes = 1,
+ .can_signal_thread = 0,
+ .can_get_source_file_name = 1,
+ .can_get_line_numbers = 1,
+ .can_get_source_debug_extension = 1,
+ .can_access_local_variables = 0,
+ .can_maintain_original_method_order = 1,
+ .can_generate_single_step_events = 1,
+ .can_generate_exception_events = 0,
+ .can_generate_frame_pop_events = 0,
+ .can_generate_breakpoint_events = 1,
+ .can_suspend = 1,
+ .can_redefine_any_class = 0,
+ .can_get_current_thread_cpu_time = 0,
+ .can_get_thread_cpu_time = 0,
+ .can_generate_method_entry_events = 1,
+ .can_generate_method_exit_events = 1,
+ .can_generate_all_class_hook_events = 0,
+ .can_generate_compiled_method_load_events = 0,
+ .can_generate_monitor_events = 0,
+ .can_generate_vm_object_alloc_events = 1,
+ .can_generate_native_method_bind_events = 1,
+ .can_generate_garbage_collection_events = 1,
+ .can_generate_object_free_events = 1,
+ .can_force_early_return = 0,
+ .can_get_owned_monitor_stack_depth_info = 0,
+ .can_get_constant_pool = 0,
+ .can_set_native_method_prefix = 0,
+ .can_retransform_classes = 1,
+ .can_retransform_any_class = 0,
+ .can_generate_resource_exhaustion_heap_events = 0,
+ .can_generate_resource_exhaustion_threads_events = 0,
+};
+
+jvmtiCapabilities GetStandardCapabilities() {
+ return standard_caps;
}
-bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error) {
+void SetStandardCapabilities(jvmtiEnv* env) {
+ if (IsJVM()) {
+ // RI is more strict about adding capabilities at runtime then ART so just give it everything.
+ SetAllCapabilities(env);
+ return;
+ }
+ jvmtiCapabilities caps = GetStandardCapabilities();
+ CheckJvmtiError(env, env->AddCapabilities(&caps));
+}
+
+void SetAllCapabilities(jvmtiEnv* env) {
+ jvmtiCapabilities caps;
+ CheckJvmtiError(env, env->GetPotentialCapabilities(&caps));
+ CheckJvmtiError(env, env->AddCapabilities(&caps));
+}
+
+bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmtienv, jvmtiError error) {
if (error == JVMTI_ERROR_NONE) {
return false;
}
@@ -58,11 +118,11 @@
}
char* err;
- CheckJvmtiError(jvmti_env, jvmti_env->GetErrorName(error, &err));
+ CheckJvmtiError(jvmtienv, jvmtienv->GetErrorName(error, &err));
env->ThrowNew(rt_exception.get(), err);
- Deallocate(jvmti_env, err);
+ Deallocate(jvmtienv, err);
return true;
}
diff --git a/test/ti-agent/jvmti_helper.h b/test/ti-agent/jvmti_helper.h
index 66d88d0..a47a402 100644
--- a/test/ti-agent/jvmti_helper.h
+++ b/test/ti-agent/jvmti_helper.h
@@ -17,16 +17,24 @@
#ifndef ART_TEST_TI_AGENT_JVMTI_HELPER_H_
#define ART_TEST_TI_AGENT_JVMTI_HELPER_H_
-#include "jni.h"
-#include "jvmti.h"
#include <memory>
#include <ostream>
+#include "jni.h"
+#include "jvmti.h"
+
#include "android-base/logging.h"
namespace art {
+// Get a standard set of capabilities for use in tests.
+jvmtiCapabilities GetStandardCapabilities();
+
+// Add all the standard capabilities to the given env.
+void SetStandardCapabilities(jvmtiEnv* env);
+
// Add all capabilities to the given env.
+// TODO Remove this in the future.
void SetAllCapabilities(jvmtiEnv* env);
// Check whether the given error is NONE. If not, print out the corresponding error message
@@ -35,7 +43,7 @@
// Convert the given error to a RuntimeException with a message derived from the error. Returns
// true on error, false if error is JVMTI_ERROR_NONE.
-bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error);
+bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmtienv, jvmtiError error);
class JvmtiDeleter {
public:
diff --git a/test/ti-agent/locals_helper.cc b/test/ti-agent/locals_helper.cc
new file mode 100644
index 0000000..e284b52
--- /dev/null
+++ b/test/ti-agent/locals_helper.cc
@@ -0,0 +1,210 @@
+/*
+ * 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 "common_helper.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace common_locals {
+
+static void DeallocateContents(jvmtiLocalVariableEntry* vars, jint nvars) {
+ for (jint i = 0; i < nvars; i++) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars[i].name));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars[i].signature));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars[i].generic_signature));
+ }
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_EnableLocalVariableAccess(JNIEnv* env, jclass) {
+ jvmtiCapabilities caps;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetCapabilities(&caps))) {
+ return;
+ }
+ caps.can_access_local_variables = 1;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableObject(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jobject val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalObject(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableDouble(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jdouble val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalDouble(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableFloat(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jfloat val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalFloat(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableLong(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jlong val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalLong(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT void Java_art_Locals_SetLocalVariableInt(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot,
+ jint val) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetLocalInt(t, depth, slot, val));
+}
+
+extern "C" JNIEXPORT jdouble Java_art_Locals_GetLocalVariableDouble(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jdouble ret = 0;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalDouble(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jfloat Java_art_Locals_GetLocalVariableFloat(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jfloat ret = 0;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalFloat(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jlong Java_art_Locals_GetLocalVariableLong(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jlong ret = 0;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalLong(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jint Java_art_Locals_GetLocalVariableInt(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jint ret = 0;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalInt(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jobject Java_art_Locals_GetLocalInstance(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth) {
+ jobject ret = nullptr;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalInstance(t, depth, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jobject Java_art_Locals_GetLocalVariableObject(JNIEnv* env,
+ jclass,
+ jthread t,
+ jint depth,
+ jint slot) {
+ jobject ret = nullptr;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLocalObject(t, depth, slot, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray Java_art_Locals_GetLocalVariableTable(JNIEnv* env,
+ jclass,
+ jobject m) {
+ jmethodID method = env->FromReflectedMethod(m);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/Locals$VariableDescription"));
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jint nvars;
+ jvmtiLocalVariableEntry* vars = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetLocalVariableTable(method, &nvars, &vars))) {
+ return nullptr;
+ }
+ jobjectArray vars_array = env->NewObjectArray(nvars, klass.get(), nullptr);
+ if (env->ExceptionCheck()) {
+ DeallocateContents(vars, nvars);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars));
+ return nullptr;
+ }
+
+ jmethodID constructor = env->GetMethodID(
+ klass.get(), "<init>", "(JILjava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ for (jint i = 0; i < nvars; i++) {
+ ScopedLocalRef<jstring> name_string(env, env->NewStringUTF(vars[i].name));
+ ScopedLocalRef<jstring> sig_string(env, env->NewStringUTF(vars[i].signature));
+ ScopedLocalRef<jstring> generic_sig_string(env, env->NewStringUTF(vars[i].generic_signature));
+ jobject var_obj = env->NewObject(klass.get(),
+ constructor,
+ vars[i].start_location,
+ vars[i].length,
+ name_string.get(),
+ sig_string.get(),
+ generic_sig_string.get(),
+ vars[i].slot);
+ if (env->ExceptionCheck()) {
+ DeallocateContents(vars, nvars);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars));
+ return nullptr;
+ }
+ env->SetObjectArrayElement(vars_array, i, var_obj);
+ if (env->ExceptionCheck()) {
+ DeallocateContents(vars, nvars);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars));
+ return nullptr;
+ }
+ }
+
+ DeallocateContents(vars, nvars);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(vars));
+ return vars_array;
+}
+
+} // namespace common_locals
+} // namespace art
diff --git a/test/ti-agent/redefinition_helper.cc b/test/ti-agent/redefinition_helper.cc
index 3b18879..76371de 100644
--- a/test/ti-agent/redefinition_helper.cc
+++ b/test/ti-agent/redefinition_helper.cc
@@ -16,9 +16,9 @@
#include "common_helper.h"
+#include <cstdio>
#include <deque>
#include <map>
-#include <stdio.h>
#include <sstream>
#include <string>
#include <vector>
@@ -332,7 +332,7 @@
"Unable to create temporary jvmtiEnv for RetransformClasses call.");
return;
}
- SetAllCapabilities(real_env);
+ SetStandardCapabilities(real_env);
} else {
real_env = jvmti_env;
}
@@ -373,15 +373,14 @@
} // namespace common_transform
static void SetupCommonRedefine() {
- jvmtiCapabilities caps;
- jvmti_env->GetPotentialCapabilities(&caps);
+ jvmtiCapabilities caps = GetStandardCapabilities();
caps.can_retransform_classes = 0;
caps.can_retransform_any_class = 0;
jvmti_env->AddCapabilities(&caps);
}
static void SetupCommonRetransform() {
- SetAllCapabilities(jvmti_env);
+ SetStandardCapabilities(jvmti_env);
jvmtiEventCallbacks cb;
memset(&cb, 0, sizeof(cb));
cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
@@ -392,8 +391,7 @@
static void SetupCommonTransform() {
// Don't set the retransform caps
- jvmtiCapabilities caps;
- jvmti_env->GetPotentialCapabilities(&caps);
+ jvmtiCapabilities caps = GetStandardCapabilities();
caps.can_retransform_classes = 0;
caps.can_retransform_any_class = 0;
jvmti_env->AddCapabilities(&caps);
diff --git a/test/ti-agent/stack_trace_helper.cc b/test/ti-agent/stack_trace_helper.cc
new file mode 100644
index 0000000..f2a8e9a
--- /dev/null
+++ b/test/ti-agent/stack_trace_helper.cc
@@ -0,0 +1,99 @@
+
+/*
+ * 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 "common_helper.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace common_stack_trace {
+
+extern "C" JNIEXPORT jint JNICALL Java_art_StackTrace_GetStackDepth(
+ JNIEnv* env, jclass, jthread thr) {
+ jint ret;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetFrameCount(thr, &ret));
+ return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray Java_art_StackTrace_nativeGetStackTrace(JNIEnv* env,
+ jclass,
+ jthread thr) {
+ jint depth;
+ ScopedLocalRef<jclass> klass(env, env->FindClass("art/StackTrace$StackFrameData"));
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jmethodID constructor = env->GetMethodID(
+ klass.get(), "<init>", "(Ljava/lang/Thread;Ljava/lang/reflect/Executable;JI)V");
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetFrameCount(thr, &depth))) {
+ return nullptr;
+ }
+ // Just give some extra space.
+ depth += 10;
+ jvmtiFrameInfo* frames;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->Allocate(depth * sizeof(jvmtiFrameInfo),
+ reinterpret_cast<unsigned char**>(&frames)))) {
+ return nullptr;
+ }
+ jint nframes = 0;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetStackTrace(thr, 0, depth, frames, &nframes))) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ jobjectArray frames_array = env->NewObjectArray(nframes, klass.get(), nullptr);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ for (jint i = 0; i < nframes; i++) {
+ jobject jmethod = GetJavaMethod(jvmti_env, env, frames[i].method);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ jobject frame_obj = env->NewObject(klass.get(),
+ constructor,
+ thr,
+ jmethod,
+ frames[i].location,
+ i);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ env->SetObjectArrayElement(frames_array, i, frame_obj);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return nullptr;
+ }
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(frames));
+ return frames_array;
+}
+
+} // namespace common_stack_trace
+} // namespace art
diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc
index 7a9d1e0..1f8ceff 100644
--- a/test/ti-agent/trace_helper.cc
+++ b/test/ti-agent/trace_helper.cc
@@ -388,10 +388,12 @@
data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
data->in_callback = false;
- void* old_data = nullptr;
- if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ TraceData* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetEnvironmentLocalStorage(
+ reinterpret_cast<void**>(&old_data)))) {
return;
- } else if (old_data != nullptr) {
+ } else if (old_data != nullptr && old_data->test_klass != nullptr) {
ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
return;
@@ -455,6 +457,21 @@
extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ // If data is null then we haven't ever enabled tracing so we don't need to do anything.
+ if (data == nullptr || data->test_klass == nullptr) {
+ return;
+ }
+ env->DeleteGlobalRef(data->test_klass);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ // Clear test_klass so we know this isn't being used
+ data->test_klass = nullptr;
if (JvmtiErrorToException(env, jvmti_env,
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
JVMTI_EVENT_FIELD_ACCESS,
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index d197acd..06d5af0 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#include <jni.h>
-#include <stdio.h>
-#include <iostream>
-#include <iomanip>
+#include <cstdio>
#include <fstream>
+#include <iomanip>
+#include <iostream>
#include <memory>
-#include <stdio.h>
#include <sstream>
#include <strstream>
-#include "jvmti.h"
+#include <jni.h>
+
#include "exec_utils.h"
+#include "jvmti.h"
#include "utils.h"
namespace art {
@@ -780,8 +780,49 @@
}
// Just get all capabilities.
- jvmtiCapabilities caps;
- jvmti->GetPotentialCapabilities(&caps);
+ jvmtiCapabilities caps = {
+ .can_tag_objects = 0,
+ .can_generate_field_modification_events = 1,
+ .can_generate_field_access_events = 1,
+ .can_get_bytecodes = 0,
+ .can_get_synthetic_attribute = 0,
+ .can_get_owned_monitor_info = 0,
+ .can_get_current_contended_monitor = 0,
+ .can_get_monitor_info = 0,
+ .can_pop_frame = 0,
+ .can_redefine_classes = 1,
+ .can_signal_thread = 0,
+ .can_get_source_file_name = 1,
+ .can_get_line_numbers = 1,
+ .can_get_source_debug_extension = 0,
+ .can_access_local_variables = 0,
+ .can_maintain_original_method_order = 0,
+ .can_generate_single_step_events = 1,
+ .can_generate_exception_events = 0,
+ .can_generate_frame_pop_events = 0,
+ .can_generate_breakpoint_events = 0,
+ .can_suspend = 0,
+ .can_redefine_any_class = 0,
+ .can_get_current_thread_cpu_time = 0,
+ .can_get_thread_cpu_time = 0,
+ .can_generate_method_entry_events = 1,
+ .can_generate_method_exit_events = 1,
+ .can_generate_all_class_hook_events = 0,
+ .can_generate_compiled_method_load_events = 0,
+ .can_generate_monitor_events = 0,
+ .can_generate_vm_object_alloc_events = 0,
+ .can_generate_native_method_bind_events = 1,
+ .can_generate_garbage_collection_events = 0,
+ .can_generate_object_free_events = 0,
+ .can_force_early_return = 0,
+ .can_get_owned_monitor_stack_depth_info = 0,
+ .can_get_constant_pool = 0,
+ .can_set_native_method_prefix = 0,
+ .can_retransform_classes = 1,
+ .can_retransform_any_class = 0,
+ .can_generate_resource_exhaustion_heap_events = 0,
+ .can_generate_resource_exhaustion_threads_events = 0,
+ };
jvmti->AddCapabilities(&caps);
// Set callbacks.
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index 38556ab..4471c0a 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -75,7 +75,11 @@
* Instance.isRoot and Instance.getRootTypes.
Release History:
- 1.3 Pending
+ 1.4 Pending
+
+ 1.3 July 25, 2017
+ Improve diffing of static and instance fields.
+ Improve startup performance by roughly 25%.
1.2 May 26, 2017
Show registered native sizes of objects.
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index cc55b7a..8262910 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -110,7 +110,7 @@
private static void printClassInstanceFields(Doc doc, Query query, AhatClassInstance inst) {
doc.section("Fields");
AhatInstance base = inst.getBaseline();
- printFields(doc, query, INSTANCE_FIELDS_ID, !base.isPlaceHolder(),
+ printFields(doc, query, INSTANCE_FIELDS_ID, inst != base && !base.isPlaceHolder(),
inst.asClassInstance().getInstanceFields(),
base.isPlaceHolder() ? null : base.asClassInstance().getInstanceFields());
}
@@ -211,7 +211,7 @@
doc.section("Static Fields");
AhatInstance base = clsobj.getBaseline();
- printFields(doc, query, STATIC_FIELDS_ID, !base.isPlaceHolder(),
+ printFields(doc, query, STATIC_FIELDS_ID, clsobj != base && !base.isPlaceHolder(),
clsobj.getStaticFieldValues(),
base.isPlaceHolder() ? null : base.asClassObj().getStaticFieldValues());
}
diff --git a/tools/ahat/src/ObjectsHandler.java b/tools/ahat/src/ObjectsHandler.java
index 86d48f1..fd226c2 100644
--- a/tools/ahat/src/ObjectsHandler.java
+++ b/tools/ahat/src/ObjectsHandler.java
@@ -43,13 +43,7 @@
Site site = mSnapshot.getSite(id, depth);
List<AhatInstance> insts = new ArrayList<AhatInstance>();
- for (AhatInstance inst : site.getObjects()) {
- if ((heapName == null || inst.getHeap().getName().equals(heapName))
- && (className == null || inst.getClassName().equals(className))) {
- insts.add(inst);
- }
- }
-
+ site.getObjects(heapName, className, insts);
Collections.sort(insts, Sort.defaultInstanceCompare(mSnapshot));
doc.title("Objects");
diff --git a/tools/ahat/src/Summarizer.java b/tools/ahat/src/Summarizer.java
index 016eab4..3e9da31 100644
--- a/tools/ahat/src/Summarizer.java
+++ b/tools/ahat/src/Summarizer.java
@@ -60,14 +60,7 @@
formatted.append("root ");
}
- // Annotate classes as classes.
- DocString linkText = new DocString();
- if (inst.isClassObj()) {
- linkText.append("class ");
- }
-
- linkText.append(inst.toString());
-
+ DocString linkText = DocString.text(inst.toString());
if (inst.isPlaceHolder()) {
// Don't make links to placeholder objects.
formatted.append(linkText);
diff --git a/tools/ahat/src/dominators/DominatorsComputation.java b/tools/ahat/src/dominators/DominatorsComputation.java
new file mode 100644
index 0000000..9a2a272
--- /dev/null
+++ b/tools/ahat/src/dominators/DominatorsComputation.java
@@ -0,0 +1,260 @@
+/*
+ * 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 com.android.ahat.dominators;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * Generic DominatorsComputation.
+ *
+ * To use the dominators computation, have your graph nodes implement the
+ * DominatorsComputation.Node interface, then call
+ * DominatorsComputation.computeDominators on the single root node.
+ */
+public class DominatorsComputation {
+ /**
+ * Interface for a directed graph to perform the dominators computation on.
+ */
+ public interface Node {
+ /**
+ * Associate the given dominator state with this node.
+ */
+ void setDominatorsComputationState(Object state);
+
+ /**
+ * Get the most recent dominator state associated with this node using
+ * setDominatorsComputationState. If setDominatorsComputationState has not
+ * yet been called, this should return null.
+ */
+ Object getDominatorsComputationState();
+
+ /**
+ * Return a collection of nodes referenced from this node, for the
+ * purposes of computing dominators.
+ */
+ Iterable<? extends Node> getReferencesForDominators();
+
+ /**
+ * Update this node's dominator based on the results of the dominators
+ * computation.
+ */
+ void setDominator(Node dominator);
+ }
+
+ // NodeS is information associated with a particular node for the
+ // purposes of computing dominators.
+ // By convention we use the suffix 'S' to name instances of NodeS.
+ private static class NodeS {
+ // The node that this NodeS is associated with.
+ public Node node;
+
+ // Unique identifier for this node, in increasing order based on the order
+ // this node was visited in a depth first search from the root. In
+ // particular, given nodes A and B, if A.id > B.id, then A cannot be a
+ // dominator of B.
+ public long id;
+
+ // Upper bound on the id of this node's dominator.
+ // The true immediate dominator of this node must have id <= domid.
+ // This upper bound is slowly tightened as part of the dominators
+ // computation.
+ public long domid;
+
+ // The current candidate dominator for this node.
+ // Invariant: (domid < domS.id) implies this node is on the queue of
+ // nodes to be revisited.
+ public NodeS domS;
+
+ // A node with a reference to this node that is one step closer to the
+ // root than this node.
+ // Invariant: srcS.id < this.id
+ public NodeS srcS;
+
+ // The set of nodes X reachable by 'this' on a path of nodes from the
+ // root with increasing ids (possibly excluding X) that this node does not
+ // dominate (this.id > X.domid).
+ // We can use a List instead of a Set for this because we guarentee that
+ // we don't add the same node more than once to the list (see below).
+ public List<NodeS> undom = new ArrayList<NodeS>();
+
+ // The largest id of the node X for which we did X.undom.add(this).
+ // This is an optimization to avoid adding duplicate node entries to the
+ // undom set.
+ //
+ // The first time we see this node, we reach it through a path of nodes
+ // with IDs 0,...,a,this. These form our src chain to the root.
+ //
+ // The next time we see this node, we reach it through a path of nodes
+ // with IDS 0,...,b,c,...,d,this. Where all 0,...,b < a and all c,...,d > a.
+ //
+ // The next time we see this node, we reach it through a path of nodes
+ // with IDS 0,...,e,f,...,g,this. With all 0,...,e < d and all f,...,g > d.
+ // And so on.
+ //
+ // The first time we see this node, we set undomid to a.id. Nodes 0,...,a
+ // will be added as undom in the 'revisit' phase of the node.
+ //
+ // The next times we see this node, we mark a+,...,d as undom and
+ // change undomid to d. And so on.
+ public long undomid;
+ }
+
+ private static class Link {
+ public NodeS srcS;
+ public Node dst;
+
+ public Link(NodeS srcS, Node dst) {
+ this.srcS = srcS;
+ this.dst = dst;
+ }
+ }
+
+ /**
+ * Compute the dominator tree rooted at the given node.
+ * There must not be any incoming references to the root node.
+ */
+ public static void computeDominators(Node root) {
+ long id = 0;
+
+ // List of all nodes seen. We keep track of this here to update all the
+ // dominators once we are done.
+ List<NodeS> nodes = new ArrayList<NodeS>();
+
+ // The set of nodes N such that N.domid < N.domS.id. These nodes need
+ // to be revisisted because their dominator is clearly wrong.
+ // Use a Queue instead of a Set because performance will be better. We
+ // avoid adding nodes already on the queue by checking whether it was
+ // already true that N.domid < N.domS.id, in which case the node is
+ // already on the queue.
+ Queue<NodeS> revisit = new ArrayDeque<NodeS>();
+
+ // Set up the root node specially.
+ NodeS rootS = new NodeS();
+ rootS.node = root;
+ rootS.id = id++;
+ root.setDominatorsComputationState(rootS);
+
+ // 1. Do a depth first search of the nodes, label them with ids and come
+ // up with intial candidate dominators for them.
+ Deque<Link> dfs = new ArrayDeque<Link>();
+ for (Node child : root.getReferencesForDominators()) {
+ dfs.push(new Link(rootS, child));
+ }
+
+ while (!dfs.isEmpty()) {
+ Link link = dfs.pop();
+ NodeS dstS = (NodeS)link.dst.getDominatorsComputationState();
+ if (dstS == null) {
+ // This is the first time we have seen the node. The candidate
+ // dominator is link src.
+ dstS = new NodeS();
+ dstS.node = link.dst;
+ dstS.id = id++;
+ dstS.domid = link.srcS.id;
+ dstS.domS = link.srcS;
+ dstS.srcS = link.srcS;
+ dstS.undomid = dstS.domid;
+ nodes.add(dstS);
+ link.dst.setDominatorsComputationState(dstS);
+
+ for (Node child : link.dst.getReferencesForDominators()) {
+ dfs.push(new Link(dstS, child));
+ }
+ } else {
+ // We have seen the node already. Update the state based on the new
+ // potential dominator.
+ NodeS srcS = link.srcS;
+ boolean revisiting = dstS.domid < dstS.domS.id;
+
+ while (srcS.id > dstS.domid) {
+ if (srcS.id > dstS.undomid) {
+ srcS.undom.add(dstS);
+ }
+ srcS = srcS.srcS;
+ }
+ dstS.undomid = link.srcS.id;
+
+ if (srcS.id < dstS.domid) {
+ // In this case, dstS.domid must be wrong, because we just found a
+ // path to dstS that does not go through dstS.domid:
+ // All nodes from root to srcS have id < domid, and all nodes from
+ // srcS to dstS had id > domid, so dstS.domid cannot be on this path
+ // from root to dstS.
+ dstS.domid = srcS.id;
+ if (!revisiting) {
+ revisit.add(dstS);
+ }
+ }
+ }
+ }
+
+ // 2. Continue revisiting nodes until they all satisfy the requirement
+ // that domS.id <= domid.
+ while (!revisit.isEmpty()) {
+ NodeS nodeS = revisit.poll();
+ NodeS domS = nodeS.domS;
+ assert nodeS.domid < domS.id;
+ while (domS.id > nodeS.domid) {
+ if (domS.domS.id < nodeS.domid) {
+ // In this case, nodeS.domid must be wrong, because there is a path
+ // from root to nodeS that does not go through nodeS.domid:
+ // * We can go from root to domS without going through nodeS.domid,
+ // because otherwise nodeS.domid would dominate domS, not
+ // domS.domS.
+ // * We can go from domS to nodeS without going through nodeS.domid
+ // because we know nodeS is reachable from domS on a path of nodes
+ // with increases ids, which cannot include nodeS.domid, which
+ // has a smaller id than domS.
+ nodeS.domid = domS.domS.id;
+ }
+ domS.undom.add(nodeS);
+ domS = domS.srcS;
+ }
+ nodeS.domS = domS;
+ nodeS.domid = domS.id;
+
+ for (NodeS xS : nodeS.undom) {
+ if (domS.id < xS.domid) {
+ // In this case, xS.domid must be wrong, because there is a path
+ // from the root to xX that does not go through xS.domid:
+ // * We can go from root to nodeS without going through xS.domid,
+ // because otherwise xS.domid would dominate nodeS, not domS.
+ // * We can go from nodeS to xS without going through xS.domid
+ // because we know xS is reachable from nodeS on a path of nodes
+ // with increasing ids, which cannot include xS.domid, which has
+ // a smaller id than nodeS.
+ boolean revisiting = xS.domid < xS.domS.id;
+ xS.domid = domS.id;
+ if (!revisiting) {
+ revisit.add(xS);
+ }
+ }
+ }
+ }
+
+ // 3. Update the dominators of the nodes.
+ root.setDominatorsComputationState(null);
+ for (NodeS nodeS : nodes) {
+ nodeS.node.setDominator(nodeS.domS.node);
+ nodeS.node.setDominatorsComputationState(null);
+ }
+ }
+}
diff --git a/tools/ahat/src/heapdump/AhatArrayInstance.java b/tools/ahat/src/heapdump/AhatArrayInstance.java
index d88cf94..6d4485d 100644
--- a/tools/ahat/src/heapdump/AhatArrayInstance.java
+++ b/tools/ahat/src/heapdump/AhatArrayInstance.java
@@ -20,6 +20,7 @@
import com.android.tools.perflib.heap.Instance;
import java.nio.charset.StandardCharsets;
import java.util.AbstractList;
+import java.util.Collections;
import java.util.List;
public class AhatArrayInstance extends AhatInstance {
@@ -37,8 +38,8 @@
super(id);
}
- @Override void initialize(AhatSnapshot snapshot, Instance inst) {
- super.initialize(snapshot, inst);
+ @Override void initialize(AhatSnapshot snapshot, Instance inst, Site site) {
+ super.initialize(snapshot, inst, site);
ArrayInstance array = (ArrayInstance)inst;
switch (array.getArrayType()) {
@@ -49,10 +50,6 @@
if (objects[i] != null) {
Instance ref = (Instance)objects[i];
insts[i] = snapshot.findInstance(ref.getId());
- if (ref.getNextInstanceToGcRoot() == inst) {
- String field = "[" + Integer.toString(i) + "]";
- insts[i].setNextInstanceToGcRoot(this, field);
- }
}
}
mValues = new AbstractList<Value>() {
@@ -132,6 +129,35 @@
return mValues.get(index);
}
+ @Override
+ ReferenceIterator getReferences() {
+ // The list of references will be empty if this is a primitive array.
+ List<Reference> refs = Collections.emptyList();
+ if (!mValues.isEmpty()) {
+ Value first = mValues.get(0);
+ if (first == null || first.isAhatInstance()) {
+ refs = new AbstractList<Reference>() {
+ @Override
+ public int size() {
+ return mValues.size();
+ }
+
+ @Override
+ public Reference get(int index) {
+ Value value = mValues.get(index);
+ if (value != null) {
+ assert value.isAhatInstance();
+ String field = "[" + Integer.toString(index) + "]";
+ return new Reference(AhatArrayInstance.this, field, value.asAhatInstance(), true);
+ }
+ return null;
+ }
+ };
+ }
+ }
+ return new ReferenceIterator(refs);
+ }
+
@Override public boolean isArrayInstance() {
return true;
}
diff --git a/tools/ahat/src/heapdump/AhatClassInstance.java b/tools/ahat/src/heapdump/AhatClassInstance.java
index 158de52..2115923 100644
--- a/tools/ahat/src/heapdump/AhatClassInstance.java
+++ b/tools/ahat/src/heapdump/AhatClassInstance.java
@@ -19,6 +19,7 @@
import com.android.tools.perflib.heap.ClassInstance;
import com.android.tools.perflib.heap.Instance;
import java.awt.image.BufferedImage;
+import java.util.AbstractList;
import java.util.Arrays;
import java.util.List;
@@ -29,8 +30,8 @@
super(id);
}
- @Override void initialize(AhatSnapshot snapshot, Instance inst) {
- super.initialize(snapshot, inst);
+ @Override void initialize(AhatSnapshot snapshot, Instance inst, Site site) {
+ super.initialize(snapshot, inst, site);
ClassInstance classInst = (ClassInstance)inst;
List<ClassInstance.FieldValue> fieldValues = classInst.getValues();
@@ -40,15 +41,7 @@
String name = field.getField().getName();
String type = field.getField().getType().toString();
Value value = snapshot.getValue(field.getValue());
-
mFieldValues[i] = new FieldValue(name, type, value);
-
- if (field.getValue() instanceof Instance) {
- Instance ref = (Instance)field.getValue();
- if (ref.getNextInstanceToGcRoot() == inst) {
- value.asAhatInstance().setNextInstanceToGcRoot(this, "." + name);
- }
- }
}
}
@@ -101,6 +94,30 @@
return Arrays.asList(mFieldValues);
}
+ @Override
+ ReferenceIterator getReferences() {
+ List<Reference> refs = new AbstractList<Reference>() {
+ @Override
+ public int size() {
+ return mFieldValues.length;
+ }
+
+ @Override
+ public Reference get(int index) {
+ FieldValue field = mFieldValues[index];
+ Value value = field.value;
+ if (value != null && value.isAhatInstance()) {
+ boolean strong = !field.name.equals("referent")
+ || !isInstanceOfClass("java.lang.ref.Reference");
+ AhatInstance ref = value.asAhatInstance();
+ return new Reference(AhatClassInstance.this, "." + field.name, ref, strong);
+ }
+ return null;
+ }
+ };
+ return new ReferenceIterator(refs);
+ }
+
/**
* Returns true if this is an instance of a class with the given name.
*/
diff --git a/tools/ahat/src/heapdump/AhatClassObj.java b/tools/ahat/src/heapdump/AhatClassObj.java
index c5ade1d..052d7a8 100644
--- a/tools/ahat/src/heapdump/AhatClassObj.java
+++ b/tools/ahat/src/heapdump/AhatClassObj.java
@@ -19,6 +19,7 @@
import com.android.tools.perflib.heap.ClassObj;
import com.android.tools.perflib.heap.Field;
import com.android.tools.perflib.heap.Instance;
+import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -34,8 +35,8 @@
super(id);
}
- @Override void initialize(AhatSnapshot snapshot, Instance inst) {
- super.initialize(snapshot, inst);
+ @Override void initialize(AhatSnapshot snapshot, Instance inst, Site site) {
+ super.initialize(snapshot, inst, site);
ClassObj classObj = (ClassObj)inst;
mClassName = classObj.getClassName();
@@ -58,13 +59,6 @@
String type = field.getKey().getType().toString();
Value value = snapshot.getValue(field.getValue());
mStaticFieldValues[index++] = new FieldValue(name, type, value);
-
- if (field.getValue() instanceof Instance) {
- Instance ref = (Instance)field.getValue();
- if (ref.getNextInstanceToGcRoot() == inst) {
- value.asAhatInstance().setNextInstanceToGcRoot(this, "." + name);
- }
- }
}
}
@@ -96,6 +90,27 @@
return Arrays.asList(mStaticFieldValues);
}
+ @Override
+ ReferenceIterator getReferences() {
+ List<Reference> refs = new AbstractList<Reference>() {
+ @Override
+ public int size() {
+ return mStaticFieldValues.length;
+ }
+
+ @Override
+ public Reference get(int index) {
+ FieldValue field = mStaticFieldValues[index];
+ Value value = field.value;
+ if (value != null && value.isAhatInstance()) {
+ return new Reference(AhatClassObj.this, "." + field.name, value.asAhatInstance(), true);
+ }
+ return null;
+ }
+ };
+ return new ReferenceIterator(refs);
+ }
+
@Override public boolean isClassObj() {
return true;
}
@@ -105,11 +120,10 @@
}
@Override public String toString() {
- return mClassName;
+ return "class " + mClassName;
}
@Override AhatInstance newPlaceHolderInstance() {
return new AhatPlaceHolderClassObj(this);
}
}
-
diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java
index af369d9..8905b76 100644
--- a/tools/ahat/src/heapdump/AhatInstance.java
+++ b/tools/ahat/src/heapdump/AhatInstance.java
@@ -16,39 +16,48 @@
package com.android.ahat.heapdump;
+import com.android.ahat.dominators.DominatorsComputation;
import com.android.tools.perflib.heap.ClassObj;
import com.android.tools.perflib.heap.Instance;
-import com.android.tools.perflib.heap.RootObj;
import java.awt.image.BufferedImage;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
+import java.util.Queue;
-public abstract class AhatInstance implements Diffable<AhatInstance> {
- private long mId;
+public abstract class AhatInstance implements Diffable<AhatInstance>,
+ DominatorsComputation.Node {
+ // The id of this instance from the heap dump.
+ private final long mId;
+
+ // Fields initialized in initialize().
private Size mSize;
- private Size[] mRetainedSizes; // Retained size indexed by heap index
- private boolean mIsReachable;
private AhatHeap mHeap;
- private AhatInstance mImmediateDominator;
- private AhatInstance mNextInstanceToGcRoot;
- private String mNextInstanceToGcRootField = "???";
private AhatClassObj mClassObj;
- private AhatInstance[] mHardReverseReferences;
- private AhatInstance[] mSoftReverseReferences;
private Site mSite;
// If this instance is a root, mRootTypes contains a set of the root types.
// If this instance is not a root, mRootTypes is null.
private List<String> mRootTypes;
- // List of instances this instance immediately dominates.
- private List<AhatInstance> mDominated = new ArrayList<AhatInstance>();
+ // Fields initialized in computeReverseReferences().
+ private AhatInstance mNextInstanceToGcRoot;
+ private String mNextInstanceToGcRootField;
+ private ArrayList<AhatInstance> mHardReverseReferences;
+ private ArrayList<AhatInstance> mSoftReverseReferences;
+ // Fields initialized in DominatorsComputation.computeDominators().
+ // mDominated - the list of instances immediately dominated by this instance.
+ // mRetainedSizes - retained size indexed by heap index.
+ private AhatInstance mImmediateDominator;
+ private List<AhatInstance> mDominated = new ArrayList<AhatInstance>();
+ private Size[] mRetainedSizes;
+ private Object mDominatorsComputationState;
+
+ // The baseline instance for purposes of diff.
private AhatInstance mBaseline;
public AhatInstance(long id) {
@@ -62,58 +71,16 @@
* There is no guarantee that the AhatInstances returned by
* snapshot.findInstance have been initialized yet.
*/
- void initialize(AhatSnapshot snapshot, Instance inst) {
- mId = inst.getId();
+ void initialize(AhatSnapshot snapshot, Instance inst, Site site) {
mSize = new Size(inst.getSize(), 0);
- mIsReachable = inst.isReachable();
-
- List<AhatHeap> heaps = snapshot.getHeaps();
-
mHeap = snapshot.getHeap(inst.getHeap().getName());
- Instance dom = inst.getImmediateDominator();
- if (dom == null || dom instanceof RootObj) {
- mImmediateDominator = null;
- } else {
- mImmediateDominator = snapshot.findInstance(dom.getId());
- mImmediateDominator.mDominated.add(this);
- }
-
ClassObj clsObj = inst.getClassObj();
if (clsObj != null) {
mClassObj = snapshot.findClassObj(clsObj.getId());
}
- // A couple notes about reverse references:
- // * perflib sometimes returns unreachable reverse references. If
- // snapshot.findInstance returns null, it means the reverse reference is
- // not reachable, so we filter it out.
- // * We store the references as AhatInstance[] instead of
- // ArrayList<AhatInstance> because it saves a lot of space and helps
- // with performance when there are a lot of AhatInstances.
- ArrayList<AhatInstance> ahatRefs = new ArrayList<AhatInstance>();
- ahatRefs = new ArrayList<AhatInstance>();
- for (Instance ref : inst.getHardReverseReferences()) {
- AhatInstance ahat = snapshot.findInstance(ref.getId());
- if (ahat != null) {
- ahatRefs.add(ahat);
- }
- }
- mHardReverseReferences = new AhatInstance[ahatRefs.size()];
- ahatRefs.toArray(mHardReverseReferences);
-
- List<Instance> refs = inst.getSoftReverseReferences();
- ahatRefs.clear();
- if (refs != null) {
- for (Instance ref : refs) {
- AhatInstance ahat = snapshot.findInstance(ref.getId());
- if (ahat != null) {
- ahatRefs.add(ahat);
- }
- }
- }
- mSoftReverseReferences = new AhatInstance[ahatRefs.size()];
- ahatRefs.toArray(mSoftReverseReferences);
+ mSite = site;
}
/**
@@ -166,7 +133,7 @@
* Returns whether this object is strongly-reachable.
*/
public boolean isReachable() {
- return mIsReachable;
+ return mImmediateDominator != null;
}
/**
@@ -177,6 +144,12 @@
}
/**
+ * Returns an iterator over the references this AhatInstance has to other
+ * AhatInstances.
+ */
+ abstract ReferenceIterator getReferences();
+
+ /**
* Returns true if this instance is marked as a root instance.
*/
public boolean isRoot() {
@@ -227,13 +200,6 @@
}
/**
- * Sets the allocation site of this instance.
- */
- void setSite(Site site) {
- mSite = site;
- }
-
- /**
* Returns true if the given instance is a class object
*/
public boolean isClassObj() {
@@ -311,14 +277,20 @@
* Returns a list of objects with hard references to this object.
*/
public List<AhatInstance> getHardReverseReferences() {
- return Arrays.asList(mHardReverseReferences);
+ if (mHardReverseReferences != null) {
+ return mHardReverseReferences;
+ }
+ return Collections.emptyList();
}
/**
* Returns a list of objects with soft references to this object.
*/
public List<AhatInstance> getSoftReverseReferences() {
- return Arrays.asList(mSoftReverseReferences);
+ if (mSoftReverseReferences != null) {
+ return mSoftReverseReferences;
+ }
+ return Collections.emptyList();
}
/**
@@ -425,8 +397,10 @@
}
void setNextInstanceToGcRoot(AhatInstance inst, String field) {
- mNextInstanceToGcRoot = inst;
- mNextInstanceToGcRootField = field;
+ if (mNextInstanceToGcRoot == null && !isRoot()) {
+ mNextInstanceToGcRoot = inst;
+ mNextInstanceToGcRootField = field;
+ }
}
/** Returns a human-readable identifier for this object.
@@ -466,6 +440,47 @@
}
/**
+ * Initialize the reverse reference fields of this instance and all other
+ * instances reachable from it. Initializes the following fields:
+ * mNextInstanceToGcRoot
+ * mNextInstanceToGcRootField
+ * mHardReverseReferences
+ * mSoftReverseReferences
+ */
+ static void computeReverseReferences(AhatInstance root) {
+ // Do a breadth first search to visit the nodes.
+ Queue<Reference> bfs = new ArrayDeque<Reference>();
+ for (Reference ref : root.getReferences()) {
+ bfs.add(ref);
+ }
+ while (!bfs.isEmpty()) {
+ Reference ref = bfs.poll();
+
+ if (ref.ref.mHardReverseReferences == null) {
+ // This is the first time we are seeing ref.ref.
+ ref.ref.mNextInstanceToGcRoot = ref.src;
+ ref.ref.mNextInstanceToGcRootField = ref.field;
+ ref.ref.mHardReverseReferences = new ArrayList<AhatInstance>();
+ for (Reference childRef : ref.ref.getReferences()) {
+ bfs.add(childRef);
+ }
+ }
+
+ // Note: ref.src is null when the src is the SuperRoot.
+ if (ref.src != null) {
+ if (ref.strong) {
+ ref.ref.mHardReverseReferences.add(ref.src);
+ } else {
+ if (ref.ref.mSoftReverseReferences == null) {
+ ref.ref.mSoftReverseReferences = new ArrayList<AhatInstance>();
+ }
+ ref.ref.mSoftReverseReferences.add(ref.src);
+ }
+ }
+ }
+ }
+
+ /**
* Recursively compute the retained size of the given instance and all
* other instances it dominates.
*/
@@ -486,8 +501,10 @@
for (int i = 0; i < numHeaps; i++) {
inst.mRetainedSizes[i] = Size.ZERO;
}
- inst.mRetainedSizes[inst.mHeap.getIndex()] =
- inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.mSize);
+ if (!(inst instanceof SuperRoot)) {
+ inst.mRetainedSizes[inst.mHeap.getIndex()] =
+ inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.mSize);
+ }
deque.push(inst);
for (AhatInstance dominated : inst.mDominated) {
deque.push(dominated);
@@ -501,4 +518,25 @@
}
}
}
+
+ @Override
+ public void setDominatorsComputationState(Object state) {
+ mDominatorsComputationState = state;
+ }
+
+ @Override
+ public Object getDominatorsComputationState() {
+ return mDominatorsComputationState;
+ }
+
+ @Override
+ public Iterable<? extends DominatorsComputation.Node> getReferencesForDominators() {
+ return new DominatorReferenceIterator(getReferences());
+ }
+
+ @Override
+ public void setDominator(DominatorsComputation.Node dominator) {
+ mImmediateDominator = (AhatInstance)dominator;
+ mImmediateDominator.mDominated.add(this);
+ }
}
diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
index 4aac804..d797b11 100644
--- a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
+++ b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
@@ -16,6 +16,9 @@
package com.android.ahat.heapdump;
+import java.util.Collections;
+import java.util.List;
+
/**
* Generic PlaceHolder instance to take the place of a real AhatInstance for
* the purposes of displaying diffs.
@@ -60,4 +63,10 @@
@Override public boolean isPlaceHolder() {
return true;
}
+
+ @Override
+ ReferenceIterator getReferences() {
+ List<Reference> refs = Collections.emptyList();
+ return new ReferenceIterator(refs);
+ }
}
diff --git a/tools/ahat/src/heapdump/AhatSnapshot.java b/tools/ahat/src/heapdump/AhatSnapshot.java
index 35d6c8a..7df78c5 100644
--- a/tools/ahat/src/heapdump/AhatSnapshot.java
+++ b/tools/ahat/src/heapdump/AhatSnapshot.java
@@ -16,6 +16,7 @@
package com.android.ahat.heapdump;
+import com.android.ahat.dominators.DominatorsComputation;
import com.android.tools.perflib.captures.DataBuffer;
import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
import com.android.tools.perflib.heap.ArrayInstance;
@@ -42,7 +43,7 @@
private final Site mRootSite = new Site("ROOT");
// Collection of objects whose immediate dominator is the SENTINEL_ROOT.
- private final List<AhatInstance> mRooted = new ArrayList<AhatInstance>();
+ private final List<AhatInstance> mRooted;
// List of all ahat instances stored in increasing order by id.
private final List<AhatInstance> mInstances = new ArrayList<AhatInstance>();
@@ -80,7 +81,6 @@
*/
private AhatSnapshot(DataBuffer buffer, ProguardMap map) throws IOException {
Snapshot snapshot = Snapshot.createSnapshot(buffer, map);
- snapshot.computeDominators();
// Properly label the class of class objects in the perflib snapshot.
final ClassObj javaLangClass = snapshot.findClass("java.lang.Class");
@@ -139,46 +139,45 @@
// and instances.
for (AhatInstance ahat : mInstances) {
Instance inst = snapshot.findInstance(ahat.getId());
- ahat.initialize(this, inst);
- Long registeredNativeSize = registeredNative.get(inst);
- if (registeredNativeSize != null) {
- ahat.addRegisteredNativeSize(registeredNativeSize);
- }
-
- if (inst.getImmediateDominator() == Snapshot.SENTINEL_ROOT) {
- mRooted.add(ahat);
- }
-
- if (inst.isReachable()) {
- ahat.getHeap().addToSize(ahat.getSize());
- }
-
- // Update sites.
StackFrame[] frames = null;
StackTrace stack = inst.getStack();
if (stack != null) {
frames = stack.getFrames();
}
Site site = mRootSite.add(frames, frames == null ? 0 : frames.length, ahat);
- ahat.setSite(site);
+ ahat.initialize(this, inst, site);
+
+ Long registeredNativeSize = registeredNative.get(inst);
+ if (registeredNativeSize != null) {
+ ahat.addRegisteredNativeSize(registeredNativeSize);
+ }
}
// Record the roots and their types.
+ SuperRoot superRoot = new SuperRoot();
for (RootObj root : snapshot.getGCRoots()) {
Instance inst = root.getReferredInstance();
if (inst != null) {
- findInstance(inst.getId()).addRootType(root.getRootType().toString());
+ AhatInstance ahat = findInstance(inst.getId());
+ if (!ahat.isRoot()) {
+ superRoot.addRoot(ahat);
+ }
+ ahat.addRootType(root.getRootType().toString());
}
}
snapshot.dispose();
- // Compute the retained sizes of objects. We do this explicitly now rather
- // than relying on the retained sizes computed by perflib so that
- // registered native sizes are included.
- for (AhatInstance inst : mRooted) {
- AhatInstance.computeRetainedSize(inst, mHeaps.size());
+ AhatInstance.computeReverseReferences(superRoot);
+ DominatorsComputation.computeDominators(superRoot);
+ AhatInstance.computeRetainedSize(superRoot, mHeaps.size());
+
+ mRooted = superRoot.getDominated();
+ for (AhatHeap heap : mHeaps) {
+ heap.addToSize(superRoot.getRetainedSize(heap));
}
+
+ mRootSite.computeObjectsInfos(mHeaps.size());
}
/**
diff --git a/tools/ahat/src/heapdump/DominatorReferenceIterator.java b/tools/ahat/src/heapdump/DominatorReferenceIterator.java
new file mode 100644
index 0000000..ce2e6ef
--- /dev/null
+++ b/tools/ahat/src/heapdump/DominatorReferenceIterator.java
@@ -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.
+ */
+
+package com.android.ahat.heapdump;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Reference iterator used for the dominators computation.
+ * This visits only strong references.
+ */
+class DominatorReferenceIterator implements Iterator<AhatInstance>,
+ Iterable<AhatInstance> {
+ private ReferenceIterator mIter;
+ private AhatInstance mNext;
+
+ public DominatorReferenceIterator(ReferenceIterator iter) {
+ mIter = iter;
+ mNext = null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ while (mNext == null && mIter.hasNext()) {
+ Reference ref = mIter.next();
+ if (ref.strong) {
+ mNext = ref.ref;
+ }
+ }
+ return mNext != null;
+ }
+
+ @Override
+ public AhatInstance next() {
+ if (hasNext()) {
+ AhatInstance next = mNext;
+ mNext = null;
+ return next;
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public Iterator<AhatInstance> iterator() {
+ return this;
+ }
+}
diff --git a/tools/ahat/src/heapdump/Reference.java b/tools/ahat/src/heapdump/Reference.java
new file mode 100644
index 0000000..980f278
--- /dev/null
+++ b/tools/ahat/src/heapdump/Reference.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.android.ahat.heapdump;
+
+/**
+ * Reference represents a reference from 'src' to 'ref' through 'field'.
+ * Field is a string description for human consumption. This is typically
+ * either "." followed by the field name or an array subscript such as "[4]".
+ * 'strong' is true if this is a strong reference, false if it is a
+ * weak/soft/other reference.
+ */
+public class Reference {
+ public final AhatInstance src;
+ public final String field;
+ public final AhatInstance ref;
+ public final boolean strong;
+
+ public Reference(AhatInstance src, String field, AhatInstance ref, boolean strong) {
+ this.src = src;
+ this.field = field;
+ this.ref = ref;
+ this.strong = strong;
+ }
+}
diff --git a/tools/ahat/src/heapdump/ReferenceIterator.java b/tools/ahat/src/heapdump/ReferenceIterator.java
new file mode 100644
index 0000000..a707fb2
--- /dev/null
+++ b/tools/ahat/src/heapdump/ReferenceIterator.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.android.ahat.heapdump;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+class ReferenceIterator implements Iterator<Reference>,
+ Iterable<Reference> {
+ private List<Reference> mRefs;
+ private int mLength;
+ private int mNextIndex;
+ private Reference mNext;
+
+ /**
+ * Construct a ReferenceIterator that iterators over the given list of
+ * references. Elements of the given list of references may be null, in
+ * which case the ReferenceIterator will skip over them.
+ */
+ public ReferenceIterator(List<Reference> refs) {
+ mRefs = refs;
+ mLength = refs.size();
+ mNextIndex = 0;
+ mNext = null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ while (mNext == null && mNextIndex < mLength) {
+ mNext = mRefs.get(mNextIndex);
+ mNextIndex++;
+ }
+ return mNext != null;
+ }
+
+ @Override
+ public Reference next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ Reference next = mNext;
+ mNext = null;
+ return next;
+ }
+
+ @Override
+ public Iterator<Reference> iterator() {
+ return this;
+ }
+}
diff --git a/tools/ahat/src/heapdump/Site.java b/tools/ahat/src/heapdump/Site.java
index fdd4eea..f0fc5d2 100644
--- a/tools/ahat/src/heapdump/Site.java
+++ b/tools/ahat/src/heapdump/Site.java
@@ -42,15 +42,15 @@
private int mDepth;
// The total size of objects allocated in this site (including child sites),
- // organized by heap index. Heap indices outside the range of mSizesByHeap
- // implicitly have size 0.
+ // organized by heap index. Computed as part of computeObjectsInfos.
private Size[] mSizesByHeap;
// List of child sites.
private List<Site> mChildren;
- // List of all objects allocated in this site (including child sites).
+ // List of objects allocated at this site (not including child sites).
private List<AhatInstance> mObjects;
+
private List<ObjectsInfo> mObjectsInfos;
private Map<AhatHeap, Map<AhatClassObj, ObjectsInfo>> mObjectsInfoMap;
@@ -111,7 +111,6 @@
mLineNumber = line;
mId = id;
mDepth = depth;
- mSizesByHeap = new Size[0];
mChildren = new ArrayList<Site>();
mObjects = new ArrayList<AhatInstance>();
mObjectsInfos = new ArrayList<ObjectsInfo>();
@@ -130,67 +129,102 @@
}
private static Site add(Site site, StackFrame[] frames, int depth, AhatInstance inst) {
- while (true) {
- site.mObjects.add(inst);
+ while (depth > 0) {
+ StackFrame next = frames[depth - 1];
+ Site child = null;
+ for (int i = 0; i < site.mChildren.size(); i++) {
+ Site curr = site.mChildren.get(i);
+ if (curr.mLineNumber == next.getLineNumber()
+ && curr.mMethodName.equals(next.getMethodName())
+ && curr.mSignature.equals(next.getSignature())
+ && curr.mFilename.equals(next.getFilename())) {
+ child = curr;
+ break;
+ }
+ }
+ if (child == null) {
+ child = new Site(site, next.getMethodName(), next.getSignature(),
+ next.getFilename(), next.getLineNumber(), inst.getId(), depth - 1);
+ site.mChildren.add(child);
+ }
+ depth = depth - 1;
+ site = child;
+ }
+ site.mObjects.add(inst);
+ return site;
+ }
- ObjectsInfo info = site.getObjectsInfo(inst.getHeap(), inst.getClassObj());
+ /**
+ * Recompute the ObjectsInfos for this and all child sites.
+ * This should be done after the sites tree has been formed. It should also
+ * be done after dominators computation has been performed to ensure only
+ * reachable objects are included in the ObjectsInfos.
+ *
+ * @param numHeaps - The number of heaps in the heap dump.
+ */
+ void computeObjectsInfos(int numHeaps) {
+ // Count up the total sizes by heap.
+ mSizesByHeap = new Size[numHeaps];
+ for (int i = 0; i < numHeaps; ++i) {
+ mSizesByHeap[i] = Size.ZERO;
+ }
+
+ // Add all reachable objects allocated at this site.
+ for (AhatInstance inst : mObjects) {
if (inst.isReachable()) {
AhatHeap heap = inst.getHeap();
- if (heap.getIndex() >= site.mSizesByHeap.length) {
- Size[] newSizes = new Size[heap.getIndex() + 1];
- for (int i = 0; i < site.mSizesByHeap.length; i++) {
- newSizes[i] = site.mSizesByHeap[i];
- }
- for (int i = site.mSizesByHeap.length; i < heap.getIndex() + 1; i++) {
- newSizes[i] = Size.ZERO;
- }
- site.mSizesByHeap = newSizes;
- }
- site.mSizesByHeap[heap.getIndex()]
- = site.mSizesByHeap[heap.getIndex()].plus(inst.getSize());
-
+ Size size = inst.getSize();
+ ObjectsInfo info = getObjectsInfo(heap, inst.getClassObj());
info.numInstances++;
- info.numBytes = info.numBytes.plus(inst.getSize());
+ info.numBytes = info.numBytes.plus(size);
+ mSizesByHeap[heap.getIndex()] = mSizesByHeap[heap.getIndex()].plus(size);
}
+ }
- if (depth > 0) {
- StackFrame next = frames[depth - 1];
- Site child = null;
- for (int i = 0; i < site.mChildren.size(); i++) {
- Site curr = site.mChildren.get(i);
- if (curr.mLineNumber == next.getLineNumber()
- && curr.mMethodName.equals(next.getMethodName())
- && curr.mSignature.equals(next.getSignature())
- && curr.mFilename.equals(next.getFilename())) {
- child = curr;
- break;
- }
- }
- if (child == null) {
- child = new Site(site, next.getMethodName(), next.getSignature(),
- next.getFilename(), next.getLineNumber(), inst.getId(), depth - 1);
- site.mChildren.add(child);
- }
- depth = depth - 1;
- site = child;
- } else {
- return site;
+ // Add objects allocated in child sites.
+ for (Site child : mChildren) {
+ child.computeObjectsInfos(numHeaps);
+ for (ObjectsInfo childInfo : child.mObjectsInfos) {
+ ObjectsInfo info = getObjectsInfo(childInfo.heap, childInfo.classObj);
+ info.numInstances += childInfo.numInstances;
+ info.numBytes = info.numBytes.plus(childInfo.numBytes);
+ }
+ for (int i = 0; i < numHeaps; ++i) {
+ mSizesByHeap[i] = mSizesByHeap[i].plus(child.mSizesByHeap[i]);
}
}
}
// Get the size of a site for a specific heap.
public Size getSize(AhatHeap heap) {
- int index = heap.getIndex();
- return index >= 0 && index < mSizesByHeap.length ? mSizesByHeap[index] : Size.ZERO;
+ return mSizesByHeap[heap.getIndex()];
}
/**
- * Get the list of objects allocated under this site. Includes objects
- * allocated in children sites.
+ * Collect the objects allocated under this site, optionally filtered by
+ * heap name or class name. Includes objects allocated in children sites.
+ * @param heapName - The name of the heap the collected objects should
+ * belong to. This may be null to indicate objects of
+ * every heap should be collected.
+ * @param className - The name of the class the collected objects should
+ * belong to. This may be null to indicate objects of
+ * every class should be collected.
+ * @param objects - Out parameter. A collection of objects that all
+ * collected objects should be added to.
*/
- public Collection<AhatInstance> getObjects() {
- return mObjects;
+ public void getObjects(String heapName, String className, Collection<AhatInstance> objects) {
+ for (AhatInstance inst : mObjects) {
+ if ((heapName == null || inst.getHeap().getName().equals(heapName))
+ && (className == null || inst.getClassName().equals(className))) {
+ objects.add(inst);
+ }
+ }
+
+ // Recursively visit children. Recursion should be okay here because the
+ // stack depth is limited by a reasonable amount (128 frames or so).
+ for (Site child : mChildren) {
+ child.getObjects(heapName, className, objects);
+ }
}
/**
@@ -220,8 +254,8 @@
// Get the combined size of the site for all heaps.
public Size getTotalSize() {
Size total = Size.ZERO;
- for (int i = 0; i < mSizesByHeap.length; i++) {
- total = total.plus(mSizesByHeap[i]);
+ for (Size size : mSizesByHeap) {
+ total = total.plus(size);
}
return total;
}
diff --git a/tools/ahat/src/heapdump/SuperRoot.java b/tools/ahat/src/heapdump/SuperRoot.java
new file mode 100644
index 0000000..54410cf
--- /dev/null
+++ b/tools/ahat/src/heapdump/SuperRoot.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.android.ahat.heapdump;
+
+import com.android.ahat.dominators.DominatorsComputation;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SuperRoot extends AhatInstance implements DominatorsComputation.Node {
+ private List<AhatInstance> mRoots = new ArrayList<AhatInstance>();
+ private Object mDominatorsComputationState;
+
+ public SuperRoot() {
+ super(0);
+ }
+
+ public void addRoot(AhatInstance root) {
+ mRoots.add(root);
+ }
+
+ @Override
+ public String toString() {
+ return "SUPER_ROOT";
+ }
+
+ @Override
+ ReferenceIterator getReferences() {
+ List<Reference> refs = new AbstractList<Reference>() {
+ @Override
+ public int size() {
+ return mRoots.size();
+ }
+
+ @Override
+ public Reference get(int index) {
+ String field = ".roots[" + Integer.toString(index) + "]";
+ return new Reference(null, field, mRoots.get(index), true);
+ }
+ };
+ return new ReferenceIterator(refs);
+ }
+}
diff --git a/tools/ahat/src/manifest.txt b/tools/ahat/src/manifest.txt
index c35ccf1..d893c5e 100644
--- a/tools/ahat/src/manifest.txt
+++ b/tools/ahat/src/manifest.txt
@@ -1,4 +1,4 @@
Name: ahat/
Implementation-Title: ahat
-Implementation-Version: 1.2
+Implementation-Version: 1.3
Main-Class: com.android.ahat.Main
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
index 3d3de78..13fd102 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/test-dump/Main.java
@@ -60,6 +60,14 @@
public StackSmasher child;
}
+ public static class Reference {
+ public Object referent;
+
+ public Reference(Object referent) {
+ this.referent = referent;
+ }
+ }
+
// We will take a heap dump that includes a single instance of this
// DumpedStuff class. Objects stored as fields in this class can be easily
// found in the hprof dump by searching for the instance of the DumpedStuff
@@ -71,6 +79,7 @@
public char[] charArray = "char thing".toCharArray();
public String nullString = null;
public Object anObject = new Object();
+ public Reference aReference = new Reference(anObject);
public ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
public PhantomReference aPhantomReference = new PhantomReference(anObject, referenceQueue);
public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue);
diff --git a/tools/ahat/test/DominatorsTest.java b/tools/ahat/test/DominatorsTest.java
new file mode 100644
index 0000000..0424e10
--- /dev/null
+++ b/tools/ahat/test/DominatorsTest.java
@@ -0,0 +1,298 @@
+/*
+ * 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 com.android.ahat;
+
+import com.android.ahat.dominators.DominatorsComputation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+public class DominatorsTest {
+ private static class Node implements DominatorsComputation.Node {
+ public String name;
+ public List<Node> depends = new ArrayList<Node>();
+ public Node dominator;
+ private Object dominatorsComputationState;
+
+ public Node(String name) {
+ this.name = name;
+ }
+
+ public void computeDominators() {
+ DominatorsComputation.computeDominators(this);
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public void setDominatorsComputationState(Object state) {
+ dominatorsComputationState = state;
+ }
+
+ @Override
+ public Object getDominatorsComputationState() {
+ return dominatorsComputationState;
+ }
+
+ @Override
+ public Collection<Node> getReferencesForDominators() {
+ return depends;
+ }
+
+ @Override
+ public void setDominator(DominatorsComputation.Node dominator) {
+ this.dominator = (Node)dominator;
+ }
+ }
+
+ @Test
+ public void singleNode() {
+ // --> n
+ // Trivial case.
+ Node n = new Node("n");
+ n.computeDominators();
+ }
+
+ @Test
+ public void parentWithChild() {
+ // --> parent --> child
+ // The child node is dominated by the parent.
+ Node parent = new Node("parent");
+ Node child = new Node("child");
+ parent.depends = Arrays.asList(child);
+
+ parent.computeDominators();
+ assertEquals(parent, child.dominator);
+ }
+
+ @Test
+ public void reachableTwoWays() {
+ // /-> right -->\
+ // --> parent child
+ // \-> left --->/
+ // The child node can be reached either by right or by left.
+ Node parent = new Node("parent");
+ Node right = new Node("right");
+ Node left = new Node("left");
+ Node child = new Node("child");
+ parent.depends = Arrays.asList(left, right);
+ right.depends = Arrays.asList(child);
+ left.depends = Arrays.asList(child);
+
+ parent.computeDominators();
+ assertEquals(parent, left.dominator);
+ assertEquals(parent, right.dominator);
+ assertEquals(parent, child.dominator);
+ }
+
+ @Test
+ public void reachableDirectAndIndirect() {
+ // /-> right -->\
+ // --> parent -----------> child
+ // The child node can be reached either by right or parent.
+ Node parent = new Node("parent");
+ Node right = new Node("right");
+ Node child = new Node("child");
+ parent.depends = Arrays.asList(right, child);
+ right.depends = Arrays.asList(child);
+
+ parent.computeDominators();
+ assertEquals(parent, child.dominator);
+ assertEquals(parent, right.dominator);
+ }
+
+ @Test
+ public void subDominator() {
+ // --> parent --> middle --> child
+ // The child is dominated by an internal node.
+ Node parent = new Node("parent");
+ Node middle = new Node("middle");
+ Node child = new Node("child");
+ parent.depends = Arrays.asList(middle);
+ middle.depends = Arrays.asList(child);
+
+ parent.computeDominators();
+ assertEquals(parent, middle.dominator);
+ assertEquals(middle, child.dominator);
+ }
+
+ @Test
+ public void childSelfLoop() {
+ // --> parent --> child -\
+ // \<---/
+ // The child points back to itself.
+ Node parent = new Node("parent");
+ Node child = new Node("child");
+ parent.depends = Arrays.asList(child);
+ child.depends = Arrays.asList(child);
+
+ parent.computeDominators();
+ assertEquals(parent, child.dominator);
+ }
+
+ @Test
+ public void singleEntryLoop() {
+ // --> parent --> a --> b --> c -\
+ // \<------------/
+ // There is a loop in the graph, with only one way into the loop.
+ Node parent = new Node("parent");
+ Node a = new Node("a");
+ Node b = new Node("b");
+ Node c = new Node("c");
+ parent.depends = Arrays.asList(a);
+ a.depends = Arrays.asList(b);
+ b.depends = Arrays.asList(c);
+ c.depends = Arrays.asList(a);
+
+ parent.computeDominators();
+ assertEquals(parent, a.dominator);
+ assertEquals(a, b.dominator);
+ assertEquals(b, c.dominator);
+ }
+
+ @Test
+ public void multiEntryLoop() {
+ // --> parent --> right --> a --> b ----\
+ // \ \<-- c <---/
+ // \--> left --->--------/
+ // There is a loop in the graph, with two different ways to enter the
+ // loop.
+ Node parent = new Node("parent");
+ Node left = new Node("left");
+ Node right = new Node("right");
+ Node a = new Node("a");
+ Node b = new Node("b");
+ Node c = new Node("c");
+ parent.depends = Arrays.asList(left, right);
+ right.depends = Arrays.asList(a);
+ left.depends = Arrays.asList(c);
+ a.depends = Arrays.asList(b);
+ b.depends = Arrays.asList(c);
+ c.depends = Arrays.asList(a);
+
+ parent.computeDominators();
+ assertEquals(parent, right.dominator);
+ assertEquals(parent, left.dominator);
+ assertEquals(parent, a.dominator);
+ assertEquals(parent, c.dominator);
+ assertEquals(a, b.dominator);
+ }
+
+ @Test
+ public void dominatorOverwrite() {
+ // /---------> right <--\
+ // --> parent --> child <--/ /
+ // \---> left ---------/
+ // Test a strange case where we have had trouble in the past with a
+ // dominator getting improperly overwritten. The relevant features of this
+ // case are: 'child' is visited after 'right', 'child' is dominated by
+ // 'parent', and 'parent' revisits 'right' after visiting 'child'.
+ Node parent = new Node("parent");
+ Node right = new Node("right");
+ Node left = new Node("left");
+ Node child = new Node("child");
+ parent.depends = Arrays.asList(left, child, right);
+ left.depends = Arrays.asList(right);
+ right.depends = Arrays.asList(child);
+
+ parent.computeDominators();
+ assertEquals(parent, left.dominator);
+ assertEquals(parent, child.dominator);
+ assertEquals(parent, right.dominator);
+ }
+
+ @Test
+ public void stackOverflow() {
+ // --> a --> b --> ... --> N
+ // Verify we don't smash the stack for deep chains.
+ Node root = new Node("root");
+ Node curr = root;
+ for (int i = 0; i < 10000; ++i) {
+ Node node = new Node("n" + i);
+ curr.depends.add(node);
+ curr = node;
+ }
+
+ root.computeDominators();
+ }
+
+ @Test
+ public void hiddenRevisit() {
+ // /-> left ---->---------\
+ // --> parent \---> a --> b --> c
+ // \-> right -/
+ // Test a case we have had trouble in the past.
+ // When a's dominator is updated from left to parent, that should trigger
+ // all reachable children's dominators to be updated too. In particular,
+ // c's dominator should be updated, even though b's dominator is
+ // unchanged.
+ Node parent = new Node("parent");
+ Node right = new Node("right");
+ Node left = new Node("left");
+ Node a = new Node("a");
+ Node b = new Node("b");
+ Node c = new Node("c");
+ parent.depends = Arrays.asList(right, left);
+ left.depends = Arrays.asList(a, c);
+ right.depends = Arrays.asList(a);
+ a.depends = Arrays.asList(b);
+ b.depends = Arrays.asList(c);
+
+ parent.computeDominators();
+ assertEquals(parent, left.dominator);
+ assertEquals(parent, right.dominator);
+ assertEquals(parent, a.dominator);
+ assertEquals(parent, c.dominator);
+ assertEquals(a, b.dominator);
+ }
+
+ @Test
+ public void preUndominatedUpdate() {
+ // /--------->--------\
+ // / /---->----\
+ // --> p -> a --> b --> c --> d --> e
+ // \---------->----------/
+ // Test a case we have had trouble in the past.
+ // The candidate dominator for e is revised from d to a, then d is shown
+ // to be reachable from p. Make sure that causes e's dominator to be
+ // refined again from a to p. The extra nodes are there to ensure the
+ // necessary scheduling to expose the bug we had.
+ Node p = new Node("p");
+ Node a = new Node("a");
+ Node b = new Node("b");
+ Node c = new Node("c");
+ Node d = new Node("d");
+ Node e = new Node("e");
+ p.depends = Arrays.asList(d, a);
+ a.depends = Arrays.asList(e, b);
+ b.depends = Arrays.asList(d, c);
+ c.depends = Arrays.asList(d);
+ d.depends = Arrays.asList(e);
+
+ p.computeDominators();
+ assertEquals(p, a.dominator);
+ assertEquals(a, b.dominator);
+ assertEquals(b, c.dominator);
+ assertEquals(p, d.dominator);
+ assertEquals(p, e.dominator);
+ }
+}
diff --git a/tools/ahat/test/InstanceTest.java b/tools/ahat/test/InstanceTest.java
index 71b081c..f0e7f44 100644
--- a/tools/ahat/test/InstanceTest.java
+++ b/tools/ahat/test/InstanceTest.java
@@ -337,7 +337,7 @@
public void classObjToString() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getAhatSnapshot().findClass("Main");
- assertEquals("Main", obj.toString());
+ assertEquals("class Main", obj.toString());
}
@Test
@@ -370,6 +370,18 @@
}
@Test
+ public void reverseReferences() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ AhatInstance obj = dump.getDumpedAhatInstance("anObject");
+ AhatInstance ref = dump.getDumpedAhatInstance("aReference");
+ AhatInstance weak = dump.getDumpedAhatInstance("aWeakReference");
+ assertTrue(obj.getHardReverseReferences().contains(ref));
+ assertFalse(obj.getHardReverseReferences().contains(weak));
+ assertFalse(obj.getSoftReverseReferences().contains(ref));
+ assertTrue(obj.getSoftReverseReferences().contains(weak));
+ }
+
+ @Test
public void asStringEmbedded() throws IOException {
// Set up a heap dump with an instance of java.lang.String of
// "hello" with instance id 0x42 that is backed by a char array that is
diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java
index a95788e..a1e3246 100644
--- a/tools/ahat/test/Tests.java
+++ b/tools/ahat/test/Tests.java
@@ -24,6 +24,7 @@
args = new String[]{
"com.android.ahat.DiffFieldsTest",
"com.android.ahat.DiffTest",
+ "com.android.ahat.DominatorsTest",
"com.android.ahat.InstanceTest",
"com.android.ahat.NativeAllocationTest",
"com.android.ahat.ObjectHandlerTest",
diff --git a/tools/art b/tools/art
index 077dc4a..15993dd 100644
--- a/tools/art
+++ b/tools/art
@@ -17,13 +17,13 @@
# Android (e.g. mksh).
# Globals
-ARCHS={arm,arm64,mips,mips64,x86,x86_64}
ART_BINARY=dalvikvm
DELETE_ANDROID_DATA="no"
LAUNCH_WRAPPER=
LIBART=libart.so
JIT_PROFILE="no"
VERBOSE="no"
+CLEAN_OAT_FILES="yes"
EXTRA_OPTIONS=()
# Follow all sym links to get the program name.
@@ -88,6 +88,7 @@
report upon completion.
--profile Run with profiling, then run using profile data.
--verbose Run script verbosely.
+ --no-clean Don't cleanup oat directories.
The ART_OPTIONS are passed directly to the Android Runtime.
@@ -120,26 +121,98 @@
env "$@"
}
+# Parse a colon-separated list into an array (e.g. "foo.dex:bar.dex" -> (foo.dex bar.dex))
+PARSE_CLASSPATH_RESULT=() # Return value will be here due to shell limitations.
+parse_classpath() {
+ local cp="$1"
+ local oldifs=$IFS
+
+ local cp_array
+ cp_array=()
+
+ IFS=":"
+ for part in $cp; do
+ cp_array+=("$part")
+ done
+ IFS=$oldifs
+
+ PARSE_CLASSPATH_RESULT=("${cp_array[@]}")
+}
+
+# Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+# e.g. (-cp foo/classes.dex:bar/classes.dex) -> (foo/classes.dex bar/classes.dex)
+find_cp_in_args() {
+ local found="false"
+ local index=0
+ local what
+
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -cp|-classpath)
+ parse_classpath "$2"
+ # Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+ # Subsequent parses will overwrite the preceding.
+ shift
+ ;;
+ esac
+ shift
+ done
+}
+
+# Delete the 'oat' directories relative to the classpath's dex files.
+# e.g. (foo/classes.dex bar/classes.dex) would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory() {
+ local classpath
+ classpath=("$@")
+
+ local dirpath
+
+ for path in "${classpath[@]}"; do
+ dirpath="$(dirname "$path")"
+ [[ -d "$dirpath" ]] && verbose_run rm -rf "$dirpath/oat"
+ done
+}
+
+# Parse -cp <CP>, -classpath <CP>, and $CLASSPATH to find the dex files.
+# Each dex file's directory will have an 'oat' file directory, delete it.
+# Input: Command line arguments to the art script.
+# e.g. -cp foo/classes.dex:bar/classes.dex would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory_for_classpath() {
+ if [ "$CLEAN_OAT_FILES" = "yes" ]; then
+ # First try: Use $CLASSPATH environment variable.
+ parse_classpath "$CLASSPATH"
+ # Second try: Look for latest -cp or -classpath arg which will take precedence.
+ find_cp_in_args "$@"
+
+ cleanup_oat_directory "${PARSE_CLASSPATH_RESULT[@]}"
+ fi
+}
+
+# Attempt to find $ANDROID_ROOT/framework/<isa>/core.art' without knowing what <isa> is.
+function check_if_boot_image_file_exists() {
+ local image_location_dir="$1"
+ local image_location_name="$2"
+
+ # Expand image_files to a list of existing image files on the disk.
+ # If no such files exist, it expands to single element 'dir/*/file' with a literal '*'.
+ local image_files
+ image_files=("$image_location_dir"/*/"$image_location_name") # avoid treating "*" as literal.
+
+ # Array always has at least 1 element. Test explicitly whether the file exists.
+ [[ -e "${image_files[0]}" ]]
+}
+
# Automatically find the boot image location. It uses core.art by default.
# On a real device, it might only have a boot.art, so use that instead when core.art does not exist.
function detect_boot_image_location() {
local image_location_dir="$ANDROID_ROOT/framework"
local image_location_name="core.art"
- local maybe_arch
- local core_image_exists="false"
-
- # Parse ARCHS={a,b,c,d} syntax.
- local array
- IFS=, read -a array <<< "${ARCHS:1:(-1)}";
- for maybe_arch in "${array[@]}"; do
- if [[ -e "$image_location_dir/$maybe_arch/$image_location_name" ]]; then
- core_image_exists="true"
- break
- fi
- done
-
- if [[ "$core_image_exists" == "false" ]]; then
+ # If there are no existing core.art, try to find boot.art.
+ # If there is no boot.art then leave it as-is, assumes -Ximage is explicitly used.
+ # Otherwise let dalvikvm give the error message about an invalid image file.
+ if ! check_if_boot_image_file_exists "$image_location_dir" "core.art" && \
+ check_if_boot_image_file_exists "$image_location_dir" "boot.art"; then
image_location_name="boot.art"
fi
@@ -147,9 +220,15 @@
echo "$image_location"
}
+# Runs dalvikvm, returns its exit code.
+# (Oat directories are cleaned up in between runs)
function run_art() {
local image_location="$(detect_boot_image_location)"
+ local ret
+ # 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 \
@@ -160,6 +239,13 @@
-Xnorelocate \
-Ximage:"$image_location" \
"$@"
+ ret=$?
+
+ # Avoid polluting disk with 'oat' files after dalvikvm has finished.
+ cleanup_oat_directory_for_classpath "$@"
+
+ # Forward exit code of dalvikvm.
+ return $ret
}
while [[ "$1" = "-"* ]]; do
@@ -209,6 +295,9 @@
--verbose)
VERBOSE="yes"
;;
+ --no-clean)
+ CLEAN_OAT_FILES="no"
+ ;;
--*)
echo "unknown option: $1" 1>&2
usage
@@ -251,7 +340,7 @@
# by default.
ANDROID_DATA="$ANDROID_DATA/local/tmp/android-data$$"
fi
- mkdir -p $ANDROID_DATA/dalvik-cache/$ARCHS
+ mkdir -p "$ANDROID_DATA"
DELETE_ANDROID_DATA="yes"
fi
@@ -264,7 +353,7 @@
# Create the profile. The runtime expects profiles to be created before
# execution.
PROFILE_PATH="$ANDROID_DATA/primary.prof"
- touch $PROFILE_PATH
+ touch "$PROFILE_PATH"
# Replace the compiler filter with quicken so that we
# can capture the profile.
@@ -278,17 +367,19 @@
-Xps-profile-path:$PROFILE_PATH \
-Xusejit:true \
"${ARGS_WITH_QUICKEN[@]}" \
- "&>" "$ANDROID_DATA/profile_gen.log"
+ &> "$ANDROID_DATA/profile_gen.log"
EXIT_STATUS=$?
if [ $EXIT_STATUS != 0 ]; then
- cat "$ANDROID_DATA/profile_gen.log"
+ echo "Profile run failed: " >&2
+ cat "$ANDROID_DATA/profile_gen.log" >&2
clean_android_data
exit $EXIT_STATUS
fi
- # Wipe dalvik-cache to prepare it for the next invocation.
- rm -rf $ANDROID_DATA/dalvik-cache/$ARCHS/*
+ # Wipe dalvik-cache so that a subsequent run_art must regenerate it.
+ # Leave $ANDROID_DATA intact since it contains our profile file.
+ rm -rf "$ANDROID_DATA/dalvik-cache"
# Append arguments so next invocation of run_art uses the profile.
EXTRA_OPTIONS+=(-Xcompiler-option --profile-file="$PROFILE_PATH")
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 75694c3..4f99ac3 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -19,6 +19,8 @@
exit 1
fi
+source build/envsetup.sh >&/dev/null # for get_build_var
+
# Logic for setting out_dir from build/make/core/envsetup.mk:
if [[ -z $OUT_DIR ]]; then
if [[ -z $OUT_DIR_COMMON_BASE ]]; then
@@ -30,10 +32,7 @@
out_dir=${OUT_DIR}
fi
-using_jack=true
-if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
- using_jack=false
-fi
+using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK)
java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES
common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target"
@@ -63,7 +62,7 @@
fi
done
-if $using_jack; then
+if [[ $using_jack == "true" ]]; then
common_targets="$common_targets ${out_dir}/host/linux-x86/bin/jack"
fi
diff --git a/tools/cpp-define-generator/constant_dexcache.def b/tools/cpp-define-generator/constant_dexcache.def
index ede16d2..743ebb7 100644
--- a/tools/cpp-define-generator/constant_dexcache.def
+++ b/tools/cpp-define-generator/constant_dexcache.def
@@ -25,4 +25,8 @@
DEFINE_EXPR(STRING_DEX_CACHE_HASH_BITS, int32_t,
art::LeastSignificantBit(art::mirror::DexCache::kDexCacheStringCacheSize))
DEFINE_EXPR(STRING_DEX_CACHE_ELEMENT_SIZE, int32_t,
- sizeof(art::mirror::StringDexCachePair))
\ No newline at end of file
+ sizeof(art::mirror::StringDexCachePair))
+DEFINE_EXPR(METHOD_DEX_CACHE_SIZE_MINUS_ONE, int32_t,
+ art::mirror::DexCache::kDexCacheMethodCacheSize - 1)
+DEFINE_EXPR(METHOD_DEX_CACHE_HASH_BITS, int32_t,
+ art::LeastSignificantBit(art::mirror::DexCache::kDexCacheMethodCacheSize))
diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def
index a3ccc72..dbaf33c 100644
--- a/tools/cpp-define-generator/constant_globals.def
+++ b/tools/cpp-define-generator/constant_globals.def
@@ -17,9 +17,12 @@
// Export global values.
#if defined(DEFINE_INCLUDE_DEPENDENCIES)
+#include <atomic> // std::memory_order_relaxed
#include "globals.h" // art::kObjectAlignment
#endif
+DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed)
+
#define DEFINE_OBJECT_EXPR(macro_name, type, constant_field_name) \
DEFINE_EXPR(OBJECT_ ## macro_name, type, constant_field_name)
diff --git a/tools/cpp-define-generator/main.cc b/tools/cpp-define-generator/main.cc
index fc99f8a..7c515be 100644
--- a/tools/cpp-define-generator/main.cc
+++ b/tools/cpp-define-generator/main.cc
@@ -14,12 +14,12 @@
* limitations under the License.
*/
+#include <algorithm>
+#include <ios>
#include <iostream>
#include <sstream>
-#include <type_traits>
-#include <ios>
-#include <algorithm>
#include <string>
+#include <type_traits>
// Art Offset file dependencies
#define DEFINE_INCLUDE_DEPENDENCIES
diff --git a/tools/cpp-define-generator/offset_runtime.def b/tools/cpp-define-generator/offset_runtime.def
index 41e7e40..1d5ce7d 100644
--- a/tools/cpp-define-generator/offset_runtime.def
+++ b/tools/cpp-define-generator/offset_runtime.def
@@ -40,6 +40,10 @@
DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_REFS_AND_ARGS, art::CalleeSaveType::kSaveRefsAndArgs)
// Offset of field Runtime::callee_save_methods_[kSaveEverything]
DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING, art::CalleeSaveType::kSaveEverything)
+// Offset of field Runtime::callee_save_methods_[kSaveEverythingForClinit]
+DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING_FOR_CLINIT, art::CalleeSaveType::kSaveEverythingForClinit)
+// Offset of field Runtime::callee_save_methods_[kSaveEverythingForSuspendCheck]
+DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING_FOR_SUSPEND_CHECK, art::CalleeSaveType::kSaveEverythingForSuspendCheck)
#undef DEFINE_RUNTIME_CALLEE_SAVE_OFFSET
#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpplint_presubmit.py b/tools/cpplint_presubmit.py
index 4781517..b42a691 100755
--- a/tools/cpplint_presubmit.py
+++ b/tools/cpplint_presubmit.py
@@ -21,7 +21,7 @@
import subprocess
import sys
-IGNORED_FILES = {"runtime/elf.h", "runtime/openjdkjvmti/include/jvmti.h"}
+IGNORED_FILES = {"runtime/elf.h", "openjdkjvmti/include/jvmti.h"}
INTERESTING_SUFFIXES = {".h", ".cc"}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
index 074672d..0367a83 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Executor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
@@ -114,8 +114,6 @@
commandBuilder.append("--oat-file=output.oat ");
commandBuilder.append("--android-root=").append(device.getAndroidHostOut()).append(" ");
- commandBuilder.append("--runtime-arg -classpath ");
- commandBuilder.append("--runtime-arg ").append(programName).append(" ");
commandBuilder.append("--dex-file=").append(programName).append(" ");
commandBuilder.append("--compiler-filter=quicken --runtime-arg -Xnorelocate ");
diff --git a/tools/generate_cmake_lists.py b/tools/generate_cmake_lists.py
new file mode 100755
index 0000000..6c3ce08
--- /dev/null
+++ b/tools/generate_cmake_lists.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""
+./generate_cmake_lists.py --project-name <project-name> --arch <arch>
+
+- project-name - name of the new project
+- arch - arch type. make generates seperate CMakeLists files for
+ each architecture. To avoid collision in targets, only one of
+ them can be included in the super project.
+
+The primary objective of this file is to generate CMakeLists files
+for CLion setup.
+
+Steps to setup CLion.
+1) Open the generated CMakeList file in CLion as a project.
+2) Change the project root ANDROID_BUILD_TOP.
+(Also, exclude projects that you don't bother about. This will make
+the indexing faster).
+"""
+
+import sys
+import os
+import subprocess
+import argparse
+
+def get_android_build_top():
+ path_to_top = os.environ.get('ANDROID_BUILD_TOP')
+ if not path_to_top:
+ # nothing set. try to guess it based on the relative path of this env.py file.
+ this_file_path = os.path.realpath(__file__)
+ path_to_top = os.path.join(os.path.dirname(this_file_path), '../..')
+ path_to_top = os.path.realpath(path_to_top)
+
+ if not os.path.exists(os.path.join(path_to_top, 'build/envsetup.sh')):
+ print path_to_top
+ raise AssertionError("geneate_cmake_lists.py must be located inside an android source tree")
+
+ return path_to_top
+
+def main():
+ # Parse arguments
+ parser = argparse.ArgumentParser(description="Generate CMakeLists files for ART")
+ parser.add_argument('--project-name', dest="project_name", required=True,
+ help='name of the project')
+ parser.add_argument('--arch', dest="arch", required=True, help='arch')
+ args = parser.parse_args()
+ project_name = args.project_name
+ arch = args.arch
+
+ # Invoke make to generate CMakeFiles
+ os.environ['SOONG_GEN_CMAKEFILES']='1'
+ os.environ['SOONG_GEN_CMAKEFILES_DEBUG']='1'
+
+ ANDROID_BUILD_TOP = get_android_build_top()
+
+ subprocess.check_output(('make -j64 -C %s') % (ANDROID_BUILD_TOP), shell=True)
+
+ out_art_cmakelists_dir = os.path.join(ANDROID_BUILD_TOP,
+ 'out/development/ide/clion/art')
+
+ # Prepare a list of directories containing generated CMakeLists files for sub projects.
+ cmake_sub_dirs = set()
+ for root, dirs, files in os.walk(out_art_cmakelists_dir):
+ for name in files:
+ if name == 'CMakeLists.txt':
+ if (os.path.samefile(root, out_art_cmakelists_dir)):
+ continue
+ if arch not in root:
+ continue
+ cmake_sub_dir = cmake_sub_dirs.add(root.replace(out_art_cmakelists_dir,
+ '.'))
+
+ # Generate CMakeLists file.
+ f = open(os.path.join(out_art_cmakelists_dir, 'CMakeLists.txt'), 'w')
+ f.write('cmake_minimum_required(VERSION 3.6)\n')
+ f.write('project(%s)\n' % (project_name))
+
+ for dr in cmake_sub_dirs:
+ f.write('add_subdirectory(%s)\n' % (dr))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc
index 4cd2335..7990c6c 100644
--- a/tools/jfuzz/jfuzz.cc
+++ b/tools/jfuzz/jfuzz.cc
@@ -18,8 +18,8 @@
#include <random>
#include <inttypes.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -55,7 +55,7 @@
* to preserve the property that a given version of JFuzz yields the same
* fuzzed program for a deterministic random seed.
*/
-const char* VERSION = "1.3";
+const char* VERSION = "1.4";
/*
* Maximum number of array dimensions, together with corresponding maximum size
@@ -804,13 +804,6 @@
return emitAssignment(); // fall back
}
- // TODO: remove this
- // The jack bug b/28862040 prevents generating while/do-while loops because otherwise
- // we get dozens of reports on the same issue per nightly/ run.
- if (true) {
- return emitAssignment();
- }
-
bool isWhile = random1(2) == 1;
fputs("{\n", out_);
indentation_ += 2;
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index 7e72aa1..58bc737 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -524,7 +524,9 @@
jfuzz_args = ['\'-{0}\''.format(arg)
for arg in jfuzz_cmd_str.strip().split(' -')][1:]
wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args]
- repro_cmd_str = (os.path.basename(__file__) + ' --num_tests 1 ' +
+ repro_cmd_str = (os.path.basename(__file__) +
+ ' --num_tests=1 ' +
+ ('--use_dx ' if self._use_dx else '') +
' '.join(wrapped_args))
comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format(
jfuzz_ver, jfuzz_cmd_str, repro_cmd_str)
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index b4c6f2b..5806b61 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -8,8 +8,11 @@
description: "Timeouts on target with gcstress and debug.",
result: EXEC_FAILED,
modes: [device],
- names: ["libcore.icu.TransliteratorTest#testAll",
+ names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
+ "libcore.icu.TransliteratorTest#testAll",
+ "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
"libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
+ "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout",
"libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
"org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]
}
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 225fb39..355646b 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -19,19 +19,19 @@
exit 1
fi
+source build/envsetup.sh >&/dev/null # for get_build_var, setpaths
+setpaths # include platform prebuilt java, javac, etc in $PATH.
+
if [ -z "$ANDROID_HOST_OUT" ] ; then
ANDROID_HOST_OUT=${OUT_DIR-$ANDROID_BUILD_TOP/out}/host/linux-x86
fi
-using_jack=true
-if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
- using_jack=false
-fi
+using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK)
function jlib_suffix {
local str=$1
local suffix="jar"
- if $using_jack; then
+ if [[ $using_jack == "true" ]]; then
suffix="jack"
fi
echo "$str.$suffix"
@@ -122,6 +122,9 @@
fi
done
+# Make sure the debuggee doesn't clean up what the debugger has generated.
+art_debugee="$art_debugee --no-clean"
+
# For the host:
#
# If, on the other hand, there is a variant set, use it to modify the art_debugee parameter to
@@ -161,7 +164,7 @@
art_debugee="$art_debugee -verbose:jdwp"
fi
-if $using_jack; then
+if [[ $using_jack == "true" ]]; then
toolchain_args="--toolchain jack --language JN --jack-arg -g"
else
toolchain_args="--toolchain jdk --language CUR"
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index eef74d2..eecdd2f 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -19,22 +19,22 @@
exit 1
fi
+source build/envsetup.sh >&/dev/null # for get_build_var, setpaths
+setpaths # include platform prebuilt java, javac, etc in $PATH.
+
if [ -z "$ANDROID_PRODUCT_OUT" ] ; then
JAVA_LIBRARIES=out/target/common/obj/JAVA_LIBRARIES
else
JAVA_LIBRARIES=${ANDROID_PRODUCT_OUT}/../../common/obj/JAVA_LIBRARIES
fi
-using_jack=true
-if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
- using_jack=false
-fi
+using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK)
function classes_jar_path {
local var="$1"
local suffix="jar"
- if $using_jack; then
+ if [[ $using_jack == "true" ]]; then
suffix="jack"
fi
@@ -146,7 +146,7 @@
vogar_args="$vogar_args --timeout 480"
# Switch between using jack or javac+desugar+dx
-if $using_jack; then
+if [[ $using_jack == "true" ]]; then
vogar_args="$vogar_args --toolchain jack --language JO"
else
vogar_args="$vogar_args --toolchain jdk --language CUR"